summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/compiler')
-rw-r--r--gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp161
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/64bit-tokenizer-safety.patch79
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp141
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DiagnosticsBase.h95
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp16
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h45
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DirectiveParser.cpp1004
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/DirectiveParser.h83
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/ExpressionParser.cpp2050
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/ExpressionParser.h43
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/ExpressionParser.y465
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Input.cpp117
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Input.h66
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Lexer.cpp16
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Lexer.h27
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Macro.cpp39
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Macro.h48
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/MacroExpander.cpp465
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/MacroExpander.h79
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Preprocessor.cpp102
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Preprocessor.h52
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/SourceLocation.h47
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Token.cpp82
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Token.h120
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Tokenizer.cpp2872
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Tokenizer.h58
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/Tokenizer.l357
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/generate_parser.sh27
-rwxr-xr-xgfx/angle/src/compiler/preprocessor/numeric_lex.h73
-rwxr-xr-xgfx/angle/src/compiler/translator/64bit-lexer-safety.patch122
-rwxr-xr-xgfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp454
-rwxr-xr-xgfx/angle/src/compiler/translator/ASTMetadataHLSL.h63
-rw-r--r--gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp59
-rw-r--r--gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h20
-rwxr-xr-xgfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp76
-rwxr-xr-xgfx/angle/src/compiler/translator/AddDefaultReturnStatements.h22
-rwxr-xr-xgfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp217
-rwxr-xr-xgfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h19
-rwxr-xr-xgfx/angle/src/compiler/translator/BaseTypes.h781
-rw-r--r--gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp106
-rw-r--r--gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h23
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp249
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h90
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp255
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h35
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp499
-rwxr-xr-xgfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h27
-rwxr-xr-xgfx/angle/src/compiler/translator/Cache.cpp104
-rwxr-xr-xgfx/angle/src/compiler/translator/Cache.h95
-rwxr-xr-xgfx/angle/src/compiler/translator/CallDAG.cpp348
-rwxr-xr-xgfx/angle/src/compiler/translator/CallDAG.h79
-rwxr-xr-xgfx/angle/src/compiler/translator/CodeGen.cpp84
-rwxr-xr-xgfx/angle/src/compiler/translator/Common.h100
-rwxr-xr-xgfx/angle/src/compiler/translator/Compiler.cpp1015
-rwxr-xr-xgfx/angle/src/compiler/translator/Compiler.h274
-rw-r--r--gfx/angle/src/compiler/translator/ConstantUnion.cpp642
-rwxr-xr-xgfx/angle/src/compiler/translator/ConstantUnion.h91
-rwxr-xr-xgfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp194
-rwxr-xr-xgfx/angle/src/compiler/translator/DeferGlobalInitializers.h23
-rwxr-xr-xgfx/angle/src/compiler/translator/Diagnostics.cpp86
-rwxr-xr-xgfx/angle/src/compiler/translator/Diagnostics.h53
-rwxr-xr-xgfx/angle/src/compiler/translator/DirectiveHandler.cpp202
-rwxr-xr-xgfx/angle/src/compiler/translator/DirectiveHandler.h57
-rwxr-xr-xgfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp137
-rwxr-xr-xgfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h28
-rwxr-xr-xgfx/angle/src/compiler/translator/EmulatePrecision.cpp726
-rwxr-xr-xgfx/angle/src/compiler/translator/EmulatePrecision.h72
-rwxr-xr-xgfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp154
-rwxr-xr-xgfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.h28
-rwxr-xr-xgfx/angle/src/compiler/translator/ExtensionBehavior.h43
-rwxr-xr-xgfx/angle/src/compiler/translator/ExtensionGLSL.cpp105
-rwxr-xr-xgfx/angle/src/compiler/translator/ExtensionGLSL.h44
-rwxr-xr-xgfx/angle/src/compiler/translator/FlagStd140Structs.cpp77
-rwxr-xr-xgfx/angle/src/compiler/translator/FlagStd140Structs.h43
-rwxr-xr-xgfx/angle/src/compiler/translator/ForLoopUnroll.cpp102
-rwxr-xr-xgfx/angle/src/compiler/translator/ForLoopUnroll.h58
-rwxr-xr-xgfx/angle/src/compiler/translator/HashNames.h18
-rwxr-xr-xgfx/angle/src/compiler/translator/InfoSink.cpp59
-rwxr-xr-xgfx/angle/src/compiler/translator/InfoSink.h121
-rwxr-xr-xgfx/angle/src/compiler/translator/Initialize.cpp762
-rwxr-xr-xgfx/angle/src/compiler/translator/Initialize.h34
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeDll.cpp43
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeDll.h16
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeGlobals.h13
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeParseContext.cpp46
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeParseContext.h21
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeVariables.cpp130
-rwxr-xr-xgfx/angle/src/compiler/translator/InitializeVariables.h31
-rwxr-xr-xgfx/angle/src/compiler/translator/IntermNode.cpp2883
-rwxr-xr-xgfx/angle/src/compiler/translator/IntermNode.h1207
-rwxr-xr-xgfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp109
-rwxr-xr-xgfx/angle/src/compiler/translator/IntermNodePatternMatcher.h58
-rwxr-xr-xgfx/angle/src/compiler/translator/IntermTraverse.cpp838
-rwxr-xr-xgfx/angle/src/compiler/translator/Intermediate.cpp387
-rwxr-xr-xgfx/angle/src/compiler/translator/Intermediate.h79
-rwxr-xr-xgfx/angle/src/compiler/translator/LoopInfo.cpp214
-rwxr-xr-xgfx/angle/src/compiler/translator/LoopInfo.h85
-rwxr-xr-xgfx/angle/src/compiler/translator/MMap.h56
-rwxr-xr-xgfx/angle/src/compiler/translator/NodeSearch.h59
-rwxr-xr-xgfx/angle/src/compiler/translator/Operator.cpp227
-rwxr-xr-xgfx/angle/src/compiler/translator/Operator.h229
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputESSL.cpp47
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputESSL.h37
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputGLSL.cpp111
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputGLSL.h36
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputGLSLBase.cpp1400
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputGLSLBase.h117
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputHLSL.cpp2857
-rwxr-xr-xgfx/angle/src/compiler/translator/OutputHLSL.h226
-rwxr-xr-xgfx/angle/src/compiler/translator/ParseContext.cpp4492
-rwxr-xr-xgfx/angle/src/compiler/translator/ParseContext.h438
-rwxr-xr-xgfx/angle/src/compiler/translator/PoolAlloc.cpp347
-rwxr-xr-xgfx/angle/src/compiler/translator/PoolAlloc.h303
-rwxr-xr-xgfx/angle/src/compiler/translator/Pragma.h32
-rwxr-xr-xgfx/angle/src/compiler/translator/PruneEmptyDeclarations.cpp113
-rwxr-xr-xgfx/angle/src/compiler/translator/PruneEmptyDeclarations.h18
-rw-r--r--gfx/angle/src/compiler/translator/QualifierTypes.cpp727
-rw-r--r--gfx/angle/src/compiler/translator/QualifierTypes.h191
-rwxr-xr-xgfx/angle/src/compiler/translator/RecordConstantPrecision.cpp167
-rwxr-xr-xgfx/angle/src/compiler/translator/RecordConstantPrecision.h26
-rwxr-xr-xgfx/angle/src/compiler/translator/RegenerateStructNames.cpp76
-rwxr-xr-xgfx/angle/src/compiler/translator/RegenerateStructNames.h46
-rwxr-xr-xgfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp513
-rwxr-xr-xgfx/angle/src/compiler/translator/RemoveDynamicIndexing.h26
-rw-r--r--gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp47
-rw-r--r--gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.h18
-rwxr-xr-xgfx/angle/src/compiler/translator/RemovePow.cpp99
-rwxr-xr-xgfx/angle/src/compiler/translator/RemovePow.h21
-rwxr-xr-xgfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp174
-rwxr-xr-xgfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h50
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteDoWhile.cpp159
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteDoWhile.h19
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteElseBlocks.cpp123
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteElseBlocks.h22
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp170
-rwxr-xr-xgfx/angle/src/compiler/translator/RewriteTexelFetchOffset.h30
-rw-r--r--gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp112
-rw-r--r--gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h20
-rwxr-xr-xgfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp306
-rwxr-xr-xgfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h26
-rwxr-xr-xgfx/angle/src/compiler/translator/SearchSymbol.cpp39
-rwxr-xr-xgfx/angle/src/compiler/translator/SearchSymbol.h33
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp91
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateArrayInitialization.h28
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateDeclarations.cpp78
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateDeclarations.h26
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp144
-rwxr-xr-xgfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h22
-rwxr-xr-xgfx/angle/src/compiler/translator/ShaderLang.cpp533
-rwxr-xr-xgfx/angle/src/compiler/translator/ShaderVars.cpp488
-rwxr-xr-xgfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp288
-rwxr-xr-xgfx/angle/src/compiler/translator/SimplifyLoopConditions.h26
-rwxr-xr-xgfx/angle/src/compiler/translator/SplitSequenceOperator.cpp158
-rwxr-xr-xgfx/angle/src/compiler/translator/SplitSequenceOperator.h29
-rwxr-xr-xgfx/angle/src/compiler/translator/StructureHLSL.cpp539
-rwxr-xr-xgfx/angle/src/compiler/translator/StructureHLSL.h85
-rwxr-xr-xgfx/angle/src/compiler/translator/SymbolTable.cpp371
-rwxr-xr-xgfx/angle/src/compiler/translator/SymbolTable.h549
-rwxr-xr-xgfx/angle/src/compiler/translator/TextureFunctionHLSL.cpp1274
-rwxr-xr-xgfx/angle/src/compiler/translator/TextureFunctionHLSL.h76
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorESSL.cpp115
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorESSL.h30
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorGLSL.cpp298
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorGLSL.h36
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorHLSL.cpp148
-rwxr-xr-xgfx/angle/src/compiler/translator/TranslatorHLSL.h39
-rwxr-xr-xgfx/angle/src/compiler/translator/Types.cpp564
-rwxr-xr-xgfx/angle/src/compiler/translator/Types.h773
-rwxr-xr-xgfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp59
-rwxr-xr-xgfx/angle/src/compiler/translator/UnfoldShortCircuitAST.h36
-rwxr-xr-xgfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp187
-rwxr-xr-xgfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.h21
-rwxr-xr-xgfx/angle/src/compiler/translator/UniformHLSL.cpp473
-rwxr-xr-xgfx/angle/src/compiler/translator/UniformHLSL.h92
-rw-r--r--gfx/angle/src/compiler/translator/UseInterfaceBlockFields.cpp163
-rw-r--r--gfx/angle/src/compiler/translator/UseInterfaceBlockFields.h30
-rwxr-xr-xgfx/angle/src/compiler/translator/UtilsHLSL.cpp414
-rwxr-xr-xgfx/angle/src/compiler/translator/UtilsHLSL.h81
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateGlobalInitializer.cpp116
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateGlobalInitializer.h21
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateLimitations.cpp502
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateLimitations.h68
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateMaxParameters.cpp40
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateMaxParameters.h34
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateOutputs.cpp113
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateOutputs.h41
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateSwitch.cpp215
-rwxr-xr-xgfx/angle/src/compiler/translator/ValidateSwitch.h59
-rwxr-xr-xgfx/angle/src/compiler/translator/VariableInfo.cpp682
-rwxr-xr-xgfx/angle/src/compiler/translator/VariableInfo.h87
-rwxr-xr-xgfx/angle/src/compiler/translator/VariablePacker.cpp269
-rwxr-xr-xgfx/angle/src/compiler/translator/VariablePacker.h40
-rwxr-xr-xgfx/angle/src/compiler/translator/VersionGLSL.cpp141
-rwxr-xr-xgfx/angle/src/compiler/translator/VersionGLSL.h74
-rwxr-xr-xgfx/angle/src/compiler/translator/blocklayout.cpp126
-rwxr-xr-xgfx/angle/src/compiler/translator/blocklayout.h103
-rwxr-xr-xgfx/angle/src/compiler/translator/blocklayoutHLSL.cpp171
-rwxr-xr-xgfx/angle/src/compiler/translator/blocklayoutHLSL.h61
-rwxr-xr-xgfx/angle/src/compiler/translator/generate_parser.sh29
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang.h24
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang.l623
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang.y1533
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang_lex.cpp3513
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang_tab.cpp5153
-rwxr-xr-xgfx/angle/src/compiler/translator/glslang_tab.h270
-rwxr-xr-xgfx/angle/src/compiler/translator/intermOut.cpp738
-rwxr-xr-xgfx/angle/src/compiler/translator/length_limits.h26
-rwxr-xr-xgfx/angle/src/compiler/translator/util.cpp620
-rwxr-xr-xgfx/angle/src/compiler/translator/util.h73
209 files changed, 62478 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp b/gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp
new file mode 100644
index 000000000..3ea117ba0
--- /dev/null
+++ b/gfx/angle/src/compiler/fuzz/translator_fuzzer.cpp
@@ -0,0 +1,161 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// translator_fuzzer.cpp: A libfuzzer fuzzer for the shader translator.
+
+#include <stddef.h>
+#include <stdint.h>
+#include <unordered_map>
+#include <iostream>
+
+#include "compiler/translator/Compiler.h"
+#include "angle_gl.h"
+
+using namespace sh;
+
+struct TranslatorCacheKey
+{
+ bool operator==(const TranslatorCacheKey &other) const
+ {
+ return type == other.type && spec == other.spec && output == other.output;
+ }
+
+ uint32_t type = 0;
+ uint32_t spec = 0;
+ uint32_t output = 0;
+};
+
+namespace std
+{
+
+template <>
+struct hash<TranslatorCacheKey>
+{
+ std::size_t operator()(const TranslatorCacheKey &k) const
+ {
+ return (hash<uint32_t>()(k.type) << 1) ^ (hash<uint32_t>()(k.spec) >> 1) ^
+ hash<uint32_t>()(k.output);
+ }
+};
+} // namespace std
+
+static std::unordered_map<TranslatorCacheKey, TCompiler *> translators;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ // Reserve some size for future compile options
+ const size_t kHeaderSize = 128;
+
+ if (size <= kHeaderSize)
+ {
+ return 0;
+ }
+
+ // Make sure the rest of data will be a valid C string so that we don't have to copy it.
+ if (data[size - 1] != 0)
+ {
+ 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);
+
+ if (type != GL_FRAGMENT_SHADER && type != GL_VERTEX_SHADER)
+ {
+ return 0;
+ }
+
+ if (spec != SH_GLES2_SPEC && type != SH_WEBGL_SPEC && spec != SH_GLES3_SPEC &&
+ spec != SH_WEBGL2_SPEC)
+ {
+ return 0;
+ }
+
+ std::vector<uint32_t> validOutputs;
+ validOutputs.push_back(SH_ESSL_OUTPUT);
+ validOutputs.push_back(SH_GLSL_COMPATIBILITY_OUTPUT);
+ validOutputs.push_back(SH_GLSL_130_OUTPUT);
+ validOutputs.push_back(SH_GLSL_140_OUTPUT);
+ validOutputs.push_back(SH_GLSL_150_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_330_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_400_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_410_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_420_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT);
+ validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT);
+ validOutputs.push_back(SH_HLSL_3_0_OUTPUT);
+ validOutputs.push_back(SH_HLSL_4_1_OUTPUT);
+ validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT);
+ bool found = false;
+ for (auto valid : validOutputs)
+ {
+ found = found || (valid == output);
+ }
+ if (!found)
+ {
+ return 0;
+ }
+
+ size -= kHeaderSize;
+ data += kHeaderSize;
+
+ if (!ShInitialize())
+ {
+ return 0;
+ }
+
+ TranslatorCacheKey key;
+ key.type = type;
+ key.spec = spec;
+ key.output = output;
+
+ if (translators.find(key) == translators.end())
+ {
+ TCompiler *translator = ConstructCompiler(type, static_cast<ShShaderSpec>(spec),
+ static_cast<ShShaderOutput>(output));
+
+ if (!translator)
+ {
+ return 0;
+ }
+
+ ShBuiltInResources resources;
+ ShInitBuiltInResources(&resources);
+
+ // Enable all the extensions to have more coverage
+ resources.OES_standard_derivatives = 1;
+ resources.OES_EGL_image_external = 1;
+ resources.OES_EGL_image_external_essl3 = 1;
+ resources.NV_EGL_stream_consumer_external = 1;
+ resources.ARB_texture_rectangle = 1;
+ resources.EXT_blend_func_extended = 1;
+ resources.EXT_draw_buffers = 1;
+ resources.EXT_frag_depth = 1;
+ resources.EXT_shader_texture_lod = 1;
+ resources.WEBGL_debug_shader_precision = 1;
+ resources.EXT_shader_framebuffer_fetch = 1;
+ resources.NV_shader_framebuffer_fetch = 1;
+ resources.ARM_shader_framebuffer_fetch = 1;
+
+ if (!translator->Init(resources))
+ {
+ DeleteCompiler(translator);
+ return 0;
+ }
+
+ translators[key] = translator;
+ }
+
+ TCompiler *translator = translators[key];
+
+ const char *shaderStrings[] = {reinterpret_cast<const char *>(data)};
+ translator->compile(shaderStrings, 1, options);
+
+ return 0;
+}
diff --git a/gfx/angle/src/compiler/preprocessor/64bit-tokenizer-safety.patch b/gfx/angle/src/compiler/preprocessor/64bit-tokenizer-safety.patch
new file mode 100755
index 000000000..912520c74
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/64bit-tokenizer-safety.patch
@@ -0,0 +1,79 @@
+diff --git a/src/compiler/preprocessor/Tokenizer.cpp b/src/compiler/preprocessor/Tokenizer.cpp
+index 0d7ad58..5ef0e5e 100644
+--- a/src/compiler/preprocessor/Tokenizer.cpp
++++ b/src/compiler/preprocessor/Tokenizer.cpp
+@@ -1703,7 +1703,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+ else
+ {
+ int num_to_read =
+- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
++ static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1);
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+@@ -1737,8 +1737,8 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+- num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+- number_to_move - 1;
++ num_to_read = static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
++ number_to_move - 1);
+
+ }
+
+@@ -1746,8 +1746,10 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
++ yy_size_t ret = 0;
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+- yyg->yy_n_chars, num_to_read );
++ ret, num_to_read );
++ yyg->yy_n_chars = static_cast<int>(ret);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+@@ -1773,13 +1775,13 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+
+ if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+- int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
++ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) pprealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+- yyg->yy_n_chars += number_to_move;
++ yyg->yy_n_chars += static_cast<int>(number_to_move);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+@@ -2171,7 +2173,7 @@ void pppop_buffer_state (yyscan_t yyscanner)
+ */
+ static void ppensure_buffer_stack (yyscan_t yyscanner)
+ {
+- int num_to_alloc;
++ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+@@ -2238,7 +2240,7 @@ YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_scan_buffer()" );
+
+- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
++ b->yy_buf_size = static_cast<int>(size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+@@ -2293,7 +2295,7 @@ YY_BUFFER_STATE pp_scan_bytes (yyconst char * yybytes, int _yybytes_len , yysc
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_scan_bytes()" );
+
+- for ( i = 0; i < _yybytes_len; ++i )
++ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
diff --git a/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp b/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
new file mode 100755
index 000000000..fcb24e4e8
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.cpp
@@ -0,0 +1,141 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/DiagnosticsBase.h"
+
+#include "common/debug.h"
+
+namespace pp
+{
+
+Diagnostics::~Diagnostics()
+{
+}
+
+void Diagnostics::report(ID id,
+ const SourceLocation &loc,
+ const std::string &text)
+{
+ // TODO(alokp): Keep a count of errors and warnings.
+ print(id, loc, text);
+}
+
+Diagnostics::Severity Diagnostics::severity(ID id)
+{
+ if ((id > PP_ERROR_BEGIN) && (id < PP_ERROR_END))
+ return PP_ERROR;
+
+ if ((id > PP_WARNING_BEGIN) && (id < PP_WARNING_END))
+ return PP_WARNING;
+
+ UNREACHABLE();
+ return PP_ERROR;
+}
+
+std::string Diagnostics::message(ID id)
+{
+ switch (id)
+ {
+ // Errors begin.
+ case PP_INTERNAL_ERROR:
+ return "internal error";
+ case PP_OUT_OF_MEMORY:
+ return "out of memory";
+ case PP_INVALID_CHARACTER:
+ return "invalid character";
+ case PP_INVALID_NUMBER:
+ return "invalid number";
+ case PP_INTEGER_OVERFLOW:
+ return "integer overflow";
+ case PP_FLOAT_OVERFLOW:
+ return "float overflow";
+ case PP_TOKEN_TOO_LONG:
+ return "token too long";
+ case PP_INVALID_EXPRESSION:
+ return "invalid expression";
+ case PP_DIVISION_BY_ZERO:
+ return "division by zero";
+ case PP_EOF_IN_COMMENT:
+ return "unexpected end of file found in comment";
+ case PP_UNEXPECTED_TOKEN:
+ return "unexpected token";
+ case PP_DIRECTIVE_INVALID_NAME:
+ return "invalid directive name";
+ case PP_MACRO_NAME_RESERVED:
+ return "macro name is reserved";
+ case PP_MACRO_REDEFINED:
+ return "macro redefined";
+ case PP_MACRO_PREDEFINED_REDEFINED:
+ return "predefined macro redefined";
+ case PP_MACRO_PREDEFINED_UNDEFINED:
+ return "predefined macro undefined";
+ case PP_MACRO_UNTERMINATED_INVOCATION:
+ return "unterminated macro invocation";
+ case PP_MACRO_UNDEFINED_WHILE_INVOKED:
+ return "macro undefined while being invoked";
+ case PP_MACRO_TOO_FEW_ARGS:
+ return "Not enough arguments for macro";
+ case PP_MACRO_TOO_MANY_ARGS:
+ return "Too many arguments for macro";
+ case PP_MACRO_DUPLICATE_PARAMETER_NAMES:
+ return "duplicate macro parameter name";
+ case PP_CONDITIONAL_ENDIF_WITHOUT_IF:
+ return "unexpected #endif found without a matching #if";
+ case PP_CONDITIONAL_ELSE_WITHOUT_IF:
+ return "unexpected #else found without a matching #if";
+ case PP_CONDITIONAL_ELSE_AFTER_ELSE:
+ return "unexpected #else found after another #else";
+ case PP_CONDITIONAL_ELIF_WITHOUT_IF:
+ return "unexpected #elif found without a matching #if";
+ case PP_CONDITIONAL_ELIF_AFTER_ELSE:
+ return "unexpected #elif found after #else";
+ case PP_CONDITIONAL_UNTERMINATED:
+ return "unexpected end of file found in conditional block";
+ case PP_INVALID_EXTENSION_NAME:
+ return "invalid extension name";
+ case PP_INVALID_EXTENSION_BEHAVIOR:
+ return "invalid extension behavior";
+ case PP_INVALID_EXTENSION_DIRECTIVE:
+ return "invalid extension directive";
+ case PP_INVALID_VERSION_NUMBER:
+ return "invalid version number";
+ case PP_INVALID_VERSION_DIRECTIVE:
+ return "invalid version directive";
+ case PP_VERSION_NOT_FIRST_STATEMENT:
+ return "#version directive must occur before anything else, "
+ "except for comments and white space";
+ case PP_VERSION_NOT_FIRST_LINE_ESSL3:
+ return "#version directive must occur on the first line of the shader";
+ case PP_INVALID_LINE_NUMBER:
+ return "invalid line number";
+ case PP_INVALID_FILE_NUMBER:
+ return "invalid file number";
+ case PP_INVALID_LINE_DIRECTIVE:
+ return "invalid line directive";
+ case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3:
+ return "extension directive must occur before any non-preprocessor tokens in ESSL3";
+ case PP_UNDEFINED_SHIFT:
+ return "shift exponent is negative or undefined";
+ // Errors end.
+ // Warnings begin.
+ case PP_EOF_IN_DIRECTIVE:
+ return "unexpected end of file found in directive";
+ case PP_CONDITIONAL_UNEXPECTED_TOKEN:
+ return "unexpected token after conditional expression";
+ case PP_UNRECOGNIZED_PRAGMA:
+ return "unrecognized pragma";
+ case PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1:
+ return "extension directive should occur before any non-preprocessor tokens";
+ case PP_WARNING_MACRO_NAME_RESERVED:
+ return "macro name with a double underscore is reserved - unintented behavior is possible";
+ // Warnings end.
+ default:
+ UNREACHABLE();
+ return "";
+ }
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.h b/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.h
new file mode 100755
index 000000000..6c3fe9ee9
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DiagnosticsBase.h
@@ -0,0 +1,95 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
+#define COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
+
+#include <string>
+
+namespace pp
+{
+
+struct SourceLocation;
+
+// Base class for reporting diagnostic messages.
+// Derived classes are responsible for formatting and printing the messages.
+class Diagnostics
+{
+ public:
+ enum Severity
+ {
+ PP_ERROR,
+ PP_WARNING
+ };
+ enum ID
+ {
+ PP_ERROR_BEGIN,
+ PP_INTERNAL_ERROR,
+ PP_OUT_OF_MEMORY,
+ PP_INVALID_CHARACTER,
+ PP_INVALID_NUMBER,
+ PP_INTEGER_OVERFLOW,
+ PP_FLOAT_OVERFLOW,
+ PP_TOKEN_TOO_LONG,
+ PP_INVALID_EXPRESSION,
+ PP_DIVISION_BY_ZERO,
+ PP_EOF_IN_COMMENT,
+ PP_UNEXPECTED_TOKEN,
+ PP_DIRECTIVE_INVALID_NAME,
+ PP_MACRO_NAME_RESERVED,
+ PP_MACRO_REDEFINED,
+ PP_MACRO_PREDEFINED_REDEFINED,
+ PP_MACRO_PREDEFINED_UNDEFINED,
+ PP_MACRO_UNTERMINATED_INVOCATION,
+ PP_MACRO_UNDEFINED_WHILE_INVOKED,
+ PP_MACRO_TOO_FEW_ARGS,
+ PP_MACRO_TOO_MANY_ARGS,
+ PP_MACRO_DUPLICATE_PARAMETER_NAMES,
+ PP_CONDITIONAL_ENDIF_WITHOUT_IF,
+ PP_CONDITIONAL_ELSE_WITHOUT_IF,
+ PP_CONDITIONAL_ELSE_AFTER_ELSE,
+ PP_CONDITIONAL_ELIF_WITHOUT_IF,
+ PP_CONDITIONAL_ELIF_AFTER_ELSE,
+ PP_CONDITIONAL_UNTERMINATED,
+ PP_CONDITIONAL_UNEXPECTED_TOKEN,
+ PP_INVALID_EXTENSION_NAME,
+ PP_INVALID_EXTENSION_BEHAVIOR,
+ PP_INVALID_EXTENSION_DIRECTIVE,
+ PP_INVALID_VERSION_NUMBER,
+ PP_INVALID_VERSION_DIRECTIVE,
+ PP_VERSION_NOT_FIRST_STATEMENT,
+ PP_VERSION_NOT_FIRST_LINE_ESSL3,
+ PP_INVALID_LINE_NUMBER,
+ PP_INVALID_FILE_NUMBER,
+ PP_INVALID_LINE_DIRECTIVE,
+ PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
+ PP_UNDEFINED_SHIFT,
+ PP_ERROR_END,
+
+ PP_WARNING_BEGIN,
+ PP_EOF_IN_DIRECTIVE,
+ PP_UNRECOGNIZED_PRAGMA,
+ PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
+ PP_WARNING_MACRO_NAME_RESERVED,
+ PP_WARNING_END
+ };
+
+ virtual ~Diagnostics();
+
+ void report(ID id, const SourceLocation &loc, const std::string &text);
+
+ protected:
+ Severity severity(ID id);
+ std::string message(ID id);
+
+ virtual void print(ID id,
+ const SourceLocation &loc,
+ const std::string &text) = 0;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_
diff --git a/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp b/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
new file mode 100755
index 000000000..049dae907
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.cpp
@@ -0,0 +1,16 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/DirectiveHandlerBase.h"
+
+namespace pp
+{
+
+DirectiveHandler::~DirectiveHandler()
+{
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h b/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
new file mode 100755
index 000000000..cf6789576
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DirectiveHandlerBase.h
@@ -0,0 +1,45 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
+#define COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
+
+#include <string>
+
+namespace pp
+{
+
+struct SourceLocation;
+
+// Base class for handling directives.
+// Preprocessor uses this class to notify the clients about certain
+// preprocessor directives. Derived classes are responsible for
+// handling them in an appropriate manner.
+class DirectiveHandler
+{
+ public:
+ virtual ~DirectiveHandler();
+
+ virtual void handleError(const SourceLocation &loc,
+ const std::string &msg) = 0;
+
+ // Handle pragma of form: #pragma name[(value)]
+ virtual void handlePragma(const SourceLocation &loc,
+ const std::string &name,
+ const std::string &value,
+ bool stdgl) = 0;
+
+ virtual void handleExtension(const SourceLocation &loc,
+ const std::string &name,
+ const std::string &behavior) = 0;
+
+ virtual void handleVersion(const SourceLocation &loc,
+ int version) = 0;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_
diff --git a/gfx/angle/src/compiler/preprocessor/DirectiveParser.cpp b/gfx/angle/src/compiler/preprocessor/DirectiveParser.cpp
new file mode 100755
index 000000000..643f73bd9
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DirectiveParser.cpp
@@ -0,0 +1,1004 @@
+//
+// Copyright (c) 2011-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/DirectiveParser.h"
+
+#include <algorithm>
+#include <cstdlib>
+#include <sstream>
+
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/DirectiveHandlerBase.h"
+#include "compiler/preprocessor/ExpressionParser.h"
+#include "compiler/preprocessor/MacroExpander.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/preprocessor/Tokenizer.h"
+
+namespace {
+enum DirectiveType
+{
+ DIRECTIVE_NONE,
+ DIRECTIVE_DEFINE,
+ DIRECTIVE_UNDEF,
+ DIRECTIVE_IF,
+ DIRECTIVE_IFDEF,
+ DIRECTIVE_IFNDEF,
+ DIRECTIVE_ELSE,
+ DIRECTIVE_ELIF,
+ DIRECTIVE_ENDIF,
+ DIRECTIVE_ERROR,
+ DIRECTIVE_PRAGMA,
+ DIRECTIVE_EXTENSION,
+ DIRECTIVE_VERSION,
+ DIRECTIVE_LINE
+};
+
+DirectiveType getDirective(const pp::Token *token)
+{
+ const char kDirectiveDefine[] = "define";
+ const char kDirectiveUndef[] = "undef";
+ const char kDirectiveIf[] = "if";
+ const char kDirectiveIfdef[] = "ifdef";
+ const char kDirectiveIfndef[] = "ifndef";
+ const char kDirectiveElse[] = "else";
+ const char kDirectiveElif[] = "elif";
+ const char kDirectiveEndif[] = "endif";
+ const char kDirectiveError[] = "error";
+ const char kDirectivePragma[] = "pragma";
+ const char kDirectiveExtension[] = "extension";
+ const char kDirectiveVersion[] = "version";
+ const char kDirectiveLine[] = "line";
+
+ if (token->type != pp::Token::IDENTIFIER)
+ return DIRECTIVE_NONE;
+
+ if (token->text == kDirectiveDefine)
+ return DIRECTIVE_DEFINE;
+ if (token->text == kDirectiveUndef)
+ return DIRECTIVE_UNDEF;
+ if (token->text == kDirectiveIf)
+ return DIRECTIVE_IF;
+ if (token->text == kDirectiveIfdef)
+ return DIRECTIVE_IFDEF;
+ if (token->text == kDirectiveIfndef)
+ return DIRECTIVE_IFNDEF;
+ if (token->text == kDirectiveElse)
+ return DIRECTIVE_ELSE;
+ if (token->text == kDirectiveElif)
+ return DIRECTIVE_ELIF;
+ if (token->text == kDirectiveEndif)
+ return DIRECTIVE_ENDIF;
+ if (token->text == kDirectiveError)
+ return DIRECTIVE_ERROR;
+ if (token->text == kDirectivePragma)
+ return DIRECTIVE_PRAGMA;
+ if (token->text == kDirectiveExtension)
+ return DIRECTIVE_EXTENSION;
+ if (token->text == kDirectiveVersion)
+ return DIRECTIVE_VERSION;
+ if (token->text == kDirectiveLine)
+ return DIRECTIVE_LINE;
+
+ return DIRECTIVE_NONE;
+}
+
+bool isConditionalDirective(DirectiveType directive)
+{
+ switch (directive)
+ {
+ case DIRECTIVE_IF:
+ case DIRECTIVE_IFDEF:
+ case DIRECTIVE_IFNDEF:
+ case DIRECTIVE_ELSE:
+ case DIRECTIVE_ELIF:
+ case DIRECTIVE_ENDIF:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Returns true if the token represents End Of Directive.
+bool isEOD(const pp::Token *token)
+{
+ return (token->type == '\n') || (token->type == pp::Token::LAST);
+}
+
+void skipUntilEOD(pp::Lexer *lexer, pp::Token *token)
+{
+ while(!isEOD(token))
+ {
+ lexer->lex(token);
+ }
+}
+
+bool isMacroNameReserved(const std::string &name)
+{
+ // Names prefixed with "GL_" and the name "defined" are reserved.
+ return name == "defined" || (name.substr(0, 3) == "GL_");
+}
+
+bool hasDoubleUnderscores(const std::string &name)
+{
+ return (name.find("__") != std::string::npos);
+}
+
+bool isMacroPredefined(const std::string &name,
+ const pp::MacroSet &macroSet)
+{
+ pp::MacroSet::const_iterator iter = macroSet.find(name);
+ return iter != macroSet.end() ? iter->second.predefined : false;
+}
+
+} // namespace anonymous
+
+namespace pp
+{
+
+class DefinedParser : public Lexer
+{
+ public:
+ DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics)
+ : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics)
+ {
+ }
+
+ protected:
+ void lex(Token *token) override
+ {
+ const char kDefined[] = "defined";
+
+ mLexer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ return;
+ if (token->text != kDefined)
+ return;
+
+ bool paren = false;
+ mLexer->lex(token);
+ if (token->type == '(')
+ {
+ paren = true;
+ mLexer->lex(token);
+ }
+
+ if (token->type != Token::IDENTIFIER)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
+ skipUntilEOD(mLexer, token);
+ return;
+ }
+ MacroSet::const_iterator iter = mMacroSet->find(token->text);
+ std::string expression = iter != mMacroSet->end() ? "1" : "0";
+
+ if (paren)
+ {
+ mLexer->lex(token);
+ if (token->type != ')')
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
+ token->text);
+ skipUntilEOD(mLexer, token);
+ return;
+ }
+ }
+
+ // We have a valid defined operator.
+ // Convert the current token into a CONST_INT token.
+ token->type = Token::CONST_INT;
+ token->text = expression;
+ }
+
+ private:
+ Lexer *mLexer;
+ const MacroSet *mMacroSet;
+ Diagnostics *mDiagnostics;
+};
+
+DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
+ MacroSet *macroSet,
+ Diagnostics *diagnostics,
+ DirectiveHandler *directiveHandler)
+ : mPastFirstStatement(false),
+ mSeenNonPreprocessorToken(false),
+ mTokenizer(tokenizer),
+ mMacroSet(macroSet),
+ mDiagnostics(diagnostics),
+ mDirectiveHandler(directiveHandler),
+ mShaderVersion(100)
+{
+}
+
+void DirectiveParser::lex(Token *token)
+{
+ do
+ {
+ mTokenizer->lex(token);
+
+ if (token->type == Token::PP_HASH)
+ {
+ parseDirective(token);
+ mPastFirstStatement = true;
+ }
+ else if (!isEOD(token))
+ {
+ mSeenNonPreprocessorToken = true;
+ }
+
+ if (token->type == Token::LAST)
+ {
+ if (!mConditionalStack.empty())
+ {
+ const ConditionalBlock &block = mConditionalStack.back();
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED,
+ block.location, block.type);
+ }
+ break;
+ }
+
+ }
+ while (skipping() || (token->type == '\n'));
+
+ mPastFirstStatement = true;
+}
+
+void DirectiveParser::parseDirective(Token *token)
+{
+ ASSERT(token->type == Token::PP_HASH);
+
+ mTokenizer->lex(token);
+ if (isEOD(token))
+ {
+ // Empty Directive.
+ return;
+ }
+
+ DirectiveType directive = getDirective(token);
+
+ // While in an excluded conditional block/group,
+ // we only parse conditional directives.
+ if (skipping() && !isConditionalDirective(directive))
+ {
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ switch(directive)
+ {
+ case DIRECTIVE_NONE:
+ mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ break;
+ case DIRECTIVE_DEFINE:
+ parseDefine(token);
+ break;
+ case DIRECTIVE_UNDEF:
+ parseUndef(token);
+ break;
+ case DIRECTIVE_IF:
+ parseIf(token);
+ break;
+ case DIRECTIVE_IFDEF:
+ parseIfdef(token);
+ break;
+ case DIRECTIVE_IFNDEF:
+ parseIfndef(token);
+ break;
+ case DIRECTIVE_ELSE:
+ parseElse(token);
+ break;
+ case DIRECTIVE_ELIF:
+ parseElif(token);
+ break;
+ case DIRECTIVE_ENDIF:
+ parseEndif(token);
+ break;
+ case DIRECTIVE_ERROR:
+ parseError(token);
+ break;
+ case DIRECTIVE_PRAGMA:
+ parsePragma(token);
+ break;
+ case DIRECTIVE_EXTENSION:
+ parseExtension(token);
+ break;
+ case DIRECTIVE_VERSION:
+ parseVersion(token);
+ break;
+ case DIRECTIVE_LINE:
+ parseLine(token);
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ skipUntilEOD(mTokenizer, token);
+ if (token->type == Token::LAST)
+ {
+ mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE,
+ token->location, token->text);
+ }
+}
+
+void DirectiveParser::parseDefine(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_DEFINE);
+
+ mTokenizer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ return;
+ }
+ if (isMacroPredefined(token->text, *mMacroSet))
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED,
+ token->location, token->text);
+ return;
+ }
+ if (isMacroNameReserved(token->text))
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED,
+ token->location, token->text);
+ return;
+ }
+ // Using double underscores is allowed, but may result in unintended
+ // behavior, so a warning is issued. At the time of writing this was
+ // specified in ESSL 3.10, but the intent judging from Khronos
+ // discussions and dEQP tests was that double underscores should be
+ // allowed in earlier ESSL versions too.
+ if (hasDoubleUnderscores(token->text))
+ {
+ mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location,
+ token->text);
+ }
+
+ Macro macro;
+ macro.type = Macro::kTypeObj;
+ macro.name = token->text;
+
+ mTokenizer->lex(token);
+ if (token->type == '(' && !token->hasLeadingSpace())
+ {
+ // Function-like macro. Collect arguments.
+ macro.type = Macro::kTypeFunc;
+ do
+ {
+ mTokenizer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ break;
+
+ if (std::find(macro.parameters.begin(), macro.parameters.end(), token->text) != macro.parameters.end())
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
+ token->location, token->text);
+ return;
+ }
+
+ macro.parameters.push_back(token->text);
+
+ mTokenizer->lex(token); // Get ','.
+ }
+ while (token->type == ',');
+
+ if (token->type != ')')
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location,
+ token->text);
+ return;
+ }
+ mTokenizer->lex(token); // Get ')'.
+ }
+
+ while ((token->type != '\n') && (token->type != Token::LAST))
+ {
+ // Reset the token location because it is unnecessary in replacement
+ // list. Resetting it also allows us to reuse Token::equals() to
+ // compare macros.
+ token->location = SourceLocation();
+ macro.replacements.push_back(*token);
+ mTokenizer->lex(token);
+ }
+ if (!macro.replacements.empty())
+ {
+ // Whitespace preceding the replacement list is not considered part of
+ // the replacement list for either form of macro.
+ macro.replacements.front().setHasLeadingSpace(false);
+ }
+
+ // Check for macro redefinition.
+ MacroSet::const_iterator iter = mMacroSet->find(macro.name);
+ if (iter != mMacroSet->end() && !macro.equals(iter->second))
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED,
+ token->location,
+ macro.name);
+ return;
+ }
+ mMacroSet->insert(std::make_pair(macro.name, macro));
+}
+
+void DirectiveParser::parseUndef(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_UNDEF);
+
+ mTokenizer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ return;
+ }
+
+ MacroSet::iterator iter = mMacroSet->find(token->text);
+ if (iter != mMacroSet->end())
+ {
+ if (iter->second.predefined)
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED,
+ token->location, token->text);
+ return;
+ }
+ else if (iter->second.expansionCount > 0)
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
+ token->text);
+ return;
+ }
+ else
+ {
+ mMacroSet->erase(iter);
+ }
+ }
+
+ mTokenizer->lex(token);
+ if (!isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ }
+}
+
+void DirectiveParser::parseIf(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_IF);
+ parseConditionalIf(token);
+}
+
+void DirectiveParser::parseIfdef(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_IFDEF);
+ parseConditionalIf(token);
+}
+
+void DirectiveParser::parseIfndef(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_IFNDEF);
+ parseConditionalIf(token);
+}
+
+void DirectiveParser::parseElse(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_ELSE);
+
+ if (mConditionalStack.empty())
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ ConditionalBlock &block = mConditionalStack.back();
+ if (block.skipBlock)
+ {
+ // No diagnostics. Just skip the whole line.
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+ if (block.foundElseGroup)
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ block.foundElseGroup = true;
+ block.skipGroup = block.foundValidGroup;
+ block.foundValidGroup = true;
+
+ // Check if there are extra tokens after #else.
+ mTokenizer->lex(token);
+ if (!isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ }
+}
+
+void DirectiveParser::parseElif(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_ELIF);
+
+ if (mConditionalStack.empty())
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ ConditionalBlock &block = mConditionalStack.back();
+ if (block.skipBlock)
+ {
+ // No diagnostics. Just skip the whole line.
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+ if (block.foundElseGroup)
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+ if (block.foundValidGroup)
+ {
+ // Do not parse the expression.
+ // Also be careful not to emit a diagnostic.
+ block.skipGroup = true;
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ int expression = parseExpressionIf(token);
+ block.skipGroup = expression == 0;
+ block.foundValidGroup = expression != 0;
+}
+
+void DirectiveParser::parseEndif(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_ENDIF);
+
+ if (mConditionalStack.empty())
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ mConditionalStack.pop_back();
+
+ // Check if there are tokens after #endif.
+ mTokenizer->lex(token);
+ if (!isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ }
+}
+
+void DirectiveParser::parseError(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_ERROR);
+
+ std::ostringstream stream;
+ mTokenizer->lex(token);
+ while ((token->type != '\n') && (token->type != Token::LAST))
+ {
+ stream << *token;
+ mTokenizer->lex(token);
+ }
+ mDirectiveHandler->handleError(token->location, stream.str());
+}
+
+// Parses pragma of form: #pragma name[(value)].
+void DirectiveParser::parsePragma(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_PRAGMA);
+
+ enum State
+ {
+ PRAGMA_NAME,
+ LEFT_PAREN,
+ PRAGMA_VALUE,
+ RIGHT_PAREN
+ };
+
+ bool valid = true;
+ std::string name, value;
+ int state = PRAGMA_NAME;
+
+ mTokenizer->lex(token);
+ bool stdgl = token->text == "STDGL";
+ if (stdgl)
+ {
+ mTokenizer->lex(token);
+ }
+ while ((token->type != '\n') && (token->type != Token::LAST))
+ {
+ switch(state++)
+ {
+ case PRAGMA_NAME:
+ name = token->text;
+ valid = valid && (token->type == Token::IDENTIFIER);
+ break;
+ case LEFT_PAREN:
+ valid = valid && (token->type == '(');
+ break;
+ case PRAGMA_VALUE:
+ value = token->text;
+ valid = valid && (token->type == Token::IDENTIFIER);
+ break;
+ case RIGHT_PAREN:
+ valid = valid && (token->type == ')');
+ break;
+ default:
+ valid = false;
+ break;
+ }
+ mTokenizer->lex(token);
+ }
+
+ valid = valid && ((state == PRAGMA_NAME) || // Empty pragma.
+ (state == LEFT_PAREN) || // Without value.
+ (state == RIGHT_PAREN + 1)); // With value.
+ if (!valid)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA,
+ token->location, name);
+ }
+ else if (state > PRAGMA_NAME) // Do not notify for empty pragma.
+ {
+ mDirectiveHandler->handlePragma(token->location, name, value, stdgl);
+ }
+}
+
+void DirectiveParser::parseExtension(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_EXTENSION);
+
+ enum State
+ {
+ EXT_NAME,
+ COLON,
+ EXT_BEHAVIOR
+ };
+
+ bool valid = true;
+ std::string name, behavior;
+ int state = EXT_NAME;
+
+ mTokenizer->lex(token);
+ while ((token->type != '\n') && (token->type != Token::LAST))
+ {
+ switch (state++)
+ {
+ case EXT_NAME:
+ if (valid && (token->type != Token::IDENTIFIER))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid) name = token->text;
+ break;
+ case COLON:
+ if (valid && (token->type != ':'))
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ valid = false;
+ }
+ break;
+ case EXT_BEHAVIOR:
+ if (valid && (token->type != Token::IDENTIFIER))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid) behavior = token->text;
+ break;
+ default:
+ if (valid)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ valid = false;
+ }
+ break;
+ }
+ mTokenizer->lex(token);
+ }
+ if (valid && (state != EXT_BEHAVIOR + 1))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid && mSeenNonPreprocessorToken)
+ {
+ if (mShaderVersion >= 300)
+ {
+ mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
+ token->location, token->text);
+ valid = false;
+ }
+ else
+ {
+ mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
+ token->location, token->text);
+ }
+ }
+ if (valid)
+ mDirectiveHandler->handleExtension(token->location, name, behavior);
+}
+
+void DirectiveParser::parseVersion(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_VERSION);
+
+ if (mPastFirstStatement)
+ {
+ mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return;
+ }
+
+ enum State
+ {
+ VERSION_NUMBER,
+ VERSION_PROFILE,
+ VERSION_ENDLINE
+ };
+
+ bool valid = true;
+ int version = 0;
+ int state = VERSION_NUMBER;
+
+ mTokenizer->lex(token);
+ while (valid && (token->type != '\n') && (token->type != Token::LAST))
+ {
+ switch (state)
+ {
+ case VERSION_NUMBER:
+ if (token->type != Token::CONST_INT)
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid && !token->iValue(&version))
+ {
+ mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW,
+ token->location, token->text);
+ valid = false;
+ }
+ if (valid)
+ {
+ state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
+ }
+ break;
+ case VERSION_PROFILE:
+ if (token->type != Token::IDENTIFIER || token->text != "es")
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
+ token->location, token->text);
+ valid = false;
+ }
+ state = VERSION_ENDLINE;
+ break;
+ default:
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ valid = false;
+ break;
+ }
+
+ mTokenizer->lex(token);
+ }
+
+ if (valid && (state != VERSION_ENDLINE))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE,
+ token->location, token->text);
+ valid = false;
+ }
+
+ if (valid && version >= 300 && token->location.line > 1)
+ {
+ mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3,
+ token->location, token->text);
+ valid = false;
+ }
+
+ if (valid)
+ {
+ mDirectiveHandler->handleVersion(token->location, version);
+ mShaderVersion = version;
+ PredefineMacro(mMacroSet, "__VERSION__", version);
+ }
+}
+
+void DirectiveParser::parseLine(Token *token)
+{
+ ASSERT(getDirective(token) == DIRECTIVE_LINE);
+
+ bool valid = true;
+ bool parsedFileNumber = false;
+ int line = 0, file = 0;
+
+ MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics);
+
+ // Lex the first token after "#line" so we can check it for EOD.
+ macroExpander.lex(token);
+
+ if (isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text);
+ valid = false;
+ }
+ else
+ {
+ ExpressionParser expressionParser(&macroExpander, mDiagnostics);
+ ExpressionParser::ErrorSettings errorSettings;
+
+ // See GLES3 section 12.42
+ errorSettings.integerLiteralsMustFit32BitSignedRange = true;
+
+ errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER;
+ // The first token was lexed earlier to check if it was EOD. Include
+ // the token in parsing for a second time by setting the
+ // parsePresetToken flag to true.
+ expressionParser.parse(token, &line, true, errorSettings, &valid);
+ if (!isEOD(token) && valid)
+ {
+ errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER;
+ // After parsing the line expression expressionParser has also
+ // advanced to the first token of the file expression - this is the
+ // token that makes the parser reduce the "input" rule for the line
+ // expression and stop. So we're using parsePresetToken = true here
+ // as well.
+ expressionParser.parse(token, &file, true, errorSettings, &valid);
+ parsedFileNumber = true;
+ }
+ if (!isEOD(token))
+ {
+ if (valid)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ valid = false;
+ }
+ skipUntilEOD(mTokenizer, token);
+ }
+ }
+
+ if (valid)
+ {
+ mTokenizer->setLineNumber(line);
+ if (parsedFileNumber)
+ mTokenizer->setFileNumber(file);
+ }
+}
+
+bool DirectiveParser::skipping() const
+{
+ if (mConditionalStack.empty())
+ return false;
+
+ const ConditionalBlock& block = mConditionalStack.back();
+ return block.skipBlock || block.skipGroup;
+}
+
+void DirectiveParser::parseConditionalIf(Token *token)
+{
+ ConditionalBlock block;
+ block.type = token->text;
+ block.location = token->location;
+
+ if (skipping())
+ {
+ // This conditional block is inside another conditional group
+ // which is skipped. As a consequence this whole block is skipped.
+ // Be careful not to parse the conditional expression that might
+ // emit a diagnostic.
+ skipUntilEOD(mTokenizer, token);
+ block.skipBlock = true;
+ }
+ else
+ {
+ DirectiveType directive = getDirective(token);
+
+ int expression = 0;
+ switch (directive)
+ {
+ case DIRECTIVE_IF:
+ expression = parseExpressionIf(token);
+ break;
+ case DIRECTIVE_IFDEF:
+ expression = parseExpressionIfdef(token);
+ break;
+ case DIRECTIVE_IFNDEF:
+ expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ block.skipGroup = expression == 0;
+ block.foundValidGroup = expression != 0;
+ }
+ mConditionalStack.push_back(block);
+}
+
+int DirectiveParser::parseExpressionIf(Token *token)
+{
+ ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
+
+ DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
+ MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
+ ExpressionParser expressionParser(&macroExpander, mDiagnostics);
+
+ int expression = 0;
+ ExpressionParser::ErrorSettings errorSettings;
+ errorSettings.integerLiteralsMustFit32BitSignedRange = false;
+ errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
+
+ bool valid = true;
+ expressionParser.parse(token, &expression, false, errorSettings, &valid);
+
+ // Check if there are tokens after #if expression.
+ if (!isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ }
+
+ return expression;
+}
+
+int DirectiveParser::parseExpressionIfdef(Token *token)
+{
+ ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF));
+
+ mTokenizer->lex(token);
+ if (token->type != Token::IDENTIFIER)
+ {
+ mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ return 0;
+ }
+
+ MacroSet::const_iterator iter = mMacroSet->find(token->text);
+ int expression = iter != mMacroSet->end() ? 1 : 0;
+
+ // Check if there are tokens after #ifdef expression.
+ mTokenizer->lex(token);
+ if (!isEOD(token))
+ {
+ mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
+ token->location, token->text);
+ skipUntilEOD(mTokenizer, token);
+ }
+ return expression;
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/DirectiveParser.h b/gfx/angle/src/compiler/preprocessor/DirectiveParser.h
new file mode 100755
index 000000000..f0e889c8a
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/DirectiveParser.h
@@ -0,0 +1,83 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
+#define COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
+
+#include "compiler/preprocessor/Lexer.h"
+#include "compiler/preprocessor/Macro.h"
+#include "compiler/preprocessor/SourceLocation.h"
+
+namespace pp
+{
+
+class Diagnostics;
+class DirectiveHandler;
+class Tokenizer;
+
+class DirectiveParser : public Lexer
+{
+ public:
+ DirectiveParser(Tokenizer *tokenizer,
+ MacroSet *macroSet,
+ Diagnostics *diagnostics,
+ DirectiveHandler *directiveHandler);
+
+ void lex(Token *token) override;
+
+ private:
+
+ void parseDirective(Token *token);
+ void parseDefine(Token *token);
+ void parseUndef(Token *token);
+ void parseIf(Token *token);
+ void parseIfdef(Token *token);
+ void parseIfndef(Token *token);
+ void parseElse(Token *token);
+ void parseElif(Token *token);
+ void parseEndif(Token *token);
+ void parseError(Token *token);
+ void parsePragma(Token *token);
+ void parseExtension(Token *token);
+ void parseVersion(Token *token);
+ void parseLine(Token *token);
+
+ bool skipping() const;
+ void parseConditionalIf(Token *token);
+ int parseExpressionIf(Token *token);
+ int parseExpressionIfdef(Token *token);
+
+ struct ConditionalBlock
+ {
+ std::string type;
+ SourceLocation location;
+ bool skipBlock;
+ bool skipGroup;
+ bool foundValidGroup;
+ bool foundElseGroup;
+
+ ConditionalBlock()
+ : skipBlock(false),
+ skipGroup(false),
+ foundValidGroup(false),
+ foundElseGroup(false)
+ {
+ }
+ };
+ bool mPastFirstStatement;
+ bool mSeenNonPreprocessorToken; // Tracks if a non-preprocessor token has been seen yet. Some macros, such as
+ // #extension must be declared before all shader code.
+ std::vector<ConditionalBlock> mConditionalStack;
+ Tokenizer *mTokenizer;
+ MacroSet *mMacroSet;
+ Diagnostics *mDiagnostics;
+ DirectiveHandler *mDirectiveHandler;
+ int mShaderVersion;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_
diff --git a/gfx/angle/src/compiler/preprocessor/ExpressionParser.cpp b/gfx/angle/src/compiler/preprocessor/ExpressionParser.cpp
new file mode 100755
index 000000000..ee20a6ff6
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/ExpressionParser.cpp
@@ -0,0 +1,2050 @@
+/* A Bison parser, made by GNU Bison 3.0.4. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.0.4"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 1
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+/* Substitute the variable and function names. */
+#define yyparse ppparse
+#define yylex pplex
+#define yyerror pperror
+#define yydebug ppdebug
+#define yynerrs ppnerrs
+
+
+/* Copy the first part of user declarations. */
+
+
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+#if defined(__GNUC__)
+// Triggered by the auto-generated pplval variable.
+#if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#else
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#endif
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4065 4244 4701 4702)
+#endif
+
+#include "ExpressionParser.h"
+
+#if defined(_MSC_VER)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include <cassert>
+#include <sstream>
+#include <stdint.h>
+
+#include "DiagnosticsBase.h"
+#include "Lexer.h"
+#include "Token.h"
+#include "common/mathutil.h"
+
+typedef int32_t YYSTYPE;
+typedef uint32_t UNSIGNED_TYPE;
+
+#define YYENABLE_NLS 0
+#define YYLTYPE_IS_TRIVIAL 1
+#define YYSTYPE_IS_TRIVIAL 1
+#define YYSTYPE_IS_DECLARED 1
+
+namespace {
+struct Context
+{
+ pp::Diagnostics* diagnostics;
+ pp::Lexer* lexer;
+ pp::Token* token;
+ int* result;
+ bool parsePresetToken;
+
+ pp::ExpressionParser::ErrorSettings errorSettings;
+ bool *valid;
+
+ void startIgnoreErrors() { ++ignoreErrors; }
+ void endIgnoreErrors() { --ignoreErrors; }
+
+ bool isIgnoringErrors() { return ignoreErrors > 0; }
+
+ int ignoreErrors;
+};
+} // namespace
+
+
+static int yylex(YYSTYPE* lvalp, Context* context);
+static void yyerror(Context* context, const char* reason);
+
+
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int ppdebug;
+#endif
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ TOK_CONST_INT = 258,
+ TOK_IDENTIFIER = 259,
+ TOK_OP_OR = 260,
+ TOK_OP_AND = 261,
+ TOK_OP_EQ = 262,
+ TOK_OP_NE = 263,
+ TOK_OP_LE = 264,
+ TOK_OP_GE = 265,
+ TOK_OP_LEFT = 266,
+ TOK_OP_RIGHT = 267,
+ TOK_UNARY = 268
+ };
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef int YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+
+int ppparse (Context *context);
+
+
+
+/* Copy the second part of user declarations. */
+
+
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__ \
+ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \
+ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+# define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+# define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 15
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 176
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 28
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 5
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 29
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 55
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+ by yylex, with out-of-bounds checking. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 268
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, without out-of-bounds checking. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 24, 2, 2, 2, 22, 9, 2,
+ 26, 27, 20, 18, 2, 19, 2, 21, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 12, 2, 13, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 8, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 7, 2, 25, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 10, 11, 14, 15, 16, 17, 23
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 108, 108, 115, 116, 127, 127, 148, 148, 169,
+ 172, 175, 178, 181, 184, 187, 190, 193, 196, 221,
+ 246, 249, 252, 278, 305, 308, 311, 314, 326, 329
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "TOK_CONST_INT", "TOK_IDENTIFIER",
+ "TOK_OP_OR", "TOK_OP_AND", "'|'", "'^'", "'&'", "TOK_OP_EQ", "TOK_OP_NE",
+ "'<'", "'>'", "TOK_OP_LE", "TOK_OP_GE", "TOK_OP_LEFT", "TOK_OP_RIGHT",
+ "'+'", "'-'", "'*'", "'/'", "'%'", "TOK_UNARY", "'!'", "'~'", "'('",
+ "')'", "$accept", "input", "expression", "$@1", "$@2", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 124, 94, 38,
+ 262, 263, 60, 62, 264, 265, 266, 267, 43, 45,
+ 42, 47, 37, 268, 33, 126, 40, 41
+};
+# endif
+
+#define YYPACT_NINF -12
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-12)))
+
+#define YYTABLE_NINF -1
+
+#define yytable_value_is_error(Yytable_value) \
+ 0
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int16 yypact[] =
+{
+ 31, -12, -12, 31, 31, 31, 31, 31, 51, 76,
+ -12, -12, -12, -12, 53, -12, -12, -12, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
+ 31, 31, 31, 31, -12, 31, 31, 124, 138, 26,
+ 149, 149, -11, -11, -11, -11, 154, 154, -8, -8,
+ -12, -12, -12, 93, 109
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 3, 4, 0, 0, 0, 0, 0, 0, 2,
+ 28, 27, 25, 26, 0, 1, 5, 7, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 29, 0, 0, 9, 10, 11,
+ 13, 12, 17, 16, 15, 14, 19, 18, 21, 20,
+ 24, 23, 22, 6, 8
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int8 yypgoto[] =
+{
+ -12, -12, -3, -12, -12
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int8 yydefgoto[] =
+{
+ -1, 8, 9, 35, 36
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_uint8 yytable[] =
+{
+ 10, 11, 12, 13, 14, 27, 28, 29, 30, 31,
+ 32, 33, 31, 32, 33, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 0, 53, 54, 1, 2, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 3,
+ 4, 15, 0, 0, 0, 5, 6, 7, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 0, 0, 0, 0,
+ 34, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 29, 30, 31, 32, 33
+};
+
+static const yytype_int8 yycheck[] =
+{
+ 3, 4, 5, 6, 7, 16, 17, 18, 19, 20,
+ 21, 22, 20, 21, 22, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, -1, 35, 36, 3, 4, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 18,
+ 19, 0, -1, -1, -1, 24, 25, 26, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, -1, -1, -1, -1,
+ 27, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 18, 19, 20, 21, 22
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 4, 18, 19, 24, 25, 26, 29, 30,
+ 30, 30, 30, 30, 30, 0, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 27, 31, 32, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 28, 29, 30, 30, 31, 30, 32, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 1, 0, 4, 0, 4, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 2, 2, 2, 3
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (context, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, context); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT. |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, Context *context)
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ YYUSE (context);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, Context *context)
+{
+ YYFPRINTF (yyoutput, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, context);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule, Context *context)
+{
+ unsigned long int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[yyssp[yyi + 1 - yynrhs]],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , context);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, Rule, context); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, Context *context)
+{
+ YYUSE (yyvaluep);
+ YYUSE (context);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (Context *context)
+{
+/* The lookahead symbol. */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol. */
+/* Default value used for initialization, for pacifying older GCCs
+ or non-GCC compilers. */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex (&yylval, context);
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+
+ {
+ *(context->result) = static_cast<int>((yyvsp[0]));
+ YYACCEPT;
+ }
+
+ break;
+
+ case 4:
+
+ {
+ if (!context->isIgnoringErrors())
+ {
+ // This rule should be applied right after the token is lexed, so we can
+ // refer to context->token in the error message.
+ context->diagnostics->report(context->errorSettings.unexpectedIdentifier,
+ context->token->location, context->token->text);
+ *(context->valid) = false;
+ }
+ (yyval) = (yyvsp[0]);
+ }
+
+ break;
+
+ case 5:
+
+ {
+ if ((yyvsp[-1]) != 0)
+ {
+ // Ignore errors in the short-circuited part of the expression.
+ // ESSL3.00 section 3.4:
+ // If an operand is not evaluated, the presence of undefined identifiers
+ // in the operand will not cause an error.
+ // Unevaluated division by zero should not cause an error either.
+ context->startIgnoreErrors();
+ }
+ }
+
+ break;
+
+ case 6:
+
+ {
+ if ((yyvsp[-3]) != 0)
+ {
+ context->endIgnoreErrors();
+ (yyval) = static_cast<YYSTYPE>(1);
+ }
+ else
+ {
+ (yyval) = (yyvsp[-3]) || (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 7:
+
+ {
+ if ((yyvsp[-1]) == 0)
+ {
+ // Ignore errors in the short-circuited part of the expression.
+ // ESSL3.00 section 3.4:
+ // If an operand is not evaluated, the presence of undefined identifiers
+ // in the operand will not cause an error.
+ // Unevaluated division by zero should not cause an error either.
+ context->startIgnoreErrors();
+ }
+ }
+
+ break;
+
+ case 8:
+
+ {
+ if ((yyvsp[-3]) == 0)
+ {
+ context->endIgnoreErrors();
+ (yyval) = static_cast<YYSTYPE>(0);
+ }
+ else
+ {
+ (yyval) = (yyvsp[-3]) && (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 9:
+
+ {
+ (yyval) = (yyvsp[-2]) | (yyvsp[0]);
+ }
+
+ break;
+
+ case 10:
+
+ {
+ (yyval) = (yyvsp[-2]) ^ (yyvsp[0]);
+ }
+
+ break;
+
+ case 11:
+
+ {
+ (yyval) = (yyvsp[-2]) & (yyvsp[0]);
+ }
+
+ break;
+
+ case 12:
+
+ {
+ (yyval) = (yyvsp[-2]) != (yyvsp[0]);
+ }
+
+ break;
+
+ case 13:
+
+ {
+ (yyval) = (yyvsp[-2]) == (yyvsp[0]);
+ }
+
+ break;
+
+ case 14:
+
+ {
+ (yyval) = (yyvsp[-2]) >= (yyvsp[0]);
+ }
+
+ break;
+
+ case 15:
+
+ {
+ (yyval) = (yyvsp[-2]) <= (yyvsp[0]);
+ }
+
+ break;
+
+ case 16:
+
+ {
+ (yyval) = (yyvsp[-2]) > (yyvsp[0]);
+ }
+
+ break;
+
+ case 17:
+
+ {
+ (yyval) = (yyvsp[-2]) < (yyvsp[0]);
+ }
+
+ break;
+
+ case 18:
+
+ {
+ if ((yyvsp[0]) < 0 || (yyvsp[0]) > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << (yyvsp[-2]) << " >> " << (yyvsp[0]);
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ (yyval) = static_cast<YYSTYPE>(0);
+ }
+ else if ((yyvsp[-2]) < 0)
+ {
+ // Logical shift right.
+ (yyval) = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>((yyvsp[-2])) >> (yyvsp[0]));
+ }
+ else
+ {
+ (yyval) = (yyvsp[-2]) >> (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 19:
+
+ {
+ if ((yyvsp[0]) < 0 || (yyvsp[0]) > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << (yyvsp[-2]) << " << " << (yyvsp[0]);
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ (yyval) = static_cast<YYSTYPE>(0);
+ }
+ else if ((yyvsp[-2]) < 0)
+ {
+ // Logical shift left.
+ (yyval) = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>((yyvsp[-2])) << (yyvsp[0]));
+ }
+ else
+ {
+ (yyval) = (yyvsp[-2]) << (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 20:
+
+ {
+ (yyval) = gl::WrappingDiff<YYSTYPE>((yyvsp[-2]), (yyvsp[0]));
+ }
+
+ break;
+
+ case 21:
+
+ {
+ (yyval) = gl::WrappingSum<YYSTYPE>((yyvsp[-2]), (yyvsp[0]));
+ }
+
+ break;
+
+ case 22:
+
+ {
+ if ((yyvsp[0]) == 0)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << (yyvsp[-2]) << " % " << (yyvsp[0]);
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ (yyval) = static_cast<YYSTYPE>(0);
+ }
+ else if (((yyvsp[-2]) == std::numeric_limits<YYSTYPE>::min()) && ((yyvsp[0]) == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this has undefined results.
+ (yyval) = 0;
+ }
+ else
+ {
+ (yyval) = (yyvsp[-2]) % (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 23:
+
+ {
+ if ((yyvsp[0]) == 0)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << (yyvsp[-2]) << " / " << (yyvsp[0]);
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ (yyval) = static_cast<YYSTYPE>(0);
+ }
+ else if (((yyvsp[-2]) == std::numeric_limits<YYSTYPE>::min()) && ((yyvsp[0]) == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this leads to integer overflow in C++, which
+ // has undefined results.
+ (yyval) = std::numeric_limits<YYSTYPE>::max();
+ }
+ else
+ {
+ (yyval) = (yyvsp[-2]) / (yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 24:
+
+ {
+ (yyval) = gl::WrappingMul((yyvsp[-2]), (yyvsp[0]));
+ }
+
+ break;
+
+ case 25:
+
+ {
+ (yyval) = ! (yyvsp[0]);
+ }
+
+ break;
+
+ case 26:
+
+ {
+ (yyval) = ~ (yyvsp[0]);
+ }
+
+ break;
+
+ case 27:
+
+ {
+ // Check for negation of minimum representable integer to prevent undefined signed int
+ // overflow.
+ if ((yyvsp[0]) == std::numeric_limits<YYSTYPE>::min())
+ {
+ (yyval) = std::numeric_limits<YYSTYPE>::min();
+ }
+ else
+ {
+ (yyval) = -(yyvsp[0]);
+ }
+ }
+
+ break;
+
+ case 28:
+
+ {
+ (yyval) = + (yyvsp[0]);
+ }
+
+ break;
+
+ case 29:
+
+ {
+ (yyval) = (yyvsp[-1]);
+ }
+
+ break;
+
+
+
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (context, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (context, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, context);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, context);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (context, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, context);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, context);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+
+
+
+int yylex(YYSTYPE *lvalp, Context *context)
+{
+ pp::Token *token = context->token;
+ if (!context->parsePresetToken)
+ {
+ context->lexer->lex(token);
+ }
+ context->parsePresetToken = false;
+
+ int type = 0;
+
+ switch (token->type)
+ {
+ case pp::Token::CONST_INT: {
+ unsigned int val = 0;
+ int testVal = 0;
+ if (!token->uValue(&val) || (!token->iValue(&testVal) &&
+ context->errorSettings.integerLiteralsMustFit32BitSignedRange))
+ {
+ context->diagnostics->report(pp::Diagnostics::PP_INTEGER_OVERFLOW,
+ token->location, token->text);
+ *(context->valid) = false;
+ }
+ *lvalp = static_cast<YYSTYPE>(val);
+ type = TOK_CONST_INT;
+ break;
+ }
+ case pp::Token::IDENTIFIER:
+ *lvalp = static_cast<YYSTYPE>(-1);
+ type = TOK_IDENTIFIER;
+ break;
+ case pp::Token::OP_OR:
+ type = TOK_OP_OR;
+ break;
+ case pp::Token::OP_AND:
+ type = TOK_OP_AND;
+ break;
+ case pp::Token::OP_NE:
+ type = TOK_OP_NE;
+ break;
+ case pp::Token::OP_EQ:
+ type = TOK_OP_EQ;
+ break;
+ case pp::Token::OP_GE:
+ type = TOK_OP_GE;
+ break;
+ case pp::Token::OP_LE:
+ type = TOK_OP_LE;
+ break;
+ case pp::Token::OP_RIGHT:
+ type = TOK_OP_RIGHT;
+ break;
+ case pp::Token::OP_LEFT:
+ type = TOK_OP_LEFT;
+ break;
+ case '|':
+ case '^':
+ case '&':
+ case '>':
+ case '<':
+ case '-':
+ case '+':
+ case '%':
+ case '/':
+ case '*':
+ case '!':
+ case '~':
+ case '(':
+ case ')':
+ type = token->type;
+ break;
+
+ default:
+ break;
+ }
+
+ return type;
+}
+
+void yyerror(Context *context, const char *reason)
+{
+ context->diagnostics->report(pp::Diagnostics::PP_INVALID_EXPRESSION,
+ context->token->location,
+ reason);
+}
+
+namespace pp {
+
+ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics)
+ : mLexer(lexer),
+ mDiagnostics(diagnostics)
+{
+}
+
+bool ExpressionParser::parse(Token *token,
+ int *result,
+ bool parsePresetToken,
+ const ErrorSettings &errorSettings,
+ bool *valid)
+{
+ Context context;
+ context.diagnostics = mDiagnostics;
+ context.lexer = mLexer;
+ context.token = token;
+ context.result = result;
+ context.ignoreErrors = 0;
+ context.parsePresetToken = parsePresetToken;
+ context.errorSettings = errorSettings;
+ context.valid = valid;
+ int ret = yyparse(&context);
+ switch (ret)
+ {
+ case 0:
+ case 1:
+ break;
+
+ case 2:
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, "");
+ break;
+
+ default:
+ assert(false);
+ mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, "");
+ break;
+ }
+
+ return ret == 0;
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/ExpressionParser.h b/gfx/angle/src/compiler/preprocessor/ExpressionParser.h
new file mode 100755
index 000000000..0f2901b87
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/ExpressionParser.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
+#define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
+
+#include "common/angleutils.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+
+namespace pp
+{
+
+class Lexer;
+struct Token;
+
+class ExpressionParser : angle::NonCopyable
+{
+ public:
+ struct ErrorSettings
+ {
+ Diagnostics::ID unexpectedIdentifier;
+ bool integerLiteralsMustFit32BitSignedRange;
+ };
+
+ ExpressionParser(Lexer *lexer, Diagnostics *diagnostics);
+
+ bool parse(Token *token,
+ int *result,
+ bool parsePresetToken,
+ const ErrorSettings &errorSettings,
+ bool *valid);
+
+ private:
+ Lexer *mLexer;
+ Diagnostics *mDiagnostics;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_
diff --git a/gfx/angle/src/compiler/preprocessor/ExpressionParser.y b/gfx/angle/src/compiler/preprocessor/ExpressionParser.y
new file mode 100755
index 000000000..4dbc9e828
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/ExpressionParser.y
@@ -0,0 +1,465 @@
+/*
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Yacc grammar for GLSL ES preprocessor expression.
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
+WHICH GENERATES THE GLSL ES preprocessor expression parser.
+*/
+
+%{
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+#if defined(__GNUC__)
+// Triggered by the auto-generated pplval variable.
+#if !defined(__clang__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#else
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#endif
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4065 4244 4701 4702)
+#endif
+
+#include "ExpressionParser.h"
+
+#if defined(_MSC_VER)
+#include <malloc.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include <cassert>
+#include <sstream>
+#include <stdint.h>
+
+#include "DiagnosticsBase.h"
+#include "Lexer.h"
+#include "Token.h"
+#include "common/mathutil.h"
+
+typedef int32_t YYSTYPE;
+typedef uint32_t UNSIGNED_TYPE;
+
+#define YYENABLE_NLS 0
+#define YYLTYPE_IS_TRIVIAL 1
+#define YYSTYPE_IS_TRIVIAL 1
+#define YYSTYPE_IS_DECLARED 1
+
+namespace {
+struct Context
+{
+ pp::Diagnostics* diagnostics;
+ pp::Lexer* lexer;
+ pp::Token* token;
+ int* result;
+ bool parsePresetToken;
+
+ pp::ExpressionParser::ErrorSettings errorSettings;
+ bool *valid;
+
+ void startIgnoreErrors() { ++ignoreErrors; }
+ void endIgnoreErrors() { --ignoreErrors; }
+
+ bool isIgnoringErrors() { return ignoreErrors > 0; }
+
+ int ignoreErrors;
+};
+} // namespace
+%}
+
+%pure-parser
+%name-prefix "pp"
+%parse-param {Context *context}
+%lex-param {Context *context}
+
+%{
+static int yylex(YYSTYPE* lvalp, Context* context);
+static void yyerror(Context* context, const char* reason);
+%}
+
+%token TOK_CONST_INT
+%token TOK_IDENTIFIER
+%left TOK_OP_OR
+%left TOK_OP_AND
+%left '|'
+%left '^'
+%left '&'
+%left TOK_OP_EQ TOK_OP_NE
+%left '<' '>' TOK_OP_LE TOK_OP_GE
+%left TOK_OP_LEFT TOK_OP_RIGHT
+%left '+' '-'
+%left '*' '/' '%'
+%right TOK_UNARY
+
+%%
+
+input
+ : expression {
+ *(context->result) = static_cast<int>($1);
+ YYACCEPT;
+ }
+;
+
+expression
+ : TOK_CONST_INT
+ | TOK_IDENTIFIER {
+ if (!context->isIgnoringErrors())
+ {
+ // This rule should be applied right after the token is lexed, so we can
+ // refer to context->token in the error message.
+ context->diagnostics->report(context->errorSettings.unexpectedIdentifier,
+ context->token->location, context->token->text);
+ *(context->valid) = false;
+ }
+ $$ = $1;
+ }
+ | expression TOK_OP_OR {
+ if ($1 != 0)
+ {
+ // Ignore errors in the short-circuited part of the expression.
+ // ESSL3.00 section 3.4:
+ // If an operand is not evaluated, the presence of undefined identifiers
+ // in the operand will not cause an error.
+ // Unevaluated division by zero should not cause an error either.
+ context->startIgnoreErrors();
+ }
+ } expression {
+ if ($1 != 0)
+ {
+ context->endIgnoreErrors();
+ $$ = static_cast<YYSTYPE>(1);
+ }
+ else
+ {
+ $$ = $1 || $4;
+ }
+ }
+ | expression TOK_OP_AND {
+ if ($1 == 0)
+ {
+ // Ignore errors in the short-circuited part of the expression.
+ // ESSL3.00 section 3.4:
+ // If an operand is not evaluated, the presence of undefined identifiers
+ // in the operand will not cause an error.
+ // Unevaluated division by zero should not cause an error either.
+ context->startIgnoreErrors();
+ }
+ } expression {
+ if ($1 == 0)
+ {
+ context->endIgnoreErrors();
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else
+ {
+ $$ = $1 && $4;
+ }
+ }
+ | expression '|' expression {
+ $$ = $1 | $3;
+ }
+ | expression '^' expression {
+ $$ = $1 ^ $3;
+ }
+ | expression '&' expression {
+ $$ = $1 & $3;
+ }
+ | expression TOK_OP_NE expression {
+ $$ = $1 != $3;
+ }
+ | expression TOK_OP_EQ expression {
+ $$ = $1 == $3;
+ }
+ | expression TOK_OP_GE expression {
+ $$ = $1 >= $3;
+ }
+ | expression TOK_OP_LE expression {
+ $$ = $1 <= $3;
+ }
+ | expression '>' expression {
+ $$ = $1 > $3;
+ }
+ | expression '<' expression {
+ $$ = $1 < $3;
+ }
+ | expression TOK_OP_RIGHT expression {
+ if ($3 < 0 || $3 > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " >> " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else if ($1 < 0)
+ {
+ // Logical shift right.
+ $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) >> $3);
+ }
+ else
+ {
+ $$ = $1 >> $3;
+ }
+ }
+ | expression TOK_OP_LEFT expression {
+ if ($3 < 0 || $3 > 31)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " << " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_UNDEFINED_SHIFT,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else if ($1 < 0)
+ {
+ // Logical shift left.
+ $$ = static_cast<YYSTYPE>(static_cast<UNSIGNED_TYPE>($1) << $3);
+ }
+ else
+ {
+ $$ = $1 << $3;
+ }
+ }
+ | expression '-' expression {
+ $$ = gl::WrappingDiff<YYSTYPE>($1, $3);
+ }
+ | expression '+' expression {
+ $$ = gl::WrappingSum<YYSTYPE>($1, $3);
+ }
+ | expression '%' expression {
+ if ($3 == 0)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " % " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this has undefined results.
+ $$ = 0;
+ }
+ else
+ {
+ $$ = $1 % $3;
+ }
+ }
+ | expression '/' expression {
+ if ($3 == 0)
+ {
+ if (!context->isIgnoringErrors())
+ {
+ std::ostringstream stream;
+ stream << $1 << " / " << $3;
+ std::string text = stream.str();
+ context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+ context->token->location,
+ text.c_str());
+ *(context->valid) = false;
+ }
+ $$ = static_cast<YYSTYPE>(0);
+ }
+ else if (($1 == std::numeric_limits<YYSTYPE>::min()) && ($3 == -1))
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this leads to integer overflow in C++, which
+ // has undefined results.
+ $$ = std::numeric_limits<YYSTYPE>::max();
+ }
+ else
+ {
+ $$ = $1 / $3;
+ }
+ }
+ | expression '*' expression {
+ $$ = gl::WrappingMul($1, $3);
+ }
+ | '!' expression %prec TOK_UNARY {
+ $$ = ! $2;
+ }
+ | '~' expression %prec TOK_UNARY {
+ $$ = ~ $2;
+ }
+ | '-' expression %prec TOK_UNARY {
+ // Check for negation of minimum representable integer to prevent undefined signed int
+ // overflow.
+ if ($2 == std::numeric_limits<YYSTYPE>::min())
+ {
+ $$ = std::numeric_limits<YYSTYPE>::min();
+ }
+ else
+ {
+ $$ = -$2;
+ }
+ }
+ | '+' expression %prec TOK_UNARY {
+ $$ = + $2;
+ }
+ | '(' expression ')' {
+ $$ = $2;
+ }
+;
+
+%%
+
+int yylex(YYSTYPE *lvalp, Context *context)
+{
+ pp::Token *token = context->token;
+ if (!context->parsePresetToken)
+ {
+ context->lexer->lex(token);
+ }
+ context->parsePresetToken = false;
+
+ int type = 0;
+
+ switch (token->type)
+ {
+ case pp::Token::CONST_INT: {
+ unsigned int val = 0;
+ int testVal = 0;
+ if (!token->uValue(&val) || (!token->iValue(&testVal) &&
+ context->errorSettings.integerLiteralsMustFit32BitSignedRange))
+ {
+ context->diagnostics->report(pp::Diagnostics::PP_INTEGER_OVERFLOW,
+ token->location, token->text);
+ *(context->valid) = false;
+ }
+ *lvalp = static_cast<YYSTYPE>(val);
+ type = TOK_CONST_INT;
+ break;
+ }
+ case pp::Token::IDENTIFIER:
+ *lvalp = static_cast<YYSTYPE>(-1);
+ type = TOK_IDENTIFIER;
+ break;
+ case pp::Token::OP_OR:
+ type = TOK_OP_OR;
+ break;
+ case pp::Token::OP_AND:
+ type = TOK_OP_AND;
+ break;
+ case pp::Token::OP_NE:
+ type = TOK_OP_NE;
+ break;
+ case pp::Token::OP_EQ:
+ type = TOK_OP_EQ;
+ break;
+ case pp::Token::OP_GE:
+ type = TOK_OP_GE;
+ break;
+ case pp::Token::OP_LE:
+ type = TOK_OP_LE;
+ break;
+ case pp::Token::OP_RIGHT:
+ type = TOK_OP_RIGHT;
+ break;
+ case pp::Token::OP_LEFT:
+ type = TOK_OP_LEFT;
+ break;
+ case '|':
+ case '^':
+ case '&':
+ case '>':
+ case '<':
+ case '-':
+ case '+':
+ case '%':
+ case '/':
+ case '*':
+ case '!':
+ case '~':
+ case '(':
+ case ')':
+ type = token->type;
+ break;
+
+ default:
+ break;
+ }
+
+ return type;
+}
+
+void yyerror(Context *context, const char *reason)
+{
+ context->diagnostics->report(pp::Diagnostics::PP_INVALID_EXPRESSION,
+ context->token->location,
+ reason);
+}
+
+namespace pp {
+
+ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics)
+ : mLexer(lexer),
+ mDiagnostics(diagnostics)
+{
+}
+
+bool ExpressionParser::parse(Token *token,
+ int *result,
+ bool parsePresetToken,
+ const ErrorSettings &errorSettings,
+ bool *valid)
+{
+ Context context;
+ context.diagnostics = mDiagnostics;
+ context.lexer = mLexer;
+ context.token = token;
+ context.result = result;
+ context.ignoreErrors = 0;
+ context.parsePresetToken = parsePresetToken;
+ context.errorSettings = errorSettings;
+ context.valid = valid;
+ int ret = yyparse(&context);
+ switch (ret)
+ {
+ case 0:
+ case 1:
+ break;
+
+ case 2:
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, "");
+ break;
+
+ default:
+ assert(false);
+ mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, "");
+ break;
+ }
+
+ return ret == 0;
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/Input.cpp b/gfx/angle/src/compiler/preprocessor/Input.cpp
new file mode 100755
index 000000000..8bce56ff2
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Input.cpp
@@ -0,0 +1,117 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/Input.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "common/debug.h"
+
+namespace pp
+{
+
+Input::Input() : mCount(0), mString(0)
+{
+}
+
+Input::Input(size_t count, const char *const string[], const int length[]) :
+ mCount(count),
+ mString(string)
+{
+ mLength.reserve(mCount);
+ for (size_t i = 0; i < mCount; ++i)
+ {
+ int len = length ? length[i] : -1;
+ mLength.push_back(len < 0 ? std::strlen(mString[i]) : len);
+ }
+}
+
+const char *Input::skipChar()
+{
+ // This function should only be called when there is a character to skip.
+ ASSERT(mReadLoc.cIndex < mLength[mReadLoc.sIndex]);
+ ++mReadLoc.cIndex;
+ if (mReadLoc.cIndex == mLength[mReadLoc.sIndex])
+ {
+ ++mReadLoc.sIndex;
+ mReadLoc.cIndex = 0;
+ }
+ if (mReadLoc.sIndex >= mCount)
+ {
+ return nullptr;
+ }
+ return mString[mReadLoc.sIndex] + mReadLoc.cIndex;
+}
+
+size_t Input::read(char *buf, size_t maxSize, int *lineNo)
+{
+ size_t nRead = 0;
+ // The previous call to read might have stopped copying the string when encountering a line
+ // continuation. Check for this possibility first.
+ if (mReadLoc.sIndex < mCount && maxSize > 0)
+ {
+ const char *c = mString[mReadLoc.sIndex] + mReadLoc.cIndex;
+ if ((*c) == '\\')
+ {
+ c = skipChar();
+ if (c != nullptr && (*c) == '\n')
+ {
+ // Line continuation of backslash + newline.
+ skipChar();
+ ++(*lineNo);
+ }
+ else if (c != nullptr && (*c) == '\r')
+ {
+ // Line continuation. Could be backslash + '\r\n' or just backslash + '\r'.
+ c = skipChar();
+ if (c != nullptr && (*c) == '\n')
+ {
+ skipChar();
+ }
+ ++(*lineNo);
+ }
+ else
+ {
+ // Not line continuation, so write the skipped backslash to buf.
+ *buf = '\\';
+ ++nRead;
+ }
+ }
+ }
+
+ size_t maxRead = maxSize;
+ while ((nRead < maxRead) && (mReadLoc.sIndex < mCount))
+ {
+ size_t size = mLength[mReadLoc.sIndex] - mReadLoc.cIndex;
+ size = std::min(size, maxSize);
+ for (size_t i = 0; i < size; ++i)
+ {
+ // Stop if a possible line continuation is encountered.
+ // It will be processed on the next call on input, which skips it
+ // and increments line number if necessary.
+ if (*(mString[mReadLoc.sIndex] + mReadLoc.cIndex + i) == '\\')
+ {
+ size = i;
+ maxRead = nRead + size; // Stop reading right before the backslash.
+ }
+ }
+ std::memcpy(buf + nRead, mString[mReadLoc.sIndex] + mReadLoc.cIndex, size);
+ nRead += size;
+ mReadLoc.cIndex += size;
+
+ // Advance string if we reached the end of current string.
+ if (mReadLoc.cIndex == mLength[mReadLoc.sIndex])
+ {
+ ++mReadLoc.sIndex;
+ mReadLoc.cIndex = 0;
+ }
+ }
+ return nRead;
+}
+
+} // namespace pp
+
diff --git a/gfx/angle/src/compiler/preprocessor/Input.h b/gfx/angle/src/compiler/preprocessor/Input.h
new file mode 100755
index 000000000..ecbb04962
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Input.h
@@ -0,0 +1,66 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_INPUT_H_
+#define COMPILER_PREPROCESSOR_INPUT_H_
+
+#include <cstddef>
+#include <vector>
+
+namespace pp
+{
+
+// Holds and reads input for Lexer.
+class Input
+{
+ public:
+ Input();
+ Input(size_t count, const char *const string[], const int length[]);
+
+ size_t count() const
+ {
+ return mCount;
+ }
+ const char *string(size_t index) const
+ {
+ return mString[index];
+ }
+ size_t length(size_t index) const
+ {
+ return mLength[index];
+ }
+
+ size_t read(char *buf, size_t maxSize, int *lineNo);
+
+ struct Location
+ {
+ size_t sIndex; // String index;
+ size_t cIndex; // Char index.
+
+ Location()
+ : sIndex(0),
+ cIndex(0)
+ {
+ }
+ };
+ const Location &readLoc() const { return mReadLoc; }
+
+ private:
+ // Skip a character and return the next character after the one that was skipped.
+ // Return nullptr if data runs out.
+ const char *skipChar();
+
+ // Input.
+ size_t mCount;
+ const char * const *mString;
+ std::vector<size_t> mLength;
+
+ Location mReadLoc;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_INPUT_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Lexer.cpp b/gfx/angle/src/compiler/preprocessor/Lexer.cpp
new file mode 100755
index 000000000..89cb3cf44
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Lexer.cpp
@@ -0,0 +1,16 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/Lexer.h"
+
+namespace pp
+{
+
+Lexer::~Lexer()
+{
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/Lexer.h b/gfx/angle/src/compiler/preprocessor/Lexer.h
new file mode 100755
index 000000000..775bc0a20
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Lexer.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_LEXER_H_
+#define COMPILER_PREPROCESSOR_LEXER_H_
+
+#include "common/angleutils.h"
+
+namespace pp
+{
+
+struct Token;
+
+class Lexer : angle::NonCopyable
+{
+ public:
+ virtual ~Lexer();
+
+ virtual void lex(Token *token) = 0;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_LEXER_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Macro.cpp b/gfx/angle/src/compiler/preprocessor/Macro.cpp
new file mode 100755
index 000000000..f5c94399d
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Macro.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/Macro.h"
+
+#include "common/angleutils.h"
+#include "compiler/preprocessor/Token.h"
+
+namespace pp
+{
+
+bool Macro::equals(const Macro &other) const
+{
+ return (type == other.type) &&
+ (name == other.name) &&
+ (parameters == other.parameters) &&
+ (replacements == other.replacements);
+}
+
+void PredefineMacro(MacroSet *macroSet, const char *name, int value)
+{
+ Token token;
+ token.type = Token::CONST_INT;
+ token.text = ToString(value);
+
+ Macro macro;
+ macro.predefined = true;
+ macro.type = Macro::kTypeObj;
+ macro.name = name;
+ macro.replacements.push_back(token);
+
+ (*macroSet)[name] = macro;
+}
+
+} // namespace pp
+
diff --git a/gfx/angle/src/compiler/preprocessor/Macro.h b/gfx/angle/src/compiler/preprocessor/Macro.h
new file mode 100755
index 000000000..557df163c
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Macro.h
@@ -0,0 +1,48 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_MACRO_H_
+#define COMPILER_PREPROCESSOR_MACRO_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace pp
+{
+
+struct Token;
+
+struct Macro
+{
+ enum Type
+ {
+ kTypeObj,
+ kTypeFunc
+ };
+ typedef std::vector<std::string> Parameters;
+ typedef std::vector<Token> Replacements;
+
+ Macro() : predefined(false), disabled(false), expansionCount(0), type(kTypeObj) {}
+ bool equals(const Macro &other) const;
+
+ bool predefined;
+ mutable bool disabled;
+ mutable int expansionCount;
+
+ Type type;
+ std::string name;
+ Parameters parameters;
+ Replacements replacements;
+};
+
+typedef std::map<std::string, Macro> MacroSet;
+
+void PredefineMacro(MacroSet *macroSet, const char *name, int value);
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_MACRO_H_
diff --git a/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp b/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
new file mode 100755
index 000000000..2f854f5a6
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/MacroExpander.cpp
@@ -0,0 +1,465 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/MacroExpander.h"
+
+#include <algorithm>
+
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/Token.h"
+
+namespace pp
+{
+
+namespace
+{
+
+const size_t kMaxContextTokens = 10000;
+
+class TokenLexer : public Lexer
+{
+ public:
+ typedef std::vector<Token> TokenVector;
+
+ TokenLexer(TokenVector *tokens)
+ {
+ tokens->swap(mTokens);
+ mIter = mTokens.begin();
+ }
+
+ void lex(Token *token) override
+ {
+ if (mIter == mTokens.end())
+ {
+ token->reset();
+ token->type = Token::LAST;
+ }
+ else
+ {
+ *token = *mIter++;
+ }
+ }
+
+ private:
+ TokenVector mTokens;
+ TokenVector::const_iterator mIter;
+};
+
+} // anonymous namespace
+
+class MacroExpander::ScopedMacroReenabler final : angle::NonCopyable
+{
+ public:
+ ScopedMacroReenabler(MacroExpander *expander);
+ ~ScopedMacroReenabler();
+
+ private:
+ MacroExpander *mExpander;
+};
+
+MacroExpander::ScopedMacroReenabler::ScopedMacroReenabler(MacroExpander *expander)
+ : mExpander(expander)
+{
+ mExpander->mDeferReenablingMacros = true;
+}
+
+MacroExpander::ScopedMacroReenabler::~ScopedMacroReenabler()
+{
+ mExpander->mDeferReenablingMacros = false;
+ for (auto *macro : mExpander->mMacrosToReenable)
+ {
+ macro->disabled = false;
+ }
+ mExpander->mMacrosToReenable.clear();
+}
+
+MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics)
+ : mLexer(lexer),
+ mMacroSet(macroSet),
+ mDiagnostics(diagnostics),
+ mTotalTokensInContexts(0),
+ mDeferReenablingMacros(false)
+{
+}
+
+MacroExpander::~MacroExpander()
+{
+ for (MacroContext *context : mContextStack)
+ {
+ delete context;
+ }
+}
+
+void MacroExpander::lex(Token *token)
+{
+ while (true)
+ {
+ getToken(token);
+
+ if (token->type != Token::IDENTIFIER)
+ break;
+
+ if (token->expansionDisabled())
+ break;
+
+ MacroSet::const_iterator iter = mMacroSet->find(token->text);
+ if (iter == mMacroSet->end())
+ break;
+
+ const Macro& macro = iter->second;
+ if (macro.disabled)
+ {
+ // If a particular token is not expanded, it is never expanded.
+ token->setExpansionDisabled(true);
+ break;
+ }
+
+ // Bump the expansion count before peeking if the next token is a '('
+ // otherwise there could be a #undef of the macro before the next token.
+ macro.expansionCount++;
+ if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
+ {
+ // If the token immediately after the macro name is not a '(',
+ // this macro should not be expanded.
+ macro.expansionCount--;
+ break;
+ }
+
+ pushMacro(macro, *token);
+ }
+}
+
+void MacroExpander::getToken(Token *token)
+{
+ if (mReserveToken.get())
+ {
+ *token = *mReserveToken;
+ mReserveToken.reset();
+ return;
+ }
+
+ // First pop all empty macro contexts.
+ while (!mContextStack.empty() && mContextStack.back()->empty())
+ {
+ popMacro();
+ }
+
+ if (!mContextStack.empty())
+ {
+ *token = mContextStack.back()->get();
+ }
+ else
+ {
+ ASSERT(mTotalTokensInContexts == 0);
+ mLexer->lex(token);
+ }
+}
+
+void MacroExpander::ungetToken(const Token &token)
+{
+ if (!mContextStack.empty())
+ {
+ MacroContext *context = mContextStack.back();
+ context->unget();
+ ASSERT(context->replacements[context->index] == token);
+ }
+ else
+ {
+ ASSERT(!mReserveToken.get());
+ mReserveToken.reset(new Token(token));
+ }
+}
+
+bool MacroExpander::isNextTokenLeftParen()
+{
+ Token token;
+ getToken(&token);
+
+ bool lparen = token.type == '(';
+ ungetToken(token);
+
+ return lparen;
+}
+
+bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
+{
+ ASSERT(!macro.disabled);
+ ASSERT(!identifier.expansionDisabled());
+ ASSERT(identifier.type == Token::IDENTIFIER);
+ ASSERT(identifier.text == macro.name);
+
+ std::vector<Token> replacements;
+ if (!expandMacro(macro, identifier, &replacements))
+ return false;
+
+ // Macro is disabled for expansion until it is popped off the stack.
+ macro.disabled = true;
+
+ MacroContext *context = new MacroContext;
+ context->macro = &macro;
+ context->replacements.swap(replacements);
+ mContextStack.push_back(context);
+ mTotalTokensInContexts += context->replacements.size();
+ return true;
+}
+
+void MacroExpander::popMacro()
+{
+ ASSERT(!mContextStack.empty());
+
+ MacroContext *context = mContextStack.back();
+ mContextStack.pop_back();
+
+ ASSERT(context->empty());
+ ASSERT(context->macro->disabled);
+ ASSERT(context->macro->expansionCount > 0);
+ if (mDeferReenablingMacros)
+ {
+ mMacrosToReenable.push_back(context->macro);
+ }
+ else
+ {
+ context->macro->disabled = false;
+ }
+ context->macro->expansionCount--;
+ mTotalTokensInContexts -= context->replacements.size();
+ delete context;
+}
+
+bool MacroExpander::expandMacro(const Macro &macro,
+ const Token &identifier,
+ std::vector<Token> *replacements)
+{
+ replacements->clear();
+
+ // In the case of an object-like macro, the replacement list gets its location
+ // from the identifier, but in the case of a function-like macro, the replacement
+ // list gets its location from the closing parenthesis of the macro invocation.
+ // This is tested by dEQP-GLES3.functional.shaders.preprocessor.predefined_macros.*
+ SourceLocation replacementLocation = identifier.location;
+ if (macro.type == Macro::kTypeObj)
+ {
+ replacements->assign(macro.replacements.begin(),
+ macro.replacements.end());
+
+ if (macro.predefined)
+ {
+ const char kLine[] = "__LINE__";
+ const char kFile[] = "__FILE__";
+
+ ASSERT(replacements->size() == 1);
+ Token& repl = replacements->front();
+ if (macro.name == kLine)
+ {
+ repl.text = ToString(identifier.location.line);
+ }
+ else if (macro.name == kFile)
+ {
+ repl.text = ToString(identifier.location.file);
+ }
+ }
+ }
+ else
+ {
+ ASSERT(macro.type == Macro::kTypeFunc);
+ std::vector<MacroArg> args;
+ args.reserve(macro.parameters.size());
+ if (!collectMacroArgs(macro, identifier, &args, &replacementLocation))
+ return false;
+
+ replaceMacroParams(macro, args, replacements);
+ }
+
+ for (std::size_t i = 0; i < replacements->size(); ++i)
+ {
+ Token& repl = replacements->at(i);
+ if (i == 0)
+ {
+ // The first token in the replacement list inherits the padding
+ // properties of the identifier token.
+ repl.setAtStartOfLine(identifier.atStartOfLine());
+ repl.setHasLeadingSpace(identifier.hasLeadingSpace());
+ }
+ repl.location = replacementLocation;
+ }
+ return true;
+}
+
+bool MacroExpander::collectMacroArgs(const Macro &macro,
+ const Token &identifier,
+ std::vector<MacroArg> *args,
+ SourceLocation *closingParenthesisLocation)
+{
+ Token token;
+ getToken(&token);
+ ASSERT(token.type == '(');
+
+ args->push_back(MacroArg());
+
+ // Defer reenabling macros until args collection is finished to avoid the possibility of
+ // infinite recursion. Otherwise infinite recursion might happen when expanding the args after
+ // macros have been popped from the context stack when parsing the args.
+ ScopedMacroReenabler deferReenablingMacros(this);
+
+ int openParens = 1;
+ while (openParens != 0)
+ {
+ getToken(&token);
+
+ if (token.type == Token::LAST)
+ {
+ mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
+ identifier.location, identifier.text);
+ // Do not lose EOF token.
+ ungetToken(token);
+ return false;
+ }
+
+ bool isArg = false; // True if token is part of the current argument.
+ switch (token.type)
+ {
+ case '(':
+ ++openParens;
+ isArg = true;
+ break;
+ case ')':
+ --openParens;
+ isArg = openParens != 0;
+ *closingParenthesisLocation = token.location;
+ break;
+ case ',':
+ // The individual arguments are separated by comma tokens, but
+ // the comma tokens between matching inner parentheses do not
+ // seperate arguments.
+ if (openParens == 1)
+ args->push_back(MacroArg());
+ isArg = openParens != 1;
+ break;
+ default:
+ isArg = true;
+ break;
+ }
+ if (isArg)
+ {
+ MacroArg &arg = args->back();
+ // Initial whitespace is not part of the argument.
+ if (arg.empty())
+ token.setHasLeadingSpace(false);
+ arg.push_back(token);
+ }
+ }
+
+ const Macro::Parameters &params = macro.parameters;
+ // If there is only one empty argument, it is equivalent to no argument.
+ if (params.empty() && (args->size() == 1) && args->front().empty())
+ {
+ args->clear();
+ }
+ // Validate the number of arguments.
+ if (args->size() != params.size())
+ {
+ Diagnostics::ID id = args->size() < macro.parameters.size() ?
+ Diagnostics::PP_MACRO_TOO_FEW_ARGS :
+ Diagnostics::PP_MACRO_TOO_MANY_ARGS;
+ mDiagnostics->report(id, identifier.location, identifier.text);
+ return false;
+ }
+
+ // Pre-expand each argument before substitution.
+ // This step expands each argument individually before they are
+ // inserted into the macro body.
+ size_t numTokens = 0;
+ for (auto &arg : *args)
+ {
+ TokenLexer lexer(&arg);
+ MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
+
+ arg.clear();
+ expander.lex(&token);
+ while (token.type != Token::LAST)
+ {
+ arg.push_back(token);
+ expander.lex(&token);
+ numTokens++;
+ if (numTokens + mTotalTokensInContexts > kMaxContextTokens)
+ {
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void MacroExpander::replaceMacroParams(const Macro &macro,
+ const std::vector<MacroArg> &args,
+ std::vector<Token> *replacements)
+{
+ for (std::size_t i = 0; i < macro.replacements.size(); ++i)
+ {
+ if (!replacements->empty() &&
+ replacements->size() + mTotalTokensInContexts > kMaxContextTokens)
+ {
+ const Token &token = replacements->back();
+ mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text);
+ return;
+ }
+
+ const Token &repl = macro.replacements[i];
+ if (repl.type != Token::IDENTIFIER)
+ {
+ replacements->push_back(repl);
+ continue;
+ }
+
+ // TODO(alokp): Optimize this.
+ // There is no need to search for macro params every time.
+ // The param index can be cached with the replacement token.
+ Macro::Parameters::const_iterator iter = std::find(
+ macro.parameters.begin(), macro.parameters.end(), repl.text);
+ if (iter == macro.parameters.end())
+ {
+ replacements->push_back(repl);
+ continue;
+ }
+
+ std::size_t iArg = std::distance(macro.parameters.begin(), iter);
+ const MacroArg &arg = args[iArg];
+ if (arg.empty())
+ {
+ continue;
+ }
+ std::size_t iRepl = replacements->size();
+ replacements->insert(replacements->end(), arg.begin(), arg.end());
+ // The replacement token inherits padding properties from
+ // macro replacement token.
+ replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
+ }
+}
+
+MacroExpander::MacroContext::MacroContext() : macro(0), index(0)
+{
+}
+
+bool MacroExpander::MacroContext::empty() const
+{
+ return index == replacements.size();
+}
+
+const Token &MacroExpander::MacroContext::get()
+{
+ return replacements[index++];
+}
+
+void MacroExpander::MacroContext::unget()
+{
+ ASSERT(index > 0);
+ --index;
+}
+
+} // namespace pp
+
diff --git a/gfx/angle/src/compiler/preprocessor/MacroExpander.h b/gfx/angle/src/compiler/preprocessor/MacroExpander.h
new file mode 100755
index 000000000..3a8450f5e
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/MacroExpander.h
@@ -0,0 +1,79 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_MACROEXPANDER_H_
+#define COMPILER_PREPROCESSOR_MACROEXPANDER_H_
+
+#include <memory>
+#include <vector>
+
+#include "compiler/preprocessor/Lexer.h"
+#include "compiler/preprocessor/Macro.h"
+
+namespace pp
+{
+
+class Diagnostics;
+struct SourceLocation;
+
+class MacroExpander : public Lexer
+{
+ public:
+ MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics);
+ ~MacroExpander() override;
+
+ void lex(Token *token) override;
+
+ private:
+ void getToken(Token *token);
+ void ungetToken(const Token &token);
+ bool isNextTokenLeftParen();
+
+ bool pushMacro(const Macro &macro, const Token &identifier);
+ void popMacro();
+
+ bool expandMacro(const Macro &macro,
+ const Token &identifier,
+ std::vector<Token> *replacements);
+
+ typedef std::vector<Token> MacroArg;
+ bool collectMacroArgs(const Macro &macro,
+ const Token &identifier,
+ std::vector<MacroArg> *args,
+ SourceLocation *closingParenthesisLocation);
+ void replaceMacroParams(const Macro &macro,
+ const std::vector<MacroArg> &args,
+ std::vector<Token> *replacements);
+
+ struct MacroContext
+ {
+ MacroContext();
+ bool empty() const;
+ const Token &get();
+ void unget();
+
+ const Macro *macro;
+ std::size_t index;
+ std::vector<Token> replacements;
+ };
+
+ Lexer *mLexer;
+ MacroSet *mMacroSet;
+ Diagnostics *mDiagnostics;
+
+ std::unique_ptr<Token> mReserveToken;
+ std::vector<MacroContext *> mContextStack;
+ size_t mTotalTokensInContexts;
+
+ bool mDeferReenablingMacros;
+ std::vector<const Macro *> mMacrosToReenable;
+
+ class ScopedMacroReenabler;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_MACROEXPANDER_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Preprocessor.cpp b/gfx/angle/src/compiler/preprocessor/Preprocessor.cpp
new file mode 100755
index 000000000..1709d6673
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Preprocessor.cpp
@@ -0,0 +1,102 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/Preprocessor.h"
+
+#include "common/debug.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/DirectiveParser.h"
+#include "compiler/preprocessor/Macro.h"
+#include "compiler/preprocessor/MacroExpander.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/preprocessor/Tokenizer.h"
+
+namespace pp
+{
+
+struct PreprocessorImpl
+{
+ Diagnostics *diagnostics;
+ MacroSet macroSet;
+ Tokenizer tokenizer;
+ DirectiveParser directiveParser;
+ MacroExpander macroExpander;
+
+ PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler)
+ : diagnostics(diag),
+ tokenizer(diag),
+ directiveParser(&tokenizer, &macroSet, diag, directiveHandler),
+ macroExpander(&directiveParser, &macroSet, diag)
+ {
+ }
+};
+
+Preprocessor::Preprocessor(Diagnostics *diagnostics,
+ DirectiveHandler *directiveHandler)
+{
+ mImpl = new PreprocessorImpl(diagnostics, directiveHandler);
+}
+
+Preprocessor::~Preprocessor()
+{
+ delete mImpl;
+}
+
+bool Preprocessor::init(size_t count,
+ const char * const string[],
+ const int length[])
+{
+ static const int kDefaultGLSLVersion = 100;
+
+ // Add standard pre-defined macros.
+ predefineMacro("__LINE__", 0);
+ predefineMacro("__FILE__", 0);
+ predefineMacro("__VERSION__", kDefaultGLSLVersion);
+ predefineMacro("GL_ES", 1);
+
+ return mImpl->tokenizer.init(count, string, length);
+}
+
+void Preprocessor::predefineMacro(const char *name, int value)
+{
+ PredefineMacro(&mImpl->macroSet, name, value);
+}
+
+void Preprocessor::lex(Token *token)
+{
+ bool validToken = false;
+ while (!validToken)
+ {
+ mImpl->macroExpander.lex(token);
+ switch (token->type)
+ {
+ // We should not be returning internal preprocessing tokens.
+ // Convert preprocessing tokens to compiler tokens or report
+ // diagnostics.
+ case Token::PP_HASH:
+ UNREACHABLE();
+ break;
+ case Token::PP_NUMBER:
+ mImpl->diagnostics->report(Diagnostics::PP_INVALID_NUMBER,
+ token->location, token->text);
+ break;
+ case Token::PP_OTHER:
+ mImpl->diagnostics->report(Diagnostics::PP_INVALID_CHARACTER,
+ token->location, token->text);
+ break;
+ default:
+ validToken = true;
+ break;
+ }
+ }
+}
+
+void Preprocessor::setMaxTokenSize(size_t maxTokenSize)
+{
+ mImpl->tokenizer.setMaxTokenSize(maxTokenSize);
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/Preprocessor.h b/gfx/angle/src/compiler/preprocessor/Preprocessor.h
new file mode 100755
index 000000000..cd699786f
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Preprocessor.h
@@ -0,0 +1,52 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_PREPROCESSOR_H_
+#define COMPILER_PREPROCESSOR_PREPROCESSOR_H_
+
+#include <cstddef>
+
+#include "common/angleutils.h"
+
+namespace pp
+{
+
+class Diagnostics;
+class DirectiveHandler;
+struct PreprocessorImpl;
+struct Token;
+
+class Preprocessor : angle::NonCopyable
+{
+ public:
+ Preprocessor(Diagnostics *diagnostics, DirectiveHandler *directiveHandler);
+ ~Preprocessor();
+
+ // count: specifies the number of elements in the string and length arrays.
+ // string: specifies an array of pointers to strings.
+ // length: specifies an array of string lengths.
+ // If length is NULL, each string is assumed to be null terminated.
+ // If length is a value other than NULL, it points to an array containing
+ // a string length for each of the corresponding elements of string.
+ // Each element in the length array may contain the length of the
+ // corresponding string or a value less than 0 to indicate that the string
+ // is null terminated.
+ bool init(size_t count, const char * const string[], const int length[]);
+ // Adds a pre-defined macro.
+ void predefineMacro(const char *name, int value);
+
+ void lex(Token *token);
+
+ // Set maximum preprocessor token size
+ void setMaxTokenSize(size_t maxTokenSize);
+
+ private:
+ PreprocessorImpl *mImpl;
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_PREPROCESSOR_H_
diff --git a/gfx/angle/src/compiler/preprocessor/SourceLocation.h b/gfx/angle/src/compiler/preprocessor/SourceLocation.h
new file mode 100755
index 000000000..af8a8d5d1
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/SourceLocation.h
@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_SOURCELOCATION_H_
+#define COMPILER_PREPROCESSOR_SOURCELOCATION_H_
+
+namespace pp
+{
+
+struct SourceLocation
+{
+ SourceLocation()
+ : file(0),
+ line(0)
+ {
+ }
+ SourceLocation(int f, int l)
+ : file(f),
+ line(l)
+ {
+ }
+
+ bool equals(const SourceLocation &other) const
+ {
+ return (file == other.file) && (line == other.line);
+ }
+
+ int file;
+ int line;
+};
+
+inline bool operator==(const SourceLocation &lhs, const SourceLocation &rhs)
+{
+ return lhs.equals(rhs);
+}
+
+inline bool operator!=(const SourceLocation &lhs, const SourceLocation &rhs)
+{
+ return !lhs.equals(rhs);
+}
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_SOURCELOCATION_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Token.cpp b/gfx/angle/src/compiler/preprocessor/Token.cpp
new file mode 100755
index 000000000..41610a402
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Token.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/preprocessor/Token.h"
+
+#include "common/debug.h"
+#include "compiler/preprocessor/numeric_lex.h"
+
+namespace pp
+{
+
+void Token::reset()
+{
+ type = 0;
+ flags = 0;
+ location = SourceLocation();
+ text.clear();
+}
+
+bool Token::equals(const Token &other) const
+{
+ return (type == other.type) &&
+ (flags == other.flags) &&
+ (location == other.location) &&
+ (text == other.text);
+}
+
+void Token::setAtStartOfLine(bool start)
+{
+ if (start)
+ flags |= AT_START_OF_LINE;
+ else
+ flags &= ~AT_START_OF_LINE;
+}
+
+void Token::setHasLeadingSpace(bool space)
+{
+ if (space)
+ flags |= HAS_LEADING_SPACE;
+ else
+ flags &= ~HAS_LEADING_SPACE;
+}
+
+void Token::setExpansionDisabled(bool disable)
+{
+ if (disable)
+ flags |= EXPANSION_DISABLED;
+ else
+ flags &= ~EXPANSION_DISABLED;
+}
+
+bool Token::iValue(int *value) const
+{
+ ASSERT(type == CONST_INT);
+ return numeric_lex_int(text, value);
+}
+
+bool Token::uValue(unsigned int *value) const
+{
+ ASSERT(type == CONST_INT);
+ return numeric_lex_int(text, value);
+}
+
+bool Token::fValue(float *value) const
+{
+ ASSERT(type == CONST_FLOAT);
+ return numeric_lex_float(text, value);
+}
+
+std::ostream &operator<<(std::ostream &out, const Token &token)
+{
+ if (token.hasLeadingSpace())
+ out << " ";
+
+ out << token.text;
+ return out;
+}
+
+} // namespace pp
diff --git a/gfx/angle/src/compiler/preprocessor/Token.h b/gfx/angle/src/compiler/preprocessor/Token.h
new file mode 100755
index 000000000..716503b32
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Token.h
@@ -0,0 +1,120 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_TOKEN_H_
+#define COMPILER_PREPROCESSOR_TOKEN_H_
+
+#include <ostream>
+#include <string>
+
+#include "compiler/preprocessor/SourceLocation.h"
+
+namespace pp
+{
+
+struct Token
+{
+ enum Type
+ {
+ LAST = 0, // EOF.
+
+ IDENTIFIER = 258,
+
+ CONST_INT,
+ CONST_FLOAT,
+
+ OP_INC,
+ OP_DEC,
+ OP_LEFT,
+ OP_RIGHT,
+ OP_LE,
+ OP_GE,
+ OP_EQ,
+ OP_NE,
+ OP_AND,
+ OP_XOR,
+ OP_OR,
+ OP_ADD_ASSIGN,
+ OP_SUB_ASSIGN,
+ OP_MUL_ASSIGN,
+ OP_DIV_ASSIGN,
+ OP_MOD_ASSIGN,
+ OP_LEFT_ASSIGN,
+ OP_RIGHT_ASSIGN,
+ OP_AND_ASSIGN,
+ OP_XOR_ASSIGN,
+ OP_OR_ASSIGN,
+
+ // Preprocessing token types.
+ // These types are used by the preprocessor internally.
+ // Preprocessor clients must not depend or check for them.
+ PP_HASH,
+ PP_NUMBER,
+ PP_OTHER
+ };
+ enum Flags
+ {
+ AT_START_OF_LINE = 1 << 0,
+ HAS_LEADING_SPACE = 1 << 1,
+ EXPANSION_DISABLED = 1 << 2
+ };
+
+ Token()
+ : type(0),
+ flags(0)
+ {
+ }
+
+ void reset();
+ bool equals(const Token &other) const;
+
+ // Returns true if this is the first token on line.
+ // It disregards any leading whitespace.
+ bool atStartOfLine() const
+ {
+ return (flags & AT_START_OF_LINE) != 0;
+ }
+ void setAtStartOfLine(bool start);
+
+ bool hasLeadingSpace() const
+ {
+ return (flags & HAS_LEADING_SPACE) != 0;
+ }
+ void setHasLeadingSpace(bool space);
+
+ bool expansionDisabled() const
+ {
+ return (flags & EXPANSION_DISABLED) != 0;
+ }
+ void setExpansionDisabled(bool disable);
+
+ // Converts text into numeric value for CONST_INT and CONST_FLOAT token.
+ // Returns false if the parsed value cannot fit into an int or float.
+ bool iValue(int *value) const;
+ bool uValue(unsigned int *value) const;
+ bool fValue(float *value) const;
+
+ int type;
+ unsigned int flags;
+ SourceLocation location;
+ std::string text;
+};
+
+inline bool operator==(const Token &lhs, const Token &rhs)
+{
+ return lhs.equals(rhs);
+}
+
+inline bool operator!=(const Token &lhs, const Token &rhs)
+{
+ return !lhs.equals(rhs);
+}
+
+std::ostream &operator<<(std::ostream &out, const Token &token);
+
+} // namepsace pp
+
+#endif // COMPILER_PREPROCESSOR_TOKEN_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Tokenizer.cpp b/gfx/angle/src/compiler/preprocessor/Tokenizer.cpp
new file mode 100755
index 000000000..40e910e22
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Tokenizer.cpp
@@ -0,0 +1,2872 @@
+#line 16 "./Tokenizer.l"
+//
+// Copyright (c) 2011-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+
+
+
+
+
+
+
+
+
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 1
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+
+
+
+
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+
+
+
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE pprestart(yyin ,yyscanner )
+
+
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+
+
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+
+
+
+ #define YY_LESS_LINENO(n)
+ #define YY_LINENO_REWIND_TO(ptr)
+
+
+
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via pprestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+
+
+
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+
+
+
+
+
+void pprestart (FILE *input_file ,yyscan_t yyscanner );
+void pp_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE pp_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void pp_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void pp_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void pppush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void pppop_buffer_state (yyscan_t yyscanner );
+
+
+static void ppensure_buffer_stack (yyscan_t yyscanner );
+static void pp_load_buffer_state (yyscan_t yyscanner );
+static void pp_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+
+
+#define YY_FLUSH_BUFFER pp_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+
+YY_BUFFER_STATE pp_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE pp_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE pp_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
+
+
+void *ppalloc (yy_size_t ,yyscan_t yyscanner );
+void *pprealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void ppfree (void * ,yyscan_t yyscanner );
+
+
+#define yy_new_buffer pp_create_buffer
+
+
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ ppensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ pp_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ ppensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ pp_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+
+/* Begin user sect3 */
+
+#define ppwrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+
+
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+
+
+
+
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yynoreturn yy_fatal_error (yyconst char* msg ,yyscan_t yyscanner );
+
+
+
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+
+
+#define YY_NUM_RULES 38
+#define YY_END_OF_BUFFER 39
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[98] =
+ { 0,
+ 0, 0, 0, 0, 39, 37, 34, 35, 35, 33,
+ 7, 33, 33, 33, 33, 33, 33, 33, 33, 9,
+ 9, 33, 33, 33, 8, 37, 33, 33, 3, 5,
+ 5, 4, 34, 35, 19, 27, 20, 30, 25, 12,
+ 23, 13, 24, 10, 2, 1, 26, 10, 9, 11,
+ 11, 11, 9, 11, 9, 9, 14, 16, 18, 17,
+ 15, 8, 36, 36, 31, 21, 32, 22, 3, 5,
+ 6, 11, 10, 11, 10, 1, 10, 11, 10, 0,
+ 10, 9, 9, 9, 28, 29, 0, 10, 10, 10,
+ 10, 9, 10, 10, 9, 10, 0
+
+ } ;
+
+static yyconst YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 5, 1, 6, 1, 7, 8, 1, 9,
+ 9, 10, 11, 9, 12, 13, 14, 15, 16, 16,
+ 16, 16, 16, 16, 16, 17, 17, 9, 9, 18,
+ 19, 20, 9, 1, 21, 21, 21, 21, 22, 23,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 25, 24, 24, 26, 24, 24,
+ 9, 27, 9, 28, 24, 1, 21, 21, 21, 21,
+
+ 22, 23, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 25, 24, 24, 26,
+ 24, 24, 9, 29, 9, 9, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst YY_CHAR yy_meta[30] =
+ { 0,
+ 1, 1, 2, 2, 1, 1, 1, 1, 1, 3,
+ 1, 1, 4, 1, 5, 5, 5, 1, 1, 1,
+ 5, 5, 5, 5, 5, 5, 1, 1, 1
+ } ;
+
+static yyconst flex_uint16_t yy_base[103] =
+ { 0,
+ 0, 0, 27, 29, 137, 194, 133, 194, 117, 100,
+ 194, 98, 26, 194, 94, 24, 28, 33, 32, 39,
+ 51, 39, 80, 50, 0, 68, 25, 54, 0, 194,
+ 88, 71, 80, 194, 194, 194, 194, 194, 194, 194,
+ 194, 194, 194, 71, 194, 0, 194, 85, 55, 64,
+ 99, 111, 53, 105, 0, 50, 55, 194, 194, 194,
+ 40, 0, 194, 38, 194, 194, 194, 194, 0, 194,
+ 194, 117, 0, 130, 0, 0, 0, 137, 0, 88,
+ 113, 0, 131, 0, 194, 194, 143, 139, 152, 150,
+ 0, 13, 153, 194, 0, 194, 194, 176, 31, 181,
+
+ 186, 188
+ } ;
+
+static yyconst flex_int16_t yy_def[103] =
+ { 0,
+ 97, 1, 98, 98, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 20, 97, 97, 97, 99, 97, 97, 97, 100, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 101, 97, 97, 20, 20,
+ 50, 51, 51, 102, 21, 51, 97, 97, 97, 97,
+ 97, 99, 97, 97, 97, 97, 97, 97, 100, 97,
+ 97, 44, 44, 72, 72, 101, 48, 51, 51, 97,
+ 52, 51, 102, 51, 97, 97, 97, 74, 78, 97,
+ 51, 51, 97, 97, 51, 97, 0, 97, 97, 97,
+
+ 97, 97
+ } ;
+
+static yyconst flex_uint16_t yy_nxt[224] =
+ { 0,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 21, 22, 23, 24,
+ 25, 25, 25, 25, 25, 25, 26, 27, 28, 30,
+ 31, 30, 31, 37, 40, 62, 32, 95, 32, 42,
+ 63, 45, 41, 65, 38, 46, 43, 44, 44, 44,
+ 47, 48, 66, 49, 49, 50, 57, 58, 86, 51,
+ 52, 51, 51, 53, 54, 55, 55, 55, 60, 61,
+ 63, 64, 67, 85, 84, 56, 51, 82, 50, 50,
+ 51, 33, 68, 72, 71, 73, 73, 73, 51, 51,
+ 70, 72, 74, 75, 72, 72, 72, 51, 59, 77,
+
+ 77, 77, 90, 90, 90, 51, 78, 79, 51, 51,
+ 51, 51, 39, 51, 51, 51, 36, 51, 35, 34,
+ 51, 80, 80, 97, 97, 81, 81, 81, 51, 51,
+ 51, 72, 72, 72, 33, 91, 97, 97, 72, 72,
+ 87, 87, 97, 51, 88, 88, 88, 87, 87, 97,
+ 97, 89, 89, 89, 51, 92, 51, 93, 93, 93,
+ 97, 75, 97, 97, 90, 90, 90, 93, 93, 93,
+ 97, 97, 94, 97, 79, 96, 29, 29, 29, 29,
+ 29, 69, 97, 97, 69, 69, 76, 97, 76, 76,
+ 76, 83, 83, 5, 97, 97, 97, 97, 97, 97,
+
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97
+ } ;
+
+static yyconst flex_int16_t yy_chk[224] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
+ 3, 4, 4, 13, 16, 99, 3, 92, 4, 17,
+ 64, 19, 16, 27, 13, 19, 17, 18, 18, 18,
+ 19, 20, 27, 20, 20, 20, 22, 22, 61, 20,
+ 20, 20, 20, 20, 20, 21, 21, 21, 24, 24,
+ 26, 26, 28, 57, 56, 21, 21, 53, 50, 50,
+ 49, 33, 28, 44, 32, 44, 44, 44, 50, 50,
+ 31, 44, 44, 44, 44, 44, 44, 48, 23, 48,
+
+ 48, 48, 80, 80, 80, 48, 48, 48, 48, 48,
+ 48, 51, 15, 51, 51, 51, 12, 54, 10, 9,
+ 51, 52, 52, 81, 81, 52, 52, 52, 54, 54,
+ 54, 72, 72, 72, 7, 81, 5, 0, 72, 72,
+ 74, 74, 0, 83, 74, 74, 74, 78, 78, 88,
+ 88, 78, 78, 78, 83, 83, 83, 87, 87, 87,
+ 0, 88, 89, 89, 90, 90, 90, 93, 93, 93,
+ 0, 0, 90, 0, 89, 93, 98, 98, 98, 98,
+ 98, 100, 0, 0, 100, 100, 101, 0, 101, 101,
+ 101, 102, 102, 97, 97, 97, 97, 97, 97, 97,
+
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/*
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Lex specification for GLSL ES preprocessor.
+Based on Microsoft Visual Studio 2010 Preprocessor Grammar:
+http://msdn.microsoft.com/en-us/library/2scxys89.aspx
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh.
+*/
+
+#if defined(_MSC_VER)
+#pragma warning(disable: 4005)
+#endif
+
+#include "compiler/preprocessor/Tokenizer.h"
+
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/Token.h"
+
+#if defined(__GNUC__)
+// Triggered by the auto-generated yy_fatal_error function.
+#pragma GCC diagnostic ignored "-Wmissing-noreturn"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4244)
+#endif
+
+// Workaround for flex using the register keyword, deprecated in C++11.
+#ifdef __cplusplus
+#if __cplusplus > 199711L
+#define register
+#endif
+#endif
+
+typedef std::string YYSTYPE;
+typedef pp::SourceLocation YYLTYPE;
+
+// Use the unused yycolumn variable to track file (string) number.
+#define yyfileno yycolumn
+
+#define YY_USER_INIT \
+ do { \
+ yyfileno = 0; \
+ yylineno = 1; \
+ yyextra->leadingSpace = false; \
+ yyextra->lineStart = true; \
+ } while(0);
+
+#define YY_USER_ACTION \
+ do \
+ { \
+ pp::Input* input = &yyextra->input; \
+ pp::Input::Location* scanLoc = &yyextra->scanLoc; \
+ while ((scanLoc->sIndex < input->count()) && \
+ (scanLoc->cIndex >= input->length(scanLoc->sIndex))) \
+ { \
+ scanLoc->cIndex -= input->length(scanLoc->sIndex++); \
+ ++yyfileno; yylineno = 1; \
+ } \
+ yylloc->file = yyfileno; \
+ yylloc->line = yylineno; \
+ scanLoc->cIndex += yyleng; \
+ } while(0);
+
+#define YY_INPUT(buf, result, maxSize) \
+ result = yyextra->input.read(buf, maxSize, &yylineno);
+
+
+
+
+
+#define INITIAL 0
+#define COMMENT 1
+
+
+
+
+
+
+#define YY_EXTRA_TYPE pp::Tokenizer::Context*
+
+
+
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+
+
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+
+
+ YYSTYPE * yylval_r;
+
+
+
+ YYLTYPE * yylloc_r;
+
+
+ }; /* end struct yyguts_t */
+
+
+
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+
+
+
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+
+
+ # define yylloc yyg->yylloc_r
+
+
+
+int pplex_init (yyscan_t* scanner);
+
+int pplex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+
+int pplex_destroy (yyscan_t yyscanner );
+
+
+
+int ppget_debug (yyscan_t yyscanner );
+
+
+
+void ppset_debug (int debug_flag ,yyscan_t yyscanner );
+
+
+
+YY_EXTRA_TYPE ppget_extra (yyscan_t yyscanner );
+
+
+
+void ppset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+
+
+FILE *ppget_in (yyscan_t yyscanner );
+
+
+
+void ppset_in (FILE * _in_str ,yyscan_t yyscanner );
+
+
+
+FILE *ppget_out (yyscan_t yyscanner );
+
+
+
+void ppset_out (FILE * _out_str ,yyscan_t yyscanner );
+
+
+
+ int ppget_leng (yyscan_t yyscanner );
+
+
+
+char *ppget_text (yyscan_t yyscanner );
+
+
+
+int ppget_lineno (yyscan_t yyscanner );
+
+
+
+void ppset_lineno (int _line_number ,yyscan_t yyscanner );
+
+
+
+
+int ppget_column (yyscan_t yyscanner );
+
+
+
+
+
+void ppset_column (int _column_no ,yyscan_t yyscanner );
+
+
+
+
+YYSTYPE * ppget_lval (yyscan_t yyscanner );
+
+
+void ppset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+
+
+ YYLTYPE *ppget_lloc (yyscan_t yyscanner );
+
+
+
+ void ppset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+
+
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int ppwrap (yyscan_t yyscanner );
+#else
+extern int ppwrap (yyscan_t yyscanner );
+#endif
+#endif
+
+
+#ifndef YY_NO_UNPUT
+
+#endif
+
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+
+
+
+
+
+
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+
+
+
+
+/* end tables serialization structures and prototypes */
+
+
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+extern int pplex \
+ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int pplex \
+ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+
+
+
+ yylval = yylval_param;
+
+
+
+ yylloc = yylloc_param;
+
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ ppensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ pp_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ pp_load_buffer_state(yyscanner );
+ }
+
+ {
+
+
+ /* Line comment */
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 98 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 97 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+
+ YY_BREAK
+/* Block comment */
+/* Line breaks are just counted - not returned. */
+/* The comment is replaced by a single space. */
+case 2:
+YY_RULE_SETUP
+{ BEGIN(COMMENT); }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+
+ YY_BREAK
+case 5:
+/* rule 5 can match eol */
+YY_RULE_SETUP
+{ ++yylineno; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+{
+ yyextra->leadingSpace = true;
+ BEGIN(INITIAL);
+}
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+{
+ // # is only valid at start of line for preprocessor directives.
+ yylval->assign(1, yytext[0]);
+ return yyextra->lineStart ? pp::Token::PP_HASH : pp::Token::PP_OTHER;
+}
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::IDENTIFIER;
+}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::CONST_INT;
+}
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::CONST_FLOAT;
+}
+ YY_BREAK
+/* Anything that starts with a {DIGIT} or .{DIGIT} must be a number. */
+/* Rule to catch all invalid integers and floats. */
+case 11:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::PP_NUMBER;
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_INC;
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_DEC;
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LEFT;
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_RIGHT;
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LE;
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_GE;
+}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_EQ;
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_NE;
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_AND;
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_XOR;
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_OR;
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_ADD_ASSIGN;
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_SUB_ASSIGN;
+}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_MUL_ASSIGN;
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_DIV_ASSIGN;
+}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_MOD_ASSIGN;
+}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LEFT_ASSIGN;
+}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_RIGHT_ASSIGN;
+}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_AND_ASSIGN;
+}
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_XOR_ASSIGN;
+}
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_OR_ASSIGN;
+}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+{
+ yylval->assign(1, yytext[0]);
+ return yytext[0];
+}
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+{ yyextra->leadingSpace = true; }
+ YY_BREAK
+case 35:
+/* rule 35 can match eol */
+YY_RULE_SETUP
+{
+ ++yylineno;
+ yylval->assign(1, '\n');
+ return '\n';
+}
+ YY_BREAK
+case 36:
+/* rule 36 can match eol */
+YY_RULE_SETUP
+{ ++yylineno; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+{
+ yylval->assign(1, yytext[0]);
+ return pp::Token::PP_OTHER;
+}
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMENT):
+{
+ // YY_USER_ACTION is not invoked for handling EOF.
+ // Set the location for EOF token manually.
+ pp::Input* input = &yyextra->input;
+ pp::Input::Location* scanLoc = &yyextra->scanLoc;
+ yy_size_t sIndexMax = input->count() ? input->count() - 1 : 0;
+ if (scanLoc->sIndex != sIndexMax)
+ {
+ // We can only reach here if there are empty strings at the
+ // end of the input.
+ scanLoc->sIndex = sIndexMax; scanLoc->cIndex = 0;
+ // FIXME: this is not 64-bit clean.
+ yyfileno = static_cast<int>(sIndexMax); yylineno = 1;
+ }
+ yylloc->file = yyfileno;
+ yylloc->line = yylineno;
+ yylval->clear();
+
+ if (YY_START == COMMENT)
+ {
+ yyextra->diagnostics->report(pp::Diagnostics::PP_EOF_IN_COMMENT,
+ pp::SourceLocation(yyfileno, yylineno),
+ "");
+ }
+ yyterminate();
+}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+ECHO;
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * pplex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( ppwrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of pplex */
+
+
+
+
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = yyg->yytext_ptr;
+ yy_size_t number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (yy_size_t) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1);
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ pprealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = static_cast<int>(YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1);
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ yy_size_t ret = 0;
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ ret, num_to_read );
+ yyg->yy_n_chars = static_cast<int>(ret);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ pprestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) pprealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += static_cast<int>(number_to_move);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 98 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ char *yy_cp = yyg->yy_c_buf_p;
+
+ YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 98 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c];
+ yy_is_jam = (yy_current_state == 97);
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+
+#ifndef YY_NO_UNPUT
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ pprestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( ppwrap(yyscanner ) )
+ return 0;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void pprestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ ppensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ pp_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ pp_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ pp_load_buffer_state(yyscanner );
+}
+
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void pp_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * pppop_buffer_state();
+ * pppush_buffer_state(new_buffer);
+ */
+ ppensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ pp_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (ppwrap()) processing, but the only time this flag
+ * is looked at is after ppwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+
+static void pp_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE pp_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) ppalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_create_buffer()" );
+
+ b->yy_buf_size = (yy_size_t)size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) ppalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ pp_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+
+/** Destroy the buffer.
+ * @param b a buffer created with pp_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void pp_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ ppfree((void *) b->yy_ch_buf ,yyscanner );
+
+ ppfree((void *) b ,yyscanner );
+}
+
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a pprestart() or at EOF.
+ */
+ static void pp_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ pp_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then pp_init_buffer was _probably_
+ * called from pprestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+
+
+ b->yy_is_interactive = 0;
+
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void pp_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ pp_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void pppush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ ppensure_buffer_stack(yyscanner);
+
+ /* This block is copied from pp_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from pp_switch_to_buffer. */
+ pp_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void pppop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ pp_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ pp_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void ppensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)ppalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ppensure_buffer_stack()" );
+
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)pprealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in ppensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+
+
+
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) ppalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_scan_buffer()" );
+
+ b->yy_buf_size = static_cast<int>(size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ pp_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+
+
+
+/** Setup the input buffer state to scan a string. The next call to pplex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * pp_scan_bytes() instead.
+ */
+YY_BUFFER_STATE pp_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return pp_scan_bytes(yystr,(int) strlen(yystr) ,yyscanner);
+}
+
+
+
+
+/** Setup the input buffer state to scan the given bytes. The next call to pplex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE pp_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ yy_size_t i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) _yybytes_len + 2;
+ buf = (char *) ppalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in pp_scan_bytes()" );
+
+ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = pp_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in pp_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+
+
+
+
+
+
+
+
+
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+
+
+/* Accessor methods (get/set functions) to struct members. */
+
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE ppget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int ppget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+
+
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int ppget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+
+
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ppget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *ppget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int ppget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *ppget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void ppset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+
+
+/** Set the current line number.
+ * @param _line_number line number
+ * @param yyscanner The scanner object.
+ */
+void ppset_lineno (int _line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "ppset_lineno called with no buffer" );
+
+ yylineno = _line_number;
+}
+
+
+
+
+/** Set the current column.
+ * @param _column_no column number
+ * @param yyscanner The scanner object.
+ */
+void ppset_column (int _column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "ppset_column called with no buffer" );
+
+ yycolumn = _column_no;
+}
+
+
+
+
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see pp_switch_to_buffer
+ */
+void ppset_in (FILE * _in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = _in_str ;
+}
+
+
+
+void ppset_out (FILE * _out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = _out_str ;
+}
+
+
+
+
+int ppget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+
+
+void ppset_debug (int _bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = _bdebug ;
+}
+
+
+/* Accessor methods for yylval and yylloc */
+
+
+YYSTYPE * ppget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+
+
+void ppset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+
+
+
+YYLTYPE *ppget_lloc (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylloc;
+}
+
+
+
+void ppset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylloc = yylloc_param;
+}
+
+
+
+
+
+/* User-visible API */
+
+/* pplex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int pplex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ppalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+
+/* pplex_init_extra has the same functionality as pplex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to ppalloc in
+ * the yyextra field.
+ */
+
+int pplex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ ppset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) ppalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ ppset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from pplex_destroy(), so don't allocate here.
+ */
+
+
+ yyg->yy_buffer_stack = NULL;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = NULL;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+
+
+
+
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * pplex_init()
+ */
+ return 0;
+}
+
+
+/* pplex_destroy is for both reentrant and non-reentrant scanners. */
+int pplex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ pp_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ pppop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ ppfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+
+ /* Destroy the start condition stack. */
+ ppfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+
+
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * pplex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ ppfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+
+
+/*
+ * Internal utility routines.
+ */
+
+
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+
+
+void *ppalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ return malloc(size);
+}
+
+
+
+void *pprealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+
+
+void ppfree (void * ptr , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ (void)yyg;
+ free( (char *) ptr ); /* see pprealloc() for (char *) cast */
+}
+
+
+#define YYTABLES_NAME "yytables"
+
+
+
+
+
+
+
+
+
+namespace pp {
+
+Tokenizer::Tokenizer(Diagnostics *diagnostics) : mHandle(nullptr), mMaxTokenSize(256)
+{
+ mContext.diagnostics = diagnostics;
+}
+
+Tokenizer::~Tokenizer()
+{
+ destroyScanner();
+}
+
+bool Tokenizer::init(size_t count, const char * const string[], const int length[])
+{
+ if ((count > 0) && (string == 0))
+ return false;
+
+ mContext.input = Input(count, string, length);
+ return initScanner();
+}
+
+void Tokenizer::setFileNumber(int file)
+{
+ // We use column number as file number.
+ // See macro yyfileno.
+ ppset_column(file,mHandle);
+}
+
+void Tokenizer::setLineNumber(int line)
+{
+ ppset_lineno(line,mHandle);
+}
+
+void Tokenizer::setMaxTokenSize(size_t maxTokenSize)
+{
+ mMaxTokenSize = maxTokenSize;
+}
+
+void Tokenizer::lex(Token *token)
+{
+ token->type = pplex(&token->text,&token->location,mHandle);
+ if (token->text.size() > mMaxTokenSize)
+ {
+ mContext.diagnostics->report(Diagnostics::PP_TOKEN_TOO_LONG,
+ token->location, token->text);
+ token->text.erase(mMaxTokenSize);
+ }
+
+ token->flags = 0;
+
+ token->setAtStartOfLine(mContext.lineStart);
+ mContext.lineStart = token->type == '\n';
+
+ token->setHasLeadingSpace(mContext.leadingSpace);
+ mContext.leadingSpace = false;
+}
+
+bool Tokenizer::initScanner()
+{
+ if ((mHandle == nullptr) && pplex_init_extra(&mContext, &mHandle))
+ return false;
+
+ pprestart(0,mHandle);
+ return true;
+}
+
+void Tokenizer::destroyScanner()
+{
+ if (mHandle == nullptr)
+ return;
+
+ pplex_destroy(mHandle);
+ mHandle = nullptr;
+}
+
+} // namespace pp
+
+
diff --git a/gfx/angle/src/compiler/preprocessor/Tokenizer.h b/gfx/angle/src/compiler/preprocessor/Tokenizer.h
new file mode 100755
index 000000000..6dfb19c66
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Tokenizer.h
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2012-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_PREPROCESSOR_TOKENIZER_H_
+#define COMPILER_PREPROCESSOR_TOKENIZER_H_
+
+#include "common/angleutils.h"
+#include "compiler/preprocessor/Input.h"
+#include "compiler/preprocessor/Lexer.h"
+
+namespace pp
+{
+
+class Diagnostics;
+
+class Tokenizer : public Lexer
+{
+ public:
+ struct Context
+ {
+ Diagnostics *diagnostics;
+
+ Input input;
+ // The location where yytext points to. Token location should track
+ // scanLoc instead of Input::mReadLoc because they may not be the same
+ // if text is buffered up in the scanner input buffer.
+ Input::Location scanLoc;
+
+ bool leadingSpace;
+ bool lineStart;
+ };
+
+ Tokenizer(Diagnostics *diagnostics);
+ ~Tokenizer();
+
+ bool init(size_t count, const char * const string[], const int length[]);
+
+ void setFileNumber(int file);
+ void setLineNumber(int line);
+ void setMaxTokenSize(size_t maxTokenSize);
+
+ void lex(Token *token) override;
+
+ private:
+ bool initScanner();
+ void destroyScanner();
+
+ void *mHandle; // Scanner handle.
+ Context mContext; // Scanner extra.
+ size_t mMaxTokenSize; // Maximum token size
+};
+
+} // namespace pp
+
+#endif // COMPILER_PREPROCESSOR_TOKENIZER_H_
diff --git a/gfx/angle/src/compiler/preprocessor/Tokenizer.l b/gfx/angle/src/compiler/preprocessor/Tokenizer.l
new file mode 100755
index 000000000..62eb4caa6
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/Tokenizer.l
@@ -0,0 +1,357 @@
+/*
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Lex specification for GLSL ES preprocessor.
+Based on Microsoft Visual Studio 2010 Preprocessor Grammar:
+http://msdn.microsoft.com/en-us/library/2scxys89.aspx
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh.
+*/
+
+%top{
+//
+// Copyright (c) 2011-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+}
+
+%{
+#if defined(_MSC_VER)
+#pragma warning(disable: 4005)
+#endif
+
+#include "compiler/preprocessor/Tokenizer.h"
+
+#include "compiler/preprocessor/DiagnosticsBase.h"
+#include "compiler/preprocessor/Token.h"
+
+#if defined(__GNUC__)
+// Triggered by the auto-generated yy_fatal_error function.
+#pragma GCC diagnostic ignored "-Wmissing-noreturn"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4244)
+#endif
+
+// Workaround for flex using the register keyword, deprecated in C++11.
+#ifdef __cplusplus
+#if __cplusplus > 199711L
+#define register
+#endif
+#endif
+
+typedef std::string YYSTYPE;
+typedef pp::SourceLocation YYLTYPE;
+
+// Use the unused yycolumn variable to track file (string) number.
+#define yyfileno yycolumn
+
+#define YY_USER_INIT \
+ do { \
+ yyfileno = 0; \
+ yylineno = 1; \
+ yyextra->leadingSpace = false; \
+ yyextra->lineStart = true; \
+ } while(0);
+
+#define YY_USER_ACTION \
+ do \
+ { \
+ pp::Input* input = &yyextra->input; \
+ pp::Input::Location* scanLoc = &yyextra->scanLoc; \
+ while ((scanLoc->sIndex < input->count()) && \
+ (scanLoc->cIndex >= input->length(scanLoc->sIndex))) \
+ { \
+ scanLoc->cIndex -= input->length(scanLoc->sIndex++); \
+ ++yyfileno; yylineno = 1; \
+ } \
+ yylloc->file = yyfileno; \
+ yylloc->line = yylineno; \
+ scanLoc->cIndex += yyleng; \
+ } while(0);
+
+#define YY_INPUT(buf, result, maxSize) \
+ result = yyextra->input.read(buf, maxSize, &yylineno);
+
+%}
+
+%option noyywrap nounput never-interactive
+%option reentrant bison-bridge bison-locations
+%option prefix="pp"
+%option extra-type="pp::Tokenizer::Context*"
+%x COMMENT
+
+NEWLINE \n|\r|\r\n
+IDENTIFIER [_a-zA-Z][_a-zA-Z0-9]*
+PUNCTUATOR [][<>(){}.+-/*%^|&~=!:;,?]
+
+DECIMAL_CONSTANT [1-9][0-9]*[uU]?
+OCTAL_CONSTANT 0[0-7]*[uU]?
+HEXADECIMAL_CONSTANT 0[xX][0-9a-fA-F]+[uU]?
+
+DIGIT [0-9]
+EXPONENT_PART [eE][+-]?{DIGIT}+
+FRACTIONAL_CONSTANT ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")
+
+%%
+
+ /* Line comment */
+"//"[^\r\n]*
+
+ /* Block comment */
+ /* Line breaks are just counted - not returned. */
+ /* The comment is replaced by a single space. */
+"/*" { BEGIN(COMMENT); }
+<COMMENT>[^*\r\n]+
+<COMMENT>"*"
+<COMMENT>{NEWLINE} { ++yylineno; }
+<COMMENT>"*/" {
+ yyextra->leadingSpace = true;
+ BEGIN(INITIAL);
+}
+
+# {
+ // # is only valid at start of line for preprocessor directives.
+ yylval->assign(1, yytext[0]);
+ return yyextra->lineStart ? pp::Token::PP_HASH : pp::Token::PP_OTHER;
+}
+
+{IDENTIFIER} {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::IDENTIFIER;
+}
+
+({DECIMAL_CONSTANT}[uU]?)|({OCTAL_CONSTANT}[uU]?)|({HEXADECIMAL_CONSTANT}[uU]?) {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::CONST_INT;
+}
+
+({DIGIT}+{EXPONENT_PART}[fF]?)|({FRACTIONAL_CONSTANT}{EXPONENT_PART}?[fF]?) {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::CONST_FLOAT;
+}
+
+ /* Anything that starts with a {DIGIT} or .{DIGIT} must be a number. */
+ /* Rule to catch all invalid integers and floats. */
+({DIGIT}+[_a-zA-Z0-9.]*)|("."{DIGIT}+[_a-zA-Z0-9.]*) {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::PP_NUMBER;
+}
+
+"++" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_INC;
+}
+"--" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_DEC;
+}
+"<<" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LEFT;
+}
+">>" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_RIGHT;
+}
+"<=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LE;
+}
+">=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_GE;
+}
+"==" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_EQ;
+}
+"!=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_NE;
+}
+"&&" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_AND;
+}
+"^^" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_XOR;
+}
+"||" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_OR;
+}
+"+=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_ADD_ASSIGN;
+}
+"-=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_SUB_ASSIGN;
+}
+"*=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_MUL_ASSIGN;
+}
+"/=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_DIV_ASSIGN;
+}
+"%=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_MOD_ASSIGN;
+}
+"<<=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_LEFT_ASSIGN;
+}
+">>=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_RIGHT_ASSIGN;
+}
+"&=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_AND_ASSIGN;
+}
+"^=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_XOR_ASSIGN;
+}
+"|=" {
+ yylval->assign(yytext, yyleng);
+ return pp::Token::OP_OR_ASSIGN;
+}
+
+{PUNCTUATOR} {
+ yylval->assign(1, yytext[0]);
+ return yytext[0];
+}
+
+[ \t\v\f]+ { yyextra->leadingSpace = true; }
+
+{NEWLINE} {
+ ++yylineno;
+ yylval->assign(1, '\n');
+ return '\n';
+}
+
+\\{NEWLINE} { ++yylineno; }
+
+. {
+ yylval->assign(1, yytext[0]);
+ return pp::Token::PP_OTHER;
+}
+
+<*><<EOF>> {
+ // YY_USER_ACTION is not invoked for handling EOF.
+ // Set the location for EOF token manually.
+ pp::Input* input = &yyextra->input;
+ pp::Input::Location* scanLoc = &yyextra->scanLoc;
+ yy_size_t sIndexMax = input->count() ? input->count() - 1 : 0;
+ if (scanLoc->sIndex != sIndexMax)
+ {
+ // We can only reach here if there are empty strings at the
+ // end of the input.
+ scanLoc->sIndex = sIndexMax; scanLoc->cIndex = 0;
+ // FIXME: this is not 64-bit clean.
+ yyfileno = static_cast<int>(sIndexMax); yylineno = 1;
+ }
+ yylloc->file = yyfileno;
+ yylloc->line = yylineno;
+ yylval->clear();
+
+ if (YY_START == COMMENT)
+ {
+ yyextra->diagnostics->report(pp::Diagnostics::PP_EOF_IN_COMMENT,
+ pp::SourceLocation(yyfileno, yylineno),
+ "");
+ }
+ yyterminate();
+}
+
+%%
+
+namespace pp {
+
+Tokenizer::Tokenizer(Diagnostics *diagnostics) : mHandle(nullptr), mMaxTokenSize(256)
+{
+ mContext.diagnostics = diagnostics;
+}
+
+Tokenizer::~Tokenizer()
+{
+ destroyScanner();
+}
+
+bool Tokenizer::init(size_t count, const char * const string[], const int length[])
+{
+ if ((count > 0) && (string == 0))
+ return false;
+
+ mContext.input = Input(count, string, length);
+ return initScanner();
+}
+
+void Tokenizer::setFileNumber(int file)
+{
+ // We use column number as file number.
+ // See macro yyfileno.
+ yyset_column(file, mHandle);
+}
+
+void Tokenizer::setLineNumber(int line)
+{
+ yyset_lineno(line, mHandle);
+}
+
+void Tokenizer::setMaxTokenSize(size_t maxTokenSize)
+{
+ mMaxTokenSize = maxTokenSize;
+}
+
+void Tokenizer::lex(Token *token)
+{
+ token->type = yylex(&token->text, &token->location, mHandle);
+ if (token->text.size() > mMaxTokenSize)
+ {
+ mContext.diagnostics->report(Diagnostics::PP_TOKEN_TOO_LONG,
+ token->location, token->text);
+ token->text.erase(mMaxTokenSize);
+ }
+
+ token->flags = 0;
+
+ token->setAtStartOfLine(mContext.lineStart);
+ mContext.lineStart = token->type == '\n';
+
+ token->setHasLeadingSpace(mContext.leadingSpace);
+ mContext.leadingSpace = false;
+}
+
+bool Tokenizer::initScanner()
+{
+ if ((mHandle == nullptr) && yylex_init_extra(&mContext, &mHandle))
+ return false;
+
+ yyrestart(0, mHandle);
+ return true;
+}
+
+void Tokenizer::destroyScanner()
+{
+ if (mHandle == nullptr)
+ return;
+
+ yylex_destroy(mHandle);
+ mHandle = nullptr;
+}
+
+} // namespace pp
+
diff --git a/gfx/angle/src/compiler/preprocessor/generate_parser.sh b/gfx/angle/src/compiler/preprocessor/generate_parser.sh
new file mode 100755
index 000000000..032858722
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/generate_parser.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Generates various components of GLSL ES preprocessor.
+
+run_flex()
+{
+input_file=$script_dir/$1
+output_source=$script_dir/$2
+flex --noline --nounistd --outfile=$output_source $input_file
+}
+
+run_bison()
+{
+input_file=$script_dir/$1
+output_source=$script_dir/$2
+bison --no-lines --skeleton=yacc.c --output=$output_source $input_file
+}
+
+script_dir=$(dirname $0)
+
+# Generate preprocessor
+run_flex Tokenizer.l Tokenizer.cpp
+run_bison ExpressionParser.y ExpressionParser.cpp
+patch --silent --forward < 64bit-tokenizer-safety.patch
diff --git a/gfx/angle/src/compiler/preprocessor/numeric_lex.h b/gfx/angle/src/compiler/preprocessor/numeric_lex.h
new file mode 100755
index 000000000..7cf976988
--- /dev/null
+++ b/gfx/angle/src/compiler/preprocessor/numeric_lex.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// numeric_lex.h: Functions to extract numeric values from string.
+
+#ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_
+#define COMPILER_PREPROCESSOR_NUMERICLEX_H_
+
+#include <cmath>
+#include <sstream>
+
+namespace pp {
+
+inline std::ios::fmtflags numeric_base_int(const std::string &str)
+{
+ if ((str.size() >= 2) &&
+ (str[0] == '0') &&
+ (str[1] == 'x' || str[1] == 'X'))
+ {
+ return std::ios::hex;
+ }
+ if ((str.size() >= 1) && (str[0] == '0'))
+ {
+ return std::ios::oct;
+ }
+ return std::ios::dec;
+}
+
+// The following functions parse the given string to extract a numerical
+// value of the given type. These functions assume that the string is
+// of the correct form. They can only fail if the parsed value is too big,
+// in which case false is returned.
+
+template<typename IntType>
+bool numeric_lex_int(const std::string &str, IntType *value)
+{
+ std::istringstream stream(str);
+ // This should not be necessary, but MSVS has a buggy implementation.
+ // It returns incorrect results if the base is not specified.
+ stream.setf(numeric_base_int(str), std::ios::basefield);
+
+ stream >> (*value);
+ return !stream.fail();
+}
+
+template<typename FloatType>
+bool numeric_lex_float(const std::string &str, FloatType *value)
+{
+// On 64-bit Intel Android, istringstream is broken. Until this is fixed in
+// a newer NDK, don't use it. Android doesn't have locale support, so this
+// doesn't have to force the C locale.
+// TODO(thakis): Remove this once this bug has been fixed in the NDK and
+// that NDK has been rolled into chromium.
+#if defined(ANGLE_PLATFORM_ANDROID) && __x86_64__
+ *value = strtod(str.c_str(), nullptr);
+ return errno != ERANGE;
+#else
+ std::istringstream stream(str);
+ // Force "C" locale so that decimal character is always '.', and
+ // not dependent on the current locale.
+ stream.imbue(std::locale::classic());
+
+ stream >> (*value);
+ return !stream.fail() && std::isfinite(*value);
+#endif
+}
+
+} // namespace pp.
+
+#endif // COMPILER_PREPROCESSOR_NUMERICLEX_H_
diff --git a/gfx/angle/src/compiler/translator/64bit-lexer-safety.patch b/gfx/angle/src/compiler/translator/64bit-lexer-safety.patch
new file mode 100755
index 000000000..e7e403771
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/64bit-lexer-safety.patch
@@ -0,0 +1,122 @@
+diff --git a/src/compiler/translator/glslang_lex.cpp b/src/compiler/translator/glslang_lex.cpp
+index 1ba63df..2a206ab 100644
+--- a/src/compiler/translator/glslang_lex.cpp
++++ b/src/compiler/translator/glslang_lex.cpp
+@@ -1,4 +1,3 @@
+-#line 17 "./glslang.l"
+ //
+ // Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+@@ -149,6 +148,7 @@ typedef int16_t flex_int16_t;
+ typedef uint16_t flex_uint16_t;
+ typedef int32_t flex_int32_t;
+ typedef uint32_t flex_uint32_t;
++typedef uint64_t flex_uint64_t;
+ #else
+ typedef signed char flex_int8_t;
+ typedef short int flex_int16_t;
+@@ -335,6 +335,11 @@ typedef size_t yy_size_t;
+
+
+
++#ifndef YY_TYPEDEF_YY_SIZE_T
++#define YY_TYPEDEF_YY_SIZE_T
++typedef size_t yy_size_t;
++#endif
++
+ #define EOB_ACT_CONTINUE_SCAN 0
+ #define EOB_ACT_END_OF_FILE 1
+ #define EOB_ACT_LAST_MATCH 2
+@@ -351,8 +356,8 @@ typedef size_t yy_size_t;
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+- int yyl;\
+- for ( yyl = n; yyl < yyleng; ++yyl )\
++ yy_size_t yyl;\
++ for ( yyl = n; yyl < static_cast<yy_site_t>(yyleng); ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+@@ -1692,7 +1697,7 @@ yy_find_action:
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ yy_size_t yyl;
+- for ( yyl = 0; yyl < yyleng; ++yyl )
++ for ( yyl = 0; yyl < static_cast<yy_size_t>(yyleng); ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+@@ -2655,7 +2660,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+ else
+ {
+ int num_to_read =
+- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
++ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - static_cast<int>(number_to_move) - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+@@ -2690,7 +2695,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+- number_to_move - 1;
++ static_cast<int>(number_to_move) - 1;
+
+ }
+
+@@ -2698,8 +2703,10 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
++ size_t result = 0;
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+- yyg->yy_n_chars, num_to_read );
++ result, num_to_read );
++ yyg->yy_n_chars = static_cast<int>(result);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+@@ -2725,13 +2732,13 @@ static int yy_get_next_buffer (yyscan_t yyscanner)
+
+ if ((int) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+- int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
++ int new_size = yyg->yy_n_chars + static_cast<int>(number_to_move) + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+- yyg->yy_n_chars += number_to_move;
++ yyg->yy_n_chars += static_cast<int>(number_to_move);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+@@ -3158,7 +3165,7 @@ static void yyensure_buffer_stack (yyscan_t yyscanner)
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+- num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
++ num_to_alloc = static_cast<int>(yyg->yy_buffer_stack_max + grow_size);
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+@@ -3196,7 +3203,7 @@ YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
++ b->yy_buf_size = static_cast<int>(size) - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+@@ -3251,7 +3258,7 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yysc
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+- for ( i = 0; i < _yybytes_len; ++i )
++ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
diff --git a/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
new file mode 100755
index 000000000..ba991b709
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
@@ -0,0 +1,454 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Analysis of the AST needed for HLSL generation
+
+#include "compiler/translator/ASTMetadataHLSL.h"
+
+#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Class used to traverse the AST of a function definition, checking if the
+// function uses a gradient, and writing the set of control flow using gradients.
+// It assumes that the analysis has already been made for the function's
+// callees.
+class PullGradient : public TIntermTraverser
+{
+ public:
+ PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)
+ : TIntermTraverser(true, false, true),
+ mMetadataList(metadataList),
+ mMetadata(&(*metadataList)[index]),
+ mIndex(index),
+ mDag(dag)
+ {
+ ASSERT(index < metadataList->size());
+ }
+
+ void traverse(TIntermFunctionDefinition *node)
+ {
+ node->traverse(this);
+ ASSERT(mParents.empty());
+ }
+
+ // Called when a gradient operation or a call to a function using a gradient is found.
+ void onGradient()
+ {
+ mMetadata->mUsesGradient = true;
+ // Mark the latest control flow as using a gradient.
+ if (!mParents.empty())
+ {
+ mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
+ }
+ }
+
+ void visitControlFlow(Visit visit, TIntermNode *node)
+ {
+ if (visit == PreVisit)
+ {
+ mParents.push_back(node);
+ }
+ else if (visit == PostVisit)
+ {
+ ASSERT(mParents.back() == node);
+ mParents.pop_back();
+ // A control flow's using a gradient means its parents are too.
+ if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty())
+ {
+ mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
+ }
+ }
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop) override
+ {
+ visitControlFlow(visit, loop);
+ return true;
+ }
+
+ bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
+ {
+ visitControlFlow(visit, ifElse);
+ return true;
+ }
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override
+ {
+ if (visit == PreVisit)
+ {
+ switch (node->getOp())
+ {
+ case EOpDFdx:
+ case EOpDFdy:
+ onGradient();
+ default:
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ if (visit == PreVisit)
+ {
+ if (node->getOp() == EOpFunctionCall)
+ {
+ if (node->isUserDefined())
+ {
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
+
+ if ((*mMetadataList)[calleeIndex].mUsesGradient) {
+ onGradient();
+ }
+ }
+ else
+ {
+ TString name =
+ TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
+
+ if (name == "texture2D" ||
+ name == "texture2DProj" ||
+ name == "textureCube")
+ {
+ onGradient();
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ MetadataList *mMetadataList;
+ ASTMetadataHLSL *mMetadata;
+ size_t mIndex;
+ const CallDAG &mDag;
+
+ // Contains a stack of the control flow nodes that are parents of the node being
+ // currently visited. It is used to mark control flows using a gradient.
+ std::vector<TIntermNode*> mParents;
+};
+
+// Traverses the AST of a function definition to compute the the discontinuous loops
+// and the if statements containing gradient loops. It assumes that the gradient loops
+// (loops that contain a gradient) have already been computed and that it has already
+// traversed the current function's callees.
+class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
+{
+ public:
+ PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
+ size_t index,
+ const CallDAG &dag)
+ : TIntermTraverser(true, false, true),
+ mMetadataList(metadataList),
+ mMetadata(&(*metadataList)[index]),
+ mIndex(index),
+ mDag(dag)
+ {
+ }
+
+ void traverse(TIntermFunctionDefinition *node)
+ {
+ node->traverse(this);
+ ASSERT(mLoopsAndSwitches.empty());
+ ASSERT(mIfs.empty());
+ }
+
+ // Called when traversing a gradient loop or a call to a function with a
+ // gradient loop in its call graph.
+ void onGradientLoop()
+ {
+ mMetadata->mHasGradientLoopInCallGraph = true;
+ // Mark the latest if as using a discontinuous loop.
+ if (!mIfs.empty())
+ {
+ mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
+ }
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop) override
+ {
+ if (visit == PreVisit)
+ {
+ mLoopsAndSwitches.push_back(loop);
+
+ if (mMetadata->hasGradientInCallGraph(loop))
+ {
+ onGradientLoop();
+ }
+ }
+ else if (visit == PostVisit)
+ {
+ ASSERT(mLoopsAndSwitches.back() == loop);
+ mLoopsAndSwitches.pop_back();
+ }
+
+ return true;
+ }
+
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override
+ {
+ if (visit == PreVisit)
+ {
+ mIfs.push_back(node);
+ }
+ else if (visit == PostVisit)
+ {
+ ASSERT(mIfs.back() == node);
+ mIfs.pop_back();
+ // An if using a discontinuous loop means its parents ifs are also discontinuous.
+ if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
+ {
+ mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
+ }
+ }
+
+ return true;
+ }
+
+ bool visitBranch(Visit visit, TIntermBranch *node) override
+ {
+ if (visit == PreVisit)
+ {
+ switch (node->getFlowOp())
+ {
+ case EOpBreak:
+ {
+ ASSERT(!mLoopsAndSwitches.empty());
+ TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
+ if (loop != nullptr)
+ {
+ mMetadata->mDiscontinuousLoops.insert(loop);
+ }
+ }
+ break;
+ case EOpContinue:
+ {
+ ASSERT(!mLoopsAndSwitches.empty());
+ TIntermLoop *loop = nullptr;
+ size_t i = mLoopsAndSwitches.size();
+ while (loop == nullptr && i > 0)
+ {
+ --i;
+ loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
+ }
+ ASSERT(loop != nullptr);
+ mMetadata->mDiscontinuousLoops.insert(loop);
+ }
+ break;
+ case EOpKill:
+ case EOpReturn:
+ // A return or discard jumps out of all the enclosing loops
+ if (!mLoopsAndSwitches.empty())
+ {
+ for (TIntermNode *intermNode : mLoopsAndSwitches)
+ {
+ TIntermLoop *loop = intermNode->getAsLoopNode();
+ if (loop)
+ {
+ mMetadata->mDiscontinuousLoops.insert(loop);
+ }
+ }
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ return true;
+ }
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ if (visit == PreVisit && node->getOp() == EOpFunctionCall)
+ {
+ if (node->isUserDefined())
+ {
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
+
+ if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
+ {
+ onGradientLoop();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override
+ {
+ if (visit == PreVisit)
+ {
+ mLoopsAndSwitches.push_back(node);
+ }
+ else if (visit == PostVisit)
+ {
+ ASSERT(mLoopsAndSwitches.back() == node);
+ mLoopsAndSwitches.pop_back();
+ }
+ return true;
+ }
+
+ private:
+ MetadataList *mMetadataList;
+ ASTMetadataHLSL *mMetadata;
+ size_t mIndex;
+ const CallDAG &mDag;
+
+ std::vector<TIntermNode*> mLoopsAndSwitches;
+ std::vector<TIntermIfElse *> mIfs;
+};
+
+// Tags all the functions called in a discontinuous loop
+class PushDiscontinuousLoops : public TIntermTraverser
+{
+ public:
+ PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
+ : TIntermTraverser(true, true, true),
+ mMetadataList(metadataList),
+ mMetadata(&(*metadataList)[index]),
+ mIndex(index),
+ mDag(dag),
+ mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
+ {
+ }
+
+ void traverse(TIntermFunctionDefinition *node)
+ {
+ node->traverse(this);
+ ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop) override
+ {
+ bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
+
+ if (visit == PreVisit && isDiscontinuous)
+ {
+ mNestedDiscont++;
+ }
+ else if (visit == PostVisit && isDiscontinuous)
+ {
+ mNestedDiscont--;
+ }
+
+ return true;
+ }
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ switch (node->getOp())
+ {
+ case EOpFunctionCall:
+ if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
+ {
+ size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
+
+ (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ private:
+ MetadataList *mMetadataList;
+ ASTMetadataHLSL *mMetadata;
+ size_t mIndex;
+ const CallDAG &mDag;
+
+ int mNestedDiscont;
+};
+
+}
+
+bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
+{
+ return mControlFlowsContainingGradient.count(node) > 0;
+}
+
+bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
+{
+ return mIfsContainingGradientLoop.count(node) > 0;
+}
+
+MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
+{
+ MetadataList metadataList(callDag.size());
+
+ // Compute all the information related to when gradient operations are used.
+ // We want to know for each function and control flow operation if they have
+ // a gradient operation in their call graph (shortened to "using a gradient"
+ // in the rest of the file).
+ //
+ // This computation is logically split in three steps:
+ // 1 - For each function compute if it uses a gradient in its body, ignoring
+ // calls to other user-defined functions.
+ // 2 - For each function determine if it uses a gradient in its call graph,
+ // using the result of step 1 and the CallDAG to know its callees.
+ // 3 - For each control flow statement of each function, check if it uses a
+ // gradient in the function's body, or if it calls a user-defined function that
+ // uses a gradient.
+ //
+ // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
+ // for leaves first, then going down the tree. This is correct because 1 doesn't
+ // depend on other functions, and 2 and 3 depend only on callees.
+ for (size_t i = 0; i < callDag.size(); i++)
+ {
+ PullGradient pull(&metadataList, i, callDag);
+ pull.traverse(callDag.getRecordFromIndex(i).node);
+ }
+
+ // Compute which loops are discontinuous and which function are called in
+ // these loops. The same way computing gradient usage is a "pull" process,
+ // computing "bing used in a discont. loop" is a push process. However we also
+ // need to know what ifs have a discontinuous loop inside so we do the same type
+ // of callgraph analysis as for the gradient.
+
+ // First compute which loops are discontinuous (no specific order) and pull
+ // the ifs and functions using a gradient loop.
+ for (size_t i = 0; i < callDag.size(); i++)
+ {
+ PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
+ pull.traverse(callDag.getRecordFromIndex(i).node);
+ }
+
+ // Then push the information to callees, either from the a local discontinuous
+ // loop or from the caller being called in a discontinuous loop already
+ for (size_t i = callDag.size(); i-- > 0;)
+ {
+ PushDiscontinuousLoops push(&metadataList, i, callDag);
+ push.traverse(callDag.getRecordFromIndex(i).node);
+ }
+
+ // We create "Lod0" version of functions with the gradient operations replaced
+ // by non-gradient operations so that the D3D compiler is happier with discont
+ // loops.
+ for (auto &metadata : metadataList)
+ {
+ metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
+ }
+
+ return metadataList;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ASTMetadataHLSL.h b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.h
new file mode 100755
index 000000000..f8ed5af4a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.h
@@ -0,0 +1,63 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Defines analyses of the AST needed for HLSL generation
+
+#ifndef COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
+#define COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
+
+#include <set>
+#include <vector>
+
+namespace sh
+{
+
+class CallDAG;
+class TIntermNode;
+class TIntermIfElse;
+class TIntermLoop;
+
+struct ASTMetadataHLSL
+{
+ ASTMetadataHLSL()
+ : mUsesGradient(false),
+ mCalledInDiscontinuousLoop(false),
+ mHasGradientLoopInCallGraph(false),
+ mNeedsLod0(false)
+ {
+ }
+
+ // Here "something uses a gradient" means here that it either contains a
+ // gradient operation, or a call to a function that uses a gradient.
+ bool hasGradientInCallGraph(TIntermLoop *node);
+ bool hasGradientLoop(TIntermIfElse *node);
+
+ // Does the function use a gradient.
+ bool mUsesGradient;
+
+ // Even if usesGradient is true, some control flow might not use a gradient
+ // so we store the set of all gradient-using control flows.
+ std::set<TIntermNode*> mControlFlowsContainingGradient;
+
+ // Remember information about the discontinuous loops and which functions
+ // are called in such loops.
+ bool mCalledInDiscontinuousLoop;
+ bool mHasGradientLoopInCallGraph;
+ std::set<TIntermLoop*> mDiscontinuousLoops;
+ std::set<TIntermIfElse *> mIfsContainingGradientLoop;
+
+ // Will we need to generate a Lod0 version of the function.
+ bool mNeedsLod0;
+};
+
+typedef std::vector<ASTMetadataHLSL> MetadataList;
+
+// Return the AST analysis result, in the order defined by the call DAG
+MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
new file mode 100644
index 000000000..0177fea96
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that rewrites for and while loops by replacing "condition" with
+// "condition && true" to work around condition bug on Intel Mac.
+class AddAndTrueToLoopConditionTraverser : public TIntermTraverser
+{
+ public:
+ AddAndTrueToLoopConditionTraverser() : TIntermTraverser(true, false, false) {}
+
+ bool visitLoop(Visit, TIntermLoop *loop) override
+ {
+ // do-while loop doesn't have this bug.
+ if (loop->getType() != ELoopFor && loop->getType() != ELoopWhile)
+ {
+ return true;
+ }
+
+ // For loop may not have a condition.
+ if (loop->getCondition() == nullptr)
+ {
+ return true;
+ }
+
+ // Constant true.
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, TType(EbtBool));
+
+ // CONDITION && true.
+ TIntermBinary *andOp = new TIntermBinary(EOpLogicalAnd, loop->getCondition(), trueValue);
+ loop->setCondition(andOp);
+
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+void AddAndTrueToLoopCondition(TIntermNode *root)
+{
+ AddAndTrueToLoopConditionTraverser traverser;
+ root->traverse(&traverser);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
new file mode 100644
index 000000000..34debe0ed
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddAndTrueToLoopCondition.h
@@ -0,0 +1,20 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Rewrite condition in for and while loops to work around driver bug on Intel Mac.
+
+#ifndef COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+#define COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void AddAndTrueToLoopCondition(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ADDANDTRUETOLOOPCONDITION_H_
diff --git a/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
new file mode 100755
index 000000000..5767aea2b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.cpp: Add default return statements to functions that do not end in a
+// return.
+//
+
+#include "compiler/translator/AddDefaultReturnStatements.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class AddDefaultReturnStatementsTraverser : private TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root)
+ {
+ AddDefaultReturnStatementsTraverser separateInit;
+ root->traverse(&separateInit);
+ separateInit.updateTree();
+ }
+
+ private:
+ AddDefaultReturnStatementsTraverser() : TIntermTraverser(true, false, false) {}
+
+ static bool IsFunctionWithoutReturnStatement(TIntermFunctionDefinition *node, TType *returnType)
+ {
+ *returnType = node->getType();
+ if (node->getType().getBasicType() == EbtVoid)
+ {
+ return false;
+ }
+
+ TIntermBlock *bodyNode = node->getBody();
+ TIntermBranch *returnNode = bodyNode->getSequence()->back()->getAsBranchNode();
+ if (returnNode != nullptr && returnNode->getFlowOp() == EOpReturn)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool visitFunctionDefinition(Visit, TIntermFunctionDefinition *node) override
+ {
+ TType returnType;
+ if (IsFunctionWithoutReturnStatement(node, &returnType))
+ {
+ TIntermBranch *branch =
+ new TIntermBranch(EOpReturn, TIntermTyped::CreateZero(returnType));
+
+ TIntermBlock *bodyNode = node->getBody();
+ bodyNode->getSequence()->push_back(branch);
+
+ return false;
+ }
+
+ return true;
+ }
+};
+} // anonymous namespace
+
+void AddDefaultReturnStatements(TIntermNode *node)
+{
+ AddDefaultReturnStatementsTraverser::Apply(node);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.h b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.h
new file mode 100755
index 000000000..d765a7ae4
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/AddDefaultReturnStatements.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AddDefaultReturnStatements.h: Add default return statements to functions that do not end in a
+// return.
+//
+
+#ifndef COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void AddDefaultReturnStatements(TIntermNode *node);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ADDDEFAULTRETURNSTATEMENTS_H_
diff --git a/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
new file mode 100755
index 000000000..766f700ee
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in
+// function definitions, prototypes, and call sites.
+
+#include "compiler/translator/ArrayReturnValueToOutParameter.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void CopyAggregateChildren(TIntermAggregate *from, TIntermAggregate *to)
+{
+ const TIntermSequence *fromSequence = from->getSequence();
+ for (size_t ii = 0; ii < fromSequence->size(); ++ii)
+ {
+ to->getSequence()->push_back(fromSequence->at(ii));
+ }
+}
+
+TIntermSymbol *CreateReturnValueSymbol(const TType &type)
+{
+ TIntermSymbol *node = new TIntermSymbol(0, "angle_return", type);
+ node->setInternal(true);
+ return node;
+}
+
+TIntermSymbol *CreateReturnValueOutSymbol(const TType &type)
+{
+ TType outType(type);
+ outType.setQualifier(EvqOut);
+ return CreateReturnValueSymbol(outType);
+}
+
+TIntermAggregate *CreateReplacementCall(TIntermAggregate *originalCall, TIntermTyped *returnValueTarget)
+{
+ TIntermAggregate *replacementCall = new TIntermAggregate(EOpFunctionCall);
+ replacementCall->setType(TType(EbtVoid));
+ replacementCall->setUserDefined();
+ *replacementCall->getFunctionSymbolInfo() = *originalCall->getFunctionSymbolInfo();
+ replacementCall->setLine(originalCall->getLine());
+ TIntermSequence *replacementParameters = replacementCall->getSequence();
+ TIntermSequence *originalParameters = originalCall->getSequence();
+ for (auto &param : *originalParameters)
+ {
+ replacementParameters->push_back(param);
+ }
+ replacementParameters->push_back(returnValueTarget);
+ return replacementCall;
+}
+
+class ArrayReturnValueToOutParameterTraverser : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermNode *root, unsigned int *temporaryIndex);
+ private:
+ ArrayReturnValueToOutParameterTraverser();
+
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBranch(Visit visit, TIntermBranch *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+
+ bool mInFunctionWithArrayReturnValue;
+};
+
+void ArrayReturnValueToOutParameterTraverser::apply(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ ArrayReturnValueToOutParameterTraverser arrayReturnValueToOutParam;
+ arrayReturnValueToOutParam.useTemporaryIndex(temporaryIndex);
+ root->traverse(&arrayReturnValueToOutParam);
+ arrayReturnValueToOutParam.updateTree();
+}
+
+ArrayReturnValueToOutParameterTraverser::ArrayReturnValueToOutParameterTraverser()
+ : TIntermTraverser(true, false, true),
+ mInFunctionWithArrayReturnValue(false)
+{
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitFunctionDefinition(
+ Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ if (node->isArray() && visit == PreVisit)
+ {
+ // Replace the parameters child node of the function definition with another node
+ // that has the out parameter added.
+ // Also set the function to return void.
+
+ TIntermAggregate *params = node->getFunctionParameters();
+ ASSERT(params != nullptr && params->getOp() == EOpParameters);
+
+ TIntermAggregate *replacementParams = new TIntermAggregate;
+ replacementParams->setOp(EOpParameters);
+ CopyAggregateChildren(params, replacementParams);
+ replacementParams->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
+ replacementParams->setLine(params->getLine());
+
+ queueReplacementWithParent(node, params, replacementParams, OriginalNode::IS_DROPPED);
+
+ node->setType(TType(EbtVoid));
+
+ mInFunctionWithArrayReturnValue = true;
+ }
+ if (visit == PostVisit)
+ {
+ // This isn't conditional on node->isArray() since the type has already been changed on
+ // PreVisit.
+ mInFunctionWithArrayReturnValue = false;
+ }
+ return true;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (visit == PreVisit)
+ {
+ if (node->isArray())
+ {
+ if (node->getOp() == EOpPrototype)
+ {
+ // Replace the whole prototype node with another node that has the out parameter added.
+ TIntermAggregate *replacement = new TIntermAggregate;
+ replacement->setOp(EOpPrototype);
+ CopyAggregateChildren(node, replacement);
+ replacement->getSequence()->push_back(CreateReturnValueOutSymbol(node->getType()));
+ replacement->setUserDefined();
+ *replacement->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
+ replacement->setLine(node->getLine());
+ replacement->setType(TType(EbtVoid));
+
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ }
+ else if (node->getOp() == EOpFunctionCall)
+ {
+ // Handle call sites where the returned array is not assigned.
+ // Examples where f() is a function returning an array:
+ // 1. f();
+ // 2. another_array == f();
+ // 3. another_function(f());
+ // 4. return f();
+ // Cases 2 to 4 are already converted to simpler cases by SeparateExpressionsReturningArrays, so we
+ // only need to worry about the case where a function call returning an array forms an expression by
+ // itself.
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ if (parentBlock)
+ {
+ nextTemporaryIndex();
+ TIntermSequence replacements;
+ replacements.push_back(createTempDeclaration(node->getType()));
+ TIntermSymbol *returnSymbol = createTempSymbol(node->getType());
+ replacements.push_back(CreateReplacementCall(node, returnSymbol));
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitBranch(Visit visit, TIntermBranch *node)
+{
+ if (mInFunctionWithArrayReturnValue && node->getFlowOp() == EOpReturn)
+ {
+ // Instead of returning a value, assign to the out parameter and then return.
+ TIntermSequence replacements;
+
+ TIntermTyped *expression = node->getExpression();
+ ASSERT(expression != nullptr);
+ TIntermSymbol *returnValueSymbol = CreateReturnValueSymbol(expression->getType());
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, returnValueSymbol, expression);
+ replacementAssignment->setLine(expression->getLine());
+ replacements.push_back(replacementAssignment);
+
+ TIntermBranch *replacementBranch = new TIntermBranch(EOpReturn, nullptr);
+ replacementBranch->setLine(node->getLine());
+ replacements.push_back(replacementBranch);
+
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(), node, replacements));
+ }
+ return false;
+}
+
+bool ArrayReturnValueToOutParameterTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->getOp() == EOpAssign && node->getLeft()->isArray())
+ {
+ TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
+ if (rightAgg != nullptr && rightAgg->getOp() == EOpFunctionCall && rightAgg->isUserDefined())
+ {
+ TIntermAggregate *replacementCall = CreateReplacementCall(rightAgg, node->getLeft());
+ queueReplacement(node, replacementCall, OriginalNode::IS_DROPPED);
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ ArrayReturnValueToOutParameterTraverser::apply(root, temporaryIndex);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h
new file mode 100755
index 000000000..e030f6ffe
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ArrayReturnValueToOutParameter.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The ArrayReturnValueToOutParameter function changes return values of an array type to out parameters in
+// function definitions, prototypes and call sites.
+
+#ifndef COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
+#define COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_
diff --git a/gfx/angle/src/compiler/translator/BaseTypes.h b/gfx/angle/src/compiler/translator/BaseTypes.h
new file mode 100755
index 000000000..e050c88da
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BaseTypes.h
@@ -0,0 +1,781 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_BASETYPES_H_
+#define COMPILER_TRANSLATOR_BASETYPES_H_
+
+#include <algorithm>
+#include <array>
+
+#include "common/debug.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+//
+// Precision qualifiers
+//
+enum TPrecision
+{
+ // These need to be kept sorted
+ EbpUndefined,
+ EbpLow,
+ EbpMedium,
+ EbpHigh,
+
+ // end of list
+ EbpLast
+};
+
+inline const char* getPrecisionString(TPrecision p)
+{
+ switch(p)
+ {
+ case EbpHigh: return "highp";
+ case EbpMedium: return "mediump";
+ case EbpLow: return "lowp";
+ default: return "mediump"; // Safest fallback
+ }
+}
+
+//
+// Basic type. Arrays, vectors, etc., are orthogonal to this.
+//
+enum TBasicType
+{
+ EbtVoid,
+ EbtFloat,
+ EbtInt,
+ EbtUInt,
+ EbtBool,
+ EbtGVec4, // non type: represents vec4, ivec4, and uvec4
+ EbtGenType, // non type: represents float, vec2, vec3, and vec4
+ EbtGenIType, // non type: represents int, ivec2, ivec3, and ivec4
+ EbtGenUType, // non type: represents uint, uvec2, uvec3, and uvec4
+ EbtGenBType, // non type: represents bool, bvec2, bvec3, and bvec4
+ EbtVec, // non type: represents vec2, vec3, and vec4
+ EbtIVec, // non type: represents ivec2, ivec3, and ivec4
+ EbtUVec, // non type: represents uvec2, uvec3, and uvec4
+ EbtBVec, // non type: represents bvec2, bvec3, and bvec4
+ EbtGuardSamplerBegin, // non type: see implementation of IsSampler()
+ EbtSampler2D,
+ EbtSampler3D,
+ EbtSamplerCube,
+ EbtSampler2DArray,
+ EbtSamplerExternalOES, // Only valid if OES_EGL_image_external exists.
+ EbtSampler2DRect, // Only valid if GL_ARB_texture_rectangle exists.
+ EbtISampler2D,
+ EbtISampler3D,
+ EbtISamplerCube,
+ EbtISampler2DArray,
+ EbtUSampler2D,
+ EbtUSampler3D,
+ EbtUSamplerCube,
+ EbtUSampler2DArray,
+ EbtSampler2DShadow,
+ EbtSamplerCubeShadow,
+ EbtSampler2DArrayShadow,
+ EbtGuardSamplerEnd, // non type: see implementation of IsSampler()
+ EbtGSampler2D, // non type: represents sampler2D, isampler2D, and usampler2D
+ EbtGSampler3D, // non type: represents sampler3D, isampler3D, and usampler3D
+ EbtGSamplerCube, // non type: represents samplerCube, isamplerCube, and usamplerCube
+ EbtGSampler2DArray, // non type: represents sampler2DArray, isampler2DArray, and
+ // usampler2DArray
+
+ // images
+ EbtGuardImageBegin,
+ EbtImage2D,
+ EbtIImage2D,
+ EbtUImage2D,
+ EbtImage3D,
+ EbtIImage3D,
+ EbtUImage3D,
+ EbtImage2DArray,
+ EbtIImage2DArray,
+ EbtUImage2DArray,
+ EbtImageCube,
+ EbtIImageCube,
+ EbtUImageCube,
+ EbtGuardImageEnd,
+
+ EbtGuardGImageBegin,
+ EbtGImage2D, // non type: represents image2D, uimage2D, iimage2D
+ EbtGImage3D, // non type: represents image3D, uimage3D, iimage3D
+ EbtGImage2DArray, // non type: represents image2DArray, uimage2DArray, iimage2DArray
+ EbtGImageCube, // non type: represents imageCube, uimageCube, iimageCube
+ EbtGuardGImageEnd,
+
+ EbtStruct,
+ EbtInterfaceBlock,
+ EbtAddress, // should be deprecated??
+
+ // end of list
+ EbtLast
+};
+
+inline TBasicType convertGImageToFloatImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtImage2D;
+ case EbtGImage3D:
+ return EbtImage3D;
+ case EbtGImage2DArray:
+ return EbtImage2DArray;
+ case EbtGImageCube:
+ return EbtImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+inline TBasicType convertGImageToIntImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtIImage2D;
+ case EbtGImage3D:
+ return EbtIImage3D;
+ case EbtGImage2DArray:
+ return EbtIImage2DArray;
+ case EbtGImageCube:
+ return EbtIImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+inline TBasicType convertGImageToUnsignedImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtGImage2D:
+ return EbtUImage2D;
+ case EbtGImage3D:
+ return EbtUImage3D;
+ case EbtGImage2DArray:
+ return EbtUImage2DArray;
+ case EbtGImageCube:
+ return EbtUImageCube;
+ default:
+ UNREACHABLE();
+ }
+ return EbtLast;
+}
+
+const char* getBasicString(TBasicType t);
+
+inline bool IsSampler(TBasicType type)
+{
+ return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd;
+}
+
+inline bool IsImage(TBasicType type)
+{
+ return type > EbtGuardImageBegin && type < EbtGuardImageEnd;
+}
+
+inline bool IsGImage(TBasicType type)
+{
+ return type > EbtGuardGImageBegin && type < EbtGuardGImageEnd;
+}
+
+inline bool IsOpaqueType(TBasicType type)
+{
+ // TODO (mradev): add atomic types as opaque.
+ return IsSampler(type) || IsImage(type);
+}
+
+inline bool IsIntegerSampler(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ return true;
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsFloatImage(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtImage2D:
+ case EbtImage3D:
+ case EbtImage2DArray:
+ case EbtImageCube:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool IsIntegerImage(TBasicType type)
+{
+
+ switch (type)
+ {
+ case EbtIImage2D:
+ case EbtIImage3D:
+ case EbtIImage2DArray:
+ case EbtIImageCube:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool IsUnsignedImage(TBasicType type)
+{
+
+ switch (type)
+ {
+ case EbtUImage2D:
+ case EbtUImage3D:
+ case EbtUImage2DArray:
+ case EbtUImageCube:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+inline bool IsSampler2D(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DRect:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ return true;
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCube:
+ case EbtSamplerCubeShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsSamplerCube(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ return true;
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsSampler3D(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ return true;
+ case EbtSampler2D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ case EbtISampler2D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsSamplerArray(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DArrayShadow:
+ return true;
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DRect:
+ case EbtSamplerExternalOES:
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCube:
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsShadowSampler(TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return true;
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSamplerExternalOES:
+ case EbtSampler2DRect:
+ case EbtSampler2DArray:
+ return false;
+ default:
+ assert(!IsSampler(type));
+ }
+
+ return false;
+}
+
+inline bool IsInteger(TBasicType type)
+{
+ return type == EbtInt || type == EbtUInt;
+}
+
+inline bool SupportsPrecision(TBasicType type)
+{
+ return type == EbtFloat || type == EbtInt || type == EbtUInt || IsOpaqueType(type);
+}
+
+//
+// Qualifiers and built-ins. These are mainly used to see what can be read
+// or written, and by the machine dependent translator to know which registers
+// to allocate variables in. Since built-ins tend to go to different registers
+// than varying or uniform, it makes sense they are peers, not sub-classes.
+//
+enum TQualifier
+{
+ EvqTemporary, // For temporaries (within a function), read/write
+ EvqGlobal, // For globals read/write
+ EvqConst, // User defined constants and non-output parameters in functions
+ EvqAttribute, // Readonly
+ EvqVaryingIn, // readonly, fragment shaders only
+ EvqVaryingOut, // vertex shaders only read/write
+ EvqUniform, // Readonly, vertex and fragment
+
+ EvqVertexIn, // Vertex shader input
+ EvqFragmentOut, // Fragment shader output
+ EvqVertexOut, // Vertex shader output
+ EvqFragmentIn, // Fragment shader input
+
+ // parameters
+ EvqIn,
+ EvqOut,
+ EvqInOut,
+ EvqConstReadOnly,
+
+ // built-ins read by vertex shader
+ EvqInstanceID,
+ EvqVertexID,
+
+ // built-ins written by vertex shader
+ EvqPosition,
+ EvqPointSize,
+
+ // built-ins read by fragment shader
+ EvqFragCoord,
+ EvqFrontFacing,
+ EvqPointCoord,
+
+ // built-ins written by fragment shader
+ EvqFragColor,
+ EvqFragData,
+
+ EvqFragDepth, // gl_FragDepth for ESSL300.
+ EvqFragDepthEXT, // gl_FragDepthEXT for ESSL100, EXT_frag_depth.
+
+ EvqSecondaryFragColorEXT, // EXT_blend_func_extended
+ EvqSecondaryFragDataEXT, // EXT_blend_func_extended
+
+ // built-ins written by the shader_framebuffer_fetch extension(s)
+ EvqLastFragColor,
+ EvqLastFragData,
+
+ // GLSL ES 3.0 vertex output and fragment input
+ EvqSmooth, // Incomplete qualifier, smooth is the default
+ EvqFlat, // Incomplete qualifier
+ EvqCentroid, // Incomplete qualifier
+ EvqSmoothOut,
+ EvqFlatOut,
+ EvqCentroidOut, // Implies smooth
+ EvqSmoothIn,
+ EvqFlatIn,
+ EvqCentroidIn, // Implies smooth
+
+ // GLSL ES 3.1 compute shader special variables
+ EvqComputeIn,
+ EvqNumWorkGroups,
+ EvqWorkGroupSize,
+ EvqWorkGroupID,
+ EvqLocalInvocationID,
+ EvqGlobalInvocationID,
+ EvqLocalInvocationIndex,
+
+ // GLSL ES 3.1 memory qualifiers
+ EvqReadOnly,
+ EvqWriteOnly,
+ EvqCoherent,
+ EvqRestrict,
+ EvqVolatile,
+
+ // end of list
+ EvqLast
+};
+
+inline bool IsQualifierUnspecified(TQualifier qualifier)
+{
+ return (qualifier == EvqTemporary || qualifier == EvqGlobal);
+}
+
+enum TLayoutImageInternalFormat
+{
+ EiifUnspecified,
+ EiifRGBA32F,
+ EiifRGBA16F,
+ EiifR32F,
+ EiifRGBA32UI,
+ EiifRGBA16UI,
+ EiifRGBA8UI,
+ EiifR32UI,
+ EiifRGBA32I,
+ EiifRGBA16I,
+ EiifRGBA8I,
+ EiifR32I,
+ EiifRGBA8,
+ EiifRGBA8_SNORM
+};
+
+enum TLayoutMatrixPacking
+{
+ EmpUnspecified,
+ EmpRowMajor,
+ EmpColumnMajor
+};
+
+enum TLayoutBlockStorage
+{
+ EbsUnspecified,
+ EbsShared,
+ EbsPacked,
+ EbsStd140
+};
+
+struct TLayoutQualifier
+{
+ int location;
+ unsigned int locationsSpecified;
+ TLayoutMatrixPacking matrixPacking;
+ TLayoutBlockStorage blockStorage;
+
+ // Compute shader layout qualifiers.
+ sh::WorkGroupSize localSize;
+
+ // Image format layout qualifier
+ TLayoutImageInternalFormat imageInternalFormat;
+
+ static TLayoutQualifier create()
+ {
+ TLayoutQualifier layoutQualifier;
+
+ layoutQualifier.location = -1;
+ layoutQualifier.locationsSpecified = 0;
+ layoutQualifier.matrixPacking = EmpUnspecified;
+ layoutQualifier.blockStorage = EbsUnspecified;
+
+ layoutQualifier.localSize.fill(-1);
+
+ layoutQualifier.imageInternalFormat = EiifUnspecified;
+ return layoutQualifier;
+ }
+
+ bool isEmpty() const
+ {
+ return location == -1 && matrixPacking == EmpUnspecified &&
+ blockStorage == EbsUnspecified && !localSize.isAnyValueSet() &&
+ imageInternalFormat == EiifUnspecified;
+ }
+
+ bool isCombinationValid() const
+ {
+ bool workSizeSpecified = localSize.isAnyValueSet();
+ bool otherLayoutQualifiersSpecified =
+ (location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified ||
+ imageInternalFormat != EiifUnspecified);
+
+ // we can have either the work group size specified, or the other layout qualifiers
+ return !(workSizeSpecified && otherLayoutQualifiersSpecified);
+ }
+
+ bool isLocalSizeEqual(const sh::WorkGroupSize &localSizeIn) const
+ {
+ return localSize.isWorkGroupSizeMatching(localSizeIn);
+ }
+};
+
+struct TMemoryQualifier
+{
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ // An image can be qualified as both readonly and writeonly. It still can be can be used with
+ // imageSize().
+ bool readonly;
+ bool writeonly;
+ bool coherent;
+
+ // restrict and volatile are reserved keywords in C/C++
+ bool restrictQualifier;
+ bool volatileQualifier;
+ static TMemoryQualifier create()
+ {
+ TMemoryQualifier memoryQualifier;
+
+ memoryQualifier.readonly = false;
+ memoryQualifier.writeonly = false;
+ memoryQualifier.coherent = false;
+ memoryQualifier.restrictQualifier = false;
+ memoryQualifier.volatileQualifier = false;
+
+ return memoryQualifier;
+ }
+
+ bool isEmpty()
+ {
+ return !readonly && !writeonly && !coherent && !restrictQualifier && !volatileQualifier;
+ }
+};
+
+inline const char *getWorkGroupSizeString(size_t dimension)
+{
+ switch (dimension)
+ {
+ case 0u:
+ return "local_size_x";
+ case 1u:
+ return "local_size_y";
+ case 2u:
+ return "local_size_z";
+ default:
+ UNREACHABLE();
+ return "dimension out of bounds";
+ }
+}
+
+//
+// This is just for debug print out, carried along with the definitions above.
+//
+inline const char* getQualifierString(TQualifier q)
+{
+ // clang-format off
+ switch(q)
+ {
+ case EvqTemporary: return "Temporary";
+ case EvqGlobal: return "Global";
+ case EvqConst: return "const";
+ case EvqAttribute: return "attribute";
+ case EvqVaryingIn: return "varying";
+ case EvqVaryingOut: return "varying";
+ case EvqUniform: return "uniform";
+ case EvqVertexIn: return "in";
+ case EvqFragmentOut: return "out";
+ case EvqVertexOut: return "out";
+ case EvqFragmentIn: return "in";
+ case EvqIn: return "in";
+ case EvqOut: return "out";
+ case EvqInOut: return "inout";
+ case EvqConstReadOnly: return "const";
+ case EvqInstanceID: return "InstanceID";
+ case EvqVertexID: return "VertexID";
+ case EvqPosition: return "Position";
+ case EvqPointSize: return "PointSize";
+ case EvqFragCoord: return "FragCoord";
+ case EvqFrontFacing: return "FrontFacing";
+ case EvqPointCoord: return "PointCoord";
+ case EvqFragColor: return "FragColor";
+ case EvqFragData: return "FragData";
+ case EvqFragDepthEXT: return "FragDepth";
+ case EvqFragDepth: return "FragDepth";
+ case EvqSecondaryFragColorEXT: return "SecondaryFragColorEXT";
+ case EvqSecondaryFragDataEXT: return "SecondaryFragDataEXT";
+ case EvqLastFragColor: return "LastFragColor";
+ case EvqLastFragData: return "LastFragData";
+ case EvqSmoothOut: return "smooth out";
+ case EvqCentroidOut: return "smooth centroid out";
+ case EvqFlatOut: return "flat out";
+ case EvqSmoothIn: return "smooth in";
+ case EvqFlatIn: return "flat in";
+ case EvqCentroidIn: return "smooth centroid in";
+ case EvqCentroid: return "centroid";
+ case EvqFlat: return "flat";
+ case EvqSmooth: return "smooth";
+ case EvqComputeIn: return "in";
+ case EvqNumWorkGroups: return "NumWorkGroups";
+ case EvqWorkGroupSize: return "WorkGroupSize";
+ case EvqWorkGroupID: return "WorkGroupID";
+ case EvqLocalInvocationID: return "LocalInvocationID";
+ case EvqGlobalInvocationID: return "GlobalInvocationID";
+ case EvqLocalInvocationIndex: return "LocalInvocationIndex";
+ case EvqReadOnly: return "readonly";
+ case EvqWriteOnly: return "writeonly";
+ default: UNREACHABLE(); return "unknown qualifier";
+ }
+ // clang-format on
+}
+
+inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq)
+{
+ switch (mpq)
+ {
+ case EmpUnspecified: return "mp_unspecified";
+ case EmpRowMajor: return "row_major";
+ case EmpColumnMajor: return "column_major";
+ default: UNREACHABLE(); return "unknown matrix packing";
+ }
+}
+
+inline const char* getBlockStorageString(TLayoutBlockStorage bsq)
+{
+ switch (bsq)
+ {
+ case EbsUnspecified: return "bs_unspecified";
+ case EbsShared: return "shared";
+ case EbsPacked: return "packed";
+ case EbsStd140: return "std140";
+ default: UNREACHABLE(); return "unknown block storage";
+ }
+}
+
+inline const char *getImageInternalFormatString(TLayoutImageInternalFormat iifq)
+{
+ switch (iifq)
+ {
+ case EiifRGBA32F:
+ return "rgba32f";
+ case EiifRGBA16F:
+ return "rgba16f";
+ case EiifR32F:
+ return "r32f";
+ case EiifRGBA32UI:
+ return "rgba32ui";
+ case EiifRGBA16UI:
+ return "rgba16ui";
+ case EiifRGBA8UI:
+ return "rgba8ui";
+ case EiifR32UI:
+ return "r32ui";
+ case EiifRGBA32I:
+ return "rgba32i";
+ case EiifRGBA16I:
+ return "rgba16i";
+ case EiifRGBA8I:
+ return "rgba8i";
+ case EiifR32I:
+ return "r32i";
+ case EiifRGBA8:
+ return "rgba8";
+ case EiifRGBA8_SNORM:
+ return "rgba8_snorm";
+ default:
+ UNREACHABLE();
+ return "unknown internal image format";
+ }
+}
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BASETYPES_H_
diff --git a/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
new file mode 100644
index 000000000..018e72cd1
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#include "BreakVariableAliasingInInnerLoops.h"
+
+#include "compiler/translator/IntermNode.h"
+
+// A HLSL compiler developer gave us more details on the root cause and the workaround needed:
+// The root problem is that if the HLSL compiler is applying aliasing information even on
+// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment
+// that comes from a series of assignments, possibly with swizzled or ternary operators with
+// known conditionals, where the source is before the loop.
+// So, a workaround is to add a +0 term to variables the first time they are assigned to in
+// an inner loop (if they are declared in an outside scope, otherwise there is no need).
+// This will break the aliasing chain.
+
+// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because
+// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure
+// assignment don't need a workaround.
+
+namespace sh
+{
+
+namespace
+{
+
+class AliasingBreaker : public TIntermTraverser
+{
+ public:
+ AliasingBreaker() : TIntermTraverser(true, false, true) {}
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *binary)
+ {
+ if (visit != PreVisit)
+ {
+ return false;
+ }
+
+ if (mLoopLevel < 2 || !binary->isAssignment())
+ {
+ return true;
+ }
+
+ TIntermTyped *B = binary->getRight();
+ TType type = B->getType();
+
+ if (!type.isScalar() && !type.isVector() && !type.isMatrix())
+ {
+ return true;
+ }
+
+ if (type.isArray() || IsSampler(type.getBasicType()))
+ {
+ return true;
+ }
+
+ // We have a scalar / vector / matrix assignment with loop depth 2.
+ // Transform it from
+ // A = B
+ // to
+ // A = (B + typeof<B>(0));
+
+ TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type));
+ bPlusZero->setLine(B->getLine());
+
+ binary->replaceChildNode(B, bPlusZero);
+
+ return true;
+ }
+
+ bool visitLoop(Visit visit, TIntermLoop *loop)
+ {
+ if (visit == PreVisit)
+ {
+ mLoopLevel++;
+ }
+ else
+ {
+ ASSERT(mLoopLevel > 0);
+ mLoopLevel--;
+ }
+
+ return true;
+ }
+
+ private:
+ int mLoopLevel = 0;
+};
+
+} // anonymous namespace
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root)
+{
+ AliasingBreaker breaker;
+ root->traverse(&breaker);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
new file mode 100644
index 000000000..b1d906f91
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BreakVariableAliasingInInnerLoops.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend
+// may record a variable as aliasing another. Sometimes the alias information gets garbled
+// so we work around this issue by breaking the aliasing chain in inner loops.
+
+#ifndef COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+#define COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void BreakVariableAliasingInInnerLoops(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BREAKVARIABLEALIASINGININNERLOOPS_H_
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
new file mode 100755
index 000000000..152251fde
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.cpp
@@ -0,0 +1,249 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "angle_gl.h"
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/Cache.h"
+
+namespace sh
+{
+
+class BuiltInFunctionEmulator::BuiltInFunctionEmulationMarker : public TIntermTraverser
+{
+ public:
+ BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator &emulator)
+ : TIntermTraverser(true, false, false),
+ mEmulator(emulator)
+ {
+ }
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override
+ {
+ if (visit == PreVisit)
+ {
+ bool needToEmulate = mEmulator.SetFunctionCalled(node->getOp(), node->getOperand()->getType());
+ if (needToEmulate)
+ node->setUseEmulatedFunction();
+ }
+ return true;
+ }
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ if (visit == PreVisit)
+ {
+ // Here we handle all the built-in functions instead of the ones we
+ // currently identified as problematic.
+ switch (node->getOp())
+ {
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ case EOpVectorEqual:
+ case EOpVectorNotEqual:
+ case EOpMod:
+ case EOpPow:
+ case EOpAtan:
+ case EOpMin:
+ case EOpMax:
+ case EOpClamp:
+ case EOpMix:
+ case EOpStep:
+ case EOpSmoothStep:
+ case EOpDistance:
+ case EOpDot:
+ case EOpCross:
+ case EOpFaceForward:
+ case EOpReflect:
+ case EOpRefract:
+ case EOpOuterProduct:
+ case EOpMul:
+ break;
+ default:
+ return true;
+ }
+ const TIntermSequence &sequence = *(node->getSequence());
+ bool needToEmulate = false;
+ // Right now we only handle built-in functions with two or three parameters.
+ if (sequence.size() == 2)
+ {
+ TIntermTyped *param1 = sequence[0]->getAsTyped();
+ TIntermTyped *param2 = sequence[1]->getAsTyped();
+ if (!param1 || !param2)
+ return true;
+ needToEmulate = mEmulator.SetFunctionCalled(
+ node->getOp(), param1->getType(), param2->getType());
+ }
+ else if (sequence.size() == 3)
+ {
+ TIntermTyped *param1 = sequence[0]->getAsTyped();
+ TIntermTyped *param2 = sequence[1]->getAsTyped();
+ TIntermTyped *param3 = sequence[2]->getAsTyped();
+ if (!param1 || !param2 || !param3)
+ return true;
+ needToEmulate = mEmulator.SetFunctionCalled(
+ node->getOp(), param1->getType(), param2->getType(), param3->getType());
+ }
+ else
+ {
+ return true;
+ }
+
+ if (needToEmulate)
+ node->setUseEmulatedFunction();
+ }
+ return true;
+ }
+
+ private:
+ BuiltInFunctionEmulator &mEmulator;
+};
+
+BuiltInFunctionEmulator::BuiltInFunctionEmulator()
+{}
+
+void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param,
+ const char *emulatedFunctionDefinition)
+{
+ mEmulatedFunctions[FunctionId(op, param)] = std::string(emulatedFunctionDefinition);
+}
+
+void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
+ const char *emulatedFunctionDefinition)
+{
+ mEmulatedFunctions[FunctionId(op, param1, param2)] = std::string(emulatedFunctionDefinition);
+}
+
+void BuiltInFunctionEmulator::addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
+ const TType *param3, const char *emulatedFunctionDefinition)
+{
+ mEmulatedFunctions[FunctionId(op, param1, param2, param3)] = std::string(emulatedFunctionDefinition);
+}
+
+bool BuiltInFunctionEmulator::IsOutputEmpty() const
+{
+ return (mFunctions.size() == 0);
+}
+
+void BuiltInFunctionEmulator::OutputEmulatedFunctions(TInfoSinkBase &out) const
+{
+ for (size_t i = 0; i < mFunctions.size(); ++i)
+ {
+ out << mEmulatedFunctions.find(mFunctions[i])->second << "\n\n";
+ }
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType &param)
+{
+ return SetFunctionCalled(FunctionId(op, &param));
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType &param1, const TType &param2)
+{
+ return SetFunctionCalled(FunctionId(op, &param1, &param2));
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op,
+ const TType &param1, const TType &param2, const TType &param3)
+{
+ return SetFunctionCalled(FunctionId(op, &param1, &param2, &param3));
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(const FunctionId &functionId)
+{
+ if (mEmulatedFunctions.find(functionId) != mEmulatedFunctions.end())
+ {
+ for (size_t i = 0; i < mFunctions.size(); ++i)
+ {
+ if (mFunctions[i] == functionId)
+ return true;
+ }
+ // Copy the functionId if it needs to be stored, to make sure that the TType pointers inside
+ // remain valid and constant.
+ mFunctions.push_back(functionId.getCopy());
+ return true;
+ }
+ return false;
+}
+
+void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(TIntermNode *root)
+{
+ ASSERT(root);
+
+ if (mEmulatedFunctions.empty())
+ return;
+
+ BuiltInFunctionEmulationMarker marker(*this);
+ root->traverse(&marker);
+}
+
+void BuiltInFunctionEmulator::Cleanup()
+{
+ mFunctions.clear();
+}
+
+//static
+TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
+ const TString &name)
+{
+ ASSERT(name[name.length() - 1] == '(');
+ return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
+}
+
+BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param)
+ : mOp(op),
+ mParam1(param),
+ mParam2(TCache::getType(EbtVoid)),
+ mParam3(TCache::getType(EbtVoid))
+{
+}
+
+BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op, const TType *param1, const TType *param2)
+ : mOp(op),
+ mParam1(param1),
+ mParam2(param2),
+ mParam3(TCache::getType(EbtVoid))
+{
+}
+
+BuiltInFunctionEmulator::FunctionId::FunctionId(TOperator op,
+ const TType *param1, const TType *param2, const TType *param3)
+ : mOp(op),
+ mParam1(param1),
+ mParam2(param2),
+ mParam3(param3)
+{
+}
+
+bool BuiltInFunctionEmulator::FunctionId::operator==(const BuiltInFunctionEmulator::FunctionId &other) const
+{
+ return (mOp == other.mOp &&
+ *mParam1 == *other.mParam1 &&
+ *mParam2 == *other.mParam2 &&
+ *mParam3 == *other.mParam3);
+}
+
+bool BuiltInFunctionEmulator::FunctionId::operator<(const BuiltInFunctionEmulator::FunctionId &other) const
+{
+ if (mOp != other.mOp)
+ return mOp < other.mOp;
+ if (*mParam1 != *other.mParam1)
+ return *mParam1 < *other.mParam1;
+ if (*mParam2 != *other.mParam2)
+ return *mParam2 < *other.mParam2;
+ if (*mParam3 != *other.mParam3)
+ return *mParam3 < *other.mParam3;
+ return false; // all fields are equal
+}
+
+BuiltInFunctionEmulator::FunctionId BuiltInFunctionEmulator::FunctionId::getCopy() const
+{
+ return FunctionId(mOp, new TType(*mParam1), new TType(*mParam2), new TType(*mParam3));
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h
new file mode 100755
index 000000000..db5c202d2
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulator.h
@@ -0,0 +1,90 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
+#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
+
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+//
+// This class decides which built-in functions need to be replaced with the
+// emulated ones.
+// It can be used to work around driver bugs or implement functions that are
+// not natively implemented on a specific platform.
+//
+class BuiltInFunctionEmulator
+{
+ public:
+ BuiltInFunctionEmulator();
+
+ void MarkBuiltInFunctionsForEmulation(TIntermNode *root);
+
+ void Cleanup();
+
+ // "name(" becomes "webgl_name_emu(".
+ static TString GetEmulatedFunctionName(const TString &name);
+
+ bool IsOutputEmpty() const;
+
+ // Output function emulation definition. This should be before any other
+ // shader source.
+ void OutputEmulatedFunctions(TInfoSinkBase &out) const;
+
+ // Add functions that need to be emulated.
+ void addEmulatedFunction(TOperator op, const TType *param, const char *emulatedFunctionDefinition);
+ void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2,
+ const char *emulatedFunctionDefinition);
+ void addEmulatedFunction(TOperator op, const TType *param1, const TType *param2, const TType *param3,
+ const char *emulatedFunctionDefinition);
+
+ private:
+ class BuiltInFunctionEmulationMarker;
+
+ // Records that a function is called by the shader and might need to be
+ // emulated. If the function is not in mEmulatedFunctions, this becomes a
+ // no-op. Returns true if the function call needs to be replaced with an
+ // emulated one.
+ bool SetFunctionCalled(TOperator op, const TType &param);
+ bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2);
+ bool SetFunctionCalled(TOperator op, const TType &param1, const TType &param2, const TType &param3);
+
+ class FunctionId {
+ public:
+ FunctionId(TOperator op, const TType *param);
+ FunctionId(TOperator op, const TType *param1, const TType *param2);
+ FunctionId(TOperator op, const TType *param1, const TType *param2, const TType *param3);
+
+ bool operator==(const FunctionId &other) const;
+ bool operator<(const FunctionId &other) const;
+
+ FunctionId getCopy() const;
+ private:
+ TOperator mOp;
+
+ // The memory that these TType objects use is freed by PoolAllocator. The BuiltInFunctionEmulator's lifetime
+ // can extend until after the memory pool is freed, but that's not an issue since this class never destructs
+ // these objects.
+ const TType *mParam1;
+ const TType *mParam2;
+ const TType *mParam3;
+ };
+
+ bool SetFunctionCalled(const FunctionId &functionId);
+
+ // Map from function id to emulated function definition
+ std::map<FunctionId, std::string> mEmulatedFunctions;
+
+ // Called function ids
+ std::vector<FunctionId> mFunctions;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
new file mode 100755
index 000000000..74397fb7f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp
@@ -0,0 +1,255 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "angle_gl.h"
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "compiler/translator/BuiltInFunctionEmulatorGLSL.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/VersionGLSL.h"
+
+namespace sh
+{
+
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType)
+{
+ if (shaderType == GL_VERTEX_SHADER)
+ {
+ const TType *int1 = TCache::getType(EbtInt);
+ emu->addEmulatedFunction(EOpAbs, int1, "int webgl_abs_emu(int x) { return x * sign(x); }");
+ }
+}
+
+void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion)
+{
+ // isnan() is supported since GLSL 1.3.
+ if (targetGLSLVersion < GLSL_VERSION_130)
+ return;
+
+ const TType *float1 = TCache::getType(EbtFloat);
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *float3 = TCache::getType(EbtFloat, 3);
+ const TType *float4 = TCache::getType(EbtFloat, 4);
+
+ // !(x > 0.0 || x < 0.0 || x == 0.0) will be optimized and always equal to false.
+ emu->addEmulatedFunction(
+ EOpIsNan, float1,
+ "bool webgl_isnan_emu(float x) { return (x > 0.0 || x < 0.0) ? false : x != 0.0; }");
+ emu->addEmulatedFunction(
+ EOpIsNan, float2,
+ "bvec2 webgl_isnan_emu(vec2 x)\n"
+ "{\n"
+ " bvec2 isnan;\n"
+ " for (int i = 0; i < 2; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+ emu->addEmulatedFunction(
+ EOpIsNan, float3,
+ "bvec3 webgl_isnan_emu(vec3 x)\n"
+ "{\n"
+ " bvec3 isnan;\n"
+ " for (int i = 0; i < 3; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+ emu->addEmulatedFunction(
+ EOpIsNan, float4,
+ "bvec4 webgl_isnan_emu(vec4 x)\n"
+ "{\n"
+ " bvec4 isnan;\n"
+ " for (int i = 0; i < 4; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+}
+
+// Emulate built-in functions missing from GLSL 1.30 and higher
+void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
+ int targetGLSLVersion)
+{
+ // Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10)
+ if (targetGLSLVersion < GLSL_VERSION_410)
+ {
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *uint1 = TCache::getType(EbtUInt);
+
+ // clang-format off
+ emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
+ "uint webgl_packUnorm2x16_emu(vec2 v)\n"
+ "{\n"
+ " int x = int(round(clamp(v.x, 0.0, 1.0) * 65535.0));\n"
+ " int y = int(round(clamp(v.y, 0.0, 1.0) * 65535.0));\n"
+ " return uint((y << 16) | (x & 0xFFFF));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
+ "vec2 webgl_unpackUnorm2x16_emu(uint u)\n"
+ "{\n"
+ " float x = float(u & 0xFFFFu) / 65535.0;\n"
+ " float y = float(u >> 16) / 65535.0;\n"
+ " return vec2(x, y);\n"
+ "}\n");
+ // clang-format on
+ }
+
+ // Emulate packSnorm2x16, packHalf2x16, unpackSnorm2x16, and unpackHalf2x16 (GLSL 4.20)
+ // by using floatBitsToInt, floatBitsToUint, intBitsToFloat, and uintBitsToFloat (GLSL 3.30).
+ if (targetGLSLVersion >= GLSL_VERSION_330 && targetGLSLVersion < GLSL_VERSION_420)
+ {
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *uint1 = TCache::getType(EbtUInt);
+
+ // clang-format off
+ emu->addEmulatedFunction(EOpPackSnorm2x16, float2,
+ "uint webgl_packSnorm2x16_emu(vec2 v)\n"
+ "{\n"
+ " #if defined(GL_ARB_shading_language_packing)\n"
+ " return packSnorm2x16(v);\n"
+ " #else\n"
+ " int x = int(round(clamp(v.x, -1.0, 1.0) * 32767.0));\n"
+ " int y = int(round(clamp(v.y, -1.0, 1.0) * 32767.0));\n"
+ " return uint((y << 16) | (x & 0xFFFF));\n"
+ " #endif\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1,
+ "#if !defined(GL_ARB_shading_language_packing)\n"
+ " float webgl_fromSnorm(uint x)\n"
+ " {\n"
+ " int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n"
+ " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
+ " }\n"
+ "#endif\n"
+ "\n"
+ "vec2 webgl_unpackSnorm2x16_emu(uint u)\n"
+ "{\n"
+ " #if defined(GL_ARB_shading_language_packing)\n"
+ " return unpackSnorm2x16(u);\n"
+ " #else\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u;\n"
+ " return vec2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n"
+ " #endif\n"
+ "}\n");
+ // Functions uint webgl_f32tof16(float val) and float webgl_f16tof32(uint val) are
+ // based on the OpenGL redbook Appendix Session "Floating-Point Formats Used in OpenGL".
+ emu->addEmulatedFunction(EOpPackHalf2x16, float2,
+ "#if !defined(GL_ARB_shading_language_packing)\n"
+ " uint webgl_f32tof16(float val)\n"
+ " {\n"
+ " uint f32 = floatBitsToUint(val);\n"
+ " uint f16 = 0u;\n"
+ " uint sign = (f32 >> 16) & 0x8000u;\n"
+ " int exponent = int((f32 >> 23) & 0xFFu) - 127;\n"
+ " uint mantissa = f32 & 0x007FFFFFu;\n"
+ " if (exponent == 128)\n"
+ " {\n"
+ " // Infinity or NaN\n"
+ " // NaN bits that are masked out by 0x3FF get discarded.\n"
+ " // This can turn some NaNs to infinity, but this is allowed by the spec.\n"
+ " f16 = sign | (0x1Fu << 10);\n"
+ " f16 |= (mantissa & 0x3FFu);\n"
+ " }\n"
+ " else if (exponent > 15)\n"
+ " {\n"
+ " // Overflow - flush to Infinity\n"
+ " f16 = sign | (0x1Fu << 10);\n"
+ " }\n"
+ " else if (exponent > -15)\n"
+ " {\n"
+ " // Representable value\n"
+ " exponent += 15;\n"
+ " mantissa >>= 13;\n"
+ " f16 = sign | uint(exponent << 10) | mantissa;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " f16 = sign;\n"
+ " }\n"
+ " return f16;\n"
+ " }\n"
+ "#endif\n"
+ "\n"
+ "uint webgl_packHalf2x16_emu(vec2 v)\n"
+ "{\n"
+ " #if defined(GL_ARB_shading_language_packing)\n"
+ " return packHalf2x16(v);\n"
+ " #else\n"
+ " uint x = webgl_f32tof16(v.x);\n"
+ " uint y = webgl_f32tof16(v.y);\n"
+ " return (y << 16) | x;\n"
+ " #endif\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1,
+ "#if !defined(GL_ARB_shading_language_packing)\n"
+ " float webgl_f16tof32(uint val)\n"
+ " {\n"
+ " uint sign = (val & 0x8000u) << 16;\n"
+ " int exponent = int((val & 0x7C00u) >> 10);\n"
+ " uint mantissa = val & 0x03FFu;\n"
+ " float f32 = 0.0;\n"
+ " if(exponent == 0)\n"
+ " {\n"
+ " if (mantissa != 0u)\n"
+ " {\n"
+ " const float scale = 1.0 / (1 << 24);\n"
+ " f32 = scale * mantissa;\n"
+ " }\n"
+ " }\n"
+ " else if (exponent == 31)\n"
+ " {\n"
+ " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " exponent -= 15;\n"
+ " float scale;\n"
+ " if(exponent < 0)\n"
+ " {\n"
+ " // The negative unary operator is buggy on OSX.\n"
+ " // Work around this by using abs instead.\n"
+ " scale = 1.0 / (1 << abs(exponent));\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " scale = 1 << exponent;\n"
+ " }\n"
+ " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n"
+ " f32 = scale * decimal;\n"
+ " }\n"
+ "\n"
+ " if (sign != 0u)\n"
+ " {\n"
+ " f32 = -f32;\n"
+ " }\n"
+ "\n"
+ " return f32;\n"
+ " }\n"
+ "#endif\n"
+ "\n"
+ "vec2 webgl_unpackHalf2x16_emu(uint u)\n"
+ "{\n"
+ " #if defined(GL_ARB_shading_language_packing)\n"
+ " return unpackHalf2x16(u);\n"
+ " #else\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u & 0xFFFFu;\n"
+ " return vec2(webgl_f16tof32(x), webgl_f16tof32(y));\n"
+ " #endif\n"
+ "}\n");
+ // clang-format on
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
new file mode 100755
index 000000000..09fc85b0d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h
@@ -0,0 +1,35 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
+#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+class BuiltInFunctionEmulator;
+
+//
+// This works around bug in Intel Mac drivers.
+//
+void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ sh::GLenum shaderType);
+
+//
+// This works around isnan() bug in Intel Mac drivers
+//
+void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion);
+
+//
+// This function is emulating built-in functions missing from GLSL 1.30 and higher.
+//
+void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, sh::GLenum shaderType,
+ int targetGLSLVersion);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
new file mode 100755
index 000000000..c51062e1e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp
@@ -0,0 +1,499 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "angle_gl.h"
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/VersionGLSL.h"
+
+namespace sh
+{
+
+void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion)
+{
+ if (targetGLSLVersion < GLSL_VERSION_130)
+ return;
+
+ TType *float1 = new TType(EbtFloat);
+ TType *float2 = new TType(EbtFloat, 2);
+ TType *float3 = new TType(EbtFloat, 3);
+ TType *float4 = new TType(EbtFloat, 4);
+
+ emu->addEmulatedFunction(EOpIsNan, float1,
+ "bool webgl_isnan_emu(float x)\n"
+ "{\n"
+ " return (x > 0.0 || x < 0.0) ? false : x != 0.0;\n"
+ "}\n"
+ "\n");
+
+ emu->addEmulatedFunction(EOpIsNan, float2,
+ "bool2 webgl_isnan_emu(float2 x)\n"
+ "{\n"
+ " bool2 isnan;\n"
+ " for (int i = 0; i < 2; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpIsNan, float3,
+ "bool3 webgl_isnan_emu(float3 x)\n"
+ "{\n"
+ " bool3 isnan;\n"
+ " for (int i = 0; i < 3; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpIsNan, float4,
+ "bool4 webgl_isnan_emu(float4 x)\n"
+ "{\n"
+ " bool4 isnan;\n"
+ " for (int i = 0; i < 4; i++)\n"
+ " {\n"
+ " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n"
+ " }\n"
+ " return isnan;\n"
+ "}\n");
+}
+
+void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu)
+{
+ TType *float1 = new TType(EbtFloat);
+ TType *float2 = new TType(EbtFloat, 2);
+ TType *float3 = new TType(EbtFloat, 3);
+ TType *float4 = new TType(EbtFloat, 4);
+
+ emu->addEmulatedFunction(EOpMod, float1, float1,
+ "float webgl_mod_emu(float x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float2, float2,
+ "float2 webgl_mod_emu(float2 x, float2 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float2, float1,
+ "float2 webgl_mod_emu(float2 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float3, float3,
+ "float3 webgl_mod_emu(float3 x, float3 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float3, float1,
+ "float3 webgl_mod_emu(float3 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float4, float4,
+ "float4 webgl_mod_emu(float4 x, float4 y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpMod, float4, float1,
+ "float4 webgl_mod_emu(float4 x, float y)\n"
+ "{\n"
+ " return x - y * floor(x / y);\n"
+ "}\n"
+ "\n");
+
+ emu->addEmulatedFunction(EOpFaceForward, float1, float1, float1,
+ "float webgl_faceforward_emu(float N, float I, float Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpFaceForward, float2, float2, float2,
+ "float2 webgl_faceforward_emu(float2 N, float2 I, float2 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpFaceForward, float3, float3, float3,
+ "float3 webgl_faceforward_emu(float3 N, float3 I, float3 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n");
+ emu->addEmulatedFunction(EOpFaceForward, float4, float4, float4,
+ "float4 webgl_faceforward_emu(float4 N, float4 I, float4 Nref)\n"
+ "{\n"
+ " if(dot(Nref, I) >= 0)\n"
+ " {\n"
+ " return -N;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " return N;\n"
+ " }\n"
+ "}\n"
+ "\n");
+
+ emu->addEmulatedFunction(EOpAtan, float1, float1,
+ "float webgl_atan_emu(float y, float x)\n"
+ "{\n"
+ " if(x == 0 && y == 0) x = 1;\n" // Avoid producing a NaN
+ " return atan2(y, x);\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtan, float2, float2,
+ "float2 webgl_atan_emu(float2 y, float2 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " return float2(atan2(y[0], x[0]), atan2(y[1], x[1]));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtan, float3, float3,
+ "float3 webgl_atan_emu(float3 y, float3 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " return float3(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtan, float4, float4,
+ "float4 webgl_atan_emu(float4 y, float4 x)\n"
+ "{\n"
+ " if(x[0] == 0 && y[0] == 0) x[0] = 1;\n"
+ " if(x[1] == 0 && y[1] == 0) x[1] = 1;\n"
+ " if(x[2] == 0 && y[2] == 0) x[2] = 1;\n"
+ " if(x[3] == 0 && y[3] == 0) x[3] = 1;\n"
+ " return float4(atan2(y[0], x[0]), atan2(y[1], x[1]), atan2(y[2], x[2]), atan2(y[3], x[3]));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpAsinh, float1,
+ "float webgl_asinh_emu(in float x) {\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAsinh, float2,
+ "float2 webgl_asinh_emu(in float2 x) {\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAsinh, float3,
+ "float3 webgl_asinh_emu(in float3 x) {\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAsinh, float4,
+ "float4 webgl_asinh_emu(in float4 x) {\n"
+ " return log(x + sqrt(pow(x, 2.0) + 1.0));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpAcosh, float1,
+ "float webgl_acosh_emu(in float x) {\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAcosh, float2,
+ "float2 webgl_acosh_emu(in float2 x) {\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAcosh, float3,
+ "float3 webgl_acosh_emu(in float3 x) {\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAcosh, float4,
+ "float4 webgl_acosh_emu(in float4 x) {\n"
+ " return log(x + sqrt(x + 1.0) * sqrt(x - 1.0));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpAtanh, float1,
+ "float webgl_atanh_emu(in float x) {\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtanh, float2,
+ "float2 webgl_atanh_emu(in float2 x) {\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtanh, float3,
+ "float3 webgl_atanh_emu(in float3 x) {\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpAtanh, float4,
+ "float4 webgl_atanh_emu(in float4 x) {\n"
+ " return 0.5 * log((1.0 + x) / (1.0 - x));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpRoundEven, float1,
+ "float webgl_roundEven_emu(in float x) {\n"
+ " return (frac(x) == 0.5 && trunc(x) % 2.0 == 0.0) ? trunc(x) : round(x);\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpRoundEven, float2,
+ "float2 webgl_roundEven_emu(in float2 x) {\n"
+ " float2 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " return v;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpRoundEven, float3,
+ "float3 webgl_roundEven_emu(in float3 x) {\n"
+ " float3 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
+ " return v;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpRoundEven, float4,
+ "float4 webgl_roundEven_emu(in float4 x) {\n"
+ " float4 v;\n"
+ " v[0] = (frac(x[0]) == 0.5 && trunc(x[0]) % 2.0 == 0.0) ? trunc(x[0]) : round(x[0]);\n"
+ " v[1] = (frac(x[1]) == 0.5 && trunc(x[1]) % 2.0 == 0.0) ? trunc(x[1]) : round(x[1]);\n"
+ " v[2] = (frac(x[2]) == 0.5 && trunc(x[2]) % 2.0 == 0.0) ? trunc(x[2]) : round(x[2]);\n"
+ " v[3] = (frac(x[3]) == 0.5 && trunc(x[3]) % 2.0 == 0.0) ? trunc(x[3]) : round(x[3]);\n"
+ " return v;\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpPackSnorm2x16, float2,
+ "int webgl_toSnorm(in float x) {\n"
+ " return int(round(clamp(x, -1.0, 1.0) * 32767.0));\n"
+ "}\n"
+ "\n"
+ "uint webgl_packSnorm2x16_emu(in float2 v) {\n"
+ " int x = webgl_toSnorm(v.x);\n"
+ " int y = webgl_toSnorm(v.y);\n"
+ " return (asuint(y) << 16) | (asuint(x) & 0xffffu);\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpPackUnorm2x16, float2,
+ "uint webgl_toUnorm(in float x) {\n"
+ " return uint(round(clamp(x, 0.0, 1.0) * 65535.0));\n"
+ "}\n"
+ "\n"
+ "uint webgl_packUnorm2x16_emu(in float2 v) {\n"
+ " uint x = webgl_toUnorm(v.x);\n"
+ " uint y = webgl_toUnorm(v.y);\n"
+ " return (y << 16) | x;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpPackHalf2x16, float2,
+ "uint webgl_packHalf2x16_emu(in float2 v) {\n"
+ " uint x = f32tof16(v.x);\n"
+ " uint y = f32tof16(v.y);\n"
+ " return (y << 16) | x;\n"
+ "}\n");
+
+ TType *uint1 = new TType(EbtUInt);
+
+ emu->addEmulatedFunction(EOpUnpackSnorm2x16, uint1,
+ "float webgl_fromSnorm(in uint x) {\n"
+ " int xi = asint(x & 0x7fffu) - asint(x & 0x8000u);\n"
+ " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n"
+ "}\n"
+ "\n"
+ "float2 webgl_unpackSnorm2x16_emu(in uint u) {\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u;\n"
+ " return float2(webgl_fromSnorm(x), webgl_fromSnorm(y));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpUnpackUnorm2x16, uint1,
+ "float webgl_fromUnorm(in uint x) {\n"
+ " return float(x) / 65535.0;\n"
+ "}\n"
+ "\n"
+ "float2 webgl_unpackUnorm2x16_emu(in uint u) {\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u & 0xffffu;\n"
+ " return float2(webgl_fromUnorm(x), webgl_fromUnorm(y));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpUnpackHalf2x16, uint1,
+ "float2 webgl_unpackHalf2x16_emu(in uint u) {\n"
+ " uint y = (u >> 16);\n"
+ " uint x = u & 0xffffu;\n"
+ " return float2(f16tof32(x), f16tof32(y));\n"
+ "}\n");
+
+ // The matrix resulting from outer product needs to be transposed
+ // (matrices are stored as transposed to simplify element access in HLSL).
+ // So the function should return transpose(c * r) where c is a column vector
+ // and r is a row vector. This can be simplified by using the following
+ // formula:
+ // transpose(c * r) = transpose(r) * transpose(c)
+ // transpose(r) and transpose(c) are in a sense free, since to get the
+ // transpose of r, we simply can build a column matrix out of the original
+ // vector instead of a row matrix.
+ emu->addEmulatedFunction(EOpOuterProduct, float2, float2,
+ "float2x2 webgl_outerProduct_emu(in float2 c, in float2 r) {\n"
+ " return mul(float2x1(r), float1x2(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float3, float3,
+ "float3x3 webgl_outerProduct_emu(in float3 c, in float3 r) {\n"
+ " return mul(float3x1(r), float1x3(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float4, float4,
+ "float4x4 webgl_outerProduct_emu(in float4 c, in float4 r) {\n"
+ " return mul(float4x1(r), float1x4(c));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpOuterProduct, float3, float2,
+ "float2x3 webgl_outerProduct_emu(in float3 c, in float2 r) {\n"
+ " return mul(float2x1(r), float1x3(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float2, float3,
+ "float3x2 webgl_outerProduct_emu(in float2 c, in float3 r) {\n"
+ " return mul(float3x1(r), float1x2(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float4, float2,
+ "float2x4 webgl_outerProduct_emu(in float4 c, in float2 r) {\n"
+ " return mul(float2x1(r), float1x4(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float2, float4,
+ "float4x2 webgl_outerProduct_emu(in float2 c, in float4 r) {\n"
+ " return mul(float4x1(r), float1x2(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float4, float3,
+ "float3x4 webgl_outerProduct_emu(in float4 c, in float3 r) {\n"
+ " return mul(float3x1(r), float1x4(c));\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpOuterProduct, float3, float4,
+ "float4x3 webgl_outerProduct_emu(in float3 c, in float4 r) {\n"
+ " return mul(float4x1(r), float1x3(c));\n"
+ "}\n");
+
+ TType *mat2 = new TType(EbtFloat, 2, 2);
+ TType *mat3 = new TType(EbtFloat, 3, 3);
+ TType *mat4 = new TType(EbtFloat, 4, 4);
+
+ // Remember here that the parameter matrix is actually the transpose
+ // of the matrix that we're trying to invert, and the resulting matrix
+ // should also be the transpose of the inverse.
+
+ // When accessing the parameter matrix with m[a][b] it can be thought of so
+ // that a is the column and b is the row of the matrix that we're inverting.
+
+ // We calculate the inverse as the adjugate matrix divided by the
+ // determinant of the matrix being inverted. However, as the result needs
+ // to be transposed, we actually use of the transpose of the adjugate matrix
+ // which happens to be the cofactor matrix. That's stored in "cof".
+
+ // We don't need to care about divide-by-zero since results are undefined
+ // for singular or poorly-conditioned matrices.
+
+ emu->addEmulatedFunction(EOpInverse, mat2,
+ "float2x2 webgl_inverse_emu(in float2x2 m) {\n"
+ " float2x2 cof = { m[1][1], -m[0][1], -m[1][0], m[0][0] };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n");
+
+ // cofAB is the cofactor for column A and row B.
+
+ emu->addEmulatedFunction(EOpInverse, mat3,
+ "float3x3 webgl_inverse_emu(in float3x3 m) {\n"
+ " float cof00 = m[1][1] * m[2][2] - m[2][1] * m[1][2];\n"
+ " float cof01 = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);\n"
+ " float cof02 = m[1][0] * m[2][1] - m[2][0] * m[1][1];\n"
+ " float cof10 = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);\n"
+ " float cof11 = m[0][0] * m[2][2] - m[2][0] * m[0][2];\n"
+ " float cof12 = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);\n"
+ " float cof20 = m[0][1] * m[1][2] - m[1][1] * m[0][2];\n"
+ " float cof21 = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);\n"
+ " float cof22 = m[0][0] * m[1][1] - m[1][0] * m[0][1];\n"
+ " float3x3 cof = { cof00, cof10, cof20, cof01, cof11, cof21, cof02, cof12, cof22 };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n");
+
+ emu->addEmulatedFunction(EOpInverse, mat4,
+ "float4x4 webgl_inverse_emu(in float4x4 m) {\n"
+ " float cof00 = m[1][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3]"
+ " - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] - m[3][1] * m[2][2] * m[1][3];\n"
+ " float cof01 = -(m[1][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[1][3] + m[3][0] * m[1][2] * m[2][3]"
+ " - m[1][0] * m[3][2] * m[2][3] - m[2][0] * m[1][2] * m[3][3] - m[3][0] * m[2][2] * m[1][3]);\n"
+ " float cof02 = m[1][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3]"
+ " - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] - m[3][0] * m[2][1] * m[1][3];\n"
+ " float cof03 = -(m[1][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[1][2] + m[3][0] * m[1][1] * m[2][2]"
+ " - m[1][0] * m[3][1] * m[2][2] - m[2][0] * m[1][1] * m[3][2] - m[3][0] * m[2][1] * m[1][2]);\n"
+ " float cof10 = -(m[0][1] * m[2][2] * m[3][3] + m[2][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[2][3]"
+ " - m[0][1] * m[3][2] * m[2][3] - m[2][1] * m[0][2] * m[3][3] - m[3][1] * m[2][2] * m[0][3]);\n"
+ " float cof11 = m[0][0] * m[2][2] * m[3][3] + m[2][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[2][3]"
+ " - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] - m[3][0] * m[2][2] * m[0][3];\n"
+ " float cof12 = -(m[0][0] * m[2][1] * m[3][3] + m[2][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[2][3]"
+ " - m[0][0] * m[3][1] * m[2][3] - m[2][0] * m[0][1] * m[3][3] - m[3][0] * m[2][1] * m[0][3]);\n"
+ " float cof13 = m[0][0] * m[2][1] * m[3][2] + m[2][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[2][2]"
+ " - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] - m[3][0] * m[2][1] * m[0][2];\n"
+ " float cof20 = m[0][1] * m[1][2] * m[3][3] + m[1][1] * m[3][2] * m[0][3] + m[3][1] * m[0][2] * m[1][3]"
+ " - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] - m[3][1] * m[1][2] * m[0][3];\n"
+ " float cof21 = -(m[0][0] * m[1][2] * m[3][3] + m[1][0] * m[3][2] * m[0][3] + m[3][0] * m[0][2] * m[1][3]"
+ " - m[0][0] * m[3][2] * m[1][3] - m[1][0] * m[0][2] * m[3][3] - m[3][0] * m[1][2] * m[0][3]);\n"
+ " float cof22 = m[0][0] * m[1][1] * m[3][3] + m[1][0] * m[3][1] * m[0][3] + m[3][0] * m[0][1] * m[1][3]"
+ " - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] - m[3][0] * m[1][1] * m[0][3];\n"
+ " float cof23 = -(m[0][0] * m[1][1] * m[3][2] + m[1][0] * m[3][1] * m[0][2] + m[3][0] * m[0][1] * m[1][2]"
+ " - m[0][0] * m[3][1] * m[1][2] - m[1][0] * m[0][1] * m[3][2] - m[3][0] * m[1][1] * m[0][2]);\n"
+ " float cof30 = -(m[0][1] * m[1][2] * m[2][3] + m[1][1] * m[2][2] * m[0][3] + m[2][1] * m[0][2] * m[1][3]"
+ " - m[0][1] * m[2][2] * m[1][3] - m[1][1] * m[0][2] * m[2][3] - m[2][1] * m[1][2] * m[0][3]);\n"
+ " float cof31 = m[0][0] * m[1][2] * m[2][3] + m[1][0] * m[2][2] * m[0][3] + m[2][0] * m[0][2] * m[1][3]"
+ " - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] - m[2][0] * m[1][2] * m[0][3];\n"
+ " float cof32 = -(m[0][0] * m[1][1] * m[2][3] + m[1][0] * m[2][1] * m[0][3] + m[2][0] * m[0][1] * m[1][3]"
+ " - m[0][0] * m[2][1] * m[1][3] - m[1][0] * m[0][1] * m[2][3] - m[2][0] * m[1][1] * m[0][3]);\n"
+ " float cof33 = m[0][0] * m[1][1] * m[2][2] + m[1][0] * m[2][1] * m[0][2] + m[2][0] * m[0][1] * m[1][2]"
+ " - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] - m[2][0] * m[1][1] * m[0][2];\n"
+ " float4x4 cof = { cof00, cof10, cof20, cof30, cof01, cof11, cof21, cof31,"
+ " cof02, cof12, cof22, cof32, cof03, cof13, cof23, cof33 };\n"
+ " return cof / determinant(transpose(m));\n"
+ "}\n");
+
+ TType *bool1 = new TType(EbtBool);
+ TType *bool2 = new TType(EbtBool, 2);
+ TType *bool3 = new TType(EbtBool, 3);
+ TType *bool4 = new TType(EbtBool, 4);
+
+ // Emulate ESSL3 variant of mix that takes last argument as boolean vector.
+ // genType mix (genType x, genType y, genBType a): Selects which vector each returned component comes from.
+ // For a component of 'a' that is false, the corresponding component of 'x' is returned.For a component of 'a' that is true,
+ // the corresponding component of 'y' is returned.
+ emu->addEmulatedFunction(EOpMix, float1, float1, bool1,
+ "float webgl_mix_emu(float x, float y, bool a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpMix, float2, float2, bool2,
+ "float2 webgl_mix_emu(float2 x, float2 y, bool2 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpMix, float3, float3, bool3,
+ "float3 webgl_mix_emu(float3 x, float3 y, bool3 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n");
+ emu->addEmulatedFunction(EOpMix, float4, float4, bool4,
+ "float4 webgl_mix_emu(float4 x, float4 y, bool4 a)\n"
+ "{\n"
+ " return a ? y : x;\n"
+ "}\n");
+
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h
new file mode 100755
index 000000000..48da73f58
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h
@@ -0,0 +1,27 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
+#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+class BuiltInFunctionEmulator;
+
+void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu);
+
+//
+// This works around isnan() bug on some Intel drivers.
+//
+void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu,
+ int targetGLSLVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/Cache.cpp b/gfx/angle/src/compiler/translator/Cache.cpp
new file mode 100755
index 000000000..094e3ff57
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Cache.cpp
@@ -0,0 +1,104 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Cache.cpp: Implements a cache for various commonly created objects.
+
+#include <limits>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "compiler/translator/Cache.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class TScopedAllocator : angle::NonCopyable
+{
+ public:
+ TScopedAllocator(TPoolAllocator *allocator)
+ : mPreviousAllocator(GetGlobalPoolAllocator())
+ {
+ SetGlobalPoolAllocator(allocator);
+ }
+ ~TScopedAllocator()
+ {
+ SetGlobalPoolAllocator(mPreviousAllocator);
+ }
+
+ private:
+ TPoolAllocator *mPreviousAllocator;
+};
+
+} // namespace
+
+TCache::TypeKey::TypeKey(TBasicType basicType,
+ TPrecision precision,
+ TQualifier qualifier,
+ unsigned char primarySize,
+ unsigned char secondarySize)
+{
+ static_assert(sizeof(components) <= sizeof(value),
+ "TypeKey::value is too small");
+
+ const size_t MaxEnumValue = std::numeric_limits<EnumComponentType>::max();
+
+ // TODO: change to static_assert() once we deprecate MSVC 2013 support
+ ASSERT(MaxEnumValue >= EbtLast &&
+ MaxEnumValue >= EbpLast &&
+ MaxEnumValue >= EvqLast &&
+ "TypeKey::EnumComponentType is too small");
+
+ value = 0;
+ components.basicType = static_cast<EnumComponentType>(basicType);
+ components.precision = static_cast<EnumComponentType>(precision);
+ components.qualifier = static_cast<EnumComponentType>(qualifier);
+ components.primarySize = primarySize;
+ components.secondarySize = secondarySize;
+}
+
+TCache *TCache::sCache = nullptr;
+
+void TCache::initialize()
+{
+ if (sCache == nullptr)
+ {
+ sCache = new TCache();
+ }
+}
+
+void TCache::destroy()
+{
+ SafeDelete(sCache);
+}
+
+const TType *TCache::getType(TBasicType basicType,
+ TPrecision precision,
+ TQualifier qualifier,
+ unsigned char primarySize,
+ unsigned char secondarySize)
+{
+ TypeKey key(basicType, precision, qualifier,
+ primarySize, secondarySize);
+ auto it = sCache->mTypes.find(key);
+ if (it != sCache->mTypes.end())
+ {
+ return it->second;
+ }
+
+ TScopedAllocator scopedAllocator(&sCache->mAllocator);
+
+ TType *type = new TType(basicType, precision, qualifier,
+ primarySize, secondarySize);
+ type->realize();
+ sCache->mTypes.insert(std::make_pair(key, type));
+
+ return type;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Cache.h b/gfx/angle/src/compiler/translator/Cache.h
new file mode 100755
index 000000000..9a5607f24
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Cache.h
@@ -0,0 +1,95 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Cache.h: Implements a cache for various commonly created objects.
+
+#ifndef COMPILER_TRANSLATOR_CACHE_H_
+#define COMPILER_TRANSLATOR_CACHE_H_
+
+#include <stdint.h>
+#include <string.h>
+#include <map>
+
+#include "compiler/translator/Types.h"
+#include "compiler/translator/PoolAlloc.h"
+
+namespace sh
+{
+
+class TCache
+{
+ public:
+
+ static void initialize();
+ static void destroy();
+
+ static const TType *getType(TBasicType basicType,
+ TPrecision precision)
+ {
+ return getType(basicType, precision, EvqTemporary,
+ 1, 1);
+ }
+ static const TType *getType(TBasicType basicType,
+ unsigned char primarySize = 1,
+ unsigned char secondarySize = 1)
+ {
+ return getType(basicType, EbpUndefined, EvqGlobal,
+ primarySize, secondarySize);
+ }
+ static const TType *getType(TBasicType basicType,
+ TQualifier qualifier,
+ unsigned char primarySize = 1,
+ unsigned char secondarySize = 1)
+ {
+ return getType(basicType, EbpUndefined, qualifier,
+ primarySize, secondarySize);
+ }
+ static const TType *getType(TBasicType basicType,
+ TPrecision precision,
+ TQualifier qualifier,
+ unsigned char primarySize,
+ unsigned char secondarySize);
+
+ private:
+ TCache()
+ {
+ }
+
+ union TypeKey
+ {
+ TypeKey(TBasicType basicType,
+ TPrecision precision,
+ TQualifier qualifier,
+ unsigned char primarySize,
+ unsigned char secondarySize);
+
+ typedef uint8_t EnumComponentType;
+ struct
+ {
+ EnumComponentType basicType;
+ EnumComponentType precision;
+ EnumComponentType qualifier;
+ unsigned char primarySize;
+ unsigned char secondarySize;
+ } components;
+ uint64_t value;
+
+ bool operator < (const TypeKey &other) const
+ {
+ return value < other.value;
+ }
+ };
+ typedef std::map<TypeKey, const TType*> TypeMap;
+
+ TypeMap mTypes;
+ TPoolAllocator mAllocator;
+
+ static TCache *sCache;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CACHE_H_
diff --git a/gfx/angle/src/compiler/translator/CallDAG.cpp b/gfx/angle/src/compiler/translator/CallDAG.cpp
new file mode 100755
index 000000000..00aa833b8
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/CallDAG.cpp
@@ -0,0 +1,348 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// CallDAG.h: Implements a call graph DAG of functions to be re-used accross
+// analyses, allows to efficiently traverse the functions in topological
+// order.
+
+#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/InfoSink.h"
+
+namespace sh
+{
+
+// The CallDAGCreator does all the processing required to create the CallDAG
+// structure so that the latter contains only the necessary variables.
+class CallDAG::CallDAGCreator : public TIntermTraverser
+{
+ public:
+ CallDAGCreator(TInfoSinkBase *info)
+ : TIntermTraverser(true, false, true),
+ mCreationInfo(info),
+ mCurrentFunction(nullptr),
+ mCurrentIndex(0)
+ {
+ }
+
+ InitResult assignIndices()
+ {
+ int skipped = 0;
+ for (auto &it : mFunctions)
+ {
+ // Skip unimplemented functions
+ if (it.second.node)
+ {
+ InitResult result = assignIndicesInternal(&it.second);
+ if (result != INITDAG_SUCCESS)
+ {
+ *mCreationInfo << "\n";
+ return result;
+ }
+ }
+ else
+ {
+ skipped++;
+ }
+ }
+
+ ASSERT(mFunctions.size() == mCurrentIndex + skipped);
+ return INITDAG_SUCCESS;
+ }
+
+ void fillDataStructures(std::vector<Record> *records, std::map<int, int> *idToIndex)
+ {
+ ASSERT(records->empty());
+ ASSERT(idToIndex->empty());
+
+ records->resize(mCurrentIndex);
+
+ for (auto &it : mFunctions)
+ {
+ CreatorFunctionData &data = it.second;
+ // Skip unimplemented functions
+ if (!data.node)
+ {
+ continue;
+ }
+ ASSERT(data.index < records->size());
+ Record &record = (*records)[data.index];
+
+ record.name = data.name.data();
+ record.node = data.node;
+
+ record.callees.reserve(data.callees.size());
+ for (auto &callee : data.callees)
+ {
+ record.callees.push_back(static_cast<int>(callee->index));
+ }
+
+ (*idToIndex)[data.node->getFunctionSymbolInfo()->getId()] =
+ static_cast<int>(data.index);
+ }
+ }
+
+ private:
+
+ struct CreatorFunctionData
+ {
+ CreatorFunctionData()
+ : node(nullptr),
+ index(0),
+ indexAssigned(false),
+ visiting(false)
+ {
+ }
+
+ std::set<CreatorFunctionData*> callees;
+ TIntermFunctionDefinition *node;
+ TString name;
+ size_t index;
+ bool indexAssigned;
+ bool visiting;
+ };
+
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
+ {
+ // Create the record if need be and remember the node.
+ if (visit == PreVisit)
+ {
+ auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
+
+ if (it == mFunctions.end())
+ {
+ mCurrentFunction = &mFunctions[node->getFunctionSymbolInfo()->getName()];
+ }
+ else
+ {
+ mCurrentFunction = &it->second;
+ }
+
+ mCurrentFunction->node = node;
+ mCurrentFunction->name = node->getFunctionSymbolInfo()->getName();
+ }
+ else if (visit == PostVisit)
+ {
+ mCurrentFunction = nullptr;
+ }
+ return true;
+ }
+
+ // Aggregates the AST node for each function as well as the name of the functions called by it
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ switch (node->getOp())
+ {
+ case EOpPrototype:
+ if (visit == PreVisit)
+ {
+ // Function declaration, create an empty record.
+ auto &record = mFunctions[node->getFunctionSymbolInfo()->getName()];
+ record.name = node->getFunctionSymbolInfo()->getName();
+ }
+ break;
+ case EOpFunctionCall:
+ {
+ // Function call, add the callees
+ if (visit == PreVisit)
+ {
+ // Do not handle calls to builtin functions
+ if (node->isUserDefined())
+ {
+ auto it = mFunctions.find(node->getFunctionSymbolInfo()->getName());
+ ASSERT(it != mFunctions.end());
+
+ // We might be in a top-level function call to set a global variable
+ if (mCurrentFunction)
+ {
+ mCurrentFunction->callees.insert(&it->second);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+ }
+
+ // Recursively assigns indices to a sub DAG
+ InitResult assignIndicesInternal(CreatorFunctionData *root)
+ {
+ // Iterative implementation of the index assignment algorithm. A recursive version
+ // would be prettier but since the CallDAG creation runs before the limiting of the
+ // call depth, we might get stack overflows (computation of the call depth uses the
+ // CallDAG).
+
+ ASSERT(root);
+
+ if (root->indexAssigned)
+ {
+ return INITDAG_SUCCESS;
+ }
+
+ // If we didn't have to detect recursion, functionsToProcess could be a simple queue
+ // in which we add the function being processed's callees. However in order to detect
+ // recursion we need to know which functions we are currently visiting. For that reason
+ // functionsToProcess will look like a concatenation of segments of the form
+ // [F visiting = true, subset of F callees with visiting = false] and the following
+ // segment (if any) will be start with a callee of F.
+ // This way we can remember when we started visiting a function, to put visiting back
+ // to false.
+ TVector<CreatorFunctionData *> functionsToProcess;
+ functionsToProcess.push_back(root);
+
+ InitResult result = INITDAG_SUCCESS;
+
+ while (!functionsToProcess.empty())
+ {
+ CreatorFunctionData *function = functionsToProcess.back();
+
+ if (function->visiting)
+ {
+ function->visiting = false;
+ function->index = mCurrentIndex++;
+ function->indexAssigned = true;
+
+ functionsToProcess.pop_back();
+ continue;
+ }
+
+ if (!function->node)
+ {
+ *mCreationInfo << "Undefined function '" << function->name
+ << ")' used in the following call chain:";
+ result = INITDAG_UNDEFINED;
+ break;
+ }
+
+ if (function->indexAssigned)
+ {
+ functionsToProcess.pop_back();
+ continue;
+ }
+
+ function->visiting = true;
+
+ for (auto callee : function->callees)
+ {
+ functionsToProcess.push_back(callee);
+
+ // Check if the callee is already being visited after pushing it so that it appears
+ // in the chain printed in the info log.
+ if (callee->visiting)
+ {
+ *mCreationInfo << "Recursive function call in the following call chain:";
+ result = INITDAG_RECURSION;
+ break;
+ }
+ }
+
+ if (result != INITDAG_SUCCESS)
+ {
+ break;
+ }
+ }
+
+ // The call chain is made of the function we were visiting when the error was detected.
+ if (result != INITDAG_SUCCESS)
+ {
+ bool first = true;
+ for (auto function : functionsToProcess)
+ {
+ if (function->visiting)
+ {
+ if (!first)
+ {
+ *mCreationInfo << " -> ";
+ }
+ *mCreationInfo << function->name << ")";
+ first = false;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ TInfoSinkBase *mCreationInfo;
+
+ std::map<TString, CreatorFunctionData> mFunctions;
+ CreatorFunctionData *mCurrentFunction;
+ size_t mCurrentIndex;
+};
+
+// CallDAG
+
+CallDAG::CallDAG()
+{
+}
+
+CallDAG::~CallDAG()
+{
+}
+
+const size_t CallDAG::InvalidIndex = std::numeric_limits<size_t>::max();
+
+size_t CallDAG::findIndex(const TFunctionSymbolInfo *functionInfo) const
+{
+ auto it = mFunctionIdToIndex.find(functionInfo->getId());
+
+ if (it == mFunctionIdToIndex.end())
+ {
+ return InvalidIndex;
+ }
+ else
+ {
+ return it->second;
+ }
+}
+
+const CallDAG::Record &CallDAG::getRecordFromIndex(size_t index) const
+{
+ ASSERT(index != InvalidIndex && index < mRecords.size());
+ return mRecords[index];
+}
+
+const CallDAG::Record &CallDAG::getRecord(const TIntermAggregate *function) const
+{
+ size_t index = findIndex(function->getFunctionSymbolInfo());
+ ASSERT(index != InvalidIndex && index < mRecords.size());
+ return mRecords[index];
+}
+
+size_t CallDAG::size() const
+{
+ return mRecords.size();
+}
+
+void CallDAG::clear()
+{
+ mRecords.clear();
+ mFunctionIdToIndex.clear();
+}
+
+CallDAG::InitResult CallDAG::init(TIntermNode *root, TInfoSinkBase *info)
+{
+ ASSERT(info);
+
+ CallDAGCreator creator(info);
+
+ // Creates the mapping of functions to callees
+ root->traverse(&creator);
+
+ // Does the topological sort and detects recursions
+ InitResult result = creator.assignIndices();
+ if (result != INITDAG_SUCCESS)
+ {
+ return result;
+ }
+
+ creator.fillDataStructures(&mRecords, &mFunctionIdToIndex);
+ return INITDAG_SUCCESS;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/CallDAG.h b/gfx/angle/src/compiler/translator/CallDAG.h
new file mode 100755
index 000000000..90c056878
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/CallDAG.h
@@ -0,0 +1,79 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// CallDAG.h: Defines a call graph DAG of functions to be re-used accross
+// analyses, allows to efficiently traverse the functions in topological
+// order.
+
+#ifndef COMPILER_TRANSLATOR_CALLDAG_H_
+#define COMPILER_TRANSLATOR_CALLDAG_H_
+
+#include <map>
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/VariableInfo.h"
+
+namespace sh
+{
+
+// The translator needs to analyze the the graph of the function calls
+// to run checks and analyses; since in GLSL recursion is not allowed
+// that graph is a DAG.
+// This class is used to precompute that function call DAG so that it
+// can be reused by multiple analyses.
+//
+// It stores a vector of function records, with one record per function.
+// Records are accessed by index but a mangled function name can be converted
+// to the index of the corresponding record. The records mostly contain the
+// AST node of the function and the indices of the function's callees.
+//
+// In addition, records are in reverse topological order: a function F being
+// called by a function G will have index index(F) < index(G), that way
+// depth-first analysis becomes analysis in the order of indices.
+
+class CallDAG : angle::NonCopyable
+{
+ public:
+ CallDAG();
+ ~CallDAG();
+
+ struct Record
+ {
+ std::string name;
+ TIntermFunctionDefinition *node;
+ std::vector<int> callees;
+ };
+
+ enum InitResult
+ {
+ INITDAG_SUCCESS,
+ INITDAG_RECURSION,
+ INITDAG_UNDEFINED,
+ };
+
+ // Returns INITDAG_SUCCESS if it was able to create the DAG, otherwise prints
+ // the initialization error in info, if present.
+ InitResult init(TIntermNode *root, TInfoSinkBase *info);
+
+ // Returns InvalidIndex if the function wasn't found
+ size_t findIndex(const TFunctionSymbolInfo *functionInfo) const;
+
+ const Record &getRecordFromIndex(size_t index) const;
+ const Record &getRecord(const TIntermAggregate *function) const;
+ size_t size() const;
+ void clear();
+
+ const static size_t InvalidIndex;
+ private:
+ std::vector<Record> mRecords;
+ std::map<int, int> mFunctionIdToIndex;
+
+ class CallDAGCreator;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CALLDAG_H_
diff --git a/gfx/angle/src/compiler/translator/CodeGen.cpp b/gfx/angle/src/compiler/translator/CodeGen.cpp
new file mode 100755
index 000000000..22f2afc98
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/CodeGen.cpp
@@ -0,0 +1,84 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifdef ANGLE_ENABLE_ESSL
+#include "compiler/translator/TranslatorESSL.h"
+#endif // ANGLE_ENABLE_ESSL
+
+#ifdef ANGLE_ENABLE_GLSL
+#include "compiler/translator/TranslatorGLSL.h"
+#endif // ANGLE_ENABLE_GLSL
+
+#ifdef ANGLE_ENABLE_HLSL
+#include "compiler/translator/TranslatorHLSL.h"
+#endif // ANGLE_ENABLE_HLSL
+
+namespace sh
+{
+
+//
+// This function must be provided to create the actual
+// compile object used by higher level code. It returns
+// a subclass of TCompiler.
+//
+TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
+{
+ switch (output)
+ {
+ case SH_ESSL_OUTPUT:
+#ifdef ANGLE_ENABLE_ESSL
+ return new TranslatorESSL(type, spec);
+#else
+ // This compiler is not supported in this configuration. Return NULL per the
+ // sh::ConstructCompiler API.
+ return nullptr;
+#endif // ANGLE_ENABLE_ESSL
+
+ case SH_GLSL_130_OUTPUT:
+ case SH_GLSL_140_OUTPUT:
+ case SH_GLSL_150_CORE_OUTPUT:
+ case SH_GLSL_330_CORE_OUTPUT:
+ case SH_GLSL_400_CORE_OUTPUT:
+ case SH_GLSL_410_CORE_OUTPUT:
+ case SH_GLSL_420_CORE_OUTPUT:
+ case SH_GLSL_430_CORE_OUTPUT:
+ case SH_GLSL_440_CORE_OUTPUT:
+ case SH_GLSL_450_CORE_OUTPUT:
+ case SH_GLSL_COMPATIBILITY_OUTPUT:
+#ifdef ANGLE_ENABLE_GLSL
+ return new TranslatorGLSL(type, spec, output);
+#else
+ // This compiler is not supported in this configuration. Return NULL per the
+ // sh::ConstructCompiler API.
+ return nullptr;
+#endif // ANGLE_ENABLE_GLSL
+
+ case SH_HLSL_3_0_OUTPUT:
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_HLSL_4_0_FL9_3_OUTPUT:
+#ifdef ANGLE_ENABLE_HLSL
+ return new TranslatorHLSL(type, spec, output);
+#else
+ // This compiler is not supported in this configuration. Return NULL per the
+ // sh::ConstructCompiler API.
+ return nullptr;
+#endif // ANGLE_ENABLE_HLSL
+
+ default:
+ // Unknown format. Return NULL per the sh::ConstructCompiler API.
+ return nullptr;
+ }
+}
+
+//
+// Delete the compiler made by ConstructCompiler
+//
+void DeleteCompiler(TCompiler *compiler)
+{
+ SafeDelete(compiler);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Common.h b/gfx/angle/src/compiler/translator/Common.h
new file mode 100755
index 000000000..853317891
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Common.h
@@ -0,0 +1,100 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_COMMON_H_
+#define COMPILER_TRANSLATOR_COMMON_H_
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <limits>
+#include <stdio.h>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "compiler/translator/PoolAlloc.h"
+
+namespace sh
+{
+
+struct TSourceLoc {
+ int first_file;
+ int first_line;
+ int last_file;
+ int last_line;
+};
+
+//
+// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme.
+//
+#define POOL_ALLOCATOR_NEW_DELETE() \
+ void* operator new(size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
+ void* operator new(size_t, void *_Where) { return (_Where); } \
+ void operator delete(void*) { } \
+ void operator delete(void *, void *) { } \
+ void* operator new[](size_t s) { return GetGlobalPoolAllocator()->allocate(s); } \
+ void* operator new[](size_t, void *_Where) { return (_Where); } \
+ void operator delete[](void*) { } \
+ void operator delete[](void *, void *) { }
+
+//
+// Pool version of string.
+//
+typedef pool_allocator<char> TStringAllocator;
+typedef std::basic_string <char, std::char_traits<char>, TStringAllocator> TString;
+typedef std::basic_ostringstream<char, std::char_traits<char>, TStringAllocator> TStringStream;
+inline TString* NewPoolTString(const char* s)
+{
+ void* memory = GetGlobalPoolAllocator()->allocate(sizeof(TString));
+ return new(memory) TString(s);
+}
+
+//
+// Persistent string memory. Should only be used for strings that survive
+// across compiles.
+//
+#define TPersistString std::string
+#define TPersistStringStream std::ostringstream
+
+//
+// Pool allocator versions of vectors, lists, and maps
+//
+template <class T>
+class TVector : public std::vector<T, pool_allocator<T>>
+{
+ public:
+ typedef typename std::vector<T, pool_allocator<T>>::size_type size_type;
+ TVector() : std::vector<T, pool_allocator<T>>() {}
+ TVector(const pool_allocator<T> &a) : std::vector<T, pool_allocator<T>>(a) {}
+ TVector(size_type i) : std::vector<T, pool_allocator<T>>(i) {}
+};
+
+template <class K, class D, class CMP = std::less<K>>
+class TMap : public std::map<K, D, CMP, pool_allocator<std::pair<const K, D>>>
+{
+ public:
+ typedef pool_allocator<std::pair<const K, D>> tAllocator;
+
+ TMap() : std::map<K, D, CMP, tAllocator>() {}
+ // use correct two-stage name lookup supported in gcc 3.4 and above
+ TMap(const tAllocator& a) : std::map<K, D, CMP, tAllocator>(std::map<K, D, CMP, tAllocator>::key_compare(), a) {}
+};
+
+// Integer to TString conversion
+template <typename T>
+inline TString str(T i)
+{
+ ASSERT(std::numeric_limits<T>::is_integer);
+ char buffer[((8 * sizeof(T)) / 3) + 3];
+ const char *formatStr = std::numeric_limits<T>::is_signed ? "%d" : "%u";
+ snprintf(buffer, sizeof(buffer), formatStr, i);
+ return buffer;
+}
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_COMMON_H_
diff --git a/gfx/angle/src/compiler/translator/Compiler.cpp b/gfx/angle/src/compiler/translator/Compiler.cpp
new file mode 100755
index 000000000..e085ed588
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Compiler.cpp
@@ -0,0 +1,1015 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/Compiler.h"
+
+#include <sstream>
+
+#include "angle_gl.h"
+#include "common/utilities.h"
+#include "compiler/translator/AddAndTrueToLoopCondition.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/DeferGlobalInitializers.h"
+#include "compiler/translator/EmulateGLFragColorBroadcast.h"
+#include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/ForLoopUnroll.h"
+#include "compiler/translator/Initialize.h"
+#include "compiler/translator/InitializeParseContext.h"
+#include "compiler/translator/InitializeVariables.h"
+#include "compiler/translator/ParseContext.h"
+#include "compiler/translator/PruneEmptyDeclarations.h"
+#include "compiler/translator/RegenerateStructNames.h"
+#include "compiler/translator/RemoveInvariantDeclaration.h"
+#include "compiler/translator/RemovePow.h"
+#include "compiler/translator/RewriteDoWhile.h"
+#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
+#include "compiler/translator/UnfoldShortCircuitAST.h"
+#include "compiler/translator/UseInterfaceBlockFields.h"
+#include "compiler/translator/ValidateLimitations.h"
+#include "compiler/translator/ValidateMaxParameters.h"
+#include "compiler/translator/ValidateOutputs.h"
+#include "compiler/translator/VariablePacker.h"
+#include "third_party/compiler/ArrayBoundsClamper.h"
+
+namespace sh
+{
+
+namespace
+{
+
+#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+void DumpFuzzerCase(char const *const *shaderStrings,
+ size_t numStrings,
+ uint32_t type,
+ uint32_t spec,
+ uint32_t output,
+ uint64_t options)
+{
+ static int fileIndex = 0;
+
+ std::ostringstream o;
+ o << "corpus/" << fileIndex++ << ".sample";
+ std::string s = o.str();
+
+ // Must match the input format of the fuzzer
+ FILE *f = fopen(s.c_str(), "w");
+ fwrite(&type, sizeof(type), 1, f);
+ fwrite(&spec, sizeof(spec), 1, f);
+ fwrite(&output, sizeof(output), 1, f);
+ fwrite(&options, sizeof(options), 1, f);
+
+ char zero[128 - 20] = {0};
+ fwrite(&zero, 128 - 20, 1, f);
+
+ for (size_t i = 0; i < numStrings; i++)
+ {
+ fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f);
+ }
+ fwrite(&zero, 1, 1, f);
+
+ fclose(f);
+}
+#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+} // anonymous namespace
+
+bool IsWebGLBasedSpec(ShShaderSpec spec)
+{
+ return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
+}
+
+bool IsGLSL130OrNewer(ShShaderOutput output)
+{
+ return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
+ output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
+ output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT ||
+ output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
+ output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
+}
+
+bool IsGLSL420OrNewer(ShShaderOutput output)
+{
+ return (output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT ||
+ output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT);
+}
+
+bool IsGLSL410OrOlder(ShShaderOutput output)
+{
+ return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT ||
+ output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT ||
+ output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT);
+}
+
+bool RemoveInvariant(sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput outputType,
+ ShCompileOptions compileOptions)
+{
+ if ((compileOptions & SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT) == 0 &&
+ shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType))
+ return true;
+
+ if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 &&
+ shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER && IsGLSL410OrOlder(outputType))
+ return true;
+
+ return false;
+}
+
+size_t GetGlobalMaxTokenSize(ShShaderSpec spec)
+{
+ // WebGL defines a max token legnth of 256, while ES2 leaves max token
+ // size undefined. ES3 defines a max size of 1024 characters.
+ switch (spec)
+ {
+ case SH_WEBGL_SPEC:
+ return 256;
+ default:
+ return 1024;
+ }
+}
+
+namespace {
+
+class TScopedPoolAllocator
+{
+ public:
+ TScopedPoolAllocator(TPoolAllocator* allocator) : mAllocator(allocator)
+ {
+ mAllocator->push();
+ SetGlobalPoolAllocator(mAllocator);
+ }
+ ~TScopedPoolAllocator()
+ {
+ SetGlobalPoolAllocator(NULL);
+ mAllocator->pop();
+ }
+
+ private:
+ TPoolAllocator* mAllocator;
+};
+
+class TScopedSymbolTableLevel
+{
+ public:
+ TScopedSymbolTableLevel(TSymbolTable* table) : mTable(table)
+ {
+ ASSERT(mTable->atBuiltInLevel());
+ mTable->push();
+ }
+ ~TScopedSymbolTableLevel()
+ {
+ while (!mTable->atBuiltInLevel())
+ mTable->pop();
+ }
+
+ private:
+ TSymbolTable* mTable;
+};
+
+int MapSpecToShaderVersion(ShShaderSpec spec)
+{
+ switch (spec)
+ {
+ case SH_GLES2_SPEC:
+ case SH_WEBGL_SPEC:
+ return 100;
+ case SH_GLES3_SPEC:
+ case SH_WEBGL2_SPEC:
+ return 300;
+ case SH_GLES3_1_SPEC:
+ case SH_WEBGL3_SPEC:
+ return 310;
+ default:
+ UNREACHABLE();
+ return 0;
+ }
+}
+
+} // namespace
+
+TShHandleBase::TShHandleBase()
+{
+ allocator.push();
+ SetGlobalPoolAllocator(&allocator);
+}
+
+TShHandleBase::~TShHandleBase()
+{
+ SetGlobalPoolAllocator(NULL);
+ allocator.popAll();
+}
+
+TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
+ : variablesCollected(false),
+ shaderType(type),
+ shaderSpec(spec),
+ outputType(output),
+ maxUniformVectors(0),
+ maxExpressionComplexity(0),
+ maxCallStackDepth(0),
+ maxFunctionParameters(0),
+ fragmentPrecisionHigh(false),
+ clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
+ builtInFunctionEmulator(),
+ mSourcePath(NULL),
+ mComputeShaderLocalSizeDeclared(false),
+ mTemporaryIndex(0)
+{
+ mComputeShaderLocalSize.fill(1);
+}
+
+TCompiler::~TCompiler()
+{
+}
+
+bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const
+{
+ // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API,
+ // validate loop and indexing as well (to verify that the shader only uses minimal functionality
+ // of ESSL 1.00 as in Appendix A of the spec).
+ return (IsWebGLBasedSpec(shaderSpec) && shaderVersion == 100) ||
+ (compileOptions & SH_VALIDATE_LOOP_INDEXING);
+}
+
+bool TCompiler::Init(const ShBuiltInResources& resources)
+{
+ shaderVersion = 100;
+ maxUniformVectors = (shaderType == GL_VERTEX_SHADER) ?
+ resources.MaxVertexUniformVectors :
+ resources.MaxFragmentUniformVectors;
+ maxExpressionComplexity = resources.MaxExpressionComplexity;
+ maxCallStackDepth = resources.MaxCallStackDepth;
+ maxFunctionParameters = resources.MaxFunctionParameters;
+
+ SetGlobalPoolAllocator(&allocator);
+
+ // Generate built-in symbol table.
+ if (!InitBuiltInSymbolTable(resources))
+ return false;
+ InitExtensionBehavior(resources, extensionBehavior);
+ fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
+
+ arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
+ clampingStrategy = resources.ArrayIndexClampingStrategy;
+
+ hashFunction = resources.HashFunction;
+
+ return true;
+}
+
+TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions)
+{
+ return compileTreeImpl(shaderStrings, numStrings, compileOptions);
+}
+
+TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
+ size_t numStrings,
+ const ShCompileOptions compileOptions)
+{
+ clearResults();
+
+ ASSERT(numStrings > 0);
+ ASSERT(GetGlobalPoolAllocator());
+
+ // Reset the extension behavior for each compilation unit.
+ ResetExtensionBehavior(extensionBehavior);
+
+ // First string is path of source file if flag is set. The actual source follows.
+ size_t firstSource = 0;
+ if (compileOptions & SH_SOURCE_PATH)
+ {
+ mSourcePath = shaderStrings[0];
+ ++firstSource;
+ }
+
+ TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
+ compileOptions, true, infoSink, getResources());
+
+ parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
+ SetGlobalParseContext(&parseContext);
+
+ // We preserve symbols at the built-in level from compile-to-compile.
+ // Start pushing the user-defined symbols at global level.
+ TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
+
+ // Parse shader.
+ bool success =
+ (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) &&
+ (parseContext.getTreeRoot() != nullptr);
+
+ shaderVersion = parseContext.getShaderVersion();
+ if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion)
+ {
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "unsupported shader version";
+ success = false;
+ }
+
+ TIntermBlock *root = nullptr;
+
+ if (success)
+ {
+ mPragma = parseContext.pragma();
+ symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
+
+ mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
+ mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
+
+ root = parseContext.getTreeRoot();
+
+ // Highp might have been auto-enabled based on shader version
+ fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
+
+ // Disallow expressions deemed too complex.
+ if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
+ success = limitExpressionComplexity(root);
+
+ // Create the function DAG and check there is no recursion
+ if (success)
+ success = initCallDag(root);
+
+ if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH))
+ success = checkCallDepth();
+
+ // Checks which functions are used and if "main" exists
+ if (success)
+ {
+ functionMetadata.clear();
+ functionMetadata.resize(mCallDag.size());
+ success = tagUsedFunctions();
+ }
+
+ if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
+ success = pruneUnusedFunctions(root);
+
+ // Prune empty declarations to work around driver bugs and to keep declaration output simple.
+ if (success)
+ PruneEmptyDeclarations(root);
+
+ if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER)
+ success = validateOutputs(root);
+
+ if (success && shouldRunLoopAndIndexingValidation(compileOptions))
+ success = validateLimitations(root);
+
+ // Fail compilation if precision emulation not supported.
+ if (success && getResources().WEBGL_debug_shader_precision &&
+ getPragma().debugShaderPrecision)
+ {
+ if (!EmulatePrecision::SupportedInLanguage(outputType))
+ {
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Precision emulation not supported for this output type.";
+ success = false;
+ }
+ }
+
+ // Unroll for-loop markup needs to happen after validateLimitations pass.
+ if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
+ {
+ ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
+ shouldRunLoopAndIndexingValidation(compileOptions));
+ root->traverse(&marker);
+ }
+ if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
+ {
+ ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex,
+ shouldRunLoopAndIndexingValidation(compileOptions));
+ root->traverse(&marker);
+ if (marker.samplerArrayIndexIsFloatLoopIndex())
+ {
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "sampler array index is float loop index";
+ success = false;
+ }
+ }
+
+ // Built-in function emulation needs to happen after validateLimitations pass.
+ if (success)
+ {
+ // TODO(jmadill): Remove global pool allocator.
+ GetGlobalPoolAllocator()->lock();
+ initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
+ GetGlobalPoolAllocator()->unlock();
+ builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
+ }
+
+ // Clamping uniform array bounds needs to happen after validateLimitations pass.
+ if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
+ arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
+
+ // gl_Position is always written in compatibility output mode
+ if (success && shaderType == GL_VERTEX_SHADER &&
+ ((compileOptions & SH_INIT_GL_POSITION) ||
+ (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
+ initializeGLPosition(root);
+
+ if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
+ sh::RemoveInvariantDeclaration(root);
+
+ // This pass might emit short circuits so keep it before the short circuit unfolding
+ if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
+ RewriteDoWhile(root, getTemporaryIndex());
+
+ if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION))
+ sh::AddAndTrueToLoopCondition(root);
+
+ if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
+ {
+ UnfoldShortCircuitAST unfoldShortCircuit;
+ root->traverse(&unfoldShortCircuit);
+ unfoldShortCircuit.updateTree();
+ }
+
+ if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
+ {
+ RemovePow(root);
+ }
+
+ if (success && shouldCollectVariables(compileOptions))
+ {
+ collectVariables(root);
+ if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
+ {
+ useAllMembersInUnusedStandardAndSharedBlocks(root);
+ }
+ if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
+ {
+ success = enforcePackingRestrictions();
+ if (!success)
+ {
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "too many uniforms";
+ }
+ }
+ if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES))
+ {
+ initializeOutputVariables(root);
+ }
+ }
+
+ if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
+ {
+ ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh,
+ &mTemporaryIndex);
+ }
+
+ if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES))
+ {
+ RegenerateStructNames gen(symbolTable, shaderVersion);
+ root->traverse(&gen);
+ }
+
+ if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
+ compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
+ IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers"))
+ {
+ EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables);
+ }
+
+ if (success)
+ {
+ DeferGlobalInitializers(root);
+ }
+ }
+
+ SetGlobalParseContext(NULL);
+ if (success)
+ return root;
+
+ return NULL;
+}
+
+bool TCompiler::compile(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptionsIn)
+{
+#if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+ DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, compileOptionsIn);
+#endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
+
+ if (numStrings == 0)
+ return true;
+
+ ShCompileOptions compileOptions = compileOptionsIn;
+
+ // Apply key workarounds.
+ if (shouldFlattenPragmaStdglInvariantAll())
+ {
+ // This should be harmless to do in all cases, but for the moment, do it only conditionally.
+ compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
+ }
+
+ ShCompileOptions unrollFlags =
+ SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX | SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX;
+ if ((compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) != 0 &&
+ (compileOptions & unrollFlags) != 0)
+ {
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info
+ << "Unsupported compile flag combination: unroll & ADD_TRUE_TO_LOOP_CONDITION";
+ return false;
+ }
+
+ TScopedPoolAllocator scopedAlloc(&allocator);
+ TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
+
+ if (root)
+ {
+ if (compileOptions & SH_INTERMEDIATE_TREE)
+ TIntermediate::outputTree(root, infoSink.info);
+
+ if (compileOptions & SH_OBJECT_CODE)
+ translate(root, compileOptions);
+
+ // The IntermNode tree doesn't need to be deleted here, since the
+ // memory will be freed in a big chunk by the PoolAllocator.
+ return true;
+ }
+ return false;
+}
+
+bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
+{
+ compileResources = resources;
+ setResourceString();
+
+ assert(symbolTable.isEmpty());
+ symbolTable.push(); // COMMON_BUILTINS
+ symbolTable.push(); // ESSL1_BUILTINS
+ symbolTable.push(); // ESSL3_BUILTINS
+ symbolTable.push(); // ESSL3_1_BUILTINS
+
+ TPublicType integer;
+ integer.initializeBasicType(EbtInt);
+
+ TPublicType floatingPoint;
+ floatingPoint.initializeBasicType(EbtFloat);
+
+ switch (shaderType)
+ {
+ case GL_FRAGMENT_SHADER:
+ symbolTable.setDefaultPrecision(integer, EbpMedium);
+ break;
+ case GL_VERTEX_SHADER:
+ symbolTable.setDefaultPrecision(integer, EbpHigh);
+ symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
+ break;
+ case GL_COMPUTE_SHADER:
+ symbolTable.setDefaultPrecision(integer, EbpHigh);
+ symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
+ break;
+ default:
+ assert(false && "Language not supported");
+ }
+ // Set defaults for sampler types that have default precision, even those that are
+ // only available if an extension exists.
+ // New sampler types in ESSL3 don't have default precision. ESSL1 types do.
+ initSamplerDefaultPrecision(EbtSampler2D);
+ initSamplerDefaultPrecision(EbtSamplerCube);
+ // SamplerExternalOES is specified in the extension to have default precision.
+ initSamplerDefaultPrecision(EbtSamplerExternalOES);
+ // It isn't specified whether Sampler2DRect has default precision.
+ initSamplerDefaultPrecision(EbtSampler2DRect);
+
+ InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
+
+ IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
+
+ return true;
+}
+
+void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
+{
+ ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
+ TPublicType sampler;
+ sampler.initializeBasicType(samplerType);
+ symbolTable.setDefaultPrecision(sampler, EbpLow);
+}
+
+void TCompiler::setResourceString()
+{
+ std::ostringstream strstream;
+
+ // clang-format off
+ strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs
+ << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
+ << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
+ << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
+ << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
+ << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits
+ << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors
+ << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers
+ << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives
+ << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external
+ << ":OES_EGL_image_external_essl3:" << compileResources.OES_EGL_image_external_essl3
+ << ":NV_EGL_stream_consumer_external:" << compileResources.NV_EGL_stream_consumer_external
+ << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle
+ << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers
+ << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
+ << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
+ << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
+ << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters
+ << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
+ << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
+ << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
+ << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
+ << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
+ << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
+ << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
+ << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
+ << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
+ << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
+ << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
+ << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
+ << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision
+ << ":MaxImageUnits:" << compileResources.MaxImageUnits
+ << ":MaxVertexImageUniforms:" << compileResources.MaxVertexImageUniforms
+ << ":MaxFragmentImageUniforms:" << compileResources.MaxFragmentImageUniforms
+ << ":MaxComputeImageUniforms:" << compileResources.MaxComputeImageUniforms
+ << ":MaxCombinedImageUniforms:" << compileResources.MaxCombinedImageUniforms
+ << ":MaxCombinedShaderOutputResources:" << compileResources.MaxCombinedShaderOutputResources
+ << ":MaxComputeWorkGroupCountX:" << compileResources.MaxComputeWorkGroupCount[0]
+ << ":MaxComputeWorkGroupCountY:" << compileResources.MaxComputeWorkGroupCount[1]
+ << ":MaxComputeWorkGroupCountZ:" << compileResources.MaxComputeWorkGroupCount[2]
+ << ":MaxComputeWorkGroupSizeX:" << compileResources.MaxComputeWorkGroupSize[0]
+ << ":MaxComputeWorkGroupSizeY:" << compileResources.MaxComputeWorkGroupSize[1]
+ << ":MaxComputeWorkGroupSizeZ:" << compileResources.MaxComputeWorkGroupSize[2]
+ << ":MaxComputeUniformComponents:" << compileResources.MaxComputeUniformComponents
+ << ":MaxComputeTextureImageUnits:" << compileResources.MaxComputeTextureImageUnits
+ << ":MaxComputeAtomicCounters:" << compileResources.MaxComputeAtomicCounters
+ << ":MaxComputeAtomicCounterBuffers:" << compileResources.MaxComputeAtomicCounterBuffers
+ << ":MaxVertexAtomicCounters:" << compileResources.MaxVertexAtomicCounters
+ << ":MaxFragmentAtomicCounters:" << compileResources.MaxFragmentAtomicCounters
+ << ":MaxCombinedAtomicCounters:" << compileResources.MaxCombinedAtomicCounters
+ << ":MaxAtomicCounterBindings:" << compileResources.MaxAtomicCounterBindings
+ << ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers
+ << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
+ << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
+ << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize;
+ // clang-format on
+
+ builtInResourcesString = strstream.str();
+}
+
+void TCompiler::clearResults()
+{
+ arrayBoundsClamper.Cleanup();
+ infoSink.info.erase();
+ infoSink.obj.erase();
+ infoSink.debug.erase();
+
+ attributes.clear();
+ outputVariables.clear();
+ uniforms.clear();
+ expandedUniforms.clear();
+ varyings.clear();
+ interfaceBlocks.clear();
+ variablesCollected = false;
+
+ builtInFunctionEmulator.Cleanup();
+
+ nameMap.clear();
+
+ mSourcePath = NULL;
+ mTemporaryIndex = 0;
+}
+
+bool TCompiler::initCallDag(TIntermNode *root)
+{
+ mCallDag.clear();
+
+ switch (mCallDag.init(root, &infoSink.info))
+ {
+ case CallDAG::INITDAG_SUCCESS:
+ return true;
+ case CallDAG::INITDAG_RECURSION:
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Function recursion detected";
+ return false;
+ case CallDAG::INITDAG_UNDEFINED:
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Unimplemented function detected";
+ return false;
+ }
+
+ UNREACHABLE();
+ return true;
+}
+
+bool TCompiler::checkCallDepth()
+{
+ std::vector<int> depths(mCallDag.size());
+
+ for (size_t i = 0; i < mCallDag.size(); i++)
+ {
+ int depth = 0;
+ auto &record = mCallDag.getRecordFromIndex(i);
+
+ for (auto &calleeIndex : record.callees)
+ {
+ depth = std::max(depth, depths[calleeIndex] + 1);
+ }
+
+ depths[i] = depth;
+
+ if (depth >= maxCallStackDepth)
+ {
+ // Trace back the function chain to have a meaningful info log.
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Call stack too deep (larger than " << maxCallStackDepth
+ << ") with the following call chain: " << record.name;
+
+ int currentFunction = static_cast<int>(i);
+ int currentDepth = depth;
+
+ while (currentFunction != -1)
+ {
+ infoSink.info << " -> " << mCallDag.getRecordFromIndex(currentFunction).name;
+
+ int nextFunction = -1;
+ for (auto& calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees)
+ {
+ if (depths[calleeIndex] == currentDepth - 1)
+ {
+ currentDepth--;
+ nextFunction = calleeIndex;
+ }
+ }
+
+ currentFunction = nextFunction;
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TCompiler::tagUsedFunctions()
+{
+ // Search from main, starting from the end of the DAG as it usually is the root.
+ for (size_t i = mCallDag.size(); i-- > 0;)
+ {
+ if (mCallDag.getRecordFromIndex(i).name == "main(")
+ {
+ internalTagUsedFunction(i);
+ return true;
+ }
+ }
+
+ infoSink.info.prefix(EPrefixError);
+ infoSink.info << "Missing main()\n";
+ return false;
+}
+
+void TCompiler::internalTagUsedFunction(size_t index)
+{
+ if (functionMetadata[index].used)
+ {
+ return;
+ }
+
+ functionMetadata[index].used = true;
+
+ for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees)
+ {
+ internalTagUsedFunction(calleeIndex);
+ }
+}
+
+// A predicate for the stl that returns if a top-level node is unused
+class TCompiler::UnusedPredicate
+{
+ public:
+ UnusedPredicate(const CallDAG *callDag, const std::vector<FunctionMetadata> *metadatas)
+ : mCallDag(callDag), mMetadatas(metadatas)
+ {
+ }
+
+ bool operator ()(TIntermNode *node)
+ {
+ const TIntermAggregate *asAggregate = node->getAsAggregate();
+ const TIntermFunctionDefinition *asFunction = node->getAsFunctionDefinition();
+
+ const TFunctionSymbolInfo *functionInfo = nullptr;
+
+ if (asFunction)
+ {
+ functionInfo = asFunction->getFunctionSymbolInfo();
+ }
+ else if (asAggregate)
+ {
+ if (asAggregate->getOp() == EOpPrototype)
+ {
+ functionInfo = asAggregate->getFunctionSymbolInfo();
+ }
+ }
+ if (functionInfo == nullptr)
+ {
+ return false;
+ }
+
+ size_t callDagIndex = mCallDag->findIndex(functionInfo);
+ if (callDagIndex == CallDAG::InvalidIndex)
+ {
+ // This happens only for unimplemented prototypes which are thus unused
+ ASSERT(asAggregate && asAggregate->getOp() == EOpPrototype);
+ return true;
+ }
+
+ ASSERT(callDagIndex < mMetadatas->size());
+ return !(*mMetadatas)[callDagIndex].used;
+ }
+
+ private:
+ const CallDAG *mCallDag;
+ const std::vector<FunctionMetadata> *mMetadatas;
+};
+
+bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
+{
+ UnusedPredicate isUnused(&mCallDag, &functionMetadata);
+ TIntermSequence *sequence = root->getSequence();
+
+ if (!sequence->empty())
+ {
+ sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), sequence->end());
+ }
+
+ return true;
+}
+
+bool TCompiler::validateOutputs(TIntermNode* root)
+{
+ ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
+ root->traverse(&validateOutputs);
+ return (validateOutputs.validateAndCountErrors(infoSink.info) == 0);
+}
+
+bool TCompiler::validateLimitations(TIntermNode* root)
+{
+ ValidateLimitations validate(shaderType, &infoSink.info);
+ root->traverse(&validate);
+ return validate.numErrors() == 0;
+}
+
+bool TCompiler::limitExpressionComplexity(TIntermNode* root)
+{
+ TMaxDepthTraverser traverser(maxExpressionComplexity + 1);
+ root->traverse(&traverser);
+
+ if (traverser.getMaxDepth() > maxExpressionComplexity)
+ {
+ infoSink.info << "Expression too complex.";
+ return false;
+ }
+
+ if (!ValidateMaxParameters::validate(root, maxFunctionParameters))
+ {
+ infoSink.info << "Function has too many parameters.";
+ return false;
+ }
+
+ return true;
+}
+
+void TCompiler::collectVariables(TIntermNode* root)
+{
+ if (!variablesCollected)
+ {
+ sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
+ &interfaceBlocks, hashFunction, symbolTable,
+ extensionBehavior);
+ root->traverse(&collect);
+
+ // This is for enforcePackingRestriction().
+ sh::ExpandUniforms(uniforms, &expandedUniforms);
+ variablesCollected = true;
+ }
+}
+
+bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions)
+{
+ return (compileOptions & SH_VARIABLES) != 0;
+}
+
+bool TCompiler::wereVariablesCollected() const
+{
+ return variablesCollected;
+}
+
+bool TCompiler::enforcePackingRestrictions()
+{
+ VariablePacker packer;
+ return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms);
+}
+
+void TCompiler::initializeGLPosition(TIntermNode* root)
+{
+ InitVariableList list;
+ sh::ShaderVariable var(GL_FLOAT_VEC4, 0);
+ var.name = "gl_Position";
+ list.push_back(var);
+ InitializeVariables(root, list, symbolTable);
+}
+
+void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root)
+{
+ sh::InterfaceBlockList list;
+
+ for (auto block : interfaceBlocks)
+ {
+ if (!block.staticUse &&
+ (block.layout == sh::BLOCKLAYOUT_STANDARD || block.layout == sh::BLOCKLAYOUT_SHARED))
+ {
+ list.push_back(block);
+ }
+ }
+
+ sh::UseInterfaceBlockFields(root, list, symbolTable);
+}
+
+void TCompiler::initializeOutputVariables(TIntermNode *root)
+{
+ InitVariableList list;
+ if (shaderType == GL_VERTEX_SHADER)
+ {
+ for (auto var : varyings)
+ {
+ list.push_back(var);
+ }
+ }
+ else
+ {
+ ASSERT(shaderType == GL_FRAGMENT_SHADER);
+ for (auto var : outputVariables)
+ {
+ list.push_back(var);
+ }
+ }
+ InitializeVariables(root, list, symbolTable);
+}
+
+const TExtensionBehavior& TCompiler::getExtensionBehavior() const
+{
+ return extensionBehavior;
+}
+
+const char *TCompiler::getSourcePath() const
+{
+ return mSourcePath;
+}
+
+const ShBuiltInResources& TCompiler::getResources() const
+{
+ return compileResources;
+}
+
+const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
+{
+ return arrayBoundsClamper;
+}
+
+ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
+{
+ return clampingStrategy;
+}
+
+const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
+{
+ return builtInFunctionEmulator;
+}
+
+void TCompiler::writePragma(ShCompileOptions compileOptions)
+{
+ if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
+ {
+ TInfoSinkBase &sink = infoSink.obj;
+ if (mPragma.stdgl.invariantAll)
+ sink << "#pragma STDGL invariant(all)\n";
+ }
+}
+
+bool TCompiler::isVaryingDefined(const char *varyingName)
+{
+ ASSERT(variablesCollected);
+ for (size_t ii = 0; ii < varyings.size(); ++ii)
+ {
+ if (varyings[ii].name == varyingName)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Compiler.h b/gfx/angle/src/compiler/translator/Compiler.h
new file mode 100755
index 000000000..42b4f8f06
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Compiler.h
@@ -0,0 +1,274 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_COMPILER_H_
+#define COMPILER_TRANSLATOR_COMPILER_H_
+
+//
+// Machine independent part of the compiler private objects
+// sent as ShHandle to the driver.
+//
+// This should not be included by driver code.
+//
+
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/Pragma.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/VariableInfo.h"
+#include "third_party/compiler/ArrayBoundsClamper.h"
+
+namespace sh
+{
+
+class TCompiler;
+#ifdef ANGLE_ENABLE_HLSL
+class TranslatorHLSL;
+#endif // ANGLE_ENABLE_HLSL
+
+//
+// Helper function to identify specs that are based on the WebGL spec.
+//
+bool IsWebGLBasedSpec(ShShaderSpec spec);
+
+//
+// Helper function to check if the shader type is GLSL.
+//
+bool IsGLSL130OrNewer(ShShaderOutput output);
+bool IsGLSL420OrNewer(ShShaderOutput output);
+bool IsGLSL410OrOlder(ShShaderOutput output);
+
+//
+// Helper function to check if the invariant qualifier can be removed.
+//
+bool RemoveInvariant(sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput outputType,
+ ShCompileOptions compileOptions);
+
+//
+// The base class used to back handles returned to the driver.
+//
+class TShHandleBase {
+public:
+ TShHandleBase();
+ virtual ~TShHandleBase();
+ virtual TCompiler* getAsCompiler() { return 0; }
+#ifdef ANGLE_ENABLE_HLSL
+ virtual TranslatorHLSL* getAsTranslatorHLSL() { return 0; }
+#endif // ANGLE_ENABLE_HLSL
+
+protected:
+ // Memory allocator. Allocates and tracks memory required by the compiler.
+ // Deallocates all memory when compiler is destructed.
+ TPoolAllocator allocator;
+};
+
+//
+// The base class for the machine dependent compiler to derive from
+// for managing object code from the compile.
+//
+class TCompiler : public TShHandleBase
+{
+ public:
+ TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
+ ~TCompiler() override;
+ TCompiler *getAsCompiler() override { return this; }
+
+ bool Init(const ShBuiltInResources& resources);
+
+ // compileTreeForTesting should be used only when tests require access to
+ // the AST. Users of this function need to manually manage the global pool
+ // allocator. Returns nullptr whenever there are compilation errors.
+ TIntermBlock *compileTreeForTesting(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions);
+
+ bool compile(const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions);
+
+ // Get results of the last compilation.
+ int getShaderVersion() const { return shaderVersion; }
+ TInfoSink& getInfoSink() { return infoSink; }
+
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
+
+ // Clears the results from the previous compilation.
+ void clearResults();
+
+ const std::vector<sh::Attribute> &getAttributes() const { return attributes; }
+ const std::vector<sh::OutputVariable> &getOutputVariables() const { return outputVariables; }
+ const std::vector<sh::Uniform> &getUniforms() const { return uniforms; }
+ const std::vector<sh::Varying> &getVaryings() const { return varyings; }
+ const std::vector<sh::InterfaceBlock> &getInterfaceBlocks() const { return interfaceBlocks; }
+
+ ShHashFunction64 getHashFunction() const { return hashFunction; }
+ NameMap& getNameMap() { return nameMap; }
+ TSymbolTable& getSymbolTable() { return symbolTable; }
+ ShShaderSpec getShaderSpec() const { return shaderSpec; }
+ ShShaderOutput getOutputType() const { return outputType; }
+ const std::string &getBuiltInResourcesString() const { return builtInResourcesString; }
+
+ bool shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const;
+
+ // Get the resources set by InitBuiltInSymbolTable
+ const ShBuiltInResources& getResources() const;
+
+ protected:
+ sh::GLenum getShaderType() const { return shaderType; }
+ // Initialize symbol-table with built-in symbols.
+ bool InitBuiltInSymbolTable(const ShBuiltInResources& resources);
+ // Compute the string representation of the built-in resources
+ void setResourceString();
+ // Return false if the call depth is exceeded.
+ bool checkCallDepth();
+ // Returns true if a program has no conflicting or missing fragment outputs
+ bool validateOutputs(TIntermNode* root);
+ // Returns true if the given shader does not exceed the minimum
+ // functionality mandated in GLSL 1.0 spec Appendix A.
+ bool validateLimitations(TIntermNode* root);
+ // Add emulated functions to the built-in function emulator.
+ virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions){};
+ // Translate to object code.
+ virtual void translate(TIntermNode *root, ShCompileOptions compileOptions) = 0;
+ // Returns true if, after applying the packing rules in the GLSL 1.017 spec
+ // Appendix A, section 7, the shader does not use too many uniforms.
+ bool enforcePackingRestrictions();
+ // Insert statements to reference all members in unused uniform blocks with standard and shared
+ // layout. This is to work around a Mac driver that treats unused standard/shared
+ // uniform blocks as inactive.
+ void useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root);
+ // Insert statements to initialize output variables in the beginning of main().
+ // This is to avoid undefined behaviors.
+ void initializeOutputVariables(TIntermNode *root);
+ // Insert gl_Position = vec4(0,0,0,0) to the beginning of main().
+ // It is to work around a Linux driver bug where missing this causes compile failure
+ // while spec says it is allowed.
+ // This function should only be applied to vertex shaders.
+ void initializeGLPosition(TIntermNode* root);
+ // Return true if the maximum expression complexity is below the limit.
+ bool limitExpressionComplexity(TIntermNode* root);
+ // Get built-in extensions with default behavior.
+ const TExtensionBehavior& getExtensionBehavior() const;
+ const char *getSourcePath() const;
+ const TPragma& getPragma() const { return mPragma; }
+ void writePragma(ShCompileOptions compileOptions);
+ unsigned int *getTemporaryIndex() { return &mTemporaryIndex; }
+ // Relies on collectVariables having been called.
+ bool isVaryingDefined(const char *varyingName);
+
+ const ArrayBoundsClamper& getArrayBoundsClamper() const;
+ ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
+ const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
+
+ virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
+ virtual bool shouldCollectVariables(ShCompileOptions compileOptions);
+
+ bool wereVariablesCollected() const;
+ std::vector<sh::Attribute> attributes;
+ std::vector<sh::OutputVariable> outputVariables;
+ std::vector<sh::Uniform> uniforms;
+ std::vector<sh::ShaderVariable> expandedUniforms;
+ std::vector<sh::Varying> varyings;
+ std::vector<sh::InterfaceBlock> interfaceBlocks;
+
+ private:
+ // Creates the function call DAG for further analysis, returning false if there is a recursion
+ bool initCallDag(TIntermNode *root);
+ // Return false if "main" doesn't exist
+ bool tagUsedFunctions();
+ void internalTagUsedFunction(size_t index);
+
+ void initSamplerDefaultPrecision(TBasicType samplerType);
+
+ // Collect info for all attribs, uniforms, varyings.
+ void collectVariables(TIntermNode *root);
+
+ bool variablesCollected;
+
+ // Removes unused function declarations and prototypes from the AST
+ class UnusedPredicate;
+ bool pruneUnusedFunctions(TIntermBlock *root);
+
+ TIntermBlock *compileTreeImpl(const char *const shaderStrings[],
+ size_t numStrings,
+ const ShCompileOptions compileOptions);
+
+ sh::GLenum shaderType;
+ ShShaderSpec shaderSpec;
+ ShShaderOutput outputType;
+
+ struct FunctionMetadata
+ {
+ FunctionMetadata()
+ : used(false)
+ {
+ }
+ bool used;
+ };
+
+ CallDAG mCallDag;
+ std::vector<FunctionMetadata> functionMetadata;
+
+ int maxUniformVectors;
+ int maxExpressionComplexity;
+ int maxCallStackDepth;
+ int maxFunctionParameters;
+
+ ShBuiltInResources compileResources;
+ std::string builtInResourcesString;
+
+ // Built-in symbol table for the given language, spec, and resources.
+ // It is preserved from compile-to-compile.
+ TSymbolTable symbolTable;
+ // Built-in extensions with default behavior.
+ TExtensionBehavior extensionBehavior;
+ bool fragmentPrecisionHigh;
+
+ ArrayBoundsClamper arrayBoundsClamper;
+ ShArrayIndexClampingStrategy clampingStrategy;
+ BuiltInFunctionEmulator builtInFunctionEmulator;
+
+ // Results of compilation.
+ int shaderVersion;
+ TInfoSink infoSink; // Output sink.
+ const char *mSourcePath; // Path of source file or NULL
+
+ // compute shader local group size
+ bool mComputeShaderLocalSizeDeclared;
+ sh::WorkGroupSize mComputeShaderLocalSize;
+
+ // name hashing.
+ ShHashFunction64 hashFunction;
+ NameMap nameMap;
+
+ TPragma mPragma;
+
+ unsigned int mTemporaryIndex;
+};
+
+//
+// This is the interface between the machine independent code
+// and the machine dependent code.
+//
+// The machine dependent code should derive from the classes
+// above. Then Construct*() and Delete*() will create and
+// destroy the machine dependent objects, which contain the
+// above machine independent information.
+//
+TCompiler* ConstructCompiler(
+ sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
+void DeleteCompiler(TCompiler*);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_COMPILER_H_
diff --git a/gfx/angle/src/compiler/translator/ConstantUnion.cpp b/gfx/angle/src/compiler/translator/ConstantUnion.cpp
new file mode 100644
index 000000000..66f444b0b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ConstantUnion.cpp
@@ -0,0 +1,642 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ConstantUnion: Constant folding helper class.
+
+#include "compiler/translator/ConstantUnion.h"
+
+#include "base/numerics/safe_math.h"
+#include "common/mathutil.h"
+#include "compiler/translator/Diagnostics.h"
+
+namespace sh
+{
+
+namespace
+{
+
+template <typename T>
+T CheckedSum(base::CheckedNumeric<T> lhs,
+ base::CheckedNumeric<T> rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ ASSERT(lhs.IsValid() && rhs.IsValid());
+ auto result = lhs + rhs;
+ if (!result.IsValid())
+ {
+ diag->error(line, "Addition out of range", "*", "");
+ return 0;
+ }
+ return result.ValueOrDefault(0);
+}
+
+template <typename T>
+T CheckedDiff(base::CheckedNumeric<T> lhs,
+ base::CheckedNumeric<T> rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ ASSERT(lhs.IsValid() && rhs.IsValid());
+ auto result = lhs - rhs;
+ if (!result.IsValid())
+ {
+ diag->error(line, "Difference out of range", "*", "");
+ return 0;
+ }
+ return result.ValueOrDefault(0);
+}
+
+template <typename T>
+T CheckedMul(base::CheckedNumeric<T> lhs,
+ base::CheckedNumeric<T> rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ ASSERT(lhs.IsValid() && rhs.IsValid());
+ auto result = lhs * rhs;
+ if (!result.IsValid())
+ {
+ diag->error(line, "Multiplication out of range", "*", "");
+ return 0;
+ }
+ return result.ValueOrDefault(0);
+}
+
+} // anonymous namespace
+
+TConstantUnion::TConstantUnion()
+{
+ iConst = 0;
+ type = EbtVoid;
+}
+
+bool TConstantUnion::cast(TBasicType newType, const TConstantUnion &constant)
+{
+ switch (newType)
+ {
+ case EbtFloat:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setFConst(static_cast<float>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setFConst(static_cast<float>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setFConst(static_cast<float>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setFConst(static_cast<float>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtInt:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setIConst(static_cast<int>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setIConst(static_cast<int>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setIConst(static_cast<int>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setIConst(static_cast<int>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtUInt:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setUConst(static_cast<unsigned int>(constant.getIConst()));
+ break;
+ case EbtUInt:
+ setUConst(static_cast<unsigned int>(constant.getUConst()));
+ break;
+ case EbtBool:
+ setUConst(static_cast<unsigned int>(constant.getBConst()));
+ break;
+ case EbtFloat:
+ setUConst(static_cast<unsigned int>(constant.getFConst()));
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtBool:
+ switch (constant.type)
+ {
+ case EbtInt:
+ setBConst(constant.getIConst() != 0);
+ break;
+ case EbtUInt:
+ setBConst(constant.getUConst() != 0);
+ break;
+ case EbtBool:
+ setBConst(constant.getBConst());
+ break;
+ case EbtFloat:
+ setBConst(constant.getFConst() != 0.0f);
+ break;
+ default:
+ return false;
+ }
+ break;
+ case EbtStruct: // Struct fields don't get cast
+ switch (constant.type)
+ {
+ case EbtInt:
+ setIConst(constant.getIConst());
+ break;
+ case EbtUInt:
+ setUConst(constant.getUConst());
+ break;
+ case EbtBool:
+ setBConst(constant.getBConst());
+ break;
+ case EbtFloat:
+ setFConst(constant.getFConst());
+ break;
+ default:
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool TConstantUnion::operator==(const int i) const
+{
+ return i == iConst;
+}
+
+bool TConstantUnion::operator==(const unsigned int u) const
+{
+ return u == uConst;
+}
+
+bool TConstantUnion::operator==(const float f) const
+{
+ return f == fConst;
+}
+
+bool TConstantUnion::operator==(const bool b) const
+{
+ return b == bConst;
+}
+
+bool TConstantUnion::operator==(const TConstantUnion &constant) const
+{
+ if (constant.type != type)
+ return false;
+
+ switch (type)
+ {
+ case EbtInt:
+ return constant.iConst == iConst;
+ case EbtUInt:
+ return constant.uConst == uConst;
+ case EbtFloat:
+ return constant.fConst == fConst;
+ case EbtBool:
+ return constant.bConst == bConst;
+ default:
+ return false;
+ }
+}
+
+bool TConstantUnion::operator!=(const int i) const
+{
+ return !operator==(i);
+}
+
+bool TConstantUnion::operator!=(const unsigned int u) const
+{
+ return !operator==(u);
+}
+
+bool TConstantUnion::operator!=(const float f) const
+{
+ return !operator==(f);
+}
+
+bool TConstantUnion::operator!=(const bool b) const
+{
+ return !operator==(b);
+}
+
+bool TConstantUnion::operator!=(const TConstantUnion &constant) const
+{
+ return !operator==(constant);
+}
+
+bool TConstantUnion::operator>(const TConstantUnion &constant) const
+{
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ return iConst > constant.iConst;
+ case EbtUInt:
+ return uConst > constant.uConst;
+ case EbtFloat:
+ return fConst > constant.fConst;
+ default:
+ return false; // Invalid operation, handled at semantic analysis
+ }
+}
+
+bool TConstantUnion::operator<(const TConstantUnion &constant) const
+{
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ return iConst < constant.iConst;
+ case EbtUInt:
+ return uConst < constant.uConst;
+ case EbtFloat:
+ return fConst < constant.fConst;
+ default:
+ return false; // Invalid operation, handled at semantic analysis
+ }
+}
+
+// static
+TConstantUnion TConstantUnion::add(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingSum<int>(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setUConst(gl::WrappingSum<unsigned int>(lhs.uConst, rhs.uConst));
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedSum<float>(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::sub(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingDiff<int>(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setUConst(gl::WrappingDiff<unsigned int>(lhs.uConst, rhs.uConst));
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedDiff<float>(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::mul(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == rhs.type);
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(gl::WrappingMul(lhs.iConst, rhs.iConst));
+ break;
+ case EbtUInt:
+ // Unsigned integer math in C++ is defined to be done in modulo 2^n, so we rely on that
+ // to implement wrapping multiplication.
+ returnValue.setUConst(lhs.uConst * rhs.uConst);
+ break;
+ case EbtFloat:
+ returnValue.setFConst(CheckedMul<float>(lhs.fConst, rhs.fConst, diag, line));
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator%(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst % constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst % constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::rshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
+ ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
+ if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
+ (rhs.type == EbtUInt && rhs.uConst > 31u))
+ {
+ diag->error(line, "Undefined shift (operand out of range)", ">>", "");
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(0);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(0u);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+ }
+
+ switch (lhs.type)
+ {
+ case EbtInt:
+ {
+ unsigned int shiftOffset = 0;
+ switch (rhs.type)
+ {
+ case EbtInt:
+ shiftOffset = static_cast<unsigned int>(rhs.iConst);
+ break;
+ case EbtUInt:
+ shiftOffset = rhs.uConst;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (shiftOffset > 0)
+ {
+ // ESSL 3.00.6 section 5.9: "If E1 is a signed integer, the right-shift will extend
+ // the sign bit." In C++ shifting negative integers is undefined, so we implement
+ // extending the sign bit manually.
+ int lhsSafe = lhs.iConst;
+ if (lhsSafe == std::numeric_limits<int>::min())
+ {
+ // The min integer needs special treatment because only bit it has set is the
+ // sign bit, which we clear later to implement safe right shift of negative
+ // numbers.
+ lhsSafe = -0x40000000;
+ --shiftOffset;
+ }
+ if (shiftOffset > 0)
+ {
+ bool extendSignBit = false;
+ if (lhsSafe < 0)
+ {
+ extendSignBit = true;
+ // Clear the sign bit so that bitshift right is defined in C++.
+ lhsSafe &= 0x7fffffff;
+ ASSERT(lhsSafe > 0);
+ }
+ returnValue.setIConst(lhsSafe >> shiftOffset);
+
+ // Manually fill in the extended sign bit if necessary.
+ if (extendSignBit)
+ {
+ int extendedSignBit = static_cast<int>(0xffffffffu << (31 - shiftOffset));
+ returnValue.setIConst(returnValue.getIConst() | extendedSignBit);
+ }
+ }
+ else
+ {
+ returnValue.setIConst(lhsSafe);
+ }
+ }
+ else
+ {
+ returnValue.setIConst(lhs.iConst);
+ }
+ break;
+ }
+ case EbtUInt:
+ switch (rhs.type)
+ {
+ case EbtInt:
+ returnValue.setUConst(lhs.uConst >> rhs.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(lhs.uConst >> rhs.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+}
+
+// static
+TConstantUnion TConstantUnion::lshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line)
+{
+ TConstantUnion returnValue;
+ ASSERT(lhs.type == EbtInt || lhs.type == EbtUInt);
+ ASSERT(rhs.type == EbtInt || rhs.type == EbtUInt);
+ if ((rhs.type == EbtInt && (rhs.iConst < 0 || rhs.iConst > 31)) ||
+ (rhs.type == EbtUInt && rhs.uConst > 31u))
+ {
+ diag->error(line, "Undefined shift (operand out of range)", "<<", "");
+ switch (lhs.type)
+ {
+ case EbtInt:
+ returnValue.setIConst(0);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(0u);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+ }
+
+ switch (lhs.type)
+ {
+ case EbtInt:
+ switch (rhs.type)
+ {
+ // Cast to unsigned integer before shifting, since ESSL 3.00.6 section 5.9 says that
+ // lhs is "interpreted as a bit pattern". This also avoids the possibility of signed
+ // integer overflow or undefined shift of a negative integer.
+ case EbtInt:
+ returnValue.setIConst(
+ static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.iConst));
+ break;
+ case EbtUInt:
+ returnValue.setIConst(
+ static_cast<int>(static_cast<uint32_t>(lhs.iConst) << rhs.uConst));
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ case EbtUInt:
+ switch (rhs.type)
+ {
+ case EbtInt:
+ returnValue.setUConst(lhs.uConst << rhs.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(lhs.uConst << rhs.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(constant.type == EbtInt || constant.type == EbtUInt);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst & constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst & constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator|(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst | constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst | constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator^(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtInt:
+ returnValue.setIConst(iConst ^ constant.iConst);
+ break;
+ case EbtUInt:
+ returnValue.setUConst(uConst ^ constant.uConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator&&(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtBool:
+ returnValue.setBConst(bConst && constant.bConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+TConstantUnion TConstantUnion::operator||(const TConstantUnion &constant) const
+{
+ TConstantUnion returnValue;
+ ASSERT(type == constant.type);
+ switch (type)
+ {
+ case EbtBool:
+ returnValue.setBConst(bConst || constant.bConst);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return returnValue;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ConstantUnion.h b/gfx/angle/src/compiler/translator/ConstantUnion.h
new file mode 100755
index 000000000..f6222ce5a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ConstantUnion.h
@@ -0,0 +1,91 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_CONSTANTUNION_H_
+#define COMPILER_TRANSLATOR_CONSTANTUNION_H_
+
+#include <assert.h>
+
+#include "compiler/translator/Common.h"
+#include "compiler/translator/BaseTypes.h"
+
+namespace sh
+{
+
+class TDiagnostics;
+
+class TConstantUnion
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TConstantUnion();
+
+ bool cast(TBasicType newType, const TConstantUnion &constant);
+
+ void setIConst(int i) {iConst = i; type = EbtInt; }
+ void setUConst(unsigned int u) { uConst = u; type = EbtUInt; }
+ void setFConst(float f) {fConst = f; type = EbtFloat; }
+ void setBConst(bool b) {bConst = b; type = EbtBool; }
+
+ int getIConst() const { return iConst; }
+ unsigned int getUConst() const { return uConst; }
+ float getFConst() const { return fConst; }
+ bool getBConst() const { return bConst; }
+
+ bool operator==(const int i) const;
+ bool operator==(const unsigned int u) const;
+ bool operator==(const float f) const;
+ bool operator==(const bool b) const;
+ bool operator==(const TConstantUnion &constant) const;
+ bool operator!=(const int i) const;
+ bool operator!=(const unsigned int u) const;
+ bool operator!=(const float f) const;
+ bool operator!=(const bool b) const;
+ bool operator!=(const TConstantUnion &constant) const;
+ bool operator>(const TConstantUnion &constant) const;
+ bool operator<(const TConstantUnion &constant) const;
+ static TConstantUnion add(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion sub(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion mul(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ TConstantUnion operator%(const TConstantUnion &constant) const;
+ static TConstantUnion rshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ static TConstantUnion lshift(const TConstantUnion &lhs,
+ const TConstantUnion &rhs,
+ TDiagnostics *diag,
+ const TSourceLoc &line);
+ TConstantUnion operator&(const TConstantUnion &constant) const;
+ TConstantUnion operator|(const TConstantUnion &constant) const;
+ TConstantUnion operator^(const TConstantUnion &constant) const;
+ TConstantUnion operator&&(const TConstantUnion &constant) const;
+ TConstantUnion operator||(const TConstantUnion &constant) const;
+
+ TBasicType getType() const { return type; }
+ private:
+ union {
+ int iConst; // used for ivec, scalar ints
+ unsigned int uConst; // used for uvec, scalar uints
+ bool bConst; // used for bvec, scalar bools
+ float fConst; // used for vec, mat, scalar floats
+ };
+
+ TBasicType type;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_
diff --git a/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
new file mode 100755
index 000000000..1c58562fc
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -0,0 +1,194 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a function, and
+// adds a function call to that function in the beginning of main().
+// This enables initialization of globals with uniforms or non-constant globals, as allowed by
+// the WebGL spec. Some initializers referencing non-constants may need to be unfolded into if
+// statements in HLSL - this kind of steps should be done after DeferGlobalInitializers is run.
+//
+
+#include "compiler/translator/DeferGlobalInitializers.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void SetInternalFunctionName(TFunctionSymbolInfo *functionInfo, const char *name)
+{
+ TString nameStr(name);
+ nameStr = TFunction::mangleName(nameStr);
+ TName nameObj(nameStr);
+ nameObj.setInternal(true);
+ functionInfo->setNameObj(nameObj);
+}
+
+TIntermAggregate *CreateFunctionPrototypeNode(const char *name, const int functionId)
+{
+ TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
+
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ TType returnType(EbtVoid);
+ functionNode->setType(returnType);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
+ TIntermBlock *functionBody,
+ const int functionId)
+{
+ TType returnType(EbtVoid);
+ TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
+ TIntermFunctionDefinition *functionNode =
+ new TIntermFunctionDefinition(returnType, paramsNode, functionBody);
+
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
+{
+ TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
+
+ functionNode->setUserDefined();
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ TType returnType(EbtVoid);
+ functionNode->setType(returnType);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+class DeferGlobalInitializersTraverser : public TIntermTraverser
+{
+ public:
+ DeferGlobalInitializersTraverser();
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+
+ void insertInitFunction(TIntermBlock *root);
+
+ private:
+ TIntermSequence mDeferredInitializers;
+};
+
+DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->getOp() == EOpInitialize)
+ {
+ TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
+ ASSERT(symbolNode);
+ TIntermTyped *expression = node->getRight();
+
+ if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
+ (expression->getAsConstantUnion() == nullptr &&
+ !expression->isConstructorWithOnlyConstantUnionParameters())))
+ {
+ // For variables which are not constant, defer their real initialization until
+ // after we initialize uniforms.
+ // Deferral is done also in any cases where the variable has not been constant folded,
+ // since otherwise there's a chance that HLSL output will generate extra statements
+ // from the initializer expression.
+ TIntermBinary *deferredInit =
+ new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
+ mDeferredInitializers.push_back(deferredInit);
+
+ // Change const global to a regular global if its initialization is deferred.
+ // This can happen if ANGLE has not been able to fold the constant expression used
+ // as an initializer.
+ ASSERT(symbolNode->getQualifier() == EvqConst ||
+ symbolNode->getQualifier() == EvqGlobal);
+ if (symbolNode->getQualifier() == EvqConst)
+ {
+ // All of the siblings in the same declaration need to have consistent qualifiers.
+ auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
+ for (TIntermNode *siblingNode : *siblings)
+ {
+ TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
+ if (siblingBinary)
+ {
+ ASSERT(siblingBinary->getOp() == EOpInitialize);
+ siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
+ }
+ siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
+ }
+ // This node is one of the siblings.
+ ASSERT(symbolNode->getQualifier() == EvqGlobal);
+ }
+ // Remove the initializer from the global scope and just declare the global instead.
+ queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
+ }
+ }
+ return false;
+}
+
+void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
+{
+ if (mDeferredInitializers.empty())
+ {
+ return;
+ }
+ const int initFunctionId = TSymbolTable::nextUniqueId();
+
+ const char *functionName = "initializeDeferredGlobals";
+
+ // Add function prototype to the beginning of the shader
+ TIntermAggregate *functionPrototypeNode =
+ CreateFunctionPrototypeNode(functionName, initFunctionId);
+ root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
+
+ // Add function definition to the end of the shader
+ TIntermBlock *functionBodyNode = new TIntermBlock();
+ TIntermSequence *functionBody = functionBodyNode->getSequence();
+ for (const auto &deferredInit : mDeferredInitializers)
+ {
+ functionBody->push_back(deferredInit);
+ }
+ TIntermFunctionDefinition *functionDefinition =
+ CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
+ root->getSequence()->push_back(functionDefinition);
+
+ // Insert call into main function
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
+ if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
+ {
+ TIntermAggregate *functionCallNode =
+ CreateFunctionCallNode(functionName, initFunctionId);
+
+ TIntermBlock *mainBody = nodeFunction->getBody();
+ ASSERT(mainBody != nullptr);
+ mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
+ }
+ }
+}
+
+} // namespace
+
+void DeferGlobalInitializers(TIntermBlock *root)
+{
+ DeferGlobalInitializersTraverser traverser;
+ root->traverse(&traverser);
+
+ // Replace the initializers of the global variables.
+ traverser.updateTree();
+
+ // Add the function with initialization and the call to that.
+ traverser.insertInitFunction(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/DeferGlobalInitializers.h b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.h
new file mode 100755
index 000000000..261ec9001
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a function, and
+// adds a function call to that function in the beginning of main().
+// This enables initialization of globals with uniforms or non-constant globals, as allowed by
+// the WebGL spec. Some initializers referencing non-constants may need to be unfolded into if
+// statements in HLSL - this kind of steps should be done after DeferGlobalInitializers is run.
+//
+
+#ifndef COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
+#define COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
+
+class TIntermBlock;
+
+namespace sh
+{
+void DeferGlobalInitializers(TIntermBlock *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_
diff --git a/gfx/angle/src/compiler/translator/Diagnostics.cpp b/gfx/angle/src/compiler/translator/Diagnostics.cpp
new file mode 100755
index 000000000..3b4168617
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Diagnostics.cpp
@@ -0,0 +1,86 @@
+//
+// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/Diagnostics.h"
+
+#include "common/debug.h"
+#include "compiler/preprocessor/SourceLocation.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+
+namespace sh
+{
+
+TDiagnostics::TDiagnostics(TInfoSink& infoSink) :
+ mInfoSink(infoSink),
+ mNumErrors(0),
+ mNumWarnings(0)
+{
+}
+
+TDiagnostics::~TDiagnostics()
+{
+}
+
+void TDiagnostics::writeInfo(Severity severity,
+ const pp::SourceLocation& loc,
+ const std::string& reason,
+ const std::string& token,
+ const std::string& extra)
+{
+ TPrefixType prefix = EPrefixNone;
+ switch (severity)
+ {
+ case PP_ERROR:
+ ++mNumErrors;
+ prefix = EPrefixError;
+ break;
+ case PP_WARNING:
+ ++mNumWarnings;
+ prefix = EPrefixWarning;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ TInfoSinkBase& sink = mInfoSink.info;
+ /* VC++ format: file(linenum) : error #: 'token' : extrainfo */
+ sink.prefix(prefix);
+ sink.location(loc.file, loc.line);
+ sink << "'" << token << "' : " << reason << " " << extra << "\n";
+}
+
+void TDiagnostics::error(const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ writeInfo(pp::Diagnostics::PP_ERROR, srcLoc, reason, token, extraInfo);
+}
+
+void TDiagnostics::warning(const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ writeInfo(pp::Diagnostics::PP_WARNING, srcLoc, reason, token, extraInfo);
+}
+
+void TDiagnostics::print(ID id,
+ const pp::SourceLocation& loc,
+ const std::string& text)
+{
+ writeInfo(severity(id), loc, message(id), text, "");
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Diagnostics.h b/gfx/angle/src/compiler/translator/Diagnostics.h
new file mode 100755
index 000000000..25a8a3a57
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Diagnostics.h
@@ -0,0 +1,53 @@
+//
+// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_DIAGNOSTICS_H_
+#define COMPILER_TRANSLATOR_DIAGNOSTICS_H_
+
+#include "common/angleutils.h"
+#include "compiler/preprocessor/DiagnosticsBase.h"
+
+namespace sh
+{
+
+class TInfoSink;
+struct TSourceLoc;
+
+class TDiagnostics : public pp::Diagnostics, angle::NonCopyable
+{
+ public:
+ TDiagnostics(TInfoSink& infoSink);
+ ~TDiagnostics() override;
+
+ TInfoSink& infoSink() { return mInfoSink; }
+
+ int numErrors() const { return mNumErrors; }
+ int numWarnings() const { return mNumWarnings; }
+
+ void writeInfo(Severity severity,
+ const pp::SourceLocation& loc,
+ const std::string& reason,
+ const std::string& token,
+ const std::string& extra);
+
+ void error(const TSourceLoc &loc, const char *reason, const char *token, const char *extraInfo);
+ void warning(const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo);
+
+ protected:
+ void print(ID id, const pp::SourceLocation &loc, const std::string &text) override;
+
+ private:
+ TInfoSink& mInfoSink;
+ int mNumErrors;
+ int mNumWarnings;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_DIAGNOSTICS_H_
diff --git a/gfx/angle/src/compiler/translator/DirectiveHandler.cpp b/gfx/angle/src/compiler/translator/DirectiveHandler.cpp
new file mode 100755
index 000000000..073994d3c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/DirectiveHandler.cpp
@@ -0,0 +1,202 @@
+//
+// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/DirectiveHandler.h"
+
+#include <sstream>
+
+#include "angle_gl.h"
+#include "common/debug.h"
+#include "compiler/translator/Diagnostics.h"
+
+namespace sh
+{
+
+static TBehavior getBehavior(const std::string& str)
+{
+ const char kRequire[] = "require";
+ const char kEnable[] = "enable";
+ const char kDisable[] = "disable";
+ const char kWarn[] = "warn";
+
+ if (str == kRequire) return EBhRequire;
+ else if (str == kEnable) return EBhEnable;
+ else if (str == kDisable) return EBhDisable;
+ else if (str == kWarn) return EBhWarn;
+ return EBhUndefined;
+}
+
+TDirectiveHandler::TDirectiveHandler(TExtensionBehavior &extBehavior,
+ TDiagnostics &diagnostics,
+ int &shaderVersion,
+ sh::GLenum shaderType,
+ bool debugShaderPrecisionSupported)
+ : mExtensionBehavior(extBehavior),
+ mDiagnostics(diagnostics),
+ mShaderVersion(shaderVersion),
+ mShaderType(shaderType),
+ mDebugShaderPrecisionSupported(debugShaderPrecisionSupported)
+{
+}
+
+TDirectiveHandler::~TDirectiveHandler()
+{
+}
+
+void TDirectiveHandler::handleError(const pp::SourceLocation& loc,
+ const std::string& msg)
+{
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc, msg, "", "");
+}
+
+void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
+ const std::string& name,
+ const std::string& value,
+ bool stdgl)
+{
+ if (stdgl)
+ {
+ const char kInvariant[] = "invariant";
+ const char kAll[] = "all";
+
+ if (name == kInvariant && value == kAll)
+ {
+ if (mShaderVersion == 300 && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ // ESSL 3.00.4 section 4.6.1
+ mDiagnostics.writeInfo(
+ pp::Diagnostics::PP_ERROR, loc,
+ "#pragma STDGL invariant(all) can not be used in fragment shader", name, value);
+ }
+ mPragma.stdgl.invariantAll = true;
+ }
+ // The STDGL pragma is used to reserve pragmas for use by future
+ // revisions of GLSL. Do not generate an error on unexpected
+ // name and value.
+ return;
+ }
+ else
+ {
+ const char kOptimize[] = "optimize";
+ const char kDebug[] = "debug";
+ const char kDebugShaderPrecision[] = "webgl_debug_shader_precision";
+ const char kOn[] = "on";
+ const char kOff[] = "off";
+
+ bool invalidValue = false;
+ if (name == kOptimize)
+ {
+ if (value == kOn) mPragma.optimize = true;
+ else if (value == kOff) mPragma.optimize = false;
+ else invalidValue = true;
+ }
+ else if (name == kDebug)
+ {
+ if (value == kOn) mPragma.debug = true;
+ else if (value == kOff) mPragma.debug = false;
+ else invalidValue = true;
+ }
+ else if (name == kDebugShaderPrecision && mDebugShaderPrecisionSupported)
+ {
+ if (value == kOn) mPragma.debugShaderPrecision = true;
+ else if (value == kOff) mPragma.debugShaderPrecision = false;
+ else invalidValue = true;
+ }
+ else
+ {
+ mDiagnostics.report(pp::Diagnostics::PP_UNRECOGNIZED_PRAGMA, loc, name);
+ return;
+ }
+
+ if (invalidValue)
+ {
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
+ "invalid pragma value", value,
+ "'on' or 'off' expected");
+ }
+ }
+}
+
+void TDirectiveHandler::handleExtension(const pp::SourceLocation& loc,
+ const std::string& name,
+ const std::string& behavior)
+{
+ const char kExtAll[] = "all";
+
+ TBehavior behaviorVal = getBehavior(behavior);
+ if (behaviorVal == EBhUndefined)
+ {
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
+ "behavior", name, "invalid");
+ return;
+ }
+
+ if (name == kExtAll)
+ {
+ if (behaviorVal == EBhRequire)
+ {
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
+ "extension", name,
+ "cannot have 'require' behavior");
+ }
+ else if (behaviorVal == EBhEnable)
+ {
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
+ "extension", name,
+ "cannot have 'enable' behavior");
+ }
+ else
+ {
+ for (TExtensionBehavior::iterator iter = mExtensionBehavior.begin();
+ iter != mExtensionBehavior.end(); ++iter)
+ iter->second = behaviorVal;
+ }
+ return;
+ }
+
+ TExtensionBehavior::iterator iter = mExtensionBehavior.find(name);
+ if (iter != mExtensionBehavior.end())
+ {
+ iter->second = behaviorVal;
+ return;
+ }
+
+ pp::Diagnostics::Severity severity = pp::Diagnostics::PP_ERROR;
+ switch (behaviorVal) {
+ case EBhRequire:
+ severity = pp::Diagnostics::PP_ERROR;
+ break;
+ case EBhEnable:
+ case EBhWarn:
+ case EBhDisable:
+ severity = pp::Diagnostics::PP_WARNING;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ mDiagnostics.writeInfo(severity, loc,
+ "extension", name, "is not supported");
+}
+
+void TDirectiveHandler::handleVersion(const pp::SourceLocation& loc,
+ int version)
+{
+ if (version == 100 || version == 300 || version == 310)
+ {
+ mShaderVersion = version;
+ }
+ else
+ {
+ std::stringstream stream;
+ stream << version;
+ std::string str = stream.str();
+ mDiagnostics.writeInfo(pp::Diagnostics::PP_ERROR, loc,
+ "version number", str, "not supported");
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/DirectiveHandler.h b/gfx/angle/src/compiler/translator/DirectiveHandler.h
new file mode 100755
index 000000000..a50b08219
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/DirectiveHandler.h
@@ -0,0 +1,57 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_
+#define COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/Pragma.h"
+#include "compiler/preprocessor/DirectiveHandlerBase.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+class TDiagnostics;
+
+class TDirectiveHandler : public pp::DirectiveHandler, angle::NonCopyable
+{
+ public:
+ TDirectiveHandler(TExtensionBehavior &extBehavior,
+ TDiagnostics &diagnostics,
+ int &shaderVersion,
+ sh::GLenum shaderType,
+ bool debugShaderPrecisionSupported);
+ ~TDirectiveHandler() override;
+
+ const TPragma& pragma() const { return mPragma; }
+ const TExtensionBehavior& extensionBehavior() const { return mExtensionBehavior; }
+
+ void handleError(const pp::SourceLocation &loc, const std::string &msg) override;
+
+ void handlePragma(const pp::SourceLocation &loc,
+ const std::string &name,
+ const std::string &value,
+ bool stdgl) override;
+
+ void handleExtension(const pp::SourceLocation &loc,
+ const std::string &name,
+ const std::string &behavior) override;
+
+ void handleVersion(const pp::SourceLocation &loc, int version) override;
+
+ private:
+ TPragma mPragma;
+ TExtensionBehavior& mExtensionBehavior;
+ TDiagnostics& mDiagnostics;
+ int& mShaderVersion;
+ sh::GLenum mShaderType;
+ bool mDebugShaderPrecisionSupported;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_
diff --git a/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
new file mode 100755
index 000000000..a4eeb9b7e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.cpp
@@ -0,0 +1,137 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// gl_FragColor needs to broadcast to all color buffers in ES2 if
+// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
+//
+// We emulate this by replacing all gl_FragColor with gl_FragData[0], and in the end
+// of main() function, assigning gl_FragData[1], ..., gl_FragData[maxDrawBuffers-1]
+// with gl_FragData[0].
+//
+
+#include "compiler/translator/EmulateGLFragColorBroadcast.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class GLFragColorBroadcastTraverser : public TIntermTraverser
+{
+ public:
+ GLFragColorBroadcastTraverser(int maxDrawBuffers)
+ : TIntermTraverser(true, false, false),
+ mMainSequence(nullptr),
+ mGLFragColorUsed(false),
+ mMaxDrawBuffers(maxDrawBuffers)
+ {
+ }
+
+ void broadcastGLFragColor();
+
+ bool isGLFragColorUsed() const { return mGLFragColorUsed; }
+
+ protected:
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+
+ TIntermBinary *constructGLFragDataNode(int index) const;
+ TIntermBinary *constructGLFragDataAssignNode(int index) const;
+
+ private:
+ TIntermSequence *mMainSequence;
+ bool mGLFragColorUsed;
+ int mMaxDrawBuffers;
+};
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataNode(int index) const
+{
+ TType gl_FragDataType = TType(EbtFloat, EbpMedium, EvqFragData, 4);
+ gl_FragDataType.setArraySize(mMaxDrawBuffers);
+
+ TIntermSymbol *symbol = new TIntermSymbol(0, "gl_FragData", gl_FragDataType);
+ TIntermTyped *indexNode = TIntermTyped::CreateIndexNode(index);
+
+ TIntermBinary *binary = new TIntermBinary(EOpIndexDirect, symbol, indexNode);
+ return binary;
+}
+
+TIntermBinary *GLFragColorBroadcastTraverser::constructGLFragDataAssignNode(int index) const
+{
+ TIntermTyped *fragDataIndex = constructGLFragDataNode(index);
+ TIntermTyped *fragDataZero = constructGLFragDataNode(0);
+
+ return new TIntermBinary(EOpAssign, fragDataIndex, fragDataZero);
+}
+
+void GLFragColorBroadcastTraverser::visitSymbol(TIntermSymbol *node)
+{
+ if (node->getSymbol() == "gl_FragColor")
+ {
+ queueReplacement(node, constructGLFragDataNode(0), OriginalNode::IS_DROPPED);
+ mGLFragColorUsed = true;
+ }
+}
+
+bool GLFragColorBroadcastTraverser::visitFunctionDefinition(Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ ASSERT(visit == PreVisit);
+ if (node->getFunctionSymbolInfo()->isMain())
+ {
+ TIntermBlock *body = node->getBody();
+ ASSERT(body);
+ mMainSequence = body->getSequence();
+ }
+ return true;
+}
+
+void GLFragColorBroadcastTraverser::broadcastGLFragColor()
+{
+ ASSERT(mMaxDrawBuffers > 1);
+ if (!mGLFragColorUsed)
+ {
+ return;
+ }
+ ASSERT(mMainSequence);
+ // Now insert statements
+ // gl_FragData[1] = gl_FragData[0];
+ // ...
+ // gl_FragData[maxDrawBuffers - 1] = gl_FragData[0];
+ for (int colorIndex = 1; colorIndex < mMaxDrawBuffers; ++colorIndex)
+ {
+ mMainSequence->insert(mMainSequence->end(), constructGLFragDataAssignNode(colorIndex));
+ }
+}
+
+} // namespace anonymous
+
+void EmulateGLFragColorBroadcast(TIntermNode *root,
+ int maxDrawBuffers,
+ std::vector<sh::OutputVariable> *outputVariables)
+{
+ ASSERT(maxDrawBuffers > 1);
+ GLFragColorBroadcastTraverser traverser(maxDrawBuffers);
+ root->traverse(&traverser);
+ if (traverser.isGLFragColorUsed())
+ {
+ traverser.updateTree();
+ traverser.broadcastGLFragColor();
+ for (auto &var : *outputVariables)
+ {
+ if (var.name == "gl_FragColor")
+ {
+ // TODO(zmo): Find a way to keep the original variable information.
+ var.name = "gl_FragData";
+ var.mappedName = "gl_FragData";
+ var.arraySize = maxDrawBuffers;
+ }
+ }
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h
new file mode 100755
index 000000000..09e8dae6d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/EmulateGLFragColorBroadcast.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Emulate gl_FragColor broadcast behaviors in ES2 where
+// GL_EXT_draw_buffers is explicitly enabled in a fragment shader.
+//
+
+#ifndef COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
+#define COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
+
+#include <vector>
+
+namespace sh
+{
+struct OutputVariable;
+class TIntermNode;
+
+// Replace all gl_FragColor with gl_FragData[0], and in the end of main() function,
+// assign gl_FragData[1] ... gl_FragData[maxDrawBuffers - 1] with gl_FragData[0].
+// If gl_FragColor is in outputVariables, it is replaced by gl_FragData.
+void EmulateGLFragColorBroadcast(TIntermNode *root,
+ int maxDrawBuffers,
+ std::vector<OutputVariable> *outputVariables);
+}
+
+#endif // COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_
diff --git a/gfx/angle/src/compiler/translator/EmulatePrecision.cpp b/gfx/angle/src/compiler/translator/EmulatePrecision.cpp
new file mode 100755
index 000000000..c3c49021c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/EmulatePrecision.cpp
@@ -0,0 +1,726 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/EmulatePrecision.h"
+
+#include <memory>
+
+namespace sh
+{
+
+namespace
+{
+
+class RoundingHelperWriter : angle::NonCopyable
+{
+ public:
+ static RoundingHelperWriter *createHelperWriter(const ShShaderOutput outputLanguage);
+
+ void writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion);
+ void writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr);
+
+ virtual ~RoundingHelperWriter() {}
+
+ protected:
+ RoundingHelperWriter(const ShShaderOutput outputLanguage) : mOutputLanguage(outputLanguage) {}
+ RoundingHelperWriter() = delete;
+
+ const ShShaderOutput mOutputLanguage;
+
+ private:
+ virtual std::string getTypeString(const char *glslType) = 0;
+ virtual void writeFloatRoundingHelpers(TInfoSinkBase &sink) = 0;
+ virtual void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) = 0;
+ virtual void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) = 0;
+};
+
+class RoundingHelperWriterGLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterGLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+class RoundingHelperWriterESSL : public RoundingHelperWriterGLSL
+{
+ public:
+ RoundingHelperWriterESSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriterGLSL(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+};
+
+class RoundingHelperWriterHLSL : public RoundingHelperWriter
+{
+ public:
+ RoundingHelperWriterHLSL(const ShShaderOutput outputLanguage)
+ : RoundingHelperWriter(outputLanguage)
+ {
+ }
+
+ private:
+ std::string getTypeString(const char *glslType) override;
+ void writeFloatRoundingHelpers(TInfoSinkBase &sink) override;
+ void writeVectorRoundingHelpers(TInfoSinkBase &sink, const unsigned int size) override;
+ void writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName) override;
+};
+
+RoundingHelperWriter *RoundingHelperWriter::createHelperWriter(const ShShaderOutput outputLanguage)
+{
+ ASSERT(EmulatePrecision::SupportedInLanguage(outputLanguage));
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ return new RoundingHelperWriterHLSL(outputLanguage);
+ case SH_ESSL_OUTPUT:
+ return new RoundingHelperWriterESSL(outputLanguage);
+ default:
+ return new RoundingHelperWriterGLSL(outputLanguage);
+ }
+}
+
+void RoundingHelperWriter::writeCommonRoundingHelpers(TInfoSinkBase &sink, const int shaderVersion)
+{
+ // Write the angle_frm functions that round floating point numbers to
+ // half precision, and angle_frl functions that round them to minimum lowp
+ // precision.
+
+ writeFloatRoundingHelpers(sink);
+ writeVectorRoundingHelpers(sink, 2);
+ writeVectorRoundingHelpers(sink, 3);
+ writeVectorRoundingHelpers(sink, 4);
+ if (shaderVersion > 100)
+ {
+ for (unsigned int columns = 2; columns <= 4; ++columns)
+ {
+ for (unsigned int rows = 2; rows <= 4; ++rows)
+ {
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frm");
+ writeMatrixRoundingHelper(sink, columns, rows, "angle_frl");
+ }
+ }
+ }
+ else
+ {
+ for (unsigned int size = 2; size <= 4; ++size)
+ {
+ writeMatrixRoundingHelper(sink, size, size, "angle_frm");
+ writeMatrixRoundingHelper(sink, size, size, "angle_frl");
+ }
+ }
+}
+
+void RoundingHelperWriter::writeCompoundAssignmentHelper(TInfoSinkBase &sink,
+ const char *lType,
+ const char *rType,
+ const char *opStr,
+ const char *opNameStr)
+{
+ std::string lTypeStr = getTypeString(lType);
+ std::string rTypeStr = getTypeString(rType);
+
+ // Note that y should be passed through angle_frm at the function call site,
+ // but x can't be passed through angle_frm there since it is an inout parameter.
+ // So only pass x and the result through angle_frm here.
+ // clang-format off
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frm(angle_frm(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ sink <<
+ lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
+ " x = angle_frl(angle_frm(x) " << opStr << " y);\n"
+ " return x;\n"
+ "}\n";
+ // clang-format on
+}
+
+std::string RoundingHelperWriterGLSL::getTypeString(const char *glslType)
+{
+ return glslType;
+}
+
+std::string RoundingHelperWriterESSL::getTypeString(const char *glslType)
+{
+ std::stringstream typeStrStr;
+ typeStrStr << "highp " << glslType;
+ return typeStrStr.str();
+}
+
+void RoundingHelperWriterGLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
+ // Unoptimized version of angle_frm for single floats:
+ //
+ // int webgl_maxNormalExponent(in int exponentBits)
+ // {
+ // int possibleExponents = int(exp2(float(exponentBits)));
+ // int exponentBias = possibleExponents / 2 - 1;
+ // int allExponentBitsOne = possibleExponents - 1;
+ // return (allExponentBitsOne - 1) - exponentBias;
+ // }
+ //
+ // float angle_frm(in float x)
+ // {
+ // int mantissaBits = 10;
+ // int exponentBits = 5;
+ // float possibleMantissas = exp2(float(mantissaBits));
+ // float mantissaMax = 2.0 - 1.0 / possibleMantissas;
+ // int maxNE = webgl_maxNormalExponent(exponentBits);
+ // float max = exp2(float(maxNE)) * mantissaMax;
+ // if (x > max)
+ // {
+ // return max;
+ // }
+ // if (x < -max)
+ // {
+ // return -max;
+ // }
+ // float exponent = floor(log2(abs(x)));
+ // if (abs(x) == 0.0 || exponent < -float(maxNE))
+ // {
+ // return 0.0 * sign(x)
+ // }
+ // x = x * exp2(-(exponent - float(mantissaBits)));
+ // x = sign(x) * floor(abs(x));
+ // return x * exp2(exponent - float(mantissaBits));
+ // }
+
+ // All numbers with a magnitude less than 2^-15 are subnormal, and are
+ // flushed to zero.
+
+ // Note the constant numbers below:
+ // a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
+ // 2^15, the maximum normal exponent.
+ // b) 10.0 is the number of mantissa bits.
+ // c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
+ // of mantissa bits.
+ // d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
+ // only affect the result of log2 on x where abs(x) < 1e-22. Since these
+ // numbers will be flushed to zero either way (2^-15 is the smallest
+ // normal positive number), this does not introduce any error.
+
+ std::string floatType = getTypeString("float");
+
+ // clang-format off
+ sink <<
+ floatType << " angle_frm(in " << floatType << " x) {\n"
+ " x = clamp(x, -65504.0, 65504.0);\n"
+ " " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
+ " bool isNonZero = (exponent >= -25.0);\n"
+ " x = x * exp2(-exponent);\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * exp2(exponent) * float(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ floatType << " angle_frl(in " << floatType << " x) {\n"
+ " x = clamp(x, -2.0, 2.0);\n"
+ " x = x * 256.0;\n"
+ " x = sign(x) * floor(abs(x));\n"
+ " return x * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterGLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "vec" << size;
+ std::string vecType = getTypeString(vecTypeStrStr.str().c_str());
+
+ // clang-format off
+ sink <<
+ vecType << " angle_frm(in " << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ vecType << " angle_frl(in " << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterGLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
+{
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "mat" << columns;
+ if (rows != columns)
+ {
+ matTypeStrStr << "x" << rows;
+ }
+ std::string matType = getTypeString(matTypeStrStr.str().c_str());
+
+ sink << matType << " " << functionName << "(in " << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
+ {
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
+ }
+
+ sink << " return rounded;\n"
+ "}\n";
+}
+
+static const char *GetHLSLTypeStr(const char *floatTypeStr)
+{
+ if (strcmp(floatTypeStr, "float") == 0)
+ {
+ return "float";
+ }
+ if (strcmp(floatTypeStr, "vec2") == 0)
+ {
+ return "float2";
+ }
+ if (strcmp(floatTypeStr, "vec3") == 0)
+ {
+ return "float3";
+ }
+ if (strcmp(floatTypeStr, "vec4") == 0)
+ {
+ return "float4";
+ }
+ if (strcmp(floatTypeStr, "mat2") == 0)
+ {
+ return "float2x2";
+ }
+ if (strcmp(floatTypeStr, "mat3") == 0)
+ {
+ return "float3x3";
+ }
+ if (strcmp(floatTypeStr, "mat4") == 0)
+ {
+ return "float4x4";
+ }
+ if (strcmp(floatTypeStr, "mat2x3") == 0)
+ {
+ return "float2x3";
+ }
+ if (strcmp(floatTypeStr, "mat2x4") == 0)
+ {
+ return "float2x4";
+ }
+ if (strcmp(floatTypeStr, "mat3x2") == 0)
+ {
+ return "float3x2";
+ }
+ if (strcmp(floatTypeStr, "mat3x4") == 0)
+ {
+ return "float3x4";
+ }
+ if (strcmp(floatTypeStr, "mat4x2") == 0)
+ {
+ return "float4x2";
+ }
+ if (strcmp(floatTypeStr, "mat4x3") == 0)
+ {
+ return "float4x3";
+ }
+ UNREACHABLE();
+ return nullptr;
+}
+
+std::string RoundingHelperWriterHLSL::getTypeString(const char *glslType)
+{
+ return GetHLSLTypeStr(glslType);
+}
+
+void RoundingHelperWriterHLSL::writeFloatRoundingHelpers(TInfoSinkBase &sink)
+{
+ // In HLSL scalars are the same as 1-vectors.
+ writeVectorRoundingHelpers(sink, 1);
+}
+
+void RoundingHelperWriterHLSL::writeVectorRoundingHelpers(TInfoSinkBase &sink,
+ const unsigned int size)
+{
+ std::stringstream vecTypeStrStr;
+ vecTypeStrStr << "float" << size;
+ std::string vecType = vecTypeStrStr.str();
+
+ // clang-format off
+ sink <<
+ vecType << " angle_frm(" << vecType << " v) {\n"
+ " v = clamp(v, -65504.0, 65504.0);\n"
+ " " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
+ " bool" << size << " isNonZero = exponent < -25.0;\n"
+ " v = v * exp2(-exponent);\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * exp2(exponent) * (float" << size << ")(isNonZero);\n"
+ "}\n";
+
+ sink <<
+ vecType << " angle_frl(" << vecType << " v) {\n"
+ " v = clamp(v, -2.0, 2.0);\n"
+ " v = v * 256.0;\n"
+ " v = sign(v) * floor(abs(v));\n"
+ " return v * 0.00390625;\n"
+ "}\n";
+ // clang-format on
+}
+
+void RoundingHelperWriterHLSL::writeMatrixRoundingHelper(TInfoSinkBase &sink,
+ const unsigned int columns,
+ const unsigned int rows,
+ const char *functionName)
+{
+ std::stringstream matTypeStrStr;
+ matTypeStrStr << "float" << columns << "x" << rows;
+ std::string matType = matTypeStrStr.str();
+
+ sink << matType << " " << functionName << "(" << matType << " m) {\n"
+ << " " << matType << " rounded;\n";
+
+ for (unsigned int i = 0; i < columns; ++i)
+ {
+ sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
+ }
+
+ sink << " return rounded;\n"
+ "}\n";
+}
+
+bool canRoundFloat(const TType &type)
+{
+ return type.getBasicType() == EbtFloat && !type.isArray() &&
+ (type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
+}
+
+TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
+{
+ TIntermAggregate *callNode = new TIntermAggregate();
+ callNode->setOp(EOpFunctionCall);
+ TName nameObj(TFunction::mangleName(name));
+ nameObj.setInternal(true);
+ callNode->getFunctionSymbolInfo()->setNameObj(nameObj);
+ callNode->getSequence()->push_back(child);
+ return callNode;
+}
+
+TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
+{
+ TString roundFunctionName;
+ if (roundedChild->getPrecision() == EbpMedium)
+ roundFunctionName = "angle_frm";
+ else
+ roundFunctionName = "angle_frl";
+ TIntermAggregate *callNode = createInternalFunctionCallNode(roundFunctionName, roundedChild);
+ callNode->setType(roundedChild->getType());
+ return callNode;
+}
+
+TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
+{
+ std::stringstream strstr;
+ if (left->getPrecision() == EbpMedium)
+ strstr << "angle_compound_" << opNameStr << "_frm";
+ else
+ strstr << "angle_compound_" << opNameStr << "_frl";
+ TString functionName = strstr.str().c_str();
+ TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
+ callNode->getSequence()->push_back(right);
+ return callNode;
+}
+
+bool parentUsesResult(TIntermNode* parent, TIntermNode* node)
+{
+ if (!parent)
+ {
+ return false;
+ }
+
+ TIntermBlock *blockParent = parent->getAsBlock();
+ // If the parent is a block, the result is not assigned anywhere,
+ // so rounding it is not needed. In particular, this can avoid a lot of
+ // unnecessary rounding of unused return values of assignment.
+ if (blockParent)
+ {
+ return false;
+ }
+ TIntermBinary *binaryParent = parent->getAsBinaryNode();
+ if (binaryParent && binaryParent->getOp() == EOpComma && (binaryParent->getRight() != node))
+ {
+ return false;
+ }
+ return true;
+}
+
+} // namespace anonymous
+
+EmulatePrecision::EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion)
+ : TLValueTrackingTraverser(true, true, true, symbolTable, shaderVersion),
+ mDeclaringVariables(false)
+{}
+
+void EmulatePrecision::visitSymbol(TIntermSymbol *node)
+{
+ if (canRoundFloat(node->getType()) && !mDeclaringVariables && !isLValueRequiredHere())
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+}
+
+
+bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
+{
+ bool visitChildren = true;
+
+ TOperator op = node->getOp();
+
+ // RHS of initialize is not being declared.
+ if (op == EOpInitialize && visit == InVisit)
+ mDeclaringVariables = false;
+
+ if ((op == EOpIndexDirectStruct) && visit == InVisit)
+ visitChildren = false;
+
+ if (visit != PreVisit)
+ return visitChildren;
+
+ const TType& type = node->getType();
+ bool roundFloat = canRoundFloat(type);
+
+ if (roundFloat) {
+ switch (op) {
+ // Math operators that can result in a float may need to apply rounding to the return
+ // value. Note that in the case of assignment, the rounding is applied to its return
+ // value here, not the value being assigned.
+ case EOpAssign:
+ case EOpAdd:
+ case EOpSub:
+ case EOpMul:
+ case EOpDiv:
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ {
+ TIntermNode *parent = getParentNode();
+ if (!parentUsesResult(parent, node))
+ {
+ break;
+ }
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ break;
+ }
+
+ // Compound assignment cases need to replace the operator with a function call.
+ case EOpAddAssign:
+ {
+ mEmulateCompoundAdd.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "add");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpSubAssign:
+ {
+ mEmulateCompoundSub.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "sub");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ {
+ mEmulateCompoundMul.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "mul");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ case EOpDivAssign:
+ {
+ mEmulateCompoundDiv.insert(
+ TypePair(type.getBuiltInTypeNameString(),
+ node->getRight()->getType().getBuiltInTypeNameString()));
+ TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(
+ node->getLeft(), node->getRight(), "div");
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ break;
+ }
+ default:
+ // The rest of the binary operations should not need precision emulation.
+ break;
+ }
+ }
+ return visitChildren;
+}
+
+bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ // Variable or interface block declaration.
+ if (visit == PreVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ return true;
+}
+
+bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ bool visitChildren = true;
+ switch (node->getOp())
+ {
+ case EOpConstructStruct:
+ break;
+ case EOpPrototype:
+ visitChildren = false;
+ break;
+ case EOpParameters:
+ visitChildren = false;
+ break;
+ case EOpInvariantDeclaration:
+ visitChildren = false;
+ break;
+ case EOpFunctionCall:
+ {
+ // Function call.
+ if (visit == PreVisit)
+ {
+ // User-defined function return values are not rounded, this relies on that
+ // calculations producing the value were rounded.
+ TIntermNode *parent = getParentNode();
+ if (canRoundFloat(node->getType()) && !isInFunctionMap(node) &&
+ parentUsesResult(parent, node))
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ }
+ break;
+ }
+ default:
+ TIntermNode *parent = getParentNode();
+ if (canRoundFloat(node->getType()) && visit == PreVisit && parentUsesResult(parent, node))
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ break;
+ }
+ return visitChildren;
+}
+
+bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
+{
+ switch (node->getOp())
+ {
+ case EOpNegative:
+ case EOpVectorLogicalNot:
+ case EOpLogicalNot:
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ break;
+ default:
+ if (canRoundFloat(node->getType()) && visit == PreVisit)
+ {
+ TIntermNode *replacement = createRoundingFunctionCallNode(node);
+ queueReplacement(node, replacement, OriginalNode::BECOMES_CHILD);
+ }
+ break;
+ }
+
+ return true;
+}
+
+void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase &sink,
+ const int shaderVersion,
+ const ShShaderOutput outputLanguage)
+{
+ std::unique_ptr<RoundingHelperWriter> roundingHelperWriter(
+ RoundingHelperWriter::createHelperWriter(outputLanguage));
+
+ roundingHelperWriter->writeCommonRoundingHelpers(sink, shaderVersion);
+
+ EmulationSet::const_iterator it;
+ for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "+", "add");
+ for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "-", "sub");
+ for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "/", "div");
+ for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
+ roundingHelperWriter->writeCompoundAssignmentHelper(sink, it->lType, it->rType, "*", "mul");
+}
+
+// static
+bool EmulatePrecision::SupportedInLanguage(const ShShaderOutput outputLanguage)
+{
+ switch (outputLanguage)
+ {
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_ESSL_OUTPUT:
+ return true;
+ default:
+ // Other languages not yet supported
+ return (outputLanguage == SH_GLSL_COMPATIBILITY_OUTPUT ||
+ sh::IsGLSL130OrNewer(outputLanguage));
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/EmulatePrecision.h b/gfx/angle/src/compiler/translator/EmulatePrecision.h
new file mode 100755
index 000000000..deb49cd2c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/EmulatePrecision.h
@@ -0,0 +1,72 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
+#define COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+#include "GLSLANG/ShaderLang.h"
+
+// This class gathers all compound assignments from the AST and can then write
+// the functions required for their precision emulation. This way there is no
+// need to write a huge number of variations of the emulated compound assignment
+// to every translated shader with emulation enabled.
+
+namespace sh
+{
+
+class EmulatePrecision : public TLValueTrackingTraverser
+{
+ public:
+ EmulatePrecision(const TSymbolTable &symbolTable, int shaderVersion);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+
+ void writeEmulationHelpers(TInfoSinkBase &sink,
+ const int shaderVersion,
+ const ShShaderOutput outputLanguage);
+
+ static bool SupportedInLanguage(const ShShaderOutput outputLanguage);
+
+ private:
+ struct TypePair
+ {
+ TypePair(const char *l, const char *r)
+ : lType(l), rType(r) { }
+
+ const char *lType;
+ const char *rType;
+ };
+
+ struct TypePairComparator
+ {
+ bool operator() (const TypePair& l, const TypePair& r) const
+ {
+ if (l.lType == r.lType)
+ return l.rType < r.rType;
+ return l.lType < r.lType;
+ }
+ };
+
+ typedef std::set<TypePair, TypePairComparator> EmulationSet;
+ EmulationSet mEmulateCompoundAdd;
+ EmulationSet mEmulateCompoundSub;
+ EmulationSet mEmulateCompoundMul;
+ EmulationSet mEmulateCompoundDiv;
+
+ bool mDeclaringVariables;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
diff --git a/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
new file mode 100755
index 000000000..c5ff7b36b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.cpp
@@ -0,0 +1,154 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of the integer pow expressions HLSL bug workaround.
+// See header for more info.
+
+#include "compiler/translator/ExpandIntegerPowExpressions.h"
+
+#include <cmath>
+#include <cstdlib>
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root, unsigned int *tempIndex);
+
+ private:
+ Traverser();
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+void Traverser::Apply(TIntermNode *root, unsigned int *tempIndex)
+{
+ Traverser traverser;
+ traverser.useTemporaryIndex(tempIndex);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+Traverser::Traverser() : TIntermTraverser(true, false, false)
+{
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+ nextTemporaryIndex();
+}
+
+bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Test 0: skip non-pow operators.
+ if (node->getOp() != EOpPow)
+ {
+ return true;
+ }
+
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence->size() == 2u);
+ const TIntermConstantUnion *constantNode = sequence->at(1)->getAsConstantUnion();
+
+ // Test 1: check for a single constant.
+ if (!constantNode || constantNode->getNominalSize() != 1)
+ {
+ return true;
+ }
+
+ const TConstantUnion *constant = constantNode->getUnionArrayPointer();
+
+ TConstantUnion asFloat;
+ asFloat.cast(EbtFloat, *constant);
+
+ float value = asFloat.getFConst();
+
+ // Test 2: value is in the problematic range.
+ if (value < -5.0f || value > 9.0f)
+ {
+ return true;
+ }
+
+ // Test 3: value is integer or pretty close to an integer.
+ float absval = std::abs(value);
+ float frac = absval - std::round(absval);
+ if (frac > 0.0001f)
+ {
+ return true;
+ }
+
+ // Test 4: skip -1, 0, and 1
+ int exponent = static_cast<int>(value);
+ int n = std::abs(exponent);
+ if (n < 2)
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround.
+ nextTemporaryIndex();
+
+ TIntermTyped *lhs = sequence->at(0)->getAsTyped();
+ ASSERT(lhs);
+
+ TIntermDeclaration *init = createTempInitDeclaration(lhs);
+ TIntermTyped *current = createTempSymbol(lhs->getType());
+
+ insertStatementInParentBlock(init);
+
+ // Create a chain of n-1 multiples.
+ for (int i = 1; i < n; ++i)
+ {
+ TIntermBinary *mul = new TIntermBinary(EOpMul, current, createTempSymbol(lhs->getType()));
+ mul->setLine(node->getLine());
+ current = mul;
+ }
+
+ // For negative pow, compute the reciprocal of the positive pow.
+ if (exponent < 0)
+ {
+ TConstantUnion *oneVal = new TConstantUnion();
+ oneVal->setFConst(1.0f);
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(oneVal, node->getType());
+ TIntermBinary *div = new TIntermBinary(EOpDiv, oneNode, current);
+ current = div;
+ }
+
+ queueReplacement(node, current, OriginalNode::IS_DROPPED);
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void ExpandIntegerPowExpressions(TIntermNode *root, unsigned int *tempIndex)
+{
+ Traverser::Apply(root, tempIndex);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.h b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.h
new file mode 100755
index 000000000..8bc8c9664
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ExpandIntegerPowExpressions.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug in the HLSL compiler optimizer with "pow" that
+// manifests under the following conditions:
+//
+// - If pow() has a literal exponent value
+// - ... and this value is integer or within 10e-6 of an integer
+// - ... and it is in {-4, -3, -2, 2, 3, 4, 5, 6, 7, 8}
+//
+// The workaround is to replace the pow with a series of multiplies.
+// See http://anglebug.com/851
+
+#ifndef COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
+#define COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
+
+class TIntermNode;
+
+namespace sh
+{
+
+void ExpandIntegerPowExpressions(TIntermNode *root, unsigned int *tempIndex);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_EXPANDINTEGERPOWEXPRESSIONS_H_
diff --git a/gfx/angle/src/compiler/translator/ExtensionBehavior.h b/gfx/angle/src/compiler/translator/ExtensionBehavior.h
new file mode 100755
index 000000000..782c1c921
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ExtensionBehavior.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
+#define COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
+
+#include <map>
+#include <string>
+
+typedef enum
+{
+ EBhRequire,
+ EBhEnable,
+ EBhWarn,
+ EBhDisable,
+ EBhUndefined
+} TBehavior;
+
+inline const char* getBehaviorString(TBehavior b)
+{
+ switch(b)
+ {
+ case EBhRequire: return "require";
+ case EBhEnable: return "enable";
+ case EBhWarn: return "warn";
+ case EBhDisable: return "disable";
+ default: return NULL;
+ }
+}
+
+// Mapping between extension name and behavior.
+typedef std::map<std::string, TBehavior> TExtensionBehavior;
+
+inline bool IsExtensionEnabled(const TExtensionBehavior &extBehavior, const char *extension)
+{
+ auto iter = extBehavior.find(extension);
+ return iter != extBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire);
+}
+
+#endif // COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_
diff --git a/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp b/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp
new file mode 100755
index 000000000..5b5dc580e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ExtensionGLSL.cpp
@@ -0,0 +1,105 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ExtensionGLSL.cpp: Implements the TExtensionGLSL class that tracks GLSL extension requirements
+// of shaders.
+
+#include "compiler/translator/ExtensionGLSL.h"
+
+#include "compiler/translator/VersionGLSL.h"
+
+namespace sh
+{
+
+TExtensionGLSL::TExtensionGLSL(ShShaderOutput output)
+ : TIntermTraverser(true, false, false), mTargetVersion(ShaderOutputTypeToGLSLVersion(output))
+{
+}
+
+const std::set<std::string> &TExtensionGLSL::getEnabledExtensions() const
+{
+ return mEnabledExtensions;
+}
+
+const std::set<std::string> &TExtensionGLSL::getRequiredExtensions() const
+{
+ return mRequiredExtensions;
+}
+
+bool TExtensionGLSL::visitUnary(Visit, TIntermUnary *node)
+{
+ checkOperator(node);
+
+ return true;
+}
+
+bool TExtensionGLSL::visitAggregate(Visit, TIntermAggregate *node)
+{
+ checkOperator(node);
+
+ return true;
+}
+
+void TExtensionGLSL::checkOperator(TIntermOperator *node)
+{
+ if (mTargetVersion < GLSL_VERSION_130)
+ {
+ return;
+ }
+
+ switch (node->getOp())
+ {
+ case EOpAbs:
+ break;
+
+ case EOpSign:
+ break;
+
+ case EOpMix:
+ break;
+
+ case EOpFloatBitsToInt:
+ case EOpFloatBitsToUint:
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ if (mTargetVersion < GLSL_VERSION_330)
+ {
+ // Bit conversion functions cannot be emulated.
+ mRequiredExtensions.insert("GL_ARB_shader_bit_encoding");
+ }
+ break;
+
+ case EOpPackSnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackHalf2x16:
+ if (mTargetVersion < GLSL_VERSION_420)
+ {
+ mEnabledExtensions.insert("GL_ARB_shading_language_packing");
+
+ if (mTargetVersion < GLSL_VERSION_330)
+ {
+ // floatBitsToUint and uintBitsToFloat are needed to emulate
+ // packHalf2x16 and unpackHalf2x16 respectively and cannot be
+ // emulated themselves.
+ mRequiredExtensions.insert("GL_ARB_shader_bit_encoding");
+ }
+ }
+ break;
+
+ case EOpPackUnorm2x16:
+ case EOpUnpackUnorm2x16:
+ if (mTargetVersion < GLSL_VERSION_410)
+ {
+ mEnabledExtensions.insert("GL_ARB_shading_language_packing");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ExtensionGLSL.h b/gfx/angle/src/compiler/translator/ExtensionGLSL.h
new file mode 100755
index 000000000..3c2dbe074
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ExtensionGLSL.h
@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ExtensionGLSL.h: Defines the TExtensionGLSL class that tracks GLSL extension requirements of
+// shaders.
+
+#ifndef COMPILER_TRANSLATOR_EXTENSIONGLSL_H_
+#define COMPILER_TRANSLATOR_EXTENSIONGLSL_H_
+
+#include <set>
+#include <string>
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+// Traverses the intermediate tree to determine which GLSL extensions are required
+// to support the shader.
+class TExtensionGLSL : public TIntermTraverser
+{
+ public:
+ TExtensionGLSL(ShShaderOutput output);
+
+ const std::set<std::string> &getEnabledExtensions() const;
+ const std::set<std::string> &getRequiredExtensions() const;
+
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ private:
+ void checkOperator(TIntermOperator *node);
+
+ int mTargetVersion;
+
+ std::set<std::string> mEnabledExtensions;
+ std::set<std::string> mRequiredExtensions;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_
diff --git a/gfx/angle/src/compiler/translator/FlagStd140Structs.cpp b/gfx/angle/src/compiler/translator/FlagStd140Structs.cpp
new file mode 100755
index 000000000..a751b768b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/FlagStd140Structs.cpp
@@ -0,0 +1,77 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/FlagStd140Structs.h"
+
+namespace sh
+{
+
+bool FlagStd140Structs::visitBinary(Visit visit, TIntermBinary *binaryNode)
+{
+ if (binaryNode->getRight()->getBasicType() == EbtStruct)
+ {
+ switch (binaryNode->getOp())
+ {
+ case EOpIndexDirectInterfaceBlock:
+ case EOpIndexDirectStruct:
+ if (isInStd140InterfaceBlock(binaryNode->getLeft()))
+ {
+ mFlaggedNodes.push_back(binaryNode);
+ }
+ break;
+
+ default: break;
+ }
+ return false;
+ }
+
+ if (binaryNode->getOp() == EOpIndexDirectStruct)
+ {
+ return false;
+ }
+
+ return visit == PreVisit;
+}
+
+void FlagStd140Structs::visitSymbol(TIntermSymbol *symbol)
+{
+ if (isInStd140InterfaceBlock(symbol) && symbol->getBasicType() == EbtStruct)
+ {
+ mFlaggedNodes.push_back(symbol);
+ }
+}
+
+bool FlagStd140Structs::isInStd140InterfaceBlock(TIntermTyped *node) const
+{
+ TIntermBinary *binaryNode = node->getAsBinaryNode();
+
+ if (binaryNode)
+ {
+ return isInStd140InterfaceBlock(binaryNode->getLeft());
+ }
+
+ const TType &type = node->getType();
+
+ // determine if we are in the standard layout
+ const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ return (interfaceBlock->blockStorage() == EbsStd140);
+ }
+
+ return false;
+}
+
+std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node)
+{
+ FlagStd140Structs flaggingTraversal;
+
+ node->traverse(&flaggingTraversal);
+
+ return flaggingTraversal.getFlaggedNodes();
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/FlagStd140Structs.h b/gfx/angle/src/compiler/translator/FlagStd140Structs.h
new file mode 100755
index 000000000..cfcd775af
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/FlagStd140Structs.h
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
+#define COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+// This class finds references to nested structs of std140 blocks that access
+// the nested struct "by value", where the padding added in the translator
+// conflicts with the "natural" unpadded type.
+class FlagStd140Structs : public TIntermTraverser
+{
+ public:
+
+ FlagStd140Structs()
+ : TIntermTraverser(true, false, false)
+ {
+ }
+
+ const std::vector<TIntermTyped *> getFlaggedNodes() const { return mFlaggedNodes; }
+
+ protected:
+ bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
+ void visitSymbol(TIntermSymbol *symbol) override;
+
+ private:
+ bool isInStd140InterfaceBlock(TIntermTyped *node) const;
+
+ std::vector<TIntermTyped *> mFlaggedNodes;
+};
+
+std::vector<TIntermTyped *> FlagStd140ValueStructs(TIntermNode *node);
+
+}
+
+#endif // COMPILER_TRANSLATOR_FLAGSTD140STRUCTS_H_
diff --git a/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
new file mode 100755
index 000000000..58cce845f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
@@ -0,0 +1,102 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ForLoopUnroll.h"
+
+#include "compiler/translator/ValidateLimitations.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
+{
+ if (mUnrollCondition != kSamplerArrayIndex)
+ return true;
+
+ // If a sampler array index is also the loop index,
+ // 1) if the index type is integer, mark the loop for unrolling;
+ // 2) if the index type if float, set a flag to later fail compile.
+ switch (node->getOp())
+ {
+ case EOpIndexIndirect:
+ if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
+ {
+ TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
+ if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
+ {
+ mVisitSamplerArrayIndexNodeInsideLoop = true;
+ node->getRight()->traverse(this);
+ mVisitSamplerArrayIndexNodeInsideLoop = false;
+ // We have already visited all the children.
+ return false;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
+{
+ bool canBeUnrolled = mHasRunLoopValidation;
+ if (!mHasRunLoopValidation)
+ {
+ canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node);
+ }
+ if (mUnrollCondition == kIntegerIndex && canBeUnrolled)
+ {
+ // Check if loop index type is integer.
+ // This is called after ValidateLimitations pass, so the loop has the limited form specified
+ // in ESSL 1.00 appendix A.
+ TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+ TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+ if (symbol->getBasicType() == EbtInt)
+ node->setUnrollFlag(true);
+ }
+
+ TIntermNode *body = node->getBody();
+ if (body != nullptr)
+ {
+ if (canBeUnrolled)
+ {
+ mLoopStack.push(node);
+ body->traverse(this);
+ mLoopStack.pop();
+ }
+ else
+ {
+ body->traverse(this);
+ }
+ }
+ // The loop is fully processed - no need to visit children.
+ return false;
+}
+
+void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
+{
+ if (!mVisitSamplerArrayIndexNodeInsideLoop)
+ return;
+ TIntermLoop *loop = mLoopStack.findLoop(symbol);
+ if (loop)
+ {
+ switch (symbol->getBasicType())
+ {
+ case EbtFloat:
+ mSamplerArrayIndexIsFloatLoopIndex = true;
+ break;
+ case EbtInt:
+ loop->setUnrollFlag(true);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ForLoopUnroll.h b/gfx/angle/src/compiler/translator/ForLoopUnroll.h
new file mode 100755
index 000000000..cda89b29f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.h
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
+#define COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
+
+#include "compiler/translator/LoopInfo.h"
+
+namespace sh
+{
+
+// This class detects for-loops that needs to be unrolled.
+// Currently we support two unroll conditions:
+// 1) kForLoopWithIntegerIndex: unroll if the index type is integer.
+// 2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
+// is also the loop integer index, and reject and fail a compile
+// where a sampler array index is also the loop float index.
+class ForLoopUnrollMarker : public TIntermTraverser
+{
+ public:
+ enum UnrollCondition
+ {
+ kIntegerIndex,
+ kSamplerArrayIndex
+ };
+
+ ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation)
+ : TIntermTraverser(true, false, false),
+ mUnrollCondition(condition),
+ mSamplerArrayIndexIsFloatLoopIndex(false),
+ mVisitSamplerArrayIndexNodeInsideLoop(false),
+ mHasRunLoopValidation(hasRunLoopValidation)
+ {
+ }
+
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitLoop(Visit, TIntermLoop *node) override;
+ void visitSymbol(TIntermSymbol *node) override;
+
+ bool samplerArrayIndexIsFloatLoopIndex() const
+ {
+ return mSamplerArrayIndexIsFloatLoopIndex;
+ }
+
+ private:
+ UnrollCondition mUnrollCondition;
+ TLoopStack mLoopStack;
+ bool mSamplerArrayIndexIsFloatLoopIndex;
+ bool mVisitSamplerArrayIndexNodeInsideLoop;
+ bool mHasRunLoopValidation;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
diff --git a/gfx/angle/src/compiler/translator/HashNames.h b/gfx/angle/src/compiler/translator/HashNames.h
new file mode 100755
index 000000000..09c959f9d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/HashNames.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_HASHNAMES_H_
+#define COMPILER_TRANSLATOR_HASHNAMES_H_
+
+#include <map>
+
+#include "compiler/translator/IntermNode.h"
+
+#define HASHED_NAME_PREFIX "webgl_"
+
+typedef std::map<TPersistString, TPersistString> NameMap;
+
+#endif // COMPILER_TRANSLATOR_HASHNAMES_H_
diff --git a/gfx/angle/src/compiler/translator/InfoSink.cpp b/gfx/angle/src/compiler/translator/InfoSink.cpp
new file mode 100755
index 000000000..e71fe51d3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InfoSink.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/InfoSink.h"
+
+namespace sh
+{
+
+void TInfoSinkBase::prefix(TPrefixType p) {
+ switch(p) {
+ case EPrefixNone:
+ break;
+ case EPrefixWarning:
+ sink.append("WARNING: ");
+ break;
+ case EPrefixError:
+ sink.append("ERROR: ");
+ break;
+ case EPrefixInternalError:
+ sink.append("INTERNAL ERROR: ");
+ break;
+ case EPrefixUnimplemented:
+ sink.append("UNIMPLEMENTED: ");
+ break;
+ case EPrefixNote:
+ sink.append("NOTE: ");
+ break;
+ default:
+ sink.append("UNKOWN ERROR: ");
+ break;
+ }
+}
+
+void TInfoSinkBase::location(int file, int line) {
+ TPersistStringStream stream;
+ if (line)
+ stream << file << ":" << line;
+ else
+ stream << file << ":? ";
+ stream << ": ";
+
+ sink.append(stream.str());
+}
+
+void TInfoSinkBase::location(const TSourceLoc& loc) {
+ location(loc.first_file, loc.first_line);
+}
+
+void TInfoSinkBase::message(TPrefixType p, const TSourceLoc& loc, const char* m) {
+ prefix(p);
+ location(loc);
+ sink.append(m);
+ sink.append("\n");
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/InfoSink.h b/gfx/angle/src/compiler/translator/InfoSink.h
new file mode 100755
index 000000000..b18e5861b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InfoSink.h
@@ -0,0 +1,121 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INFOSINK_H_
+#define COMPILER_TRANSLATOR_INFOSINK_H_
+
+#include <math.h>
+#include <stdlib.h>
+#include "compiler/translator/Common.h"
+
+namespace sh
+{
+
+// Returns the fractional part of the given floating-point number.
+inline float fractionalPart(float f) {
+ float intPart = 0.0f;
+ return modff(f, &intPart);
+}
+
+//
+// TPrefixType is used to centralize how info log messages start.
+// See below.
+//
+enum TPrefixType {
+ EPrefixNone,
+ EPrefixWarning,
+ EPrefixError,
+ EPrefixInternalError,
+ EPrefixUnimplemented,
+ EPrefixNote
+};
+
+//
+// Encapsulate info logs for all objects that have them.
+//
+// The methods are a general set of tools for getting a variety of
+// messages and types inserted into the log.
+//
+class TInfoSinkBase {
+public:
+ TInfoSinkBase() {}
+
+ template <typename T>
+ TInfoSinkBase& operator<<(const T& t) {
+ TPersistStringStream stream;
+ stream << t;
+ sink.append(stream.str());
+ return *this;
+ }
+ // Override << operator for specific types. It is faster to append strings
+ // and characters directly to the sink.
+ TInfoSinkBase& operator<<(char c) {
+ sink.append(1, c);
+ return *this;
+ }
+ TInfoSinkBase& operator<<(const char* str) {
+ sink.append(str);
+ return *this;
+ }
+ TInfoSinkBase& operator<<(const TPersistString& str) {
+ sink.append(str);
+ return *this;
+ }
+ TInfoSinkBase& operator<<(const TString& str) {
+ sink.append(str.c_str());
+ return *this;
+ }
+ // Make sure floats are written with correct precision.
+ TInfoSinkBase& operator<<(float f) {
+ // Make sure that at least one decimal point is written. If a number
+ // does not have a fractional part, the default precision format does
+ // not write the decimal portion which gets interpreted as integer by
+ // the compiler.
+ TPersistStringStream stream;
+ if (fractionalPart(f) == 0.0f) {
+ stream.precision(1);
+ stream << std::showpoint << std::fixed << f;
+ } else {
+ stream.unsetf(std::ios::fixed);
+ stream.unsetf(std::ios::scientific);
+ stream.precision(8);
+ stream << f;
+ }
+ sink.append(stream.str());
+ return *this;
+ }
+ // Write boolean values as their names instead of integral value.
+ TInfoSinkBase& operator<<(bool b) {
+ const char* str = b ? "true" : "false";
+ sink.append(str);
+ return *this;
+ }
+
+ void erase() { sink.clear(); }
+ int size() { return static_cast<int>(sink.size()); }
+
+ const TPersistString& str() const { return sink; }
+ const char* c_str() const { return sink.c_str(); }
+
+ void prefix(TPrefixType p);
+ void location(int file, int line);
+ void location(const TSourceLoc& loc);
+ void message(TPrefixType p, const TSourceLoc& loc, const char* m);
+
+private:
+ TPersistString sink;
+};
+
+class TInfoSink {
+public:
+ TInfoSinkBase info;
+ TInfoSinkBase debug;
+ TInfoSinkBase obj;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INFOSINK_H_
diff --git a/gfx/angle/src/compiler/translator/Initialize.cpp b/gfx/angle/src/compiler/translator/Initialize.cpp
new file mode 100755
index 000000000..a0b35f636
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Initialize.cpp
@@ -0,0 +1,762 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Create symbols that declare built-in definitions, add built-ins that
+// cannot be expressed in the files, and establish mappings between
+// built-in functions and operators.
+//
+
+#include "compiler/translator/Initialize.h"
+#include "compiler/translator/Cache.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable)
+{
+ const TType *voidType = TCache::getType(EbtVoid);
+ const TType *float1 = TCache::getType(EbtFloat);
+ const TType *float2 = TCache::getType(EbtFloat, 2);
+ const TType *float3 = TCache::getType(EbtFloat, 3);
+ const TType *float4 = TCache::getType(EbtFloat, 4);
+ const TType *int1 = TCache::getType(EbtInt);
+ const TType *int2 = TCache::getType(EbtInt, 2);
+ const TType *int3 = TCache::getType(EbtInt, 3);
+ const TType *uint1 = TCache::getType(EbtUInt);
+ const TType *bool1 = TCache::getType(EbtBool);
+ const TType *genType = TCache::getType(EbtGenType);
+ const TType *genIType = TCache::getType(EbtGenIType);
+ const TType *genUType = TCache::getType(EbtGenUType);
+ const TType *genBType = TCache::getType(EbtGenBType);
+
+ //
+ // Angle and Trigonometric Functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRadians, genType, "radians", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDegrees, genType, "degrees", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSin, genType, "sin", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCos, genType, "cos", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpTan, genType, "tan", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAsin, genType, "asin", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAcos, genType, "acos", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAtan, genType, "atan", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAtan, genType, "atan", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpSinh, genType, "sinh", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpCosh, genType, "cosh", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTanh, genType, "tanh", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAsinh, genType, "asinh", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAcosh, genType, "acosh", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAtanh, genType, "atanh", genType);
+
+ //
+ // Exponential Functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpPow, genType, "pow", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpExp, genType, "exp", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLog, genType, "log", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpExp2, genType, "exp2", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLog2, genType, "log2", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSqrt, genType, "sqrt", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpInverseSqrt, genType, "inversesqrt", genType);
+
+ //
+ // Common Functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAbs, genType, "abs", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpAbs, genIType, "abs", genIType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSign, genType, "sign", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpSign, genIType, "sign", genIType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFloor, genType, "floor", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTrunc, genType, "trunc", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpRound, genType, "round", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpRoundEven, genType, "roundEven", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCeil, genType, "ceil", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFract, genType, "fract", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMod, genType, "mod", genType, float1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMod, genType, "mod", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMin, genType, "min", genType, float1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMin, genType, "min", genType, genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genIType, "min", genIType, genIType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genIType, "min", genIType, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genUType, "min", genUType, genUType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMin, genUType, "min", genUType, uint1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMax, genType, "max", genType, float1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMax, genType, "max", genType, genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genIType, "max", genIType, genIType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genIType, "max", genIType, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genUType, "max", genUType, genUType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMax, genUType, "max", genUType, uint1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpClamp, genType, "clamp", genType, float1, float1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpClamp, genType, "clamp", genType, genType, genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genIType, "clamp", genIType, int1, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genIType, "clamp", genIType, genIType, genIType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, uint1, uint1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpClamp, genUType, "clamp", genUType, genUType, genUType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, float1);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMix, genType, "mix", genType, genType, genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMix, genType, "mix", genType, genType, genBType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpStep, genType, "step", float1, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", genType, genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpSmoothStep, genType, "smoothstep", float1, float1, genType);
+
+ const TType *outFloat1 = TCache::getType(EbtFloat, EvqOut);
+ const TType *outFloat2 = TCache::getType(EbtFloat, EvqOut, 2);
+ const TType *outFloat3 = TCache::getType(EbtFloat, EvqOut, 3);
+ const TType *outFloat4 = TCache::getType(EbtFloat, EvqOut, 4);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float1, "modf", float1, outFloat1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float2, "modf", float2, outFloat2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float3, "modf", float3, outFloat3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpModf, float4, "modf", float4, outFloat4);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIsNan, genBType, "isnan", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIsInf, genBType, "isinf", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFloatBitsToInt, genIType, "floatBitsToInt", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFloatBitsToUint, genUType, "floatBitsToUint", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpIntBitsToFloat, genType, "intBitsToFloat", genIType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUintBitsToFloat, genType, "uintBitsToFloat", genUType);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackSnorm2x16, uint1, "packSnorm2x16", float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackUnorm2x16, uint1, "packUnorm2x16", float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpPackHalf2x16, uint1, "packHalf2x16", float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackSnorm2x16, float2, "unpackSnorm2x16", uint1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackUnorm2x16, float2, "unpackUnorm2x16", uint1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpUnpackHalf2x16, float2, "unpackHalf2x16", uint1);
+
+ //
+ // Geometric Functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLength, float1, "length", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDistance, float1, "distance", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpDot, float1, "dot", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpCross, float3, "cross", float3, float3);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpNormalize, genType, "normalize", genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpFaceForward, genType, "faceforward", genType, genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpReflect, genType, "reflect", genType, genType);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpRefract, genType, "refract", genType, genType, float1);
+
+ const TType *mat2 = TCache::getType(EbtFloat, 2, 2);
+ const TType *mat3 = TCache::getType(EbtFloat, 3, 3);
+ const TType *mat4 = TCache::getType(EbtFloat, 4, 4);
+ const TType *mat2x3 = TCache::getType(EbtFloat, 2, 3);
+ const TType *mat3x2 = TCache::getType(EbtFloat, 3, 2);
+ const TType *mat2x4 = TCache::getType(EbtFloat, 2, 4);
+ const TType *mat4x2 = TCache::getType(EbtFloat, 4, 2);
+ const TType *mat3x4 = TCache::getType(EbtFloat, 3, 4);
+ const TType *mat4x3 = TCache::getType(EbtFloat, 4, 3);
+
+ //
+ // Matrix Functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat2, "matrixCompMult", mat2, mat2);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat3, "matrixCompMult", mat3, mat3);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpMul, mat4, "matrixCompMult", mat4, mat4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat2x3, "matrixCompMult", mat2x3, mat2x3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat3x2, "matrixCompMult", mat3x2, mat3x2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat2x4, "matrixCompMult", mat2x4, mat2x4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat4x2, "matrixCompMult", mat4x2, mat4x2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat3x4, "matrixCompMult", mat3x4, mat3x4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpMul, mat4x3, "matrixCompMult", mat4x3, mat4x3);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2, "outerProduct", float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3, "outerProduct", float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4, "outerProduct", float4, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2x3, "outerProduct", float3, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3x2, "outerProduct", float2, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat2x4, "outerProduct", float4, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4x2, "outerProduct", float2, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat3x4, "outerProduct", float4, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpOuterProduct, mat4x3, "outerProduct", float3, float4);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2, "transpose", mat2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3, "transpose", mat3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4, "transpose", mat4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2x3, "transpose", mat3x2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3x2, "transpose", mat2x3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat2x4, "transpose", mat4x2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4x2, "transpose", mat2x4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat3x4, "transpose", mat4x3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpTranspose, mat4x3, "transpose", mat3x4);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDeterminant, float1, "determinant", mat4);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat2, "inverse", mat2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat3, "inverse", mat3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpInverse, mat4, "inverse", mat4);
+
+ const TType *vec = TCache::getType(EbtVec);
+ const TType *ivec = TCache::getType(EbtIVec);
+ const TType *uvec = TCache::getType(EbtUVec);
+ const TType *bvec = TCache::getType(EbtBVec);
+
+ //
+ // Vector relational functions.
+ //
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThan, bvec, "lessThan", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThan, bvec, "lessThan", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpLessThan, bvec, "lessThan", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpLessThanEqual, bvec, "lessThanEqual", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThan, bvec, "greaterThan", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThan, bvec, "greaterThan", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpGreaterThan, bvec, "greaterThan", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpGreaterThanEqual, bvec, "greaterThanEqual", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpVectorEqual, bvec, "equal", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorEqual, bvec, "equal", bvec, bvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", vec, vec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", ivec, ivec);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", uvec, uvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorNotEqual, bvec, "notEqual", bvec, bvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAny, bool1, "any", bvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpAll, bool1, "all", bvec);
+ symbolTable.insertBuiltIn(COMMON_BUILTINS, EOpVectorLogicalNot, bvec, "not", bvec);
+
+ const TType *sampler2D = TCache::getType(EbtSampler2D);
+ const TType *samplerCube = TCache::getType(EbtSamplerCube);
+
+ //
+ // Texture Functions for GLSL ES 1.0
+ //
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", sampler2D, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float4);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCube", samplerCube, float3);
+
+ if (resources.OES_EGL_image_external || resources.NV_EGL_stream_consumer_external)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", samplerExternalOES, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", samplerExternalOES, float4);
+ }
+
+ if (resources.ARB_texture_rectangle)
+ {
+ const TType *sampler2DRect = TCache::getType(EbtSampler2DRect);
+
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRect", sampler2DRect, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float3);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DRectProj", sampler2DRect, float4);
+ }
+
+ if (resources.EXT_shader_texture_lod)
+ {
+ /* The *Grad* variants are new to both vertex and fragment shaders; the fragment
+ * shader specific pieces are added separately below.
+ */
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DGradEXT", sampler2D, float2, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjGradEXT", sampler2D, float3, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjGradEXT", sampler2D, float4, float2, float2);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "textureCubeGradEXT", samplerCube, float3, float3, float3);
+ }
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2D", sampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProj", sampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCube", samplerCube, float3, float1);
+
+ if (resources.OES_standard_derivatives)
+ {
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpDFdx, "GL_OES_standard_derivatives", genType, "dFdx", genType);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpDFdy, "GL_OES_standard_derivatives", genType, "dFdy", genType);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, EOpFwidth, "GL_OES_standard_derivatives", genType, "fwidth", genType);
+ }
+
+ if (resources.EXT_shader_texture_lod)
+ {
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DLodEXT", sampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjLodEXT", sampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "texture2DProjLodEXT", sampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, "GL_EXT_shader_texture_lod", float4, "textureCubeLodEXT", samplerCube, float3, float1);
+ }
+ }
+
+ if (type == GL_VERTEX_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DLod", sampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "texture2DProjLod", sampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL1_BUILTINS, float4, "textureCubeLod", samplerCube, float3, float1);
+ }
+
+ const TType *gvec4 = TCache::getType(EbtGVec4);
+
+ const TType *gsampler2D = TCache::getType(EbtGSampler2D);
+ const TType *gsamplerCube = TCache::getType(EbtGSamplerCube);
+ const TType *gsampler3D = TCache::getType(EbtGSampler3D);
+ const TType *gsampler2DArray = TCache::getType(EbtGSampler2DArray);
+
+ //
+ // Texture Functions for GLSL ES 3.0
+ //
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2D, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler3D, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsamplerCube, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2DArray, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler3D, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsampler3D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsamplerCube, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLod", gsampler2DArray, float3, float1);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texture", samplerExternalOES, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float4);
+ }
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2D, float2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler3D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsamplerCube, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texture", gsampler2DArray, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProj", gsampler3D, float4, float1);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texture", samplerExternalOES, float2,
+ float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "textureProj", samplerExternalOES,
+ float4, float1);
+ }
+ }
+
+ const TType *sampler2DShadow = TCache::getType(EbtSampler2DShadow);
+ const TType *samplerCubeShadow = TCache::getType(EbtSamplerCubeShadow);
+ const TType *sampler2DArrayShadow = TCache::getType(EbtSampler2DArrayShadow);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DArrayShadow, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProj", sampler2DShadow, float4);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLod", sampler2DShadow, float3, float1);
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", sampler2DShadow, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "texture", samplerCubeShadow, float4, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProj", sampler2DShadow, float4, float1);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", gsampler2D, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int3, "textureSize", gsampler3D, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", gsamplerCube, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int3, "textureSize", gsampler2DArray, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", sampler2DShadow, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", samplerCubeShadow, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int3, "textureSize", sampler2DArrayShadow, int1);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, int2, "textureSize", samplerExternalOES, int1);
+ }
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDFdx, genType, "dFdx", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpDFdy, genType, "dFdy", genType);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, EOpFwidth, genType, "fwidth", genType);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2D, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler3D, float3, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3, int2);
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2D, float2, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler3D, float3, int3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureOffset", sampler2DShadow, float3, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureOffset", gsampler2DArray, float3, int2, float1);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float3, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float4, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler3D, float4, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow, float4, int2);
+
+ if (type == GL_FRAGMENT_SHADER)
+ {
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float3, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler2D, float4, int2, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjOffset", gsampler3D, float4, int3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjOffset", sampler2DShadow, float4, int2, float1);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2D, float2, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler3D, float3, float1, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureLodOffset", sampler2DShadow, float3, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureLodOffset", gsampler2DArray, float3, float1, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler2D, float3, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler2D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLod", gsampler3D, float4, float1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLod", sampler2DShadow, float4, float1);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float3, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler2D, float4, float1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjLodOffset", gsampler3D, float4, float1, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjLodOffset", sampler2DShadow, float4, float1, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler2D, int2, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler3D, int3, int1);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetch", gsampler2DArray, int3, int1);
+
+ if (resources.OES_EGL_image_external_essl3)
+ {
+ const TType *samplerExternalOES = TCache::getType(EbtSamplerExternalOES);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float4, "texelFetch", samplerExternalOES, int2,
+ int1);
+ }
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2D, int2, int1, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler3D, int3, int1, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "texelFetchOffset", gsampler2DArray, int3, int1, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2D, float2, float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler3D, float3, float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsamplerCube, float3, float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DShadow, float3, float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", samplerCubeShadow, float4, float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGrad", gsampler2DArray, float3, float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGrad", sampler2DArrayShadow, float4, float2, float2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2D, float2, float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler3D, float3, float3, float3, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DShadow, float3, float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureGradOffset", gsampler2DArray, float3, float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureGradOffset", sampler2DArrayShadow, float4, float2, float2, int2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float3, float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler2D, float4, float2, float2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGrad", gsampler3D, float4, float3, float3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGrad", sampler2DShadow, float4, float2, float2);
+
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float3, float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler2D, float4, float2, float2, int2);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, gvec4, "textureProjGradOffset", gsampler3D, float4, float3, float3, int3);
+ symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGradOffset", sampler2DShadow, float4, float2, float2, int2);
+
+ const TType *gimage2D = TCache::getType(EbtGImage2D);
+ const TType *gimage3D = TCache::getType(EbtGImage3D);
+ const TType *gimage2DArray = TCache::getType(EbtGImage2DArray);
+ const TType *gimageCube = TCache::getType(EbtGImageCube);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage2D, int2, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage3D, int3, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimage2DArray, int3, gvec4);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, voidType, "imageStore", gimageCube, int3, gvec4);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage2D, int2);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage3D, int3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimage2DArray, int3);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, gvec4, "imageLoad", gimageCube, int3);
+
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int2, "imageSize", gimage2D);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int3, "imageSize", gimage3D);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int3, "imageSize", gimage2DArray);
+ symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, int3, "imageSize", gimageCube);
+
+ //
+ // Depth range in window coordinates
+ //
+ TFieldList *fields = NewPoolTFieldList();
+ TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
+ auto highpFloat1 = new TType(EbtFloat, EbpHigh, EvqGlobal, 1);
+ TField *near = new TField(highpFloat1, NewPoolTString("near"), zeroSourceLoc);
+ TField *far = new TField(highpFloat1, NewPoolTString("far"), zeroSourceLoc);
+ TField *diff = new TField(highpFloat1, NewPoolTString("diff"), zeroSourceLoc);
+ fields->push_back(near);
+ fields->push_back(far);
+ fields->push_back(diff);
+ TStructure *depthRangeStruct = new TStructure(NewPoolTString("gl_DepthRangeParameters"), fields);
+ TVariable *depthRangeParameters =
+ new TVariable(&depthRangeStruct->name(), TType(depthRangeStruct), true);
+ symbolTable.insert(COMMON_BUILTINS, depthRangeParameters);
+ TVariable *depthRange = new TVariable(NewPoolTString("gl_DepthRange"), TType(depthRangeStruct));
+ depthRange->setQualifier(EvqUniform);
+ // Ensure we evaluate the mangled name for depth range, so we allocate to the current scope.
+ depthRangeParameters->getType().getMangledName();
+ depthRange->getType().getMangledName();
+ symbolTable.insert(COMMON_BUILTINS, depthRange);
+
+ //
+ // Implementation dependent built-in constants.
+ //
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexAttribs", resources.MaxVertexAttribs,
+ EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexUniformVectors",
+ resources.MaxVertexUniformVectors, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxVertexTextureImageUnits",
+ resources.MaxVertexTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxCombinedTextureImageUnits",
+ resources.MaxCombinedTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxTextureImageUnits",
+ resources.MaxTextureImageUnits, EbpMedium);
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxFragmentUniformVectors",
+ resources.MaxFragmentUniformVectors, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL1_BUILTINS, "gl_MaxVaryingVectors", resources.MaxVaryingVectors,
+ EbpMedium);
+
+ symbolTable.insertConstInt(COMMON_BUILTINS, "gl_MaxDrawBuffers", resources.MaxDrawBuffers,
+ EbpMedium);
+ if (resources.EXT_blend_func_extended)
+ {
+ symbolTable.insertConstIntExt(COMMON_BUILTINS, "GL_EXT_blend_func_extended",
+ "gl_MaxDualSourceDrawBuffersEXT",
+ resources.MaxDualSourceDrawBuffers);
+ }
+
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxVertexOutputVectors",
+ resources.MaxVertexOutputVectors, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxFragmentInputVectors",
+ resources.MaxFragmentInputVectors, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MinProgramTexelOffset",
+ resources.MinProgramTexelOffset, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_BUILTINS, "gl_MaxProgramTexelOffset",
+ resources.MaxProgramTexelOffset, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxImageUnits", resources.MaxImageUnits,
+ EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexImageUniforms",
+ resources.MaxVertexImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentImageUniforms",
+ resources.MaxFragmentImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeImageUniforms",
+ resources.MaxComputeImageUniforms, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedImageUniforms",
+ resources.MaxCombinedImageUniforms, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedShaderOutputResources",
+ resources.MaxCombinedShaderOutputResources, EbpMedium);
+
+ symbolTable.insertConstIvec3(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupCount",
+ resources.MaxComputeWorkGroupCount, EbpHigh);
+ symbolTable.insertConstIvec3(ESSL3_1_BUILTINS, "gl_MaxComputeWorkGroupSize",
+ resources.MaxComputeWorkGroupSize, EbpHigh);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeUniformComponents",
+ resources.MaxComputeUniformComponents, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeTextureImageUnits",
+ resources.MaxComputeTextureImageUnits, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounters",
+ resources.MaxComputeAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxComputeAtomicCounterBuffers",
+ resources.MaxComputeAtomicCounterBuffers, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounters",
+ resources.MaxVertexAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounters",
+ resources.MaxFragmentAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounters",
+ resources.MaxCombinedAtomicCounters, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBindings",
+ resources.MaxAtomicCounterBindings, EbpMedium);
+
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxVertexAtomicCounterBuffers",
+ resources.MaxVertexAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxFragmentAtomicCounterBuffers",
+ resources.MaxFragmentAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxCombinedAtomicCounterBuffers",
+ resources.MaxCombinedAtomicCounterBuffers, EbpMedium);
+ symbolTable.insertConstInt(ESSL3_1_BUILTINS, "gl_MaxAtomicCounterBufferSize",
+ resources.MaxAtomicCounterBufferSize, EbpMedium);
+}
+
+void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
+ const ShBuiltInResources &resources,
+ TSymbolTable &symbolTable)
+{
+ //
+ // Insert some special built-in variables that are not in
+ // the built-in header files.
+ //
+ switch (type)
+ {
+ case GL_FRAGMENT_SHADER:
+ {
+ symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FragCoord"),
+ TType(EbtFloat, EbpMedium, EvqFragCoord, 4)));
+ symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_FrontFacing"),
+ TType(EbtBool, EbpUndefined, EvqFrontFacing, 1)));
+ symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointCoord"),
+ TType(EbtFloat, EbpMedium, EvqPointCoord, 2)));
+
+ symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragColor"),
+ TType(EbtFloat, EbpMedium, EvqFragColor, 4)));
+ TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, 1, true);
+ fragData.setArraySize(resources.MaxDrawBuffers);
+ symbolTable.insert(ESSL1_BUILTINS, new TVariable(NewPoolTString("gl_FragData"), fragData));
+
+ if (resources.EXT_blend_func_extended)
+ {
+ symbolTable.insert(
+ ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
+ new TVariable(NewPoolTString("gl_SecondaryFragColorEXT"),
+ TType(EbtFloat, EbpMedium, EvqSecondaryFragColorEXT, 4)));
+ TType secondaryFragData(EbtFloat, EbpMedium, EvqSecondaryFragDataEXT, 4, 1, true);
+ secondaryFragData.setArraySize(resources.MaxDualSourceDrawBuffers);
+ symbolTable.insert(
+ ESSL1_BUILTINS, "GL_EXT_blend_func_extended",
+ new TVariable(NewPoolTString("gl_SecondaryFragDataEXT"), secondaryFragData));
+ }
+
+ if (resources.EXT_frag_depth)
+ {
+ symbolTable.insert(
+ ESSL1_BUILTINS, "GL_EXT_frag_depth",
+ new TVariable(
+ NewPoolTString("gl_FragDepthEXT"),
+ TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
+ EvqFragDepthEXT, 1)));
+ }
+
+ symbolTable.insert(ESSL3_BUILTINS,
+ new TVariable(NewPoolTString("gl_FragDepth"),
+ TType(EbtFloat, EbpHigh, EvqFragDepth, 1)));
+
+ if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch)
+ {
+ TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
+ lastFragData.setArraySize(resources.MaxDrawBuffers);
+
+ if (resources.EXT_shader_framebuffer_fetch)
+ {
+ symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_shader_framebuffer_fetch",
+ new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+ }
+ else if (resources.NV_shader_framebuffer_fetch)
+ {
+ symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
+ new TVariable(NewPoolTString("gl_LastFragColor"),
+ TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
+ symbolTable.insert(ESSL1_BUILTINS, "GL_NV_shader_framebuffer_fetch",
+ new TVariable(NewPoolTString("gl_LastFragData"), lastFragData));
+ }
+ }
+ else if (resources.ARM_shader_framebuffer_fetch)
+ {
+ symbolTable.insert(ESSL1_BUILTINS, "GL_ARM_shader_framebuffer_fetch",
+ new TVariable(NewPoolTString("gl_LastFragColorARM"),
+ TType(EbtFloat, EbpMedium, EvqLastFragColor, 4)));
+ }
+ }
+
+ break;
+
+ case GL_VERTEX_SHADER:
+ symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_Position"),
+ TType(EbtFloat, EbpHigh, EvqPosition, 4)));
+ symbolTable.insert(COMMON_BUILTINS, new TVariable(NewPoolTString("gl_PointSize"),
+ TType(EbtFloat, EbpMedium, EvqPointSize, 1)));
+ symbolTable.insert(ESSL3_BUILTINS, new TVariable(NewPoolTString("gl_InstanceID"),
+ TType(EbtInt, EbpHigh, EvqInstanceID, 1)));
+ symbolTable.insert(ESSL3_BUILTINS, new TVariable(NewPoolTString("gl_VertexID"),
+ TType(EbtInt, EbpHigh, EvqVertexID, 1)));
+ break;
+ case GL_COMPUTE_SHADER:
+ {
+ symbolTable.insert(ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_NumWorkGroups"),
+ TType(EbtUInt, EbpUndefined, EvqNumWorkGroups, 3)));
+ symbolTable.insert(ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_WorkGroupSize"),
+ TType(EbtUInt, EbpUndefined, EvqWorkGroupSize, 3)));
+ symbolTable.insert(ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_WorkGroupID"),
+ TType(EbtUInt, EbpUndefined, EvqWorkGroupID, 3)));
+ symbolTable.insert(ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_LocalInvocationID"),
+ TType(EbtUInt, EbpUndefined, EvqLocalInvocationID, 3)));
+ symbolTable.insert(ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_GlobalInvocationID"),
+ TType(EbtUInt, EbpUndefined, EvqGlobalInvocationID, 3)));
+ symbolTable.insert(
+ ESSL3_1_BUILTINS,
+ new TVariable(NewPoolTString("gl_LocalInvocationIndex"),
+ TType(EbtUInt, EbpUndefined, EvqLocalInvocationIndex, 1)));
+ }
+ break;
+
+ default:
+ assert(false && "Language not supported");
+ }
+}
+
+void InitExtensionBehavior(const ShBuiltInResources& resources,
+ TExtensionBehavior& extBehavior)
+{
+ if (resources.OES_standard_derivatives)
+ extBehavior["GL_OES_standard_derivatives"] = EBhUndefined;
+ if (resources.OES_EGL_image_external)
+ extBehavior["GL_OES_EGL_image_external"] = EBhUndefined;
+ if (resources.OES_EGL_image_external_essl3)
+ extBehavior["GL_OES_EGL_image_external_essl3"] = EBhUndefined;
+ if (resources.NV_EGL_stream_consumer_external)
+ extBehavior["GL_NV_EGL_stream_consumer_external"] = EBhUndefined;
+ if (resources.ARB_texture_rectangle)
+ extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined;
+ if (resources.EXT_blend_func_extended)
+ extBehavior["GL_EXT_blend_func_extended"] = EBhUndefined;
+ if (resources.EXT_draw_buffers)
+ extBehavior["GL_EXT_draw_buffers"] = EBhUndefined;
+ if (resources.EXT_frag_depth)
+ extBehavior["GL_EXT_frag_depth"] = EBhUndefined;
+ if (resources.EXT_shader_texture_lod)
+ extBehavior["GL_EXT_shader_texture_lod"] = EBhUndefined;
+ if (resources.EXT_shader_framebuffer_fetch)
+ extBehavior["GL_EXT_shader_framebuffer_fetch"] = EBhUndefined;
+ if (resources.NV_shader_framebuffer_fetch)
+ extBehavior["GL_NV_shader_framebuffer_fetch"] = EBhUndefined;
+ if (resources.ARM_shader_framebuffer_fetch)
+ extBehavior["GL_ARM_shader_framebuffer_fetch"] = EBhUndefined;
+}
+
+void ResetExtensionBehavior(TExtensionBehavior &extBehavior)
+{
+ for (auto ext_iter = extBehavior.begin();
+ ext_iter != extBehavior.end();
+ ++ext_iter)
+ {
+ ext_iter->second = EBhUndefined;
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Initialize.h b/gfx/angle/src/compiler/translator/Initialize.h
new file mode 100755
index 000000000..0f1b60ba3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Initialize.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INITIALIZE_H_
+#define COMPILER_TRANSLATOR_INITIALIZE_H_
+
+#include "compiler/translator/Common.h"
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &table);
+
+void IdentifyBuiltIns(sh::GLenum type, ShShaderSpec spec,
+ const ShBuiltInResources& resources,
+ TSymbolTable& symbolTable);
+
+void InitExtensionBehavior(const ShBuiltInResources& resources,
+ TExtensionBehavior& extensionBehavior);
+
+// Resets the behavior of the extensions listed in |extensionBehavior| to the
+// undefined state. These extensions will only be those initially supported in
+// the ShBuiltInResources object for this compiler instance. All other
+// extensions will remain unsupported.
+void ResetExtensionBehavior(TExtensionBehavior &extensionBehavior);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INITIALIZE_H_
diff --git a/gfx/angle/src/compiler/translator/InitializeDll.cpp b/gfx/angle/src/compiler/translator/InitializeDll.cpp
new file mode 100755
index 000000000..89901935c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeDll.cpp
@@ -0,0 +1,43 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/InitializeDll.h"
+#include "compiler/translator/InitializeGlobals.h"
+#include "compiler/translator/InitializeParseContext.h"
+
+#include "common/platform.h"
+
+#include <assert.h>
+
+namespace sh
+{
+
+bool InitProcess()
+{
+ if (!InitializePoolIndex()) {
+ assert(0 && "InitProcess(): Failed to initalize global pool");
+ return false;
+ }
+
+ if (!InitializeParseContextIndex()) {
+ assert(0 && "InitProcess(): Failed to initalize parse context");
+ return false;
+ }
+
+ TCache::initialize();
+
+ return true;
+}
+
+void DetachProcess()
+{
+ FreeParseContextIndex();
+ FreePoolIndex();
+ TCache::destroy();
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/InitializeDll.h b/gfx/angle/src/compiler/translator/InitializeDll.h
new file mode 100755
index 000000000..b2c787a8c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeDll.h
@@ -0,0 +1,16 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#ifndef COMPILER_TRANSLATOR_INITIALIZEDLL_H_
+#define COMPILER_TRANSLATOR_INITIALIZEDLL_H_
+
+namespace sh
+{
+bool InitProcess();
+void DetachProcess();
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INITIALIZEDLL_H_
+
diff --git a/gfx/angle/src/compiler/translator/InitializeGlobals.h b/gfx/angle/src/compiler/translator/InitializeGlobals.h
new file mode 100755
index 000000000..8c65cb28d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeGlobals.h
@@ -0,0 +1,13 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_
+#define COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_
+
+bool InitializePoolIndex();
+void FreePoolIndex();
+
+#endif // COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_
diff --git a/gfx/angle/src/compiler/translator/InitializeParseContext.cpp b/gfx/angle/src/compiler/translator/InitializeParseContext.cpp
new file mode 100755
index 000000000..67a248b60
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeParseContext.cpp
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/InitializeParseContext.h"
+
+#include "common/tls.h"
+
+#include <assert.h>
+
+namespace sh
+{
+
+TLSIndex GlobalParseContextIndex = TLS_INVALID_INDEX;
+
+bool InitializeParseContextIndex()
+{
+ assert(GlobalParseContextIndex == TLS_INVALID_INDEX);
+
+ GlobalParseContextIndex = CreateTLSIndex();
+ return GlobalParseContextIndex != TLS_INVALID_INDEX;
+}
+
+void FreeParseContextIndex()
+{
+ assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
+
+ DestroyTLSIndex(GlobalParseContextIndex);
+ GlobalParseContextIndex = TLS_INVALID_INDEX;
+}
+
+void SetGlobalParseContext(TParseContext* context)
+{
+ assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
+ SetTLSValue(GlobalParseContextIndex, context);
+}
+
+TParseContext* GetGlobalParseContext()
+{
+ assert(GlobalParseContextIndex != TLS_INVALID_INDEX);
+ return static_cast<TParseContext*>(GetTLSValue(GlobalParseContextIndex));
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/InitializeParseContext.h b/gfx/angle/src/compiler/translator/InitializeParseContext.h
new file mode 100755
index 000000000..9c315be3f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeParseContext.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
+#define COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
+
+namespace sh
+{
+
+bool InitializeParseContextIndex();
+void FreeParseContextIndex();
+
+class TParseContext;
+extern void SetGlobalParseContext(TParseContext* context);
+extern TParseContext* GetGlobalParseContext();
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_
diff --git a/gfx/angle/src/compiler/translator/InitializeVariables.cpp b/gfx/angle/src/compiler/translator/InitializeVariables.cpp
new file mode 100755
index 000000000..dafea1bd6
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeVariables.cpp
@@ -0,0 +1,130 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/InitializeVariables.h"
+
+#include "angle_gl.h"
+#include "common/debug.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class VariableInitializer : public TIntermTraverser
+{
+ public:
+ VariableInitializer(const InitVariableList &vars, const TSymbolTable &symbolTable)
+ : TIntermTraverser(true, false, false),
+ mVariables(vars),
+ mSymbolTable(symbolTable),
+ mCodeInserted(false)
+ {
+ ASSERT(mSymbolTable.atGlobalLevel());
+ }
+
+ protected:
+ bool visitBinary(Visit, TIntermBinary *node) override { return false; }
+ bool visitUnary(Visit, TIntermUnary *node) override { return false; }
+ bool visitIfElse(Visit, TIntermIfElse *node) override { return false; }
+ bool visitLoop(Visit, TIntermLoop *node) override { return false; }
+ bool visitBranch(Visit, TIntermBranch *node) override { return false; }
+ bool visitAggregate(Visit, TIntermAggregate *node) override { return false; }
+
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+
+ private:
+ void insertInitCode(TIntermSequence *sequence);
+
+ const InitVariableList &mVariables;
+ const TSymbolTable &mSymbolTable;
+ bool mCodeInserted;
+};
+
+// VariableInitializer implementation.
+
+bool VariableInitializer::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ // Function definition.
+ ASSERT(visit == PreVisit);
+ if (node->getFunctionSymbolInfo()->isMain())
+ {
+ TIntermBlock *body = node->getBody();
+ insertInitCode(body->getSequence());
+ mCodeInserted = true;
+ }
+ return false;
+}
+
+void VariableInitializer::insertInitCode(TIntermSequence *sequence)
+{
+ for (const auto &var : mVariables)
+ {
+ TString name = TString(var.name.c_str());
+
+ if (var.isArray())
+ {
+ // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
+ // doesn't have array assignment.
+ size_t pos = name.find_last_of('[');
+ if (pos != TString::npos)
+ {
+ name = name.substr(0, pos);
+ }
+ TType elementType = sh::GetShaderVariableBasicType(var);
+ TType arrayType = elementType;
+ arrayType.setArraySize(var.elementCount());
+
+ for (unsigned int i = 0; i < var.arraySize; ++i)
+ {
+ TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, arrayType);
+ TIntermBinary *element = new TIntermBinary(EOpIndexDirect, arraySymbol,
+ TIntermTyped::CreateIndexNode(i));
+
+ TIntermTyped *zero = TIntermTyped::CreateZero(elementType);
+ TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
+
+ sequence->insert(sequence->begin(), assignment);
+ }
+ }
+ else if (var.isStruct())
+ {
+ TVariable *structInfo = reinterpret_cast<TVariable *>(mSymbolTable.findGlobal(name));
+ ASSERT(structInfo);
+
+ TIntermSymbol *symbol = new TIntermSymbol(0, name, structInfo->getType());
+ TIntermTyped *zero = TIntermTyped::CreateZero(structInfo->getType());
+
+ TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
+ sequence->insert(sequence->begin(), assign);
+ }
+ else
+ {
+ TType type = sh::GetShaderVariableBasicType(var);
+ TIntermSymbol *symbol = new TIntermSymbol(0, name, type);
+ TIntermTyped *zero = TIntermTyped::CreateZero(type);
+
+ TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
+ sequence->insert(sequence->begin(), assign);
+ }
+ }
+}
+
+} // namespace anonymous
+
+void InitializeVariables(TIntermNode *root,
+ const InitVariableList &vars,
+ const TSymbolTable &symbolTable)
+{
+ VariableInitializer initializer(vars, symbolTable);
+ root->traverse(&initializer);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/InitializeVariables.h b/gfx/angle/src/compiler/translator/InitializeVariables.h
new file mode 100755
index 000000000..9a34245a5
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/InitializeVariables.h
@@ -0,0 +1,31 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
+#define COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
+
+#include <GLSLANG/ShaderLang.h>
+
+namespace sh
+{
+class TIntermNode;
+class TSymbolTable;
+
+typedef std::vector<sh::ShaderVariable> InitVariableList;
+
+// Currently this function is only capable of initializing variables of basic types,
+// array of basic types, or struct of basic types.
+// For now it is used for the following two scenarios:
+// 1. initializing gl_Position;
+// 2. initializing ESSL 3.00 shaders' output variables (which might be structs).
+// Specifically, it's not feasible to make it work for local variables because if their
+// types are structs, we can't look into TSymbolTable to find the TType data.
+void InitializeVariables(TIntermNode *root,
+ const InitVariableList &vars,
+ const TSymbolTable &symbolTable);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
diff --git a/gfx/angle/src/compiler/translator/IntermNode.cpp b/gfx/angle/src/compiler/translator/IntermNode.cpp
new file mode 100755
index 000000000..b91b43ecf
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/IntermNode.cpp
@@ -0,0 +1,2883 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Build the intermediate representation.
+//
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <vector>
+
+#include "common/mathutil.h"
+#include "common/matrix_utils.h"
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/HashNames.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+const float kPi = 3.14159265358979323846f;
+const float kDegreesToRadiansMultiplier = kPi / 180.0f;
+const float kRadiansToDegreesMultiplier = 180.0f / kPi;
+
+TPrecision GetHigherPrecision(TPrecision left, TPrecision right)
+{
+ return left > right ? left : right;
+}
+
+TConstantUnion *Vectorize(const TConstantUnion &constant, size_t size)
+{
+ TConstantUnion *constUnion = new TConstantUnion[size];
+ for (unsigned int i = 0; i < size; ++i)
+ constUnion[i] = constant;
+
+ return constUnion;
+}
+
+void UndefinedConstantFoldingError(const TSourceLoc &loc,
+ TOperator op,
+ TBasicType basicType,
+ TDiagnostics *diagnostics,
+ TConstantUnion *result)
+{
+ diagnostics->warning(loc, "operation result is undefined for the values passed in",
+ GetOperatorString(op), "");
+
+ switch (basicType)
+ {
+ case EbtFloat :
+ result->setFConst(0.0f);
+ break;
+ case EbtInt:
+ result->setIConst(0);
+ break;
+ case EbtUInt:
+ result->setUConst(0u);
+ break;
+ case EbtBool:
+ result->setBConst(false);
+ break;
+ default:
+ break;
+ }
+}
+
+float VectorLength(const TConstantUnion *paramArray, size_t paramArraySize)
+{
+ float result = 0.0f;
+ for (size_t i = 0; i < paramArraySize; i++)
+ {
+ float f = paramArray[i].getFConst();
+ result += f * f;
+ }
+ return sqrtf(result);
+}
+
+float VectorDotProduct(const TConstantUnion *paramArray1,
+ const TConstantUnion *paramArray2,
+ size_t paramArraySize)
+{
+ float result = 0.0f;
+ for (size_t i = 0; i < paramArraySize; i++)
+ result += paramArray1[i].getFConst() * paramArray2[i].getFConst();
+ return result;
+}
+
+TIntermTyped *CreateFoldedNode(const TConstantUnion *constArray,
+ const TIntermTyped *originalNode,
+ TQualifier qualifier)
+{
+ if (constArray == nullptr)
+ {
+ return nullptr;
+ }
+ TIntermTyped *folded = new TIntermConstantUnion(constArray, originalNode->getType());
+ folded->getTypePointer()->setQualifier(qualifier);
+ folded->setLine(originalNode->getLine());
+ return folded;
+}
+
+angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray,
+ const unsigned int &rows,
+ const unsigned int &cols)
+{
+ std::vector<float> elements;
+ for (size_t i = 0; i < rows * cols; i++)
+ elements.push_back(paramArray[i].getFConst());
+ // Transpose is used since the Matrix constructor expects arguments in row-major order,
+ // whereas the paramArray is in column-major order. Rows/cols parameters are also flipped below
+ // so that the created matrix will have the expected dimensions after the transpose.
+ return angle::Matrix<float>(elements, cols, rows).transpose();
+}
+
+angle::Matrix<float> GetMatrix(const TConstantUnion *paramArray, const unsigned int &size)
+{
+ std::vector<float> elements;
+ for (size_t i = 0; i < size * size; i++)
+ elements.push_back(paramArray[i].getFConst());
+ // Transpose is used since the Matrix constructor expects arguments in row-major order,
+ // whereas the paramArray is in column-major order.
+ return angle::Matrix<float>(elements, size).transpose();
+}
+
+void SetUnionArrayFromMatrix(const angle::Matrix<float> &m, TConstantUnion *resultArray)
+{
+ // Transpose is used since the input Matrix is in row-major order,
+ // whereas the actual result should be in column-major order.
+ angle::Matrix<float> result = m.transpose();
+ std::vector<float> resultElements = result.elements();
+ for (size_t i = 0; i < resultElements.size(); i++)
+ resultArray[i].setFConst(resultElements[i]);
+}
+
+} // namespace anonymous
+
+
+////////////////////////////////////////////////////////////////
+//
+// Member functions of the nodes used for building the tree.
+//
+////////////////////////////////////////////////////////////////
+
+void TIntermTyped::setTypePreservePrecision(const TType &t)
+{
+ TPrecision precision = getPrecision();
+ mType = t;
+ ASSERT(mType.getBasicType() != EbtBool || precision == EbpUndefined);
+ mType.setPrecision(precision);
+}
+
+#define REPLACE_IF_IS(node, type, original, replacement) \
+ if (node == original) { \
+ node = static_cast<type *>(replacement); \
+ return true; \
+ }
+
+bool TIntermLoop::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ ASSERT(original != nullptr); // This risks replacing multiple children.
+ REPLACE_IF_IS(mInit, TIntermNode, original, replacement);
+ REPLACE_IF_IS(mCond, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mExpr, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mBody, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermBranch::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mExpression, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermSwizzle::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType());
+ REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermBinary::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mLeft, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mRight, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermUnary::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ ASSERT(original->getAsTyped()->getType() == replacement->getAsTyped()->getType());
+ REPLACE_IF_IS(mOperand, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermFunctionDefinition::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mParameters, TIntermAggregate, original, replacement);
+ REPLACE_IF_IS(mBody, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermAggregate::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermBlock::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermDeclaration::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ return replaceChildNodeInternal(original, replacement);
+}
+
+bool TIntermAggregateBase::replaceChildNodeInternal(TIntermNode *original, TIntermNode *replacement)
+{
+ for (size_t ii = 0; ii < getSequence()->size(); ++ii)
+ {
+ REPLACE_IF_IS((*getSequence())[ii], TIntermNode, original, replacement);
+ }
+ return false;
+}
+
+bool TIntermAggregateBase::replaceChildNodeWithMultiple(TIntermNode *original,
+ const TIntermSequence &replacements)
+{
+ for (auto it = getSequence()->begin(); it < getSequence()->end(); ++it)
+ {
+ if (*it == original)
+ {
+ it = getSequence()->erase(it);
+ getSequence()->insert(it, replacements.begin(), replacements.end());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TIntermAggregateBase::insertChildNodes(TIntermSequence::size_type position,
+ const TIntermSequence &insertions)
+{
+ if (position > getSequence()->size())
+ {
+ return false;
+ }
+ auto it = getSequence()->begin() + position;
+ getSequence()->insert(it, insertions.begin(), insertions.end());
+ return true;
+}
+
+bool TIntermAggregate::areChildrenConstQualified()
+{
+ for (TIntermNode *&child : mSequence)
+ {
+ TIntermTyped *typed = child->getAsTyped();
+ if (typed && typed->getQualifier() != EvqConst)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void TIntermAggregate::setPrecisionFromChildren()
+{
+ mGotPrecisionFromChildren = true;
+ if (getBasicType() == EbtBool)
+ {
+ mType.setPrecision(EbpUndefined);
+ return;
+ }
+
+ TPrecision precision = EbpUndefined;
+ TIntermSequence::iterator childIter = mSequence.begin();
+ while (childIter != mSequence.end())
+ {
+ TIntermTyped *typed = (*childIter)->getAsTyped();
+ if (typed)
+ precision = GetHigherPrecision(typed->getPrecision(), precision);
+ ++childIter;
+ }
+ mType.setPrecision(precision);
+}
+
+void TIntermAggregate::setBuiltInFunctionPrecision()
+{
+ // All built-ins returning bool should be handled as ops, not functions.
+ ASSERT(getBasicType() != EbtBool);
+
+ TPrecision precision = EbpUndefined;
+ TIntermSequence::iterator childIter = mSequence.begin();
+ while (childIter != mSequence.end())
+ {
+ TIntermTyped *typed = (*childIter)->getAsTyped();
+ // ESSL spec section 8: texture functions get their precision from the sampler.
+ if (typed && IsSampler(typed->getBasicType()))
+ {
+ precision = typed->getPrecision();
+ break;
+ }
+ ++childIter;
+ }
+ // ESSL 3.0 spec section 8: textureSize always gets highp precision.
+ // All other functions that take a sampler are assumed to be texture functions.
+ if (mFunctionInfo.getName().find("textureSize") == 0)
+ mType.setPrecision(EbpHigh);
+ else
+ mType.setPrecision(precision);
+}
+
+void TIntermBlock::appendStatement(TIntermNode *statement)
+{
+ // Declaration nodes with no children can appear if all the declarators just added constants to
+ // the symbol table instead of generating code. They're no-ops so they aren't added to blocks.
+ if (statement != nullptr && (statement->getAsDeclarationNode() == nullptr ||
+ !statement->getAsDeclarationNode()->getSequence()->empty()))
+ {
+ mStatements.push_back(statement);
+ }
+}
+
+void TIntermDeclaration::appendDeclarator(TIntermTyped *declarator)
+{
+ ASSERT(declarator != nullptr);
+ ASSERT(declarator->getAsSymbolNode() != nullptr ||
+ (declarator->getAsBinaryNode() != nullptr &&
+ declarator->getAsBinaryNode()->getOp() == EOpInitialize));
+ ASSERT(mDeclarators.empty() ||
+ declarator->getType().sameElementType(mDeclarators.back()->getAsTyped()->getType()));
+ mDeclarators.push_back(declarator);
+}
+
+bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mTrueExpression, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement);
+ return false;
+}
+
+bool TIntermIfElse::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mTrueBlock, TIntermBlock, original, replacement);
+ REPLACE_IF_IS(mFalseBlock, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermSwitch::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mInit, TIntermTyped, original, replacement);
+ REPLACE_IF_IS(mStatementList, TIntermBlock, original, replacement);
+ return false;
+}
+
+bool TIntermCase::replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement)
+{
+ REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+ return false;
+}
+
+TIntermTyped::TIntermTyped(const TIntermTyped &node) : TIntermNode(), mType(node.mType)
+{
+ // Copy constructor is disallowed for TIntermNode in order to disallow it for subclasses that
+ // don't explicitly allow it, so normal TIntermNode constructor is used to construct the copy.
+ // We need to manually copy any fields of TIntermNode besides handling fields in TIntermTyped.
+ mLine = node.mLine;
+}
+
+bool TIntermTyped::isConstructorWithOnlyConstantUnionParameters()
+{
+ TIntermAggregate *constructor = getAsAggregate();
+ if (!constructor || !constructor->isConstructor())
+ {
+ return false;
+ }
+ for (TIntermNode *&node : *constructor->getSequence())
+ {
+ if (!node->getAsConstantUnion())
+ return false;
+ }
+ return true;
+}
+
+// static
+TIntermTyped *TIntermTyped::CreateIndexNode(int index)
+{
+ TConstantUnion *u = new TConstantUnion[1];
+ u[0].setIConst(index);
+
+ TType type(EbtInt, EbpUndefined, EvqConst, 1);
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
+ return node;
+}
+
+// static
+TIntermTyped *TIntermTyped::CreateZero(const TType &type)
+{
+ TType constType(type);
+ constType.setQualifier(EvqConst);
+
+ if (!type.isArray() && type.getBasicType() != EbtStruct)
+ {
+ ASSERT(type.isScalar() || type.isVector() || type.isMatrix());
+
+ size_t size = constType.getObjectSize();
+ TConstantUnion *u = new TConstantUnion[size];
+ for (size_t i = 0; i < size; ++i)
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ u[i].setFConst(0.0f);
+ break;
+ case EbtInt:
+ u[i].setIConst(0);
+ break;
+ case EbtUInt:
+ u[i].setUConst(0u);
+ break;
+ case EbtBool:
+ u[i].setBConst(false);
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+
+ TIntermConstantUnion *node = new TIntermConstantUnion(u, constType);
+ return node;
+ }
+
+ TIntermAggregate *constructor = new TIntermAggregate(sh::TypeToConstructorOperator(type));
+ constructor->setType(constType);
+
+ if (type.isArray())
+ {
+ TType elementType(type);
+ elementType.clearArrayness();
+
+ size_t arraySize = type.getArraySize();
+ for (size_t i = 0; i < arraySize; ++i)
+ {
+ constructor->getSequence()->push_back(CreateZero(elementType));
+ }
+ }
+ else
+ {
+ ASSERT(type.getBasicType() == EbtStruct);
+
+ TStructure *structure = type.getStruct();
+ for (const auto &field : structure->fields())
+ {
+ constructor->getSequence()->push_back(CreateZero(*field->type()));
+ }
+ }
+
+ return constructor;
+}
+
+TIntermConstantUnion::TIntermConstantUnion(const TIntermConstantUnion &node) : TIntermTyped(node)
+{
+ mUnionArrayPointer = node.mUnionArrayPointer;
+}
+
+void TFunctionSymbolInfo::setFromFunction(const TFunction &function)
+{
+ setName(function.getMangledName());
+ setId(function.getUniqueId());
+}
+
+TIntermAggregate::TIntermAggregate(const TIntermAggregate &node)
+ : TIntermOperator(node),
+ mUserDefined(node.mUserDefined),
+ mUseEmulatedFunction(node.mUseEmulatedFunction),
+ mGotPrecisionFromChildren(node.mGotPrecisionFromChildren),
+ mFunctionInfo(node.mFunctionInfo)
+{
+ for (TIntermNode *child : node.mSequence)
+ {
+ TIntermTyped *typedChild = child->getAsTyped();
+ ASSERT(typedChild != nullptr);
+ TIntermTyped *childCopy = typedChild->deepCopy();
+ mSequence.push_back(childCopy);
+ }
+}
+
+TIntermSwizzle::TIntermSwizzle(const TIntermSwizzle &node) : TIntermTyped(node)
+{
+ TIntermTyped *operandCopy = node.mOperand->deepCopy();
+ ASSERT(operandCopy != nullptr);
+ mOperand = operandCopy;
+}
+
+TIntermBinary::TIntermBinary(const TIntermBinary &node)
+ : TIntermOperator(node), mAddIndexClamp(node.mAddIndexClamp)
+{
+ TIntermTyped *leftCopy = node.mLeft->deepCopy();
+ TIntermTyped *rightCopy = node.mRight->deepCopy();
+ ASSERT(leftCopy != nullptr && rightCopy != nullptr);
+ mLeft = leftCopy;
+ mRight = rightCopy;
+}
+
+TIntermUnary::TIntermUnary(const TIntermUnary &node)
+ : TIntermOperator(node), mUseEmulatedFunction(node.mUseEmulatedFunction)
+{
+ TIntermTyped *operandCopy = node.mOperand->deepCopy();
+ ASSERT(operandCopy != nullptr);
+ mOperand = operandCopy;
+}
+
+TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node)
+{
+ TIntermTyped *conditionCopy = node.mCondition->deepCopy();
+ TIntermTyped *trueCopy = node.mTrueExpression->deepCopy();
+ TIntermTyped *falseCopy = node.mFalseExpression->deepCopy();
+ ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
+ mCondition = conditionCopy;
+ mTrueExpression = trueCopy;
+ mFalseExpression = falseCopy;
+}
+
+bool TIntermOperator::isAssignment() const
+{
+ return IsAssignment(mOp);
+}
+
+bool TIntermOperator::isMultiplication() const
+{
+ switch (mOp)
+ {
+ case EOpMul:
+ case EOpMatrixTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpVectorTimesScalar:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//
+// returns true if the operator is for one of the constructors
+//
+bool TIntermOperator::isConstructor() const
+{
+ switch (mOp)
+ {
+ case EOpConstructVec2:
+ case EOpConstructVec3:
+ case EOpConstructVec4:
+ case EOpConstructMat2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4:
+ case EOpConstructFloat:
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ case EOpConstructInt:
+ case EOpConstructUVec2:
+ case EOpConstructUVec3:
+ case EOpConstructUVec4:
+ case EOpConstructUInt:
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructBool:
+ case EOpConstructStruct:
+ return true;
+ default:
+ return false;
+ }
+}
+
+TOperator TIntermBinary::GetMulOpBasedOnOperands(const TType &left, const TType &right)
+{
+ if (left.isMatrix())
+ {
+ if (right.isMatrix())
+ {
+ return EOpMatrixTimesMatrix;
+ }
+ else
+ {
+ if (right.isVector())
+ {
+ return EOpMatrixTimesVector;
+ }
+ else
+ {
+ return EOpMatrixTimesScalar;
+ }
+ }
+ }
+ else
+ {
+ if (right.isMatrix())
+ {
+ if (left.isVector())
+ {
+ return EOpVectorTimesMatrix;
+ }
+ else
+ {
+ return EOpMatrixTimesScalar;
+ }
+ }
+ else
+ {
+ // Neither operand is a matrix.
+ if (left.isVector() == right.isVector())
+ {
+ // Leave as component product.
+ return EOpMul;
+ }
+ else
+ {
+ return EOpVectorTimesScalar;
+ }
+ }
+ }
+}
+
+TOperator TIntermBinary::GetMulAssignOpBasedOnOperands(const TType &left, const TType &right)
+{
+ if (left.isMatrix())
+ {
+ if (right.isMatrix())
+ {
+ return EOpMatrixTimesMatrixAssign;
+ }
+ else
+ {
+ // right should be scalar, but this may not be validated yet.
+ return EOpMatrixTimesScalarAssign;
+ }
+ }
+ else
+ {
+ if (right.isMatrix())
+ {
+ // Left should be a vector, but this may not be validated yet.
+ return EOpVectorTimesMatrixAssign;
+ }
+ else
+ {
+ // Neither operand is a matrix.
+ if (left.isVector() == right.isVector())
+ {
+ // Leave as component product.
+ return EOpMulAssign;
+ }
+ else
+ {
+ // left should be vector and right should be scalar, but this may not be validated
+ // yet.
+ return EOpVectorTimesScalarAssign;
+ }
+ }
+ }
+}
+
+//
+// Make sure the type of a unary operator is appropriate for its
+// combination of operation and operand type.
+//
+void TIntermUnary::promote()
+{
+ TQualifier resultQualifier = EvqTemporary;
+ if (mOperand->getQualifier() == EvqConst)
+ resultQualifier = EvqConst;
+
+ unsigned char operandPrimarySize =
+ static_cast<unsigned char>(mOperand->getType().getNominalSize());
+ switch (mOp)
+ {
+ case EOpFloatBitsToInt:
+ setType(TType(EbtInt, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpFloatBitsToUint:
+ setType(TType(EbtUInt, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpIntBitsToFloat:
+ case EOpUintBitsToFloat:
+ setType(TType(EbtFloat, EbpHigh, resultQualifier, operandPrimarySize));
+ break;
+ case EOpPackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpPackHalf2x16:
+ setType(TType(EbtUInt, EbpHigh, resultQualifier));
+ break;
+ case EOpUnpackSnorm2x16:
+ case EOpUnpackUnorm2x16:
+ setType(TType(EbtFloat, EbpHigh, resultQualifier, 2));
+ break;
+ case EOpUnpackHalf2x16:
+ setType(TType(EbtFloat, EbpMedium, resultQualifier, 2));
+ break;
+ case EOpAny:
+ case EOpAll:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
+ case EOpLength:
+ case EOpDeterminant:
+ setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier));
+ break;
+ case EOpTranspose:
+ setType(TType(EbtFloat, mOperand->getType().getPrecision(), resultQualifier,
+ static_cast<unsigned char>(mOperand->getType().getRows()),
+ static_cast<unsigned char>(mOperand->getType().getCols())));
+ break;
+ case EOpIsInf:
+ case EOpIsNan:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier, operandPrimarySize));
+ break;
+ default:
+ setType(mOperand->getType());
+ mType.setQualifier(resultQualifier);
+ break;
+ }
+}
+
+TIntermSwizzle::TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets)
+ : TIntermTyped(TType(EbtFloat, EbpUndefined)),
+ mOperand(operand),
+ mSwizzleOffsets(swizzleOffsets)
+{
+ ASSERT(mSwizzleOffsets.size() <= 4);
+ promote();
+}
+
+TIntermUnary::TIntermUnary(TOperator op, TIntermTyped *operand)
+ : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false)
+{
+ promote();
+}
+
+TIntermBinary::TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right)
+ : TIntermOperator(op), mLeft(left), mRight(right), mAddIndexClamp(false)
+{
+ promote();
+}
+
+TIntermTernary::TIntermTernary(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression)
+ : TIntermTyped(trueExpression->getType()),
+ mCondition(cond),
+ mTrueExpression(trueExpression),
+ mFalseExpression(falseExpression)
+{
+ getTypePointer()->setQualifier(
+ TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression));
+}
+
+// static
+TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression)
+{
+ if (cond->getQualifier() == EvqConst && trueExpression->getQualifier() == EvqConst &&
+ falseExpression->getQualifier() == EvqConst)
+ {
+ return EvqConst;
+ }
+ return EvqTemporary;
+}
+
+void TIntermSwizzle::promote()
+{
+ TQualifier resultQualifier = EvqTemporary;
+ if (mOperand->getQualifier() == EvqConst)
+ resultQualifier = EvqConst;
+
+ auto numFields = mSwizzleOffsets.size();
+ setType(TType(mOperand->getBasicType(), mOperand->getPrecision(), resultQualifier,
+ static_cast<unsigned char>(numFields)));
+}
+
+bool TIntermSwizzle::hasDuplicateOffsets() const
+{
+ int offsetCount[4] = {0u, 0u, 0u, 0u};
+ for (const auto offset : mSwizzleOffsets)
+ {
+ offsetCount[offset]++;
+ if (offsetCount[offset] > 1)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void TIntermSwizzle::writeOffsetsAsXYZW(TInfoSinkBase *out) const
+{
+ for (const int offset : mSwizzleOffsets)
+ {
+ switch (offset)
+ {
+ case 0:
+ *out << "x";
+ break;
+ case 1:
+ *out << "y";
+ break;
+ case 2:
+ *out << "z";
+ break;
+ case 3:
+ *out << "w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+}
+
+TQualifier TIntermBinary::GetCommaQualifier(int shaderVersion,
+ const TIntermTyped *left,
+ const TIntermTyped *right)
+{
+ // ESSL3.00 section 12.43: The result of a sequence operator is not a constant-expression.
+ if (shaderVersion >= 300 || left->getQualifier() != EvqConst ||
+ right->getQualifier() != EvqConst)
+ {
+ return EvqTemporary;
+ }
+ return EvqConst;
+}
+
+// Establishes the type of the result of the binary operation.
+void TIntermBinary::promote()
+{
+ ASSERT(!isMultiplication() ||
+ mOp == GetMulOpBasedOnOperands(mLeft->getType(), mRight->getType()));
+
+ // Comma is handled as a special case.
+ if (mOp == EOpComma)
+ {
+ setType(mRight->getType());
+ return;
+ }
+
+ // Base assumption: just make the type the same as the left
+ // operand. Then only deviations from this need be coded.
+ setType(mLeft->getType());
+
+ TQualifier resultQualifier = EvqConst;
+ // Binary operations results in temporary variables unless both
+ // operands are const.
+ if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
+ {
+ resultQualifier = EvqTemporary;
+ getTypePointer()->setQualifier(EvqTemporary);
+ }
+
+ // Handle indexing ops.
+ switch (mOp)
+ {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ if (mLeft->isArray())
+ {
+ mType.clearArrayness();
+ }
+ else if (mLeft->isMatrix())
+ {
+ setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier,
+ static_cast<unsigned char>(mLeft->getRows())));
+ }
+ else if (mLeft->isVector())
+ {
+ setType(TType(mLeft->getBasicType(), mLeft->getPrecision(), resultQualifier));
+ }
+ else
+ {
+ UNREACHABLE();
+ }
+ return;
+ case EOpIndexDirectStruct:
+ {
+ const TFieldList &fields = mLeft->getType().getStruct()->fields();
+ const int i = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[i]->type());
+ getTypePointer()->setQualifier(resultQualifier);
+ return;
+ }
+ case EOpIndexDirectInterfaceBlock:
+ {
+ const TFieldList &fields = mLeft->getType().getInterfaceBlock()->fields();
+ const int i = mRight->getAsConstantUnion()->getIConst(0);
+ setType(*fields[i]->type());
+ getTypePointer()->setQualifier(resultQualifier);
+ return;
+ }
+ default:
+ break;
+ }
+
+ ASSERT(mLeft->isArray() == mRight->isArray());
+
+ // The result gets promoted to the highest precision.
+ TPrecision higherPrecision = GetHigherPrecision(mLeft->getPrecision(), mRight->getPrecision());
+ getTypePointer()->setPrecision(higherPrecision);
+
+ const int nominalSize =
+ std::max(mLeft->getNominalSize(), mRight->getNominalSize());
+
+ //
+ // All scalars or structs. Code after this test assumes this case is removed!
+ //
+ if (nominalSize == 1)
+ {
+ switch (mOp)
+ {
+ //
+ // Promote to conditional
+ //
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
+
+ //
+ // And and Or operate on conditionals
+ //
+ case EOpLogicalAnd:
+ case EOpLogicalXor:
+ case EOpLogicalOr:
+ ASSERT(mLeft->getBasicType() == EbtBool && mRight->getBasicType() == EbtBool);
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
+
+ default:
+ break;
+ }
+ return;
+ }
+
+ // If we reach here, at least one of the operands is vector or matrix.
+ // The other operand could be a scalar, vector, or matrix.
+ TBasicType basicType = mLeft->getBasicType();
+
+ switch (mOp)
+ {
+ case EOpMul:
+ break;
+ case EOpMatrixTimesScalar:
+ if (mRight->isMatrix())
+ {
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mRight->getCols()),
+ static_cast<unsigned char>(mRight->getRows())));
+ }
+ break;
+ case EOpMatrixTimesVector:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mLeft->getRows()), 1));
+ break;
+ case EOpMatrixTimesMatrix:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mRight->getCols()),
+ static_cast<unsigned char>(mLeft->getRows())));
+ break;
+ case EOpVectorTimesScalar:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(nominalSize), 1));
+ break;
+ case EOpVectorTimesMatrix:
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(mRight->getCols()), 1));
+ break;
+ case EOpMulAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ ASSERT(mOp == GetMulAssignOpBasedOnOperands(mLeft->getType(), mRight->getType()));
+ break;
+ case EOpAssign:
+ case EOpInitialize:
+ ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
+ (mLeft->getSecondarySize() == mRight->getSecondarySize()));
+ break;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ {
+ const int secondarySize =
+ std::max(mLeft->getSecondarySize(), mRight->getSecondarySize());
+ setType(TType(basicType, higherPrecision, resultQualifier,
+ static_cast<unsigned char>(nominalSize),
+ static_cast<unsigned char>(secondarySize)));
+ ASSERT(!mLeft->isArray() && !mRight->isArray());
+ break;
+ }
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ ASSERT((mLeft->getNominalSize() == mRight->getNominalSize()) &&
+ (mLeft->getSecondarySize() == mRight->getSecondarySize()));
+ setType(TType(EbtBool, EbpUndefined, resultQualifier));
+ break;
+
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectInterfaceBlock:
+ case EOpIndexDirectStruct:
+ // These ops should be already fully handled.
+ UNREACHABLE();
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+}
+
+const TConstantUnion *TIntermConstantUnion::foldIndexing(int index)
+{
+ if (isArray())
+ {
+ ASSERT(index < static_cast<int>(getType().getArraySize()));
+ TType arrayElementType = getType();
+ arrayElementType.clearArrayness();
+ size_t arrayElementSize = arrayElementType.getObjectSize();
+ return &mUnionArrayPointer[arrayElementSize * index];
+ }
+ else if (isMatrix())
+ {
+ ASSERT(index < getType().getCols());
+ int size = getType().getRows();
+ return &mUnionArrayPointer[size * index];
+ }
+ else if (isVector())
+ {
+ ASSERT(index < getType().getNominalSize());
+ return &mUnionArrayPointer[index];
+ }
+ else
+ {
+ UNREACHABLE();
+ return nullptr;
+ }
+}
+
+TIntermTyped *TIntermSwizzle::fold()
+{
+ TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
+ if (operandConstant == nullptr)
+ {
+ return nullptr;
+ }
+
+ TConstantUnion *constArray = new TConstantUnion[mSwizzleOffsets.size()];
+ for (size_t i = 0; i < mSwizzleOffsets.size(); ++i)
+ {
+ constArray[i] = *operandConstant->foldIndexing(mSwizzleOffsets.at(i));
+ }
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+}
+
+TIntermTyped *TIntermBinary::fold(TDiagnostics *diagnostics)
+{
+ TIntermConstantUnion *leftConstant = mLeft->getAsConstantUnion();
+ TIntermConstantUnion *rightConstant = mRight->getAsConstantUnion();
+ switch (mOp)
+ {
+ case EOpIndexDirect:
+ {
+ if (leftConstant == nullptr || rightConstant == nullptr)
+ {
+ return nullptr;
+ }
+ int index = rightConstant->getIConst(0);
+
+ const TConstantUnion *constArray = leftConstant->foldIndexing(index);
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+ }
+ case EOpIndexDirectStruct:
+ {
+ if (leftConstant == nullptr || rightConstant == nullptr)
+ {
+ return nullptr;
+ }
+ const TFieldList &fields = mLeft->getType().getStruct()->fields();
+ size_t index = static_cast<size_t>(rightConstant->getIConst(0));
+
+ size_t previousFieldsSize = 0;
+ for (size_t i = 0; i < index; ++i)
+ {
+ previousFieldsSize += fields[i]->type()->getObjectSize();
+ }
+
+ const TConstantUnion *constArray = leftConstant->getUnionArrayPointer();
+ return CreateFoldedNode(constArray + previousFieldsSize, this, mType.getQualifier());
+ }
+ case EOpIndexIndirect:
+ case EOpIndexDirectInterfaceBlock:
+ // Can never be constant folded.
+ return nullptr;
+ default:
+ {
+ if (leftConstant == nullptr || rightConstant == nullptr)
+ {
+ return nullptr;
+ }
+ TConstantUnion *constArray =
+ leftConstant->foldBinary(mOp, rightConstant, diagnostics, mLeft->getLine());
+
+ // Nodes may be constant folded without being qualified as constant.
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+ }
+ }
+}
+
+TIntermTyped *TIntermUnary::fold(TDiagnostics *diagnostics)
+{
+ TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
+ if (operandConstant == nullptr)
+ {
+ return nullptr;
+ }
+
+ TConstantUnion *constArray = nullptr;
+ switch (mOp)
+ {
+ case EOpAny:
+ case EOpAll:
+ case EOpLength:
+ case EOpTranspose:
+ case EOpDeterminant:
+ case EOpInverse:
+ case EOpPackSnorm2x16:
+ case EOpUnpackSnorm2x16:
+ case EOpPackUnorm2x16:
+ case EOpUnpackUnorm2x16:
+ case EOpPackHalf2x16:
+ case EOpUnpackHalf2x16:
+ constArray = operandConstant->foldUnaryNonComponentWise(mOp);
+ break;
+ default:
+ constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics);
+ break;
+ }
+
+ // Nodes may be constant folded without being qualified as constant.
+ return CreateFoldedNode(constArray, this, mType.getQualifier());
+}
+
+TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics)
+{
+ // Make sure that all params are constant before actual constant folding.
+ for (auto *param : *getSequence())
+ {
+ if (param->getAsConstantUnion() == nullptr)
+ {
+ return nullptr;
+ }
+ }
+ TConstantUnion *constArray = nullptr;
+ if (isConstructor())
+ constArray = TIntermConstantUnion::FoldAggregateConstructor(this);
+ else
+ constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics);
+
+ // Nodes may be constant folded without being qualified as constant.
+ TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary;
+ return CreateFoldedNode(constArray, this, resultQualifier);
+}
+
+//
+// The fold functions see if an operation on a constant can be done in place,
+// without generating run-time code.
+//
+// Returns the constant value to keep using or nullptr.
+//
+TConstantUnion *TIntermConstantUnion::foldBinary(TOperator op,
+ TIntermConstantUnion *rightNode,
+ TDiagnostics *diagnostics,
+ const TSourceLoc &line)
+{
+ const TConstantUnion *leftArray = getUnionArrayPointer();
+ const TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
+
+ ASSERT(leftArray && rightArray);
+
+ size_t objectSize = getType().getObjectSize();
+
+ // for a case like float f = vec4(2, 3, 4, 5) + 1.2;
+ if (rightNode->getType().getObjectSize() == 1 && objectSize > 1)
+ {
+ rightArray = Vectorize(*rightNode->getUnionArrayPointer(), objectSize);
+ }
+ else if (rightNode->getType().getObjectSize() > 1 && objectSize == 1)
+ {
+ // for a case like float f = 1.2 + vec4(2, 3, 4, 5);
+ leftArray = Vectorize(*getUnionArrayPointer(), rightNode->getType().getObjectSize());
+ objectSize = rightNode->getType().getObjectSize();
+ }
+
+ TConstantUnion *resultArray = nullptr;
+
+ switch(op)
+ {
+ case EOpAdd:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = TConstantUnion::add(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+ case EOpSub:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = TConstantUnion::sub(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+
+ case EOpMul:
+ case EOpVectorTimesScalar:
+ case EOpMatrixTimesScalar:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = TConstantUnion::mul(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+
+ case EOpMatrixTimesMatrix:
+ {
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(getType().getBasicType() == EbtFloat && rightNode->getBasicType() == EbtFloat);
+
+ const int leftCols = getCols();
+ const int leftRows = getRows();
+ const int rightCols = rightNode->getType().getCols();
+ const int rightRows = rightNode->getType().getRows();
+ const int resultCols = rightCols;
+ const int resultRows = leftRows;
+
+ resultArray = new TConstantUnion[resultCols * resultRows];
+ for (int row = 0; row < resultRows; row++)
+ {
+ for (int column = 0; column < resultCols; column++)
+ {
+ resultArray[resultRows * column + row].setFConst(0.0f);
+ for (int i = 0; i < leftCols; i++)
+ {
+ resultArray[resultRows * column + row].setFConst(
+ resultArray[resultRows * column + row].getFConst() +
+ leftArray[i * leftRows + row].getFConst() *
+ rightArray[column * rightRows + i].getFConst());
+ }
+ }
+ }
+ }
+ break;
+
+ case EOpDiv:
+ case EOpIMod:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ if (rightArray[i] == 0.0f)
+ {
+ diagnostics->warning(
+ getLine(), "Divide by zero error during constant folding", "/", "");
+ resultArray[i].setFConst(leftArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX);
+ }
+ else
+ {
+ ASSERT(op == EOpDiv);
+ resultArray[i].setFConst(leftArray[i].getFConst() / rightArray[i].getFConst());
+ }
+ break;
+
+ case EbtInt:
+ if (rightArray[i] == 0)
+ {
+ diagnostics->warning(
+ getLine(), "Divide by zero error during constant folding", "/", "");
+ resultArray[i].setIConst(INT_MAX);
+ }
+ else
+ {
+ int lhs = leftArray[i].getIConst();
+ int divisor = rightArray[i].getIConst();
+ if (op == EOpDiv)
+ {
+ // Check for the special case where the minimum representable number is
+ // divided by -1. If left alone this leads to integer overflow in C++.
+ // ESSL 3.00.6 section 4.1.3 Integers:
+ // "However, for the case where the minimum representable value is
+ // divided by -1, it is allowed to return either the minimum
+ // representable value or the maximum representable value."
+ if (lhs == -0x7fffffff - 1 && divisor == -1)
+ {
+ resultArray[i].setIConst(0x7fffffff);
+ }
+ else
+ {
+ resultArray[i].setIConst(lhs / divisor);
+ }
+ }
+ else
+ {
+ ASSERT(op == EOpIMod);
+ if (lhs < 0 || divisor < 0)
+ {
+ // ESSL 3.00.6 section 5.9: Results of modulus are undefined when
+ // either one of the operands is negative.
+ diagnostics->warning(getLine(),
+ "Negative modulus operator operand "
+ "encountered during constant folding",
+ "%", "");
+ resultArray[i].setIConst(0);
+ }
+ else
+ {
+ resultArray[i].setIConst(lhs % divisor);
+ }
+ }
+ }
+ break;
+
+ case EbtUInt:
+ if (rightArray[i] == 0)
+ {
+ diagnostics->warning(
+ getLine(), "Divide by zero error during constant folding", "/", "");
+ resultArray[i].setUConst(UINT_MAX);
+ }
+ else
+ {
+ if (op == EOpDiv)
+ {
+ resultArray[i].setUConst(leftArray[i].getUConst() / rightArray[i].getUConst());
+ }
+ else
+ {
+ ASSERT(op == EOpIMod);
+ resultArray[i].setUConst(leftArray[i].getUConst() % rightArray[i].getUConst());
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+ }
+ break;
+
+ case EOpMatrixTimesVector:
+ {
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(rightNode->getBasicType() == EbtFloat);
+
+ const int matrixCols = getCols();
+ const int matrixRows = getRows();
+
+ resultArray = new TConstantUnion[matrixRows];
+
+ for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
+ {
+ resultArray[matrixRow].setFConst(0.0f);
+ for (int col = 0; col < matrixCols; col++)
+ {
+ resultArray[matrixRow].setFConst(resultArray[matrixRow].getFConst() +
+ leftArray[col * matrixRows + matrixRow].getFConst() *
+ rightArray[col].getFConst());
+ }
+ }
+ }
+ break;
+
+ case EOpVectorTimesMatrix:
+ {
+ // TODO(jmadll): This code should check for overflows.
+ ASSERT(getType().getBasicType() == EbtFloat);
+
+ const int matrixCols = rightNode->getType().getCols();
+ const int matrixRows = rightNode->getType().getRows();
+
+ resultArray = new TConstantUnion[matrixCols];
+
+ for (int matrixCol = 0; matrixCol < matrixCols; matrixCol++)
+ {
+ resultArray[matrixCol].setFConst(0.0f);
+ for (int matrixRow = 0; matrixRow < matrixRows; matrixRow++)
+ {
+ resultArray[matrixCol].setFConst(resultArray[matrixCol].getFConst() +
+ leftArray[matrixRow].getFConst() *
+ rightArray[matrixCol * matrixRows + matrixRow].getFConst());
+ }
+ }
+ }
+ break;
+
+ case EOpLogicalAnd:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ resultArray[i] = leftArray[i] && rightArray[i];
+ }
+ }
+ break;
+
+ case EOpLogicalOr:
+ {
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ resultArray[i] = leftArray[i] || rightArray[i];
+ }
+ }
+ break;
+
+ case EOpLogicalXor:
+ {
+ ASSERT(getType().getBasicType() == EbtBool);
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ resultArray[i].setBConst(leftArray[i] != rightArray[i]);
+ }
+ }
+ break;
+
+ case EOpBitwiseAnd:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] & rightArray[i];
+ break;
+ case EOpBitwiseXor:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] ^ rightArray[i];
+ break;
+ case EOpBitwiseOr:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = leftArray[i] | rightArray[i];
+ break;
+ case EOpBitShiftLeft:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = TConstantUnion::lshift(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+ case EOpBitShiftRight:
+ resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ resultArray[i] = TConstantUnion::rshift(leftArray[i], rightArray[i], diagnostics, line);
+ break;
+
+ case EOpLessThan:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(*leftArray < *rightArray);
+ break;
+
+ case EOpGreaterThan:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(*leftArray > *rightArray);
+ break;
+
+ case EOpLessThanEqual:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(!(*leftArray > *rightArray));
+ break;
+
+ case EOpGreaterThanEqual:
+ ASSERT(objectSize == 1);
+ resultArray = new TConstantUnion[1];
+ resultArray->setBConst(!(*leftArray < *rightArray));
+ break;
+
+ case EOpEqual:
+ case EOpNotEqual:
+ {
+ resultArray = new TConstantUnion[1];
+ bool equal = true;
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ if (leftArray[i] != rightArray[i])
+ {
+ equal = false;
+ break; // break out of for loop
+ }
+ }
+ if (op == EOpEqual)
+ {
+ resultArray->setBConst(equal);
+ }
+ else
+ {
+ resultArray->setBConst(!equal);
+ }
+ }
+ break;
+
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ return resultArray;
+}
+
+// The fold functions do operations on a constant at GLSL compile time, without generating run-time
+// code. Returns the constant value to keep using. Nullptr should not be returned.
+TConstantUnion *TIntermConstantUnion::foldUnaryNonComponentWise(TOperator op)
+{
+ // Do operations where the return type may have a different number of components compared to the
+ // operand type.
+
+ const TConstantUnion *operandArray = getUnionArrayPointer();
+ ASSERT(operandArray);
+
+ size_t objectSize = getType().getObjectSize();
+ TConstantUnion *resultArray = nullptr;
+ switch (op)
+ {
+ case EOpAny:
+ ASSERT(getType().getBasicType() == EbtBool);
+ resultArray = new TConstantUnion();
+ resultArray->setBConst(false);
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ if (operandArray[i].getBConst())
+ {
+ resultArray->setBConst(true);
+ break;
+ }
+ }
+ break;
+
+ case EOpAll:
+ ASSERT(getType().getBasicType() == EbtBool);
+ resultArray = new TConstantUnion();
+ resultArray->setBConst(true);
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ if (!operandArray[i].getBConst())
+ {
+ resultArray->setBConst(false);
+ break;
+ }
+ }
+ break;
+
+ case EOpLength:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray = new TConstantUnion();
+ resultArray->setFConst(VectorLength(operandArray, objectSize));
+ break;
+
+ case EOpTranspose:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray = new TConstantUnion[objectSize];
+ angle::Matrix<float> result =
+ GetMatrix(operandArray, getType().getRows(), getType().getCols()).transpose();
+ SetUnionArrayFromMatrix(result, resultArray);
+ break;
+ }
+
+ case EOpDeterminant:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ unsigned int size = getType().getNominalSize();
+ ASSERT(size >= 2 && size <= 4);
+ resultArray = new TConstantUnion();
+ resultArray->setFConst(GetMatrix(operandArray, size).determinant());
+ break;
+ }
+
+ case EOpInverse:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ unsigned int size = getType().getNominalSize();
+ ASSERT(size >= 2 && size <= 4);
+ resultArray = new TConstantUnion[objectSize];
+ angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
+ SetUnionArrayFromMatrix(result, resultArray);
+ break;
+ }
+
+ case EOpPackSnorm2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ ASSERT(getType().getNominalSize() == 2);
+ resultArray = new TConstantUnion();
+ resultArray->setUConst(
+ gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ break;
+
+ case EOpUnpackSnorm2x16:
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray = new TConstantUnion[2];
+ float f1, f2;
+ gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2);
+ resultArray[0].setFConst(f1);
+ resultArray[1].setFConst(f2);
+ break;
+ }
+
+ case EOpPackUnorm2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ ASSERT(getType().getNominalSize() == 2);
+ resultArray = new TConstantUnion();
+ resultArray->setUConst(
+ gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ break;
+
+ case EOpUnpackUnorm2x16:
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray = new TConstantUnion[2];
+ float f1, f2;
+ gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2);
+ resultArray[0].setFConst(f1);
+ resultArray[1].setFConst(f2);
+ break;
+ }
+
+ case EOpPackHalf2x16:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ ASSERT(getType().getNominalSize() == 2);
+ resultArray = new TConstantUnion();
+ resultArray->setUConst(
+ gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+ break;
+
+ case EOpUnpackHalf2x16:
+ {
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray = new TConstantUnion[2];
+ float f1, f2;
+ gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2);
+ resultArray[0].setFConst(f1);
+ resultArray[1].setFConst(f2);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ return resultArray;
+}
+
+TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op,
+ TDiagnostics *diagnostics)
+{
+ // Do unary operations where each component of the result is computed based on the corresponding
+ // component of the operand. Also folds normalize, though the divisor in that case takes all
+ // components into account.
+
+ const TConstantUnion *operandArray = getUnionArrayPointer();
+ ASSERT(operandArray);
+
+ size_t objectSize = getType().getObjectSize();
+
+ TConstantUnion *resultArray = new TConstantUnion[objectSize];
+ for (size_t i = 0; i < objectSize; i++)
+ {
+ switch(op)
+ {
+ case EOpNegative:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(-operandArray[i].getFConst());
+ break;
+ case EbtInt:
+ if (operandArray[i] == std::numeric_limits<int>::min())
+ {
+ // The minimum representable integer doesn't have a positive
+ // counterpart, rather the negation overflows and in ESSL is supposed to
+ // wrap back to the minimum representable integer. Make sure that we
+ // don't actually let the negation overflow, which has undefined
+ // behavior in C++.
+ resultArray[i].setIConst(std::numeric_limits<int>::min());
+ }
+ else
+ {
+ resultArray[i].setIConst(-operandArray[i].getIConst());
+ }
+ break;
+ case EbtUInt:
+ if (operandArray[i] == 0x80000000u)
+ {
+ resultArray[i].setUConst(0x80000000u);
+ }
+ else
+ {
+ resultArray[i].setUConst(static_cast<unsigned int>(
+ -static_cast<int>(operandArray[i].getUConst())));
+ }
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpPositive:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(operandArray[i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(operandArray[i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(static_cast<unsigned int>(
+ static_cast<int>(operandArray[i].getUConst())));
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpLogicalNot:
+ switch (getType().getBasicType())
+ {
+ case EbtBool:
+ resultArray[i].setBConst(!operandArray[i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpBitwiseNot:
+ switch (getType().getBasicType())
+ {
+ case EbtInt:
+ resultArray[i].setIConst(~operandArray[i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(~operandArray[i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpRadians:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst());
+ break;
+
+ case EOpDegrees:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
+ break;
+
+ case EOpSin:
+ foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]);
+ break;
+
+ case EOpCos:
+ foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]);
+ break;
+
+ case EOpTan:
+ foldFloatTypeUnary(operandArray[i], &tanf, &resultArray[i]);
+ break;
+
+ case EOpAsin:
+ // For asin(x), results are undefined if |x| > 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) > 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &asinf, &resultArray[i]);
+ break;
+
+ case EOpAcos:
+ // For acos(x), results are undefined if |x| > 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) > 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &acosf, &resultArray[i]);
+ break;
+
+ case EOpAtan:
+ foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]);
+ break;
+
+ case EOpSinh:
+ foldFloatTypeUnary(operandArray[i], &sinhf, &resultArray[i]);
+ break;
+
+ case EOpCosh:
+ foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]);
+ break;
+
+ case EOpTanh:
+ foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]);
+ break;
+
+ case EOpAsinh:
+ foldFloatTypeUnary(operandArray[i], &asinhf, &resultArray[i]);
+ break;
+
+ case EOpAcosh:
+ // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() < 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &acoshf, &resultArray[i]);
+ break;
+
+ case EOpAtanh:
+ // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to
+ // 0.
+ if (fabsf(operandArray[i].getFConst()) >= 1.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &atanhf, &resultArray[i]);
+ break;
+
+ case EOpAbs:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+ break;
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpSign:
+ switch (getType().getBasicType())
+ {
+ case EbtFloat:
+ {
+ float fConst = operandArray[i].getFConst();
+ float fResult = 0.0f;
+ if (fConst > 0.0f)
+ fResult = 1.0f;
+ else if (fConst < 0.0f)
+ fResult = -1.0f;
+ resultArray[i].setFConst(fResult);
+ break;
+ }
+ case EbtInt:
+ {
+ int iConst = operandArray[i].getIConst();
+ int iResult = 0;
+ if (iConst > 0)
+ iResult = 1;
+ else if (iConst < 0)
+ iResult = -1;
+ resultArray[i].setIConst(iResult);
+ break;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ break;
+
+ case EOpFloor:
+ foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]);
+ break;
+
+ case EOpTrunc:
+ foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]);
+ break;
+
+ case EOpRound:
+ foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]);
+ break;
+
+ case EOpRoundEven:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ float x = operandArray[i].getFConst();
+ float result;
+ float fractPart = modff(x, &result);
+ if (fabsf(fractPart) == 0.5f)
+ result = 2.0f * roundf(x / 2.0f);
+ else
+ result = roundf(x);
+ resultArray[i].setFConst(result);
+ break;
+ }
+
+ case EOpCeil:
+ foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]);
+ break;
+
+ case EOpFract:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ float x = operandArray[i].getFConst();
+ resultArray[i].setFConst(x - floorf(x));
+ break;
+ }
+
+ case EOpIsNan:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst()));
+ break;
+
+ case EOpIsInf:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst()));
+ break;
+
+ case EOpFloatBitsToInt:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst()));
+ break;
+
+ case EOpFloatBitsToUint:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst()));
+ break;
+
+ case EOpIntBitsToFloat:
+ ASSERT(getType().getBasicType() == EbtInt);
+ resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst()));
+ break;
+
+ case EOpUintBitsToFloat:
+ ASSERT(getType().getBasicType() == EbtUInt);
+ resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst()));
+ break;
+
+ case EOpExp:
+ foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]);
+ break;
+
+ case EOpLog:
+ // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+ break;
+
+ case EOpExp2:
+ foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]);
+ break;
+
+ case EOpLog2:
+ // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
+ // And log2f is not available on some plarforms like old android, so just using
+ // log(x)/log(2) here.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ {
+ foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+ resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
+ }
+ break;
+
+ case EOpSqrt:
+ // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
+ if (operandArray[i].getFConst() < 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+ break;
+
+ case EOpInverseSqrt:
+ // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
+ // so getting the square root first using builtin function sqrt() and then taking
+ // its inverse.
+ // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set
+ // result to 0.
+ if (operandArray[i].getFConst() <= 0.0f)
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ else
+ {
+ foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+ resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
+ }
+ break;
+
+ case EOpVectorLogicalNot:
+ ASSERT(getType().getBasicType() == EbtBool);
+ resultArray[i].setBConst(!operandArray[i].getBConst());
+ break;
+
+ case EOpNormalize:
+ {
+ ASSERT(getType().getBasicType() == EbtFloat);
+ float x = operandArray[i].getFConst();
+ float length = VectorLength(operandArray, objectSize);
+ if (length)
+ resultArray[i].setFConst(x / length);
+ else
+ UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+ diagnostics, &resultArray[i]);
+ break;
+ }
+
+ case EOpDFdx:
+ case EOpDFdy:
+ case EOpFwidth:
+ ASSERT(getType().getBasicType() == EbtFloat);
+ // Derivatives of constant arguments should be 0.
+ resultArray[i].setFConst(0.0f);
+ break;
+
+ default:
+ return nullptr;
+ }
+ }
+
+ return resultArray;
+}
+
+void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter,
+ FloatTypeUnaryFunc builtinFunc,
+ TConstantUnion *result) const
+{
+ ASSERT(builtinFunc);
+
+ ASSERT(getType().getBasicType() == EbtFloat);
+ result->setFConst(builtinFunc(parameter.getFConst()));
+}
+
+// static
+TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate)
+{
+ ASSERT(aggregate->getSequence()->size() > 0u);
+ size_t resultSize = aggregate->getType().getObjectSize();
+ TConstantUnion *resultArray = new TConstantUnion[resultSize];
+ TBasicType basicType = aggregate->getBasicType();
+
+ size_t resultIndex = 0u;
+
+ if (aggregate->getSequence()->size() == 1u)
+ {
+ TIntermNode *argument = aggregate->getSequence()->front();
+ TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion();
+ const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer();
+ // Check the special case of constructing a matrix diagonal from a single scalar,
+ // or a vector from a single scalar.
+ if (argumentConstant->getType().getObjectSize() == 1u)
+ {
+ if (aggregate->isMatrix())
+ {
+ int resultCols = aggregate->getType().getCols();
+ int resultRows = aggregate->getType().getRows();
+ for (int col = 0; col < resultCols; ++col)
+ {
+ for (int row = 0; row < resultRows; ++row)
+ {
+ if (col == row)
+ {
+ resultArray[resultIndex].cast(basicType, argumentUnionArray[0]);
+ }
+ else
+ {
+ resultArray[resultIndex].setFConst(0.0f);
+ }
+ ++resultIndex;
+ }
+ }
+ }
+ else
+ {
+ while (resultIndex < resultSize)
+ {
+ resultArray[resultIndex].cast(basicType, argumentUnionArray[0]);
+ ++resultIndex;
+ }
+ }
+ ASSERT(resultIndex == resultSize);
+ return resultArray;
+ }
+ else if (aggregate->isMatrix() && argumentConstant->isMatrix())
+ {
+ // The special case of constructing a matrix from a matrix.
+ int argumentCols = argumentConstant->getType().getCols();
+ int argumentRows = argumentConstant->getType().getRows();
+ int resultCols = aggregate->getType().getCols();
+ int resultRows = aggregate->getType().getRows();
+ for (int col = 0; col < resultCols; ++col)
+ {
+ for (int row = 0; row < resultRows; ++row)
+ {
+ if (col < argumentCols && row < argumentRows)
+ {
+ resultArray[resultIndex].cast(basicType,
+ argumentUnionArray[col * argumentRows + row]);
+ }
+ else if (col == row)
+ {
+ resultArray[resultIndex].setFConst(1.0f);
+ }
+ else
+ {
+ resultArray[resultIndex].setFConst(0.0f);
+ }
+ ++resultIndex;
+ }
+ }
+ ASSERT(resultIndex == resultSize);
+ return resultArray;
+ }
+ }
+
+ for (TIntermNode *&argument : *aggregate->getSequence())
+ {
+ TIntermConstantUnion *argumentConstant = argument->getAsConstantUnion();
+ size_t argumentSize = argumentConstant->getType().getObjectSize();
+ const TConstantUnion *argumentUnionArray = argumentConstant->getUnionArrayPointer();
+ for (size_t i = 0u; i < argumentSize; ++i)
+ {
+ if (resultIndex >= resultSize)
+ break;
+ resultArray[resultIndex].cast(basicType, argumentUnionArray[i]);
+ ++resultIndex;
+ }
+ }
+ ASSERT(resultIndex == resultSize);
+ return resultArray;
+}
+
+// static
+TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+ TDiagnostics *diagnostics)
+{
+ TOperator op = aggregate->getOp();
+ TIntermSequence *sequence = aggregate->getSequence();
+ unsigned int paramsCount = static_cast<unsigned int>(sequence->size());
+ std::vector<const TConstantUnion *> unionArrays(paramsCount);
+ std::vector<size_t> objectSizes(paramsCount);
+ size_t maxObjectSize = 0;
+ TBasicType basicType = EbtVoid;
+ TSourceLoc loc;
+ for (unsigned int i = 0; i < paramsCount; i++)
+ {
+ TIntermConstantUnion *paramConstant = (*sequence)[i]->getAsConstantUnion();
+ ASSERT(paramConstant != nullptr); // Should be checked already.
+
+ if (i == 0)
+ {
+ basicType = paramConstant->getType().getBasicType();
+ loc = paramConstant->getLine();
+ }
+ unionArrays[i] = paramConstant->getUnionArrayPointer();
+ objectSizes[i] = paramConstant->getType().getObjectSize();
+ if (objectSizes[i] > maxObjectSize)
+ maxObjectSize = objectSizes[i];
+ }
+
+ if (!(*sequence)[0]->getAsTyped()->isMatrix() && aggregate->getOp() != EOpOuterProduct)
+ {
+ for (unsigned int i = 0; i < paramsCount; i++)
+ if (objectSizes[i] != maxObjectSize)
+ unionArrays[i] = Vectorize(*unionArrays[i], maxObjectSize);
+ }
+
+ TConstantUnion *resultArray = nullptr;
+ if (paramsCount == 2)
+ {
+ //
+ // Binary built-in
+ //
+ switch (op)
+ {
+ case EOpAtan:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float y = unionArrays[0][i].getFConst();
+ float x = unionArrays[1][i].getFConst();
+ // Results are undefined if x and y are both 0.
+ if (x == 0.0f && y == 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setFConst(atan2f(y, x));
+ }
+ break;
+ }
+
+ case EOpPow:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ // Results are undefined if x < 0.
+ // Results are undefined if x = 0 and y <= 0.
+ if (x < 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else if (x == 0.0f && y <= 0.0f)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setFConst(powf(x, y));
+ }
+ break;
+ }
+
+ case EOpMod:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ resultArray[i].setFConst(x - y * floorf(x / y));
+ }
+ break;
+ }
+
+ case EOpMin:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(),
+ unionArrays[1][i].getFConst()));
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(),
+ unionArrays[1][i].getIConst()));
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(),
+ unionArrays[1][i].getUConst()));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpMax:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(),
+ unionArrays[1][i].getFConst()));
+ break;
+ case EbtInt:
+ resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(),
+ unionArrays[1][i].getIConst()));
+ break;
+ case EbtUInt:
+ resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(),
+ unionArrays[1][i].getUConst()));
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpStep:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ resultArray[i].setFConst(
+ unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f
+ : 1.0f);
+ break;
+ }
+
+ case EOpLessThan:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() <
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() <
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() <
+ unionArrays[1][i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpLessThanEqual:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() <=
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() <=
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() <=
+ unionArrays[1][i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpGreaterThan:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() >
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() >
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() >
+ unionArrays[1][i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+ case EOpGreaterThanEqual:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() >=
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() >=
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() >=
+ unionArrays[1][i].getUConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+ break;
+
+ case EOpVectorEqual:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() ==
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() ==
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() ==
+ unionArrays[1][i].getUConst());
+ break;
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() ==
+ unionArrays[1][i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpVectorNotEqual:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ resultArray[i].setBConst(unionArrays[0][i].getFConst() !=
+ unionArrays[1][i].getFConst());
+ break;
+ case EbtInt:
+ resultArray[i].setBConst(unionArrays[0][i].getIConst() !=
+ unionArrays[1][i].getIConst());
+ break;
+ case EbtUInt:
+ resultArray[i].setBConst(unionArrays[0][i].getUConst() !=
+ unionArrays[1][i].getUConst());
+ break;
+ case EbtBool:
+ resultArray[i].setBConst(unionArrays[0][i].getBConst() !=
+ unionArrays[1][i].getBConst());
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpDistance:
+ {
+ ASSERT(basicType == EbtFloat);
+ TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
+ resultArray = new TConstantUnion();
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ distanceArray[i].setFConst(x - y);
+ }
+ resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
+ break;
+ }
+
+ case EOpDot:
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion();
+ resultArray->setFConst(
+ VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+ break;
+
+ case EOpCross:
+ {
+ ASSERT(basicType == EbtFloat && maxObjectSize == 3);
+ resultArray = new TConstantUnion[maxObjectSize];
+ float x0 = unionArrays[0][0].getFConst();
+ float x1 = unionArrays[0][1].getFConst();
+ float x2 = unionArrays[0][2].getFConst();
+ float y0 = unionArrays[1][0].getFConst();
+ float y1 = unionArrays[1][1].getFConst();
+ float y2 = unionArrays[1][2].getFConst();
+ resultArray[0].setFConst(x1 * y2 - y1 * x2);
+ resultArray[1].setFConst(x2 * y0 - y2 * x0);
+ resultArray[2].setFConst(x0 * y1 - y0 * x1);
+ break;
+ }
+
+ case EOpReflect:
+ {
+ ASSERT(basicType == EbtFloat);
+ // genType reflect (genType I, genType N) :
+ // For the incident vector I and surface orientation N, returns the reflection
+ // direction:
+ // I - 2 * dot(N, I) * N.
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float result = unionArrays[0][i].getFConst() -
+ 2.0f * dotProduct * unionArrays[1][i].getFConst();
+ resultArray[i].setFConst(result);
+ }
+ break;
+ }
+
+ case EOpMul:
+ {
+ ASSERT(basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
+ (*sequence)[1]->getAsTyped()->isMatrix());
+ // Perform component-wise matrix multiplication.
+ resultArray = new TConstantUnion[maxObjectSize];
+ int size = (*sequence)[0]->getAsTyped()->getNominalSize();
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
+ SetUnionArrayFromMatrix(result, resultArray);
+ break;
+ }
+
+ case EOpOuterProduct:
+ {
+ ASSERT(basicType == EbtFloat);
+ size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
+ size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
+ resultArray = new TConstantUnion[numRows * numCols];
+ angle::Matrix<float> result =
+ GetMatrix(unionArrays[0], static_cast<int>(numRows), 1)
+ .outerProduct(GetMatrix(unionArrays[1], 1, static_cast<int>(numCols)));
+ SetUnionArrayFromMatrix(result, resultArray);
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ // TODO: Add constant folding support for other built-in operations that take 2
+ // parameters and not handled above.
+ return nullptr;
+ }
+ }
+ else if (paramsCount == 3)
+ {
+ //
+ // Ternary built-in
+ //
+ switch (op)
+ {
+ case EOpClamp:
+ {
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ switch (basicType)
+ {
+ case EbtFloat:
+ {
+ float x = unionArrays[0][i].getFConst();
+ float min = unionArrays[1][i].getFConst();
+ float max = unionArrays[2][i].getFConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setFConst(gl::clamp(x, min, max));
+ break;
+ }
+
+ case EbtInt:
+ {
+ int x = unionArrays[0][i].getIConst();
+ int min = unionArrays[1][i].getIConst();
+ int max = unionArrays[2][i].getIConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setIConst(gl::clamp(x, min, max));
+ break;
+ }
+ case EbtUInt:
+ {
+ unsigned int x = unionArrays[0][i].getUConst();
+ unsigned int min = unionArrays[1][i].getUConst();
+ unsigned int max = unionArrays[2][i].getUConst();
+ // Results are undefined if min > max.
+ if (min > max)
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ else
+ resultArray[i].setUConst(gl::clamp(x, min, max));
+ break;
+ }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
+ break;
+ }
+
+ case EOpMix:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float x = unionArrays[0][i].getFConst();
+ float y = unionArrays[1][i].getFConst();
+ TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
+ if (type == EbtFloat)
+ {
+ // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
+ float a = unionArrays[2][i].getFConst();
+ resultArray[i].setFConst(x * (1.0f - a) + y * a);
+ }
+ else // 3rd parameter is EbtBool
+ {
+ ASSERT(type == EbtBool);
+ // Selects which vector each returned component comes from.
+ // For a component of a that is false, the corresponding component of x is
+ // returned.
+ // For a component of a that is true, the corresponding component of y is
+ // returned.
+ bool a = unionArrays[2][i].getBConst();
+ resultArray[i].setFConst(a ? y : x);
+ }
+ }
+ break;
+ }
+
+ case EOpSmoothStep:
+ {
+ ASSERT(basicType == EbtFloat);
+ resultArray = new TConstantUnion[maxObjectSize];
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float edge0 = unionArrays[0][i].getFConst();
+ float edge1 = unionArrays[1][i].getFConst();
+ float x = unionArrays[2][i].getFConst();
+ // Results are undefined if edge0 >= edge1.
+ if (edge0 >= edge1)
+ {
+ UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+ &resultArray[i]);
+ }
+ else
+ {
+ // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
+ // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
+ float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+ resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
+ }
+ }
+ break;
+ }
+
+ case EOpFaceForward:
+ {
+ ASSERT(basicType == EbtFloat);
+ // genType faceforward(genType N, genType I, genType Nref) :
+ // If dot(Nref, I) < 0 return N, otherwise return -N.
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ if (dotProduct < 0)
+ resultArray[i].setFConst(unionArrays[0][i].getFConst());
+ else
+ resultArray[i].setFConst(-unionArrays[0][i].getFConst());
+ }
+ break;
+ }
+
+ case EOpRefract:
+ {
+ ASSERT(basicType == EbtFloat);
+ // genType refract(genType I, genType N, float eta) :
+ // For the incident vector I and surface normal N, and the ratio of indices of
+ // refraction eta,
+ // return the refraction vector. The result is computed by
+ // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
+ // if (k < 0.0)
+ // return genType(0.0)
+ // else
+ // return eta * I - (eta * dot(N, I) + sqrt(k)) * N
+ resultArray = new TConstantUnion[maxObjectSize];
+ float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
+ for (size_t i = 0; i < maxObjectSize; i++)
+ {
+ float eta = unionArrays[2][i].getFConst();
+ float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
+ if (k < 0.0f)
+ resultArray[i].setFConst(0.0f);
+ else
+ resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
+ (eta * dotProduct + sqrtf(k)) *
+ unionArrays[1][i].getFConst());
+ }
+ break;
+ }
+
+ default:
+ UNREACHABLE();
+ // TODO: Add constant folding support for other built-in operations that take 3
+ // parameters and not handled above.
+ return nullptr;
+ }
+ }
+ return resultArray;
+}
+
+// static
+TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunction)
+{
+ if (hashFunction == NULL || name.empty())
+ return name;
+ khronos_uint64_t number = (*hashFunction)(name.c_str(), name.length());
+ TStringStream stream;
+ stream << HASHED_NAME_PREFIX << std::hex << number;
+ TString hashedName = stream.str();
+ return hashedName;
+}
+
+void TIntermTraverser::updateTree()
+{
+ for (size_t ii = 0; ii < mInsertions.size(); ++ii)
+ {
+ const NodeInsertMultipleEntry &insertion = mInsertions[ii];
+ ASSERT(insertion.parent);
+ if (!insertion.insertionsAfter.empty())
+ {
+ bool inserted = insertion.parent->insertChildNodes(insertion.position + 1,
+ insertion.insertionsAfter);
+ ASSERT(inserted);
+ }
+ if (!insertion.insertionsBefore.empty())
+ {
+ bool inserted =
+ insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore);
+ ASSERT(inserted);
+ }
+ }
+ for (size_t ii = 0; ii < mReplacements.size(); ++ii)
+ {
+ const NodeUpdateEntry &replacement = mReplacements[ii];
+ ASSERT(replacement.parent);
+ bool replaced = replacement.parent->replaceChildNode(
+ replacement.original, replacement.replacement);
+ ASSERT(replaced);
+
+ if (!replacement.originalBecomesChildOfReplacement)
+ {
+ // In AST traversing, a parent is visited before its children.
+ // After we replace a node, if its immediate child is to
+ // be replaced, we need to make sure we don't update the replaced
+ // node; instead, we update the replacement node.
+ for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
+ {
+ NodeUpdateEntry &replacement2 = mReplacements[jj];
+ if (replacement2.parent == replacement.original)
+ replacement2.parent = replacement.replacement;
+ }
+ }
+ }
+ for (size_t ii = 0; ii < mMultiReplacements.size(); ++ii)
+ {
+ const NodeReplaceWithMultipleEntry &replacement = mMultiReplacements[ii];
+ ASSERT(replacement.parent);
+ bool replaced = replacement.parent->replaceChildNodeWithMultiple(
+ replacement.original, replacement.replacements);
+ ASSERT(replaced);
+ }
+
+ clearReplacementQueue();
+}
+
+void TIntermTraverser::clearReplacementQueue()
+{
+ mReplacements.clear();
+ mMultiReplacements.clear();
+ mInsertions.clear();
+}
+
+void TIntermTraverser::queueReplacement(TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus)
+{
+ queueReplacementWithParent(getParentNode(), original, replacement, originalStatus);
+}
+
+void TIntermTraverser::queueReplacementWithParent(TIntermNode *parent,
+ TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus)
+{
+ bool originalBecomesChild = (originalStatus == OriginalNode::BECOMES_CHILD);
+ mReplacements.push_back(NodeUpdateEntry(parent, original, replacement, originalBecomesChild));
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/IntermNode.h b/gfx/angle/src/compiler/translator/IntermNode.h
new file mode 100755
index 000000000..94811bd1c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/IntermNode.h
@@ -0,0 +1,1207 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Definition of the in-memory high-level intermediate representation
+// of shaders. This is a tree that parser creates.
+//
+// Nodes in the tree are defined as a hierarchy of classes derived from
+// TIntermNode. Each is a node in a tree. There is no preset branching factor;
+// each node can have it's own type of list of children.
+//
+
+#ifndef COMPILER_TRANSLATOR_INTERMNODE_H_
+#define COMPILER_TRANSLATOR_INTERMNODE_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include <algorithm>
+#include <queue>
+
+#include "common/angleutils.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/ConstantUnion.h"
+#include "compiler/translator/Operator.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+
+class TDiagnostics;
+
+class TIntermTraverser;
+class TIntermAggregate;
+class TIntermBlock;
+class TIntermDeclaration;
+class TIntermFunctionDefinition;
+class TIntermSwizzle;
+class TIntermBinary;
+class TIntermUnary;
+class TIntermConstantUnion;
+class TIntermTernary;
+class TIntermIfElse;
+class TIntermSwitch;
+class TIntermCase;
+class TIntermTyped;
+class TIntermSymbol;
+class TIntermLoop;
+class TInfoSink;
+class TInfoSinkBase;
+class TIntermRaw;
+class TIntermBranch;
+
+class TSymbolTable;
+class TFunction;
+
+// Encapsulate an identifier string and track whether it is coming from the original shader code
+// (not internal) or from ANGLE (internal). Usually internal names shouldn't be decorated or hashed.
+class TName
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ explicit TName(const TString &name) : mName(name), mIsInternal(false) {}
+ TName() : mName(), mIsInternal(false) {}
+ TName(const TName &) = default;
+ TName &operator=(const TName &) = default;
+
+ const TString &getString() const { return mName; }
+ void setString(const TString &string) { mName = string; }
+ bool isInternal() const { return mIsInternal; }
+ void setInternal(bool isInternal) { mIsInternal = isInternal; }
+
+ private:
+ TString mName;
+ bool mIsInternal;
+};
+
+//
+// Base class for the tree nodes
+//
+class TIntermNode : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TIntermNode()
+ {
+ // TODO: Move this to TSourceLoc constructor
+ // after getting rid of TPublicType.
+ mLine.first_file = mLine.last_file = 0;
+ mLine.first_line = mLine.last_line = 0;
+ }
+ virtual ~TIntermNode() { }
+
+ const TSourceLoc &getLine() const { return mLine; }
+ void setLine(const TSourceLoc &l) { mLine = l; }
+
+ virtual void traverse(TIntermTraverser *) = 0;
+ virtual TIntermTyped *getAsTyped() { return 0; }
+ virtual TIntermConstantUnion *getAsConstantUnion() { return 0; }
+ virtual TIntermFunctionDefinition *getAsFunctionDefinition() { return nullptr; }
+ virtual TIntermAggregate *getAsAggregate() { return 0; }
+ virtual TIntermBlock *getAsBlock() { return nullptr; }
+ virtual TIntermDeclaration *getAsDeclarationNode() { return nullptr; }
+ virtual TIntermSwizzle *getAsSwizzleNode() { return nullptr; }
+ virtual TIntermBinary *getAsBinaryNode() { return 0; }
+ virtual TIntermUnary *getAsUnaryNode() { return 0; }
+ virtual TIntermTernary *getAsTernaryNode() { return nullptr; }
+ virtual TIntermIfElse *getAsIfElseNode() { return nullptr; }
+ virtual TIntermSwitch *getAsSwitchNode() { return 0; }
+ virtual TIntermCase *getAsCaseNode() { return 0; }
+ virtual TIntermSymbol *getAsSymbolNode() { return 0; }
+ virtual TIntermLoop *getAsLoopNode() { return 0; }
+ virtual TIntermRaw *getAsRawNode() { return 0; }
+ virtual TIntermBranch *getAsBranchNode() { return 0; }
+
+ // Replace a child node. Return true if |original| is a child
+ // node and it is replaced; otherwise, return false.
+ virtual bool replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement) = 0;
+
+ protected:
+ TSourceLoc mLine;
+};
+
+//
+// This is just to help yacc.
+//
+struct TIntermNodePair
+{
+ TIntermNode *node1;
+ TIntermNode *node2;
+};
+
+//
+// Intermediate class for nodes that have a type.
+//
+class TIntermTyped : public TIntermNode
+{
+ public:
+ TIntermTyped(const TType &t) : mType(t) { }
+
+ virtual TIntermTyped *deepCopy() const = 0;
+
+ TIntermTyped *getAsTyped() override { return this; }
+
+ virtual bool hasSideEffects() const = 0;
+
+ void setType(const TType &t) { mType = t; }
+ void setTypePreservePrecision(const TType &t);
+ const TType &getType() const { return mType; }
+ TType *getTypePointer() { return &mType; }
+
+ TBasicType getBasicType() const { return mType.getBasicType(); }
+ TQualifier getQualifier() const { return mType.getQualifier(); }
+ TPrecision getPrecision() const { return mType.getPrecision(); }
+ TMemoryQualifier getMemoryQualifier() const { return mType.getMemoryQualifier(); }
+ int getCols() const { return mType.getCols(); }
+ int getRows() const { return mType.getRows(); }
+ int getNominalSize() const { return mType.getNominalSize(); }
+ int getSecondarySize() const { return mType.getSecondarySize(); }
+
+ bool isInterfaceBlock() const { return mType.isInterfaceBlock(); }
+ bool isMatrix() const { return mType.isMatrix(); }
+ bool isArray() const { return mType.isArray(); }
+ bool isVector() const { return mType.isVector(); }
+ bool isScalar() const { return mType.isScalar(); }
+ bool isScalarInt() const { return mType.isScalarInt(); }
+ const char *getBasicString() const { return mType.getBasicString(); }
+ TString getCompleteString() const { return mType.getCompleteString(); }
+
+ unsigned int getArraySize() const { return mType.getArraySize(); }
+
+ bool isConstructorWithOnlyConstantUnionParameters();
+
+ static TIntermTyped *CreateIndexNode(int index);
+ static TIntermTyped *CreateZero(const TType &type);
+
+ protected:
+ TType mType;
+
+ TIntermTyped(const TIntermTyped &node);
+};
+
+//
+// Handle for, do-while, and while loops.
+//
+enum TLoopType
+{
+ ELoopFor,
+ ELoopWhile,
+ ELoopDoWhile
+};
+
+class TIntermLoop : public TIntermNode
+{
+ public:
+ TIntermLoop(TLoopType type,
+ TIntermNode *init,
+ TIntermTyped *cond,
+ TIntermTyped *expr,
+ TIntermBlock *body)
+ : mType(type), mInit(init), mCond(cond), mExpr(expr), mBody(body), mUnrollFlag(false)
+ {
+ }
+
+ TIntermLoop *getAsLoopNode() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ TLoopType getType() const { return mType; }
+ TIntermNode *getInit() { return mInit; }
+ TIntermTyped *getCondition() { return mCond; }
+ TIntermTyped *getExpression() { return mExpr; }
+ TIntermBlock *getBody() { return mBody; }
+
+ void setCondition(TIntermTyped *condition) { mCond = condition; }
+ void setExpression(TIntermTyped *expression) { mExpr = expression; }
+ void setBody(TIntermBlock *body) { mBody = body; }
+
+ void setUnrollFlag(bool flag) { mUnrollFlag = flag; }
+ bool getUnrollFlag() const { return mUnrollFlag; }
+
+ protected:
+ TLoopType mType;
+ TIntermNode *mInit; // for-loop initialization
+ TIntermTyped *mCond; // loop exit condition
+ TIntermTyped *mExpr; // for-loop expression
+ TIntermBlock *mBody; // loop body
+
+ bool mUnrollFlag; // Whether the loop should be unrolled or not.
+};
+
+//
+// Handle break, continue, return, and kill.
+//
+class TIntermBranch : public TIntermNode
+{
+ public:
+ TIntermBranch(TOperator op, TIntermTyped *e)
+ : mFlowOp(op),
+ mExpression(e) { }
+
+ void traverse(TIntermTraverser *it) override;
+ TIntermBranch *getAsBranchNode() override { return this; }
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ TOperator getFlowOp() { return mFlowOp; }
+ TIntermTyped* getExpression() { return mExpression; }
+
+protected:
+ TOperator mFlowOp;
+ TIntermTyped *mExpression; // non-zero except for "return exp;" statements
+};
+
+//
+// Nodes that correspond to symbols or constants in the source code.
+//
+class TIntermSymbol : public TIntermTyped
+{
+ public:
+ // if symbol is initialized as symbol(sym), the memory comes from the poolallocator of sym.
+ // If sym comes from per process globalpoolallocator, then it causes increased memory usage
+ // per compile it is essential to use "symbol = sym" to assign to symbol
+ TIntermSymbol(int id, const TString &symbol, const TType &type)
+ : TIntermTyped(type), mId(id), mSymbol(symbol)
+ {
+ }
+
+ TIntermTyped *deepCopy() const override { return new TIntermSymbol(*this); }
+
+ bool hasSideEffects() const override { return false; }
+
+ int getId() const { return mId; }
+ const TString &getSymbol() const { return mSymbol.getString(); }
+ const TName &getName() const { return mSymbol; }
+
+ void setId(int newId) { mId = newId; }
+
+ void setInternal(bool internal) { mSymbol.setInternal(internal); }
+
+ void traverse(TIntermTraverser *it) override;
+ TIntermSymbol *getAsSymbolNode() override { return this; }
+ bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
+
+ protected:
+ int mId;
+ TName mSymbol;
+
+ private:
+ TIntermSymbol(const TIntermSymbol &) = default; // Note: not deleted, just private!
+};
+
+// A Raw node stores raw code, that the translator will insert verbatim
+// into the output stream. Useful for transformation operations that make
+// complex code that might not fit naturally into the GLSL model.
+class TIntermRaw : public TIntermTyped
+{
+ public:
+ TIntermRaw(const TType &type, const TString &rawText)
+ : TIntermTyped(type),
+ mRawText(rawText) { }
+ TIntermRaw(const TIntermRaw &) = delete;
+
+ TIntermTyped *deepCopy() const override
+ {
+ UNREACHABLE();
+ return nullptr;
+ }
+
+ bool hasSideEffects() const override { return false; }
+
+ TString getRawText() const { return mRawText; }
+
+ void traverse(TIntermTraverser *it) override;
+
+ TIntermRaw *getAsRawNode() override { return this; }
+ bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
+
+ protected:
+ TString mRawText;
+};
+
+// Constant folded node.
+// Note that nodes may be constant folded and not be constant expressions with the EvqConst
+// qualifier. This happens for example when the following expression is processed:
+// "true ? 1.0 : non_constant"
+// Other nodes than TIntermConstantUnion may also be constant expressions.
+//
+class TIntermConstantUnion : public TIntermTyped
+{
+ public:
+ TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type)
+ : TIntermTyped(type), mUnionArrayPointer(unionPointer)
+ {
+ ASSERT(unionPointer);
+ }
+
+ TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); }
+
+ bool hasSideEffects() const override { return false; }
+
+ const TConstantUnion *getUnionArrayPointer() const { return mUnionArrayPointer; }
+
+ int getIConst(size_t index) const
+ {
+ return mUnionArrayPointer ? mUnionArrayPointer[index].getIConst() : 0;
+ }
+ unsigned int getUConst(size_t index) const
+ {
+ return mUnionArrayPointer ? mUnionArrayPointer[index].getUConst() : 0;
+ }
+ float getFConst(size_t index) const
+ {
+ return mUnionArrayPointer ? mUnionArrayPointer[index].getFConst() : 0.0f;
+ }
+ bool getBConst(size_t index) const
+ {
+ return mUnionArrayPointer ? mUnionArrayPointer[index].getBConst() : false;
+ }
+
+ void replaceConstantUnion(const TConstantUnion *safeConstantUnion)
+ {
+ ASSERT(safeConstantUnion);
+ // Previous union pointer freed on pool deallocation.
+ mUnionArrayPointer = safeConstantUnion;
+ }
+
+ TIntermConstantUnion *getAsConstantUnion() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *, TIntermNode *) override { return false; }
+
+ TConstantUnion *foldBinary(TOperator op,
+ TIntermConstantUnion *rightNode,
+ TDiagnostics *diagnostics,
+ const TSourceLoc &line);
+ const TConstantUnion *foldIndexing(int index);
+ TConstantUnion *foldUnaryNonComponentWise(TOperator op);
+ TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics);
+
+ static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate);
+ static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+ TDiagnostics *diagnostics);
+
+ protected:
+ // Same data may be shared between multiple constant unions, so it can't be modified.
+ const TConstantUnion *mUnionArrayPointer;
+
+ private:
+ typedef float(*FloatTypeUnaryFunc) (float);
+ void foldFloatTypeUnary(const TConstantUnion &parameter,
+ FloatTypeUnaryFunc builtinFunc,
+ TConstantUnion *result) const;
+
+ TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private!
+};
+
+//
+// Intermediate class for node types that hold operators.
+//
+class TIntermOperator : public TIntermTyped
+{
+ public:
+ TOperator getOp() const { return mOp; }
+
+ bool isAssignment() const;
+ bool isMultiplication() const;
+ bool isConstructor() const;
+
+ bool hasSideEffects() const override { return isAssignment(); }
+
+ protected:
+ TIntermOperator(TOperator op)
+ : TIntermTyped(TType(EbtFloat, EbpUndefined)),
+ mOp(op) {}
+ TIntermOperator(TOperator op, const TType &type)
+ : TIntermTyped(type),
+ mOp(op) {}
+
+ TIntermOperator(const TIntermOperator &) = default;
+
+ TOperator mOp;
+};
+
+// Node for vector swizzles.
+class TIntermSwizzle : public TIntermTyped
+{
+ public:
+ // This constructor determines the type of the node based on the operand.
+ TIntermSwizzle(TIntermTyped *operand, const TVector<int> &swizzleOffsets);
+
+ TIntermTyped *deepCopy() const override { return new TIntermSwizzle(*this); }
+
+ TIntermSwizzle *getAsSwizzleNode() override { return this; };
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ bool hasSideEffects() const override { return mOperand->hasSideEffects(); }
+
+ TIntermTyped *getOperand() { return mOperand; }
+ void writeOffsetsAsXYZW(TInfoSinkBase *out) const;
+
+ bool hasDuplicateOffsets() const;
+
+ TIntermTyped *fold();
+
+ protected:
+ TIntermTyped *mOperand;
+ TVector<int> mSwizzleOffsets;
+
+ private:
+ void promote();
+
+ TIntermSwizzle(const TIntermSwizzle &node); // Note: not deleted, just private!
+};
+
+//
+// Nodes for all the basic binary math operators.
+//
+class TIntermBinary : public TIntermOperator
+{
+ public:
+ // This constructor determines the type of the binary node based on the operands and op.
+ TIntermBinary(TOperator op, TIntermTyped *left, TIntermTyped *right);
+
+ TIntermTyped *deepCopy() const override { return new TIntermBinary(*this); }
+
+ static TOperator GetMulOpBasedOnOperands(const TType &left, const TType &right);
+ static TOperator GetMulAssignOpBasedOnOperands(const TType &left, const TType &right);
+ static TQualifier GetCommaQualifier(int shaderVersion,
+ const TIntermTyped *left,
+ const TIntermTyped *right);
+
+ TIntermBinary *getAsBinaryNode() override { return this; };
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ bool hasSideEffects() const override
+ {
+ return isAssignment() || mLeft->hasSideEffects() || mRight->hasSideEffects();
+ }
+
+ TIntermTyped *getLeft() const { return mLeft; }
+ TIntermTyped *getRight() const { return mRight; }
+ TIntermTyped *fold(TDiagnostics *diagnostics);
+
+ void setAddIndexClamp() { mAddIndexClamp = true; }
+ bool getAddIndexClamp() { return mAddIndexClamp; }
+
+ protected:
+ TIntermTyped* mLeft;
+ TIntermTyped* mRight;
+
+ // If set to true, wrap any EOpIndexIndirect with a clamp to bounds.
+ bool mAddIndexClamp;
+
+ private:
+ void promote();
+
+ TIntermBinary(const TIntermBinary &node); // Note: not deleted, just private!
+};
+
+//
+// Nodes for unary math operators.
+//
+class TIntermUnary : public TIntermOperator
+{
+ public:
+ TIntermUnary(TOperator op, TIntermTyped *operand);
+
+ TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); }
+
+ void traverse(TIntermTraverser *it) override;
+ TIntermUnary *getAsUnaryNode() override { return this; }
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ bool hasSideEffects() const override { return isAssignment() || mOperand->hasSideEffects(); }
+
+ TIntermTyped *getOperand() { return mOperand; }
+ TIntermTyped *fold(TDiagnostics *diagnostics);
+
+ void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
+ bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
+
+ protected:
+ TIntermTyped *mOperand;
+
+ // If set to true, replace the built-in function call with an emulated one
+ // to work around driver bugs.
+ bool mUseEmulatedFunction;
+
+ private:
+ void promote();
+
+ TIntermUnary(const TIntermUnary &node); // note: not deleted, just private!
+};
+
+class TFunctionSymbolInfo
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TFunctionSymbolInfo() : mId(0) {}
+
+ TFunctionSymbolInfo(const TFunctionSymbolInfo &) = default;
+ TFunctionSymbolInfo &operator=(const TFunctionSymbolInfo &) = default;
+
+ void setFromFunction(const TFunction &function);
+
+ void setNameObj(const TName &name) { mName = name; }
+ const TName &getNameObj() const { return mName; }
+
+ const TString &getName() const { return mName.getString(); }
+ void setName(const TString &name) { mName.setString(name); }
+ bool isMain() const { return mName.getString() == "main("; }
+
+ void setId(int functionId) { mId = functionId; }
+ int getId() const { return mId; }
+ private:
+ TName mName;
+ int mId;
+};
+
+// Node for function definitions.
+class TIntermFunctionDefinition : public TIntermTyped
+{
+ public:
+ // TODO(oetuaho@nvidia.com): See if TFunctionSymbolInfo could be added to constructor
+ // parameters.
+ TIntermFunctionDefinition(const TType &type, TIntermAggregate *parameters, TIntermBlock *body)
+ : TIntermTyped(type), mParameters(parameters), mBody(body)
+ {
+ ASSERT(parameters != nullptr);
+ ASSERT(body != nullptr);
+ }
+
+ TIntermFunctionDefinition *getAsFunctionDefinition() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ TIntermTyped *deepCopy() const override
+ {
+ UNREACHABLE();
+ return nullptr;
+ }
+ bool hasSideEffects() const override
+ {
+ UNREACHABLE();
+ return true;
+ }
+
+ TIntermAggregate *getFunctionParameters() const { return mParameters; }
+ TIntermBlock *getBody() const { return mBody; }
+
+ TFunctionSymbolInfo *getFunctionSymbolInfo() { return &mFunctionInfo; }
+ const TFunctionSymbolInfo *getFunctionSymbolInfo() const { return &mFunctionInfo; }
+
+ private:
+ TIntermAggregate *mParameters;
+ TIntermBlock *mBody;
+
+ TFunctionSymbolInfo mFunctionInfo;
+};
+
+typedef TVector<TIntermNode *> TIntermSequence;
+typedef TVector<int> TQualifierList;
+
+// Interface for node classes that have an arbitrarily sized set of children.
+class TIntermAggregateBase
+{
+ public:
+ virtual ~TIntermAggregateBase() {}
+
+ virtual TIntermSequence *getSequence() = 0;
+ virtual const TIntermSequence *getSequence() const = 0;
+
+ bool replaceChildNodeWithMultiple(TIntermNode *original, const TIntermSequence &replacements);
+ bool insertChildNodes(TIntermSequence::size_type position, const TIntermSequence &insertions);
+
+ protected:
+ TIntermAggregateBase() {}
+
+ bool replaceChildNodeInternal(TIntermNode *original, TIntermNode *replacement);
+};
+
+//
+// Nodes that operate on an arbitrary sized set of children.
+//
+class TIntermAggregate : public TIntermOperator, public TIntermAggregateBase
+{
+ public:
+ TIntermAggregate()
+ : TIntermOperator(EOpNull),
+ mUserDefined(false),
+ mUseEmulatedFunction(false),
+ mGotPrecisionFromChildren(false)
+ {
+ }
+ TIntermAggregate(TOperator op)
+ : TIntermOperator(op),
+ mUserDefined(false),
+ mUseEmulatedFunction(false),
+ mGotPrecisionFromChildren(false)
+ {
+ }
+ ~TIntermAggregate() { }
+
+ // Note: only supported for nodes that can be a part of an expression.
+ TIntermTyped *deepCopy() const override { return new TIntermAggregate(*this); }
+
+ void setOp(TOperator op) { mOp = op; }
+
+ TIntermAggregate *getAsAggregate() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ // Conservatively assume function calls and other aggregate operators have side-effects
+ bool hasSideEffects() const override { return true; }
+ TIntermTyped *fold(TDiagnostics *diagnostics);
+
+ TIntermSequence *getSequence() override { return &mSequence; }
+ const TIntermSequence *getSequence() const override { return &mSequence; }
+
+ void setUserDefined() { mUserDefined = true; }
+ bool isUserDefined() const { return mUserDefined; }
+
+ void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
+ bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
+
+ bool areChildrenConstQualified();
+ void setPrecisionFromChildren();
+ void setBuiltInFunctionPrecision();
+
+ // Returns true if changing parameter precision may affect the return value.
+ bool gotPrecisionFromChildren() const { return mGotPrecisionFromChildren; }
+
+ TFunctionSymbolInfo *getFunctionSymbolInfo() { return &mFunctionInfo; }
+ const TFunctionSymbolInfo *getFunctionSymbolInfo() const { return &mFunctionInfo; }
+
+ protected:
+ TIntermSequence mSequence;
+ bool mUserDefined; // used for user defined function names
+
+ // If set to true, replace the built-in function call with an emulated one
+ // to work around driver bugs.
+ bool mUseEmulatedFunction;
+
+ bool mGotPrecisionFromChildren;
+
+ TFunctionSymbolInfo mFunctionInfo;
+
+ private:
+ TIntermAggregate(const TIntermAggregate &node); // note: not deleted, just private!
+};
+
+// A list of statements. Either the root node which contains declarations and function definitions,
+// or a block that can be marked with curly braces {}.
+class TIntermBlock : public TIntermNode, public TIntermAggregateBase
+{
+ public:
+ TIntermBlock() : TIntermNode() {}
+ ~TIntermBlock() {}
+
+ TIntermBlock *getAsBlock() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ // Only intended for initially building the block.
+ void appendStatement(TIntermNode *statement);
+
+ TIntermSequence *getSequence() override { return &mStatements; }
+ const TIntermSequence *getSequence() const override { return &mStatements; }
+
+ protected:
+ TIntermSequence mStatements;
+};
+
+// Struct, interface block or variable declaration. Can contain multiple variable declarators.
+class TIntermDeclaration : public TIntermNode, public TIntermAggregateBase
+{
+ public:
+ TIntermDeclaration() : TIntermNode() {}
+ ~TIntermDeclaration() {}
+
+ TIntermDeclaration *getAsDeclarationNode() override { return this; }
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ // Only intended for initially building the declaration.
+ // The declarator node should be either TIntermSymbol or TIntermBinary with op set to
+ // EOpInitialize.
+ void appendDeclarator(TIntermTyped *declarator);
+
+ TIntermSequence *getSequence() override { return &mDeclarators; }
+ const TIntermSequence *getSequence() const override { return &mDeclarators; }
+ protected:
+ TIntermSequence mDeclarators;
+};
+
+// For ternary operators like a ? b : c.
+class TIntermTernary : public TIntermTyped
+{
+ public:
+ TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression);
+
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ TIntermTyped *getCondition() const { return mCondition; }
+ TIntermTyped *getTrueExpression() const { return mTrueExpression; }
+ TIntermTyped *getFalseExpression() const { return mFalseExpression; }
+ TIntermTernary *getAsTernaryNode() override { return this; }
+
+ TIntermTyped *deepCopy() const override { return new TIntermTernary(*this); }
+
+ bool hasSideEffects() const override
+ {
+ return mCondition->hasSideEffects() || mTrueExpression->hasSideEffects() ||
+ mFalseExpression->hasSideEffects();
+ }
+
+ static TQualifier DetermineQualifier(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression);
+
+ private:
+ TIntermTernary(const TIntermTernary &node); // Note: not deleted, just private!
+
+ TIntermTyped *mCondition;
+ TIntermTyped *mTrueExpression;
+ TIntermTyped *mFalseExpression;
+};
+
+class TIntermIfElse : public TIntermNode
+{
+ public:
+ TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB)
+ : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
+ {
+ }
+
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+ TIntermTyped *getCondition() const { return mCondition; }
+ TIntermBlock *getTrueBlock() const { return mTrueBlock; }
+ TIntermBlock *getFalseBlock() const { return mFalseBlock; }
+ TIntermIfElse *getAsIfElseNode() override { return this; }
+
+ protected:
+ TIntermTyped *mCondition;
+ TIntermBlock *mTrueBlock;
+ TIntermBlock *mFalseBlock;
+};
+
+//
+// Switch statement.
+//
+class TIntermSwitch : public TIntermNode
+{
+ public:
+ TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList)
+ : TIntermNode(), mInit(init), mStatementList(statementList)
+ {
+ }
+
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement) override;
+
+ TIntermSwitch *getAsSwitchNode() override { return this; }
+
+ TIntermTyped *getInit() { return mInit; }
+ TIntermBlock *getStatementList() { return mStatementList; }
+ void setStatementList(TIntermBlock *statementList) { mStatementList = statementList; }
+
+ protected:
+ TIntermTyped *mInit;
+ TIntermBlock *mStatementList;
+};
+
+//
+// Case label.
+//
+class TIntermCase : public TIntermNode
+{
+ public:
+ TIntermCase(TIntermTyped *condition)
+ : TIntermNode(),
+ mCondition(condition)
+ {
+ }
+
+ void traverse(TIntermTraverser *it) override;
+ bool replaceChildNode(
+ TIntermNode *original, TIntermNode *replacement) override;
+
+ TIntermCase *getAsCaseNode() override { return this; }
+
+ bool hasCondition() const { return mCondition != nullptr; }
+ TIntermTyped *getCondition() const { return mCondition; }
+
+ protected:
+ TIntermTyped *mCondition;
+};
+
+enum Visit
+{
+ PreVisit,
+ InVisit,
+ PostVisit
+};
+
+//
+// For traversing the tree. User should derive from this class overriding the visit functions,
+// and then pass an object of the subclass to a traverse method of a node.
+//
+// The traverse*() functions may also be overridden do other bookkeeping on the tree to provide
+// contextual information to the visit functions, such as whether the node is the target of an
+// assignment.
+//
+// When using this, just fill in the methods for nodes you want visited.
+// Return false from a pre-visit to skip visiting that node's subtree.
+//
+class TIntermTraverser : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TIntermTraverser(bool preVisit, bool inVisit, bool postVisit);
+ virtual ~TIntermTraverser();
+
+ virtual void visitSymbol(TIntermSymbol *node) {}
+ virtual void visitRaw(TIntermRaw *node) {}
+ virtual void visitConstantUnion(TIntermConstantUnion *node) {}
+ virtual bool visitSwizzle(Visit visit, TIntermSwizzle *node) { return true; }
+ virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; }
+ virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; }
+ virtual bool visitTernary(Visit visit, TIntermTernary *node) { return true; }
+ virtual bool visitIfElse(Visit visit, TIntermIfElse *node) { return true; }
+ virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; }
+ virtual bool visitCase(Visit visit, TIntermCase *node) { return true; }
+ virtual bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+ {
+ return true;
+ }
+ virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; }
+ virtual bool visitBlock(Visit visit, TIntermBlock *node) { return true; }
+ virtual bool visitDeclaration(Visit visit, TIntermDeclaration *node) { return true; }
+ virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; }
+ virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; }
+
+ // The traverse functions contain logic for iterating over the children of the node
+ // and calling the visit functions in the appropriate places. They also track some
+ // context that may be used by the visit functions.
+ virtual void traverseSymbol(TIntermSymbol *node);
+ virtual void traverseRaw(TIntermRaw *node);
+ virtual void traverseConstantUnion(TIntermConstantUnion *node);
+ virtual void traverseSwizzle(TIntermSwizzle *node);
+ virtual void traverseBinary(TIntermBinary *node);
+ virtual void traverseUnary(TIntermUnary *node);
+ virtual void traverseTernary(TIntermTernary *node);
+ virtual void traverseIfElse(TIntermIfElse *node);
+ virtual void traverseSwitch(TIntermSwitch *node);
+ virtual void traverseCase(TIntermCase *node);
+ virtual void traverseFunctionDefinition(TIntermFunctionDefinition *node);
+ virtual void traverseAggregate(TIntermAggregate *node);
+ virtual void traverseBlock(TIntermBlock *node);
+ virtual void traverseDeclaration(TIntermDeclaration *node);
+ virtual void traverseLoop(TIntermLoop *node);
+ virtual void traverseBranch(TIntermBranch *node);
+
+ int getMaxDepth() const { return mMaxDepth; }
+
+ // Return the original name if hash function pointer is NULL;
+ // otherwise return the hashed name.
+ static TString hash(const TString &name, ShHashFunction64 hashFunction);
+
+ // If traversers need to replace nodes, they can add the replacements in
+ // mReplacements/mMultiReplacements during traversal and the user of the traverser should call
+ // this function after traversal to perform them.
+ void updateTree();
+
+ // Start creating temporary symbols from the given temporary symbol index + 1.
+ void useTemporaryIndex(unsigned int *temporaryIndex);
+
+ protected:
+ void incrementDepth(TIntermNode *current)
+ {
+ mDepth++;
+ mMaxDepth = std::max(mMaxDepth, mDepth);
+ mPath.push_back(current);
+ }
+
+ void decrementDepth()
+ {
+ mDepth--;
+ mPath.pop_back();
+ }
+
+ TIntermNode *getParentNode()
+ {
+ return mPath.size() == 0 ? NULL : mPath.back();
+ }
+
+ // Return the nth ancestor of the node being traversed. getAncestorNode(0) == getParentNode()
+ TIntermNode *getAncestorNode(unsigned int n)
+ {
+ if (mPath.size() > n)
+ {
+ return mPath[mPath.size() - n - 1u];
+ }
+ return nullptr;
+ }
+
+ void pushParentBlock(TIntermBlock *node);
+ void incrementParentBlockPos();
+ void popParentBlock();
+
+ bool parentNodeIsBlock()
+ {
+ return !mParentBlockStack.empty() && getParentNode() == mParentBlockStack.back().node;
+ }
+
+ // To replace a single node with multiple nodes on the parent aggregate node
+ struct NodeReplaceWithMultipleEntry
+ {
+ NodeReplaceWithMultipleEntry(TIntermAggregateBase *_parent,
+ TIntermNode *_original,
+ TIntermSequence _replacements)
+ : parent(_parent), original(_original), replacements(_replacements)
+ {
+ }
+
+ TIntermAggregateBase *parent;
+ TIntermNode *original;
+ TIntermSequence replacements;
+ };
+
+ // To insert multiple nodes on the parent aggregate node
+ struct NodeInsertMultipleEntry
+ {
+ NodeInsertMultipleEntry(TIntermBlock *_parent,
+ TIntermSequence::size_type _position,
+ TIntermSequence _insertionsBefore,
+ TIntermSequence _insertionsAfter)
+ : parent(_parent),
+ position(_position),
+ insertionsBefore(_insertionsBefore),
+ insertionsAfter(_insertionsAfter)
+ {
+ }
+
+ TIntermBlock *parent;
+ TIntermSequence::size_type position;
+ TIntermSequence insertionsBefore;
+ TIntermSequence insertionsAfter;
+ };
+
+ // Helper to insert statements in the parent block (sequence) of the node currently being traversed.
+ // The statements will be inserted before the node being traversed once updateTree is called.
+ // Should only be called during PreVisit or PostVisit from sequence nodes.
+ // Note that inserting more than one set of nodes to the same parent node on a single updateTree call is not
+ // supported.
+ void insertStatementsInParentBlock(const TIntermSequence &insertions);
+
+ // Same as above, but supports simultaneous insertion of statements before and after the node
+ // currently being traversed.
+ void insertStatementsInParentBlock(const TIntermSequence &insertionsBefore,
+ const TIntermSequence &insertionsAfter);
+
+ // Helper to insert a single statement.
+ void insertStatementInParentBlock(TIntermNode *statement);
+
+ // Helper to create a temporary symbol node with the given qualifier.
+ TIntermSymbol *createTempSymbol(const TType &type, TQualifier qualifier);
+ // Helper to create a temporary symbol node.
+ TIntermSymbol *createTempSymbol(const TType &type);
+ // Create a node that declares but doesn't initialize a temporary symbol.
+ TIntermDeclaration *createTempDeclaration(const TType &type);
+ // Create a node that initializes the current temporary symbol with initializer having the given qualifier.
+ TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier);
+ // Create a node that initializes the current temporary symbol with initializer.
+ TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer);
+ // Create a node that assigns rightNode to the current temporary symbol.
+ TIntermBinary *createTempAssignment(TIntermTyped *rightNode);
+ // Increment temporary symbol index.
+ void nextTemporaryIndex();
+
+ enum class OriginalNode
+ {
+ BECOMES_CHILD,
+ IS_DROPPED
+ };
+
+ void clearReplacementQueue();
+ void queueReplacement(TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus);
+ void queueReplacementWithParent(TIntermNode *parent,
+ TIntermNode *original,
+ TIntermNode *replacement,
+ OriginalNode originalStatus);
+
+ const bool preVisit;
+ const bool inVisit;
+ const bool postVisit;
+
+ int mDepth;
+ int mMaxDepth;
+
+ // All the nodes from root to the current node's parent during traversing.
+ TVector<TIntermNode *> mPath;
+
+ bool mInGlobalScope;
+
+ // During traversing, save all the changes that need to happen into
+ // mReplacements/mMultiReplacements, then do them by calling updateTree().
+ // Multi replacements are processed after single replacements.
+ std::vector<NodeReplaceWithMultipleEntry> mMultiReplacements;
+ std::vector<NodeInsertMultipleEntry> mInsertions;
+
+ private:
+ // To replace a single node with another on the parent node
+ struct NodeUpdateEntry
+ {
+ NodeUpdateEntry(TIntermNode *_parent,
+ TIntermNode *_original,
+ TIntermNode *_replacement,
+ bool _originalBecomesChildOfReplacement)
+ : parent(_parent),
+ original(_original),
+ replacement(_replacement),
+ originalBecomesChildOfReplacement(_originalBecomesChildOfReplacement)
+ {
+ }
+
+ TIntermNode *parent;
+ TIntermNode *original;
+ TIntermNode *replacement;
+ bool originalBecomesChildOfReplacement;
+ };
+
+ struct ParentBlock
+ {
+ ParentBlock(TIntermBlock *nodeIn, TIntermSequence::size_type posIn)
+ : node(nodeIn), pos(posIn)
+ {
+ }
+
+ TIntermBlock *node;
+ TIntermSequence::size_type pos;
+ };
+
+ std::vector<NodeUpdateEntry> mReplacements;
+
+ // All the code blocks from the root to the current node's parent during traversal.
+ std::vector<ParentBlock> mParentBlockStack;
+
+ unsigned int *mTemporaryIndex;
+};
+
+// Traverser parent class that tracks where a node is a destination of a write operation and so is
+// required to be an l-value.
+class TLValueTrackingTraverser : public TIntermTraverser
+{
+ public:
+ TLValueTrackingTraverser(bool preVisit,
+ bool inVisit,
+ bool postVisit,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+ : TIntermTraverser(preVisit, inVisit, postVisit),
+ mOperatorRequiresLValue(false),
+ mInFunctionCallOutParameter(false),
+ mSymbolTable(symbolTable),
+ mShaderVersion(shaderVersion)
+ {
+ }
+ virtual ~TLValueTrackingTraverser() {}
+
+ void traverseBinary(TIntermBinary *node) final;
+ void traverseUnary(TIntermUnary *node) final;
+ void traverseFunctionDefinition(TIntermFunctionDefinition *node) final;
+ void traverseAggregate(TIntermAggregate *node) final;
+
+ protected:
+ bool isLValueRequiredHere() const
+ {
+ return mOperatorRequiresLValue || mInFunctionCallOutParameter;
+ }
+
+ // Return true if the prototype or definition of the function being called has been encountered
+ // during traversal.
+ bool isInFunctionMap(const TIntermAggregate *callNode) const;
+
+ private:
+ // Track whether an l-value is required in the node that is currently being traversed by the
+ // surrounding operator.
+ // Use isLValueRequiredHere to check all conditions which require an l-value.
+ void setOperatorRequiresLValue(bool lValueRequired)
+ {
+ mOperatorRequiresLValue = lValueRequired;
+ }
+ bool operatorRequiresLValue() const { return mOperatorRequiresLValue; }
+
+ // Add a function encountered during traversal to the function map.
+ void addToFunctionMap(const TName &name, TIntermSequence *paramSequence);
+
+ // Return the parameters sequence from the function definition or prototype.
+ TIntermSequence *getFunctionParameters(const TIntermAggregate *callNode);
+
+ // Track whether an l-value is required inside a function call.
+ void setInFunctionCallOutParameter(bool inOutParameter);
+ bool isInFunctionCallOutParameter() const;
+
+ bool mOperatorRequiresLValue;
+ bool mInFunctionCallOutParameter;
+
+ struct TNameComparator
+ {
+ bool operator()(const TName &a, const TName &b) const
+ {
+ int compareResult = a.getString().compare(b.getString());
+ if (compareResult != 0)
+ return compareResult < 0;
+ // Internal functions may have same names as non-internal functions.
+ return !a.isInternal() && b.isInternal();
+ }
+ };
+
+ // Map from mangled function names to their parameter sequences
+ TMap<TName, TIntermSequence *, TNameComparator> mFunctionMap;
+
+ const TSymbolTable &mSymbolTable;
+ const int mShaderVersion;
+};
+
+//
+// For traversing the tree, and computing max depth.
+// Takes a maximum depth limit to prevent stack overflow.
+//
+class TMaxDepthTraverser : public TIntermTraverser
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TMaxDepthTraverser(int depthLimit)
+ : TIntermTraverser(true, true, false),
+ mDepthLimit(depthLimit) { }
+
+ bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); }
+ bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); }
+ bool visitTernary(Visit, TIntermTernary *) override { return depthCheck(); }
+ bool visitIfElse(Visit, TIntermIfElse *) override { return depthCheck(); }
+ bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); }
+ bool visitBlock(Visit, TIntermBlock *) override { return depthCheck(); }
+ bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); }
+ bool visitBranch(Visit, TIntermBranch *) override { return depthCheck(); }
+
+ protected:
+ bool depthCheck() const { return mMaxDepth < mDepthLimit; }
+
+ int mDepthLimit;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INTERMNODE_H_
diff --git a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
new file mode 100755
index 000000000..dd2054f68
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNodePatternMatcher is a helper class for matching node trees to given patterns.
+// It can be used whenever the same checks for certain node structures are common to multiple AST
+// traversers.
+//
+
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mMask(mask)
+{
+}
+
+// static
+bool IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node)
+{
+ return node->getOp() == EOpIndexIndirect && !node->getLeft()->isArray() &&
+ node->getLeft()->getBasicType() != EbtStruct;
+}
+
+bool IntermNodePatternMatcher::matchInternal(TIntermBinary *node, TIntermNode *parentNode)
+{
+ if ((mMask & kExpressionReturningArray) != 0)
+ {
+ if (node->isArray() && node->getOp() == EOpAssign && parentNode != nullptr &&
+ !parentNode->getAsBlock())
+ {
+ return true;
+ }
+ }
+
+ if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+ {
+ if (node->getRight()->hasSideEffects() &&
+ (node->getOp() == EOpLogicalOr || node->getOp() == EOpLogicalAnd))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNode)
+{
+ // L-value tracking information is needed to check for dynamic indexing in L-value.
+ // Traversers that don't track l-values can still use this class and match binary nodes with
+ // this variation of this method if they don't need to check for dynamic indexing in l-values.
+ ASSERT((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) == 0);
+ return matchInternal(node, parentNode);
+}
+
+bool IntermNodePatternMatcher::match(TIntermBinary *node,
+ TIntermNode *parentNode,
+ bool isLValueRequiredHere)
+{
+ if (matchInternal(node, parentNode))
+ {
+ return true;
+ }
+ if ((mMask & kDynamicIndexingOfVectorOrMatrixInLValue) != 0)
+ {
+ if (isLValueRequiredHere && IsDynamicIndexingOfVectorOrMatrix(node))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode)
+{
+ if ((mMask & kExpressionReturningArray) != 0)
+ {
+ if (parentNode != nullptr)
+ {
+ TIntermBinary *parentBinary = parentNode->getAsBinaryNode();
+ bool parentIsAssignment =
+ (parentBinary != nullptr &&
+ (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
+
+ if (node->getType().isArray() && !parentIsAssignment &&
+ (node->isConstructor() || node->getOp() == EOpFunctionCall) &&
+ !parentNode->getAsBlock())
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermTernary *node)
+{
+ if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+ {
+ return true;
+ }
+ return false;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
new file mode 100755
index 000000000..10f9657ed
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/IntermNodePatternMatcher.h
@@ -0,0 +1,58 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNodePatternMatcher is a helper class for matching node trees to given patterns.
+// It can be used whenever the same checks for certain node structures are common to multiple AST
+// traversers.
+//
+
+#ifndef COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
+#define COMPILER_TRANSLATOR_INTERMNODEPATTERNMATCHER_H_
+
+namespace sh
+{
+
+class TIntermAggregate;
+class TIntermBinary;
+class TIntermNode;
+class TIntermTernary;
+
+class IntermNodePatternMatcher
+{
+ public:
+ static bool IsDynamicIndexingOfVectorOrMatrix(TIntermBinary *node);
+
+ enum PatternType
+ {
+ // Matches expressions that are unfolded to if statements by UnfoldShortCircuitToIf
+ kUnfoldedShortCircuitExpression = 0x0001,
+
+ // Matches expressions that return arrays with the exception of simple statements where a
+ // constructor or function call result is assigned.
+ kExpressionReturningArray = 0x0002,
+
+ // Matches dynamic indexing of vectors or matrices in l-values.
+ kDynamicIndexingOfVectorOrMatrixInLValue = 0x0004
+ };
+ IntermNodePatternMatcher(const unsigned int mask);
+
+ bool match(TIntermBinary *node, TIntermNode *parentNode);
+
+ // Use this version for checking binary node matches in case you're using flag
+ // kDynamicIndexingOfVectorOrMatrixInLValue.
+ bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere);
+
+ bool match(TIntermAggregate *node, TIntermNode *parentNode);
+ bool match(TIntermTernary *node);
+
+ private:
+ const unsigned int mMask;
+
+ bool matchInternal(TIntermBinary *node, TIntermNode *parentNode);
+};
+
+} // namespace sh
+
+#endif
diff --git a/gfx/angle/src/compiler/translator/IntermTraverse.cpp b/gfx/angle/src/compiler/translator/IntermTraverse.cpp
new file mode 100755
index 000000000..7f91595d4
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/IntermTraverse.cpp
@@ -0,0 +1,838 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+void TIntermSymbol::traverse(TIntermTraverser *it)
+{
+ it->traverseSymbol(this);
+}
+
+void TIntermRaw::traverse(TIntermTraverser *it)
+{
+ it->traverseRaw(this);
+}
+
+void TIntermConstantUnion::traverse(TIntermTraverser *it)
+{
+ it->traverseConstantUnion(this);
+}
+
+void TIntermSwizzle::traverse(TIntermTraverser *it)
+{
+ it->traverseSwizzle(this);
+}
+
+void TIntermBinary::traverse(TIntermTraverser *it)
+{
+ it->traverseBinary(this);
+}
+
+void TIntermUnary::traverse(TIntermTraverser *it)
+{
+ it->traverseUnary(this);
+}
+
+void TIntermTernary::traverse(TIntermTraverser *it)
+{
+ it->traverseTernary(this);
+}
+
+void TIntermIfElse::traverse(TIntermTraverser *it)
+{
+ it->traverseIfElse(this);
+}
+
+void TIntermSwitch::traverse(TIntermTraverser *it)
+{
+ it->traverseSwitch(this);
+}
+
+void TIntermCase::traverse(TIntermTraverser *it)
+{
+ it->traverseCase(this);
+}
+
+void TIntermFunctionDefinition::traverse(TIntermTraverser *it)
+{
+ it->traverseFunctionDefinition(this);
+}
+
+void TIntermBlock::traverse(TIntermTraverser *it)
+{
+ it->traverseBlock(this);
+}
+
+void TIntermDeclaration::traverse(TIntermTraverser *it)
+{
+ it->traverseDeclaration(this);
+}
+
+void TIntermAggregate::traverse(TIntermTraverser *it)
+{
+ it->traverseAggregate(this);
+}
+
+void TIntermLoop::traverse(TIntermTraverser *it)
+{
+ it->traverseLoop(this);
+}
+
+void TIntermBranch::traverse(TIntermTraverser *it)
+{
+ it->traverseBranch(this);
+}
+
+TIntermTraverser::TIntermTraverser(bool preVisit, bool inVisit, bool postVisit)
+ : preVisit(preVisit),
+ inVisit(inVisit),
+ postVisit(postVisit),
+ mDepth(0),
+ mMaxDepth(0),
+ mInGlobalScope(true),
+ mTemporaryIndex(nullptr)
+{
+}
+
+TIntermTraverser::~TIntermTraverser()
+{
+}
+
+void TIntermTraverser::pushParentBlock(TIntermBlock *node)
+{
+ mParentBlockStack.push_back(ParentBlock(node, 0));
+}
+
+void TIntermTraverser::incrementParentBlockPos()
+{
+ ++mParentBlockStack.back().pos;
+}
+
+void TIntermTraverser::popParentBlock()
+{
+ ASSERT(!mParentBlockStack.empty());
+ mParentBlockStack.pop_back();
+}
+
+void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertions)
+{
+ TIntermSequence emptyInsertionsAfter;
+ insertStatementsInParentBlock(insertions, emptyInsertionsAfter);
+}
+
+void TIntermTraverser::insertStatementsInParentBlock(const TIntermSequence &insertionsBefore,
+ const TIntermSequence &insertionsAfter)
+{
+ ASSERT(!mParentBlockStack.empty());
+ NodeInsertMultipleEntry insert(mParentBlockStack.back().node, mParentBlockStack.back().pos,
+ insertionsBefore, insertionsAfter);
+ mInsertions.push_back(insert);
+}
+
+void TIntermTraverser::insertStatementInParentBlock(TIntermNode *statement)
+{
+ TIntermSequence insertions;
+ insertions.push_back(statement);
+ insertStatementsInParentBlock(insertions);
+}
+
+TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier)
+{
+ // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal.
+ TInfoSinkBase symbolNameOut;
+ ASSERT(mTemporaryIndex != nullptr);
+ symbolNameOut << "s" << (*mTemporaryIndex);
+ TString symbolName = symbolNameOut.c_str();
+
+ TIntermSymbol *node = new TIntermSymbol(0, symbolName, type);
+ node->setInternal(true);
+
+ ASSERT(qualifier == EvqTemporary || qualifier == EvqConst || qualifier == EvqGlobal);
+ node->getTypePointer()->setQualifier(qualifier);
+ // TODO(oetuaho): Might be useful to sanitize layout qualifier etc. on the type of the created
+ // symbol. This might need to be done in other places as well.
+ return node;
+}
+
+TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type)
+{
+ return createTempSymbol(type, EvqTemporary);
+}
+
+TIntermDeclaration *TIntermTraverser::createTempDeclaration(const TType &type)
+{
+ TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
+ tempDeclaration->appendDeclarator(createTempSymbol(type));
+ return tempDeclaration;
+}
+
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer,
+ TQualifier qualifier)
+{
+ ASSERT(initializer != nullptr);
+ TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier);
+ TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
+ TIntermBinary *tempInit = new TIntermBinary(EOpInitialize, tempSymbol, initializer);
+ tempDeclaration->appendDeclarator(tempInit);
+ return tempDeclaration;
+}
+
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
+{
+ return createTempInitDeclaration(initializer, EvqTemporary);
+}
+
+TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode)
+{
+ ASSERT(rightNode != nullptr);
+ TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType());
+ TIntermBinary *assignment = new TIntermBinary(EOpAssign, tempSymbol, rightNode);
+ return assignment;
+}
+
+void TIntermTraverser::useTemporaryIndex(unsigned int *temporaryIndex)
+{
+ mTemporaryIndex = temporaryIndex;
+}
+
+void TIntermTraverser::nextTemporaryIndex()
+{
+ ASSERT(mTemporaryIndex != nullptr);
+ ++(*mTemporaryIndex);
+}
+
+void TLValueTrackingTraverser::addToFunctionMap(const TName &name, TIntermSequence *paramSequence)
+{
+ mFunctionMap[name] = paramSequence;
+}
+
+bool TLValueTrackingTraverser::isInFunctionMap(const TIntermAggregate *callNode) const
+{
+ ASSERT(callNode->getOp() == EOpFunctionCall);
+ return (mFunctionMap.find(callNode->getFunctionSymbolInfo()->getNameObj()) !=
+ mFunctionMap.end());
+}
+
+TIntermSequence *TLValueTrackingTraverser::getFunctionParameters(const TIntermAggregate *callNode)
+{
+ ASSERT(isInFunctionMap(callNode));
+ return mFunctionMap[callNode->getFunctionSymbolInfo()->getNameObj()];
+}
+
+void TLValueTrackingTraverser::setInFunctionCallOutParameter(bool inOutParameter)
+{
+ mInFunctionCallOutParameter = inOutParameter;
+}
+
+bool TLValueTrackingTraverser::isInFunctionCallOutParameter() const
+{
+ return mInFunctionCallOutParameter;
+}
+
+//
+// Traverse the intermediate representation tree, and
+// call a node type specific function for each node.
+// Done recursively through the member function Traverse().
+// Node types can be skipped if their function to call is 0,
+// but their subtree will still be traversed.
+// Nodes with children can have their whole subtree skipped
+// if preVisit is turned on and the type specific function
+// returns false.
+//
+
+//
+// Traversal functions for terminals are straighforward....
+//
+void TIntermTraverser::traverseSymbol(TIntermSymbol *node)
+{
+ visitSymbol(node);
+}
+
+void TIntermTraverser::traverseConstantUnion(TIntermConstantUnion *node)
+{
+ visitConstantUnion(node);
+}
+
+void TIntermTraverser::traverseSwizzle(TIntermSwizzle *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitSwizzle(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ node->getOperand()->traverse(this);
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitSwizzle(PostVisit, node);
+}
+
+//
+// Traverse a binary node.
+//
+void TIntermTraverser::traverseBinary(TIntermBinary *node)
+{
+ bool visit = true;
+
+ //
+ // visit the node before children if pre-visiting.
+ //
+ if (preVisit)
+ visit = visitBinary(PreVisit, node);
+
+ //
+ // Visit the children, in the right order.
+ //
+ if (visit)
+ {
+ incrementDepth(node);
+
+ if (node->getLeft())
+ node->getLeft()->traverse(this);
+
+ if (inVisit)
+ visit = visitBinary(InVisit, node);
+
+ if (visit && node->getRight())
+ node->getRight()->traverse(this);
+
+ decrementDepth();
+ }
+
+ //
+ // Visit the node after the children, if requested and the traversal
+ // hasn't been cancelled yet.
+ //
+ if (visit && postVisit)
+ visitBinary(PostVisit, node);
+}
+
+void TLValueTrackingTraverser::traverseBinary(TIntermBinary *node)
+{
+ bool visit = true;
+
+ //
+ // visit the node before children if pre-visiting.
+ //
+ if (preVisit)
+ visit = visitBinary(PreVisit, node);
+
+ //
+ // Visit the children, in the right order.
+ //
+ if (visit)
+ {
+ incrementDepth(node);
+
+ // Some binary operations like indexing can be inside an expression which must be an
+ // l-value.
+ bool parentOperatorRequiresLValue = operatorRequiresLValue();
+ bool parentInFunctionCallOutParameter = isInFunctionCallOutParameter();
+ if (node->isAssignment())
+ {
+ ASSERT(!isLValueRequiredHere());
+ setOperatorRequiresLValue(true);
+ }
+
+ if (node->getLeft())
+ node->getLeft()->traverse(this);
+
+ if (inVisit)
+ visit = visitBinary(InVisit, node);
+
+ if (node->isAssignment())
+ setOperatorRequiresLValue(false);
+
+ // Index is not required to be an l-value even when the surrounding expression is required
+ // to be an l-value.
+ TOperator op = node->getOp();
+ if (op == EOpIndexDirect || op == EOpIndexDirectInterfaceBlock ||
+ op == EOpIndexDirectStruct || op == EOpIndexIndirect)
+ {
+ setOperatorRequiresLValue(false);
+ setInFunctionCallOutParameter(false);
+ }
+
+ if (visit && node->getRight())
+ node->getRight()->traverse(this);
+
+ setOperatorRequiresLValue(parentOperatorRequiresLValue);
+ setInFunctionCallOutParameter(parentInFunctionCallOutParameter);
+
+ decrementDepth();
+ }
+
+ //
+ // Visit the node after the children, if requested and the traversal
+ // hasn't been cancelled yet.
+ //
+ if (visit && postVisit)
+ visitBinary(PostVisit, node);
+}
+
+//
+// Traverse a unary node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseUnary(TIntermUnary *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitUnary(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ node->getOperand()->traverse(this);
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitUnary(PostVisit, node);
+}
+
+void TLValueTrackingTraverser::traverseUnary(TIntermUnary *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitUnary(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ ASSERT(!operatorRequiresLValue());
+ switch (node->getOp())
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ setOperatorRequiresLValue(true);
+ break;
+ default:
+ break;
+ }
+
+ node->getOperand()->traverse(this);
+
+ setOperatorRequiresLValue(false);
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitUnary(PostVisit, node);
+}
+
+// Traverse a function definition node.
+void TIntermTraverser::traverseFunctionDefinition(TIntermFunctionDefinition *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitFunctionDefinition(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+ mInGlobalScope = false;
+
+ node->getFunctionParameters()->traverse(this);
+ if (inVisit)
+ visit = visitFunctionDefinition(InVisit, node);
+ node->getBody()->traverse(this);
+
+ mInGlobalScope = true;
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitFunctionDefinition(PostVisit, node);
+}
+
+// Traverse a block node.
+void TIntermTraverser::traverseBlock(TIntermBlock *node)
+{
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitBlock(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+ pushParentBlock(node);
+
+ for (auto *child : *sequence)
+ {
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitBlock(InVisit, node);
+ }
+
+ incrementParentBlockPos();
+ }
+
+ popParentBlock();
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitBlock(PostVisit, node);
+}
+
+// Traverse a declaration node.
+void TIntermTraverser::traverseDeclaration(TIntermDeclaration *node)
+{
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitDeclaration(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ for (auto *child : *sequence)
+ {
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitDeclaration(InVisit, node);
+ }
+ }
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitDeclaration(PostVisit, node);
+}
+
+// Traverse an aggregate node. Same comments in binary node apply here.
+void TIntermTraverser::traverseAggregate(TIntermAggregate *node)
+{
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+
+ if (preVisit)
+ visit = visitAggregate(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ for (auto *child : *sequence)
+ {
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
+ }
+ }
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitAggregate(PostVisit, node);
+}
+
+void TLValueTrackingTraverser::traverseFunctionDefinition(TIntermFunctionDefinition *node)
+{
+ TIntermAggregate *params = node->getFunctionParameters();
+ ASSERT(params != nullptr);
+ ASSERT(params->getOp() == EOpParameters);
+ addToFunctionMap(node->getFunctionSymbolInfo()->getNameObj(), params->getSequence());
+
+ TIntermTraverser::traverseFunctionDefinition(node);
+}
+
+void TLValueTrackingTraverser::traverseAggregate(TIntermAggregate *node)
+{
+ bool visit = true;
+
+ TIntermSequence *sequence = node->getSequence();
+ if (node->getOp() == EOpPrototype)
+ {
+ addToFunctionMap(node->getFunctionSymbolInfo()->getNameObj(), sequence);
+ }
+
+ if (preVisit)
+ visit = visitAggregate(PreVisit, node);
+
+ if (visit)
+ {
+ bool inFunctionMap = false;
+ if (node->getOp() == EOpFunctionCall)
+ {
+ inFunctionMap = isInFunctionMap(node);
+ if (!inFunctionMap)
+ {
+ // The function is not user-defined - it is likely built-in texture function.
+ // Assume that those do not have out parameters.
+ setInFunctionCallOutParameter(false);
+ }
+ }
+
+ incrementDepth(node);
+
+ if (inFunctionMap)
+ {
+ TIntermSequence *params = getFunctionParameters(node);
+ TIntermSequence::iterator paramIter = params->begin();
+ for (auto *child : *sequence)
+ {
+ ASSERT(paramIter != params->end());
+ TQualifier qualifier = (*paramIter)->getAsTyped()->getQualifier();
+ setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
+
+ child->traverse(this);
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
+ }
+
+ ++paramIter;
+ }
+
+ setInFunctionCallOutParameter(false);
+ }
+ else
+ {
+ // Find the built-in function corresponding to this op so that we can determine the
+ // in/out qualifiers of its parameters.
+ TFunction *builtInFunc = nullptr;
+ TString opString = GetOperatorString(node->getOp());
+ if (!node->isConstructor() && !opString.empty())
+ {
+ // The return type doesn't affect the mangled name of the function, which is used
+ // to look it up from the symbol table.
+ TType dummyReturnType;
+ TFunction call(&opString, &dummyReturnType, node->getOp());
+ for (auto *child : *sequence)
+ {
+ TType *paramType = child->getAsTyped()->getTypePointer();
+ TConstParameter p(paramType);
+ call.addParameter(p);
+ }
+
+ TSymbol *sym = mSymbolTable.findBuiltIn(call.getMangledName(), mShaderVersion);
+ if (sym != nullptr && sym->isFunction())
+ {
+ builtInFunc = static_cast<TFunction *>(sym);
+ ASSERT(builtInFunc->getParamCount() == sequence->size());
+ }
+ }
+
+ size_t paramIndex = 0;
+
+ for (auto *child : *sequence)
+ {
+ TQualifier qualifier = EvqIn;
+ if (builtInFunc != nullptr)
+ qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
+ setInFunctionCallOutParameter(qualifier == EvqOut || qualifier == EvqInOut);
+ child->traverse(this);
+
+ if (visit && inVisit)
+ {
+ if (child != sequence->back())
+ visit = visitAggregate(InVisit, node);
+ }
+
+ ++paramIndex;
+ }
+
+ setInFunctionCallOutParameter(false);
+ }
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitAggregate(PostVisit, node);
+}
+
+//
+// Traverse a ternary node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseTernary(TIntermTernary *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitTernary(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+ node->getCondition()->traverse(this);
+ if (node->getTrueExpression())
+ node->getTrueExpression()->traverse(this);
+ if (node->getFalseExpression())
+ node->getFalseExpression()->traverse(this);
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitTernary(PostVisit, node);
+}
+
+// Traverse an if-else node. Same comments in binary node apply here.
+void TIntermTraverser::traverseIfElse(TIntermIfElse *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitIfElse(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+ node->getCondition()->traverse(this);
+ if (node->getTrueBlock())
+ node->getTrueBlock()->traverse(this);
+ if (node->getFalseBlock())
+ node->getFalseBlock()->traverse(this);
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitIfElse(PostVisit, node);
+}
+
+//
+// Traverse a switch node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseSwitch(TIntermSwitch *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitSwitch(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+ node->getInit()->traverse(this);
+ if (inVisit)
+ visit = visitSwitch(InVisit, node);
+ if (visit && node->getStatementList())
+ node->getStatementList()->traverse(this);
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitSwitch(PostVisit, node);
+}
+
+//
+// Traverse a case node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseCase(TIntermCase *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitCase(PreVisit, node);
+
+ if (visit && node->getCondition())
+ {
+ incrementDepth(node);
+ node->getCondition()->traverse(this);
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitCase(PostVisit, node);
+}
+
+//
+// Traverse a loop node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseLoop(TIntermLoop *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitLoop(PreVisit, node);
+
+ if (visit)
+ {
+ incrementDepth(node);
+
+ if (node->getInit())
+ node->getInit()->traverse(this);
+
+ if (node->getCondition())
+ node->getCondition()->traverse(this);
+
+ if (node->getBody())
+ node->getBody()->traverse(this);
+
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitLoop(PostVisit, node);
+}
+
+//
+// Traverse a branch node. Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseBranch(TIntermBranch *node)
+{
+ bool visit = true;
+
+ if (preVisit)
+ visit = visitBranch(PreVisit, node);
+
+ if (visit && node->getExpression())
+ {
+ incrementDepth(node);
+ node->getExpression()->traverse(this);
+ decrementDepth();
+ }
+
+ if (visit && postVisit)
+ visitBranch(PostVisit, node);
+}
+
+void TIntermTraverser::traverseRaw(TIntermRaw *node)
+{
+ visitRaw(node);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Intermediate.cpp b/gfx/angle/src/compiler/translator/Intermediate.cpp
new file mode 100755
index 000000000..9e3e455ae
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Intermediate.cpp
@@ -0,0 +1,387 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Build the intermediate representation.
+//
+
+#include <float.h>
+#include <limits.h>
+#include <algorithm>
+
+#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+////////////////////////////////////////////////////////////////////////////
+//
+// First set of functions are to help build the intermediate representation.
+// These functions are not member functions of the nodes.
+// They are called from parser productions.
+//
+/////////////////////////////////////////////////////////////////////////////
+
+//
+// Add a terminal node for an identifier in an expression.
+//
+// Returns the added node.
+//
+TIntermSymbol *TIntermediate::addSymbol(
+ int id, const TString &name, const TType &type, const TSourceLoc &line)
+{
+ TIntermSymbol *node = new TIntermSymbol(id, name, type);
+ node->setLine(line);
+
+ return node;
+}
+
+//
+// Connect two nodes through an index operator, where the left node is the base
+// of an array or struct, and the right node is a direct or indirect offset.
+//
+// Returns the added node.
+// The caller should set the type of the returned node.
+//
+TIntermTyped *TIntermediate::addIndex(TOperator op,
+ TIntermTyped *base,
+ TIntermTyped *index,
+ const TSourceLoc &line,
+ TDiagnostics *diagnostics)
+{
+ TIntermBinary *node = new TIntermBinary(op, base, index);
+ node->setLine(line);
+
+ TIntermTyped *folded = node->fold(diagnostics);
+ if (folded)
+ {
+ return folded;
+ }
+
+ return node;
+}
+
+// This is the safe way to change the operator on an aggregate, as it
+// does lots of error checking and fixing. Especially for establishing
+// a function call's operation on it's set of parameters.
+//
+// Returns an aggregate node, which could be the one passed in if
+// it was already an aggregate but no operator was set.
+TIntermAggregate *TIntermediate::setAggregateOperator(
+ TIntermNode *node, TOperator op, const TSourceLoc &line)
+{
+ TIntermAggregate *aggNode;
+
+ //
+ // Make sure we have an aggregate. If not turn it into one.
+ //
+ if (node)
+ {
+ aggNode = node->getAsAggregate();
+ if (aggNode == NULL || aggNode->getOp() != EOpNull)
+ {
+ //
+ // Make an aggregate containing this node.
+ //
+ aggNode = new TIntermAggregate();
+ aggNode->getSequence()->push_back(node);
+ }
+ }
+ else
+ {
+ aggNode = new TIntermAggregate();
+ }
+
+ //
+ // Set the operator.
+ //
+ aggNode->setOp(op);
+ aggNode->setLine(line);
+
+ return aggNode;
+}
+
+//
+// Safe way to combine two nodes into an aggregate. Works with null pointers,
+// a node that's not a aggregate yet, etc.
+//
+// Returns the resulting aggregate, unless 0 was passed in for
+// both existing nodes.
+//
+TIntermAggregate *TIntermediate::growAggregate(
+ TIntermNode *left, TIntermNode *right, const TSourceLoc &line)
+{
+ if (left == NULL && right == NULL)
+ return NULL;
+
+ TIntermAggregate *aggNode = NULL;
+ if (left)
+ aggNode = left->getAsAggregate();
+ if (!aggNode || aggNode->getOp() != EOpNull)
+ {
+ aggNode = new TIntermAggregate;
+ if (left)
+ aggNode->getSequence()->push_back(left);
+ }
+
+ if (right)
+ aggNode->getSequence()->push_back(right);
+
+ aggNode->setLine(line);
+
+ return aggNode;
+}
+
+//
+// Turn an existing node into an aggregate.
+//
+// Returns an aggregate, unless NULL was passed in for the existing node.
+//
+TIntermAggregate *TIntermediate::MakeAggregate(TIntermNode *node, const TSourceLoc &line)
+{
+ if (node == nullptr)
+ return nullptr;
+
+ TIntermAggregate *aggNode = new TIntermAggregate;
+ aggNode->getSequence()->push_back(node);
+
+ aggNode->setLine(line);
+
+ return aggNode;
+}
+
+// If the input node is nullptr, return nullptr.
+// If the input node is a block node, return it.
+// If the input node is not a block node, put it inside a block node and return that.
+TIntermBlock *TIntermediate::EnsureBlock(TIntermNode *node)
+{
+ if (node == nullptr)
+ return nullptr;
+ TIntermBlock *blockNode = node->getAsBlock();
+ if (blockNode != nullptr)
+ return blockNode;
+
+ blockNode = new TIntermBlock();
+ blockNode->setLine(node->getLine());
+ blockNode->getSequence()->push_back(node);
+ return blockNode;
+}
+
+// For "if" test nodes. There are three children; a condition,
+// a true path, and a false path. The two paths are in the
+// nodePair.
+//
+// Returns the node created.
+TIntermNode *TIntermediate::addIfElse(TIntermTyped *cond,
+ TIntermNodePair nodePair,
+ const TSourceLoc &line)
+{
+ // For compile time constant conditions, prune the code now.
+
+ if (cond->getAsConstantUnion())
+ {
+ if (cond->getAsConstantUnion()->getBConst(0) == true)
+ {
+ return EnsureBlock(nodePair.node1);
+ }
+ else
+ {
+ return EnsureBlock(nodePair.node2);
+ }
+ }
+
+ TIntermIfElse *node =
+ new TIntermIfElse(cond, EnsureBlock(nodePair.node1), EnsureBlock(nodePair.node2));
+ node->setLine(line);
+
+ return node;
+}
+
+TIntermTyped *TIntermediate::AddComma(TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &line,
+ int shaderVersion)
+{
+ TIntermTyped *commaNode = nullptr;
+ if (!left->hasSideEffects())
+ {
+ commaNode = right;
+ }
+ else
+ {
+ commaNode = new TIntermBinary(EOpComma, left, right);
+ commaNode->setLine(line);
+ }
+ TQualifier resultQualifier = TIntermBinary::GetCommaQualifier(shaderVersion, left, right);
+ commaNode->getTypePointer()->setQualifier(resultQualifier);
+ return commaNode;
+}
+
+// For "?:" test nodes. There are three children; a condition,
+// a true path, and a false path. The two paths are specified
+// as separate parameters.
+//
+// Returns the ternary node created, or one of trueExpression and falseExpression if the expression
+// could be folded.
+TIntermTyped *TIntermediate::AddTernarySelection(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
+ const TSourceLoc &line)
+{
+ // Note that the node resulting from here can be a constant union without being qualified as
+ // constant.
+ if (cond->getAsConstantUnion())
+ {
+ TQualifier resultQualifier =
+ TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression);
+ if (cond->getAsConstantUnion()->getBConst(0))
+ {
+ trueExpression->getTypePointer()->setQualifier(resultQualifier);
+ return trueExpression;
+ }
+ else
+ {
+ falseExpression->getTypePointer()->setQualifier(resultQualifier);
+ return falseExpression;
+ }
+ }
+
+ // Make a ternary node.
+ TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
+ node->setLine(line);
+
+ return node;
+}
+
+TIntermSwitch *TIntermediate::addSwitch(TIntermTyped *init,
+ TIntermBlock *statementList,
+ const TSourceLoc &line)
+{
+ TIntermSwitch *node = new TIntermSwitch(init, statementList);
+ node->setLine(line);
+
+ return node;
+}
+
+TIntermCase *TIntermediate::addCase(
+ TIntermTyped *condition, const TSourceLoc &line)
+{
+ TIntermCase *node = new TIntermCase(condition);
+ node->setLine(line);
+
+ return node;
+}
+
+//
+// Constant terminal nodes. Has a union that contains bool, float or int constants
+//
+// Returns the constant union node created.
+//
+
+TIntermConstantUnion *TIntermediate::addConstantUnion(const TConstantUnion *constantUnion,
+ const TType &type,
+ const TSourceLoc &line)
+{
+ TIntermConstantUnion *node = new TIntermConstantUnion(constantUnion, type);
+ node->setLine(line);
+
+ return node;
+}
+
+TIntermTyped *TIntermediate::AddSwizzle(TIntermTyped *baseExpression,
+ const TVectorFields &fields,
+ const TSourceLoc &dotLocation)
+{
+ TVector<int> fieldsVector;
+ for (int i = 0; i < fields.num; ++i)
+ {
+ fieldsVector.push_back(fields.offsets[i]);
+ }
+ TIntermSwizzle *node = new TIntermSwizzle(baseExpression, fieldsVector);
+ node->setLine(dotLocation);
+
+ TIntermTyped *folded = node->fold();
+ if (folded)
+ {
+ return folded;
+ }
+
+ return node;
+}
+
+//
+// Create loop nodes.
+//
+TIntermNode *TIntermediate::addLoop(
+ TLoopType type, TIntermNode *init, TIntermTyped *cond, TIntermTyped *expr,
+ TIntermNode *body, const TSourceLoc &line)
+{
+ TIntermNode *node = new TIntermLoop(type, init, cond, expr, EnsureBlock(body));
+ node->setLine(line);
+
+ return node;
+}
+
+//
+// Add branches.
+//
+TIntermBranch* TIntermediate::addBranch(
+ TOperator branchOp, const TSourceLoc &line)
+{
+ return addBranch(branchOp, 0, line);
+}
+
+TIntermBranch* TIntermediate::addBranch(
+ TOperator branchOp, TIntermTyped *expression, const TSourceLoc &line)
+{
+ TIntermBranch *node = new TIntermBranch(branchOp, expression);
+ node->setLine(line);
+
+ return node;
+}
+
+TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate,
+ TDiagnostics *diagnostics)
+{
+ switch (aggregate->getOp())
+ {
+ case EOpAtan:
+ case EOpPow:
+ case EOpMod:
+ case EOpMin:
+ case EOpMax:
+ case EOpClamp:
+ case EOpMix:
+ case EOpStep:
+ case EOpSmoothStep:
+ case EOpMul:
+ case EOpOuterProduct:
+ case EOpLessThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThan:
+ case EOpGreaterThanEqual:
+ case EOpVectorEqual:
+ case EOpVectorNotEqual:
+ case EOpDistance:
+ case EOpDot:
+ case EOpCross:
+ case EOpFaceForward:
+ case EOpReflect:
+ case EOpRefract:
+ return aggregate->fold(diagnostics);
+ default:
+ // TODO: Add support for folding array constructors
+ if (aggregate->isConstructor() && !aggregate->isArray())
+ {
+ return aggregate->fold(diagnostics);
+ }
+ // Constant folding not supported for the built-in.
+ return nullptr;
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Intermediate.h b/gfx/angle/src/compiler/translator/Intermediate.h
new file mode 100755
index 000000000..d712bf953
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Intermediate.h
@@ -0,0 +1,79 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_INTERMEDIATE_H_
+#define COMPILER_TRANSLATOR_INTERMEDIATE_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+struct TVectorFields
+{
+ int offsets[4];
+ int num;
+};
+
+//
+// Set of helper functions to help build the tree.
+//
+class TIntermediate
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TIntermediate() {}
+
+ TIntermSymbol *addSymbol(
+ int id, const TString &, const TType &, const TSourceLoc &);
+ TIntermTyped *addIndex(TOperator op,
+ TIntermTyped *base,
+ TIntermTyped *index,
+ const TSourceLoc &line,
+ TDiagnostics *diagnostics);
+ TIntermTyped *addUnaryMath(
+ TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType);
+ TIntermAggregate *growAggregate(
+ TIntermNode *left, TIntermNode *right, const TSourceLoc &);
+ static TIntermAggregate *MakeAggregate(TIntermNode *node, const TSourceLoc &line);
+ static TIntermBlock *EnsureBlock(TIntermNode *node);
+ TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &);
+ TIntermNode *addIfElse(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &line);
+ static TIntermTyped *AddTernarySelection(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
+ const TSourceLoc &line);
+ TIntermSwitch *addSwitch(TIntermTyped *init,
+ TIntermBlock *statementList,
+ const TSourceLoc &line);
+ TIntermCase *addCase(
+ TIntermTyped *condition, const TSourceLoc &line);
+ static TIntermTyped *AddComma(TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &line,
+ int shaderVersion);
+ TIntermConstantUnion *addConstantUnion(const TConstantUnion *constantUnion,
+ const TType &type,
+ const TSourceLoc &line);
+ TIntermNode *addLoop(TLoopType, TIntermNode *, TIntermTyped *, TIntermTyped *,
+ TIntermNode *, const TSourceLoc &);
+ TIntermBranch *addBranch(TOperator, const TSourceLoc &);
+ TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &);
+ static TIntermTyped *AddSwizzle(TIntermTyped *baseExpression,
+ const TVectorFields &fields,
+ const TSourceLoc &dotLocation);
+
+ static void outputTree(TIntermNode *, TInfoSinkBase &);
+
+ TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate, TDiagnostics *diagnostics);
+
+ private:
+ void operator=(TIntermediate &); // prevent assignments
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_INTERMEDIATE_H_
diff --git a/gfx/angle/src/compiler/translator/LoopInfo.cpp b/gfx/angle/src/compiler/translator/LoopInfo.cpp
new file mode 100755
index 000000000..48fa24472
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/LoopInfo.cpp
@@ -0,0 +1,214 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/LoopInfo.h"
+
+namespace sh
+{
+
+namespace
+{
+
+int EvaluateIntConstant(TIntermConstantUnion *node)
+{
+ ASSERT(node && node->getUnionArrayPointer());
+ return node->getIConst(0);
+}
+
+int GetLoopIntIncrement(TIntermLoop *node)
+{
+ TIntermNode *expr = node->getExpression();
+ // for expression has one of the following forms:
+ // loop_index++
+ // loop_index--
+ // loop_index += constant_expression
+ // loop_index -= constant_expression
+ // ++loop_index
+ // --loop_index
+ // The last two forms are not specified in the spec, but I am assuming
+ // its an oversight.
+ TIntermUnary *unOp = expr->getAsUnaryNode();
+ TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
+
+ TOperator op = EOpNull;
+ TIntermConstantUnion *incrementNode = NULL;
+ if (unOp)
+ {
+ op = unOp->getOp();
+ }
+ else if (binOp)
+ {
+ op = binOp->getOp();
+ ASSERT(binOp->getRight());
+ incrementNode = binOp->getRight()->getAsConstantUnion();
+ ASSERT(incrementNode);
+ }
+
+ int increment = 0;
+ // The operator is one of: ++ -- += -=.
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPreIncrement:
+ ASSERT(unOp && !binOp);
+ increment = 1;
+ break;
+ case EOpPostDecrement:
+ case EOpPreDecrement:
+ ASSERT(unOp && !binOp);
+ increment = -1;
+ break;
+ case EOpAddAssign:
+ ASSERT(!unOp && binOp);
+ increment = EvaluateIntConstant(incrementNode);
+ break;
+ case EOpSubAssign:
+ ASSERT(!unOp && binOp);
+ increment = - EvaluateIntConstant(incrementNode);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return increment;
+}
+
+} // namespace anonymous
+
+TLoopIndexInfo::TLoopIndexInfo()
+ : mId(-1),
+ mType(EbtVoid),
+ mInitValue(0),
+ mStopValue(0),
+ mIncrementValue(0),
+ mOp(EOpNull),
+ mCurrentValue(0)
+{
+}
+
+void TLoopIndexInfo::fillInfo(TIntermLoop *node)
+{
+ if (node == NULL)
+ return;
+
+ // Here we assume all the operations are valid, because the loop node is
+ // already validated in ValidateLimitations.
+ TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+ TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
+ TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
+
+ mId = symbol->getId();
+ mType = symbol->getBasicType();
+
+ if (mType == EbtInt)
+ {
+ TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
+ mInitValue = EvaluateIntConstant(initNode);
+ mCurrentValue = mInitValue;
+ mIncrementValue = GetLoopIntIncrement(node);
+
+ TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
+ mStopValue = EvaluateIntConstant(
+ binOp->getRight()->getAsConstantUnion());
+ mOp = binOp->getOp();
+ }
+}
+
+bool TLoopIndexInfo::satisfiesLoopCondition() const
+{
+ // Relational operator is one of: > >= < <= == or !=.
+ switch (mOp)
+ {
+ case EOpEqual:
+ return (mCurrentValue == mStopValue);
+ case EOpNotEqual:
+ return (mCurrentValue != mStopValue);
+ case EOpLessThan:
+ return (mCurrentValue < mStopValue);
+ case EOpGreaterThan:
+ return (mCurrentValue > mStopValue);
+ case EOpLessThanEqual:
+ return (mCurrentValue <= mStopValue);
+ case EOpGreaterThanEqual:
+ return (mCurrentValue >= mStopValue);
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+TLoopInfo::TLoopInfo()
+ : loop(NULL)
+{
+}
+
+TLoopInfo::TLoopInfo(TIntermLoop *node)
+ : loop(node)
+{
+ index.fillInfo(node);
+}
+
+TIntermLoop *TLoopStack::findLoop(TIntermSymbol *symbol)
+{
+ if (!symbol)
+ return NULL;
+ for (iterator iter = begin(); iter != end(); ++iter)
+ {
+ if (iter->index.getId() == symbol->getId())
+ return iter->loop;
+ }
+ return NULL;
+}
+
+TLoopIndexInfo *TLoopStack::getIndexInfo(TIntermSymbol *symbol)
+{
+ if (!symbol)
+ return NULL;
+ for (iterator iter = begin(); iter != end(); ++iter)
+ {
+ if (iter->index.getId() == symbol->getId())
+ return &(iter->index);
+ }
+ return NULL;
+}
+
+void TLoopStack::step()
+{
+ ASSERT(!empty());
+ rbegin()->index.step();
+}
+
+bool TLoopStack::satisfiesLoopCondition()
+{
+ ASSERT(!empty());
+ return rbegin()->index.satisfiesLoopCondition();
+}
+
+bool TLoopStack::needsToReplaceSymbolWithValue(TIntermSymbol *symbol)
+{
+ TIntermLoop *loop = findLoop(symbol);
+ return loop && loop->getUnrollFlag();
+}
+
+int TLoopStack::getLoopIndexValue(TIntermSymbol *symbol)
+{
+ TLoopIndexInfo *info = getIndexInfo(symbol);
+ ASSERT(info);
+ return info->getCurrentValue();
+}
+
+void TLoopStack::push(TIntermLoop *loop)
+{
+ TLoopInfo info(loop);
+ push_back(info);
+}
+
+void TLoopStack::pop()
+{
+ pop_back();
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/LoopInfo.h b/gfx/angle/src/compiler/translator/LoopInfo.h
new file mode 100755
index 000000000..393aa64f6
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/LoopInfo.h
@@ -0,0 +1,85 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_LOOPINFO_H_
+#define COMPILER_TRANSLATOR_LOOPINFO_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+class TLoopIndexInfo
+{
+ public:
+ TLoopIndexInfo();
+
+ // If type is EbtInt, fill all fields of the structure with info
+ // extracted from a loop node.
+ // If type is not EbtInt, only fill id and type.
+ void fillInfo(TIntermLoop *node);
+
+ int getId() const { return mId; }
+ void setId(int id) { mId = id; }
+ TBasicType getType() const { return mType; }
+ void setType(TBasicType type) { mType = type; }
+ int getCurrentValue() const { return mCurrentValue; }
+
+ void step() { mCurrentValue += mIncrementValue; }
+
+ // Check if the current value satisfies the loop condition.
+ bool satisfiesLoopCondition() const;
+
+ private:
+ int mId;
+ TBasicType mType; // Either EbtInt or EbtFloat
+
+ // Below fields are only valid if the index's type is int.
+ int mInitValue;
+ int mStopValue;
+ int mIncrementValue;
+ TOperator mOp;
+ int mCurrentValue;
+};
+
+struct TLoopInfo
+{
+ TLoopIndexInfo index;
+ TIntermLoop *loop;
+
+ TLoopInfo();
+ TLoopInfo(TIntermLoop *node);
+};
+
+class TLoopStack : public TVector<TLoopInfo>
+{
+ public:
+ // Search loop stack for a loop whose index matches the input symbol.
+ TIntermLoop *findLoop(TIntermSymbol *symbol);
+
+ // Find the loop index info in the loop stack by the input symbol.
+ TLoopIndexInfo *getIndexInfo(TIntermSymbol *symbol);
+
+ // Update the currentValue for the next loop iteration.
+ void step();
+
+ // Return false if loop condition is no longer satisfied.
+ bool satisfiesLoopCondition();
+
+ // Check if the symbol is the index of a loop that's unrolled.
+ bool needsToReplaceSymbolWithValue(TIntermSymbol *symbol);
+
+ // Return the current value of a given loop index symbol.
+ int getLoopIndexValue(TIntermSymbol *symbol);
+
+ void push(TIntermLoop *info);
+ void pop();
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_LOOPINFO_H_
+
diff --git a/gfx/angle/src/compiler/translator/MMap.h b/gfx/angle/src/compiler/translator/MMap.h
new file mode 100755
index 000000000..fca843992
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/MMap.h
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_MMAP_H_
+#define COMPILER_TRANSLATOR_MMAP_H_
+
+//
+// Encapsulate memory mapped files
+//
+
+class TMMap {
+public:
+ TMMap(const char* fileName) :
+ fSize(-1), // -1 is the error value returned by GetFileSize()
+ fp(NULL),
+ fBuff(0) // 0 is the error value returned by MapViewOfFile()
+ {
+ if ((fp = fopen(fileName, "r")) == NULL)
+ return;
+ char c = getc(fp);
+ fSize = 0;
+ while (c != EOF) {
+ fSize++;
+ c = getc(fp);
+ }
+ if (c == EOF)
+ fSize++;
+ rewind(fp);
+ fBuff = (char*)malloc(sizeof(char) * fSize);
+ int count = 0;
+ c = getc(fp);
+ while (c != EOF) {
+ fBuff[count++] = c;
+ c = getc(fp);
+ }
+ fBuff[count++] = c;
+ }
+
+ char* getData() { return fBuff; }
+ int getSize() { return fSize; }
+
+ ~TMMap() {
+ if (fp != NULL)
+ fclose(fp);
+ }
+
+private:
+ int fSize; // size of file to map in
+ FILE *fp;
+ char* fBuff; // the actual data;
+};
+
+#endif // COMPILER_TRANSLATOR_MMAP_H_
diff --git a/gfx/angle/src/compiler/translator/NodeSearch.h b/gfx/angle/src/compiler/translator/NodeSearch.h
new file mode 100755
index 000000000..b13b1baab
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/NodeSearch.h
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// NodeSearch.h: Utilities for searching translator node graphs
+//
+
+#ifndef COMPILER_TRANSLATOR_NODESEARCH_H_
+#define COMPILER_TRANSLATOR_NODESEARCH_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+template <class Parent>
+class NodeSearchTraverser : public TIntermTraverser
+{
+ public:
+ NodeSearchTraverser()
+ : TIntermTraverser(true, false, false),
+ mFound(false)
+ {}
+
+ bool found() const { return mFound; }
+
+ static bool search(TIntermNode *node)
+ {
+ Parent searchTraverser;
+ node->traverse(&searchTraverser);
+ return searchTraverser.found();
+ }
+
+ protected:
+ bool mFound;
+};
+
+class FindDiscard : public NodeSearchTraverser<FindDiscard>
+{
+ public:
+ virtual bool visitBranch(Visit visit, TIntermBranch *node)
+ {
+ switch (node->getFlowOp())
+ {
+ case EOpKill:
+ mFound = true;
+ break;
+
+ default: break;
+ }
+
+ return !mFound;
+ }
+};
+
+}
+
+#endif // COMPILER_TRANSLATOR_NODESEARCH_H_
diff --git a/gfx/angle/src/compiler/translator/Operator.cpp b/gfx/angle/src/compiler/translator/Operator.cpp
new file mode 100755
index 000000000..57878b930
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Operator.cpp
@@ -0,0 +1,227 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/Operator.h"
+
+const char *GetOperatorString(TOperator op)
+{
+ switch (op)
+ {
+ // Note: ops from EOpNull to EOpPrototype can't be handled here.
+
+ case EOpNegative: return "-";
+ case EOpPositive: return "+";
+ case EOpLogicalNot: return "!";
+ case EOpVectorLogicalNot: return "not";
+ case EOpBitwiseNot: return "~";
+
+ case EOpPostIncrement: return "++";
+ case EOpPostDecrement: return "--";
+ case EOpPreIncrement: return "++";
+ case EOpPreDecrement: return "--";
+
+ case EOpAdd: return "+";
+ case EOpSub: return "-";
+ case EOpMul: return "*";
+ case EOpDiv: return "/";
+ case EOpIMod: return "%";
+ case EOpEqual: return "==";
+ case EOpNotEqual: return "!=";
+ case EOpVectorEqual: return "equal";
+ case EOpVectorNotEqual: return "notEqual";
+ case EOpLessThan: return "<";
+ case EOpGreaterThan: return ">";
+ case EOpLessThanEqual: return "<=";
+ case EOpGreaterThanEqual: return ">=";
+ case EOpComma: return ",";
+
+ // Fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar: return "*";
+
+ case EOpLogicalOr: return "||";
+ case EOpLogicalXor: return "^^";
+ case EOpLogicalAnd: return "&&";
+
+ case EOpBitShiftLeft: return "<<";
+ case EOpBitShiftRight: return ">>";
+
+ case EOpBitwiseAnd: return "&";
+ case EOpBitwiseXor: return "^";
+ case EOpBitwiseOr: return "|";
+
+ // Fall-through.
+ case EOpIndexDirect:
+ case EOpIndexIndirect: return "[]";
+
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock: return ".";
+
+ case EOpRadians: return "radians";
+ case EOpDegrees: return "degrees";
+ case EOpSin: return "sin";
+ case EOpCos: return "cos";
+ case EOpTan: return "tan";
+ case EOpAsin: return "asin";
+ case EOpAcos: return "acos";
+ case EOpAtan: return "atan";
+
+ case EOpSinh: return "sinh";
+ case EOpCosh: return "cosh";
+ case EOpTanh: return "tanh";
+ case EOpAsinh: return "asinh";
+ case EOpAcosh: return "acosh";
+ case EOpAtanh: return "atanh";
+
+ case EOpPow: return "pow";
+ case EOpExp: return "exp";
+ case EOpLog: return "log";
+ case EOpExp2: return "exp2";
+ case EOpLog2: return "log2";
+ case EOpSqrt: return "sqrt";
+ case EOpInverseSqrt: return "inversesqrt";
+
+ case EOpAbs: return "abs";
+ case EOpSign: return "sign";
+ case EOpFloor: return "floor";
+ case EOpTrunc: return "trunc";
+ case EOpRound: return "round";
+ case EOpRoundEven: return "roundEven";
+ case EOpCeil: return "ceil";
+ case EOpFract: return "fract";
+ case EOpMod: return "mod";
+ case EOpModf: return "modf";
+ case EOpMin: return "min";
+ case EOpMax: return "max";
+ case EOpClamp: return "clamp";
+ case EOpMix: return "mix";
+ case EOpStep: return "step";
+ case EOpSmoothStep: return "smoothstep";
+ case EOpIsNan: return "isnan";
+ case EOpIsInf: return "isinf";
+
+ case EOpFloatBitsToInt: return "floatBitsToInt";
+ case EOpFloatBitsToUint: return "floatBitsToUint";
+ case EOpIntBitsToFloat: return "intBitsToFloat";
+ case EOpUintBitsToFloat: return "uintBitsToFloat";
+
+ case EOpPackSnorm2x16: return "packSnorm2x16";
+ case EOpPackUnorm2x16: return "packUnorm2x16";
+ case EOpPackHalf2x16: return "packHalf2x16";
+ case EOpUnpackSnorm2x16: return "unpackSnorm2x16";
+ case EOpUnpackUnorm2x16: return "unpackUnorm2x16";
+ case EOpUnpackHalf2x16: return "unpackHalf2x16";
+
+ case EOpLength: return "length";
+ case EOpDistance: return "distance";
+ case EOpDot: return "dot";
+ case EOpCross: return "cross";
+ case EOpNormalize: return "normalize";
+ case EOpFaceForward: return "faceforward";
+ case EOpReflect: return "reflect";
+ case EOpRefract: return "refract";
+
+ case EOpDFdx: return "dFdx";
+ case EOpDFdy: return "dFdy";
+ case EOpFwidth: return "fwidth";
+
+ case EOpMatrixTimesMatrix: return "*";
+
+ case EOpOuterProduct: return "outerProduct";
+ case EOpTranspose: return "transpose";
+ case EOpDeterminant: return "determinant";
+ case EOpInverse: return "inverse";
+
+ case EOpAny: return "any";
+ case EOpAll: return "all";
+
+ case EOpKill: return "kill";
+ case EOpReturn: return "return";
+ case EOpBreak: return "break";
+ case EOpContinue: return "continue";
+
+ case EOpConstructInt: return "int";
+ case EOpConstructUInt: return "uint";
+ case EOpConstructBool: return "bool";
+ case EOpConstructFloat: return "float";
+ case EOpConstructVec2: return "vec2";
+ case EOpConstructVec3: return "vec3";
+ case EOpConstructVec4: return "vec4";
+ case EOpConstructBVec2: return "bvec2";
+ case EOpConstructBVec3: return "bvec3";
+ case EOpConstructBVec4: return "bvec4";
+ case EOpConstructIVec2: return "ivec2";
+ case EOpConstructIVec3: return "ivec3";
+ case EOpConstructIVec4: return "ivec4";
+ case EOpConstructUVec2: return "uvec2";
+ case EOpConstructUVec3: return "uvec3";
+ case EOpConstructUVec4: return "uvec4";
+ case EOpConstructMat2: return "mat2";
+ case EOpConstructMat2x3: return "mat2x3";
+ case EOpConstructMat2x4: return "mat2x4";
+ case EOpConstructMat3x2: return "mat3x2";
+ case EOpConstructMat3: return "mat3";
+ case EOpConstructMat3x4: return "mat3x4";
+ case EOpConstructMat4x2: return "mat4x2";
+ case EOpConstructMat4x3: return "mat4x3";
+ case EOpConstructMat4: return "mat4";
+ // Note: EOpConstructStruct can't be handled here
+
+ case EOpAssign: return "=";
+ case EOpInitialize: return "=";
+ case EOpAddAssign: return "+=";
+ case EOpSubAssign: return "-=";
+
+ // Fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign: return "*=";
+
+ case EOpDivAssign: return "/=";
+ case EOpIModAssign: return "%=";
+ case EOpBitShiftLeftAssign: return "<<=";
+ case EOpBitShiftRightAssign: return ">>=";
+ case EOpBitwiseAndAssign: return "&=";
+ case EOpBitwiseXorAssign: return "^=";
+ case EOpBitwiseOrAssign: return "|=";
+
+ default: break;
+ }
+ return "";
+}
+
+bool IsAssignment(TOperator op)
+{
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ case EOpAssign:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ return true;
+ default:
+ return false;
+ }
+} \ No newline at end of file
diff --git a/gfx/angle/src/compiler/translator/Operator.h b/gfx/angle/src/compiler/translator/Operator.h
new file mode 100755
index 000000000..f7706f8ed
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Operator.h
@@ -0,0 +1,229 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_OPERATOR_H_
+#define COMPILER_TRANSLATOR_OPERATOR_H_
+
+//
+// Operators used by the high-level (parse tree) representation.
+//
+enum TOperator
+{
+ EOpNull, // if in a node, should only mean a node is still being built
+ EOpFunctionCall,
+ EOpParameters, // an aggregate listing the parameters to a function
+
+ EOpInvariantDeclaration, // Specialized declarations for attributing invariance
+ EOpPrototype,
+
+ //
+ // Unary operators
+ //
+
+ EOpNegative,
+ EOpPositive,
+ EOpLogicalNot,
+ EOpVectorLogicalNot,
+ EOpBitwiseNot,
+
+ EOpPostIncrement,
+ EOpPostDecrement,
+ EOpPreIncrement,
+ EOpPreDecrement,
+
+ //
+ // binary operations
+ //
+
+ EOpAdd,
+ EOpSub,
+ EOpMul,
+ EOpDiv,
+ EOpIMod,
+ EOpEqual,
+ EOpNotEqual,
+ EOpVectorEqual,
+ EOpVectorNotEqual,
+ EOpLessThan,
+ EOpGreaterThan,
+ EOpLessThanEqual,
+ EOpGreaterThanEqual,
+ EOpComma,
+
+ EOpVectorTimesScalar,
+ EOpVectorTimesMatrix,
+ EOpMatrixTimesVector,
+ EOpMatrixTimesScalar,
+
+ EOpLogicalOr,
+ EOpLogicalXor,
+ EOpLogicalAnd,
+
+ EOpBitShiftLeft,
+ EOpBitShiftRight,
+
+ EOpBitwiseAnd,
+ EOpBitwiseXor,
+ EOpBitwiseOr,
+
+ EOpIndexDirect,
+ EOpIndexIndirect,
+ EOpIndexDirectStruct,
+ EOpIndexDirectInterfaceBlock,
+
+ //
+ // Built-in functions potentially mapped to operators
+ //
+
+ EOpRadians,
+ EOpDegrees,
+ EOpSin,
+ EOpCos,
+ EOpTan,
+ EOpAsin,
+ EOpAcos,
+ EOpAtan,
+
+ EOpSinh,
+ EOpCosh,
+ EOpTanh,
+ EOpAsinh,
+ EOpAcosh,
+ EOpAtanh,
+
+ EOpPow,
+ EOpExp,
+ EOpLog,
+ EOpExp2,
+ EOpLog2,
+ EOpSqrt,
+ EOpInverseSqrt,
+
+ EOpAbs,
+ EOpSign,
+ EOpFloor,
+ EOpTrunc,
+ EOpRound,
+ EOpRoundEven,
+ EOpCeil,
+ EOpFract,
+ EOpMod,
+ EOpModf,
+ EOpMin,
+ EOpMax,
+ EOpClamp,
+ EOpMix,
+ EOpStep,
+ EOpSmoothStep,
+ EOpIsNan,
+ EOpIsInf,
+
+ EOpFloatBitsToInt,
+ EOpFloatBitsToUint,
+ EOpIntBitsToFloat,
+ EOpUintBitsToFloat,
+
+ EOpPackSnorm2x16,
+ EOpPackUnorm2x16,
+ EOpPackHalf2x16,
+ EOpUnpackSnorm2x16,
+ EOpUnpackUnorm2x16,
+ EOpUnpackHalf2x16,
+
+ EOpLength,
+ EOpDistance,
+ EOpDot,
+ EOpCross,
+ EOpNormalize,
+ EOpFaceForward,
+ EOpReflect,
+ EOpRefract,
+
+ EOpDFdx, // Fragment only, OES_standard_derivatives extension
+ EOpDFdy, // Fragment only, OES_standard_derivatives extension
+ EOpFwidth, // Fragment only, OES_standard_derivatives extension
+
+ EOpMatrixTimesMatrix,
+
+ EOpOuterProduct,
+ EOpTranspose,
+ EOpDeterminant,
+ EOpInverse,
+
+ EOpAny,
+ EOpAll,
+
+ //
+ // Branch
+ //
+
+ EOpKill, // Fragment only
+ EOpReturn,
+ EOpBreak,
+ EOpContinue,
+
+ //
+ // Constructors
+ //
+
+ EOpConstructInt,
+ EOpConstructUInt,
+ EOpConstructBool,
+ EOpConstructFloat,
+ EOpConstructVec2,
+ EOpConstructVec3,
+ EOpConstructVec4,
+ EOpConstructBVec2,
+ EOpConstructBVec3,
+ EOpConstructBVec4,
+ EOpConstructIVec2,
+ EOpConstructIVec3,
+ EOpConstructIVec4,
+ EOpConstructUVec2,
+ EOpConstructUVec3,
+ EOpConstructUVec4,
+ EOpConstructMat2,
+ EOpConstructMat2x3,
+ EOpConstructMat2x4,
+ EOpConstructMat3x2,
+ EOpConstructMat3,
+ EOpConstructMat3x4,
+ EOpConstructMat4x2,
+ EOpConstructMat4x3,
+ EOpConstructMat4,
+ EOpConstructStruct,
+
+ //
+ // moves
+ //
+
+ EOpAssign,
+ EOpInitialize,
+ EOpAddAssign,
+ EOpSubAssign,
+
+ EOpMulAssign,
+ EOpVectorTimesMatrixAssign,
+ EOpVectorTimesScalarAssign,
+ EOpMatrixTimesScalarAssign,
+ EOpMatrixTimesMatrixAssign,
+
+ EOpDivAssign,
+ EOpIModAssign,
+ EOpBitShiftLeftAssign,
+ EOpBitShiftRightAssign,
+ EOpBitwiseAndAssign,
+ EOpBitwiseXorAssign,
+ EOpBitwiseOrAssign
+};
+
+// Returns the string corresponding to the operator in GLSL
+const char* GetOperatorString(TOperator op);
+
+// Say whether or not a binary or unary operation changes the value of a variable.
+bool IsAssignment(TOperator op);
+
+#endif // COMPILER_TRANSLATOR_OPERATOR_H_
diff --git a/gfx/angle/src/compiler/translator/OutputESSL.cpp b/gfx/angle/src/compiler/translator/OutputESSL.cpp
new file mode 100755
index 000000000..e55d6c544
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputESSL.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/OutputESSL.h"
+
+namespace sh
+{
+
+TOutputESSL::TOutputESSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ bool forceHighp,
+ ShCompileOptions compileOptions)
+ : TOutputGLSLBase(objSink,
+ clampingStrategy,
+ hashFunction,
+ nameMap,
+ symbolTable,
+ shaderType,
+ shaderVersion,
+ SH_ESSL_OUTPUT,
+ compileOptions),
+ mForceHighp(forceHighp)
+{
+}
+
+bool TOutputESSL::writeVariablePrecision(TPrecision precision)
+{
+ if (precision == EbpUndefined)
+ return false;
+
+ TInfoSinkBase& out = objSink();
+ if (mForceHighp)
+ out << getPrecisionString(EbpHigh);
+ else
+ out << getPrecisionString(precision);
+ return true;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/OutputESSL.h b/gfx/angle/src/compiler/translator/OutputESSL.h
new file mode 100755
index 000000000..5b2cb9492
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputESSL.h
@@ -0,0 +1,37 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_OUTPUTESSL_H_
+#define COMPILER_TRANSLATOR_OUTPUTESSL_H_
+
+#include "compiler/translator/OutputGLSLBase.h"
+
+namespace sh
+{
+
+class TOutputESSL : public TOutputGLSLBase
+{
+ public:
+ TOutputESSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ bool forceHighp,
+ ShCompileOptions compileOptions);
+
+ protected:
+ bool writeVariablePrecision(TPrecision precision) override;
+
+ private:
+ bool mForceHighp;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_
diff --git a/gfx/angle/src/compiler/translator/OutputGLSL.cpp b/gfx/angle/src/compiler/translator/OutputGLSL.cpp
new file mode 100755
index 000000000..fc2b18471
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputGLSL.cpp
@@ -0,0 +1,111 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/OutputGLSL.h"
+
+namespace sh
+{
+
+TOutputGLSL::TOutputGLSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions)
+ : TOutputGLSLBase(objSink,
+ clampingStrategy,
+ hashFunction,
+ nameMap,
+ symbolTable,
+ shaderType,
+ shaderVersion,
+ output,
+ compileOptions)
+{
+}
+
+bool TOutputGLSL::writeVariablePrecision(TPrecision)
+{
+ return false;
+}
+
+void TOutputGLSL::visitSymbol(TIntermSymbol *node)
+{
+ TInfoSinkBase& out = objSink();
+
+ const TString &symbol = node->getSymbol();
+ if (symbol == "gl_FragDepthEXT")
+ {
+ out << "gl_FragDepth";
+ }
+ else if (symbol == "gl_FragColor" && sh::IsGLSL130OrNewer(getShaderOutput()))
+ {
+ out << "webgl_FragColor";
+ }
+ else if (symbol == "gl_FragData" && sh::IsGLSL130OrNewer(getShaderOutput()))
+ {
+ out << "webgl_FragData";
+ }
+ else if (symbol == "gl_SecondaryFragColorEXT")
+ {
+ out << "angle_SecondaryFragColor";
+ }
+ else if (symbol == "gl_SecondaryFragDataEXT")
+ {
+ out << "angle_SecondaryFragData";
+ }
+ else
+ {
+ TOutputGLSLBase::visitSymbol(node);
+ }
+}
+
+TString TOutputGLSL::translateTextureFunction(TString &name)
+{
+ static const char *simpleRename[] = {
+ "texture2DLodEXT", "texture2DLod",
+ "texture2DProjLodEXT", "texture2DProjLod",
+ "textureCubeLodEXT", "textureCubeLod",
+ "texture2DGradEXT", "texture2DGradARB",
+ "texture2DProjGradEXT", "texture2DProjGradARB",
+ "textureCubeGradEXT", "textureCubeGradARB",
+ NULL, NULL
+ };
+ static const char *legacyToCoreRename[] = {
+ "texture2D", "texture",
+ "texture2DProj", "textureProj",
+ "texture2DLod", "textureLod",
+ "texture2DProjLod", "textureProjLod",
+ "texture2DRect", "texture",
+ "textureCube", "texture",
+ "textureCubeLod", "textureLod",
+ // Extensions
+ "texture2DLodEXT", "textureLod",
+ "texture2DProjLodEXT", "textureProjLod",
+ "textureCubeLodEXT", "textureLod",
+ "texture2DGradEXT", "textureGrad",
+ "texture2DProjGradEXT", "textureProjGrad",
+ "textureCubeGradEXT", "textureGrad",
+ NULL, NULL
+ };
+ const char **mapping =
+ (sh::IsGLSL130OrNewer(getShaderOutput())) ? legacyToCoreRename : simpleRename;
+
+ for (int i = 0; mapping[i] != NULL; i += 2)
+ {
+ if (name == mapping[i])
+ {
+ return mapping[i+1];
+ }
+ }
+
+ return name;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/OutputGLSL.h b/gfx/angle/src/compiler/translator/OutputGLSL.h
new file mode 100755
index 000000000..d910c0004
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputGLSL.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_OUTPUTGLSL_H_
+#define COMPILER_TRANSLATOR_OUTPUTGLSL_H_
+
+#include "compiler/translator/OutputGLSLBase.h"
+
+namespace sh
+{
+
+class TOutputGLSL : public TOutputGLSLBase
+{
+ public:
+ TOutputGLSL(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions);
+
+ protected:
+ bool writeVariablePrecision(TPrecision) override;
+ void visitSymbol(TIntermSymbol *node) override;
+ TString translateTextureFunction(TString &name) override;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_OUTPUTGLSL_H_
diff --git a/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
new file mode 100755
index 000000000..2c32b2ea5
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.cpp
@@ -0,0 +1,1400 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/OutputGLSLBase.h"
+
+#include "common/debug.h"
+
+#include <cfloat>
+
+namespace sh
+{
+
+namespace
+{
+TString arrayBrackets(const TType &type)
+{
+ ASSERT(type.isArray());
+ TInfoSinkBase out;
+ out << "[" << type.getArraySize() << "]";
+ return TString(out.c_str());
+}
+
+bool isSingleStatement(TIntermNode *node)
+{
+ if (node->getAsFunctionDefinition())
+ {
+ return false;
+ }
+ else if (node->getAsBlock())
+ {
+ return false;
+ }
+ else if (node->getAsIfElseNode())
+ {
+ return false;
+ }
+ else if (node->getAsLoopNode())
+ {
+ return false;
+ }
+ else if (node->getAsSwitchNode())
+ {
+ return false;
+ }
+ else if (node->getAsCaseNode())
+ {
+ return false;
+ }
+ return true;
+}
+
+// If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever
+// variables with specified layout qualifiers are copied. Additional checks are needed against the
+// type and storage qualifier of the variable to verify that layout qualifiers have to be outputted.
+// TODO (mradev): Fix layout qualifier spilling in ScalarizeVecAndMatConstructorArgs and remove
+// NeedsToWriteLayoutQualifier.
+bool NeedsToWriteLayoutQualifier(const TType &type)
+{
+ if (type.getBasicType() == EbtInterfaceBlock)
+ {
+ return false;
+ }
+
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+
+ if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) &&
+ layoutQualifier.location >= 0)
+ {
+ return true;
+ }
+ if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions)
+ : TIntermTraverser(true, true, true),
+ mObjSink(objSink),
+ mDeclaringVariables(false),
+ mClampingStrategy(clampingStrategy),
+ mHashFunction(hashFunction),
+ mNameMap(nameMap),
+ mSymbolTable(symbolTable),
+ mShaderType(shaderType),
+ mShaderVersion(shaderVersion),
+ mOutput(output),
+ mCompileOptions(compileOptions)
+{
+}
+
+void TOutputGLSLBase::writeInvariantQualifier(const TType &type)
+{
+ if (!sh::RemoveInvariant(mShaderType, mShaderVersion, mOutput, mCompileOptions))
+ {
+ TInfoSinkBase &out = objSink();
+ out << "invariant ";
+ }
+}
+
+void TOutputGLSLBase::writeTriplet(
+ Visit visit, const char *preStr, const char *inStr, const char *postStr)
+{
+ TInfoSinkBase &out = objSink();
+ if (visit == PreVisit && preStr)
+ out << preStr;
+ else if (visit == InVisit && inStr)
+ out << inStr;
+ else if (visit == PostVisit && postStr)
+ out << postStr;
+}
+
+void TOutputGLSLBase::writeBuiltInFunctionTriplet(
+ Visit visit, const char *preStr, bool useEmulatedFunction)
+{
+ TString preString = useEmulatedFunction ?
+ BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr) : preStr;
+ writeTriplet(visit, preString.c_str(), ", ", ")");
+}
+
+void TOutputGLSLBase::writeLayoutQualifier(const TType &type)
+{
+ if (!NeedsToWriteLayoutQualifier(type))
+ {
+ return;
+ }
+
+ TInfoSinkBase &out = objSink();
+ const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
+ out << "layout(";
+
+ if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn)
+ {
+ if (layoutQualifier.location >= 0)
+ {
+ out << "location = " << layoutQualifier.location;
+ }
+ }
+
+ if (IsImage(type.getBasicType()) && layoutQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ ASSERT(type.getQualifier() == EvqTemporary || type.getQualifier() == EvqUniform);
+ out << getImageInternalFormatString(layoutQualifier.imageInternalFormat);
+ }
+
+ out << ") ";
+}
+
+const char *TOutputGLSLBase::mapQualifierToString(TQualifier qualifier)
+{
+ if (sh::IsGLSL410OrOlder(mOutput) && mShaderVersion >= 300 &&
+ (mCompileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0)
+ {
+ switch (qualifier)
+ {
+ // The return string is consistent with sh::getQualifierString() from
+ // BaseTypes.h minus the "centroid" keyword.
+ case EvqCentroid:
+ return "";
+ case EvqCentroidIn:
+ return "smooth in";
+ case EvqCentroidOut:
+ return "smooth out";
+ default:
+ break;
+ }
+ }
+ if (sh::IsGLSL130OrNewer(mOutput))
+ {
+ switch (qualifier)
+ {
+ case EvqAttribute:
+ return "in";
+ case EvqVaryingIn:
+ return "in";
+ case EvqVaryingOut:
+ return "out";
+ default:
+ break;
+ }
+ }
+ return sh::getQualifierString(qualifier);
+}
+
+void TOutputGLSLBase::writeVariableType(const TType &type)
+{
+ TQualifier qualifier = type.getQualifier();
+ TInfoSinkBase &out = objSink();
+ if (type.isInvariant())
+ {
+ writeInvariantQualifier(type);
+ }
+ if (type.getBasicType() == EbtInterfaceBlock)
+ {
+ TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ declareInterfaceBlockLayout(interfaceBlock);
+ }
+ if (qualifier != EvqTemporary && qualifier != EvqGlobal)
+ {
+ const char *qualifierString = mapQualifierToString(qualifier);
+ if (qualifierString && qualifierString[0] != '\0')
+ {
+ out << qualifierString << " ";
+ }
+ }
+
+ const TMemoryQualifier &memoryQualifier = type.getMemoryQualifier();
+ if (memoryQualifier.readonly)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "readonly ";
+ }
+
+ if (memoryQualifier.writeonly)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "writeonly ";
+ }
+
+ if (memoryQualifier.coherent)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "coherent ";
+ }
+
+ if (memoryQualifier.restrictQualifier)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "restrict ";
+ }
+
+ if (memoryQualifier.volatileQualifier)
+ {
+ ASSERT(IsImage(type.getBasicType()));
+ out << "volatile ";
+ }
+
+ // Declare the struct if we have not done so already.
+ if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct()))
+ {
+ TStructure *structure = type.getStruct();
+
+ declareStruct(structure);
+
+ if (!structure->name().empty())
+ {
+ mDeclaredStructs.insert(structure->uniqueId());
+ }
+ }
+ else if (type.getBasicType() == EbtInterfaceBlock)
+ {
+ TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
+ declareInterfaceBlock(interfaceBlock);
+ }
+ else
+ {
+ if (writeVariablePrecision(type.getPrecision()))
+ out << " ";
+ out << getTypeName(type);
+ }
+}
+
+void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence &args)
+{
+ TInfoSinkBase &out = objSink();
+ for (TIntermSequence::const_iterator iter = args.begin();
+ iter != args.end(); ++iter)
+ {
+ const TIntermSymbol *arg = (*iter)->getAsSymbolNode();
+ ASSERT(arg != NULL);
+
+ const TType &type = arg->getType();
+ writeVariableType(type);
+
+ if (!arg->getName().getString().empty())
+ out << " " << hashName(arg->getName());
+ if (type.isArray())
+ out << arrayBrackets(type);
+
+ // Put a comma if this is not the last argument.
+ if (iter != args.end() - 1)
+ out << ", ";
+ }
+}
+
+const TConstantUnion *TOutputGLSLBase::writeConstantUnion(
+ const TType &type, const TConstantUnion *pConstUnion)
+{
+ TInfoSinkBase &out = objSink();
+
+ if (type.getBasicType() == EbtStruct)
+ {
+ const TStructure *structure = type.getStruct();
+ out << hashName(TName(structure->name())) << "(";
+
+ const TFieldList &fields = structure->fields();
+ for (size_t i = 0; i < fields.size(); ++i)
+ {
+ const TType *fieldType = fields[i]->type();
+ ASSERT(fieldType != NULL);
+ pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
+ if (i != fields.size() - 1)
+ out << ", ";
+ }
+ out << ")";
+ }
+ else
+ {
+ size_t size = type.getObjectSize();
+ bool writeType = size > 1;
+ if (writeType)
+ out << getTypeName(type) << "(";
+ for (size_t i = 0; i < size; ++i, ++pConstUnion)
+ {
+ switch (pConstUnion->getType())
+ {
+ case EbtFloat:
+ out << std::min(FLT_MAX, std::max(-FLT_MAX, pConstUnion->getFConst()));
+ break;
+ case EbtInt:
+ out << pConstUnion->getIConst();
+ break;
+ case EbtUInt:
+ out << pConstUnion->getUConst() << "u";
+ break;
+ case EbtBool:
+ out << pConstUnion->getBConst();
+ break;
+ default: UNREACHABLE();
+ }
+ if (i != size - 1)
+ out << ", ";
+ }
+ if (writeType)
+ out << ")";
+ }
+ return pConstUnion;
+}
+
+void TOutputGLSLBase::writeConstructorTriplet(Visit visit, const TType &type)
+{
+ TInfoSinkBase &out = objSink();
+ if (visit == PreVisit)
+ {
+ if (type.isArray())
+ {
+ out << getTypeName(type);
+ out << arrayBrackets(type);
+ out << "(";
+ }
+ else
+ {
+ out << getTypeName(type) << "(";
+ }
+ }
+ else
+ {
+ writeTriplet(visit, nullptr, ", ", ")");
+ }
+}
+
+void TOutputGLSLBase::visitSymbol(TIntermSymbol *node)
+{
+ TInfoSinkBase &out = objSink();
+ if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
+ out << mLoopUnrollStack.getLoopIndexValue(node);
+ else
+ out << hashVariableName(node->getName());
+
+ if (mDeclaringVariables && node->getType().isArray())
+ out << arrayBrackets(node->getType());
+}
+
+void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion *node)
+{
+ writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+}
+
+bool TOutputGLSLBase::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = objSink();
+ if (visit == PostVisit)
+ {
+ out << ".";
+ node->writeOffsetsAsXYZW(&out);
+ }
+ return true;
+}
+
+bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase &out = objSink();
+ switch (node->getOp())
+ {
+ case EOpComma:
+ writeTriplet(visit, "(", ", ", ")");
+ break;
+ case EOpInitialize:
+ if (visit == InVisit)
+ {
+ out << " = ";
+ // RHS of initialize is not being declared.
+ mDeclaringVariables = false;
+ }
+ break;
+ case EOpAssign:
+ writeTriplet(visit, "(", " = ", ")");
+ break;
+ case EOpAddAssign:
+ writeTriplet(visit, "(", " += ", ")");
+ break;
+ case EOpSubAssign:
+ writeTriplet(visit, "(", " -= ", ")");
+ break;
+ case EOpDivAssign:
+ writeTriplet(visit, "(", " /= ", ")");
+ break;
+ case EOpIModAssign:
+ writeTriplet(visit, "(", " %= ", ")");
+ break;
+ // Notice the fall-through.
+ case EOpMulAssign:
+ case EOpVectorTimesMatrixAssign:
+ case EOpVectorTimesScalarAssign:
+ case EOpMatrixTimesScalarAssign:
+ case EOpMatrixTimesMatrixAssign:
+ writeTriplet(visit, "(", " *= ", ")");
+ break;
+ case EOpBitShiftLeftAssign:
+ writeTriplet(visit, "(", " <<= ", ")");
+ break;
+ case EOpBitShiftRightAssign:
+ writeTriplet(visit, "(", " >>= ", ")");
+ break;
+ case EOpBitwiseAndAssign:
+ writeTriplet(visit, "(", " &= ", ")");
+ break;
+ case EOpBitwiseXorAssign:
+ writeTriplet(visit, "(", " ^= ", ")");
+ break;
+ case EOpBitwiseOrAssign:
+ writeTriplet(visit, "(", " |= ", ")");
+ break;
+
+ case EOpIndexDirect:
+ writeTriplet(visit, NULL, "[", "]");
+ break;
+ case EOpIndexIndirect:
+ if (node->getAddIndexClamp())
+ {
+ if (visit == InVisit)
+ {
+ if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+ out << "[int(clamp(float(";
+ else
+ out << "[webgl_int_clamp(";
+ }
+ else if (visit == PostVisit)
+ {
+ int maxSize;
+ TIntermTyped *left = node->getLeft();
+ TType leftType = left->getType();
+
+ if (left->isArray())
+ {
+ // The shader will fail validation if the array length is not > 0.
+ maxSize = static_cast<int>(leftType.getArraySize()) - 1;
+ }
+ else
+ {
+ maxSize = leftType.getNominalSize() - 1;
+ }
+
+ if (mClampingStrategy == SH_CLAMP_WITH_CLAMP_INTRINSIC)
+ out << "), 0.0, float(" << maxSize << ")))]";
+ else
+ out << ", 0, " << maxSize << ")]";
+ }
+ }
+ else
+ {
+ writeTriplet(visit, NULL, "[", "]");
+ }
+ break;
+ case EOpIndexDirectStruct:
+ if (visit == InVisit)
+ {
+ // Here we are writing out "foo.bar", where "foo" is struct
+ // and "bar" is field. In AST, it is represented as a binary
+ // node, where left child represents "foo" and right child "bar".
+ // The node itself represents ".". The struct field "bar" is
+ // actually stored as an index into TStructure::fields.
+ out << ".";
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
+
+ TString fieldName = field->name();
+ if (!mSymbolTable.findBuiltIn(structure->name(), mShaderVersion))
+ fieldName = hashName(TName(fieldName));
+
+ out << fieldName;
+ visitChildren = false;
+ }
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ if (visit == InVisit)
+ {
+ out << ".";
+ const TInterfaceBlock *interfaceBlock =
+ node->getLeft()->getType().getInterfaceBlock();
+ const TIntermConstantUnion *index = node->getRight()->getAsConstantUnion();
+ const TField *field = interfaceBlock->fields()[index->getIConst(0)];
+
+ TString fieldName = field->name();
+ ASSERT(!mSymbolTable.findBuiltIn(interfaceBlock->name(), mShaderVersion));
+ fieldName = hashName(TName(fieldName));
+
+ out << fieldName;
+ visitChildren = false;
+ }
+ break;
+
+ case EOpAdd:
+ writeTriplet(visit, "(", " + ", ")");
+ break;
+ case EOpSub:
+ writeTriplet(visit, "(", " - ", ")");
+ break;
+ case EOpMul:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+ case EOpDiv:
+ writeTriplet(visit, "(", " / ", ")");
+ break;
+ case EOpIMod:
+ writeTriplet(visit, "(", " % ", ")");
+ break;
+ case EOpBitShiftLeft:
+ writeTriplet(visit, "(", " << ", ")");
+ break;
+ case EOpBitShiftRight:
+ writeTriplet(visit, "(", " >> ", ")");
+ break;
+ case EOpBitwiseAnd:
+ writeTriplet(visit, "(", " & ", ")");
+ break;
+ case EOpBitwiseXor:
+ writeTriplet(visit, "(", " ^ ", ")");
+ break;
+ case EOpBitwiseOr:
+ writeTriplet(visit, "(", " | ", ")");
+ break;
+
+ case EOpEqual:
+ writeTriplet(visit, "(", " == ", ")");
+ break;
+ case EOpNotEqual:
+ writeTriplet(visit, "(", " != ", ")");
+ break;
+ case EOpLessThan:
+ writeTriplet(visit, "(", " < ", ")");
+ break;
+ case EOpGreaterThan:
+ writeTriplet(visit, "(", " > ", ")");
+ break;
+ case EOpLessThanEqual:
+ writeTriplet(visit, "(", " <= ", ")");
+ break;
+ case EOpGreaterThanEqual:
+ writeTriplet(visit, "(", " >= ", ")");
+ break;
+
+ // Notice the fall-through.
+ case EOpVectorTimesScalar:
+ case EOpVectorTimesMatrix:
+ case EOpMatrixTimesVector:
+ case EOpMatrixTimesScalar:
+ case EOpMatrixTimesMatrix:
+ writeTriplet(visit, "(", " * ", ")");
+ break;
+
+ case EOpLogicalOr:
+ writeTriplet(visit, "(", " || ", ")");
+ break;
+ case EOpLogicalXor:
+ writeTriplet(visit, "(", " ^^ ", ")");
+ break;
+ case EOpLogicalAnd:
+ writeTriplet(visit, "(", " && ", ")");
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return visitChildren;
+}
+
+bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary *node)
+{
+ TString preString;
+ TString postString = ")";
+
+ switch (node->getOp())
+ {
+ case EOpNegative: preString = "(-"; break;
+ case EOpPositive: preString = "(+"; break;
+ case EOpVectorLogicalNot: preString = "not("; break;
+ case EOpLogicalNot: preString = "(!"; break;
+ case EOpBitwiseNot: preString = "(~"; break;
+
+ case EOpPostIncrement: preString = "("; postString = "++)"; break;
+ case EOpPostDecrement: preString = "("; postString = "--)"; break;
+ case EOpPreIncrement: preString = "(++"; break;
+ case EOpPreDecrement: preString = "(--"; break;
+
+ case EOpRadians:
+ preString = "radians(";
+ break;
+ case EOpDegrees:
+ preString = "degrees(";
+ break;
+ case EOpSin:
+ preString = "sin(";
+ break;
+ case EOpCos:
+ preString = "cos(";
+ break;
+ case EOpTan:
+ preString = "tan(";
+ break;
+ case EOpAsin:
+ preString = "asin(";
+ break;
+ case EOpAcos:
+ preString = "acos(";
+ break;
+ case EOpAtan:
+ preString = "atan(";
+ break;
+
+ case EOpSinh:
+ preString = "sinh(";
+ break;
+ case EOpCosh:
+ preString = "cosh(";
+ break;
+ case EOpTanh:
+ preString = "tanh(";
+ break;
+ case EOpAsinh:
+ preString = "asinh(";
+ break;
+ case EOpAcosh:
+ preString = "acosh(";
+ break;
+ case EOpAtanh:
+ preString = "atanh(";
+ break;
+
+ case EOpExp:
+ preString = "exp(";
+ break;
+ case EOpLog:
+ preString = "log(";
+ break;
+ case EOpExp2:
+ preString = "exp2(";
+ break;
+ case EOpLog2:
+ preString = "log2(";
+ break;
+ case EOpSqrt:
+ preString = "sqrt(";
+ break;
+ case EOpInverseSqrt:
+ preString = "inversesqrt(";
+ break;
+
+ case EOpAbs:
+ preString = "abs(";
+ break;
+ case EOpSign:
+ preString = "sign(";
+ break;
+ case EOpFloor:
+ preString = "floor(";
+ break;
+ case EOpTrunc:
+ preString = "trunc(";
+ break;
+ case EOpRound:
+ preString = "round(";
+ break;
+ case EOpRoundEven:
+ preString = "roundEven(";
+ break;
+ case EOpCeil:
+ preString = "ceil(";
+ break;
+ case EOpFract:
+ preString = "fract(";
+ break;
+ case EOpIsNan:
+ preString = "isnan(";
+ break;
+ case EOpIsInf:
+ preString = "isinf(";
+ break;
+
+ case EOpFloatBitsToInt:
+ preString = "floatBitsToInt(";
+ break;
+ case EOpFloatBitsToUint:
+ preString = "floatBitsToUint(";
+ break;
+ case EOpIntBitsToFloat:
+ preString = "intBitsToFloat(";
+ break;
+ case EOpUintBitsToFloat:
+ preString = "uintBitsToFloat(";
+ break;
+
+ case EOpPackSnorm2x16:
+ preString = "packSnorm2x16(";
+ break;
+ case EOpPackUnorm2x16:
+ preString = "packUnorm2x16(";
+ break;
+ case EOpPackHalf2x16:
+ preString = "packHalf2x16(";
+ break;
+ case EOpUnpackSnorm2x16:
+ preString = "unpackSnorm2x16(";
+ break;
+ case EOpUnpackUnorm2x16:
+ preString = "unpackUnorm2x16(";
+ break;
+ case EOpUnpackHalf2x16:
+ preString = "unpackHalf2x16(";
+ break;
+
+ case EOpLength:
+ preString = "length(";
+ break;
+ case EOpNormalize:
+ preString = "normalize(";
+ break;
+
+ case EOpDFdx:
+ preString = "dFdx(";
+ break;
+ case EOpDFdy:
+ preString = "dFdy(";
+ break;
+ case EOpFwidth:
+ preString = "fwidth(";
+ break;
+
+ case EOpTranspose:
+ preString = "transpose(";
+ break;
+ case EOpDeterminant:
+ preString = "determinant(";
+ break;
+ case EOpInverse:
+ preString = "inverse(";
+ break;
+
+ case EOpAny:
+ preString = "any(";
+ break;
+ case EOpAll:
+ preString = "all(";
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ if (visit == PreVisit && node->getUseEmulatedFunction())
+ preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
+ writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
+
+ return true;
+}
+
+bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node)
+{
+ TInfoSinkBase &out = objSink();
+ // Notice two brackets at the beginning and end. The outer ones
+ // encapsulate the whole ternary expression. This preserves the
+ // order of precedence when ternary expressions are used in a
+ // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+ out << "((";
+ node->getCondition()->traverse(this);
+ out << ") ? (";
+ node->getTrueExpression()->traverse(this);
+ out << ") : (";
+ node->getFalseExpression()->traverse(this);
+ out << "))";
+ return false;
+}
+
+bool TOutputGLSLBase::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ TInfoSinkBase &out = objSink();
+
+ out << "if (";
+ node->getCondition()->traverse(this);
+ out << ")\n";
+
+ incrementDepth(node);
+ visitCodeBlock(node->getTrueBlock());
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+ visitCodeBlock(node->getFalseBlock());
+ }
+ decrementDepth();
+ return false;
+}
+
+bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ if (node->getStatementList())
+ {
+ writeTriplet(visit, "switch (", ") ", nullptr);
+ // The curly braces get written when visiting the statementList aggregate
+ }
+ else
+ {
+ // No statementList, so it won't output curly braces
+ writeTriplet(visit, "switch (", ") {", "}\n");
+ }
+ return true;
+}
+
+bool TOutputGLSLBase::visitCase(Visit visit, TIntermCase *node)
+{
+ if (node->hasCondition())
+ {
+ writeTriplet(visit, "case (", nullptr, "):\n");
+ return true;
+ }
+ else
+ {
+ TInfoSinkBase &out = objSink();
+ out << "default:\n";
+ return false;
+ }
+}
+
+bool TOutputGLSLBase::visitBlock(Visit visit, TIntermBlock *node)
+{
+ TInfoSinkBase &out = objSink();
+ // Scope the blocks except when at the global scope.
+ if (mDepth > 0)
+ {
+ out << "{\n";
+ }
+
+ incrementDepth(node);
+ for (TIntermSequence::const_iterator iter = node->getSequence()->begin();
+ iter != node->getSequence()->end(); ++iter)
+ {
+ TIntermNode *curNode = *iter;
+ ASSERT(curNode != nullptr);
+ curNode->traverse(this);
+
+ if (isSingleStatement(curNode))
+ out << ";\n";
+ }
+ decrementDepth();
+
+ // Scope the blocks except when at the global scope.
+ if (mDepth > 0)
+ {
+ out << "}\n";
+ }
+ return false;
+}
+
+bool TOutputGLSLBase::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ TInfoSinkBase &out = objSink();
+
+ ASSERT(visit == PreVisit);
+ {
+ const TType &type = node->getType();
+ writeVariableType(type);
+ if (type.isArray())
+ out << arrayBrackets(type);
+ }
+
+ out << " " << hashFunctionNameIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+
+ incrementDepth(node);
+
+ // Traverse function parameters.
+ TIntermAggregate *params = node->getFunctionParameters()->getAsAggregate();
+ ASSERT(params->getOp() == EOpParameters);
+ params->traverse(this);
+
+ // Traverse function body.
+ visitCodeBlock(node->getBody());
+ decrementDepth();
+
+ // Fully processed; no need to visit children.
+ return false;
+}
+
+bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ bool visitChildren = true;
+ TInfoSinkBase &out = objSink();
+ bool useEmulatedFunction = (visit == PreVisit && node->getUseEmulatedFunction());
+ switch (node->getOp())
+ {
+ case EOpPrototype:
+ // Function declaration.
+ ASSERT(visit == PreVisit);
+ {
+ const TType &type = node->getType();
+ writeVariableType(type);
+ if (type.isArray())
+ out << arrayBrackets(type);
+ }
+
+ out << " " << hashFunctionNameIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+
+ out << "(";
+ writeFunctionParameters(*(node->getSequence()));
+ out << ")";
+
+ visitChildren = false;
+ break;
+ case EOpFunctionCall:
+ // Function call.
+ if (visit == PreVisit)
+ out << hashFunctionNameIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "(";
+ else if (visit == InVisit)
+ out << ", ";
+ else
+ out << ")";
+ break;
+ case EOpParameters:
+ // Function parameters.
+ ASSERT(visit == PreVisit);
+ out << "(";
+ writeFunctionParameters(*(node->getSequence()));
+ out << ")";
+ visitChildren = false;
+ break;
+ case EOpInvariantDeclaration:
+ // Invariant declaration.
+ ASSERT(visit == PreVisit);
+ {
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence && sequence->size() == 1);
+ const TIntermSymbol *symbol = sequence->front()->getAsSymbolNode();
+ ASSERT(symbol);
+ out << "invariant " << hashVariableName(symbol->getName());
+ }
+ visitChildren = false;
+ break;
+ case EOpConstructFloat:
+ case EOpConstructVec2:
+ case EOpConstructVec3:
+ case EOpConstructVec4:
+ case EOpConstructBool:
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructInt:
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ case EOpConstructUInt:
+ case EOpConstructUVec2:
+ case EOpConstructUVec3:
+ case EOpConstructUVec4:
+ case EOpConstructMat2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4:
+ case EOpConstructStruct:
+ writeConstructorTriplet(visit, node->getType());
+ break;
+
+ case EOpOuterProduct:
+ writeBuiltInFunctionTriplet(visit, "outerProduct(", useEmulatedFunction);
+ break;
+
+ case EOpLessThan:
+ writeBuiltInFunctionTriplet(visit, "lessThan(", useEmulatedFunction);
+ break;
+ case EOpGreaterThan:
+ writeBuiltInFunctionTriplet(visit, "greaterThan(", useEmulatedFunction);
+ break;
+ case EOpLessThanEqual:
+ writeBuiltInFunctionTriplet(visit, "lessThanEqual(", useEmulatedFunction);
+ break;
+ case EOpGreaterThanEqual:
+ writeBuiltInFunctionTriplet(visit, "greaterThanEqual(", useEmulatedFunction);
+ break;
+ case EOpVectorEqual:
+ writeBuiltInFunctionTriplet(visit, "equal(", useEmulatedFunction);
+ break;
+ case EOpVectorNotEqual:
+ writeBuiltInFunctionTriplet(visit, "notEqual(", useEmulatedFunction);
+ break;
+
+ case EOpMod:
+ writeBuiltInFunctionTriplet(visit, "mod(", useEmulatedFunction);
+ break;
+ case EOpModf:
+ writeBuiltInFunctionTriplet(visit, "modf(", useEmulatedFunction);
+ break;
+ case EOpPow:
+ writeBuiltInFunctionTriplet(visit, "pow(", useEmulatedFunction);
+ break;
+ case EOpAtan:
+ writeBuiltInFunctionTriplet(visit, "atan(", useEmulatedFunction);
+ break;
+ case EOpMin:
+ writeBuiltInFunctionTriplet(visit, "min(", useEmulatedFunction);
+ break;
+ case EOpMax:
+ writeBuiltInFunctionTriplet(visit, "max(", useEmulatedFunction);
+ break;
+ case EOpClamp:
+ writeBuiltInFunctionTriplet(visit, "clamp(", useEmulatedFunction);
+ break;
+ case EOpMix:
+ writeBuiltInFunctionTriplet(visit, "mix(", useEmulatedFunction);
+ break;
+ case EOpStep:
+ writeBuiltInFunctionTriplet(visit, "step(", useEmulatedFunction);
+ break;
+ case EOpSmoothStep:
+ writeBuiltInFunctionTriplet(visit, "smoothstep(", useEmulatedFunction);
+ break;
+ case EOpDistance:
+ writeBuiltInFunctionTriplet(visit, "distance(", useEmulatedFunction);
+ break;
+ case EOpDot:
+ writeBuiltInFunctionTriplet(visit, "dot(", useEmulatedFunction);
+ break;
+ case EOpCross:
+ writeBuiltInFunctionTriplet(visit, "cross(", useEmulatedFunction);
+ break;
+ case EOpFaceForward:
+ writeBuiltInFunctionTriplet(visit, "faceforward(", useEmulatedFunction);
+ break;
+ case EOpReflect:
+ writeBuiltInFunctionTriplet(visit, "reflect(", useEmulatedFunction);
+ break;
+ case EOpRefract:
+ writeBuiltInFunctionTriplet(visit, "refract(", useEmulatedFunction);
+ break;
+ case EOpMul:
+ writeBuiltInFunctionTriplet(visit, "matrixCompMult(", useEmulatedFunction);
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+ return visitChildren;
+}
+
+bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ TInfoSinkBase &out = objSink();
+
+ // Variable declaration.
+ if (visit == PreVisit)
+ {
+ const TIntermSequence &sequence = *(node->getSequence());
+ const TIntermTyped *variable = sequence.front()->getAsTyped();
+ writeLayoutQualifier(variable->getType());
+ writeVariableType(variable->getType());
+ out << " ";
+ mDeclaringVariables = true;
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ mDeclaringVariables = true;
+ }
+ else
+ {
+ mDeclaringVariables = false;
+ }
+ return true;
+}
+
+bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
+{
+ TInfoSinkBase &out = objSink();
+
+ incrementDepth(node);
+
+ TLoopType loopType = node->getType();
+
+ // Only for loops can be unrolled
+ ASSERT(!node->getUnrollFlag() || loopType == ELoopFor);
+
+ if (loopType == ELoopFor) // for loop
+ {
+ if (!node->getUnrollFlag())
+ {
+ out << "for (";
+ if (node->getInit())
+ node->getInit()->traverse(this);
+ out << "; ";
+
+ if (node->getCondition())
+ node->getCondition()->traverse(this);
+ out << "; ";
+
+ if (node->getExpression())
+ node->getExpression()->traverse(this);
+ out << ")\n";
+
+ visitCodeBlock(node->getBody());
+ }
+ else
+ {
+ // Need to put a one-iteration loop here to handle break.
+ TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
+ TIntermSymbol *indexSymbol =
+ (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
+ TString name = hashVariableName(indexSymbol->getName());
+ out << "for (int " << name << " = 0; "
+ << name << " < 1; "
+ << "++" << name << ")\n";
+
+ out << "{\n";
+ mLoopUnrollStack.push(node);
+ while (mLoopUnrollStack.satisfiesLoopCondition())
+ {
+ visitCodeBlock(node->getBody());
+ mLoopUnrollStack.step();
+ }
+ mLoopUnrollStack.pop();
+ out << "}\n";
+ }
+ }
+ else if (loopType == ELoopWhile) // while loop
+ {
+ out << "while (";
+ ASSERT(node->getCondition() != NULL);
+ node->getCondition()->traverse(this);
+ out << ")\n";
+
+ visitCodeBlock(node->getBody());
+ }
+ else // do-while loop
+ {
+ ASSERT(loopType == ELoopDoWhile);
+ out << "do\n";
+
+ visitCodeBlock(node->getBody());
+
+ out << "while (";
+ ASSERT(node->getCondition() != NULL);
+ node->getCondition()->traverse(this);
+ out << ");\n";
+ }
+
+ decrementDepth();
+
+ // No need to visit children. They have been already processed in
+ // this function.
+ return false;
+}
+
+bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch *node)
+{
+ switch (node->getFlowOp())
+ {
+ case EOpKill:
+ writeTriplet(visit, "discard", NULL, NULL);
+ break;
+ case EOpBreak:
+ writeTriplet(visit, "break", NULL, NULL);
+ break;
+ case EOpContinue:
+ writeTriplet(visit, "continue", NULL, NULL);
+ break;
+ case EOpReturn:
+ writeTriplet(visit, "return ", NULL, NULL);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return true;
+}
+
+void TOutputGLSLBase::visitCodeBlock(TIntermBlock *node)
+{
+ TInfoSinkBase &out = objSink();
+ if (node != NULL)
+ {
+ node->traverse(this);
+ // Single statements not part of a sequence need to be terminated
+ // with semi-colon.
+ if (isSingleStatement(node))
+ out << ";\n";
+ }
+ else
+ {
+ out << "{\n}\n"; // Empty code block.
+ }
+}
+
+TString TOutputGLSLBase::getTypeName(const TType &type)
+{
+ if (type.getBasicType() == EbtStruct)
+ return hashName(TName(type.getStruct()->name()));
+ else
+ return type.getBuiltInTypeNameString();
+}
+
+TString TOutputGLSLBase::hashName(const TName &name)
+{
+ if (name.getString().empty())
+ {
+ ASSERT(!name.isInternal());
+ return name.getString();
+ }
+ if (name.isInternal())
+ {
+ // TODO(oetuaho): Would be nicer to prefix non-internal names with "_" instead, like is
+ // done in the HLSL output, but that requires fairly complex changes elsewhere in the code
+ // as well.
+ // We need to use a prefix that is reserved in WebGL in order to guarantee that the internal
+ // names don't conflict with user-defined names from WebGL.
+ return "webgl_angle_" + name.getString();
+ }
+ if (mHashFunction == nullptr)
+ {
+ return name.getString();
+ }
+ NameMap::const_iterator it = mNameMap.find(name.getString().c_str());
+ if (it != mNameMap.end())
+ return it->second.c_str();
+ TString hashedName = TIntermTraverser::hash(name.getString(), mHashFunction);
+ mNameMap[name.getString().c_str()] = hashedName.c_str();
+ return hashedName;
+}
+
+TString TOutputGLSLBase::hashVariableName(const TName &name)
+{
+ if (mSymbolTable.findBuiltIn(name.getString(), mShaderVersion) != NULL)
+ return name.getString();
+ return hashName(name);
+}
+
+TString TOutputGLSLBase::hashFunctionNameIfNeeded(const TName &mangledName)
+{
+ TString mangledStr = mangledName.getString();
+ TString name = TFunction::unmangleName(mangledStr);
+ if (mSymbolTable.findBuiltIn(mangledStr, mShaderVersion) != nullptr || name == "main")
+ return translateTextureFunction(name);
+ if (mangledName.isInternal())
+ {
+ // Internal function names are outputted as-is - they may refer to functions manually added
+ // to the output shader source that are not included in the AST at all.
+ return name;
+ }
+ else
+ {
+ TName nameObj(name);
+ return hashName(nameObj);
+ }
+}
+
+bool TOutputGLSLBase::structDeclared(const TStructure *structure) const
+{
+ ASSERT(structure);
+ if (structure->name().empty())
+ {
+ return false;
+ }
+
+ return (mDeclaredStructs.count(structure->uniqueId()) > 0);
+}
+
+void TOutputGLSLBase::declareStruct(const TStructure *structure)
+{
+ TInfoSinkBase &out = objSink();
+
+ out << "struct " << hashName(TName(structure->name())) << "{\n";
+ const TFieldList &fields = structure->fields();
+ for (size_t i = 0; i < fields.size(); ++i)
+ {
+ const TField *field = fields[i];
+ if (writeVariablePrecision(field->type()->getPrecision()))
+ out << " ";
+ out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
+ if (field->type()->isArray())
+ out << arrayBrackets(*field->type());
+ out << ";\n";
+ }
+ out << "}";
+}
+
+void TOutputGLSLBase::declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock)
+{
+ TInfoSinkBase &out = objSink();
+
+ out << "layout(";
+
+ switch (interfaceBlock->blockStorage())
+ {
+ case EbsUnspecified:
+ case EbsShared:
+ // Default block storage is shared.
+ out << "shared";
+ break;
+
+ case EbsPacked:
+ out << "packed";
+ break;
+
+ case EbsStd140:
+ out << "std140";
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ out << ", ";
+
+ switch (interfaceBlock->matrixPacking())
+ {
+ case EmpUnspecified:
+ case EmpColumnMajor:
+ // Default matrix packing is column major.
+ out << "column_major";
+ break;
+
+ case EmpRowMajor:
+ out << "row_major";
+ break;
+
+ default:
+ UNREACHABLE();
+ break;
+ }
+
+ out << ") ";
+}
+
+void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBlock)
+{
+ TInfoSinkBase &out = objSink();
+
+ out << hashName(TName(interfaceBlock->name())) << "{\n";
+ const TFieldList &fields = interfaceBlock->fields();
+ for (size_t i = 0; i < fields.size(); ++i)
+ {
+ const TField *field = fields[i];
+ if (writeVariablePrecision(field->type()->getPrecision()))
+ out << " ";
+ out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
+ if (field->type()->isArray())
+ out << arrayBrackets(*field->type());
+ out << ";\n";
+ }
+ out << "}";
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/OutputGLSLBase.h b/gfx/angle/src/compiler/translator/OutputGLSLBase.h
new file mode 100755
index 000000000..ede4c4925
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputGLSLBase.h
@@ -0,0 +1,117 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
+#define COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
+
+#include <set>
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/LoopInfo.h"
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
+
+class TOutputGLSLBase : public TIntermTraverser
+{
+ public:
+ TOutputGLSLBase(TInfoSinkBase &objSink,
+ ShArrayIndexClampingStrategy clampingStrategy,
+ ShHashFunction64 hashFunction,
+ NameMap &nameMap,
+ TSymbolTable &symbolTable,
+ sh::GLenum shaderType,
+ int shaderVersion,
+ ShShaderOutput output,
+ ShCompileOptions compileOptions);
+
+ ShShaderOutput getShaderOutput() const
+ {
+ return mOutput;
+ }
+
+ protected:
+ TInfoSinkBase &objSink() { return mObjSink; }
+ void writeTriplet(Visit visit, const char *preStr, const char *inStr, const char *postStr);
+ void writeLayoutQualifier(const TType &type);
+ void writeInvariantQualifier(const TType &type);
+ void writeVariableType(const TType &type);
+ virtual bool writeVariablePrecision(TPrecision precision) = 0;
+ void writeFunctionParameters(const TIntermSequence &args);
+ const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *pConstUnion);
+ void writeConstructorTriplet(Visit visit, const TType &type);
+ TString getTypeName(const TType &type);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ void visitConstantUnion(TIntermConstantUnion *node) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+ bool visitCase(Visit visit, TIntermCase *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitLoop(Visit visit, TIntermLoop *node) override;
+ bool visitBranch(Visit visit, TIntermBranch *node) override;
+
+ void visitCodeBlock(TIntermBlock *node);
+
+ // Return the original name if hash function pointer is NULL;
+ // otherwise return the hashed name.
+ TString hashName(const TName &name);
+ // Same as hashName(), but without hashing built-in variables.
+ TString hashVariableName(const TName &name);
+ // Same as hashName(), but without hashing built-in functions and with unmangling.
+ TString hashFunctionNameIfNeeded(const TName &mangledName);
+ // Used to translate function names for differences between ESSL and GLSL
+ virtual TString translateTextureFunction(TString &name) { return name; }
+
+ private:
+ bool structDeclared(const TStructure *structure) const;
+ void declareStruct(const TStructure *structure);
+
+ void declareInterfaceBlockLayout(const TInterfaceBlock *interfaceBlock);
+ void declareInterfaceBlock(const TInterfaceBlock *interfaceBlock);
+
+ void writeBuiltInFunctionTriplet(Visit visit, const char *preStr, bool useEmulatedFunction);
+
+ const char *mapQualifierToString(TQualifier qialifier);
+
+ TInfoSinkBase &mObjSink;
+ bool mDeclaringVariables;
+
+ // This set contains all the ids of the structs from every scope.
+ std::set<int> mDeclaredStructs;
+
+ // Stack of loops that need to be unrolled.
+ TLoopStack mLoopUnrollStack;
+
+ ShArrayIndexClampingStrategy mClampingStrategy;
+
+ // name hashing.
+ ShHashFunction64 mHashFunction;
+
+ NameMap &mNameMap;
+
+ TSymbolTable &mSymbolTable;
+
+ sh::GLenum mShaderType;
+
+ const int mShaderVersion;
+
+ ShShaderOutput mOutput;
+
+ ShCompileOptions mCompileOptions;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.cpp b/gfx/angle/src/compiler/translator/OutputHLSL.cpp
new file mode 100755
index 000000000..5ef2e89f9
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp
@@ -0,0 +1,2857 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/OutputHLSL.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <stdio.h>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "common/utilities.h"
+#include "compiler/translator/BuiltInFunctionEmulator.h"
+#include "compiler/translator/BuiltInFunctionEmulatorHLSL.h"
+#include "compiler/translator/FlagStd140Structs.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/NodeSearch.h"
+#include "compiler/translator/RemoveSwitchFallThrough.h"
+#include "compiler/translator/SearchSymbol.h"
+#include "compiler/translator/StructureHLSL.h"
+#include "compiler/translator/TextureFunctionHLSL.h"
+#include "compiler/translator/TranslatorHLSL.h"
+#include "compiler/translator/UniformHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+#include "compiler/translator/blocklayout.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void WriteSingleConstant(TInfoSinkBase &out, const TConstantUnion *const constUnion)
+{
+ ASSERT(constUnion != nullptr);
+ switch (constUnion->getType())
+ {
+ case EbtFloat:
+ out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst()));
+ break;
+ case EbtInt:
+ out << constUnion->getIConst();
+ break;
+ case EbtUInt:
+ out << constUnion->getUConst();
+ break;
+ case EbtBool:
+ out << constUnion->getBConst();
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+const TConstantUnion *WriteConstantUnionArray(TInfoSinkBase &out,
+ const TConstantUnion *const constUnion,
+ const size_t size)
+{
+ const TConstantUnion *constUnionIterated = constUnion;
+ for (size_t i = 0; i < size; i++, constUnionIterated++)
+ {
+ WriteSingleConstant(out, constUnionIterated);
+
+ if (i != size - 1)
+ {
+ out << ", ";
+ }
+ }
+ return constUnionIterated;
+}
+
+} // namespace
+
+OutputHLSL::OutputHLSL(sh::GLenum shaderType,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ const char *sourcePath,
+ ShShaderOutput outputType,
+ int numRenderTargets,
+ const std::vector<Uniform> &uniforms,
+ ShCompileOptions compileOptions)
+ : TIntermTraverser(true, true, true),
+ mShaderType(shaderType),
+ mShaderVersion(shaderVersion),
+ mExtensionBehavior(extensionBehavior),
+ mSourcePath(sourcePath),
+ mOutputType(outputType),
+ mCompileOptions(compileOptions),
+ mNumRenderTargets(numRenderTargets),
+ mCurrentFunctionMetadata(nullptr)
+{
+ mInsideFunction = false;
+
+ mUsesFragColor = false;
+ mUsesFragData = false;
+ mUsesDepthRange = false;
+ mUsesFragCoord = false;
+ mUsesPointCoord = false;
+ mUsesFrontFacing = false;
+ mUsesPointSize = false;
+ mUsesInstanceID = false;
+ mUsesVertexID = false;
+ mUsesFragDepth = false;
+ mUsesXor = false;
+ mUsesDiscardRewriting = false;
+ mUsesNestedBreak = false;
+ mRequiresIEEEStrictCompiling = false;
+
+ mUniqueIndex = 0;
+
+ mOutputLod0Function = false;
+ mInsideDiscontinuousLoop = false;
+ mNestedLoopDepth = 0;
+
+ mExcessiveLoopIndex = NULL;
+
+ mStructureHLSL = new StructureHLSL;
+ mUniformHLSL = new UniformHLSL(mStructureHLSL, outputType, uniforms);
+ mTextureFunctionHLSL = new TextureFunctionHLSL;
+
+ if (mOutputType == SH_HLSL_3_0_OUTPUT)
+ {
+ // Fragment shaders need dx_DepthRange, dx_ViewCoords and dx_DepthFront.
+ // Vertex shaders need a slightly different set: dx_DepthRange, dx_ViewCoords and dx_ViewAdjust.
+ // In both cases total 3 uniform registers need to be reserved.
+ mUniformHLSL->reserveUniformRegisters(3);
+ }
+
+ // Reserve registers for the default uniform block and driver constants
+ mUniformHLSL->reserveInterfaceBlockRegisters(2);
+}
+
+OutputHLSL::~OutputHLSL()
+{
+ SafeDelete(mStructureHLSL);
+ SafeDelete(mUniformHLSL);
+ SafeDelete(mTextureFunctionHLSL);
+ for (auto &eqFunction : mStructEqualityFunctions)
+ {
+ SafeDelete(eqFunction);
+ }
+ for (auto &eqFunction : mArrayEqualityFunctions)
+ {
+ SafeDelete(eqFunction);
+ }
+}
+
+void OutputHLSL::output(TIntermNode *treeRoot, TInfoSinkBase &objSink)
+{
+ const std::vector<TIntermTyped*> &flaggedStructs = FlagStd140ValueStructs(treeRoot);
+ makeFlaggedStructMaps(flaggedStructs);
+
+ BuiltInFunctionEmulator builtInFunctionEmulator;
+ InitBuiltInFunctionEmulatorForHLSL(&builtInFunctionEmulator);
+ if ((mCompileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION) != 0)
+ {
+ InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(&builtInFunctionEmulator,
+ mShaderVersion);
+ }
+
+ builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(treeRoot);
+
+ // Now that we are done changing the AST, do the analyses need for HLSL generation
+ CallDAG::InitResult success = mCallDag.init(treeRoot, &objSink);
+ ASSERT(success == CallDAG::INITDAG_SUCCESS);
+ mASTMetadataList = CreateASTMetadataHLSL(treeRoot, mCallDag);
+
+ // Output the body and footer first to determine what has to go in the header
+ mInfoSinkStack.push(&mBody);
+ treeRoot->traverse(this);
+ mInfoSinkStack.pop();
+
+ mInfoSinkStack.push(&mFooter);
+ mInfoSinkStack.pop();
+
+ mInfoSinkStack.push(&mHeader);
+ header(mHeader, &builtInFunctionEmulator);
+ mInfoSinkStack.pop();
+
+ objSink << mHeader.c_str();
+ objSink << mBody.c_str();
+ objSink << mFooter.c_str();
+
+ builtInFunctionEmulator.Cleanup();
+}
+
+void OutputHLSL::makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs)
+{
+ for (unsigned int structIndex = 0; structIndex < flaggedStructs.size(); structIndex++)
+ {
+ TIntermTyped *flaggedNode = flaggedStructs[structIndex];
+
+ TInfoSinkBase structInfoSink;
+ mInfoSinkStack.push(&structInfoSink);
+
+ // This will mark the necessary block elements as referenced
+ flaggedNode->traverse(this);
+
+ TString structName(structInfoSink.c_str());
+ mInfoSinkStack.pop();
+
+ mFlaggedStructOriginalNames[flaggedNode] = structName;
+
+ for (size_t pos = structName.find('.'); pos != std::string::npos; pos = structName.find('.'))
+ {
+ structName.erase(pos, 1);
+ }
+
+ mFlaggedStructMappedNames[flaggedNode] = "map" + structName;
+ }
+}
+
+const std::map<std::string, unsigned int> &OutputHLSL::getInterfaceBlockRegisterMap() const
+{
+ return mUniformHLSL->getInterfaceBlockRegisterMap();
+}
+
+const std::map<std::string, unsigned int> &OutputHLSL::getUniformRegisterMap() const
+{
+ return mUniformHLSL->getUniformRegisterMap();
+}
+
+int OutputHLSL::vectorSize(const TType &type) const
+{
+ int elementSize = type.isMatrix() ? type.getCols() : 1;
+ unsigned int arraySize = type.isArray() ? type.getArraySize() : 1u;
+
+ return elementSize * arraySize;
+}
+
+TString OutputHLSL::structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName)
+{
+ TString init;
+
+ TString preIndentString;
+ TString fullIndentString;
+
+ for (int spaces = 0; spaces < (indent * 4); spaces++)
+ {
+ preIndentString += ' ';
+ }
+
+ for (int spaces = 0; spaces < ((indent+1) * 4); spaces++)
+ {
+ fullIndentString += ' ';
+ }
+
+ init += preIndentString + "{\n";
+
+ const TFieldList &fields = structure.fields();
+ for (unsigned int fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+ {
+ const TField &field = *fields[fieldIndex];
+ const TString &fieldName = rhsStructName + "." + Decorate(field.name());
+ const TType &fieldType = *field.type();
+
+ if (fieldType.getStruct())
+ {
+ init += structInitializerString(indent + 1, *fieldType.getStruct(), fieldName);
+ }
+ else
+ {
+ init += fullIndentString + fieldName + ",\n";
+ }
+ }
+
+ init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n";
+
+ return init;
+}
+
+void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator)
+{
+ TString varyings;
+ TString attributes;
+ TString flaggedStructs;
+
+ for (std::map<TIntermTyped*, TString>::const_iterator flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++)
+ {
+ TIntermTyped *structNode = flaggedStructIt->first;
+ const TString &mappedName = flaggedStructIt->second;
+ const TStructure &structure = *structNode->getType().getStruct();
+ const TString &originalName = mFlaggedStructOriginalNames[structNode];
+
+ flaggedStructs += "static " + Decorate(structure.name()) + " " + mappedName + " =\n";
+ flaggedStructs += structInitializerString(0, structure, originalName);
+ flaggedStructs += "\n";
+ }
+
+ for (ReferencedSymbols::const_iterator varying = mReferencedVaryings.begin(); varying != mReferencedVaryings.end(); varying++)
+ {
+ const TType &type = varying->second->getType();
+ const TString &name = varying->second->getSymbol();
+
+ // Program linking depends on this exact format
+ varyings += "static " + InterpolationString(type.getQualifier()) + " " + TypeString(type) + " " +
+ Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
+ }
+
+ for (ReferencedSymbols::const_iterator attribute = mReferencedAttributes.begin(); attribute != mReferencedAttributes.end(); attribute++)
+ {
+ const TType &type = attribute->second->getType();
+ const TString &name = attribute->second->getSymbol();
+
+ attributes += "static " + TypeString(type) + " " + Decorate(name) + ArrayString(type) + " = " + initializer(type) + ";\n";
+ }
+
+ out << mStructureHLSL->structsHeader();
+
+ mUniformHLSL->uniformsHeader(out, mOutputType, mReferencedUniforms);
+ out << mUniformHLSL->interfaceBlocksHeader(mReferencedInterfaceBlocks);
+
+ if (!mEqualityFunctions.empty())
+ {
+ out << "\n// Equality functions\n\n";
+ for (const auto &eqFunction : mEqualityFunctions)
+ {
+ out << eqFunction->functionDefinition << "\n";
+ }
+ }
+ if (!mArrayAssignmentFunctions.empty())
+ {
+ out << "\n// Assignment functions\n\n";
+ for (const auto &assignmentFunction : mArrayAssignmentFunctions)
+ {
+ out << assignmentFunction.functionDefinition << "\n";
+ }
+ }
+ if (!mArrayConstructIntoFunctions.empty())
+ {
+ out << "\n// Array constructor functions\n\n";
+ for (const auto &constructIntoFunction : mArrayConstructIntoFunctions)
+ {
+ out << constructIntoFunction.functionDefinition << "\n";
+ }
+ }
+
+ if (mUsesDiscardRewriting)
+ {
+ out << "#define ANGLE_USES_DISCARD_REWRITING\n";
+ }
+
+ if (mUsesNestedBreak)
+ {
+ out << "#define ANGLE_USES_NESTED_BREAK\n";
+ }
+
+ if (mRequiresIEEEStrictCompiling)
+ {
+ out << "#define ANGLE_REQUIRES_IEEE_STRICT_COMPILING\n";
+ }
+
+ out << "#ifdef ANGLE_ENABLE_LOOP_FLATTEN\n"
+ "#define LOOP [loop]\n"
+ "#define FLATTEN [flatten]\n"
+ "#else\n"
+ "#define LOOP\n"
+ "#define FLATTEN\n"
+ "#endif\n";
+
+ if (mShaderType == GL_FRAGMENT_SHADER)
+ {
+ TExtensionBehavior::const_iterator iter = mExtensionBehavior.find("GL_EXT_draw_buffers");
+ const bool usingMRTExtension = (iter != mExtensionBehavior.end() && (iter->second == EBhEnable || iter->second == EBhRequire));
+
+ out << "// Varyings\n";
+ out << varyings;
+ out << "\n";
+
+ if (mShaderVersion >= 300)
+ {
+ for (ReferencedSymbols::const_iterator outputVariableIt = mReferencedOutputVariables.begin(); outputVariableIt != mReferencedOutputVariables.end(); outputVariableIt++)
+ {
+ const TString &variableName = outputVariableIt->first;
+ const TType &variableType = outputVariableIt->second->getType();
+
+ out << "static " + TypeString(variableType) + " out_" + variableName + ArrayString(variableType) +
+ " = " + initializer(variableType) + ";\n";
+ }
+ }
+ else
+ {
+ const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1;
+
+ out << "static float4 gl_Color[" << numColorValues << "] =\n"
+ "{\n";
+ for (unsigned int i = 0; i < numColorValues; i++)
+ {
+ out << " float4(0, 0, 0, 0)";
+ if (i + 1 != numColorValues)
+ {
+ out << ",";
+ }
+ out << "\n";
+ }
+
+ out << "};\n";
+ }
+
+ if (mUsesFragDepth)
+ {
+ out << "static float gl_Depth = 0.0;\n";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << "static float4 gl_FragCoord = float4(0, 0, 0, 0);\n";
+ }
+
+ if (mUsesPointCoord)
+ {
+ out << "static float2 gl_PointCoord = float2(0.5, 0.5);\n";
+ }
+
+ if (mUsesFrontFacing)
+ {
+ out << "static bool gl_FrontFacing = false;\n";
+ }
+
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "struct gl_DepthRangeParameters\n"
+ "{\n"
+ " float near;\n"
+ " float far;\n"
+ " float diff;\n"
+ "};\n"
+ "\n";
+ }
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ out << "cbuffer DriverConstants : register(b1)\n"
+ "{\n";
+
+ if (mUsesDepthRange)
+ {
+ out << " float3 dx_DepthRange : packoffset(c0);\n";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << " float4 dx_ViewCoords : packoffset(c1);\n";
+ }
+
+ if (mUsesFragCoord || mUsesFrontFacing)
+ {
+ out << " float3 dx_DepthFront : packoffset(c2);\n";
+ }
+
+ if (mUsesFragCoord)
+ {
+ // dx_ViewScale is only used in the fragment shader to correct
+ // the value for glFragCoord if necessary
+ out << " float2 dx_ViewScale : packoffset(c3);\n";
+ }
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ mUniformHLSL->samplerMetadataUniforms(out, "c4");
+ }
+
+ out << "};\n";
+ }
+ else
+ {
+ if (mUsesDepthRange)
+ {
+ out << "uniform float3 dx_DepthRange : register(c0);";
+ }
+
+ if (mUsesFragCoord)
+ {
+ out << "uniform float4 dx_ViewCoords : register(c1);\n";
+ }
+
+ if (mUsesFragCoord || mUsesFrontFacing)
+ {
+ out << "uniform float3 dx_DepthFront : register(c2);\n";
+ }
+ }
+
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ "\n";
+ }
+
+ if (!flaggedStructs.empty())
+ {
+ out << "// Std140 Structures accessed by value\n";
+ out << "\n";
+ out << flaggedStructs;
+ out << "\n";
+ }
+
+ if (usingMRTExtension && mNumRenderTargets > 1)
+ {
+ out << "#define GL_USES_MRT\n";
+ }
+
+ if (mUsesFragColor)
+ {
+ out << "#define GL_USES_FRAG_COLOR\n";
+ }
+
+ if (mUsesFragData)
+ {
+ out << "#define GL_USES_FRAG_DATA\n";
+ }
+ }
+ else // Vertex shader
+ {
+ out << "// Attributes\n";
+ out << attributes;
+ out << "\n"
+ "static float4 gl_Position = float4(0, 0, 0, 0);\n";
+
+ if (mUsesPointSize)
+ {
+ out << "static float gl_PointSize = float(1);\n";
+ }
+
+ if (mUsesInstanceID)
+ {
+ out << "static int gl_InstanceID;";
+ }
+
+ if (mUsesVertexID)
+ {
+ out << "static int gl_VertexID;";
+ }
+
+ out << "\n"
+ "// Varyings\n";
+ out << varyings;
+ out << "\n";
+
+ if (mUsesDepthRange)
+ {
+ out << "struct gl_DepthRangeParameters\n"
+ "{\n"
+ " float near;\n"
+ " float far;\n"
+ " float diff;\n"
+ "};\n"
+ "\n";
+ }
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT || mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ out << "cbuffer DriverConstants : register(b1)\n"
+ "{\n";
+
+ if (mUsesDepthRange)
+ {
+ out << " float3 dx_DepthRange : packoffset(c0);\n";
+ }
+
+ // dx_ViewAdjust and dx_ViewCoords will only be used in Feature Level 9
+ // shaders. However, we declare it for all shaders (including Feature Level 10+).
+ // The bytecode is the same whether we declare it or not, since D3DCompiler removes it
+ // if it's unused.
+ out << " float4 dx_ViewAdjust : packoffset(c1);\n";
+ out << " float2 dx_ViewCoords : packoffset(c2);\n";
+ out << " float2 dx_ViewScale : packoffset(c3);\n";
+
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ mUniformHLSL->samplerMetadataUniforms(out, "c4");
+ }
+
+ out << "};\n"
+ "\n";
+ }
+ else
+ {
+ if (mUsesDepthRange)
+ {
+ out << "uniform float3 dx_DepthRange : register(c0);\n";
+ }
+
+ out << "uniform float4 dx_ViewAdjust : register(c1);\n";
+ out << "uniform float2 dx_ViewCoords : register(c2);\n"
+ "\n";
+ }
+
+ if (mUsesDepthRange)
+ {
+ out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n"
+ "\n";
+ }
+
+ if (!flaggedStructs.empty())
+ {
+ out << "// Std140 Structures accessed by value\n";
+ out << "\n";
+ out << flaggedStructs;
+ out << "\n";
+ }
+ }
+
+ bool getDimensionsIgnoresBaseLevel =
+ (mCompileOptions & SH_HLSL_GET_DIMENSIONS_IGNORES_BASE_LEVEL) != 0;
+ mTextureFunctionHLSL->textureFunctionHeader(out, mOutputType, getDimensionsIgnoresBaseLevel);
+
+ if (mUsesFragCoord)
+ {
+ out << "#define GL_USES_FRAG_COORD\n";
+ }
+
+ if (mUsesPointCoord)
+ {
+ out << "#define GL_USES_POINT_COORD\n";
+ }
+
+ if (mUsesFrontFacing)
+ {
+ out << "#define GL_USES_FRONT_FACING\n";
+ }
+
+ if (mUsesPointSize)
+ {
+ out << "#define GL_USES_POINT_SIZE\n";
+ }
+
+ if (mUsesFragDepth)
+ {
+ out << "#define GL_USES_FRAG_DEPTH\n";
+ }
+
+ if (mUsesDepthRange)
+ {
+ out << "#define GL_USES_DEPTH_RANGE\n";
+ }
+
+ if (mUsesXor)
+ {
+ out << "bool xor(bool p, bool q)\n"
+ "{\n"
+ " return (p || q) && !(p && q);\n"
+ "}\n"
+ "\n";
+ }
+
+ builtInFunctionEmulator->OutputEmulatedFunctions(out);
+}
+
+void OutputHLSL::visitSymbol(TIntermSymbol *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ // Handle accessing std140 structs by value
+ if (mFlaggedStructMappedNames.count(node) > 0)
+ {
+ out << mFlaggedStructMappedNames[node];
+ return;
+ }
+
+ TString name = node->getSymbol();
+
+ if (name == "gl_DepthRange")
+ {
+ mUsesDepthRange = true;
+ out << name;
+ }
+ else
+ {
+ TQualifier qualifier = node->getQualifier();
+
+ if (qualifier == EvqUniform)
+ {
+ const TType &nodeType = node->getType();
+ const TInterfaceBlock *interfaceBlock = nodeType.getInterfaceBlock();
+
+ if (interfaceBlock)
+ {
+ mReferencedInterfaceBlocks[interfaceBlock->name()] = node;
+ }
+ else
+ {
+ mReferencedUniforms[name] = node;
+ }
+
+ ensureStructDefined(nodeType);
+
+ const TName &nameWithMetadata = node->getName();
+ out << DecorateUniform(nameWithMetadata, nodeType);
+ }
+ else if (qualifier == EvqAttribute || qualifier == EvqVertexIn)
+ {
+ mReferencedAttributes[name] = node;
+ out << Decorate(name);
+ }
+ else if (IsVarying(qualifier))
+ {
+ mReferencedVaryings[name] = node;
+ out << Decorate(name);
+ }
+ else if (qualifier == EvqFragmentOut)
+ {
+ mReferencedOutputVariables[name] = node;
+ out << "out_" << name;
+ }
+ else if (qualifier == EvqFragColor)
+ {
+ out << "gl_Color[0]";
+ mUsesFragColor = true;
+ }
+ else if (qualifier == EvqFragData)
+ {
+ out << "gl_Color";
+ mUsesFragData = true;
+ }
+ else if (qualifier == EvqFragCoord)
+ {
+ mUsesFragCoord = true;
+ out << name;
+ }
+ else if (qualifier == EvqPointCoord)
+ {
+ mUsesPointCoord = true;
+ out << name;
+ }
+ else if (qualifier == EvqFrontFacing)
+ {
+ mUsesFrontFacing = true;
+ out << name;
+ }
+ else if (qualifier == EvqPointSize)
+ {
+ mUsesPointSize = true;
+ out << name;
+ }
+ else if (qualifier == EvqInstanceID)
+ {
+ mUsesInstanceID = true;
+ out << name;
+ }
+ else if (qualifier == EvqVertexID)
+ {
+ mUsesVertexID = true;
+ out << name;
+ }
+ else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth")
+ {
+ mUsesFragDepth = true;
+ out << "gl_Depth";
+ }
+ else
+ {
+ out << DecorateIfNeeded(node->getName());
+ }
+ }
+}
+
+void OutputHLSL::visitRaw(TIntermRaw *node)
+{
+ getInfoSink() << node->getRawText();
+}
+
+void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out)
+{
+ if (type.isScalar() && !type.isArray())
+ {
+ if (op == EOpEqual)
+ {
+ outputTriplet(out, visit, "(", " == ", ")");
+ }
+ else
+ {
+ outputTriplet(out, visit, "(", " != ", ")");
+ }
+ }
+ else
+ {
+ if (visit == PreVisit && op == EOpNotEqual)
+ {
+ out << "!";
+ }
+
+ if (type.isArray())
+ {
+ const TString &functionName = addArrayEqualityFunction(type);
+ outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
+ }
+ else if (type.getBasicType() == EbtStruct)
+ {
+ const TStructure &structure = *type.getStruct();
+ const TString &functionName = addStructEqualityFunction(structure);
+ outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
+ }
+ else
+ {
+ ASSERT(type.isMatrix() || type.isVector());
+ outputTriplet(out, visit, "all(", " == ", ")");
+ }
+ }
+}
+
+bool OutputHLSL::ancestorEvaluatesToSamplerInStruct(Visit visit)
+{
+ // Inside InVisit the current node is already in the path.
+ const unsigned int initialN = visit == InVisit ? 1u : 0u;
+ for (unsigned int n = initialN; getAncestorNode(n) != nullptr; ++n)
+ {
+ TIntermNode *ancestor = getAncestorNode(n);
+ const TIntermBinary *ancestorBinary = ancestor->getAsBinaryNode();
+ if (ancestorBinary == nullptr)
+ {
+ return false;
+ }
+ switch (ancestorBinary->getOp())
+ {
+ case EOpIndexDirectStruct:
+ {
+ const TStructure *structure = ancestorBinary->getLeft()->getType().getStruct();
+ const TIntermConstantUnion *index =
+ ancestorBinary->getRight()->getAsConstantUnion();
+ const TField *field = structure->fields()[index->getIConst(0)];
+ if (IsSampler(field->type()->getBasicType()))
+ {
+ return true;
+ }
+ break;
+ }
+ case EOpIndexDirect:
+ break;
+ default:
+ // Returning a sampler from indirect indexing is not supported.
+ return false;
+ }
+ }
+ return false;
+}
+
+bool OutputHLSL::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+ if (visit == PostVisit)
+ {
+ out << ".";
+ node->writeOffsetsAsXYZW(&out);
+ }
+ return true;
+}
+
+bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ // Handle accessing std140 structs by value
+ if (mFlaggedStructMappedNames.count(node) > 0)
+ {
+ out << mFlaggedStructMappedNames[node];
+ return false;
+ }
+
+ switch (node->getOp())
+ {
+ case EOpComma:
+ outputTriplet(out, visit, "(", ", ", ")");
+ break;
+ case EOpAssign:
+ if (node->getLeft()->isArray())
+ {
+ TIntermAggregate *rightAgg = node->getRight()->getAsAggregate();
+ if (rightAgg != nullptr && rightAgg->isConstructor())
+ {
+ const TString &functionName = addArrayConstructIntoFunction(node->getType());
+ out << functionName << "(";
+ node->getLeft()->traverse(this);
+ TIntermSequence *seq = rightAgg->getSequence();
+ for (auto &arrayElement : *seq)
+ {
+ out << ", ";
+ arrayElement->traverse(this);
+ }
+ out << ")";
+ return false;
+ }
+ // ArrayReturnValueToOutParameter should have eliminated expressions where a
+ // function call is assigned.
+ ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall);
+
+ const TString &functionName = addArrayAssignmentFunction(node->getType());
+ outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
+ }
+ else
+ {
+ outputTriplet(out, visit, "(", " = ", ")");
+ }
+ break;
+ case EOpInitialize:
+ if (visit == PreVisit)
+ {
+ TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
+ ASSERT(symbolNode);
+ TIntermTyped *expression = node->getRight();
+
+ // Global initializers must be constant at this point.
+ ASSERT(symbolNode->getQualifier() != EvqGlobal ||
+ canWriteAsHLSLLiteral(expression));
+
+ // GLSL allows to write things like "float x = x;" where a new variable x is defined
+ // and the value of an existing variable x is assigned. HLSL uses C semantics (the
+ // new variable is created before the assignment is evaluated), so we need to
+ // convert
+ // this to "float t = x, x = t;".
+ if (writeSameSymbolInitializer(out, symbolNode, expression))
+ {
+ // Skip initializing the rest of the expression
+ return false;
+ }
+ else if (writeConstantInitialization(out, symbolNode, expression))
+ {
+ return false;
+ }
+ }
+ else if (visit == InVisit)
+ {
+ out << " = ";
+ }
+ break;
+ case EOpAddAssign:
+ outputTriplet(out, visit, "(", " += ", ")");
+ break;
+ case EOpSubAssign:
+ outputTriplet(out, visit, "(", " -= ", ")");
+ break;
+ case EOpMulAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpVectorTimesScalarAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpMatrixTimesScalarAssign:
+ outputTriplet(out, visit, "(", " *= ", ")");
+ break;
+ case EOpVectorTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = mul(";
+ node->getLeft()->traverse(this);
+ out << ", transpose(";
+ }
+ else
+ {
+ out << ")))";
+ }
+ break;
+ case EOpMatrixTimesMatrixAssign:
+ if (visit == PreVisit)
+ {
+ out << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << " = transpose(mul(transpose(";
+ node->getLeft()->traverse(this);
+ out << "), transpose(";
+ }
+ else
+ {
+ out << "))))";
+ }
+ break;
+ case EOpDivAssign:
+ outputTriplet(out, visit, "(", " /= ", ")");
+ break;
+ case EOpIModAssign:
+ outputTriplet(out, visit, "(", " %= ", ")");
+ break;
+ case EOpBitShiftLeftAssign:
+ outputTriplet(out, visit, "(", " <<= ", ")");
+ break;
+ case EOpBitShiftRightAssign:
+ outputTriplet(out, visit, "(", " >>= ", ")");
+ break;
+ case EOpBitwiseAndAssign:
+ outputTriplet(out, visit, "(", " &= ", ")");
+ break;
+ case EOpBitwiseXorAssign:
+ outputTriplet(out, visit, "(", " ^= ", ")");
+ break;
+ case EOpBitwiseOrAssign:
+ outputTriplet(out, visit, "(", " |= ", ")");
+ break;
+ case EOpIndexDirect:
+ {
+ const TType& leftType = node->getLeft()->getType();
+ if (leftType.isInterfaceBlock())
+ {
+ if (visit == PreVisit)
+ {
+ TInterfaceBlock* interfaceBlock = leftType.getInterfaceBlock();
+ const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
+ mReferencedInterfaceBlocks[interfaceBlock->instanceName()] = node->getLeft()->getAsSymbolNode();
+ out << mUniformHLSL->interfaceBlockInstanceString(*interfaceBlock, arrayIndex);
+ return false;
+ }
+ }
+ else if (ancestorEvaluatesToSamplerInStruct(visit))
+ {
+ // All parts of an expression that access a sampler in a struct need to use _ as
+ // separator to access the sampler variable that has been moved out of the struct.
+ outputTriplet(out, visit, "", "_", "");
+ }
+ else
+ {
+ outputTriplet(out, visit, "", "[", "]");
+ }
+ }
+ break;
+ case EOpIndexIndirect:
+ // We do not currently support indirect references to interface blocks
+ ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
+ outputTriplet(out, visit, "", "[", "]");
+ break;
+ case EOpIndexDirectStruct:
+ {
+ const TStructure* structure = node->getLeft()->getType().getStruct();
+ const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
+ const TField* field = structure->fields()[index->getIConst(0)];
+
+ // In cases where indexing returns a sampler, we need to access the sampler variable
+ // that has been moved out of the struct.
+ bool indexingReturnsSampler = IsSampler(field->type()->getBasicType());
+ if (visit == PreVisit && indexingReturnsSampler)
+ {
+ // Samplers extracted from structs have "angle" prefix to avoid name conflicts.
+ // This prefix is only output at the beginning of the indexing expression, which
+ // may have multiple parts.
+ out << "angle";
+ }
+ if (!indexingReturnsSampler)
+ {
+ // All parts of an expression that access a sampler in a struct need to use _ as
+ // separator to access the sampler variable that has been moved out of the struct.
+ indexingReturnsSampler = ancestorEvaluatesToSamplerInStruct(visit);
+ }
+ if (visit == InVisit)
+ {
+ if (indexingReturnsSampler)
+ {
+ out << "_" + field->name();
+ }
+ else
+ {
+ out << "." + DecorateField(field->name(), *structure);
+ }
+
+ return false;
+ }
+ }
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ if (visit == InVisit)
+ {
+ const TInterfaceBlock* interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
+ const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
+ const TField* field = interfaceBlock->fields()[index->getIConst(0)];
+ out << "." + Decorate(field->name());
+
+ return false;
+ }
+ break;
+ case EOpAdd:
+ outputTriplet(out, visit, "(", " + ", ")");
+ break;
+ case EOpSub:
+ outputTriplet(out, visit, "(", " - ", ")");
+ break;
+ case EOpMul:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpDiv:
+ outputTriplet(out, visit, "(", " / ", ")");
+ break;
+ case EOpIMod:
+ outputTriplet(out, visit, "(", " % ", ")");
+ break;
+ case EOpBitShiftLeft:
+ outputTriplet(out, visit, "(", " << ", ")");
+ break;
+ case EOpBitShiftRight:
+ outputTriplet(out, visit, "(", " >> ", ")");
+ break;
+ case EOpBitwiseAnd:
+ outputTriplet(out, visit, "(", " & ", ")");
+ break;
+ case EOpBitwiseXor:
+ outputTriplet(out, visit, "(", " ^ ", ")");
+ break;
+ case EOpBitwiseOr:
+ outputTriplet(out, visit, "(", " | ", ")");
+ break;
+ case EOpEqual:
+ case EOpNotEqual:
+ outputEqual(visit, node->getLeft()->getType(), node->getOp(), out);
+ break;
+ case EOpLessThan:
+ outputTriplet(out, visit, "(", " < ", ")");
+ break;
+ case EOpGreaterThan:
+ outputTriplet(out, visit, "(", " > ", ")");
+ break;
+ case EOpLessThanEqual:
+ outputTriplet(out, visit, "(", " <= ", ")");
+ break;
+ case EOpGreaterThanEqual:
+ outputTriplet(out, visit, "(", " >= ", ")");
+ break;
+ case EOpVectorTimesScalar:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpMatrixTimesScalar:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ case EOpVectorTimesMatrix:
+ outputTriplet(out, visit, "mul(", ", transpose(", "))");
+ break;
+ case EOpMatrixTimesVector:
+ outputTriplet(out, visit, "mul(transpose(", "), ", ")");
+ break;
+ case EOpMatrixTimesMatrix:
+ outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))");
+ break;
+ case EOpLogicalOr:
+ // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded.
+ ASSERT(!node->getRight()->hasSideEffects());
+ outputTriplet(out, visit, "(", " || ", ")");
+ return true;
+ case EOpLogicalXor:
+ mUsesXor = true;
+ outputTriplet(out, visit, "xor(", ", ", ")");
+ break;
+ case EOpLogicalAnd:
+ // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded.
+ ASSERT(!node->getRight()->hasSideEffects());
+ outputTriplet(out, visit, "(", " && ", ")");
+ return true;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ switch (node->getOp())
+ {
+ case EOpNegative:
+ outputTriplet(out, visit, "(-", "", ")");
+ break;
+ case EOpPositive:
+ outputTriplet(out, visit, "(+", "", ")");
+ break;
+ case EOpVectorLogicalNot:
+ outputTriplet(out, visit, "(!", "", ")");
+ break;
+ case EOpLogicalNot:
+ outputTriplet(out, visit, "(!", "", ")");
+ break;
+ case EOpBitwiseNot:
+ outputTriplet(out, visit, "(~", "", ")");
+ break;
+ case EOpPostIncrement:
+ outputTriplet(out, visit, "(", "", "++)");
+ break;
+ case EOpPostDecrement:
+ outputTriplet(out, visit, "(", "", "--)");
+ break;
+ case EOpPreIncrement:
+ outputTriplet(out, visit, "(++", "", ")");
+ break;
+ case EOpPreDecrement:
+ outputTriplet(out, visit, "(--", "", ")");
+ break;
+ case EOpRadians:
+ outputTriplet(out, visit, "radians(", "", ")");
+ break;
+ case EOpDegrees:
+ outputTriplet(out, visit, "degrees(", "", ")");
+ break;
+ case EOpSin:
+ outputTriplet(out, visit, "sin(", "", ")");
+ break;
+ case EOpCos:
+ outputTriplet(out, visit, "cos(", "", ")");
+ break;
+ case EOpTan:
+ outputTriplet(out, visit, "tan(", "", ")");
+ break;
+ case EOpAsin:
+ outputTriplet(out, visit, "asin(", "", ")");
+ break;
+ case EOpAcos:
+ outputTriplet(out, visit, "acos(", "", ")");
+ break;
+ case EOpAtan:
+ outputTriplet(out, visit, "atan(", "", ")");
+ break;
+ case EOpSinh:
+ outputTriplet(out, visit, "sinh(", "", ")");
+ break;
+ case EOpCosh:
+ outputTriplet(out, visit, "cosh(", "", ")");
+ break;
+ case EOpTanh:
+ outputTriplet(out, visit, "tanh(", "", ")");
+ break;
+ case EOpAsinh:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "asinh(");
+ break;
+ case EOpAcosh:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "acosh(");
+ break;
+ case EOpAtanh:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "atanh(");
+ break;
+ case EOpExp:
+ outputTriplet(out, visit, "exp(", "", ")");
+ break;
+ case EOpLog:
+ outputTriplet(out, visit, "log(", "", ")");
+ break;
+ case EOpExp2:
+ outputTriplet(out, visit, "exp2(", "", ")");
+ break;
+ case EOpLog2:
+ outputTriplet(out, visit, "log2(", "", ")");
+ break;
+ case EOpSqrt:
+ outputTriplet(out, visit, "sqrt(", "", ")");
+ break;
+ case EOpInverseSqrt:
+ outputTriplet(out, visit, "rsqrt(", "", ")");
+ break;
+ case EOpAbs:
+ outputTriplet(out, visit, "abs(", "", ")");
+ break;
+ case EOpSign:
+ outputTriplet(out, visit, "sign(", "", ")");
+ break;
+ case EOpFloor:
+ outputTriplet(out, visit, "floor(", "", ")");
+ break;
+ case EOpTrunc:
+ outputTriplet(out, visit, "trunc(", "", ")");
+ break;
+ case EOpRound:
+ outputTriplet(out, visit, "round(", "", ")");
+ break;
+ case EOpRoundEven:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "roundEven(");
+ break;
+ case EOpCeil:
+ outputTriplet(out, visit, "ceil(", "", ")");
+ break;
+ case EOpFract:
+ outputTriplet(out, visit, "frac(", "", ")");
+ break;
+ case EOpIsNan:
+ if (node->getUseEmulatedFunction())
+ writeEmulatedFunctionTriplet(out, visit, "isnan(");
+ else
+ outputTriplet(out, visit, "isnan(", "", ")");
+ mRequiresIEEEStrictCompiling = true;
+ break;
+ case EOpIsInf:
+ outputTriplet(out, visit, "isinf(", "", ")");
+ break;
+ case EOpFloatBitsToInt:
+ outputTriplet(out, visit, "asint(", "", ")");
+ break;
+ case EOpFloatBitsToUint:
+ outputTriplet(out, visit, "asuint(", "", ")");
+ break;
+ case EOpIntBitsToFloat:
+ outputTriplet(out, visit, "asfloat(", "", ")");
+ break;
+ case EOpUintBitsToFloat:
+ outputTriplet(out, visit, "asfloat(", "", ")");
+ break;
+ case EOpPackSnorm2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16(");
+ break;
+ case EOpPackUnorm2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16(");
+ break;
+ case EOpPackHalf2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "packHalf2x16(");
+ break;
+ case EOpUnpackSnorm2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16(");
+ break;
+ case EOpUnpackUnorm2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16(");
+ break;
+ case EOpUnpackHalf2x16:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16(");
+ break;
+ case EOpLength:
+ outputTriplet(out, visit, "length(", "", ")");
+ break;
+ case EOpNormalize:
+ outputTriplet(out, visit, "normalize(", "", ")");
+ break;
+ case EOpDFdx:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "ddx(", "", ")");
+ }
+ break;
+ case EOpDFdy:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "ddy(", "", ")");
+ }
+ break;
+ case EOpFwidth:
+ if(mInsideDiscontinuousLoop || mOutputLod0Function)
+ {
+ outputTriplet(out, visit, "(", "", ", 0.0)");
+ }
+ else
+ {
+ outputTriplet(out, visit, "fwidth(", "", ")");
+ }
+ break;
+ case EOpTranspose:
+ outputTriplet(out, visit, "transpose(", "", ")");
+ break;
+ case EOpDeterminant:
+ outputTriplet(out, visit, "determinant(transpose(", "", "))");
+ break;
+ case EOpInverse:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "inverse(");
+ break;
+
+ case EOpAny:
+ outputTriplet(out, visit, "any(", "", ")");
+ break;
+ case EOpAll:
+ outputTriplet(out, visit, "all(", "", ")");
+ break;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+TString OutputHLSL::samplerNamePrefixFromStruct(TIntermTyped *node)
+{
+ if (node->getAsSymbolNode())
+ {
+ return node->getAsSymbolNode()->getSymbol();
+ }
+ TIntermBinary *nodeBinary = node->getAsBinaryNode();
+ switch (nodeBinary->getOp())
+ {
+ case EOpIndexDirect:
+ {
+ int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
+
+ TInfoSinkBase prefixSink;
+ prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_" << index;
+ return TString(prefixSink.c_str());
+ }
+ case EOpIndexDirectStruct:
+ {
+ TStructure *s = nodeBinary->getLeft()->getAsTyped()->getType().getStruct();
+ int index = nodeBinary->getRight()->getAsConstantUnion()->getIConst(0);
+ const TField *field = s->fields()[index];
+
+ TInfoSinkBase prefixSink;
+ prefixSink << samplerNamePrefixFromStruct(nodeBinary->getLeft()) << "_"
+ << field->name();
+ return TString(prefixSink.c_str());
+ }
+ default:
+ UNREACHABLE();
+ return TString("");
+ }
+}
+
+bool OutputHLSL::visitBlock(Visit visit, TIntermBlock *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ if (mInsideFunction)
+ {
+ outputLineDirective(out, node->getLine().first_line);
+ out << "{\n";
+ }
+
+ for (TIntermSequence::iterator sit = node->getSequence()->begin();
+ sit != node->getSequence()->end(); sit++)
+ {
+ outputLineDirective(out, (*sit)->getLine().first_line);
+
+ (*sit)->traverse(this);
+
+ // Don't output ; after case labels, they're terminated by :
+ // This is needed especially since outputting a ; after a case statement would turn empty
+ // case statements into non-empty case statements, disallowing fall-through from them.
+ // Also no need to output ; after if statements or sequences. This is done just for
+ // code clarity.
+ if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsIfElseNode() == nullptr &&
+ (*sit)->getAsBlock() == nullptr)
+ out << ";\n";
+ }
+
+ if (mInsideFunction)
+ {
+ outputLineDirective(out, node->getLine().last_line);
+ out << "}\n";
+ }
+
+ return false;
+}
+
+bool OutputHLSL::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ ASSERT(mCurrentFunctionMetadata == nullptr);
+
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(index != CallDAG::InvalidIndex);
+ mCurrentFunctionMetadata = &mASTMetadataList[index];
+
+ out << TypeString(node->getType()) << " ";
+
+ TIntermSequence *parameters = node->getFunctionParameters()->getSequence();
+
+ if (node->getFunctionSymbolInfo()->isMain())
+ {
+ out << "gl_main(";
+ }
+ else
+ {
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj())
+ << DisambiguateFunctionName(parameters) << (mOutputLod0Function ? "Lod0(" : "(");
+ }
+
+ for (unsigned int i = 0; i < parameters->size(); i++)
+ {
+ TIntermSymbol *symbol = (*parameters)[i]->getAsSymbolNode();
+
+ if (symbol)
+ {
+ ensureStructDefined(symbol->getType());
+
+ out << argumentString(symbol);
+
+ if (i < parameters->size() - 1)
+ {
+ out << ", ";
+ }
+ }
+ else
+ UNREACHABLE();
+ }
+
+ out << ")\n";
+
+ mInsideFunction = true;
+ // The function body node will output braces.
+ node->getBody()->traverse(this);
+ mInsideFunction = false;
+
+ mCurrentFunctionMetadata = nullptr;
+
+ bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
+ if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ ASSERT(!node->getFunctionSymbolInfo()->isMain());
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
+
+ return false;
+}
+
+bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+ if (visit == PreVisit)
+ {
+ TIntermSequence *sequence = node->getSequence();
+ TIntermTyped *variable = (*sequence)[0]->getAsTyped();
+ ASSERT(sequence->size() == 1);
+
+ if (variable &&
+ (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal ||
+ variable->getQualifier() == EvqConst))
+ {
+ ensureStructDefined(variable->getType());
+
+ if (!variable->getAsSymbolNode() ||
+ variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration
+ {
+ if (!mInsideFunction)
+ {
+ out << "static ";
+ }
+
+ out << TypeString(variable->getType()) + " ";
+
+ TIntermSymbol *symbol = variable->getAsSymbolNode();
+
+ if (symbol)
+ {
+ symbol->traverse(this);
+ out << ArrayString(symbol->getType());
+ out << " = " + initializer(symbol->getType());
+ }
+ else
+ {
+ variable->traverse(this);
+ }
+ }
+ else if (variable->getAsSymbolNode() &&
+ variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration
+ {
+ // Already added to constructor map
+ }
+ else
+ UNREACHABLE();
+ }
+ else if (variable && IsVaryingOut(variable->getQualifier()))
+ {
+ for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++)
+ {
+ TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
+
+ if (symbol)
+ {
+ // Vertex (output) varyings which are declared but not written to should
+ // still be declared to allow successful linking
+ mReferencedVaryings[symbol->getSymbol()] = symbol;
+ }
+ else
+ {
+ (*sit)->traverse(this);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ switch (node->getOp())
+ {
+ case EOpInvariantDeclaration:
+ // Do not do any translation
+ return false;
+ case EOpPrototype:
+ if (visit == PreVisit)
+ {
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
+ // Skip the prototype if it is not implemented (and thus not used)
+ if (index == CallDAG::InvalidIndex)
+ {
+ return false;
+ }
+
+ TIntermSequence *arguments = node->getSequence();
+
+ TString name =
+ DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+ out << TypeString(node->getType()) << " " << name
+ << DisambiguateFunctionName(arguments) << (mOutputLod0Function ? "Lod0(" : "(");
+
+ for (unsigned int i = 0; i < arguments->size(); i++)
+ {
+ TIntermSymbol *symbol = (*arguments)[i]->getAsSymbolNode();
+
+ if (symbol)
+ {
+ out << argumentString(symbol);
+
+ if (i < arguments->size() - 1)
+ {
+ out << ", ";
+ }
+ }
+ else
+ UNREACHABLE();
+ }
+
+ out << ");\n";
+
+ // Also prototype the Lod0 variant if needed
+ bool needsLod0 = mASTMetadataList[index].mNeedsLod0;
+ if (needsLod0 && !mOutputLod0Function && mShaderType == GL_FRAGMENT_SHADER)
+ {
+ mOutputLod0Function = true;
+ node->traverse(this);
+ mOutputLod0Function = false;
+ }
+
+ return false;
+ }
+ break;
+ case EOpFunctionCall:
+ {
+ TIntermSequence *arguments = node->getSequence();
+
+ bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function;
+ if (node->isUserDefined())
+ {
+ if (node->isArray())
+ {
+ UNIMPLEMENTED();
+ }
+ size_t index = mCallDag.findIndex(node->getFunctionSymbolInfo());
+ ASSERT(index != CallDAG::InvalidIndex);
+ lod0 &= mASTMetadataList[index].mNeedsLod0;
+
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj());
+ out << DisambiguateFunctionName(node->getSequence());
+ out << (lod0 ? "Lod0(" : "(");
+ }
+ else if (node->getFunctionSymbolInfo()->getNameObj().isInternal())
+ {
+ // This path is used for internal functions that don't have their definitions in the
+ // AST, such as precision emulation functions.
+ out << DecorateFunctionIfNeeded(node->getFunctionSymbolInfo()->getNameObj()) << "(";
+ }
+ else
+ {
+ TString name = TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
+ TBasicType samplerType = (*arguments)[0]->getAsTyped()->getType().getBasicType();
+ int coords = (*arguments)[1]->getAsTyped()->getNominalSize();
+ TString textureFunctionName = mTextureFunctionHLSL->useTextureFunction(
+ name, samplerType, coords, arguments->size(), lod0, mShaderType);
+ out << textureFunctionName << "(";
+ }
+
+ for (TIntermSequence::iterator arg = arguments->begin(); arg != arguments->end(); arg++)
+ {
+ TIntermTyped *typedArg = (*arg)->getAsTyped();
+ if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(typedArg->getBasicType()))
+ {
+ out << "texture_";
+ (*arg)->traverse(this);
+ out << ", sampler_";
+ }
+
+ (*arg)->traverse(this);
+
+ if (typedArg->getType().isStructureContainingSamplers())
+ {
+ const TType &argType = typedArg->getType();
+ TVector<TIntermSymbol *> samplerSymbols;
+ TString structName = samplerNamePrefixFromStruct(typedArg);
+ argType.createSamplerSymbols("angle_" + structName, "",
+ argType.isArray() ? argType.getArraySize() : 0u,
+ &samplerSymbols, nullptr);
+ for (const TIntermSymbol *sampler : samplerSymbols)
+ {
+ if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ out << ", texture_" << sampler->getSymbol();
+ out << ", sampler_" << sampler->getSymbol();
+ }
+ else
+ {
+ // In case of HLSL 4.1+, this symbol is the sampler index, and in case
+ // of D3D9, it's the sampler variable.
+ out << ", " + sampler->getSymbol();
+ }
+ }
+ }
+
+ if (arg < arguments->end() - 1)
+ {
+ out << ", ";
+ }
+ }
+
+ out << ")";
+
+ return false;
+ }
+ case EOpParameters:
+ outputTriplet(out, visit, "(", ", ", ")\n{\n");
+ break;
+ case EOpConstructFloat:
+ outputConstructor(out, visit, node->getType(), "vec1", node->getSequence());
+ break;
+ case EOpConstructVec2:
+ outputConstructor(out, visit, node->getType(), "vec2", node->getSequence());
+ break;
+ case EOpConstructVec3:
+ outputConstructor(out, visit, node->getType(), "vec3", node->getSequence());
+ break;
+ case EOpConstructVec4:
+ outputConstructor(out, visit, node->getType(), "vec4", node->getSequence());
+ break;
+ case EOpConstructBool:
+ outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence());
+ break;
+ case EOpConstructBVec2:
+ outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence());
+ break;
+ case EOpConstructBVec3:
+ outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence());
+ break;
+ case EOpConstructBVec4:
+ outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence());
+ break;
+ case EOpConstructInt:
+ outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence());
+ break;
+ case EOpConstructIVec2:
+ outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence());
+ break;
+ case EOpConstructIVec3:
+ outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence());
+ break;
+ case EOpConstructIVec4:
+ outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence());
+ break;
+ case EOpConstructUInt:
+ outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence());
+ break;
+ case EOpConstructUVec2:
+ outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence());
+ break;
+ case EOpConstructUVec3:
+ outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence());
+ break;
+ case EOpConstructUVec4:
+ outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence());
+ break;
+ case EOpConstructMat2:
+ outputConstructor(out, visit, node->getType(), "mat2", node->getSequence());
+ break;
+ case EOpConstructMat2x3:
+ outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence());
+ break;
+ case EOpConstructMat2x4:
+ outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence());
+ break;
+ case EOpConstructMat3x2:
+ outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence());
+ break;
+ case EOpConstructMat3:
+ outputConstructor(out, visit, node->getType(), "mat3", node->getSequence());
+ break;
+ case EOpConstructMat3x4:
+ outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence());
+ break;
+ case EOpConstructMat4x2:
+ outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence());
+ break;
+ case EOpConstructMat4x3:
+ outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence());
+ break;
+ case EOpConstructMat4:
+ outputConstructor(out, visit, node->getType(), "mat4", node->getSequence());
+ break;
+ case EOpConstructStruct:
+ {
+ if (node->getType().isArray())
+ {
+ UNIMPLEMENTED();
+ }
+ const TString &structName = StructNameString(*node->getType().getStruct());
+ mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence());
+ outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")");
+ }
+ break;
+ case EOpLessThan:
+ outputTriplet(out, visit, "(", " < ", ")");
+ break;
+ case EOpGreaterThan:
+ outputTriplet(out, visit, "(", " > ", ")");
+ break;
+ case EOpLessThanEqual:
+ outputTriplet(out, visit, "(", " <= ", ")");
+ break;
+ case EOpGreaterThanEqual:
+ outputTriplet(out, visit, "(", " >= ", ")");
+ break;
+ case EOpVectorEqual:
+ outputTriplet(out, visit, "(", " == ", ")");
+ break;
+ case EOpVectorNotEqual:
+ outputTriplet(out, visit, "(", " != ", ")");
+ break;
+ case EOpMod:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "mod(");
+ break;
+ case EOpModf:
+ outputTriplet(out, visit, "modf(", ", ", ")");
+ break;
+ case EOpPow:
+ outputTriplet(out, visit, "pow(", ", ", ")");
+ break;
+ case EOpAtan:
+ ASSERT(node->getSequence()->size() == 2); // atan(x) is a unary operator
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "atan(");
+ break;
+ case EOpMin:
+ outputTriplet(out, visit, "min(", ", ", ")");
+ break;
+ case EOpMax:
+ outputTriplet(out, visit, "max(", ", ", ")");
+ break;
+ case EOpClamp:
+ outputTriplet(out, visit, "clamp(", ", ", ")");
+ break;
+ case EOpMix:
+ {
+ TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped();
+ if (lastParamNode->getType().getBasicType() == EbtBool)
+ {
+ // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)",
+ // so use emulated version.
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "mix(");
+ }
+ else
+ {
+ outputTriplet(out, visit, "lerp(", ", ", ")");
+ }
+ break;
+ }
+ case EOpStep:
+ outputTriplet(out, visit, "step(", ", ", ")");
+ break;
+ case EOpSmoothStep:
+ outputTriplet(out, visit, "smoothstep(", ", ", ")");
+ break;
+ case EOpDistance:
+ outputTriplet(out, visit, "distance(", ", ", ")");
+ break;
+ case EOpDot:
+ outputTriplet(out, visit, "dot(", ", ", ")");
+ break;
+ case EOpCross:
+ outputTriplet(out, visit, "cross(", ", ", ")");
+ break;
+ case EOpFaceForward:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "faceforward(");
+ break;
+ case EOpReflect:
+ outputTriplet(out, visit, "reflect(", ", ", ")");
+ break;
+ case EOpRefract:
+ outputTriplet(out, visit, "refract(", ", ", ")");
+ break;
+ case EOpOuterProduct:
+ ASSERT(node->getUseEmulatedFunction());
+ writeEmulatedFunctionTriplet(out, visit, "outerProduct(");
+ break;
+ case EOpMul:
+ outputTriplet(out, visit, "(", " * ", ")");
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return true;
+}
+
+void OutputHLSL::writeIfElse(TInfoSinkBase &out, TIntermIfElse *node)
+{
+ out << "if (";
+
+ node->getCondition()->traverse(this);
+
+ out << ")\n";
+
+ outputLineDirective(out, node->getLine().first_line);
+
+ bool discard = false;
+
+ if (node->getTrueBlock())
+ {
+ // The trueBlock child node will output braces.
+ node->getTrueBlock()->traverse(this);
+
+ // Detect true discard
+ discard = (discard || FindDiscard::search(node->getTrueBlock()));
+ }
+ else
+ {
+ // TODO(oetuaho): Check if the semicolon inside is necessary.
+ // It's there as a result of conservative refactoring of the output.
+ out << "{;}\n";
+ }
+
+ outputLineDirective(out, node->getLine().first_line);
+
+ if (node->getFalseBlock())
+ {
+ out << "else\n";
+
+ outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
+
+ // The falseBlock child node will output braces.
+ node->getFalseBlock()->traverse(this);
+
+ outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
+
+ // Detect false discard
+ discard = (discard || FindDiscard::search(node->getFalseBlock()));
+ }
+
+ // ANGLE issue 486: Detect problematic conditional discard
+ if (discard)
+ {
+ mUsesDiscardRewriting = true;
+ }
+}
+
+bool OutputHLSL::visitTernary(Visit, TIntermTernary *)
+{
+ // Ternary ops should have been already converted to something else in the AST. HLSL ternary
+ // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator.
+ UNREACHABLE();
+ return false;
+}
+
+bool OutputHLSL::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ ASSERT(mInsideFunction);
+
+ // D3D errors when there is a gradient operation in a loop in an unflattened if.
+ if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
+ {
+ out << "FLATTEN ";
+ }
+
+ writeIfElse(out, node);
+
+ return false;
+}
+
+bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ if (node->getStatementList())
+ {
+ node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList()));
+ outputTriplet(out, visit, "switch (", ") ", "");
+ // The curly braces get written when visiting the statementList aggregate
+ }
+ else
+ {
+ // No statementList, so it won't output curly braces
+ outputTriplet(out, visit, "switch (", ") {", "}\n");
+ }
+ return true;
+}
+
+bool OutputHLSL::visitCase(Visit visit, TIntermCase *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ if (node->hasCondition())
+ {
+ outputTriplet(out, visit, "case (", "", "):\n");
+ return true;
+ }
+ else
+ {
+ out << "default:\n";
+ return false;
+ }
+}
+
+void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+ writeConstantUnion(out, node->getType(), node->getUnionArrayPointer());
+}
+
+bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
+{
+ mNestedLoopDepth++;
+
+ bool wasDiscontinuous = mInsideDiscontinuousLoop;
+ mInsideDiscontinuousLoop = mInsideDiscontinuousLoop ||
+ mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0;
+
+ TInfoSinkBase &out = getInfoSink();
+
+ if (mOutputType == SH_HLSL_3_0_OUTPUT)
+ {
+ if (handleExcessiveLoop(out, node))
+ {
+ mInsideDiscontinuousLoop = wasDiscontinuous;
+ mNestedLoopDepth--;
+
+ return false;
+ }
+ }
+
+ const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
+ if (node->getType() == ELoopDoWhile)
+ {
+ out << "{" << unroll << " do\n";
+
+ outputLineDirective(out, node->getLine().first_line);
+ }
+ else
+ {
+ out << "{" << unroll << " for(";
+
+ if (node->getInit())
+ {
+ node->getInit()->traverse(this);
+ }
+
+ out << "; ";
+
+ if (node->getCondition())
+ {
+ node->getCondition()->traverse(this);
+ }
+
+ out << "; ";
+
+ if (node->getExpression())
+ {
+ node->getExpression()->traverse(this);
+ }
+
+ out << ")\n";
+
+ outputLineDirective(out, node->getLine().first_line);
+ }
+
+ if (node->getBody())
+ {
+ // The loop body node will output braces.
+ node->getBody()->traverse(this);
+ }
+ else
+ {
+ // TODO(oetuaho): Check if the semicolon inside is necessary.
+ // It's there as a result of conservative refactoring of the output.
+ out << "{;}\n";
+ }
+
+ outputLineDirective(out, node->getLine().first_line);
+
+ if (node->getType() == ELoopDoWhile)
+ {
+ outputLineDirective(out, node->getCondition()->getLine().first_line);
+ out << "while(\n";
+
+ node->getCondition()->traverse(this);
+
+ out << ");";
+ }
+
+ out << "}\n";
+
+ mInsideDiscontinuousLoop = wasDiscontinuous;
+ mNestedLoopDepth--;
+
+ return false;
+}
+
+bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node)
+{
+ TInfoSinkBase &out = getInfoSink();
+
+ switch (node->getFlowOp())
+ {
+ case EOpKill:
+ outputTriplet(out, visit, "discard;\n", "", "");
+ break;
+ case EOpBreak:
+ if (visit == PreVisit)
+ {
+ if (mNestedLoopDepth > 1)
+ {
+ mUsesNestedBreak = true;
+ }
+
+ if (mExcessiveLoopIndex)
+ {
+ out << "{Break";
+ mExcessiveLoopIndex->traverse(this);
+ out << " = true; break;}\n";
+ }
+ else
+ {
+ out << "break;\n";
+ }
+ }
+ break;
+ case EOpContinue:
+ outputTriplet(out, visit, "continue;\n", "", "");
+ break;
+ case EOpReturn:
+ if (visit == PreVisit)
+ {
+ if (node->getExpression())
+ {
+ out << "return ";
+ }
+ else
+ {
+ out << "return;\n";
+ }
+ }
+ else if (visit == PostVisit)
+ {
+ if (node->getExpression())
+ {
+ out << ";\n";
+ }
+ }
+ break;
+ default: UNREACHABLE();
+ }
+
+ return true;
+}
+
+// Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
+// (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254).
+bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
+{
+ const int MAX_LOOP_ITERATIONS = 254;
+
+ // Parse loops of the form:
+ // for(int index = initial; index [comparator] limit; index += increment)
+ TIntermSymbol *index = NULL;
+ TOperator comparator = EOpNull;
+ int initial = 0;
+ int limit = 0;
+ int increment = 0;
+
+ // Parse index name and intial value
+ if (node->getInit())
+ {
+ TIntermDeclaration *init = node->getInit()->getAsDeclarationNode();
+
+ if (init)
+ {
+ TIntermSequence *sequence = init->getSequence();
+ TIntermTyped *variable = (*sequence)[0]->getAsTyped();
+
+ if (variable && variable->getQualifier() == EvqTemporary)
+ {
+ TIntermBinary *assign = variable->getAsBinaryNode();
+
+ if (assign->getOp() == EOpInitialize)
+ {
+ TIntermSymbol *symbol = assign->getLeft()->getAsSymbolNode();
+ TIntermConstantUnion *constant = assign->getRight()->getAsConstantUnion();
+
+ if (symbol && constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->isScalar())
+ {
+ index = symbol;
+ initial = constant->getIConst(0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Parse comparator and limit value
+ if (index != NULL && node->getCondition())
+ {
+ TIntermBinary *test = node->getCondition()->getAsBinaryNode();
+
+ if (test && test->getLeft()->getAsSymbolNode()->getId() == index->getId())
+ {
+ TIntermConstantUnion *constant = test->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->isScalar())
+ {
+ comparator = test->getOp();
+ limit = constant->getIConst(0);
+ }
+ }
+ }
+ }
+
+ // Parse increment
+ if (index != NULL && comparator != EOpNull && node->getExpression())
+ {
+ TIntermBinary *binaryTerminal = node->getExpression()->getAsBinaryNode();
+ TIntermUnary *unaryTerminal = node->getExpression()->getAsUnaryNode();
+
+ if (binaryTerminal)
+ {
+ TOperator op = binaryTerminal->getOp();
+ TIntermConstantUnion *constant = binaryTerminal->getRight()->getAsConstantUnion();
+
+ if (constant)
+ {
+ if (constant->getBasicType() == EbtInt && constant->isScalar())
+ {
+ int value = constant->getIConst(0);
+
+ switch (op)
+ {
+ case EOpAddAssign: increment = value; break;
+ case EOpSubAssign: increment = -value; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+ }
+ else if (unaryTerminal)
+ {
+ TOperator op = unaryTerminal->getOp();
+
+ switch (op)
+ {
+ case EOpPostIncrement: increment = 1; break;
+ case EOpPostDecrement: increment = -1; break;
+ case EOpPreIncrement: increment = 1; break;
+ case EOpPreDecrement: increment = -1; break;
+ default: UNIMPLEMENTED();
+ }
+ }
+ }
+
+ if (index != NULL && comparator != EOpNull && increment != 0)
+ {
+ if (comparator == EOpLessThanEqual)
+ {
+ comparator = EOpLessThan;
+ limit += 1;
+ }
+
+ if (comparator == EOpLessThan)
+ {
+ int iterations = (limit - initial) / increment;
+
+ if (iterations <= MAX_LOOP_ITERATIONS)
+ {
+ return false; // Not an excessive loop
+ }
+
+ TIntermSymbol *restoreIndex = mExcessiveLoopIndex;
+ mExcessiveLoopIndex = index;
+
+ out << "{int ";
+ index->traverse(this);
+ out << ";\n"
+ "bool Break";
+ index->traverse(this);
+ out << " = false;\n";
+
+ bool firstLoopFragment = true;
+
+ while (iterations > 0)
+ {
+ int clampedLimit = initial + increment * std::min(MAX_LOOP_ITERATIONS, iterations);
+
+ if (!firstLoopFragment)
+ {
+ out << "if (!Break";
+ index->traverse(this);
+ out << ") {\n";
+ }
+
+ if (iterations <= MAX_LOOP_ITERATIONS) // Last loop fragment
+ {
+ mExcessiveLoopIndex = NULL; // Stops setting the Break flag
+ }
+
+ // for(int index = initial; index < clampedLimit; index += increment)
+ const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
+
+ out << unroll << " for(";
+ index->traverse(this);
+ out << " = ";
+ out << initial;
+
+ out << "; ";
+ index->traverse(this);
+ out << " < ";
+ out << clampedLimit;
+
+ out << "; ";
+ index->traverse(this);
+ out << " += ";
+ out << increment;
+ out << ")\n";
+
+ outputLineDirective(out, node->getLine().first_line);
+ out << "{\n";
+
+ if (node->getBody())
+ {
+ node->getBody()->traverse(this);
+ }
+
+ outputLineDirective(out, node->getLine().first_line);
+ out << ";}\n";
+
+ if (!firstLoopFragment)
+ {
+ out << "}\n";
+ }
+
+ firstLoopFragment = false;
+
+ initial += MAX_LOOP_ITERATIONS * increment;
+ iterations -= MAX_LOOP_ITERATIONS;
+ }
+
+ out << "}";
+
+ mExcessiveLoopIndex = restoreIndex;
+
+ return true;
+ }
+ else UNIMPLEMENTED();
+ }
+
+ return false; // Not handled as an excessive loop
+}
+
+void OutputHLSL::outputTriplet(TInfoSinkBase &out,
+ Visit visit,
+ const char *preString,
+ const char *inString,
+ const char *postString)
+{
+ if (visit == PreVisit)
+ {
+ out << preString;
+ }
+ else if (visit == InVisit)
+ {
+ out << inString;
+ }
+ else if (visit == PostVisit)
+ {
+ out << postString;
+ }
+}
+
+void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line)
+{
+ if ((mCompileOptions & SH_LINE_DIRECTIVES) && (line > 0))
+ {
+ out << "\n";
+ out << "#line " << line;
+
+ if (mSourcePath)
+ {
+ out << " \"" << mSourcePath << "\"";
+ }
+
+ out << "\n";
+ }
+}
+
+TString OutputHLSL::argumentString(const TIntermSymbol *symbol)
+{
+ TQualifier qualifier = symbol->getQualifier();
+ const TType &type = symbol->getType();
+ const TName &name = symbol->getName();
+ TString nameStr;
+
+ if (name.getString().empty()) // HLSL demands named arguments, also for prototypes
+ {
+ nameStr = "x" + str(mUniqueIndex++);
+ }
+ else
+ {
+ nameStr = DecorateIfNeeded(name);
+ }
+
+ if (IsSampler(type.getBasicType()))
+ {
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ // Samplers are passed as indices to the sampler array.
+ ASSERT(qualifier != EvqOut && qualifier != EvqInOut);
+ return "const uint " + nameStr + ArrayString(type);
+ }
+ if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ return QualifierString(qualifier) + " " + TextureString(type.getBasicType()) +
+ " texture_" + nameStr + ArrayString(type) + ", " + QualifierString(qualifier) +
+ " " + SamplerString(type.getBasicType()) + " sampler_" + nameStr +
+ ArrayString(type);
+ }
+ }
+
+ TStringStream argString;
+ argString << QualifierString(qualifier) << " " << TypeString(type) << " " << nameStr
+ << ArrayString(type);
+
+ // If the structure parameter contains samplers, they need to be passed into the function as
+ // separate parameters. HLSL doesn't natively support samplers in structs.
+ if (type.isStructureContainingSamplers())
+ {
+ ASSERT(qualifier != EvqOut && qualifier != EvqInOut);
+ TVector<TIntermSymbol *> samplerSymbols;
+ type.createSamplerSymbols("angle" + nameStr, "", 0u, &samplerSymbols, nullptr);
+ for (const TIntermSymbol *sampler : samplerSymbols)
+ {
+ if (mOutputType == SH_HLSL_4_1_OUTPUT)
+ {
+ argString << ", const uint " << sampler->getSymbol() << ArrayString(type);
+ }
+ else if (mOutputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ const TType &samplerType = sampler->getType();
+ ASSERT((!type.isArray() && !samplerType.isArray()) ||
+ type.getArraySize() == samplerType.getArraySize());
+ ASSERT(IsSampler(samplerType.getBasicType()));
+ argString << ", " << QualifierString(qualifier) << " "
+ << TextureString(samplerType.getBasicType()) << " texture_"
+ << sampler->getSymbol() << ArrayString(type) << ", "
+ << QualifierString(qualifier) << " "
+ << SamplerString(samplerType.getBasicType()) << " sampler_"
+ << sampler->getSymbol() << ArrayString(type);
+ }
+ else
+ {
+ const TType &samplerType = sampler->getType();
+ ASSERT((!type.isArray() && !samplerType.isArray()) ||
+ type.getArraySize() == samplerType.getArraySize());
+ ASSERT(IsSampler(samplerType.getBasicType()));
+ argString << ", " << QualifierString(qualifier) << " " << TypeString(samplerType)
+ << " " << sampler->getSymbol() << ArrayString(type);
+ }
+ }
+ }
+
+ return argString.str();
+}
+
+TString OutputHLSL::initializer(const TType &type)
+{
+ TString string;
+
+ size_t size = type.getObjectSize();
+ for (size_t component = 0; component < size; component++)
+ {
+ string += "0";
+
+ if (component + 1 < size)
+ {
+ string += ", ";
+ }
+ }
+
+ return "{" + string + "}";
+}
+
+void OutputHLSL::outputConstructor(TInfoSinkBase &out,
+ Visit visit,
+ const TType &type,
+ const char *name,
+ const TIntermSequence *parameters)
+{
+ if (type.isArray())
+ {
+ UNIMPLEMENTED();
+ }
+
+ if (visit == PreVisit)
+ {
+ TString constructorName = mStructureHLSL->addConstructor(type, name, parameters);
+
+ out << constructorName << "(";
+ }
+ else if (visit == InVisit)
+ {
+ out << ", ";
+ }
+ else if (visit == PostVisit)
+ {
+ out << ")";
+ }
+}
+
+const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
+ const TType &type,
+ const TConstantUnion *const constUnion)
+{
+ const TConstantUnion *constUnionIterated = constUnion;
+
+ const TStructure* structure = type.getStruct();
+ if (structure)
+ {
+ out << StructNameString(*structure) + "_ctor(";
+
+ const TFieldList& fields = structure->fields();
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TType *fieldType = fields[i]->type();
+ constUnionIterated = writeConstantUnion(out, *fieldType, constUnionIterated);
+
+ if (i != fields.size() - 1)
+ {
+ out << ", ";
+ }
+ }
+
+ out << ")";
+ }
+ else
+ {
+ size_t size = type.getObjectSize();
+ bool writeType = size > 1;
+
+ if (writeType)
+ {
+ out << TypeString(type) << "(";
+ }
+ constUnionIterated = WriteConstantUnionArray(out, constUnionIterated, size);
+ if (writeType)
+ {
+ out << ")";
+ }
+ }
+
+ return constUnionIterated;
+}
+
+void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr)
+{
+ TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr);
+ outputTriplet(out, visit, preString.c_str(), ", ", ")");
+}
+
+bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression)
+{
+ sh::SearchSymbol searchSymbol(symbolNode->getSymbol());
+ expression->traverse(&searchSymbol);
+
+ if (searchSymbol.foundMatch())
+ {
+ // Type already printed
+ out << "t" + str(mUniqueIndex) + " = ";
+ expression->traverse(this);
+ out << ", ";
+ symbolNode->traverse(this);
+ out << " = t" + str(mUniqueIndex);
+
+ mUniqueIndex++;
+ return true;
+ }
+
+ return false;
+}
+
+bool OutputHLSL::canWriteAsHLSLLiteral(TIntermTyped *expression)
+{
+ // We support writing constant unions and constructors that only take constant unions as
+ // parameters as HLSL literals.
+ return expression->getAsConstantUnion() ||
+ expression->isConstructorWithOnlyConstantUnionParameters();
+}
+
+bool OutputHLSL::writeConstantInitialization(TInfoSinkBase &out,
+ TIntermSymbol *symbolNode,
+ TIntermTyped *expression)
+{
+ if (canWriteAsHLSLLiteral(expression))
+ {
+ symbolNode->traverse(this);
+ if (expression->getType().isArray())
+ {
+ out << "[" << expression->getType().getArraySize() << "]";
+ }
+ out << " = {";
+ if (expression->getAsConstantUnion())
+ {
+ TIntermConstantUnion *nodeConst = expression->getAsConstantUnion();
+ const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
+ WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+ }
+ else
+ {
+ TIntermAggregate *constructor = expression->getAsAggregate();
+ ASSERT(constructor != nullptr);
+ for (TIntermNode *&node : *constructor->getSequence())
+ {
+ TIntermConstantUnion *nodeConst = node->getAsConstantUnion();
+ ASSERT(nodeConst);
+ const TConstantUnion *constUnion = nodeConst->getUnionArrayPointer();
+ WriteConstantUnionArray(out, constUnion, nodeConst->getType().getObjectSize());
+ if (node != constructor->getSequence()->back())
+ {
+ out << ", ";
+ }
+ }
+ }
+ out << "}";
+ return true;
+ }
+ return false;
+}
+
+TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
+{
+ const TFieldList &fields = structure.fields();
+
+ for (const auto &eqFunction : mStructEqualityFunctions)
+ {
+ if (eqFunction->structure == &structure)
+ {
+ return eqFunction->functionName;
+ }
+ }
+
+ const TString &structNameString = StructNameString(structure);
+
+ StructEqualityFunction *function = new StructEqualityFunction();
+ function->structure = &structure;
+ function->functionName = "angle_eq_" + structNameString;
+
+ TInfoSinkBase fnOut;
+
+ fnOut << "bool " << function->functionName << "(" << structNameString << " a, " << structNameString + " b)\n"
+ << "{\n"
+ " return ";
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ const TField *field = fields[i];
+ const TType *fieldType = field->type();
+
+ const TString &fieldNameA = "a." + Decorate(field->name());
+ const TString &fieldNameB = "b." + Decorate(field->name());
+
+ if (i > 0)
+ {
+ fnOut << " && ";
+ }
+
+ fnOut << "(";
+ outputEqual(PreVisit, *fieldType, EOpEqual, fnOut);
+ fnOut << fieldNameA;
+ outputEqual(InVisit, *fieldType, EOpEqual, fnOut);
+ fnOut << fieldNameB;
+ outputEqual(PostVisit, *fieldType, EOpEqual, fnOut);
+ fnOut << ")";
+ }
+
+ fnOut << ";\n" << "}\n";
+
+ function->functionDefinition = fnOut.c_str();
+
+ mStructEqualityFunctions.push_back(function);
+ mEqualityFunctions.push_back(function);
+
+ return function->functionName;
+}
+
+TString OutputHLSL::addArrayEqualityFunction(const TType& type)
+{
+ for (const auto &eqFunction : mArrayEqualityFunctions)
+ {
+ if (eqFunction->type == type)
+ {
+ return eqFunction->functionName;
+ }
+ }
+
+ const TString &typeName = TypeString(type);
+
+ ArrayHelperFunction *function = new ArrayHelperFunction();
+ function->type = type;
+
+ TInfoSinkBase fnNameOut;
+ fnNameOut << "angle_eq_" << type.getArraySize() << "_" << typeName;
+ function->functionName = fnNameOut.c_str();
+
+ TType nonArrayType = type;
+ nonArrayType.clearArrayness();
+
+ TInfoSinkBase fnOut;
+
+ fnOut << "bool " << function->functionName << "("
+ << typeName << " a[" << type.getArraySize() << "], "
+ << typeName << " b[" << type.getArraySize() << "])\n"
+ << "{\n"
+ " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n"
+ " {\n"
+ " if (";
+
+ outputEqual(PreVisit, nonArrayType, EOpNotEqual, fnOut);
+ fnOut << "a[i]";
+ outputEqual(InVisit, nonArrayType, EOpNotEqual, fnOut);
+ fnOut << "b[i]";
+ outputEqual(PostVisit, nonArrayType, EOpNotEqual, fnOut);
+
+ fnOut << ") { return false; }\n"
+ " }\n"
+ " return true;\n"
+ "}\n";
+
+ function->functionDefinition = fnOut.c_str();
+
+ mArrayEqualityFunctions.push_back(function);
+ mEqualityFunctions.push_back(function);
+
+ return function->functionName;
+}
+
+TString OutputHLSL::addArrayAssignmentFunction(const TType& type)
+{
+ for (const auto &assignFunction : mArrayAssignmentFunctions)
+ {
+ if (assignFunction.type == type)
+ {
+ return assignFunction.functionName;
+ }
+ }
+
+ const TString &typeName = TypeString(type);
+
+ ArrayHelperFunction function;
+ function.type = type;
+
+ TInfoSinkBase fnNameOut;
+ fnNameOut << "angle_assign_" << type.getArraySize() << "_" << typeName;
+ function.functionName = fnNameOut.c_str();
+
+ TInfoSinkBase fnOut;
+
+ fnOut << "void " << function.functionName << "(out "
+ << typeName << " a[" << type.getArraySize() << "], "
+ << typeName << " b[" << type.getArraySize() << "])\n"
+ << "{\n"
+ " for (int i = 0; i < " << type.getArraySize() << "; ++i)\n"
+ " {\n"
+ " a[i] = b[i];\n"
+ " }\n"
+ "}\n";
+
+ function.functionDefinition = fnOut.c_str();
+
+ mArrayAssignmentFunctions.push_back(function);
+
+ return function.functionName;
+}
+
+TString OutputHLSL::addArrayConstructIntoFunction(const TType& type)
+{
+ for (const auto &constructIntoFunction : mArrayConstructIntoFunctions)
+ {
+ if (constructIntoFunction.type == type)
+ {
+ return constructIntoFunction.functionName;
+ }
+ }
+
+ const TString &typeName = TypeString(type);
+
+ ArrayHelperFunction function;
+ function.type = type;
+
+ TInfoSinkBase fnNameOut;
+ fnNameOut << "angle_construct_into_" << type.getArraySize() << "_" << typeName;
+ function.functionName = fnNameOut.c_str();
+
+ TInfoSinkBase fnOut;
+
+ fnOut << "void " << function.functionName << "(out "
+ << typeName << " a[" << type.getArraySize() << "]";
+ for (unsigned int i = 0u; i < type.getArraySize(); ++i)
+ {
+ fnOut << ", " << typeName << " b" << i;
+ }
+ fnOut << ")\n"
+ "{\n";
+
+ for (unsigned int i = 0u; i < type.getArraySize(); ++i)
+ {
+ fnOut << " a[" << i << "] = b" << i << ";\n";
+ }
+ fnOut << "}\n";
+
+ function.functionDefinition = fnOut.c_str();
+
+ mArrayConstructIntoFunctions.push_back(function);
+
+ return function.functionName;
+}
+
+void OutputHLSL::ensureStructDefined(const TType &type)
+{
+ TStructure *structure = type.getStruct();
+
+ if (structure)
+ {
+ mStructureHLSL->addConstructor(type, StructNameString(*structure), nullptr);
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/OutputHLSL.h b/gfx/angle/src/compiler/translator/OutputHLSL.h
new file mode 100755
index 000000000..833f4736b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.h
@@ -0,0 +1,226 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_OUTPUTHLSL_H_
+#define COMPILER_TRANSLATOR_OUTPUTHLSL_H_
+
+#include <list>
+#include <map>
+#include <stack>
+
+#include "angle_gl.h"
+#include "compiler/translator/ASTMetadataHLSL.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/ParseContext.h"
+
+class BuiltInFunctionEmulator;
+
+namespace sh
+{
+class StructureHLSL;
+class TextureFunctionHLSL;
+class UnfoldShortCircuit;
+class UniformHLSL;
+
+typedef std::map<TString, TIntermSymbol*> ReferencedSymbols;
+
+class OutputHLSL : public TIntermTraverser
+{
+ public:
+ OutputHLSL(sh::GLenum shaderType,
+ int shaderVersion,
+ const TExtensionBehavior &extensionBehavior,
+ const char *sourcePath,
+ ShShaderOutput outputType,
+ int numRenderTargets,
+ const std::vector<Uniform> &uniforms,
+ ShCompileOptions compileOptions);
+
+ ~OutputHLSL();
+
+ void output(TIntermNode *treeRoot, TInfoSinkBase &objSink);
+
+ const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const;
+ const std::map<std::string, unsigned int> &getUniformRegisterMap() const;
+
+ static TString initializer(const TType &type);
+
+ TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); }
+
+ static bool canWriteAsHLSLLiteral(TIntermTyped *expression);
+
+ protected:
+ void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator);
+
+ // Visit AST nodes and output their code to the body stream
+ void visitSymbol(TIntermSymbol*);
+ void visitRaw(TIntermRaw*);
+ void visitConstantUnion(TIntermConstantUnion*);
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary*);
+ bool visitUnary(Visit visit, TIntermUnary*);
+ bool visitTernary(Visit visit, TIntermTernary *);
+ bool visitIfElse(Visit visit, TIntermIfElse *);
+ bool visitSwitch(Visit visit, TIntermSwitch *);
+ bool visitCase(Visit visit, TIntermCase *);
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate*);
+ bool visitBlock(Visit visit, TIntermBlock *node);
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node);
+ bool visitLoop(Visit visit, TIntermLoop*);
+ bool visitBranch(Visit visit, TIntermBranch*);
+
+ bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node);
+
+ // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString.
+ void outputTriplet(TInfoSinkBase &out,
+ Visit visit,
+ const char *preString,
+ const char *inString,
+ const char *postString);
+ void outputLineDirective(TInfoSinkBase &out, int line);
+ TString argumentString(const TIntermSymbol *symbol);
+ int vectorSize(const TType &type) const;
+
+ // Emit constructor. Called with literal names so using const char* instead of TString.
+ void outputConstructor(TInfoSinkBase &out,
+ Visit visit,
+ const TType &type,
+ const char *name,
+ const TIntermSequence *parameters);
+ const TConstantUnion *writeConstantUnion(TInfoSinkBase &out,
+ const TType &type,
+ const TConstantUnion *constUnion);
+
+ void outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out);
+
+ void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr);
+ void makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs);
+
+ // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting)
+ bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
+ // Returns true if variable initializer could be written using literal {} notation.
+ bool writeConstantInitialization(TInfoSinkBase &out,
+ TIntermSymbol *symbolNode,
+ TIntermTyped *expression);
+
+ void writeDeferredGlobalInitializers(TInfoSinkBase &out);
+ void writeIfElse(TInfoSinkBase &out, TIntermIfElse *node);
+
+ // Returns the function name
+ TString addStructEqualityFunction(const TStructure &structure);
+ TString addArrayEqualityFunction(const TType &type);
+ TString addArrayAssignmentFunction(const TType &type);
+ TString addArrayConstructIntoFunction(const TType &type);
+
+ // Ensures if the type is a struct, the struct is defined
+ void ensureStructDefined(const TType &type);
+
+ sh::GLenum mShaderType;
+ int mShaderVersion;
+ const TExtensionBehavior &mExtensionBehavior;
+ const char *mSourcePath;
+ const ShShaderOutput mOutputType;
+ ShCompileOptions mCompileOptions;
+
+ bool mInsideFunction;
+
+ // Output streams
+ TInfoSinkBase mHeader;
+ TInfoSinkBase mBody;
+ TInfoSinkBase mFooter;
+
+ // A stack is useful when we want to traverse in the header, or in helper functions, but not always
+ // write to the body. Instead use an InfoSink stack to keep our current state intact.
+ // TODO (jmadill): Just passing an InfoSink in function parameters would be simpler.
+ std::stack<TInfoSinkBase *> mInfoSinkStack;
+
+ ReferencedSymbols mReferencedUniforms;
+ ReferencedSymbols mReferencedInterfaceBlocks;
+ ReferencedSymbols mReferencedAttributes;
+ ReferencedSymbols mReferencedVaryings;
+ ReferencedSymbols mReferencedOutputVariables;
+
+ StructureHLSL *mStructureHLSL;
+ UniformHLSL *mUniformHLSL;
+ TextureFunctionHLSL *mTextureFunctionHLSL;
+
+ // Parameters determining what goes in the header output
+ bool mUsesFragColor;
+ bool mUsesFragData;
+ bool mUsesDepthRange;
+ bool mUsesFragCoord;
+ bool mUsesPointCoord;
+ bool mUsesFrontFacing;
+ bool mUsesPointSize;
+ bool mUsesInstanceID;
+ bool mUsesVertexID;
+ bool mUsesFragDepth;
+ bool mUsesXor;
+ bool mUsesDiscardRewriting;
+ bool mUsesNestedBreak;
+ bool mRequiresIEEEStrictCompiling;
+
+
+ int mNumRenderTargets;
+
+ int mUniqueIndex; // For creating unique names
+
+ CallDAG mCallDag;
+ MetadataList mASTMetadataList;
+ ASTMetadataHLSL *mCurrentFunctionMetadata;
+ bool mOutputLod0Function;
+ bool mInsideDiscontinuousLoop;
+ int mNestedLoopDepth;
+
+ TIntermSymbol *mExcessiveLoopIndex;
+
+ TString structInitializerString(int indent, const TStructure &structure, const TString &rhsStructName);
+
+ std::map<TIntermTyped*, TString> mFlaggedStructMappedNames;
+ std::map<TIntermTyped*, TString> mFlaggedStructOriginalNames;
+
+ struct HelperFunction
+ {
+ TString functionName;
+ TString functionDefinition;
+
+ virtual ~HelperFunction() {}
+ };
+
+ // A list of all equality comparison functions. It's important to preserve the order at
+ // which we add the functions, since nested structures call each other recursively, and
+ // structure equality functions may need to call array equality functions and vice versa.
+ // The ownership of the pointers is maintained by the type-specific arrays.
+ std::vector<HelperFunction*> mEqualityFunctions;
+
+ struct StructEqualityFunction : public HelperFunction
+ {
+ const TStructure *structure;
+ };
+ std::vector<StructEqualityFunction*> mStructEqualityFunctions;
+
+ struct ArrayHelperFunction : public HelperFunction
+ {
+ TType type;
+ };
+ std::vector<ArrayHelperFunction*> mArrayEqualityFunctions;
+
+ std::vector<ArrayHelperFunction> mArrayAssignmentFunctions;
+
+ // The construct-into functions are functions that fill an N-element array passed as an out parameter
+ // with the other N parameters of the function. This is used to work around that arrays can't be
+ // return values in HLSL.
+ std::vector<ArrayHelperFunction> mArrayConstructIntoFunctions;
+
+ private:
+ TString samplerNamePrefixFromStruct(TIntermTyped *node);
+ bool ancestorEvaluatesToSamplerInStruct(Visit visit);
+};
+
+}
+
+#endif // COMPILER_TRANSLATOR_OUTPUTHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/ParseContext.cpp b/gfx/angle/src/compiler/translator/ParseContext.cpp
new file mode 100755
index 000000000..4ad597c4f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ParseContext.cpp
@@ -0,0 +1,4492 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ParseContext.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "compiler/preprocessor/SourceLocation.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/glslang.h"
+#include "compiler/translator/ValidateSwitch.h"
+#include "compiler/translator/ValidateGlobalInitializer.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+///////////////////////////////////////////////////////////////////////
+//
+// Sub- vector and matrix fields
+//
+////////////////////////////////////////////////////////////////////////
+
+namespace
+{
+
+const int kWebGLMaxStructNesting = 4;
+
+bool ContainsSampler(const TType &type)
+{
+ if (IsSampler(type.getBasicType()))
+ return true;
+
+ if (type.getBasicType() == EbtStruct || type.isInterfaceBlock())
+ {
+ const TFieldList &fields = type.getStruct()->fields();
+ for (unsigned int i = 0; i < fields.size(); ++i)
+ {
+ if (ContainsSampler(*fields[i]->type()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ContainsImage(const TType &type)
+{
+ if (IsImage(type.getBasicType()))
+ return true;
+
+ if (type.getBasicType() == EbtStruct || type.isInterfaceBlock())
+ {
+ const TFieldList &fields = type.getStruct()->fields();
+ for (unsigned int i = 0; i < fields.size(); ++i)
+ {
+ if (ContainsImage(*fields[i]->type()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+TParseContext::TParseContext(TSymbolTable &symt,
+ TExtensionBehavior &ext,
+ sh::GLenum type,
+ ShShaderSpec spec,
+ ShCompileOptions options,
+ bool checksPrecErrors,
+ TInfoSink &is,
+ const ShBuiltInResources &resources)
+ : intermediate(),
+ symbolTable(symt),
+ mDeferredSingleDeclarationErrorCheck(false),
+ mShaderType(type),
+ mShaderSpec(spec),
+ mCompileOptions(options),
+ mShaderVersion(100),
+ mTreeRoot(nullptr),
+ mLoopNestingLevel(0),
+ mStructNestingLevel(0),
+ mSwitchNestingLevel(0),
+ mCurrentFunctionType(nullptr),
+ mFunctionReturnsValue(false),
+ mChecksPrecisionErrors(checksPrecErrors),
+ mFragmentPrecisionHighOnESSL1(false),
+ mDefaultMatrixPacking(EmpColumnMajor),
+ mDefaultBlockStorage(sh::IsWebGLBasedSpec(spec) ? EbsStd140 : EbsShared),
+ mDiagnostics(is),
+ mDirectiveHandler(ext,
+ mDiagnostics,
+ mShaderVersion,
+ mShaderType,
+ resources.WEBGL_debug_shader_precision == 1),
+ mPreprocessor(&mDiagnostics, &mDirectiveHandler),
+ mScanner(nullptr),
+ mUsesFragData(false),
+ mUsesFragColor(false),
+ mUsesSecondaryOutputs(false),
+ mMinProgramTexelOffset(resources.MinProgramTexelOffset),
+ mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
+ mComputeShaderLocalSizeDeclared(false),
+ mDeclaringFunction(false)
+{
+ mComputeShaderLocalSize.fill(-1);
+}
+
+//
+// Look at a '.' field selector string and change it into offsets
+// for a vector.
+//
+bool TParseContext::parseVectorFields(const TString &compString,
+ int vecSize,
+ TVectorFields &fields,
+ const TSourceLoc &line)
+{
+ fields.num = (int)compString.size();
+ if (fields.num > 4)
+ {
+ error(line, "illegal vector field selection", compString.c_str());
+ return false;
+ }
+
+ enum
+ {
+ exyzw,
+ ergba,
+ estpq
+ } fieldSet[4];
+
+ for (int i = 0; i < fields.num; ++i)
+ {
+ switch (compString[i])
+ {
+ case 'x':
+ fields.offsets[i] = 0;
+ fieldSet[i] = exyzw;
+ break;
+ case 'r':
+ fields.offsets[i] = 0;
+ fieldSet[i] = ergba;
+ break;
+ case 's':
+ fields.offsets[i] = 0;
+ fieldSet[i] = estpq;
+ break;
+ case 'y':
+ fields.offsets[i] = 1;
+ fieldSet[i] = exyzw;
+ break;
+ case 'g':
+ fields.offsets[i] = 1;
+ fieldSet[i] = ergba;
+ break;
+ case 't':
+ fields.offsets[i] = 1;
+ fieldSet[i] = estpq;
+ break;
+ case 'z':
+ fields.offsets[i] = 2;
+ fieldSet[i] = exyzw;
+ break;
+ case 'b':
+ fields.offsets[i] = 2;
+ fieldSet[i] = ergba;
+ break;
+ case 'p':
+ fields.offsets[i] = 2;
+ fieldSet[i] = estpq;
+ break;
+
+ case 'w':
+ fields.offsets[i] = 3;
+ fieldSet[i] = exyzw;
+ break;
+ case 'a':
+ fields.offsets[i] = 3;
+ fieldSet[i] = ergba;
+ break;
+ case 'q':
+ fields.offsets[i] = 3;
+ fieldSet[i] = estpq;
+ break;
+ default:
+ error(line, "illegal vector field selection", compString.c_str());
+ return false;
+ }
+ }
+
+ for (int i = 0; i < fields.num; ++i)
+ {
+ if (fields.offsets[i] >= vecSize)
+ {
+ error(line, "vector field selection out of range", compString.c_str());
+ return false;
+ }
+
+ if (i > 0)
+ {
+ if (fieldSet[i] != fieldSet[i - 1])
+ {
+ error(line, "illegal - vector component fields not from the same set",
+ compString.c_str());
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// Errors
+//
+////////////////////////////////////////////////////////////////////////
+
+
+//
+// Used by flex/bison to output all syntax and parsing errors.
+//
+void TParseContext::error(const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo)
+{
+ mDiagnostics.error(loc, reason, token, extraInfo);
+}
+
+void TParseContext::warning(const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo)
+{
+ mDiagnostics.warning(loc, reason, token, extraInfo);
+}
+
+void TParseContext::outOfRangeError(bool isError,
+ const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo)
+{
+ if (isError)
+ {
+ error(loc, reason, token, extraInfo);
+ }
+ else
+ {
+ warning(loc, reason, token, extraInfo);
+ }
+}
+
+//
+// Same error message for all places assignments don't work.
+//
+void TParseContext::assignError(const TSourceLoc &line, const char *op, TString left, TString right)
+{
+ std::stringstream extraInfoStream;
+ extraInfoStream << "cannot convert from '" << right << "' to '" << left << "'";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, "", op, extraInfo.c_str());
+}
+
+//
+// Same error message for all places unary operations don't work.
+//
+void TParseContext::unaryOpError(const TSourceLoc &line, const char *op, TString operand)
+{
+ std::stringstream extraInfoStream;
+ extraInfoStream << "no operation '" << op << "' exists that takes an operand of type "
+ << operand << " (or there is no acceptable conversion)";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, " wrong operand type", op, extraInfo.c_str());
+}
+
+//
+// Same error message for all binary operations don't work.
+//
+void TParseContext::binaryOpError(const TSourceLoc &line,
+ const char *op,
+ TString left,
+ TString right)
+{
+ std::stringstream extraInfoStream;
+ extraInfoStream << "no operation '" << op << "' exists that takes a left-hand operand of type '"
+ << left << "' and a right operand of type '" << right
+ << "' (or there is no acceptable conversion)";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, " wrong operand types ", op, extraInfo.c_str());
+}
+
+void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
+ TPrecision precision,
+ TBasicType type)
+{
+ if (!mChecksPrecisionErrors)
+ return;
+
+ if (precision != EbpUndefined && !SupportsPrecision(type))
+ {
+ error(line, "illegal type for precision qualifier", getBasicString(type));
+ }
+
+ if (precision == EbpUndefined)
+ {
+ switch (type)
+ {
+ case EbtFloat:
+ error(line, "No precision specified for (float)", "");
+ return;
+ case EbtInt:
+ case EbtUInt:
+ UNREACHABLE(); // there's always a predeclared qualifier
+ error(line, "No precision specified (int)", "");
+ return;
+ default:
+ if (IsSampler(type))
+ {
+ error(line, "No precision specified (sampler)", "");
+ return;
+ }
+ if (IsImage(type))
+ {
+ error(line, "No precision specified (image)", "");
+ return;
+ }
+ }
+ }
+}
+
+// Both test and if necessary, spit out an error, to see if the node is really
+// an l-value that can be operated on this way.
+bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node)
+{
+ TIntermSymbol *symNode = node->getAsSymbolNode();
+ TIntermBinary *binaryNode = node->getAsBinaryNode();
+ TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
+
+ if (swizzleNode)
+ {
+ bool ok = checkCanBeLValue(line, op, swizzleNode->getOperand());
+ if (ok && swizzleNode->hasDuplicateOffsets())
+ {
+ error(line, " l-value of swizzle cannot have duplicate components", op);
+ return false;
+ }
+ return ok;
+ }
+
+ if (binaryNode)
+ {
+ switch (binaryNode->getOp())
+ {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ return checkCanBeLValue(line, op, binaryNode->getLeft());
+ default:
+ break;
+ }
+ error(line, " l-value required", op);
+ return false;
+ }
+
+ const char *symbol = 0;
+ if (symNode != 0)
+ symbol = symNode->getSymbol().c_str();
+
+ const char *message = 0;
+ switch (node->getQualifier())
+ {
+ case EvqConst:
+ message = "can't modify a const";
+ break;
+ case EvqConstReadOnly:
+ message = "can't modify a const";
+ break;
+ case EvqAttribute:
+ message = "can't modify an attribute";
+ break;
+ case EvqFragmentIn:
+ message = "can't modify an input";
+ break;
+ case EvqVertexIn:
+ message = "can't modify an input";
+ break;
+ case EvqUniform:
+ message = "can't modify a uniform";
+ break;
+ case EvqVaryingIn:
+ message = "can't modify a varying";
+ break;
+ case EvqFragCoord:
+ message = "can't modify gl_FragCoord";
+ break;
+ case EvqFrontFacing:
+ message = "can't modify gl_FrontFacing";
+ break;
+ case EvqPointCoord:
+ message = "can't modify gl_PointCoord";
+ break;
+ case EvqNumWorkGroups:
+ message = "can't modify gl_NumWorkGroups";
+ break;
+ case EvqWorkGroupSize:
+ message = "can't modify gl_WorkGroupSize";
+ break;
+ case EvqWorkGroupID:
+ message = "can't modify gl_WorkGroupID";
+ break;
+ case EvqLocalInvocationID:
+ message = "can't modify gl_LocalInvocationID";
+ break;
+ case EvqGlobalInvocationID:
+ message = "can't modify gl_GlobalInvocationID";
+ break;
+ case EvqLocalInvocationIndex:
+ message = "can't modify gl_LocalInvocationIndex";
+ break;
+ case EvqComputeIn:
+ message = "can't modify work group size variable";
+ break;
+ default:
+ //
+ // Type that can't be written to?
+ //
+ if (node->getBasicType() == EbtVoid)
+ {
+ message = "can't modify void";
+ }
+ if (IsSampler(node->getBasicType()))
+ {
+ message = "can't modify a sampler";
+ }
+ if (IsImage(node->getBasicType()))
+ {
+ message = "can't modify an image";
+ }
+ }
+
+ if (message == 0 && binaryNode == 0 && symNode == 0)
+ {
+ error(line, " l-value required", op);
+
+ return false;
+ }
+
+ //
+ // Everything else is okay, no error.
+ //
+ if (message == 0)
+ return true;
+
+ //
+ // If we get here, we have an error and a message.
+ //
+ if (symNode)
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "\"" << symbol << "\" (" << message << ")";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, " l-value required", op, extraInfo.c_str());
+ }
+ else
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "(" << message << ")";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, " l-value required", op, extraInfo.c_str());
+ }
+
+ return false;
+}
+
+// Both test, and if necessary spit out an error, to see if the node is really
+// a constant.
+void TParseContext::checkIsConst(TIntermTyped *node)
+{
+ if (node->getQualifier() != EvqConst)
+ {
+ error(node->getLine(), "constant expression required", "");
+ }
+}
+
+// Both test, and if necessary spit out an error, to see if the node is really
+// an integer.
+void TParseContext::checkIsScalarInteger(TIntermTyped *node, const char *token)
+{
+ if (!node->isScalarInt())
+ {
+ error(node->getLine(), "integer expression required", token);
+ }
+}
+
+// Both test, and if necessary spit out an error, to see if we are currently
+// globally scoped.
+bool TParseContext::checkIsAtGlobalLevel(const TSourceLoc &line, const char *token)
+{
+ if (!symbolTable.atGlobalLevel())
+ {
+ error(line, "only allowed at global scope", token);
+ return false;
+ }
+ return true;
+}
+
+// For now, keep it simple: if it starts "gl_", it's reserved, independent
+// of scope. Except, if the symbol table is at the built-in push-level,
+// which is when we are parsing built-ins.
+// Also checks for "webgl_" and "_webgl_" reserved identifiers if parsing a
+// webgl shader.
+bool TParseContext::checkIsNotReserved(const TSourceLoc &line, const TString &identifier)
+{
+ static const char *reservedErrMsg = "reserved built-in name";
+ if (!symbolTable.atBuiltInLevel())
+ {
+ if (identifier.compare(0, 3, "gl_") == 0)
+ {
+ error(line, reservedErrMsg, "gl_");
+ return false;
+ }
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ if (identifier.compare(0, 6, "webgl_") == 0)
+ {
+ error(line, reservedErrMsg, "webgl_");
+ return false;
+ }
+ if (identifier.compare(0, 7, "_webgl_") == 0)
+ {
+ error(line, reservedErrMsg, "_webgl_");
+ return false;
+ }
+ }
+ if (identifier.find("__") != TString::npos)
+ {
+ error(line,
+ "identifiers containing two consecutive underscores (__) are reserved as "
+ "possible future keywords",
+ identifier.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Make sure there is enough data provided to the constructor to build
+// something of the type of the constructor. Also returns the type of
+// the constructor.
+bool TParseContext::checkConstructorArguments(const TSourceLoc &line,
+ TIntermNode *argumentsNode,
+ const TFunction &function,
+ TOperator op,
+ const TType &type)
+{
+ bool constructingMatrix = false;
+ switch (op)
+ {
+ case EOpConstructMat2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4:
+ constructingMatrix = true;
+ break;
+ default:
+ break;
+ }
+
+ //
+ // Note: It's okay to have too many components available, but not okay to have unused
+ // arguments. 'full' will go to true when enough args have been seen. If we loop
+ // again, there is an extra argument, so 'overfull' will become true.
+ //
+
+ size_t size = 0;
+ bool full = false;
+ bool overFull = false;
+ bool matrixInMatrix = false;
+ bool arrayArg = false;
+ for (size_t i = 0; i < function.getParamCount(); ++i)
+ {
+ const TConstParameter &param = function.getParam(i);
+ size += param.type->getObjectSize();
+
+ if (constructingMatrix && param.type->isMatrix())
+ matrixInMatrix = true;
+ if (full)
+ overFull = true;
+ if (op != EOpConstructStruct && !type.isArray() && size >= type.getObjectSize())
+ full = true;
+ if (param.type->isArray())
+ arrayArg = true;
+ }
+
+ if (type.isArray())
+ {
+ // The size of an unsized constructor should already have been determined.
+ ASSERT(!type.isUnsizedArray());
+ if (static_cast<size_t>(type.getArraySize()) != function.getParamCount())
+ {
+ error(line, "array constructor needs one argument per array element", "constructor");
+ return false;
+ }
+ }
+
+ if (arrayArg && op != EOpConstructStruct)
+ {
+ error(line, "constructing from a non-dereferenced array", "constructor");
+ return false;
+ }
+
+ if (matrixInMatrix && !type.isArray())
+ {
+ if (function.getParamCount() != 1)
+ {
+ error(line, "constructing matrix from matrix can only take one argument",
+ "constructor");
+ return false;
+ }
+ }
+
+ if (overFull)
+ {
+ error(line, "too many arguments", "constructor");
+ return false;
+ }
+
+ if (op == EOpConstructStruct && !type.isArray() &&
+ type.getStruct()->fields().size() != function.getParamCount())
+ {
+ error(line,
+ "Number of constructor parameters does not match the number of structure fields",
+ "constructor");
+ return false;
+ }
+
+ if (!type.isMatrix() || !matrixInMatrix)
+ {
+ if ((op != EOpConstructStruct && size != 1 && size < type.getObjectSize()) ||
+ (op == EOpConstructStruct && size < type.getObjectSize()))
+ {
+ error(line, "not enough data provided for construction", "constructor");
+ return false;
+ }
+ }
+
+ if (argumentsNode == nullptr)
+ {
+ error(line, "constructor does not have any arguments", "constructor");
+ return false;
+ }
+
+ TIntermAggregate *argumentsAgg = argumentsNode->getAsAggregate();
+ for (TIntermNode *&argNode : *argumentsAgg->getSequence())
+ {
+ TIntermTyped *argTyped = argNode->getAsTyped();
+ ASSERT(argTyped != nullptr);
+ if (op != EOpConstructStruct && IsSampler(argTyped->getBasicType()))
+ {
+ error(line, "cannot convert a sampler", "constructor");
+ return false;
+ }
+ if (op != EOpConstructStruct && IsImage(argTyped->getBasicType()))
+ {
+ error(line, "cannot convert an image", "constructor");
+ return false;
+ }
+ if (argTyped->getBasicType() == EbtVoid)
+ {
+ error(line, "cannot convert a void", "constructor");
+ return false;
+ }
+ }
+
+ if (type.isArray())
+ {
+ // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of
+ // the array.
+ for (TIntermNode *&argNode : *argumentsAgg->getSequence())
+ {
+ const TType &argType = argNode->getAsTyped()->getType();
+ // It has already been checked that the argument is not an array.
+ ASSERT(!argType.isArray());
+ if (!argType.sameElementType(type))
+ {
+ error(line, "Array constructor argument has an incorrect type", "Error");
+ return false;
+ }
+ }
+ }
+ else if (op == EOpConstructStruct)
+ {
+ const TFieldList &fields = type.getStruct()->fields();
+ TIntermSequence *args = argumentsAgg->getSequence();
+
+ for (size_t i = 0; i < fields.size(); i++)
+ {
+ if (i >= args->size() || (*args)[i]->getAsTyped()->getType() != *fields[i]->type())
+ {
+ error(line, "Structure constructor arguments do not match structure fields",
+ "Error");
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// This function checks to see if a void variable has been declared and raise an error message for
+// such a case
+//
+// returns true in case of an error
+//
+bool TParseContext::checkIsNonVoid(const TSourceLoc &line,
+ const TString &identifier,
+ const TBasicType &type)
+{
+ if (type == EbtVoid)
+ {
+ error(line, "illegal use of type 'void'", identifier.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// This function checks to see if the node (for the expression) contains a scalar boolean expression
+// or not.
+void TParseContext::checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type)
+{
+ if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector())
+ {
+ error(line, "boolean expression expected", "");
+ }
+}
+
+// This function checks to see if the node (for the expression) contains a scalar boolean expression
+// or not.
+void TParseContext::checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType)
+{
+ if (pType.getBasicType() != EbtBool || pType.isAggregate())
+ {
+ error(line, "boolean expression expected", "");
+ }
+}
+
+bool TParseContext::checkIsNotSampler(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason)
+{
+ if (pType.type == EbtStruct)
+ {
+ if (ContainsSampler(*pType.userDef))
+ {
+ error(line, reason, getBasicString(pType.type), "(structure contains a sampler)");
+ return false;
+ }
+
+ return true;
+ }
+ else if (IsSampler(pType.type))
+ {
+ error(line, reason, getBasicString(pType.type));
+ return false;
+ }
+
+ return true;
+}
+
+bool TParseContext::checkIsNotImage(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason)
+{
+ if (pType.type == EbtStruct)
+ {
+ if (ContainsImage(*pType.userDef))
+ {
+ error(line, reason, getBasicString(pType.type), "(structure contains an image)");
+
+ return false;
+ }
+
+ return true;
+ }
+ else if (IsImage(pType.type))
+ {
+ error(line, reason, getBasicString(pType.type));
+
+ return false;
+ }
+
+ return true;
+}
+
+void TParseContext::checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line,
+ const TPublicType &pType)
+{
+ if (pType.layoutQualifier.location != -1)
+ {
+ error(line, "location must only be specified for a single input or output variable",
+ "location");
+ }
+}
+
+void TParseContext::checkLocationIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier)
+{
+ if (layoutQualifier.location != -1)
+ {
+ error(location, "invalid layout qualifier:", "location",
+ "only valid on program inputs and outputs");
+ }
+}
+
+void TParseContext::checkOutParameterIsNotOpaqueType(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type)
+{
+ checkOutParameterIsNotSampler(line, qualifier, type);
+ checkOutParameterIsNotImage(line, qualifier, type);
+}
+
+void TParseContext::checkOutParameterIsNotSampler(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type)
+{
+ ASSERT(qualifier == EvqOut || qualifier == EvqInOut);
+ if (IsSampler(type.getBasicType()))
+ {
+ error(line, "samplers cannot be output parameters", type.getBasicString());
+ }
+}
+
+void TParseContext::checkOutParameterIsNotImage(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type)
+{
+ ASSERT(qualifier == EvqOut || qualifier == EvqInOut);
+ if (IsImage(type.getBasicType()))
+ {
+ error(line, "images cannot be output parameters", type.getBasicString());
+ }
+}
+
+// Do size checking for an array type's size.
+unsigned int TParseContext::checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr)
+{
+ TIntermConstantUnion *constant = expr->getAsConstantUnion();
+
+ // TODO(oetuaho@nvidia.com): Get rid of the constant == nullptr check here once all constant
+ // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't
+ // fold as array size.
+ if (expr->getQualifier() != EvqConst || constant == nullptr || !constant->isScalarInt())
+ {
+ error(line, "array size must be a constant integer expression", "");
+ return 1u;
+ }
+
+ unsigned int size = 0u;
+
+ if (constant->getBasicType() == EbtUInt)
+ {
+ size = constant->getUConst(0);
+ }
+ else
+ {
+ int signedSize = constant->getIConst(0);
+
+ if (signedSize < 0)
+ {
+ error(line, "array size must be non-negative", "");
+ return 1u;
+ }
+
+ size = static_cast<unsigned int>(signedSize);
+ }
+
+ if (size == 0u)
+ {
+ error(line, "array size must be greater than zero", "");
+ return 1u;
+ }
+
+ // The size of arrays is restricted here to prevent issues further down the
+ // compiler/translator/driver stack. Shader Model 5 generation hardware is limited to
+ // 4096 registers so this should be reasonable even for aggressively optimizable code.
+ const unsigned int sizeLimit = 65536;
+
+ if (size > sizeLimit)
+ {
+ error(line, "array size too large", "");
+ return 1u;
+ }
+
+ return size;
+}
+
+// See if this qualifier can be an array.
+bool TParseContext::checkIsValidQualifierForArray(const TSourceLoc &line,
+ const TPublicType &elementQualifier)
+{
+ if ((elementQualifier.qualifier == EvqAttribute) ||
+ (elementQualifier.qualifier == EvqVertexIn) ||
+ (elementQualifier.qualifier == EvqConst && mShaderVersion < 300))
+ {
+ error(line, "cannot declare arrays of this qualifier",
+ TType(elementQualifier).getQualifierString());
+ return false;
+ }
+
+ return true;
+}
+
+// See if this element type can be formed into an array.
+bool TParseContext::checkIsValidTypeForArray(const TSourceLoc &line, const TPublicType &elementType)
+{
+ //
+ // Can the type be an array?
+ //
+ if (elementType.array)
+ {
+ error(line, "cannot declare arrays of arrays",
+ TType(elementType).getCompleteString().c_str());
+ return false;
+ }
+ // In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere.
+ // In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section
+ // 4.3.4).
+ if (mShaderVersion >= 300 && elementType.getBasicType() == EbtStruct &&
+ sh::IsVarying(elementType.qualifier))
+ {
+ error(line, "cannot declare arrays of structs of this qualifier",
+ TType(elementType).getCompleteString().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+// Check if this qualified element type can be formed into an array.
+bool TParseContext::checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
+ const TPublicType &elementType)
+{
+ if (checkIsValidTypeForArray(indexLocation, elementType))
+ {
+ return checkIsValidQualifierForArray(indexLocation, elementType);
+ }
+ return false;
+}
+
+// Enforce non-initializer type/qualifier rules.
+void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ TPublicType *type)
+{
+ ASSERT(type != nullptr);
+ if (type->qualifier == EvqConst)
+ {
+ // Make the qualifier make sense.
+ type->qualifier = EvqTemporary;
+
+ // Generate informative error messages for ESSL1.
+ // In ESSL3 arrays and structures containing arrays can be constant.
+ if (mShaderVersion < 300 && type->isStructureContainingArrays())
+ {
+ error(line,
+ "structures containing arrays may not be declared constant since they cannot be "
+ "initialized",
+ identifier.c_str());
+ }
+ else
+ {
+ error(line, "variables with qualifier 'const' must be initialized", identifier.c_str());
+ }
+ return;
+ }
+ if (type->isUnsizedArray())
+ {
+ error(line, "implicitly sized arrays need to be initialized", identifier.c_str());
+ }
+}
+
+// Do some simple checks that are shared between all variable declarations,
+// and update the symbol table.
+//
+// Returns true if declaring the variable succeeded.
+//
+bool TParseContext::declareVariable(const TSourceLoc &line,
+ const TString &identifier,
+ const TType &type,
+ TVariable **variable)
+{
+ ASSERT((*variable) == nullptr);
+
+ bool needsReservedCheck = true;
+
+ // gl_LastFragData may be redeclared with a new precision qualifier
+ if (type.isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
+ {
+ const TVariable *maxDrawBuffers = static_cast<const TVariable *>(
+ symbolTable.findBuiltIn("gl_MaxDrawBuffers", mShaderVersion));
+ if (static_cast<int>(type.getArraySize()) == maxDrawBuffers->getConstPointer()->getIConst())
+ {
+ if (TSymbol *builtInSymbol = symbolTable.findBuiltIn(identifier, mShaderVersion))
+ {
+ needsReservedCheck = !checkCanUseExtension(line, builtInSymbol->getExtension());
+ }
+ }
+ else
+ {
+ error(line, "redeclaration of gl_LastFragData with size != gl_MaxDrawBuffers",
+ identifier.c_str());
+ return false;
+ }
+ }
+
+ if (needsReservedCheck && !checkIsNotReserved(line, identifier))
+ return false;
+
+ (*variable) = new TVariable(&identifier, type);
+ if (!symbolTable.declare(*variable))
+ {
+ error(line, "redefinition", identifier.c_str());
+ *variable = nullptr;
+ return false;
+ }
+
+ if (!checkIsNonVoid(line, identifier, type.getBasicType()))
+ return false;
+
+ return true;
+}
+
+void TParseContext::checkIsParameterQualifierValid(
+ const TSourceLoc &line,
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TType *type)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics);
+
+ if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
+ {
+ checkOutParameterIsNotOpaqueType(line, typeQualifier.qualifier, *type);
+ }
+
+ if (!IsImage(type->getBasicType()))
+ {
+ checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, line);
+ }
+ else
+ {
+ type->setMemoryQualifier(typeQualifier.memoryQualifier);
+ }
+
+ type->setQualifier(typeQualifier.qualifier);
+
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ type->setPrecision(typeQualifier.precision);
+ }
+}
+
+bool TParseContext::checkCanUseExtension(const TSourceLoc &line, const TString &extension)
+{
+ const TExtensionBehavior &extBehavior = extensionBehavior();
+ TExtensionBehavior::const_iterator iter = extBehavior.find(extension.c_str());
+ if (iter == extBehavior.end())
+ {
+ error(line, "extension", extension.c_str(), "is not supported");
+ return false;
+ }
+ // In GLSL ES, an extension's default behavior is "disable".
+ if (iter->second == EBhDisable || iter->second == EBhUndefined)
+ {
+ error(line, "extension", extension.c_str(), "is disabled");
+ return false;
+ }
+ if (iter->second == EBhWarn)
+ {
+ warning(line, "extension", extension.c_str(), "is being used");
+ return true;
+ }
+
+ return true;
+}
+
+// These checks are common for all declarations starting a declarator list, and declarators that
+// follow an empty declaration.
+void TParseContext::singleDeclarationErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation)
+{
+ switch (publicType.qualifier)
+ {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ case EvqAttribute:
+ case EvqVertexIn:
+ case EvqFragmentOut:
+ case EvqComputeIn:
+ if (publicType.getBasicType() == EbtStruct)
+ {
+ error(identifierLocation, "cannot be used with a structure",
+ getQualifierString(publicType.qualifier));
+ return;
+ }
+
+ default:
+ break;
+ }
+
+ if (publicType.qualifier != EvqUniform &&
+ !checkIsNotSampler(identifierLocation, publicType.typeSpecifierNonArray,
+ "samplers must be uniform"))
+ {
+ return;
+ }
+ if (publicType.qualifier != EvqUniform &&
+ !checkIsNotImage(identifierLocation, publicType.typeSpecifierNonArray,
+ "images must be uniform"))
+ {
+ return;
+ }
+
+ // check for layout qualifier issues
+ const TLayoutQualifier layoutQualifier = publicType.layoutQualifier;
+
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ error(identifierLocation, "layout qualifier",
+ getMatrixPackingString(layoutQualifier.matrixPacking),
+ "only valid for interface blocks");
+ return;
+ }
+
+ if (layoutQualifier.blockStorage != EbsUnspecified)
+ {
+ error(identifierLocation, "layout qualifier",
+ getBlockStorageString(layoutQualifier.blockStorage),
+ "only valid for interface blocks");
+ return;
+ }
+
+ if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut)
+ {
+ checkLocationIsNotSpecified(identifierLocation, publicType.layoutQualifier);
+ }
+
+ if (IsImage(publicType.getBasicType()))
+ {
+
+ switch (layoutQualifier.imageInternalFormat)
+ {
+ case EiifRGBA32F:
+ case EiifRGBA16F:
+ case EiifR32F:
+ case EiifRGBA8:
+ case EiifRGBA8_SNORM:
+ if (!IsFloatImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires a floating image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifRGBA32I:
+ case EiifRGBA16I:
+ case EiifRGBA8I:
+ case EiifR32I:
+ if (!IsIntegerImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires an integer image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifRGBA32UI:
+ case EiifRGBA16UI:
+ case EiifRGBA8UI:
+ case EiifR32UI:
+ if (!IsUnsignedImage(publicType.getBasicType()))
+ {
+ error(identifierLocation,
+ "internal image format requires an unsigned image type",
+ getBasicString(publicType.getBasicType()));
+ return;
+ }
+ break;
+ case EiifUnspecified:
+ error(identifierLocation, "layout qualifier", "No image internal format specified");
+ return;
+ default:
+ error(identifierLocation, "layout qualifier", "unrecognized token");
+ return;
+ }
+
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ switch (layoutQualifier.imageInternalFormat)
+ {
+ case EiifR32F:
+ case EiifR32I:
+ case EiifR32UI:
+ break;
+ default:
+ if (!publicType.memoryQualifier.readonly && !publicType.memoryQualifier.writeonly)
+ {
+ error(identifierLocation, "layout qualifier",
+ "Except for images with the r32f, r32i and r32ui format qualifiers, "
+ "image variables must be qualified readonly and/or writeonly");
+ return;
+ }
+ break;
+ }
+ }
+ else
+ {
+
+ if (!checkInternalFormatIsNotSpecified(identifierLocation,
+ layoutQualifier.imageInternalFormat))
+ {
+ return;
+ }
+
+ if (!checkIsMemoryQualifierNotSpecified(publicType.memoryQualifier, identifierLocation))
+ {
+ return;
+ }
+ }
+}
+
+void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired)
+{
+
+ if (mShaderVersion < versionRequired)
+ {
+ error(location, "invalid layout qualifier:", layoutQualifierName.c_str(), "not supported");
+ }
+}
+
+bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier)
+{
+ const sh::WorkGroupSize &localSize = layoutQualifier.localSize;
+ for (size_t i = 0u; i < localSize.size(); ++i)
+ {
+ if (localSize[i] != -1)
+ {
+ error(location, "invalid layout qualifier:", getWorkGroupSizeString(i),
+ "only valid when used with 'in' in a compute shader global layout declaration");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TParseContext::checkInternalFormatIsNotSpecified(const TSourceLoc &location,
+ TLayoutImageInternalFormat internalFormat)
+{
+ if (internalFormat != EiifUnspecified)
+ {
+ error(location, "invalid layout qualifier:", getImageInternalFormatString(internalFormat),
+ "only valid when used with images");
+ return false;
+ }
+ return true;
+}
+
+void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
+ TIntermAggregate *fnCall)
+{
+ for (size_t i = 0; i < fnCandidate->getParamCount(); ++i)
+ {
+ TQualifier qual = fnCandidate->getParam(i).type->getQualifier();
+ if (qual == EvqOut || qual == EvqInOut)
+ {
+ TIntermTyped *argument = (*(fnCall->getSequence()))[i]->getAsTyped();
+ if (!checkCanBeLValue(argument->getLine(), "assign", argument))
+ {
+ error(argument->getLine(),
+ "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error");
+ return;
+ }
+ }
+ }
+}
+
+void TParseContext::checkInvariantVariableQualifier(bool invariant,
+ const TQualifier qualifier,
+ const TSourceLoc &invariantLocation)
+{
+ if (!invariant)
+ return;
+
+ if (mShaderVersion < 300)
+ {
+ // input variables in the fragment shader can be also qualified as invariant
+ if (!sh::CanBeInvariantESSL1(qualifier))
+ {
+ error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+ }
+ }
+ else
+ {
+ if (!sh::CanBeInvariantESSL3OrGreater(qualifier))
+ {
+ error(invariantLocation, "Cannot be qualified as invariant.", "invariant");
+ }
+ }
+}
+
+bool TParseContext::supportsExtension(const char *extension)
+{
+ const TExtensionBehavior &extbehavior = extensionBehavior();
+ TExtensionBehavior::const_iterator iter = extbehavior.find(extension);
+ return (iter != extbehavior.end());
+}
+
+bool TParseContext::isExtensionEnabled(const char *extension) const
+{
+ return ::IsExtensionEnabled(extensionBehavior(), extension);
+}
+
+void TParseContext::handleExtensionDirective(const TSourceLoc &loc,
+ const char *extName,
+ const char *behavior)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ mDirectiveHandler.handleExtension(srcLoc, extName, behavior);
+}
+
+void TParseContext::handlePragmaDirective(const TSourceLoc &loc,
+ const char *name,
+ const char *value,
+ bool stdgl)
+{
+ pp::SourceLocation srcLoc;
+ srcLoc.file = loc.first_file;
+ srcLoc.line = loc.first_line;
+ mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl);
+}
+
+sh::WorkGroupSize TParseContext::getComputeShaderLocalSize() const
+{
+ sh::WorkGroupSize result;
+ for (size_t i = 0u; i < result.size(); ++i)
+ {
+ if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
+ {
+ result[i] = 1;
+ }
+ else
+ {
+ result[i] = mComputeShaderLocalSize[i];
+ }
+ }
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+//
+// Non-Errors.
+//
+/////////////////////////////////////////////////////////////////////////////////
+
+const TVariable *TParseContext::getNamedVariable(const TSourceLoc &location,
+ const TString *name,
+ const TSymbol *symbol)
+{
+ const TVariable *variable = NULL;
+
+ if (!symbol)
+ {
+ error(location, "undeclared identifier", name->c_str());
+ }
+ else if (!symbol->isVariable())
+ {
+ error(location, "variable expected", name->c_str());
+ }
+ else
+ {
+ variable = static_cast<const TVariable *>(symbol);
+
+ if (symbolTable.findBuiltIn(variable->getName(), mShaderVersion) &&
+ !variable->getExtension().empty())
+ {
+ checkCanUseExtension(location, variable->getExtension());
+ }
+
+ // Reject shaders using both gl_FragData and gl_FragColor
+ TQualifier qualifier = variable->getType().getQualifier();
+ if (qualifier == EvqFragData || qualifier == EvqSecondaryFragDataEXT)
+ {
+ mUsesFragData = true;
+ }
+ else if (qualifier == EvqFragColor || qualifier == EvqSecondaryFragColorEXT)
+ {
+ mUsesFragColor = true;
+ }
+ if (qualifier == EvqSecondaryFragDataEXT || qualifier == EvqSecondaryFragColorEXT)
+ {
+ mUsesSecondaryOutputs = true;
+ }
+
+ // This validation is not quite correct - it's only an error to write to
+ // both FragData and FragColor. For simplicity, and because users shouldn't
+ // be rewarded for reading from undefined varaibles, return an error
+ // if they are both referenced, rather than assigned.
+ if (mUsesFragData && mUsesFragColor)
+ {
+ const char *errorMessage = "cannot use both gl_FragData and gl_FragColor";
+ if (mUsesSecondaryOutputs)
+ {
+ errorMessage =
+ "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)"
+ " and (gl_FragColor, gl_SecondaryFragColorEXT)";
+ }
+ error(location, errorMessage, name->c_str());
+ }
+
+ // GLSL ES 3.1 Revision 4, 7.1.3 Compute Shader Special Variables
+ if (getShaderType() == GL_COMPUTE_SHADER && !mComputeShaderLocalSizeDeclared &&
+ qualifier == EvqWorkGroupSize)
+ {
+ error(location,
+ "It is an error to use gl_WorkGroupSize before declaring the local group size",
+ "gl_WorkGroupSize");
+ }
+ }
+
+ if (!variable)
+ {
+ TType type(EbtFloat, EbpUndefined);
+ TVariable *fakeVariable = new TVariable(name, type);
+ symbolTable.declare(fakeVariable);
+ variable = fakeVariable;
+ }
+
+ return variable;
+}
+
+TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
+ const TString *name,
+ const TSymbol *symbol)
+{
+ const TVariable *variable = getNamedVariable(location, name, symbol);
+
+ if (variable->getConstPointer())
+ {
+ const TConstantUnion *constArray = variable->getConstPointer();
+ return intermediate.addConstantUnion(constArray, variable->getType(), location);
+ }
+ else
+ {
+ return intermediate.addSymbol(variable->getUniqueId(), variable->getName(),
+ variable->getType(), location);
+ }
+}
+
+//
+// Look up a function name in the symbol table, and make sure it is a function.
+//
+// Return the function symbol if found, otherwise 0.
+//
+const TFunction *TParseContext::findFunction(const TSourceLoc &line,
+ TFunction *call,
+ int inputShaderVersion,
+ bool *builtIn)
+{
+ // First find by unmangled name to check whether the function name has been
+ // hidden by a variable name or struct typename.
+ // If a function is found, check for one with a matching argument list.
+ const TSymbol *symbol = symbolTable.find(call->getName(), inputShaderVersion, builtIn);
+ if (symbol == 0 || symbol->isFunction())
+ {
+ symbol = symbolTable.find(call->getMangledName(), inputShaderVersion, builtIn);
+ }
+
+ if (symbol == 0)
+ {
+ error(line, "no matching overloaded function found", call->getName().c_str());
+ return 0;
+ }
+
+ if (!symbol->isFunction())
+ {
+ error(line, "function name expected", call->getName().c_str());
+ return 0;
+ }
+
+ return static_cast<const TFunction *>(symbol);
+}
+
+//
+// Initializers show up in several places in the grammar. Have one set of
+// code to handle them here.
+//
+// Returns true on error, false if no error
+//
+bool TParseContext::executeInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ const TPublicType &pType,
+ TIntermTyped *initializer,
+ TIntermBinary **initNode)
+{
+ ASSERT(initNode != nullptr);
+ ASSERT(*initNode == nullptr);
+ TType type = TType(pType);
+
+ TVariable *variable = nullptr;
+ if (type.isUnsizedArray())
+ {
+ // We have not checked yet whether the initializer actually is an array or not.
+ if (initializer->isArray())
+ {
+ type.setArraySize(initializer->getArraySize());
+ }
+ else
+ {
+ // Having a non-array initializer for an unsized array will result in an error later,
+ // so we don't generate an error message here.
+ type.setArraySize(1u);
+ }
+ }
+ if (!declareVariable(line, identifier, type, &variable))
+ {
+ return true;
+ }
+
+ bool globalInitWarning = false;
+ if (symbolTable.atGlobalLevel() &&
+ !ValidateGlobalInitializer(initializer, this, &globalInitWarning))
+ {
+ // Error message does not completely match behavior with ESSL 1.00, but
+ // we want to steer developers towards only using constant expressions.
+ error(line, "global variable initializers must be constant expressions", "=");
+ return true;
+ }
+ if (globalInitWarning)
+ {
+ warning(
+ line,
+ "global variable initializers should be constant expressions "
+ "(uniforms and globals are allowed in global initializers for legacy compatibility)",
+ "=");
+ }
+
+ //
+ // identifier must be of type constant, a global, or a temporary
+ //
+ TQualifier qualifier = variable->getType().getQualifier();
+ if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal) && (qualifier != EvqConst))
+ {
+ error(line, " cannot initialize this type of qualifier ",
+ variable->getType().getQualifierString());
+ return true;
+ }
+ //
+ // test for and propagate constant
+ //
+
+ if (qualifier == EvqConst)
+ {
+ if (qualifier != initializer->getType().getQualifier())
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "'" << variable->getType().getCompleteString() << "'";
+ std::string extraInfo = extraInfoStream.str();
+ error(line, " assigning non-constant to", "=", extraInfo.c_str());
+ variable->getType().setQualifier(EvqTemporary);
+ return true;
+ }
+ if (type != initializer->getType())
+ {
+ error(line, " non-matching types for const initializer ",
+ variable->getType().getQualifierString());
+ variable->getType().setQualifier(EvqTemporary);
+ return true;
+ }
+
+ // Save the constant folded value to the variable if possible. For example array
+ // initializers are not folded, since that way copying the array literal to multiple places
+ // in the shader is avoided.
+ // TODO(oetuaho@nvidia.com): Consider constant folding array initialization in cases where
+ // it would be beneficial.
+ if (initializer->getAsConstantUnion())
+ {
+ variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer());
+ *initNode = nullptr;
+ return false;
+ }
+ else if (initializer->getAsSymbolNode())
+ {
+ const TSymbol *symbol =
+ symbolTable.find(initializer->getAsSymbolNode()->getSymbol(), 0);
+ const TVariable *tVar = static_cast<const TVariable *>(symbol);
+
+ const TConstantUnion *constArray = tVar->getConstPointer();
+ if (constArray)
+ {
+ variable->shareConstPointer(constArray);
+ *initNode = nullptr;
+ return false;
+ }
+ }
+ }
+
+ TIntermSymbol *intermSymbol = intermediate.addSymbol(
+ variable->getUniqueId(), variable->getName(), variable->getType(), line);
+ *initNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
+ if (*initNode == nullptr)
+ {
+ assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
+ return true;
+ }
+
+ return false;
+}
+
+void TParseContext::addFullySpecifiedType(TPublicType *typeSpecifier)
+{
+ checkPrecisionSpecified(typeSpecifier->getLine(), typeSpecifier->precision,
+ typeSpecifier->getBasicType());
+
+ if (mShaderVersion < 300 && typeSpecifier->array)
+ {
+ error(typeSpecifier->getLine(), "not supported", "first-class array");
+ typeSpecifier->clearArrayness();
+ }
+}
+
+TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TPublicType &typeSpecifier)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+ TPublicType returnType = typeSpecifier;
+ returnType.qualifier = typeQualifier.qualifier;
+ returnType.invariant = typeQualifier.invariant;
+ returnType.layoutQualifier = typeQualifier.layoutQualifier;
+ returnType.memoryQualifier = typeQualifier.memoryQualifier;
+ returnType.precision = typeSpecifier.precision;
+
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ returnType.precision = typeQualifier.precision;
+ }
+
+ checkPrecisionSpecified(typeSpecifier.getLine(), returnType.precision,
+ typeSpecifier.getBasicType());
+
+ checkInvariantVariableQualifier(returnType.invariant, returnType.qualifier,
+ typeSpecifier.getLine());
+
+ checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), returnType.layoutQualifier);
+
+ if (mShaderVersion < 300)
+ {
+ if (typeSpecifier.array)
+ {
+ error(typeSpecifier.getLine(), "not supported", "first-class array");
+ returnType.clearArrayness();
+ }
+
+ if (returnType.qualifier == EvqAttribute &&
+ (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
+ {
+ error(typeSpecifier.getLine(), "cannot be bool or int",
+ getQualifierString(returnType.qualifier));
+ }
+
+ if ((returnType.qualifier == EvqVaryingIn || returnType.qualifier == EvqVaryingOut) &&
+ (typeSpecifier.getBasicType() == EbtBool || typeSpecifier.getBasicType() == EbtInt))
+ {
+ error(typeSpecifier.getLine(), "cannot be bool or int",
+ getQualifierString(returnType.qualifier));
+ }
+ }
+ else
+ {
+ if (!returnType.layoutQualifier.isEmpty())
+ {
+ checkIsAtGlobalLevel(typeSpecifier.getLine(), "layout");
+ }
+ if (sh::IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn ||
+ returnType.qualifier == EvqFragmentOut)
+ {
+ checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier,
+ typeSpecifier.getLine());
+ }
+ if (returnType.qualifier == EvqComputeIn)
+ {
+ error(typeSpecifier.getLine(), "'in' can be only used to specify the local group size",
+ "in");
+ }
+ }
+
+ return returnType;
+}
+
+void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier,
+ const TPublicType &type,
+ const TSourceLoc &qualifierLocation)
+{
+ // An input/output variable can never be bool or a sampler. Samplers are checked elsewhere.
+ if (type.getBasicType() == EbtBool)
+ {
+ error(qualifierLocation, "cannot be bool", getQualifierString(qualifier));
+ }
+
+ // Specific restrictions apply for vertex shader inputs and fragment shader outputs.
+ switch (qualifier)
+ {
+ case EvqVertexIn:
+ // ESSL 3.00 section 4.3.4
+ if (type.array)
+ {
+ error(qualifierLocation, "cannot be array", getQualifierString(qualifier));
+ }
+ // Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck
+ return;
+ case EvqFragmentOut:
+ // ESSL 3.00 section 4.3.6
+ if (type.typeSpecifierNonArray.isMatrix())
+ {
+ error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier));
+ }
+ // Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck
+ return;
+ default:
+ break;
+ }
+
+ // Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of
+ // restrictions.
+ bool typeContainsIntegers =
+ (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt ||
+ type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt));
+ if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut)
+ {
+ error(qualifierLocation, "must use 'flat' interpolation here",
+ getQualifierString(qualifier));
+ }
+
+ if (type.getBasicType() == EbtStruct)
+ {
+ // ESSL 3.00 sections 4.3.4 and 4.3.6.
+ // These restrictions are only implied by the ESSL 3.00 spec, but
+ // the ESSL 3.10 spec lists these restrictions explicitly.
+ if (type.array)
+ {
+ error(qualifierLocation, "cannot be an array of structures",
+ getQualifierString(qualifier));
+ }
+ if (type.isStructureContainingArrays())
+ {
+ error(qualifierLocation, "cannot be a structure containing an array",
+ getQualifierString(qualifier));
+ }
+ if (type.isStructureContainingType(EbtStruct))
+ {
+ error(qualifierLocation, "cannot be a structure containing a structure",
+ getQualifierString(qualifier));
+ }
+ if (type.isStructureContainingType(EbtBool))
+ {
+ error(qualifierLocation, "cannot be a structure containing a bool",
+ getQualifierString(qualifier));
+ }
+ }
+}
+
+void TParseContext::checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier)
+{
+ if (qualifier.getType() == QtStorage)
+ {
+ const TStorageQualifierWrapper &storageQualifier =
+ static_cast<const TStorageQualifierWrapper &>(qualifier);
+ if (!declaringFunction() && storageQualifier.getQualifier() != EvqConst &&
+ !symbolTable.atGlobalLevel())
+ {
+ error(storageQualifier.getLine(),
+ "Local variables can only use the const storage qualifier.",
+ storageQualifier.getQualifierString().c_str());
+ }
+ }
+}
+
+bool TParseContext::checkIsMemoryQualifierNotSpecified(const TMemoryQualifier &memoryQualifier,
+ const TSourceLoc &location)
+{
+ if (memoryQualifier.readonly)
+ {
+ error(location, "Only allowed with images.", "readonly");
+ return false;
+ }
+ if (memoryQualifier.writeonly)
+ {
+ error(location, "Only allowed with images.", "writeonly");
+ return false;
+ }
+ if (memoryQualifier.coherent)
+ {
+ error(location, "Only allowed with images.", "coherent");
+ return false;
+ }
+ if (memoryQualifier.restrictQualifier)
+ {
+ error(location, "Only allowed with images.", "restrict");
+ return false;
+ }
+ if (memoryQualifier.volatileQualifier)
+ {
+ error(location, "Only allowed with images.", "volatile");
+ return false;
+ }
+ return true;
+}
+
+TIntermDeclaration *TParseContext::parseSingleDeclaration(
+ TPublicType &publicType,
+ const TSourceLoc &identifierOrTypeLocation,
+ const TString &identifier)
+{
+ TType type(publicType);
+ if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
+ mDirectiveHandler.pragma().stdgl.invariantAll)
+ {
+ TQualifier qualifier = type.getQualifier();
+
+ // The directive handler has already taken care of rejecting invalid uses of this pragma
+ // (for example, in ESSL 3.00 fragment shaders), so at this point, flatten it into all
+ // affected variable declarations:
+ //
+ // 1. Built-in special variables which are inputs to the fragment shader. (These are handled
+ // elsewhere, in TranslatorGLSL.)
+ //
+ // 2. Outputs from vertex shaders in ESSL 1.00 and 3.00 (EvqVaryingOut and EvqVertexOut). It
+ // is actually less likely that there will be bugs in the handling of ESSL 3.00 shaders, but
+ // the way this is currently implemented we have to enable this compiler option before
+ // parsing the shader and determining the shading language version it uses. If this were
+ // implemented as a post-pass, the workaround could be more targeted.
+ //
+ // 3. Inputs in ESSL 1.00 fragment shaders (EvqVaryingIn). This is somewhat in violation of
+ // the specification, but there are desktop OpenGL drivers that expect that this is the
+ // behavior of the #pragma when specified in ESSL 1.00 fragment shaders.
+ if (qualifier == EvqVaryingOut || qualifier == EvqVertexOut || qualifier == EvqVaryingIn)
+ {
+ type.setInvariant(true);
+ }
+ }
+
+ TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, type, identifierOrTypeLocation);
+
+ bool emptyDeclaration = (identifier == "");
+
+ mDeferredSingleDeclarationErrorCheck = emptyDeclaration;
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierOrTypeLocation);
+
+ if (emptyDeclaration)
+ {
+ if (publicType.isUnsizedArray())
+ {
+ // ESSL3 spec section 4.1.9: Array declaration which leaves the size unspecified is an
+ // error. It is assumed that this applies to empty declarations as well.
+ error(identifierOrTypeLocation, "empty array declaration needs to specify a size",
+ identifier.c_str());
+ }
+ }
+ else
+ {
+ singleDeclarationErrorCheck(publicType, identifierOrTypeLocation);
+
+ checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &publicType);
+
+ TVariable *variable = nullptr;
+ declareVariable(identifierOrTypeLocation, identifier, type, &variable);
+
+ if (variable && symbol)
+ {
+ symbol->setId(variable->getUniqueId());
+ }
+ }
+
+ // We append the symbol even if the declaration is empty, mainly because of struct declarations
+ // that may just declare a type.
+ declaration->appendDeclarator(symbol);
+
+ return declaration;
+}
+
+TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression)
+{
+ mDeferredSingleDeclarationErrorCheck = false;
+
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
+
+ checkIsValidTypeAndQualifierForArray(indexLocation, publicType);
+
+ TType arrayType(publicType);
+
+ unsigned int size = checkIsValidArraySize(identifierLocation, indexExpression);
+ // Make the type an array even if size check failed.
+ // This ensures useless error messages regarding the variable's non-arrayness won't follow.
+ arrayType.setArraySize(size);
+
+ TVariable *variable = nullptr;
+ declareVariable(identifierLocation, identifier, arrayType, &variable);
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
+
+ TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
+ if (variable && symbol)
+ {
+ symbol->setId(variable->getUniqueId());
+ declaration->appendDeclarator(symbol);
+ }
+
+ return declaration;
+}
+
+TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer)
+{
+ mDeferredSingleDeclarationErrorCheck = false;
+
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
+
+ TIntermBinary *initNode = nullptr;
+ if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode))
+ {
+ if (initNode)
+ {
+ declaration->appendDeclarator(initNode);
+ }
+ }
+ return declaration;
+}
+
+TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration(
+ TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer)
+{
+ mDeferredSingleDeclarationErrorCheck = false;
+
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+
+ checkIsValidTypeAndQualifierForArray(indexLocation, publicType);
+
+ TPublicType arrayType(publicType);
+
+ unsigned int size = 0u;
+ // If indexExpression is nullptr, then the array will eventually get its size implicitly from
+ // the initializer.
+ if (indexExpression != nullptr)
+ {
+ size = checkIsValidArraySize(identifierLocation, indexExpression);
+ }
+ // Make the type an array even if size check failed.
+ // This ensures useless error messages regarding the variable's non-arrayness won't follow.
+ arrayType.setArraySize(size);
+
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->setLine(identifierLocation);
+
+ // initNode will correspond to the whole of "type b[n] = initializer".
+ TIntermBinary *initNode = nullptr;
+ if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
+ {
+ if (initNode)
+ {
+ declaration->appendDeclarator(initNode);
+ }
+ }
+
+ return declaration;
+}
+
+TIntermAggregate *TParseContext::parseInvariantDeclaration(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &identifierLoc,
+ const TString *identifier,
+ const TSymbol *symbol)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+ if (!typeQualifier.invariant)
+ {
+ error(identifierLoc, "Expected invariant", identifier->c_str());
+ return nullptr;
+ }
+ if (!checkIsAtGlobalLevel(identifierLoc, "invariant varying"))
+ {
+ return nullptr;
+ }
+ if (!symbol)
+ {
+ error(identifierLoc, "undeclared identifier declared as invariant", identifier->c_str());
+ return nullptr;
+ }
+ if (!IsQualifierUnspecified(typeQualifier.qualifier))
+ {
+ error(identifierLoc, "invariant declaration specifies qualifier",
+ getQualifierString(typeQualifier.qualifier));
+ }
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ error(identifierLoc, "invariant declaration specifies precision",
+ getPrecisionString(typeQualifier.precision));
+ }
+ if (!typeQualifier.layoutQualifier.isEmpty())
+ {
+ error(identifierLoc, "invariant declaration specifies layout", "'layout'");
+ }
+
+ const TVariable *variable = getNamedVariable(identifierLoc, identifier, symbol);
+ ASSERT(variable);
+ const TType &type = variable->getType();
+
+ checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(),
+ typeQualifier.line);
+ checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ symbolTable.addInvariantVarying(std::string(identifier->c_str()));
+
+ TIntermSymbol *intermSymbol =
+ intermediate.addSymbol(variable->getUniqueId(), *identifier, type, identifierLoc);
+
+ TIntermAggregate *aggregate = TIntermediate::MakeAggregate(intermSymbol, identifierLoc);
+ aggregate->setOp(EOpInvariantDeclaration);
+ return aggregate;
+}
+
+void TParseContext::parseDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ TIntermDeclaration *declarationOut)
+{
+ // If the declaration starting this declarator list was empty (example: int,), some checks were
+ // not performed.
+ if (mDeferredSingleDeclarationErrorCheck)
+ {
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredSingleDeclarationErrorCheck = false;
+ }
+
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
+
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
+
+ TVariable *variable = nullptr;
+ declareVariable(identifierLocation, identifier, TType(publicType), &variable);
+
+ TIntermSymbol *symbol =
+ intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation);
+ if (variable && symbol)
+ {
+ symbol->setId(variable->getUniqueId());
+ declarationOut->appendDeclarator(symbol);
+ }
+}
+
+void TParseContext::parseArrayDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &arrayLocation,
+ TIntermTyped *indexExpression,
+ TIntermDeclaration *declarationOut)
+{
+ // If the declaration starting this declarator list was empty (example: int,), some checks were
+ // not performed.
+ if (mDeferredSingleDeclarationErrorCheck)
+ {
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredSingleDeclarationErrorCheck = false;
+ }
+
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
+
+ checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
+
+ if (checkIsValidTypeAndQualifierForArray(arrayLocation, publicType))
+ {
+ TType arrayType = TType(publicType);
+ unsigned int size = checkIsValidArraySize(arrayLocation, indexExpression);
+ arrayType.setArraySize(size);
+
+ TVariable *variable = nullptr;
+ declareVariable(identifierLocation, identifier, arrayType, &variable);
+
+ TIntermSymbol *symbol =
+ intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
+ if (variable && symbol)
+ symbol->setId(variable->getUniqueId());
+
+ declarationOut->appendDeclarator(symbol);
+ }
+}
+
+void TParseContext::parseInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut)
+{
+ // If the declaration starting this declarator list was empty (example: int,), some checks were
+ // not performed.
+ if (mDeferredSingleDeclarationErrorCheck)
+ {
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredSingleDeclarationErrorCheck = false;
+ }
+
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
+
+ TIntermBinary *initNode = nullptr;
+ if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode))
+ {
+ //
+ // build the intermediate representation
+ //
+ if (initNode)
+ {
+ declarationOut->appendDeclarator(initNode);
+ }
+ }
+}
+
+void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut)
+{
+ // If the declaration starting this declarator list was empty (example: int,), some checks were
+ // not performed.
+ if (mDeferredSingleDeclarationErrorCheck)
+ {
+ singleDeclarationErrorCheck(publicType, identifierLocation);
+ mDeferredSingleDeclarationErrorCheck = false;
+ }
+
+ checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
+
+ checkIsValidTypeAndQualifierForArray(indexLocation, publicType);
+
+ TPublicType arrayType(publicType);
+
+ unsigned int size = 0u;
+ // If indexExpression is nullptr, then the array will eventually get its size implicitly from
+ // the initializer.
+ if (indexExpression != nullptr)
+ {
+ size = checkIsValidArraySize(identifierLocation, indexExpression);
+ }
+ // Make the type an array even if size check failed.
+ // This ensures useless error messages regarding the variable's non-arrayness won't follow.
+ arrayType.setArraySize(size);
+
+ // initNode will correspond to the whole of "b[n] = initializer".
+ TIntermBinary *initNode = nullptr;
+ if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
+ {
+ if (initNode)
+ {
+ declarationOut->appendDeclarator(initNode);
+ }
+ }
+}
+
+void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+ const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
+
+ checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
+ typeQualifier.line);
+
+ // It should never be the case, but some strange parser errors can send us here.
+ if (layoutQualifier.isEmpty())
+ {
+ error(typeQualifier.line, "Error during layout qualifier parsing.", "?");
+ return;
+ }
+
+ if (!layoutQualifier.isCombinationValid())
+ {
+ error(typeQualifier.line, "invalid combination:", "layout");
+ return;
+ }
+
+ checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ checkInternalFormatIsNotSpecified(typeQualifier.line, layoutQualifier.imageInternalFormat);
+
+ if (typeQualifier.qualifier == EvqComputeIn)
+ {
+ if (mComputeShaderLocalSizeDeclared &&
+ !layoutQualifier.isLocalSizeEqual(mComputeShaderLocalSize))
+ {
+ error(typeQualifier.line, "Work group size does not match the previous declaration",
+ "layout");
+ return;
+ }
+
+ if (mShaderVersion < 310)
+ {
+ error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
+ return;
+ }
+
+ if (!layoutQualifier.localSize.isAnyValueSet())
+ {
+ error(typeQualifier.line, "No local work group size specified", "layout");
+ return;
+ }
+
+ const TVariable *maxComputeWorkGroupSize = static_cast<const TVariable *>(
+ symbolTable.findBuiltIn("gl_MaxComputeWorkGroupSize", mShaderVersion));
+
+ const TConstantUnion *maxComputeWorkGroupSizeData =
+ maxComputeWorkGroupSize->getConstPointer();
+
+ for (size_t i = 0u; i < layoutQualifier.localSize.size(); ++i)
+ {
+ if (layoutQualifier.localSize[i] != -1)
+ {
+ mComputeShaderLocalSize[i] = layoutQualifier.localSize[i];
+ const int maxComputeWorkGroupSizeValue = maxComputeWorkGroupSizeData[i].getIConst();
+ if (mComputeShaderLocalSize[i] < 1 ||
+ mComputeShaderLocalSize[i] > maxComputeWorkGroupSizeValue)
+ {
+ std::stringstream errorMessageStream;
+ errorMessageStream << "Value must be at least 1 and no greater than "
+ << maxComputeWorkGroupSizeValue;
+ const std::string &errorMessage = errorMessageStream.str();
+
+ error(typeQualifier.line, "invalid value:", getWorkGroupSizeString(i),
+ errorMessage.c_str());
+ return;
+ }
+ }
+ }
+
+ mComputeShaderLocalSizeDeclared = true;
+ }
+ else
+ {
+
+ if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier))
+ {
+ return;
+ }
+
+ if (typeQualifier.qualifier != EvqUniform)
+ {
+ error(typeQualifier.line, "invalid qualifier:",
+ getQualifierString(typeQualifier.qualifier), "global layout must be uniform");
+ return;
+ }
+
+ if (mShaderVersion < 300)
+ {
+ error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and above",
+ "layout");
+ return;
+ }
+
+ checkLocationIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier);
+
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ mDefaultMatrixPacking = layoutQualifier.matrixPacking;
+ }
+
+ if (layoutQualifier.blockStorage != EbsUnspecified)
+ {
+ mDefaultBlockStorage = layoutQualifier.blockStorage;
+ }
+ }
+}
+
+TIntermAggregate *TParseContext::addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
+ const TSourceLoc &location)
+{
+ // Note: function found from the symbol table could be the same as parsedFunction if this is the
+ // first declaration. Either way the instance in the symbol table is used to track whether the
+ // function is declared multiple times.
+ TFunction *function = static_cast<TFunction *>(
+ symbolTable.find(parsedFunction.getMangledName(), getShaderVersion()));
+ if (function->hasPrototypeDeclaration() && mShaderVersion == 100)
+ {
+ // ESSL 1.00.17 section 4.2.7.
+ // Doesn't apply to ESSL 3.00.4: see section 4.2.3.
+ error(location, "duplicate function prototype declarations are not allowed", "function");
+ }
+ function->setHasPrototypeDeclaration();
+
+ TIntermAggregate *prototype = new TIntermAggregate;
+ // TODO(oetuaho@nvidia.com): Instead of converting the function information here, the node could
+ // point to the data that already exists in the symbol table.
+ prototype->setType(function->getReturnType());
+ prototype->getFunctionSymbolInfo()->setFromFunction(*function);
+
+ for (size_t i = 0; i < function->getParamCount(); i++)
+ {
+ const TConstParameter &param = function->getParam(i);
+ if (param.name != 0)
+ {
+ TVariable variable(param.name, *param.type);
+
+ TIntermSymbol *paramSymbol = intermediate.addSymbol(
+ variable.getUniqueId(), variable.getName(), variable.getType(), location);
+ prototype = intermediate.growAggregate(prototype, paramSymbol, location);
+ }
+ else
+ {
+ TIntermSymbol *paramSymbol = intermediate.addSymbol(0, "", *param.type, location);
+ prototype = intermediate.growAggregate(prototype, paramSymbol, location);
+ }
+ }
+
+ prototype->setOp(EOpPrototype);
+
+ symbolTable.pop();
+
+ if (!symbolTable.atGlobalLevel())
+ {
+ // ESSL 3.00.4 section 4.2.4.
+ error(location, "local function prototype declarations are not allowed", "function");
+ }
+
+ return prototype;
+}
+
+TIntermFunctionDefinition *TParseContext::addFunctionDefinition(
+ const TFunction &function,
+ TIntermAggregate *functionParameters,
+ TIntermBlock *functionBody,
+ const TSourceLoc &location)
+{
+ // Check that non-void functions have at least one return statement.
+ if (mCurrentFunctionType->getBasicType() != EbtVoid && !mFunctionReturnsValue)
+ {
+ error(location, "function does not return a value:", "", function.getName().c_str());
+ }
+
+ if (functionBody == nullptr)
+ {
+ functionBody = new TIntermBlock();
+ functionBody->setLine(location);
+ }
+ TIntermFunctionDefinition *functionNode =
+ new TIntermFunctionDefinition(function.getReturnType(), functionParameters, functionBody);
+ functionNode->setLine(location);
+
+ functionNode->getFunctionSymbolInfo()->setFromFunction(function);
+
+ symbolTable.pop();
+ return functionNode;
+}
+
+void TParseContext::parseFunctionDefinitionHeader(const TSourceLoc &location,
+ TFunction **function,
+ TIntermAggregate **aggregateOut)
+{
+ ASSERT(function);
+ ASSERT(*function);
+ const TSymbol *builtIn =
+ symbolTable.findBuiltIn((*function)->getMangledName(), getShaderVersion());
+
+ if (builtIn)
+ {
+ error(location, "built-in functions cannot be redefined", (*function)->getName().c_str());
+ }
+ else
+ {
+ TFunction *prevDec = static_cast<TFunction *>(
+ symbolTable.find((*function)->getMangledName(), getShaderVersion()));
+
+ // Note: 'prevDec' could be 'function' if this is the first time we've seen function as it
+ // would have just been put in the symbol table. Otherwise, we're looking up an earlier
+ // occurance.
+ if (*function != prevDec)
+ {
+ // Swap the parameters of the previous declaration to the parameters of the function
+ // definition (parameter names may differ).
+ prevDec->swapParameters(**function);
+
+ // The function definition will share the same symbol as any previous declaration.
+ *function = prevDec;
+ }
+
+ if ((*function)->isDefined())
+ {
+ error(location, "function already has a body", (*function)->getName().c_str());
+ }
+
+ (*function)->setDefined();
+ }
+
+ // Raise error message if main function takes any parameters or return anything other than void
+ if ((*function)->getName() == "main")
+ {
+ if ((*function)->getParamCount() > 0)
+ {
+ error(location, "function cannot take any parameter(s)",
+ (*function)->getName().c_str());
+ }
+ if ((*function)->getReturnType().getBasicType() != EbtVoid)
+ {
+ error(location, "", (*function)->getReturnType().getBasicString(),
+ "main function cannot return a value");
+ }
+ }
+
+ //
+ // Remember the return type for later checking for RETURN statements.
+ //
+ mCurrentFunctionType = &((*function)->getReturnType());
+ mFunctionReturnsValue = false;
+
+ //
+ // Insert parameters into the symbol table.
+ // If the parameter has no name, it's not an error, just don't insert it
+ // (could be used for unused args).
+ //
+ // Also, accumulate the list of parameters into the HIL, so lower level code
+ // knows where to find parameters.
+ //
+ TIntermAggregate *paramNodes = new TIntermAggregate;
+ for (size_t i = 0; i < (*function)->getParamCount(); i++)
+ {
+ const TConstParameter &param = (*function)->getParam(i);
+ if (param.name != 0)
+ {
+ TVariable *variable = new TVariable(param.name, *param.type);
+ //
+ // Insert the parameters with name in the symbol table.
+ //
+ if (!symbolTable.declare(variable))
+ {
+ error(location, "redefinition", variable->getName().c_str());
+ paramNodes = intermediate.growAggregate(
+ paramNodes, intermediate.addSymbol(0, "", *param.type, location), location);
+ continue;
+ }
+
+ //
+ // Add the parameter to the HIL
+ //
+ TIntermSymbol *symbol = intermediate.addSymbol(
+ variable->getUniqueId(), variable->getName(), variable->getType(), location);
+
+ paramNodes = intermediate.growAggregate(paramNodes, symbol, location);
+ }
+ else
+ {
+ paramNodes = intermediate.growAggregate(
+ paramNodes, intermediate.addSymbol(0, "", *param.type, location), location);
+ }
+ }
+ intermediate.setAggregateOperator(paramNodes, EOpParameters, location);
+ *aggregateOut = paramNodes;
+ setLoopNestingLevel(0);
+}
+
+TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TFunction *function)
+{
+ //
+ // We don't know at this point whether this is a function definition or a prototype.
+ // The definition production code will check for redefinitions.
+ // In the case of ESSL 1.00 the prototype production code will also check for redeclarations.
+ //
+ // Return types and parameter qualifiers must match in all redeclarations, so those are checked
+ // here.
+ //
+ TFunction *prevDec =
+ static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
+
+ if (getShaderVersion() >= 300 && symbolTable.hasUnmangledBuiltIn(function->getName().c_str()))
+ {
+ // With ESSL 3.00, names of built-in functions cannot be redeclared as functions.
+ // Therefore overloading or redefining builtin functions is an error.
+ error(location, "Name of a built-in function cannot be redeclared as function",
+ function->getName().c_str());
+ }
+ else if (prevDec)
+ {
+ if (prevDec->getReturnType() != function->getReturnType())
+ {
+ error(location, "function must have the same return type in all of its declarations",
+ function->getReturnType().getBasicString());
+ }
+ for (size_t i = 0; i < prevDec->getParamCount(); ++i)
+ {
+ if (prevDec->getParam(i).type->getQualifier() !=
+ function->getParam(i).type->getQualifier())
+ {
+ error(location,
+ "function must have the same parameter qualifiers in all of its declarations",
+ function->getParam(i).type->getQualifierString());
+ }
+ }
+ }
+
+ //
+ // Check for previously declared variables using the same name.
+ //
+ TSymbol *prevSym = symbolTable.find(function->getName(), getShaderVersion());
+ if (prevSym)
+ {
+ if (!prevSym->isFunction())
+ {
+ error(location, "redefinition", function->getName().c_str(), "function");
+ }
+ }
+ else
+ {
+ // Insert the unmangled name to detect potential future redefinition as a variable.
+ symbolTable.getOuterLevel()->insertUnmangled(function);
+ }
+
+ // We're at the inner scope level of the function's arguments and body statement.
+ // Add the function prototype to the surrounding scope instead.
+ symbolTable.getOuterLevel()->insert(function);
+
+ //
+ // If this is a redeclaration, it could also be a definition, in which case, we want to use the
+ // variable names from this one, and not the one that's
+ // being redeclared. So, pass back up this declaration, not the one in the symbol table.
+ //
+ return function;
+}
+
+TFunction *TParseContext::parseFunctionHeader(const TPublicType &type,
+ const TString *name,
+ const TSourceLoc &location)
+{
+ if (type.qualifier != EvqGlobal && type.qualifier != EvqTemporary)
+ {
+ error(location, "no qualifiers allowed for function return",
+ getQualifierString(type.qualifier));
+ }
+ if (!type.layoutQualifier.isEmpty())
+ {
+ error(location, "no qualifiers allowed for function return", "layout");
+ }
+ // make sure a sampler or an image is not involved as well...
+ checkIsNotSampler(location, type.typeSpecifierNonArray,
+ "samplers can't be function return values");
+ checkIsNotImage(location, type.typeSpecifierNonArray, "images can't be function return values");
+ if (mShaderVersion < 300)
+ {
+ // Array return values are forbidden, but there's also no valid syntax for declaring array
+ // return values in ESSL 1.00.
+ ASSERT(type.arraySize == 0 || mDiagnostics.numErrors() > 0);
+
+ if (type.isStructureContainingArrays())
+ {
+ // ESSL 1.00.17 section 6.1 Function Definitions
+ error(location, "structures containing arrays can't be function return values",
+ TType(type).getCompleteString().c_str());
+ }
+ }
+
+ // Add the function as a prototype after parsing it (we do not support recursion)
+ return new TFunction(name, new TType(type));
+}
+
+TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn)
+{
+ TPublicType publicType = publicTypeIn;
+ if (publicType.isStructSpecifier())
+ {
+ error(publicType.getLine(), "constructor can't be a structure definition",
+ getBasicString(publicType.getBasicType()));
+ }
+
+ TOperator op = EOpNull;
+ if (publicType.getUserDef())
+ {
+ op = EOpConstructStruct;
+ }
+ else
+ {
+ op = sh::TypeToConstructorOperator(TType(publicType));
+ if (op == EOpNull)
+ {
+ error(publicType.getLine(), "cannot construct this type",
+ getBasicString(publicType.getBasicType()));
+ publicType.setBasicType(EbtFloat);
+ op = EOpConstructFloat;
+ }
+ }
+
+ TString tempString;
+ const TType *type = new TType(publicType);
+ return new TFunction(&tempString, type, op);
+}
+
+// This function is used to test for the correctness of the parameters passed to various constructor
+// functions and also convert them to the right datatype if it is allowed and required.
+//
+// Returns a node to add to the tree regardless of if an error was generated or not.
+//
+TIntermTyped *TParseContext::addConstructor(TIntermNode *arguments,
+ TOperator op,
+ TFunction *fnCall,
+ const TSourceLoc &line)
+{
+ TType type = fnCall->getReturnType();
+ if (type.isUnsizedArray())
+ {
+ if (fnCall->getParamCount() == 0)
+ {
+ error(line, "implicitly sized array constructor must have at least one argument", "[]");
+ type.setArraySize(1u);
+ return TIntermTyped::CreateZero(type);
+ }
+ type.setArraySize(static_cast<unsigned int>(fnCall->getParamCount()));
+ }
+ bool constType = true;
+ for (size_t i = 0; i < fnCall->getParamCount(); ++i)
+ {
+ const TConstParameter &param = fnCall->getParam(i);
+ if (param.type->getQualifier() != EvqConst)
+ constType = false;
+ }
+ if (constType)
+ type.setQualifier(EvqConst);
+
+ if (!checkConstructorArguments(line, arguments, *fnCall, op, type))
+ {
+ TIntermTyped *dummyNode = intermediate.setAggregateOperator(nullptr, op, line);
+ dummyNode->setType(type);
+ return dummyNode;
+ }
+ TIntermAggregate *constructor = arguments->getAsAggregate();
+ ASSERT(constructor != nullptr);
+
+ // Turn the argument list itself into a constructor
+ constructor->setOp(op);
+ constructor->setLine(line);
+ ASSERT(constructor->isConstructor());
+
+ // Need to set type before setPrecisionFromChildren() because bool doesn't have precision.
+ constructor->setType(type);
+
+ // Structs should not be precision qualified, the individual members may be.
+ // Built-in types on the other hand should be precision qualified.
+ if (op != EOpConstructStruct)
+ {
+ constructor->setPrecisionFromChildren();
+ type.setPrecision(constructor->getPrecision());
+ }
+
+ constructor->setType(type);
+
+ TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor, &mDiagnostics);
+ if (constConstructor)
+ {
+ return constConstructor;
+ }
+
+ return constructor;
+}
+
+//
+// Interface/uniform blocks
+//
+TIntermDeclaration *TParseContext::addInterfaceBlock(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &nameLine,
+ const TString &blockName,
+ TFieldList *fieldList,
+ const TString *instanceName,
+ const TSourceLoc &instanceLine,
+ TIntermTyped *arrayIndex,
+ const TSourceLoc &arrayIndexLine)
+{
+ checkIsNotReserved(nameLine, blockName);
+
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+ if (typeQualifier.qualifier != EvqUniform)
+ {
+ error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
+ "interface blocks must be uniform");
+ }
+
+ if (typeQualifier.invariant)
+ {
+ error(typeQualifier.line, "invalid qualifier on interface block member", "invariant");
+ }
+
+ checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
+ checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
+
+ if (blockLayoutQualifier.matrixPacking == EmpUnspecified)
+ {
+ blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking;
+ }
+
+ if (blockLayoutQualifier.blockStorage == EbsUnspecified)
+ {
+ blockLayoutQualifier.blockStorage = mDefaultBlockStorage;
+ }
+
+ checkWorkGroupSizeIsNotSpecified(nameLine, blockLayoutQualifier);
+
+ checkInternalFormatIsNotSpecified(nameLine, blockLayoutQualifier.imageInternalFormat);
+
+ TSymbol *blockNameSymbol = new TInterfaceBlockName(&blockName);
+ if (!symbolTable.declare(blockNameSymbol))
+ {
+ error(nameLine, "redefinition", blockName.c_str(), "interface block name");
+ }
+
+ // check for sampler types and apply layout qualifiers
+ for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex)
+ {
+ TField *field = (*fieldList)[memberIndex];
+ TType *fieldType = field->type();
+ if (IsSampler(fieldType->getBasicType()))
+ {
+ error(field->line(), "unsupported type", fieldType->getBasicString(),
+ "sampler types are not allowed in interface blocks");
+ }
+
+ if (IsImage(fieldType->getBasicType()))
+ {
+ error(field->line(), "unsupported type", fieldType->getBasicString(),
+ "image types are not allowed in interface blocks");
+ }
+
+ const TQualifier qualifier = fieldType->getQualifier();
+ switch (qualifier)
+ {
+ case EvqGlobal:
+ case EvqUniform:
+ break;
+ default:
+ error(field->line(), "invalid qualifier on interface block member",
+ getQualifierString(qualifier));
+ break;
+ }
+
+ if (fieldType->isInvariant())
+ {
+ error(field->line(), "invalid qualifier on interface block member", "invariant");
+ }
+
+ // check layout qualifiers
+ TLayoutQualifier fieldLayoutQualifier = fieldType->getLayoutQualifier();
+ checkLocationIsNotSpecified(field->line(), fieldLayoutQualifier);
+
+ if (fieldLayoutQualifier.blockStorage != EbsUnspecified)
+ {
+ error(field->line(), "invalid layout qualifier:",
+ getBlockStorageString(fieldLayoutQualifier.blockStorage), "cannot be used here");
+ }
+
+ if (fieldLayoutQualifier.matrixPacking == EmpUnspecified)
+ {
+ fieldLayoutQualifier.matrixPacking = blockLayoutQualifier.matrixPacking;
+ }
+ else if (!fieldType->isMatrix() && fieldType->getBasicType() != EbtStruct)
+ {
+ warning(field->line(), "extraneous layout qualifier:",
+ getMatrixPackingString(fieldLayoutQualifier.matrixPacking),
+ "only has an effect on matrix types");
+ }
+
+ fieldType->setLayoutQualifier(fieldLayoutQualifier);
+ }
+
+ // add array index
+ unsigned int arraySize = 0;
+ if (arrayIndex != nullptr)
+ {
+ arraySize = checkIsValidArraySize(arrayIndexLine, arrayIndex);
+ }
+
+ TInterfaceBlock *interfaceBlock =
+ new TInterfaceBlock(&blockName, fieldList, instanceName, arraySize, blockLayoutQualifier);
+ TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier,
+ arraySize);
+
+ TString symbolName = "";
+ int symbolId = 0;
+
+ if (!instanceName)
+ {
+ // define symbols for the members of the interface block
+ for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex)
+ {
+ TField *field = (*fieldList)[memberIndex];
+ TType *fieldType = field->type();
+
+ // set parent pointer of the field variable
+ fieldType->setInterfaceBlock(interfaceBlock);
+
+ TVariable *fieldVariable = new TVariable(&field->name(), *fieldType);
+ fieldVariable->setQualifier(typeQualifier.qualifier);
+
+ if (!symbolTable.declare(fieldVariable))
+ {
+ error(field->line(), "redefinition", field->name().c_str(),
+ "interface block member name");
+ }
+ }
+ }
+ else
+ {
+ checkIsNotReserved(instanceLine, *instanceName);
+
+ // add a symbol for this interface block
+ TVariable *instanceTypeDef = new TVariable(instanceName, interfaceBlockType, false);
+ instanceTypeDef->setQualifier(typeQualifier.qualifier);
+
+ if (!symbolTable.declare(instanceTypeDef))
+ {
+ error(instanceLine, "redefinition", instanceName->c_str(),
+ "interface block instance name");
+ }
+
+ symbolId = instanceTypeDef->getUniqueId();
+ symbolName = instanceTypeDef->getName();
+ }
+
+ TIntermSymbol *blockSymbol =
+ intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line);
+ TIntermDeclaration *declaration = new TIntermDeclaration();
+ declaration->appendDeclarator(blockSymbol);
+ declaration->setLine(nameLine);
+
+ exitStructDeclaration();
+ return declaration;
+}
+
+void TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier)
+{
+ ++mStructNestingLevel;
+
+ // Embedded structure definitions are not supported per GLSL ES spec.
+ // They aren't allowed in GLSL either, but we need to detect this here
+ // so we don't rely on the GLSL compiler to catch it.
+ if (mStructNestingLevel > 1)
+ {
+ error(line, "", "Embedded struct definitions are not allowed");
+ }
+}
+
+void TParseContext::exitStructDeclaration()
+{
+ --mStructNestingLevel;
+}
+
+void TParseContext::checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field)
+{
+ if (!sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ return;
+ }
+
+ if (field.type()->getBasicType() != EbtStruct)
+ {
+ return;
+ }
+
+ // We're already inside a structure definition at this point, so add
+ // one to the field's struct nesting.
+ if (1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting)
+ {
+ std::stringstream reasonStream;
+ reasonStream << "Reference of struct type " << field.type()->getStruct()->name().c_str()
+ << " exceeds maximum allowed nesting level of " << kWebGLMaxStructNesting;
+ std::string reason = reasonStream.str();
+ error(line, reason.c_str(), field.name().c_str(), "");
+ return;
+ }
+}
+
+//
+// Parse an array index expression
+//
+TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
+ const TSourceLoc &location,
+ TIntermTyped *indexExpression)
+{
+ if (!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector())
+ {
+ if (baseExpression->getAsSymbolNode())
+ {
+ error(location, " left of '[' is not of type array, matrix, or vector ",
+ baseExpression->getAsSymbolNode()->getSymbol().c_str());
+ }
+ else
+ {
+ error(location, " left of '[' is not of type array, matrix, or vector ", "expression");
+ }
+
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setFConst(0.0f);
+ return intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst),
+ location);
+ }
+
+ TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
+
+ // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able
+ // to constant fold all constant expressions. Right now we don't allow indexing interface blocks
+ // or fragment outputs with expressions that ANGLE is not able to constant fold, even if the
+ // index is a constant expression.
+ if (indexExpression->getQualifier() != EvqConst || indexConstantUnion == nullptr)
+ {
+ if (baseExpression->isInterfaceBlock())
+ {
+ error(
+ location, "", "[",
+ "array indexes for interface blocks arrays must be constant integral expressions");
+ }
+ else if (baseExpression->getQualifier() == EvqFragmentOut)
+ {
+ error(location, "", "[",
+ "array indexes for fragment outputs must be constant integral expressions");
+ }
+ else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData)
+ {
+ error(location, "", "[", "array index for gl_FragData must be constant zero");
+ }
+ }
+
+ if (indexConstantUnion)
+ {
+ // If an out-of-range index is not qualified as constant, the behavior in the spec is
+ // undefined. This applies even if ANGLE has been able to constant fold it (ANGLE may
+ // constant fold expressions that are not constant expressions). The most compatible way to
+ // handle this case is to report a warning instead of an error and force the index to be in
+ // the correct range.
+ bool outOfRangeIndexIsError = indexExpression->getQualifier() == EvqConst;
+ int index = indexConstantUnion->getIConst(0);
+
+ int safeIndex = -1;
+
+ if (baseExpression->isArray())
+ {
+ if (baseExpression->getQualifier() == EvqFragData && index > 0)
+ {
+ if (mShaderSpec == SH_WEBGL2_SPEC)
+ {
+ // Error has been already generated if index is not const.
+ if (indexExpression->getQualifier() == EvqConst)
+ {
+ error(location, "", "[",
+ "array index for gl_FragData must be constant zero");
+ }
+ safeIndex = 0;
+ }
+ else if (!isExtensionEnabled("GL_EXT_draw_buffers"))
+ {
+ outOfRangeError(outOfRangeIndexIsError, location, "", "[",
+ "array index for gl_FragData must be zero when "
+ "GL_EXT_draw_buffers is disabled");
+ safeIndex = 0;
+ }
+ }
+ // Only do generic out-of-range check if similar error hasn't already been reported.
+ if (safeIndex < 0)
+ {
+ safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+ baseExpression->getArraySize(),
+ "array index out of range", "[]");
+ }
+ }
+ else if (baseExpression->isMatrix())
+ {
+ safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getCols(),
+ "matrix field selection out of range", "[]");
+ }
+ else if (baseExpression->isVector())
+ {
+ safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getNominalSize(),
+ "vector field selection out of range", "[]");
+ }
+
+ ASSERT(safeIndex >= 0);
+ // Data of constant unions can't be changed, because it may be shared with other
+ // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
+ // sanitized object.
+ if (safeIndex != index)
+ {
+ TConstantUnion *safeConstantUnion = new TConstantUnion();
+ safeConstantUnion->setIConst(safeIndex);
+ indexConstantUnion->replaceConstantUnion(safeConstantUnion);
+ }
+
+ return intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location,
+ &mDiagnostics);
+ }
+ else
+ {
+ return intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location,
+ &mDiagnostics);
+ }
+}
+
+int TParseContext::checkIndexOutOfRange(bool outOfRangeIndexIsError,
+ const TSourceLoc &location,
+ int index,
+ int arraySize,
+ const char *reason,
+ const char *token)
+{
+ if (index >= arraySize || index < 0)
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream << "'" << index << "'";
+ std::string extraInfo = extraInfoStream.str();
+ outOfRangeError(outOfRangeIndexIsError, location, reason, token, extraInfo.c_str());
+ if (index < 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return arraySize - 1;
+ }
+ }
+ return index;
+}
+
+TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpression,
+ const TSourceLoc &dotLocation,
+ const TString &fieldString,
+ const TSourceLoc &fieldLocation)
+{
+ if (baseExpression->isArray())
+ {
+ error(fieldLocation, "cannot apply dot operator to an array", ".");
+ return baseExpression;
+ }
+
+ if (baseExpression->isVector())
+ {
+ TVectorFields fields;
+ if (!parseVectorFields(fieldString, baseExpression->getNominalSize(), fields,
+ fieldLocation))
+ {
+ fields.num = 1;
+ fields.offsets[0] = 0;
+ }
+
+ return TIntermediate::AddSwizzle(baseExpression, fields, dotLocation);
+ }
+ else if (baseExpression->getBasicType() == EbtStruct)
+ {
+ const TFieldList &fields = baseExpression->getType().getStruct()->fields();
+ if (fields.empty())
+ {
+ error(dotLocation, "structure has no fields", "Internal Error");
+ return baseExpression;
+ }
+ else
+ {
+ bool fieldFound = false;
+ unsigned int i;
+ for (i = 0; i < fields.size(); ++i)
+ {
+ if (fields[i]->name() == fieldString)
+ {
+ fieldFound = true;
+ break;
+ }
+ }
+ if (fieldFound)
+ {
+ TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
+ index->setLine(fieldLocation);
+ return intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index,
+ dotLocation, &mDiagnostics);
+ }
+ else
+ {
+ error(dotLocation, " no such field in structure", fieldString.c_str());
+ return baseExpression;
+ }
+ }
+ }
+ else if (baseExpression->isInterfaceBlock())
+ {
+ const TFieldList &fields = baseExpression->getType().getInterfaceBlock()->fields();
+ if (fields.empty())
+ {
+ error(dotLocation, "interface block has no fields", "Internal Error");
+ return baseExpression;
+ }
+ else
+ {
+ bool fieldFound = false;
+ unsigned int i;
+ for (i = 0; i < fields.size(); ++i)
+ {
+ if (fields[i]->name() == fieldString)
+ {
+ fieldFound = true;
+ break;
+ }
+ }
+ if (fieldFound)
+ {
+ TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
+ index->setLine(fieldLocation);
+ return intermediate.addIndex(EOpIndexDirectInterfaceBlock, baseExpression, index,
+ dotLocation, &mDiagnostics);
+ }
+ else
+ {
+ error(dotLocation, " no such field in interface block", fieldString.c_str());
+ return baseExpression;
+ }
+ }
+ }
+ else
+ {
+ if (mShaderVersion < 300)
+ {
+ error(dotLocation, " field selection requires structure or vector on left hand side",
+ fieldString.c_str());
+ }
+ else
+ {
+ error(dotLocation,
+ " field selection requires structure, vector, or interface block on left hand "
+ "side",
+ fieldString.c_str());
+ }
+ return baseExpression;
+ }
+}
+
+TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine)
+{
+ TLayoutQualifier qualifier = TLayoutQualifier::create();
+
+ if (qualifierType == "shared")
+ {
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ error(qualifierTypeLine, "Only std140 layout is allowed in WebGL", "shared");
+ }
+ qualifier.blockStorage = EbsShared;
+ }
+ else if (qualifierType == "packed")
+ {
+ if (sh::IsWebGLBasedSpec(mShaderSpec))
+ {
+ error(qualifierTypeLine, "Only std140 layout is allowed in WebGL", "packed");
+ }
+ qualifier.blockStorage = EbsPacked;
+ }
+ else if (qualifierType == "std140")
+ {
+ qualifier.blockStorage = EbsStd140;
+ }
+ else if (qualifierType == "row_major")
+ {
+ qualifier.matrixPacking = EmpRowMajor;
+ }
+ else if (qualifierType == "column_major")
+ {
+ qualifier.matrixPacking = EmpColumnMajor;
+ }
+ else if (qualifierType == "location")
+ {
+ error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(),
+ "location requires an argument");
+ }
+ else if (qualifierType == "rgba32f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32F;
+ }
+ else if (qualifierType == "rgba16f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16F;
+ }
+ else if (qualifierType == "r32f")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32F;
+ }
+ else if (qualifierType == "rgba8")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8;
+ }
+ else if (qualifierType == "rgba8_snorm")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8_SNORM;
+ }
+ else if (qualifierType == "rgba32i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32I;
+ }
+ else if (qualifierType == "rgba16i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16I;
+ }
+ else if (qualifierType == "rgba8i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8I;
+ }
+ else if (qualifierType == "r32i")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32I;
+ }
+ else if (qualifierType == "rgba32ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA32UI;
+ }
+ else if (qualifierType == "rgba16ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA16UI;
+ }
+ else if (qualifierType == "rgba8ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifRGBA8UI;
+ }
+ else if (qualifierType == "r32ui")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ qualifier.imageInternalFormat = EiifR32UI;
+ }
+
+ else
+ {
+ error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+ }
+
+ return qualifier;
+}
+
+void TParseContext::parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ sh::WorkGroupSize *localSize)
+{
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ if (intValue < 1)
+ {
+ std::string errorMessage = std::string(getWorkGroupSizeString(index)) + " must be positive";
+ error(intValueLine, "out of range:", intValueString.c_str(), errorMessage.c_str());
+ }
+ (*localSize)[index] = intValue;
+}
+
+TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine)
+{
+ TLayoutQualifier qualifier = TLayoutQualifier::create();
+
+ std::string intValueString = Str(intValue);
+
+ if (qualifierType == "location")
+ {
+ // must check that location is non-negative
+ if (intValue < 0)
+ {
+ error(intValueLine, "out of range:", intValueString.c_str(),
+ "location must be non-negative");
+ }
+ else
+ {
+ qualifier.location = intValue;
+ qualifier.locationsSpecified = 1;
+ }
+ }
+ else if (qualifierType == "local_size_x")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_y")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 1u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_z")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
+ &qualifier.localSize);
+ }
+ else
+ {
+ error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+ }
+
+ return qualifier;
+}
+
+TTypeQualifierBuilder *TParseContext::createTypeQualifierBuilder(const TSourceLoc &loc)
+{
+ return new TTypeQualifierBuilder(
+ new TStorageQualifierWrapper(symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary, loc),
+ mShaderVersion);
+}
+
+TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation)
+{
+ return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
+ &mDiagnostics);
+}
+
+TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TPublicType *typeSpecifier,
+ TFieldList *fieldList)
+{
+ TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
+
+ typeSpecifier->qualifier = typeQualifier.qualifier;
+ typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
+ typeSpecifier->memoryQualifier = typeQualifier.memoryQualifier;
+ typeSpecifier->invariant = typeQualifier.invariant;
+ if (typeQualifier.precision != EbpUndefined)
+ {
+ typeSpecifier->precision = typeQualifier.precision;
+ }
+ return addStructDeclaratorList(*typeSpecifier, fieldList);
+}
+
+TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecifier,
+ TFieldList *fieldList)
+{
+ checkPrecisionSpecified(typeSpecifier.getLine(), typeSpecifier.precision,
+ typeSpecifier.getBasicType());
+
+ checkIsNonVoid(typeSpecifier.getLine(), (*fieldList)[0]->name(), typeSpecifier.getBasicType());
+
+ checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
+
+ for (unsigned int i = 0; i < fieldList->size(); ++i)
+ {
+ //
+ // Careful not to replace already known aspects of type, like array-ness
+ //
+ TType *type = (*fieldList)[i]->type();
+ type->setBasicType(typeSpecifier.getBasicType());
+ type->setPrimarySize(typeSpecifier.getPrimarySize());
+ type->setSecondarySize(typeSpecifier.getSecondarySize());
+ type->setPrecision(typeSpecifier.precision);
+ type->setQualifier(typeSpecifier.qualifier);
+ type->setLayoutQualifier(typeSpecifier.layoutQualifier);
+ type->setMemoryQualifier(typeSpecifier.memoryQualifier);
+ type->setInvariant(typeSpecifier.invariant);
+
+ // don't allow arrays of arrays
+ if (type->isArray())
+ {
+ checkIsValidTypeForArray(typeSpecifier.getLine(), typeSpecifier);
+ }
+ if (typeSpecifier.array)
+ type->setArraySize(static_cast<unsigned int>(typeSpecifier.arraySize));
+ if (typeSpecifier.getUserDef())
+ {
+ type->setStruct(typeSpecifier.getUserDef()->getStruct());
+ }
+
+ checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *(*fieldList)[i]);
+ }
+
+ return fieldList;
+}
+
+TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
+ const TSourceLoc &nameLine,
+ const TString *structName,
+ TFieldList *fieldList)
+{
+ TStructure *structure = new TStructure(structName, fieldList);
+ TType *structureType = new TType(structure);
+
+ // Store a bool in the struct if we're at global scope, to allow us to
+ // skip the local struct scoping workaround in HLSL.
+ structure->setAtGlobalScope(symbolTable.atGlobalLevel());
+
+ if (!structName->empty())
+ {
+ checkIsNotReserved(nameLine, *structName);
+ TVariable *userTypeDef = new TVariable(structName, *structureType, true);
+ if (!symbolTable.declare(userTypeDef))
+ {
+ error(nameLine, "redefinition", structName->c_str(), "struct");
+ }
+ }
+
+ // ensure we do not specify any storage qualifiers on the struct members
+ for (unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++)
+ {
+ const TField &field = *(*fieldList)[typeListIndex];
+ const TQualifier qualifier = field.type()->getQualifier();
+ switch (qualifier)
+ {
+ case EvqGlobal:
+ case EvqTemporary:
+ break;
+ default:
+ error(field.line(), "invalid qualifier on struct member",
+ getQualifierString(qualifier));
+ break;
+ }
+ if (field.type()->isInvariant())
+ {
+ error(field.line(), "invalid qualifier on struct member", "invariant");
+ }
+ if (IsImage(field.type()->getBasicType()))
+ {
+ error(field.line(), "disallowed type in struct", field.type()->getBasicString());
+ }
+
+ checkIsMemoryQualifierNotSpecified(field.type()->getMemoryQualifier(), field.line());
+
+ checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier());
+ }
+
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ typeSpecifierNonArray.initialize(EbtStruct, structLine);
+ typeSpecifierNonArray.userDef = structureType;
+ typeSpecifierNonArray.isStructSpecifier = true;
+ exitStructDeclaration();
+
+ return typeSpecifierNonArray;
+}
+
+TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc)
+{
+ TBasicType switchType = init->getBasicType();
+ if ((switchType != EbtInt && switchType != EbtUInt) || init->isMatrix() || init->isArray() ||
+ init->isVector())
+ {
+ error(init->getLine(), "init-expression in a switch statement must be a scalar integer",
+ "switch");
+ return nullptr;
+ }
+
+ if (statementList)
+ {
+ if (!ValidateSwitch::validate(switchType, this, statementList, loc))
+ {
+ return nullptr;
+ }
+ }
+
+ TIntermSwitch *node = intermediate.addSwitch(init, statementList, loc);
+ if (node == nullptr)
+ {
+ error(loc, "erroneous switch statement", "switch");
+ return nullptr;
+ }
+ return node;
+}
+
+TIntermCase *TParseContext::addCase(TIntermTyped *condition, const TSourceLoc &loc)
+{
+ if (mSwitchNestingLevel == 0)
+ {
+ error(loc, "case labels need to be inside switch statements", "case");
+ return nullptr;
+ }
+ if (condition == nullptr)
+ {
+ error(loc, "case label must have a condition", "case");
+ return nullptr;
+ }
+ if ((condition->getBasicType() != EbtInt && condition->getBasicType() != EbtUInt) ||
+ condition->isMatrix() || condition->isArray() || condition->isVector())
+ {
+ error(condition->getLine(), "case label must be a scalar integer", "case");
+ }
+ TIntermConstantUnion *conditionConst = condition->getAsConstantUnion();
+ // TODO(oetuaho@nvidia.com): Get rid of the conditionConst == nullptr check once all constant
+ // expressions can be folded. Right now we don't allow constant expressions that ANGLE can't
+ // fold in case labels.
+ if (condition->getQualifier() != EvqConst || conditionConst == nullptr)
+ {
+ error(condition->getLine(), "case label must be constant", "case");
+ }
+ TIntermCase *node = intermediate.addCase(condition, loc);
+ if (node == nullptr)
+ {
+ error(loc, "erroneous case statement", "case");
+ return nullptr;
+ }
+ return node;
+}
+
+TIntermCase *TParseContext::addDefault(const TSourceLoc &loc)
+{
+ if (mSwitchNestingLevel == 0)
+ {
+ error(loc, "default labels need to be inside switch statements", "default");
+ return nullptr;
+ }
+ TIntermCase *node = intermediate.addCase(nullptr, loc);
+ if (node == nullptr)
+ {
+ error(loc, "erroneous default statement", "default");
+ return nullptr;
+ }
+ return node;
+}
+
+TIntermTyped *TParseContext::createUnaryMath(TOperator op,
+ TIntermTyped *child,
+ const TSourceLoc &loc,
+ const TType *funcReturnType)
+{
+ if (child == nullptr)
+ {
+ return nullptr;
+ }
+
+ switch (op)
+ {
+ case EOpLogicalNot:
+ if (child->getBasicType() != EbtBool || child->isMatrix() || child->isArray() ||
+ child->isVector())
+ {
+ return nullptr;
+ }
+ break;
+ case EOpBitwiseNot:
+ if ((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) ||
+ child->isMatrix() || child->isArray())
+ {
+ return nullptr;
+ }
+ break;
+ case EOpPostIncrement:
+ case EOpPreIncrement:
+ case EOpPostDecrement:
+ case EOpPreDecrement:
+ case EOpNegative:
+ case EOpPositive:
+ if (child->getBasicType() == EbtStruct || child->getBasicType() == EbtBool ||
+ child->isArray() || IsOpaqueType(child->getBasicType()))
+ {
+ return nullptr;
+ }
+ // Operators for built-ins are already type checked against their prototype.
+ default:
+ break;
+ }
+
+ TIntermUnary *node = new TIntermUnary(op, child);
+ node->setLine(loc);
+
+ TIntermTyped *foldedNode = node->fold(&mDiagnostics);
+ if (foldedNode)
+ return foldedNode;
+
+ return node;
+}
+
+TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
+{
+ TIntermTyped *node = createUnaryMath(op, child, loc, nullptr);
+ if (node == nullptr)
+ {
+ unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+ return child;
+ }
+ return node;
+}
+
+TIntermTyped *TParseContext::addUnaryMathLValue(TOperator op,
+ TIntermTyped *child,
+ const TSourceLoc &loc)
+{
+ checkCanBeLValue(loc, GetOperatorString(op), child);
+ return addUnaryMath(op, child, loc);
+}
+
+bool TParseContext::binaryOpCommonCheck(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ if (left->getType().getStruct() || right->getType().getStruct())
+ {
+ switch (op)
+ {
+ case EOpIndexDirectStruct:
+ ASSERT(left->getType().getStruct());
+ break;
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpAssign:
+ case EOpInitialize:
+ if (left->getType() != right->getType())
+ {
+ return false;
+ }
+ break;
+ default:
+ error(loc, "Invalid operation for structs", GetOperatorString(op));
+ return false;
+ }
+ }
+
+ if (left->isArray() || right->isArray())
+ {
+ if (mShaderVersion < 300)
+ {
+ error(loc, "Invalid operation for arrays", GetOperatorString(op));
+ return false;
+ }
+
+ if (left->isArray() != right->isArray())
+ {
+ error(loc, "array / non-array mismatch", GetOperatorString(op));
+ return false;
+ }
+
+ switch (op)
+ {
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpAssign:
+ case EOpInitialize:
+ break;
+ default:
+ error(loc, "Invalid operation for arrays", GetOperatorString(op));
+ return false;
+ }
+ // At this point, size of implicitly sized arrays should be resolved.
+ if (left->getArraySize() != right->getArraySize())
+ {
+ error(loc, "array size mismatch", GetOperatorString(op));
+ return false;
+ }
+ }
+
+ // Check ops which require integer / ivec parameters
+ bool isBitShift = false;
+ switch (op)
+ {
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ // Unsigned can be bit-shifted by signed and vice versa, but we need to
+ // check that the basic type is an integer type.
+ isBitShift = true;
+ if (!IsInteger(left->getBasicType()) || !IsInteger(right->getBasicType()))
+ {
+ return false;
+ }
+ break;
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ // It is enough to check the type of only one operand, since later it
+ // is checked that the operand types match.
+ if (!IsInteger(left->getBasicType()))
+ {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // GLSL ES 1.00 and 3.00 do not support implicit type casting.
+ // So the basic type should usually match.
+ if (!isBitShift && left->getBasicType() != right->getBasicType())
+ {
+ return false;
+ }
+
+ // Check that:
+ // 1. Type sizes match exactly on ops that require that.
+ // 2. Restrictions for structs that contain arrays or samplers are respected.
+ // 3. Arithmetic op type dimensionality restrictions for ops other than multiply are respected.
+ switch (op)
+ {
+ case EOpAssign:
+ case EOpInitialize:
+ case EOpEqual:
+ case EOpNotEqual:
+ // ESSL 1.00 sections 5.7, 5.8, 5.9
+ if (mShaderVersion < 300 && left->getType().isStructureContainingArrays())
+ {
+ error(loc, "undefined operation for structs containing arrays",
+ GetOperatorString(op));
+ return false;
+ }
+ // Samplers as l-values are disallowed also in ESSL 3.00, see section 4.1.7,
+ // we interpret the spec so that this extends to structs containing samplers,
+ // similarly to ESSL 1.00 spec.
+ if ((mShaderVersion < 300 || op == EOpAssign || op == EOpInitialize) &&
+ left->getType().isStructureContainingSamplers())
+ {
+ error(loc, "undefined operation for structs containing samplers",
+ GetOperatorString(op));
+ return false;
+ }
+
+ if ((op == EOpAssign || op == EOpInitialize) &&
+ left->getType().isStructureContainingImages())
+ {
+ error(loc, "undefined operation for structs containing images",
+ GetOperatorString(op));
+ return false;
+ }
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ if ((left->getNominalSize() != right->getNominalSize()) ||
+ (left->getSecondarySize() != right->getSecondarySize()))
+ {
+ return false;
+ }
+ break;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpIMod:
+ case EOpBitShiftLeft:
+ case EOpBitShiftRight:
+ case EOpBitwiseAnd:
+ case EOpBitwiseXor:
+ case EOpBitwiseOr:
+ case EOpAddAssign:
+ case EOpSubAssign:
+ case EOpDivAssign:
+ case EOpIModAssign:
+ case EOpBitShiftLeftAssign:
+ case EOpBitShiftRightAssign:
+ case EOpBitwiseAndAssign:
+ case EOpBitwiseXorAssign:
+ case EOpBitwiseOrAssign:
+ if ((left->isMatrix() && right->isVector()) || (left->isVector() && right->isMatrix()))
+ {
+ return false;
+ }
+
+ // Are the sizes compatible?
+ if (left->getNominalSize() != right->getNominalSize() ||
+ left->getSecondarySize() != right->getSecondarySize())
+ {
+ // If the nominal sizes of operands do not match:
+ // One of them must be a scalar.
+ if (!left->isScalar() && !right->isScalar())
+ return false;
+
+ // In the case of compound assignment other than multiply-assign,
+ // the right side needs to be a scalar. Otherwise a vector/matrix
+ // would be assigned to a scalar. A scalar can't be shifted by a
+ // vector either.
+ if (!right->isScalar() &&
+ (IsAssignment(op) || op == EOpBitShiftLeft || op == EOpBitShiftRight))
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool TParseContext::isMultiplicationTypeCombinationValid(TOperator op,
+ const TType &left,
+ const TType &right)
+{
+ switch (op)
+ {
+ case EOpMul:
+ case EOpMulAssign:
+ return left.getNominalSize() == right.getNominalSize() &&
+ left.getSecondarySize() == right.getSecondarySize();
+ case EOpVectorTimesScalar:
+ return true;
+ case EOpVectorTimesScalarAssign:
+ ASSERT(!left.isMatrix() && !right.isMatrix());
+ return left.isVector() && !right.isVector();
+ case EOpVectorTimesMatrix:
+ return left.getNominalSize() == right.getRows();
+ case EOpVectorTimesMatrixAssign:
+ ASSERT(!left.isMatrix() && right.isMatrix());
+ return left.isVector() && left.getNominalSize() == right.getRows() &&
+ left.getNominalSize() == right.getCols();
+ case EOpMatrixTimesVector:
+ return left.getCols() == right.getNominalSize();
+ case EOpMatrixTimesScalar:
+ return true;
+ case EOpMatrixTimesScalarAssign:
+ ASSERT(left.isMatrix() && !right.isMatrix());
+ return !right.isVector();
+ case EOpMatrixTimesMatrix:
+ return left.getCols() == right.getRows();
+ case EOpMatrixTimesMatrixAssign:
+ ASSERT(left.isMatrix() && right.isMatrix());
+ // We need to check two things:
+ // 1. The matrix multiplication step is valid.
+ // 2. The result will have the same number of columns as the lvalue.
+ return left.getCols() == right.getRows() && left.getCols() == right.getCols();
+
+ default:
+ UNREACHABLE();
+ return false;
+ }
+}
+
+TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ if (!binaryOpCommonCheck(op, left, right, loc))
+ return nullptr;
+
+ switch (op)
+ {
+ case EOpEqual:
+ case EOpNotEqual:
+ break;
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ if (left->isMatrix() || left->isVector())
+ {
+ return nullptr;
+ }
+ break;
+ case EOpLogicalOr:
+ case EOpLogicalXor:
+ case EOpLogicalAnd:
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ if (left->getBasicType() != EbtBool || !left->isScalar() || !right->isScalar())
+ {
+ return nullptr;
+ }
+ // Basic types matching should have been already checked.
+ ASSERT(right->getBasicType() == EbtBool);
+ break;
+ case EOpAdd:
+ case EOpSub:
+ case EOpDiv:
+ case EOpMul:
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ if (left->getBasicType() == EbtBool)
+ {
+ return nullptr;
+ }
+ break;
+ case EOpIMod:
+ ASSERT(!left->isArray() && !right->isArray() && !left->getType().getStruct() &&
+ !right->getType().getStruct());
+ // Note that this is only for the % operator, not for mod()
+ if (left->getBasicType() == EbtBool || left->getBasicType() == EbtFloat)
+ {
+ return nullptr;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (op == EOpMul)
+ {
+ op = TIntermBinary::GetMulOpBasedOnOperands(left->getType(), right->getType());
+ if (!isMultiplicationTypeCombinationValid(op, left->getType(), right->getType()))
+ {
+ return nullptr;
+ }
+ }
+
+ TIntermBinary *node = new TIntermBinary(op, left, right);
+ node->setLine(loc);
+
+ // See if we can fold constants.
+ TIntermTyped *foldedNode = node->fold(&mDiagnostics);
+ if (foldedNode)
+ return foldedNode;
+
+ return node;
+}
+
+TIntermTyped *TParseContext::addBinaryMath(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ TIntermTyped *node = addBinaryMathInternal(op, left, right, loc);
+ if (node == 0)
+ {
+ binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
+ right->getCompleteString());
+ return left;
+ }
+ return node;
+}
+
+TIntermTyped *TParseContext::addBinaryMathBooleanResult(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ TIntermTyped *node = addBinaryMathInternal(op, left, right, loc);
+ if (node == 0)
+ {
+ binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
+ right->getCompleteString());
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setBConst(false);
+ return intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst),
+ loc);
+ }
+ return node;
+}
+
+TIntermBinary *TParseContext::createAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ if (binaryOpCommonCheck(op, left, right, loc))
+ {
+ if (op == EOpMulAssign)
+ {
+ op = TIntermBinary::GetMulAssignOpBasedOnOperands(left->getType(), right->getType());
+ if (!isMultiplicationTypeCombinationValid(op, left->getType(), right->getType()))
+ {
+ return nullptr;
+ }
+ }
+ TIntermBinary *node = new TIntermBinary(op, left, right);
+ node->setLine(loc);
+
+ return node;
+ }
+ return nullptr;
+}
+
+TIntermTyped *TParseContext::addAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ TIntermTyped *node = createAssign(op, left, right, loc);
+ if (node == nullptr)
+ {
+ assignError(loc, "assign", left->getCompleteString(), right->getCompleteString());
+ return left;
+ }
+ return node;
+}
+
+TIntermTyped *TParseContext::addComma(TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc)
+{
+ // WebGL2 section 5.26, the following results in an error:
+ // "Sequence operator applied to void, arrays, or structs containing arrays"
+ if (mShaderSpec == SH_WEBGL2_SPEC && (left->isArray() || left->getBasicType() == EbtVoid ||
+ left->getType().isStructureContainingArrays() ||
+ right->isArray() || right->getBasicType() == EbtVoid ||
+ right->getType().isStructureContainingArrays()))
+ {
+ error(loc,
+ "sequence operator is not allowed for void, arrays, or structs containing arrays",
+ ",");
+ }
+
+ return TIntermediate::AddComma(left, right, loc, mShaderVersion);
+}
+
+TIntermBranch *TParseContext::addBranch(TOperator op, const TSourceLoc &loc)
+{
+ switch (op)
+ {
+ case EOpContinue:
+ if (mLoopNestingLevel <= 0)
+ {
+ error(loc, "continue statement only allowed in loops", "");
+ }
+ break;
+ case EOpBreak:
+ if (mLoopNestingLevel <= 0 && mSwitchNestingLevel <= 0)
+ {
+ error(loc, "break statement only allowed in loops and switch statements", "");
+ }
+ break;
+ case EOpReturn:
+ if (mCurrentFunctionType->getBasicType() != EbtVoid)
+ {
+ error(loc, "non-void function must return a value", "return");
+ }
+ break;
+ default:
+ // No checks for discard
+ break;
+ }
+ return intermediate.addBranch(op, loc);
+}
+
+TIntermBranch *TParseContext::addBranch(TOperator op,
+ TIntermTyped *returnValue,
+ const TSourceLoc &loc)
+{
+ ASSERT(op == EOpReturn);
+ mFunctionReturnsValue = true;
+ if (mCurrentFunctionType->getBasicType() == EbtVoid)
+ {
+ error(loc, "void function cannot return a value", "return");
+ }
+ else if (*mCurrentFunctionType != returnValue->getType())
+ {
+ error(loc, "function return is not matching type:", "return");
+ }
+ return intermediate.addBranch(op, returnValue, loc);
+}
+
+void TParseContext::checkTextureOffsetConst(TIntermAggregate *functionCall)
+{
+ ASSERT(!functionCall->isUserDefined());
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+ TIntermNode *offset = nullptr;
+ TIntermSequence *arguments = functionCall->getSequence();
+ if (name.compare(0, 16, "texelFetchOffset") == 0 ||
+ name.compare(0, 16, "textureLodOffset") == 0 ||
+ name.compare(0, 20, "textureProjLodOffset") == 0 ||
+ name.compare(0, 17, "textureGradOffset") == 0 ||
+ name.compare(0, 21, "textureProjGradOffset") == 0)
+ {
+ offset = arguments->back();
+ }
+ else if (name.compare(0, 13, "textureOffset") == 0 ||
+ name.compare(0, 17, "textureProjOffset") == 0)
+ {
+ // A bias parameter might follow the offset parameter.
+ ASSERT(arguments->size() >= 3);
+ offset = (*arguments)[2];
+ }
+ if (offset != nullptr)
+ {
+ TIntermConstantUnion *offsetConstantUnion = offset->getAsConstantUnion();
+ if (offset->getAsTyped()->getQualifier() != EvqConst || !offsetConstantUnion)
+ {
+ TString unmangledName = TFunction::unmangleName(name);
+ error(functionCall->getLine(), "Texture offset must be a constant expression",
+ unmangledName.c_str());
+ }
+ else
+ {
+ ASSERT(offsetConstantUnion->getBasicType() == EbtInt);
+ size_t size = offsetConstantUnion->getType().getObjectSize();
+ const TConstantUnion *values = offsetConstantUnion->getUnionArrayPointer();
+ for (size_t i = 0u; i < size; ++i)
+ {
+ int offsetValue = values[i].getIConst();
+ if (offsetValue > mMaxProgramTexelOffset || offsetValue < mMinProgramTexelOffset)
+ {
+ std::stringstream tokenStream;
+ tokenStream << offsetValue;
+ std::string token = tokenStream.str();
+ error(offset->getLine(), "Texture offset value out of valid range",
+ token.c_str());
+ }
+ }
+ }
+ }
+}
+
+// GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+void TParseContext::checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall)
+{
+ ASSERT(!functionCall->isUserDefined());
+ const TString &name = functionCall->getFunctionSymbolInfo()->getName();
+
+ if (name.compare(0, 5, "image") == 0)
+ {
+ TIntermSequence *arguments = functionCall->getSequence();
+ TIntermNode *imageNode = (*arguments)[0];
+ TIntermSymbol *imageSymbol = imageNode->getAsSymbolNode();
+
+ const TMemoryQualifier &memoryQualifier = imageSymbol->getMemoryQualifier();
+
+ if (name.compare(5, 5, "Store") == 0)
+ {
+ if (memoryQualifier.readonly)
+ {
+ error(imageNode->getLine(),
+ "'imageStore' cannot be used with images qualified as 'readonly'",
+ imageSymbol->getSymbol().c_str());
+ }
+ }
+ else if (name.compare(5, 4, "Load") == 0)
+ {
+ if (memoryQualifier.writeonly)
+ {
+ error(imageNode->getLine(),
+ "'imageLoad' cannot be used with images qualified as 'writeonly'",
+ imageSymbol->getSymbol().c_str());
+ }
+ }
+ }
+}
+
+// GLSL ES 3.10 Revision 4, 13.51 Matching of Memory Qualifiers in Function Parameters
+void TParseContext::checkImageMemoryAccessForUserDefinedFunctions(
+ const TFunction *functionDefinition,
+ const TIntermAggregate *functionCall)
+{
+ ASSERT(functionCall->isUserDefined());
+
+ const TIntermSequence &arguments = *functionCall->getSequence();
+
+ ASSERT(functionDefinition->getParamCount() == arguments.size());
+
+ for (size_t i = 0; i < arguments.size(); ++i)
+ {
+ const TType &functionArgumentType = arguments[i]->getAsTyped()->getType();
+ const TType &functionParameterType = *functionDefinition->getParam(i).type;
+ ASSERT(functionArgumentType.getBasicType() == functionParameterType.getBasicType());
+
+ if (IsImage(functionArgumentType.getBasicType()))
+ {
+ const TMemoryQualifier &functionArgumentMemoryQualifier =
+ functionArgumentType.getMemoryQualifier();
+ const TMemoryQualifier &functionParameterMemoryQualifier =
+ functionParameterType.getMemoryQualifier();
+ if (functionArgumentMemoryQualifier.readonly &&
+ !functionParameterMemoryQualifier.readonly)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'readonly' qualifier from image",
+ arguments[i]->getAsSymbolNode()->getSymbol().c_str());
+ }
+
+ if (functionArgumentMemoryQualifier.writeonly &&
+ !functionParameterMemoryQualifier.writeonly)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'writeonly' qualifier from image",
+ arguments[i]->getAsSymbolNode()->getSymbol().c_str());
+ }
+
+ if (functionArgumentMemoryQualifier.coherent &&
+ !functionParameterMemoryQualifier.coherent)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'coherent' qualifier from image",
+ arguments[i]->getAsSymbolNode()->getSymbol().c_str());
+ }
+
+ if (functionArgumentMemoryQualifier.volatileQualifier &&
+ !functionParameterMemoryQualifier.volatileQualifier)
+ {
+ error(functionCall->getLine(),
+ "Function call discards the 'volatile' qualifier from image",
+ arguments[i]->getAsSymbolNode()->getSymbol().c_str());
+ }
+ }
+ }
+}
+
+TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
+ TIntermNode *paramNode,
+ TIntermNode *thisNode,
+ const TSourceLoc &loc,
+ bool *fatalError)
+{
+ *fatalError = false;
+ TOperator op = fnCall->getBuiltInOp();
+ TIntermTyped *callNode = nullptr;
+
+ if (thisNode != nullptr)
+ {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ int arraySize = 0;
+ TIntermTyped *typedThis = thisNode->getAsTyped();
+ if (fnCall->getName() != "length")
+ {
+ error(loc, "invalid method", fnCall->getName().c_str());
+ }
+ else if (paramNode != nullptr)
+ {
+ error(loc, "method takes no parameters", "length");
+ }
+ else if (typedThis == nullptr || !typedThis->isArray())
+ {
+ error(loc, "length can only be called on arrays", "length");
+ }
+ else
+ {
+ arraySize = typedThis->getArraySize();
+ if (typedThis->getAsSymbolNode() == nullptr)
+ {
+ // This code path can be hit with expressions like these:
+ // (a = b).length()
+ // (func()).length()
+ // (int[3](0, 1, 2)).length()
+ // ESSL 3.00 section 5.9 defines expressions so that this is not actually a valid
+ // expression.
+ // It allows "An array name with the length method applied" in contrast to GLSL 4.4
+ // spec section 5.9 which allows "An array, vector or matrix expression with the
+ // length method applied".
+ error(loc, "length can only be called on array names, not on array expressions",
+ "length");
+ }
+ }
+ unionArray->setIConst(arraySize);
+ callNode =
+ intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), loc);
+ }
+ else if (op != EOpNull)
+ {
+ // Then this should be a constructor.
+ callNode = addConstructor(paramNode, op, fnCall, loc);
+ }
+ else
+ {
+ //
+ // Not a constructor. Find it in the symbol table.
+ //
+ const TFunction *fnCandidate;
+ bool builtIn;
+ fnCandidate = findFunction(loc, fnCall, mShaderVersion, &builtIn);
+ if (fnCandidate)
+ {
+ //
+ // A declared function.
+ //
+ if (builtIn && !fnCandidate->getExtension().empty())
+ {
+ checkCanUseExtension(loc, fnCandidate->getExtension());
+ }
+ op = fnCandidate->getBuiltInOp();
+ if (builtIn && op != EOpNull)
+ {
+ //
+ // A function call mapped to a built-in operation.
+ //
+ if (fnCandidate->getParamCount() == 1)
+ {
+ //
+ // Treat it like a built-in unary operator.
+ //
+ TIntermAggregate *paramAgg = paramNode->getAsAggregate();
+ paramNode = paramAgg->getSequence()->front();
+ callNode = createUnaryMath(op, paramNode->getAsTyped(), loc,
+ &fnCandidate->getReturnType());
+ if (callNode == nullptr)
+ {
+ std::stringstream extraInfoStream;
+ extraInfoStream
+ << "built in unary operator function. Type: "
+ << static_cast<TIntermTyped *>(paramNode)->getCompleteString();
+ std::string extraInfo = extraInfoStream.str();
+ error(paramNode->getLine(), " wrong operand type", "Internal Error",
+ extraInfo.c_str());
+ *fatalError = true;
+ return nullptr;
+ }
+ }
+ else
+ {
+ TIntermAggregate *aggregate =
+ intermediate.setAggregateOperator(paramNode, op, loc);
+ aggregate->setType(fnCandidate->getReturnType());
+ aggregate->setPrecisionFromChildren();
+ if (aggregate->areChildrenConstQualified())
+ {
+ aggregate->getTypePointer()->setQualifier(EvqConst);
+ }
+
+ // Some built-in functions have out parameters too.
+ functionCallLValueErrorCheck(fnCandidate, aggregate);
+
+ // See if we can constant fold a built-in. Note that this may be possible even
+ // if it is not const-qualified.
+ TIntermTyped *foldedNode =
+ intermediate.foldAggregateBuiltIn(aggregate, &mDiagnostics);
+ if (foldedNode)
+ {
+ callNode = foldedNode;
+ }
+ else
+ {
+ callNode = aggregate;
+ }
+ }
+ }
+ else
+ {
+ // This is a real function call
+ TIntermAggregate *aggregate =
+ intermediate.setAggregateOperator(paramNode, EOpFunctionCall, loc);
+ aggregate->setType(fnCandidate->getReturnType());
+
+ // this is how we know whether the given function is a builtIn function or a user
+ // defined function
+ // if builtIn == false, it's a userDefined -> could be an overloaded
+ // builtIn function also
+ // if builtIn == true, it's definitely a builtIn function with EOpNull
+ if (!builtIn)
+ aggregate->setUserDefined();
+ aggregate->getFunctionSymbolInfo()->setFromFunction(*fnCandidate);
+
+ // This needs to happen after the function info including name is set
+ if (builtIn)
+ {
+ aggregate->setBuiltInFunctionPrecision();
+
+ checkTextureOffsetConst(aggregate);
+
+ checkImageMemoryAccessForBuiltinFunctions(aggregate);
+ }
+ else
+ {
+ checkImageMemoryAccessForUserDefinedFunctions(fnCandidate, aggregate);
+ }
+
+ callNode = aggregate;
+
+ functionCallLValueErrorCheck(fnCandidate, aggregate);
+ }
+ }
+ else
+ {
+ // error message was put out by findFunction()
+ // Put on a dummy node for error recovery
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setFConst(0.0f);
+ callNode = intermediate.addConstantUnion(unionArray,
+ TType(EbtFloat, EbpUndefined, EvqConst), loc);
+ }
+ }
+ return callNode;
+}
+
+TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
+ const TSourceLoc &loc)
+{
+ checkIsScalarBool(loc, cond);
+
+ if (trueExpression->getType() != falseExpression->getType())
+ {
+ binaryOpError(loc, ":", trueExpression->getCompleteString(),
+ falseExpression->getCompleteString());
+ return falseExpression;
+ }
+ if (IsOpaqueType(trueExpression->getBasicType()))
+ {
+ // ESSL 1.00 section 4.1.7
+ // ESSL 3.00 section 4.1.7
+ // Opaque/sampler types are not allowed in most types of expressions, including ternary.
+ // Note that structs containing opaque types don't need to be checked as structs are
+ // forbidden below.
+ error(loc, "ternary operator is not allowed for opaque types", ":");
+ return falseExpression;
+ }
+
+ // ESSL1 sections 5.2 and 5.7:
+ // ESSL3 section 5.7:
+ // Ternary operator is not among the operators allowed for structures/arrays.
+ if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct)
+ {
+ error(loc, "ternary operator is not allowed for structures or arrays", ":");
+ return falseExpression;
+ }
+ // WebGL2 section 5.26, the following results in an error:
+ // "Ternary operator applied to void, arrays, or structs containing arrays"
+ if (mShaderSpec == SH_WEBGL2_SPEC && trueExpression->getBasicType() == EbtVoid)
+ {
+ error(loc, "ternary operator is not allowed for void", ":");
+ return falseExpression;
+ }
+
+ return TIntermediate::AddTernarySelection(cond, trueExpression, falseExpression, loc);
+}
+
+//
+// Parse an array of strings using yyparse.
+//
+// Returns 0 for success.
+//
+int PaParseStrings(size_t count,
+ const char *const string[],
+ const int length[],
+ TParseContext *context)
+{
+ if ((count == 0) || (string == NULL))
+ return 1;
+
+ if (glslang_initialize(context))
+ return 1;
+
+ int error = glslang_scan(count, string, length, context);
+ if (!error)
+ error = glslang_parse(context);
+
+ glslang_finalize(context);
+
+ return (error == 0) && (context->numErrors() == 0) ? 0 : 1;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ParseContext.h b/gfx/angle/src/compiler/translator/ParseContext.h
new file mode 100755
index 000000000..cdc80755a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ParseContext.h
@@ -0,0 +1,438 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+#ifndef COMPILER_TRANSLATOR_PARSECONTEXT_H_
+#define COMPILER_TRANSLATOR_PARSECONTEXT_H_
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/DirectiveHandler.h"
+#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/QualifierTypes.h"
+#include "compiler/preprocessor/Preprocessor.h"
+
+namespace sh
+{
+
+struct TMatrixFields
+{
+ bool wholeRow;
+ bool wholeCol;
+ int row;
+ int col;
+};
+
+//
+// The following are extra variables needed during parsing, grouped together so
+// they can be passed to the parser without needing a global.
+//
+class TParseContext : angle::NonCopyable
+{
+ public:
+ TParseContext(TSymbolTable &symt,
+ TExtensionBehavior &ext,
+ sh::GLenum type,
+ ShShaderSpec spec,
+ ShCompileOptions options,
+ bool checksPrecErrors,
+ TInfoSink &is,
+ const ShBuiltInResources &resources);
+
+ const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
+ pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
+ void *getScanner() const { return mScanner; }
+ void setScanner(void *scanner) { mScanner = scanner; }
+ int getShaderVersion() const { return mShaderVersion; }
+ sh::GLenum getShaderType() const { return mShaderType; }
+ ShShaderSpec getShaderSpec() const { return mShaderSpec; }
+ int numErrors() const { return mDiagnostics.numErrors(); }
+ TInfoSink &infoSink() { return mDiagnostics.infoSink(); }
+ void error(const TSourceLoc &loc, const char *reason, const char *token,
+ const char *extraInfo="");
+ void warning(const TSourceLoc &loc, const char *reason, const char *token,
+ const char *extraInfo="");
+
+ // If isError is false, a warning will be reported instead.
+ void outOfRangeError(bool isError,
+ const TSourceLoc &loc,
+ const char *reason,
+ const char *token,
+ const char *extraInfo = "");
+
+ TIntermBlock *getTreeRoot() const { return mTreeRoot; }
+ void setTreeRoot(TIntermBlock *treeRoot) { mTreeRoot = treeRoot; }
+
+ bool getFragmentPrecisionHigh() const
+ {
+ return mFragmentPrecisionHighOnESSL1 || mShaderVersion >= 300;
+ }
+ void setFragmentPrecisionHighOnESSL1(bool fragmentPrecisionHigh)
+ {
+ mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh;
+ }
+
+ void setLoopNestingLevel(int loopNestintLevel)
+ {
+ mLoopNestingLevel = loopNestintLevel;
+ }
+
+ void incrLoopNestingLevel() { ++mLoopNestingLevel; }
+ void decrLoopNestingLevel() { --mLoopNestingLevel; }
+
+ void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
+ void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
+
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ sh::WorkGroupSize getComputeShaderLocalSize() const;
+
+ void enterFunctionDeclaration() { mDeclaringFunction = true; }
+
+ void exitFunctionDeclaration() { mDeclaringFunction = false; }
+
+ bool declaringFunction() const { return mDeclaringFunction; }
+
+ // This method is guaranteed to succeed, even if no variable with 'name' exists.
+ const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
+ TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
+ const TString *name,
+ const TSymbol *symbol);
+
+ bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line);
+
+ void assignError(const TSourceLoc &line, const char *op, TString left, TString right);
+ void unaryOpError(const TSourceLoc &line, const char *op, TString operand);
+ void binaryOpError(const TSourceLoc &line, const char *op, TString left, TString right);
+
+ // Check functions - the ones that return bool return false if an error was generated.
+
+ bool checkIsNotReserved(const TSourceLoc &line, const TString &identifier);
+ void checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type);
+ bool checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node);
+ void checkIsConst(TIntermTyped *node);
+ void checkIsScalarInteger(TIntermTyped *node, const char *token);
+ bool checkIsAtGlobalLevel(const TSourceLoc &line, const char *token);
+ bool checkConstructorArguments(const TSourceLoc &line,
+ TIntermNode *argumentsNode,
+ const TFunction &function,
+ TOperator op,
+ const TType &type);
+
+ // Returns a sanitized array size to use (the size is at least 1).
+ unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
+ bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
+ bool checkIsValidTypeForArray(const TSourceLoc &line, const TPublicType &elementType);
+ bool checkIsNonVoid(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
+ void checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
+ void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
+ bool checkIsNotSampler(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason);
+ bool checkIsNotImage(const TSourceLoc &line,
+ const TTypeSpecifierNonArray &pType,
+ const char *reason);
+ void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType);
+ void checkLocationIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier);
+ void checkIsParameterQualifierValid(const TSourceLoc &line,
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TType *type);
+ bool checkCanUseExtension(const TSourceLoc &line, const TString &extension);
+ void singleDeclarationErrorCheck(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation);
+ void checkLayoutQualifierSupported(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired);
+ bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier);
+ bool checkInternalFormatIsNotSpecified(const TSourceLoc &location,
+ TLayoutImageInternalFormat internalFormat);
+ void functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
+ void checkInvariantVariableQualifier(bool invariant,
+ const TQualifier qualifier,
+ const TSourceLoc &invariantLocation);
+ void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
+ const TPublicType &type,
+ const TSourceLoc &qualifierLocation);
+ void checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier);
+ const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
+ const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); }
+ bool supportsExtension(const char *extension);
+ bool isExtensionEnabled(const char *extension) const;
+ void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior);
+ void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl);
+
+ const TFunction* findFunction(
+ const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0);
+ bool executeInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ const TPublicType &pType,
+ TIntermTyped *initializer,
+ TIntermBinary **initNode);
+
+ void addFullySpecifiedType(TPublicType *typeSpecifier);
+ TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TPublicType &typeSpecifier);
+
+ TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
+ const TSourceLoc &identifierOrTypeLocation,
+ const TString &identifier);
+ TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression);
+ TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer);
+
+ // Parse a declaration like "type a[n] = initializer"
+ // Note that this does not apply to declarations like "type[n] a = initializer"
+ TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer);
+
+ TIntermAggregate *parseInvariantDeclaration(const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &identifierLoc,
+ const TString *identifier,
+ const TSymbol *symbol);
+
+ void parseDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ TIntermDeclaration *declarationOut);
+ void parseArrayDeclarator(TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &arrayLocation,
+ TIntermTyped *indexExpression,
+ TIntermDeclaration *declarationOut);
+ void parseInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut);
+
+ // Parse a declarator like "a[n] = initializer"
+ void parseArrayInitDeclarator(const TPublicType &publicType,
+ const TSourceLoc &identifierLocation,
+ const TString &identifier,
+ const TSourceLoc &indexLocation,
+ TIntermTyped *indexExpression,
+ const TSourceLoc &initLocation,
+ TIntermTyped *initializer,
+ TIntermDeclaration *declarationOut);
+
+ void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
+ TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
+ const TSourceLoc &location);
+ TIntermFunctionDefinition *addFunctionDefinition(const TFunction &function,
+ TIntermAggregate *functionParameters,
+ TIntermBlock *functionBody,
+ const TSourceLoc &location);
+ void parseFunctionDefinitionHeader(const TSourceLoc &location,
+ TFunction **function,
+ TIntermAggregate **aggregateOut);
+ TFunction *parseFunctionDeclarator(const TSourceLoc &location,
+ TFunction *function);
+ TFunction *parseFunctionHeader(const TPublicType &type,
+ const TString *name,
+ const TSourceLoc &location);
+ TFunction *addConstructorFunc(const TPublicType &publicType);
+ TIntermTyped *addConstructor(TIntermNode *arguments,
+ TOperator op,
+ TFunction *fnCall,
+ const TSourceLoc &line);
+
+ TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
+ const TSourceLoc& location,
+ TIntermTyped *indexExpression);
+ TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression,
+ const TSourceLoc &dotLocation,
+ const TString &fieldString,
+ const TSourceLoc &fieldLocation);
+
+ TFieldList *addStructDeclaratorListWithQualifiers(
+ const TTypeQualifierBuilder &typeQualifierBuilder,
+ TPublicType *typeSpecifier,
+ TFieldList *fieldList);
+ TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
+ TTypeSpecifierNonArray addStructure(const TSourceLoc &structLine,
+ const TSourceLoc &nameLine,
+ const TString *structName,
+ TFieldList *fieldList);
+
+ TIntermDeclaration *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
+ const TSourceLoc &nameLine,
+ const TString &blockName,
+ TFieldList *fieldList,
+ const TString *instanceName,
+ const TSourceLoc &instanceLine,
+ TIntermTyped *arrayIndex,
+ const TSourceLoc &arrayIndexLine);
+
+ void parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ sh::WorkGroupSize *localSize);
+ TLayoutQualifier parseLayoutQualifier(
+ const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
+ TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine);
+ TTypeQualifierBuilder *createTypeQualifierBuilder(const TSourceLoc &loc);
+ TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation);
+
+ // Performs an error check for embedded struct declarations.
+ void enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
+ void exitStructDeclaration();
+
+ void checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field);
+
+ TIntermSwitch *addSwitch(TIntermTyped *init,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc);
+ TIntermCase *addCase(TIntermTyped *condition, const TSourceLoc &loc);
+ TIntermCase *addDefault(const TSourceLoc &loc);
+
+ TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
+ TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
+ TIntermTyped *addBinaryMath(
+ TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+ TIntermTyped *addBinaryMathBooleanResult(
+ TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+ TIntermTyped *addAssign(
+ TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+
+ TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+
+ TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
+ TIntermBranch *addBranch(TOperator op, TIntermTyped *returnValue, const TSourceLoc &loc);
+
+ void checkTextureOffsetConst(TIntermAggregate *functionCall);
+ void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
+ void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
+ const TIntermAggregate *functionCall);
+ TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall,
+ TIntermNode *paramNode,
+ TIntermNode *thisNode,
+ const TSourceLoc &loc,
+ bool *fatalError);
+
+ TIntermTyped *addTernarySelection(TIntermTyped *cond,
+ TIntermTyped *trueExpression,
+ TIntermTyped *falseExpression,
+ const TSourceLoc &line);
+
+ // TODO(jmadill): make these private
+ TIntermediate intermediate; // to build a parse tree
+ TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed
+
+ private:
+ // Returns a clamped index.
+ int checkIndexOutOfRange(bool outOfRangeIndexIsError,
+ const TSourceLoc &location,
+ int index,
+ int arraySize,
+ const char *reason,
+ const char *token);
+
+ bool declareVariable(const TSourceLoc &line, const TString &identifier, const TType &type, TVariable **variable);
+
+ void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
+ const TString &identifier,
+ TPublicType *type);
+
+ bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
+ const TPublicType &elementType);
+
+ // Assumes that multiplication op has already been set based on the types.
+ bool isMultiplicationTypeCombinationValid(TOperator op, const TType &left, const TType &right);
+
+ bool checkIsMemoryQualifierNotSpecified(const TMemoryQualifier &memoryQualifier,
+ const TSourceLoc &location);
+ void checkOutParameterIsNotImage(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type);
+ void checkOutParameterIsNotOpaqueType(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type);
+ void checkOutParameterIsNotSampler(const TSourceLoc &line,
+ TQualifier qualifier,
+ const TType &type);
+
+ TIntermTyped *addBinaryMathInternal(
+ TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+ TIntermBinary *createAssign(TOperator op,
+ TIntermTyped *left,
+ TIntermTyped *right,
+ const TSourceLoc &loc);
+ // The funcReturnType parameter is expected to be non-null when the operation is a built-in function.
+ // It is expected to be null for other unary operators.
+ TIntermTyped *createUnaryMath(
+ TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType);
+
+ // Return true if the checks pass
+ bool binaryOpCommonCheck(
+ TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+
+ // Set to true when the last/current declarator list was started with an empty declaration.
+ bool mDeferredSingleDeclarationErrorCheck;
+
+ sh::GLenum mShaderType; // vertex or fragment language (future: pack or unpack)
+ ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
+ ShCompileOptions mCompileOptions; // Options passed to TCompiler
+ int mShaderVersion;
+ TIntermBlock *mTreeRoot; // root of parse tree being created
+ int mLoopNestingLevel; // 0 if outside all loops
+ int mStructNestingLevel; // incremented while parsing a struct declaration
+ int mSwitchNestingLevel; // 0 if outside all switch statements
+ const TType
+ *mCurrentFunctionType; // the return type of the function that's currently being parsed
+ bool mFunctionReturnsValue; // true if a non-void function has a return
+ bool mChecksPrecisionErrors; // true if an error will be generated when a variable is declared
+ // without precision, explicit or implicit.
+ bool mFragmentPrecisionHighOnESSL1; // true if highp precision is supported when compiling
+ // ESSL1.
+ TLayoutMatrixPacking mDefaultMatrixPacking;
+ TLayoutBlockStorage mDefaultBlockStorage;
+ TString mHashErrMsg;
+ TDiagnostics mDiagnostics;
+ TDirectiveHandler mDirectiveHandler;
+ pp::Preprocessor mPreprocessor;
+ void *mScanner;
+ bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor
+ bool mUsesFragColor;
+ bool mUsesSecondaryOutputs; // Track if we are using either gl_SecondaryFragData or
+ // gl_Secondary FragColor or both.
+ int mMinProgramTexelOffset;
+ int mMaxProgramTexelOffset;
+
+ // keep track of local group size declared in layout. It should be declared only once.
+ bool mComputeShaderLocalSizeDeclared;
+ sh::WorkGroupSize mComputeShaderLocalSize;
+ // keeps track whether we are declaring / defining a function
+ bool mDeclaringFunction;
+};
+
+int PaParseStrings(
+ size_t count, const char *const string[], const int length[], TParseContext *context);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_
diff --git a/gfx/angle/src/compiler/translator/PoolAlloc.cpp b/gfx/angle/src/compiler/translator/PoolAlloc.cpp
new file mode 100755
index 000000000..3b44afe33
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/PoolAlloc.cpp
@@ -0,0 +1,347 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/PoolAlloc.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "common/platform.h"
+#include "common/tls.h"
+#include "compiler/translator/InitializeGlobals.h"
+
+TLSIndex PoolIndex = TLS_INVALID_INDEX;
+
+bool InitializePoolIndex()
+{
+ assert(PoolIndex == TLS_INVALID_INDEX);
+
+ PoolIndex = CreateTLSIndex();
+ return PoolIndex != TLS_INVALID_INDEX;
+}
+
+void FreePoolIndex()
+{
+ assert(PoolIndex != TLS_INVALID_INDEX);
+
+ DestroyTLSIndex(PoolIndex);
+ PoolIndex = TLS_INVALID_INDEX;
+}
+
+TPoolAllocator* GetGlobalPoolAllocator()
+{
+ assert(PoolIndex != TLS_INVALID_INDEX);
+ return static_cast<TPoolAllocator*>(GetTLSValue(PoolIndex));
+}
+
+void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator)
+{
+ assert(PoolIndex != TLS_INVALID_INDEX);
+ SetTLSValue(PoolIndex, poolAllocator);
+}
+
+//
+// Implement the functionality of the TPoolAllocator class, which
+// is documented in PoolAlloc.h.
+//
+TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment)
+ : alignment(allocationAlignment),
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ pageSize(growthIncrement),
+ freeList(0),
+ inUseList(0),
+ numCalls(0),
+ totalBytes(0),
+#endif
+ mLocked(false)
+{
+ //
+ // Adjust alignment to be at least pointer aligned and
+ // power of 2.
+ //
+ size_t minAlign = sizeof(void*);
+ alignment &= ~(minAlign - 1);
+ if (alignment < minAlign)
+ alignment = minAlign;
+ size_t a = 1;
+ while (a < alignment)
+ a <<= 1;
+ alignment = a;
+ alignmentMask = a - 1;
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ //
+ // Don't allow page sizes we know are smaller than all common
+ // OS page sizes.
+ //
+ if (pageSize < 4 * 1024)
+ pageSize = 4 * 1024;
+
+ //
+ // A large currentPageOffset indicates a new page needs to
+ // be obtained to allocate memory.
+ //
+ currentPageOffset = pageSize;
+
+ //
+ // Align header skip
+ //
+ headerSkip = minAlign;
+ if (headerSkip < sizeof(tHeader)) {
+ headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
+ }
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ mStack.push_back({});
+#endif
+}
+
+TPoolAllocator::~TPoolAllocator()
+{
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ while (inUseList) {
+ tHeader* next = inUseList->nextPage;
+ inUseList->~tHeader();
+ delete [] reinterpret_cast<char*>(inUseList);
+ inUseList = next;
+ }
+
+ // We should not check the guard blocks
+ // here, because we did it already when the block was
+ // placed into the free list.
+ //
+ while (freeList) {
+ tHeader* next = freeList->nextPage;
+ delete [] reinterpret_cast<char*>(freeList);
+ freeList = next;
+ }
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ for (auto &allocs : mStack)
+ {
+ for (auto alloc : allocs)
+ {
+ free(alloc);
+ }
+ }
+ mStack.clear();
+#endif
+}
+
+// Support MSVC++ 6.0
+const unsigned char TAllocation::guardBlockBeginVal = 0xfb;
+const unsigned char TAllocation::guardBlockEndVal = 0xfe;
+const unsigned char TAllocation::userDataFill = 0xcd;
+
+#ifdef GUARD_BLOCKS
+ const size_t TAllocation::guardBlockSize = 16;
+#else
+ const size_t TAllocation::guardBlockSize = 0;
+#endif
+
+//
+// Check a single guard block for damage
+//
+void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
+{
+#ifdef GUARD_BLOCKS
+ for (size_t x = 0; x < guardBlockSize; x++) {
+ if (blockMem[x] != val) {
+ char assertMsg[80];
+
+ // We don't print the assert message. It's here just to be helpful.
+#if defined(_MSC_VER)
+ snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %Iu byte allocation at 0x%p\n",
+ locText, size, data());
+#else
+ snprintf(assertMsg, sizeof(assertMsg), "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
+ locText, size, data());
+#endif
+ assert(0 && "PoolAlloc: Damage in guard block");
+ }
+ }
+#endif
+}
+
+
+void TPoolAllocator::push()
+{
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ tAllocState state = { currentPageOffset, inUseList };
+
+ mStack.push_back(state);
+
+ //
+ // Indicate there is no current page to allocate from.
+ //
+ currentPageOffset = pageSize;
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ mStack.push_back({});
+#endif
+}
+
+//
+// Do a mass-deallocation of all the individual allocations
+// that have occurred since the last push(), or since the
+// last pop(), or since the object's creation.
+//
+// The deallocated pages are saved for future allocations.
+//
+void TPoolAllocator::pop()
+{
+ if (mStack.size() < 1)
+ return;
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ tHeader *page = mStack.back().page;
+ currentPageOffset = mStack.back().offset;
+
+ while (inUseList != page) {
+ // invoke destructor to free allocation list
+ inUseList->~tHeader();
+
+ tHeader* nextInUse = inUseList->nextPage;
+ if (inUseList->pageCount > 1)
+ delete [] reinterpret_cast<char*>(inUseList);
+ else {
+ inUseList->nextPage = freeList;
+ freeList = inUseList;
+ }
+ inUseList = nextInUse;
+ }
+
+ mStack.pop_back();
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ for (auto &alloc : mStack.back())
+ {
+ free(alloc);
+ }
+ mStack.pop_back();
+#endif
+}
+
+//
+// Do a mass-deallocation of all the individual allocations
+// that have occurred.
+//
+void TPoolAllocator::popAll()
+{
+ while (mStack.size() > 0)
+ pop();
+}
+
+void* TPoolAllocator::allocate(size_t numBytes)
+{
+ ASSERT(!mLocked);
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ //
+ // Just keep some interesting statistics.
+ //
+ ++numCalls;
+ totalBytes += numBytes;
+
+ // If we are using guard blocks, all allocations are bracketed by
+ // them: [guardblock][allocation][guardblock]. numBytes is how
+ // much memory the caller asked for. allocationSize is the total
+ // size including guard blocks. In release build,
+ // guardBlockSize=0 and this all gets optimized away.
+ size_t allocationSize = TAllocation::allocationSize(numBytes);
+ // Detect integer overflow.
+ if (allocationSize < numBytes)
+ return 0;
+
+ //
+ // Do the allocation, most likely case first, for efficiency.
+ // This step could be moved to be inline sometime.
+ //
+ if (allocationSize <= pageSize - currentPageOffset) {
+ //
+ // Safe to allocate from currentPageOffset.
+ //
+ unsigned char* memory = reinterpret_cast<unsigned char *>(inUseList) + currentPageOffset;
+ currentPageOffset += allocationSize;
+ currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
+
+ return initializeAllocation(inUseList, memory, numBytes);
+ }
+
+ if (allocationSize > pageSize - headerSkip) {
+ //
+ // Do a multi-page allocation. Don't mix these with the others.
+ // The OS is efficient and allocating and free-ing multiple pages.
+ //
+ size_t numBytesToAlloc = allocationSize + headerSkip;
+ // Detect integer overflow.
+ if (numBytesToAlloc < allocationSize)
+ return 0;
+
+ tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
+ if (memory == 0)
+ return 0;
+
+ // Use placement-new to initialize header
+ new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
+ inUseList = memory;
+
+ currentPageOffset = pageSize; // make next allocation come from a new page
+
+ // No guard blocks for multi-page allocations (yet)
+ return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(memory) + headerSkip);
+ }
+
+ //
+ // Need a simple page to allocate from.
+ //
+ tHeader* memory;
+ if (freeList) {
+ memory = freeList;
+ freeList = freeList->nextPage;
+ } else {
+ memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
+ if (memory == 0)
+ return 0;
+ }
+
+ // Use placement-new to initialize header
+ new(memory) tHeader(inUseList, 1);
+ inUseList = memory;
+
+ unsigned char* ret = reinterpret_cast<unsigned char *>(inUseList) + headerSkip;
+ currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
+
+ return initializeAllocation(inUseList, ret, numBytes);
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ void *alloc = malloc(numBytes + alignmentMask);
+ mStack.back().push_back(alloc);
+
+ intptr_t intAlloc = reinterpret_cast<intptr_t>(alloc);
+ intAlloc = (intAlloc + alignmentMask) & ~alignmentMask;
+ return reinterpret_cast<void *>(intAlloc);
+#endif
+}
+
+void TPoolAllocator::lock()
+{
+ ASSERT(!mLocked);
+ mLocked = true;
+}
+
+void TPoolAllocator::unlock()
+{
+ ASSERT(mLocked);
+ mLocked = false;
+}
+
+//
+// Check all allocations in a list for damage by calling check on each.
+//
+void TAllocation::checkAllocList() const
+{
+ for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc)
+ alloc->check();
+}
diff --git a/gfx/angle/src/compiler/translator/PoolAlloc.h b/gfx/angle/src/compiler/translator/PoolAlloc.h
new file mode 100755
index 000000000..f15b3e05d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/PoolAlloc.h
@@ -0,0 +1,303 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_POOLALLOC_H_
+#define COMPILER_TRANSLATOR_POOLALLOC_H_
+
+#ifdef _DEBUG
+#define GUARD_BLOCKS // define to enable guard block sanity checking
+#endif
+
+//
+// This header defines an allocator that can be used to efficiently
+// allocate a large number of small requests for heap memory, with the
+// intention that they are not individually deallocated, but rather
+// collectively deallocated at one time.
+//
+// This simultaneously
+//
+// * Makes each individual allocation much more efficient; the
+// typical allocation is trivial.
+// * Completely avoids the cost of doing individual deallocation.
+// * Saves the trouble of tracking down and plugging a large class of leaks.
+//
+// Individual classes can use this allocator by supplying their own
+// new and delete methods.
+//
+// STL containers can use this allocator by using the pool_allocator
+// class as the allocator (second) template argument.
+//
+
+#include <stddef.h>
+#include <string.h>
+#include <vector>
+
+// If we are using guard blocks, we must track each indivual
+// allocation. If we aren't using guard blocks, these
+// never get instantiated, so won't have any impact.
+//
+
+class TAllocation {
+public:
+ TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) :
+ size(size), mem(mem), prevAlloc(prev) {
+ // Allocations are bracketed:
+ // [allocationHeader][initialGuardBlock][userData][finalGuardBlock]
+ // This would be cleaner with if (guardBlockSize)..., but that
+ // makes the compiler print warnings about 0 length memsets,
+ // even with the if() protecting them.
+#ifdef GUARD_BLOCKS
+ memset(preGuard(), guardBlockBeginVal, guardBlockSize);
+ memset(data(), userDataFill, size);
+ memset(postGuard(), guardBlockEndVal, guardBlockSize);
+#endif
+ }
+
+ void check() const {
+ checkGuardBlock(preGuard(), guardBlockBeginVal, "before");
+ checkGuardBlock(postGuard(), guardBlockEndVal, "after");
+ }
+
+ void checkAllocList() const;
+
+ // Return total size needed to accomodate user buffer of 'size',
+ // plus our tracking data.
+ inline static size_t allocationSize(size_t size) {
+ return size + 2 * guardBlockSize + headerSize();
+ }
+
+ // Offset from surrounding buffer to get to user data buffer.
+ inline static unsigned char* offsetAllocation(unsigned char* m) {
+ return m + guardBlockSize + headerSize();
+ }
+
+private:
+ void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const;
+
+ // Find offsets to pre and post guard blocks, and user data buffer
+ unsigned char* preGuard() const { return mem + headerSize(); }
+ unsigned char* data() const { return preGuard() + guardBlockSize; }
+ unsigned char* postGuard() const { return data() + size; }
+
+ size_t size; // size of the user data area
+ unsigned char* mem; // beginning of our allocation (pts to header)
+ TAllocation* prevAlloc; // prior allocation in the chain
+
+ // Support MSVC++ 6.0
+ const static unsigned char guardBlockBeginVal;
+ const static unsigned char guardBlockEndVal;
+ const static unsigned char userDataFill;
+
+ const static size_t guardBlockSize;
+#ifdef GUARD_BLOCKS
+ inline static size_t headerSize() { return sizeof(TAllocation); }
+#else
+ inline static size_t headerSize() { return 0; }
+#endif
+};
+
+//
+// There are several stacks. One is to track the pushing and popping
+// of the user, and not yet implemented. The others are simply a
+// repositories of free pages or used pages.
+//
+// Page stacks are linked together with a simple header at the beginning
+// of each allocation obtained from the underlying OS. Multi-page allocations
+// are returned to the OS. Individual page allocations are kept for future
+// re-use.
+//
+// The "page size" used is not, nor must it match, the underlying OS
+// page size. But, having it be about that size or equal to a set of
+// pages is likely most optimal.
+//
+class TPoolAllocator {
+public:
+ TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16);
+
+ //
+ // Don't call the destructor just to free up the memory, call pop()
+ //
+ ~TPoolAllocator();
+
+ //
+ // Call push() to establish a new place to pop memory too. Does not
+ // have to be called to get things started.
+ //
+ void push();
+
+ //
+ // Call pop() to free all memory allocated since the last call to push(),
+ // or if no last call to push, frees all memory since first allocation.
+ //
+ void pop();
+
+ //
+ // Call popAll() to free all memory allocated.
+ //
+ void popAll();
+
+ //
+ // Call allocate() to actually acquire memory. Returns 0 if no memory
+ // available, otherwise a properly aligned pointer to 'numBytes' of memory.
+ //
+ void* allocate(size_t numBytes);
+
+ //
+ // There is no deallocate. The point of this class is that
+ // deallocation can be skipped by the user of it, as the model
+ // of use is to simultaneously deallocate everything at once
+ // by calling pop(), and to not have to solve memory leak problems.
+ //
+
+ // Catch unwanted allocations.
+ // TODO(jmadill): Remove this when we remove the global allocator.
+ void lock();
+ void unlock();
+
+ private:
+ size_t alignment; // all returned allocations will be aligned at
+ // this granularity, which will be a power of 2
+ size_t alignmentMask;
+
+#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ friend struct tHeader;
+
+ struct tHeader {
+ tHeader(tHeader* nextPage, size_t pageCount) :
+ nextPage(nextPage),
+ pageCount(pageCount)
+#ifdef GUARD_BLOCKS
+ , lastAllocation(0)
+#endif
+ { }
+
+ ~tHeader() {
+#ifdef GUARD_BLOCKS
+ if (lastAllocation)
+ lastAllocation->checkAllocList();
+#endif
+ }
+
+ tHeader* nextPage;
+ size_t pageCount;
+#ifdef GUARD_BLOCKS
+ TAllocation* lastAllocation;
+#endif
+ };
+
+ struct tAllocState {
+ size_t offset;
+ tHeader* page;
+ };
+ typedef std::vector<tAllocState> tAllocStack;
+
+ // Track allocations if and only if we're using guard blocks
+ void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) {
+#ifdef GUARD_BLOCKS
+ new(memory) TAllocation(numBytes, memory, block->lastAllocation);
+ block->lastAllocation = reinterpret_cast<TAllocation*>(memory);
+#endif
+ // This is optimized entirely away if GUARD_BLOCKS is not defined.
+ return TAllocation::offsetAllocation(memory);
+ }
+
+ size_t pageSize; // granularity of allocation from the OS
+ size_t headerSkip; // amount of memory to skip to make room for the
+ // header (basically, size of header, rounded
+ // up to make it aligned
+ size_t currentPageOffset; // next offset in top of inUseList to allocate from
+ tHeader* freeList; // list of popped memory
+ tHeader* inUseList; // list of all memory currently being used
+ tAllocStack mStack; // stack of where to allocate from, to partition pool
+
+ int numCalls; // just an interesting statistic
+ size_t totalBytes; // just an interesting statistic
+
+#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC)
+ std::vector<std::vector<void *>> mStack;
+#endif
+
+ TPoolAllocator& operator=(const TPoolAllocator&); // dont allow assignment operator
+ TPoolAllocator(const TPoolAllocator&); // dont allow default copy constructor
+ bool mLocked;
+};
+
+
+//
+// There could potentially be many pools with pops happening at
+// different times. But a simple use is to have a global pop
+// with everyone using the same global allocator.
+//
+extern TPoolAllocator* GetGlobalPoolAllocator();
+extern void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator);
+
+//
+// This STL compatible allocator is intended to be used as the allocator
+// parameter to templatized STL containers, like vector and map.
+//
+// It will use the pools for allocation, and not
+// do any deallocation, but will still do destruction.
+//
+template<class T>
+class pool_allocator {
+public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ template<class Other>
+ struct rebind {
+ typedef pool_allocator<Other> other;
+ };
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pool_allocator() { }
+
+ template<class Other>
+ pool_allocator(const pool_allocator<Other>& p) { }
+
+ template <class Other>
+ pool_allocator<T>& operator=(const pool_allocator<Other>& p) { return *this; }
+
+#if defined(__SUNPRO_CC) && !defined(_RWSTD_ALLOCATOR)
+ // libCStd on some platforms have a different allocate/deallocate interface.
+ // Caller pre-bakes sizeof(T) into 'n' which is the number of bytes to be
+ // allocated, not the number of elements.
+ void* allocate(size_type n) {
+ return getAllocator().allocate(n);
+ }
+ void* allocate(size_type n, const void*) {
+ return getAllocator().allocate(n);
+ }
+ void deallocate(void*, size_type) {}
+#else
+ pointer allocate(size_type n) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T)));
+ }
+ pointer allocate(size_type n, const void*) {
+ return reinterpret_cast<pointer>(getAllocator().allocate(n * sizeof(T)));
+ }
+ void deallocate(pointer, size_type) {}
+#endif // _RWSTD_ALLOCATOR
+
+ void construct(pointer p, const T& val) { new ((void *)p) T(val); }
+ void destroy(pointer p) { p->T::~T(); }
+
+ bool operator==(const pool_allocator& rhs) const { return true; }
+ bool operator!=(const pool_allocator& rhs) const { return false; }
+
+ size_type max_size() const { return static_cast<size_type>(-1) / sizeof(T); }
+ size_type max_size(int size) const { return static_cast<size_type>(-1) / size; }
+
+ TPoolAllocator& getAllocator() const { return *GetGlobalPoolAllocator(); }
+};
+
+#endif // COMPILER_TRANSLATOR_POOLALLOC_H_
diff --git a/gfx/angle/src/compiler/translator/Pragma.h b/gfx/angle/src/compiler/translator/Pragma.h
new file mode 100755
index 000000000..57b113497
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Pragma.h
@@ -0,0 +1,32 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_PRAGMA_H_
+#define COMPILER_TRANSLATOR_PRAGMA_H_
+
+struct TPragma
+{
+ struct STDGL
+ {
+ STDGL() : invariantAll(false) { }
+
+ bool invariantAll;
+ };
+
+
+ // By default optimization is turned on and debug is turned off.
+ // Precision emulation is turned on by default, but has no effect unless
+ // the extension is enabled.
+ TPragma() : optimize(true), debug(false), debugShaderPrecision(true) { }
+ TPragma(bool o, bool d) : optimize(o), debug(d), debugShaderPrecision(true) { }
+
+ bool optimize;
+ bool debug;
+ bool debugShaderPrecision;
+ STDGL stdgl;
+};
+
+#endif // COMPILER_TRANSLATOR_PRAGMA_H_
diff --git a/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.cpp b/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.cpp
new file mode 100755
index 000000000..7ec434796
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
+
+#include "compiler/translator/PruneEmptyDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class PruneEmptyDeclarationsTraverser : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermNode *root);
+ private:
+ PruneEmptyDeclarationsTraverser();
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+};
+
+void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
+{
+ PruneEmptyDeclarationsTraverser prune;
+ root->traverse(&prune);
+ prune.updateTree();
+}
+
+PruneEmptyDeclarationsTraverser::PruneEmptyDeclarationsTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool PruneEmptyDeclarationsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ if (sequence->size() >= 1)
+ {
+ TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
+ // Prune declarations without a variable name, unless it's an interface block declaration.
+ if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
+ {
+ if (sequence->size() > 1)
+ {
+ // Generate a replacement that will remove the empty declarator in the beginning of
+ // a declarator list. Example of a declaration that will be changed:
+ // float, a;
+ // will be changed to
+ // float a;
+ // This applies also to struct declarations.
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
+ }
+ else if (sym->getBasicType() != EbtStruct)
+ {
+ // Single struct declarations may just declare the struct type and no variables, so
+ // they should not be pruned. All other single empty declarations can be pruned
+ // entirely. Example of an empty declaration that will be pruned:
+ // float;
+ TIntermSequence emptyReplacement;
+ TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
+ // The declaration may be inside a block or in a loop init expression.
+ ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
+ if (parentAsBlock)
+ {
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
+ }
+ else
+ {
+ queueReplacement(node, nullptr, OriginalNode::IS_DROPPED);
+ }
+ }
+ else if (sym->getType().getQualifier() != EvqGlobal &&
+ sym->getType().getQualifier() != EvqTemporary)
+ {
+ // We've hit an empty struct declaration with a qualifier, for example like
+ // this:
+ // const struct a { int i; };
+ // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so
+ // we convert the declaration to a regular struct declaration. This is okay,
+ // since ESSL 1.00 spec section 4.1.8 says about structs that "The optional
+ // qualifiers only apply to any declarators, and are not part of the type being
+ // defined for name."
+
+ if (mInGlobalScope)
+ {
+ sym->getTypePointer()->setQualifier(EvqGlobal);
+ }
+ else
+ {
+ sym->getTypePointer()->setQualifier(EvqTemporary);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void PruneEmptyDeclarations(TIntermNode *root)
+{
+ PruneEmptyDeclarationsTraverser::apply(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.h b/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.h
new file mode 100755
index 000000000..f03657766
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/PruneEmptyDeclarations.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
+
+#ifndef COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
+#define COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void PruneEmptyDeclarations(TIntermNode *root);
+}
+
+#endif // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
diff --git a/gfx/angle/src/compiler/translator/QualifierTypes.cpp b/gfx/angle/src/compiler/translator/QualifierTypes.cpp
new file mode 100644
index 000000000..302f5177d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/QualifierTypes.cpp
@@ -0,0 +1,727 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/QualifierTypes.h"
+
+#include "compiler/translator/Diagnostics.h"
+
+#include <algorithm>
+
+namespace sh
+{
+
+namespace
+{
+
+// GLSL ES 3.10 does not impose a strict order on type qualifiers and allows multiple layout
+// declarations.
+// GLSL ES 3.10 Revision 4, 4.10 Order of Qualification
+bool AreTypeQualifierChecksRelaxed(int shaderVersion)
+{
+ return shaderVersion >= 310;
+}
+
+bool IsScopeQualifier(TQualifier qualifier)
+{
+ return qualifier == EvqGlobal || qualifier == EvqTemporary;
+}
+
+bool IsScopeQualifierWrapper(const TQualifierWrapperBase *qualifier)
+{
+ if (qualifier->getType() != QtStorage)
+ return false;
+ const TStorageQualifierWrapper *storageQualifier =
+ static_cast<const TStorageQualifierWrapper *>(qualifier);
+ TQualifier q = storageQualifier->getQualifier();
+ return IsScopeQualifier(q);
+}
+
+// Returns true if the invariant for the qualifier sequence holds
+bool IsInvariantCorrect(const TTypeQualifierBuilder::QualifierSequence &qualifiers)
+{
+ // We should have at least one qualifier.
+ // The first qualifier always tells the scope.
+ return qualifiers.size() >= 1 && IsScopeQualifierWrapper(qualifiers[0]);
+}
+
+// Returns true if there are qualifiers which have been specified multiple times
+// If areQualifierChecksRelaxed is set to true, then layout qualifier repetition is allowed.
+bool HasRepeatingQualifiers(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
+ bool areQualifierChecksRelaxed,
+ std::string *errorMessage)
+{
+ bool invariantFound = false;
+ bool precisionFound = false;
+ bool layoutFound = false;
+ bool interpolationFound = false;
+
+ unsigned int locationsSpecified = 0;
+ bool isOut = false;
+
+ // The iteration starts from one since the first qualifier only reveals the scope of the
+ // expression. It is inserted first whenever the sequence gets created.
+ for (size_t i = 1; i < qualifiers.size(); ++i)
+ {
+ switch (qualifiers[i]->getType())
+ {
+ case QtInvariant:
+ {
+ if (invariantFound)
+ {
+ *errorMessage = "The invariant qualifier specified multiple times.";
+ return true;
+ }
+ invariantFound = true;
+ break;
+ }
+ case QtPrecision:
+ {
+ if (precisionFound)
+ {
+ *errorMessage = "The precision qualifier specified multiple times.";
+ return true;
+ }
+ precisionFound = true;
+ break;
+ }
+ case QtLayout:
+ {
+ if (layoutFound && !areQualifierChecksRelaxed)
+ {
+ *errorMessage = "The layout qualifier specified multiple times.";
+ return true;
+ }
+ if (invariantFound && !areQualifierChecksRelaxed)
+ {
+ // This combination is not correct according to the syntax specified in the
+ // formal grammar in the ESSL 3.00 spec. In ESSL 3.10 the grammar does not have
+ // a similar restriction.
+ *errorMessage =
+ "The layout qualifier and invariant qualifier cannot coexist in the same "
+ "declaration according to the grammar.";
+ return true;
+ }
+ layoutFound = true;
+ const TLayoutQualifier &currentQualifier =
+ static_cast<const TLayoutQualifierWrapper *>(qualifiers[i])->getQualifier();
+ locationsSpecified += currentQualifier.locationsSpecified;
+ break;
+ }
+ case QtInterpolation:
+ {
+ // 'centroid' is treated as a storage qualifier
+ // 'flat centroid' will be squashed to 'flat'
+ // 'smooth centroid' will be squashed to 'centroid'
+ if (interpolationFound)
+ {
+ *errorMessage = "The interpolation qualifier specified multiple times.";
+ return true;
+ }
+ interpolationFound = true;
+ break;
+ }
+ case QtStorage:
+ {
+ // Go over all of the storage qualifiers up until the current one and check for
+ // repetitions.
+ TQualifier currentQualifier =
+ static_cast<const TStorageQualifierWrapper *>(qualifiers[i])->getQualifier();
+ if (currentQualifier == EvqVertexOut || currentQualifier == EvqFragmentOut)
+ {
+ isOut = true;
+ }
+ for (size_t j = 1; j < i; ++j)
+ {
+ if (qualifiers[j]->getType() == QtStorage)
+ {
+ const TStorageQualifierWrapper *previousQualifierWrapper =
+ static_cast<const TStorageQualifierWrapper *>(qualifiers[j]);
+ TQualifier previousQualifier = previousQualifierWrapper->getQualifier();
+ if (currentQualifier == previousQualifier)
+ {
+ *errorMessage = previousQualifierWrapper->getQualifierString().c_str();
+ *errorMessage += " specified multiple times";
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ case QtMemory:
+ {
+ // Go over all of the memory qualifiers up until the current one and check for
+ // repetitions.
+ // Having both readonly and writeonly in a sequence is valid.
+ // GLSL ES 3.10 Revision 4, 4.9 Memory Access Qualifiers
+ TQualifier currentQualifier =
+ static_cast<const TMemoryQualifierWrapper *>(qualifiers[i])->getQualifier();
+ for (size_t j = 1; j < i; ++j)
+ {
+ if (qualifiers[j]->getType() == QtMemory)
+ {
+ const TMemoryQualifierWrapper *previousQualifierWrapper =
+ static_cast<const TMemoryQualifierWrapper *>(qualifiers[j]);
+ TQualifier previousQualifier = previousQualifierWrapper->getQualifier();
+ if (currentQualifier == previousQualifier)
+ {
+ *errorMessage = previousQualifierWrapper->getQualifierString().c_str();
+ *errorMessage += " specified multiple times";
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (locationsSpecified > 1 && isOut)
+ {
+ // GLSL ES 3.00.6 section 4.3.8.2 Output Layout Qualifiers
+ // GLSL ES 3.10 section 4.4.2 Output Layout Qualifiers
+ // "The qualifier may appear at most once within a declaration."
+ *errorMessage = "Output layout location specified multiple times.";
+ return true;
+ }
+
+ return false;
+}
+
+// GLSL ES 3.00_6, 4.7 Order of Qualification
+// The correct order of qualifiers is:
+// invariant-qualifier interpolation-qualifier storage-qualifier precision-qualifier
+// layout-qualifier has to be before storage-qualifier.
+bool AreQualifiersInOrder(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
+ std::string *errorMessage)
+{
+ bool foundInterpolation = false;
+ bool foundStorage = false;
+ bool foundPrecision = false;
+ for (size_t i = 1; i < qualifiers.size(); ++i)
+ {
+ switch (qualifiers[i]->getType())
+ {
+ case QtInvariant:
+ if (foundInterpolation || foundStorage || foundPrecision)
+ {
+ *errorMessage = "The invariant qualifier has to be first in the expression.";
+ return false;
+ }
+ break;
+ case QtInterpolation:
+ if (foundStorage)
+ {
+ *errorMessage = "Storage qualifiers have to be after interpolation qualifiers.";
+ return false;
+ }
+ else if (foundPrecision)
+ {
+ *errorMessage =
+ "Precision qualifiers have to be after interpolation qualifiers.";
+ return false;
+ }
+ foundInterpolation = true;
+ break;
+ case QtLayout:
+ if (foundStorage)
+ {
+ *errorMessage = "Storage qualifiers have to be after layout qualifiers.";
+ return false;
+ }
+ else if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after layout qualifiers.";
+ return false;
+ }
+ break;
+ case QtStorage:
+ if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after storage qualifiers.";
+ return false;
+ }
+ foundStorage = true;
+ break;
+ case QtMemory:
+ if (foundPrecision)
+ {
+ *errorMessage = "Precision qualifiers have to be after memory qualifiers.";
+ return false;
+ }
+ break;
+ case QtPrecision:
+ foundPrecision = true;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ return true;
+}
+
+struct QualifierComparator
+{
+ bool operator()(const TQualifierWrapperBase *q1, const TQualifierWrapperBase *q2)
+ {
+ return q1->getRank() < q2->getRank();
+ }
+};
+
+void SortSequence(TTypeQualifierBuilder::QualifierSequence &qualifiers)
+{
+ // We need a stable sorting algorithm since the order of layout-qualifier declarations matter.
+ // The sorting starts from index 1, instead of 0, since the element at index 0 tells the scope
+ // and we always want it to be first.
+ std::stable_sort(qualifiers.begin() + 1, qualifiers.end(), QualifierComparator());
+}
+
+// Handles the joining of storage qualifiers for variables.
+bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
+{
+ switch (*joinedQualifier)
+ {
+ case EvqGlobal:
+ *joinedQualifier = storageQualifier;
+ break;
+ case EvqTemporary:
+ {
+ switch (storageQualifier)
+ {
+ case EvqConst:
+ *joinedQualifier = storageQualifier;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqSmooth:
+ {
+ switch (storageQualifier)
+ {
+ case EvqCentroid:
+ *joinedQualifier = EvqCentroid;
+ break;
+ case EvqVertexOut:
+ *joinedQualifier = EvqSmoothOut;
+ break;
+ case EvqFragmentIn:
+ *joinedQualifier = EvqSmoothIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqFlat:
+ {
+ switch (storageQualifier)
+ {
+ case EvqCentroid:
+ *joinedQualifier = EvqFlat;
+ break;
+ case EvqVertexOut:
+ *joinedQualifier = EvqFlatOut;
+ break;
+ case EvqFragmentIn:
+ *joinedQualifier = EvqFlatIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ case EvqCentroid:
+ {
+ switch (storageQualifier)
+ {
+ case EvqVertexOut:
+ *joinedQualifier = EvqCentroidOut;
+ break;
+ case EvqFragmentIn:
+ *joinedQualifier = EvqCentroidIn;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+// Handles the joining of storage qualifiers for a parameter in a function.
+bool JoinParameterStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
+{
+ switch (*joinedQualifier)
+ {
+ case EvqTemporary:
+ *joinedQualifier = storageQualifier;
+ break;
+ case EvqConst:
+ {
+ switch (storageQualifier)
+ {
+ case EvqIn:
+ *joinedQualifier = EvqConstReadOnly;
+ break;
+ default:
+ return false;
+ }
+ break;
+ }
+ default:
+ return false;
+ }
+ return true;
+}
+
+bool JoinMemoryQualifier(TMemoryQualifier *joinedMemoryQualifier, TQualifier memoryQualifier)
+{
+ switch (memoryQualifier)
+ {
+ case EvqReadOnly:
+ joinedMemoryQualifier->readonly = true;
+ break;
+ case EvqWriteOnly:
+ joinedMemoryQualifier->writeonly = true;
+ break;
+ case EvqCoherent:
+ joinedMemoryQualifier->coherent = true;
+ break;
+ case EvqRestrict:
+ joinedMemoryQualifier->restrictQualifier = true;
+ break;
+ case EvqVolatile:
+ // Variables having the volatile qualifier are automatcally treated as coherent as well.
+ // GLSL ES 3.10, Revision 4, 4.9 Memory Access Qualifiers
+ joinedMemoryQualifier->volatileQualifier = true;
+ joinedMemoryQualifier->coherent = true;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return true;
+}
+
+TTypeQualifier GetVariableTypeQualifierFromSortedSequence(
+ const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
+ TDiagnostics *diagnostics)
+{
+ TTypeQualifier typeQualifier(
+ static_cast<const TStorageQualifierWrapper *>(sortedSequence[0])->getQualifier(),
+ sortedSequence[0]->getLine());
+ for (size_t i = 1; i < sortedSequence.size(); ++i)
+ {
+ const TQualifierWrapperBase *qualifier = sortedSequence[i];
+ bool isQualifierValid = false;
+ switch (qualifier->getType())
+ {
+ case QtInvariant:
+ isQualifierValid = true;
+ typeQualifier.invariant = true;
+ break;
+ case QtInterpolation:
+ {
+ switch (typeQualifier.qualifier)
+ {
+ case EvqGlobal:
+ isQualifierValid = true;
+ typeQualifier.qualifier =
+ static_cast<const TInterpolationQualifierWrapper *>(qualifier)
+ ->getQualifier();
+ break;
+ default:
+ isQualifierValid = false;
+ }
+ break;
+ }
+ case QtLayout:
+ {
+ const TLayoutQualifierWrapper *layoutQualifierWrapper =
+ static_cast<const TLayoutQualifierWrapper *>(qualifier);
+ isQualifierValid = true;
+ typeQualifier.layoutQualifier = sh::JoinLayoutQualifiers(
+ typeQualifier.layoutQualifier, layoutQualifierWrapper->getQualifier(),
+ layoutQualifierWrapper->getLine(), diagnostics);
+ break;
+ }
+ case QtStorage:
+ isQualifierValid = JoinVariableStorageQualifier(
+ &typeQualifier.qualifier,
+ static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtPrecision:
+ isQualifierValid = true;
+ typeQualifier.precision =
+ static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
+ ASSERT(typeQualifier.precision != EbpUndefined);
+ break;
+ case QtMemory:
+ isQualifierValid = JoinMemoryQualifier(
+ &typeQualifier.memoryQualifier,
+ static_cast<const TMemoryQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (!isQualifierValid)
+ {
+ const TString &qualifierString = qualifier->getQualifierString();
+ diagnostics->error(qualifier->getLine(), "invalid qualifier combination",
+ qualifierString.c_str(), "");
+ break;
+ }
+ }
+ return typeQualifier;
+}
+
+TTypeQualifier GetParameterTypeQualifierFromSortedSequence(
+ const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
+ TDiagnostics *diagnostics)
+{
+ TTypeQualifier typeQualifier(EvqTemporary, sortedSequence[0]->getLine());
+ for (size_t i = 1; i < sortedSequence.size(); ++i)
+ {
+ const TQualifierWrapperBase *qualifier = sortedSequence[i];
+ bool isQualifierValid = false;
+ switch (qualifier->getType())
+ {
+ case QtInvariant:
+ case QtInterpolation:
+ case QtLayout:
+ break;
+ case QtMemory:
+ isQualifierValid = JoinMemoryQualifier(
+ &typeQualifier.memoryQualifier,
+ static_cast<const TMemoryQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtStorage:
+ isQualifierValid = JoinParameterStorageQualifier(
+ &typeQualifier.qualifier,
+ static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
+ break;
+ case QtPrecision:
+ isQualifierValid = true;
+ typeQualifier.precision =
+ static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
+ ASSERT(typeQualifier.precision != EbpUndefined);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (!isQualifierValid)
+ {
+ const TString &qualifierString = qualifier->getQualifierString();
+ diagnostics->error(qualifier->getLine(), "invalid parameter qualifier",
+ qualifierString.c_str(), "");
+ break;
+ }
+ }
+
+ switch (typeQualifier.qualifier)
+ {
+ case EvqIn:
+ case EvqConstReadOnly: // const in
+ case EvqOut:
+ case EvqInOut:
+ break;
+ case EvqConst:
+ typeQualifier.qualifier = EvqConstReadOnly;
+ break;
+ case EvqTemporary:
+ // no qualifier has been specified, set it to EvqIn which is the default
+ typeQualifier.qualifier = EvqIn;
+ break;
+ default:
+ diagnostics->error(sortedSequence[0]->getLine(), "Invalid parameter qualifier ",
+ getQualifierString(typeQualifier.qualifier), "");
+ }
+ return typeQualifier;
+}
+} // namespace
+
+TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation,
+ TDiagnostics *diagnostics)
+{
+ TLayoutQualifier joinedQualifier = leftQualifier;
+
+ if (rightQualifier.location != -1)
+ {
+ joinedQualifier.location = rightQualifier.location;
+ ++joinedQualifier.locationsSpecified;
+ }
+ if (rightQualifier.matrixPacking != EmpUnspecified)
+ {
+ joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
+ }
+ if (rightQualifier.blockStorage != EbsUnspecified)
+ {
+ joinedQualifier.blockStorage = rightQualifier.blockStorage;
+ }
+
+ for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
+ {
+ if (rightQualifier.localSize[i] != -1)
+ {
+ if (joinedQualifier.localSize[i] != -1 &&
+ joinedQualifier.localSize[i] != rightQualifier.localSize[i])
+ {
+ diagnostics->error(rightQualifierLocation,
+ "Cannot have multiple different work group size specifiers",
+ getWorkGroupSizeString(i), "");
+ }
+ joinedQualifier.localSize[i] = rightQualifier.localSize[i];
+ }
+ }
+
+ if (rightQualifier.imageInternalFormat != EiifUnspecified)
+ {
+ joinedQualifier.imageInternalFormat = rightQualifier.imageInternalFormat;
+ }
+
+ return joinedQualifier;
+}
+
+unsigned int TInvariantQualifierWrapper::getRank() const
+{
+ return 0u;
+}
+
+unsigned int TInterpolationQualifierWrapper::getRank() const
+{
+ return 1u;
+}
+
+unsigned int TLayoutQualifierWrapper::getRank() const
+{
+ return 2u;
+}
+
+unsigned int TStorageQualifierWrapper::getRank() const
+{
+ // Force the 'centroid' auxilary storage qualifier to be always first among all storage
+ // qualifiers.
+ if (mStorageQualifier == EvqCentroid)
+ {
+ return 3u;
+ }
+ else
+ {
+ return 4u;
+ }
+}
+
+unsigned int TMemoryQualifierWrapper::getRank() const
+{
+ return 4u;
+}
+
+unsigned int TPrecisionQualifierWrapper::getRank() const
+{
+ return 5u;
+}
+
+TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc)
+ : layoutQualifier(TLayoutQualifier::create()),
+ memoryQualifier(TMemoryQualifier::create()),
+ precision(EbpUndefined),
+ qualifier(scope),
+ invariant(false),
+ line(loc)
+{
+ ASSERT(IsScopeQualifier(qualifier));
+}
+
+TTypeQualifierBuilder::TTypeQualifierBuilder(const TStorageQualifierWrapper *scope,
+ int shaderVersion)
+ : mShaderVersion(shaderVersion)
+{
+ ASSERT(IsScopeQualifier(scope->getQualifier()));
+ mQualifiers.push_back(scope);
+}
+
+void TTypeQualifierBuilder::appendQualifier(const TQualifierWrapperBase *qualifier)
+{
+ mQualifiers.push_back(qualifier);
+}
+
+bool TTypeQualifierBuilder::checkSequenceIsValid(TDiagnostics *diagnostics) const
+{
+ bool areQualifierChecksRelaxed = AreTypeQualifierChecksRelaxed(mShaderVersion);
+ std::string errorMessage;
+ if (HasRepeatingQualifiers(mQualifiers, areQualifierChecksRelaxed, &errorMessage))
+ {
+ diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
+ "");
+ return false;
+ }
+
+ if (!areQualifierChecksRelaxed && !AreQualifiersInOrder(mQualifiers, &errorMessage))
+ {
+ diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
+ "");
+ return false;
+ }
+
+ return true;
+}
+
+TTypeQualifier TTypeQualifierBuilder::getParameterTypeQualifier(TDiagnostics *diagnostics) const
+{
+ ASSERT(IsInvariantCorrect(mQualifiers));
+ ASSERT(static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier() ==
+ EvqTemporary);
+
+ if (!checkSequenceIsValid(diagnostics))
+ {
+ return TTypeQualifier(EvqTemporary, mQualifiers[0]->getLine());
+ }
+
+ // If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
+ // that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
+ // combine the qualifiers.
+ if (AreTypeQualifierChecksRelaxed(mShaderVersion))
+ {
+ // Copy the qualifier sequence so that we can sort them.
+ QualifierSequence sortedQualifierSequence = mQualifiers;
+ SortSequence(sortedQualifierSequence);
+ return GetParameterTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
+ }
+ return GetParameterTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
+}
+
+TTypeQualifier TTypeQualifierBuilder::getVariableTypeQualifier(TDiagnostics *diagnostics) const
+{
+ ASSERT(IsInvariantCorrect(mQualifiers));
+
+ if (!checkSequenceIsValid(diagnostics))
+ {
+ return TTypeQualifier(
+ static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier(),
+ mQualifiers[0]->getLine());
+ }
+
+ // If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
+ // that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
+ // combine the qualifiers.
+ if (AreTypeQualifierChecksRelaxed(mShaderVersion))
+ {
+ // Copy the qualifier sequence so that we can sort them.
+ QualifierSequence sortedQualifierSequence = mQualifiers;
+ SortSequence(sortedQualifierSequence);
+ return GetVariableTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
+ }
+ return GetVariableTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/QualifierTypes.h b/gfx/angle/src/compiler/translator/QualifierTypes.h
new file mode 100644
index 000000000..10bdeed89
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/QualifierTypes.h
@@ -0,0 +1,191 @@
+//
+// Copyright (c) 2002-2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
+#define COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Types.h"
+
+namespace sh
+{
+class TDiagnostics;
+
+TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation,
+ TDiagnostics *diagnostics);
+
+enum TQualifierType
+{
+ QtInvariant,
+ QtInterpolation,
+ QtLayout,
+ QtStorage,
+ QtPrecision,
+ QtMemory
+};
+
+class TQualifierWrapperBase : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TQualifierWrapperBase(const TSourceLoc &line) : mLine(line) {}
+ virtual ~TQualifierWrapperBase(){};
+ virtual TQualifierType getType() const = 0;
+ virtual TString getQualifierString() const = 0;
+ virtual unsigned int getRank() const = 0;
+ const TSourceLoc &getLine() const { return mLine; }
+ private:
+ TSourceLoc mLine;
+};
+
+class TInvariantQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TInvariantQualifierWrapper(const TSourceLoc &line) : TQualifierWrapperBase(line) {}
+ ~TInvariantQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtInvariant; }
+ TString getQualifierString() const { return "invariant"; }
+ unsigned int getRank() const;
+};
+
+class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TInterpolationQualifierWrapper(TQualifier interpolationQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mInterpolationQualifier(interpolationQualifier)
+ {
+ }
+ ~TInterpolationQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtInterpolation; }
+ TString getQualifierString() const { return sh::getQualifierString(mInterpolationQualifier); }
+ TQualifier getQualifier() const { return mInterpolationQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mInterpolationQualifier;
+};
+
+class TLayoutQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TLayoutQualifierWrapper(TLayoutQualifier layoutQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mLayoutQualifier(layoutQualifier)
+ {
+ }
+ ~TLayoutQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtLayout; }
+ TString getQualifierString() const { return "layout"; }
+ const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TLayoutQualifier mLayoutQualifier;
+};
+
+class TStorageQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TStorageQualifierWrapper(TQualifier storageQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mStorageQualifier(storageQualifier)
+ {
+ }
+ ~TStorageQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtStorage; }
+ TString getQualifierString() const { return sh::getQualifierString(mStorageQualifier); }
+ TQualifier getQualifier() const { return mStorageQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mStorageQualifier;
+};
+
+class TPrecisionQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TPrecisionQualifierWrapper(TPrecision precisionQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mPrecisionQualifier(precisionQualifier)
+ {
+ }
+ ~TPrecisionQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtPrecision; }
+ TString getQualifierString() const { return sh::getPrecisionString(mPrecisionQualifier); }
+ TPrecision getQualifier() const { return mPrecisionQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TPrecision mPrecisionQualifier;
+};
+
+class TMemoryQualifierWrapper final : public TQualifierWrapperBase
+{
+ public:
+ TMemoryQualifierWrapper(TQualifier memoryQualifier, const TSourceLoc &line)
+ : TQualifierWrapperBase(line), mMemoryQualifier(memoryQualifier)
+ {
+ }
+ ~TMemoryQualifierWrapper() {}
+
+ TQualifierType getType() const { return QtMemory; }
+ TString getQualifierString() const { return sh::getQualifierString(mMemoryQualifier); }
+ TQualifier getQualifier() const { return mMemoryQualifier; }
+ unsigned int getRank() const;
+
+ private:
+ TQualifier mMemoryQualifier;
+};
+
+// TTypeQualifier tightly covers type_qualifier from the grammar
+struct TTypeQualifier
+{
+ // initializes all of the qualifiers and sets the scope
+ TTypeQualifier(TQualifier scope, const TSourceLoc &loc);
+
+ TLayoutQualifier layoutQualifier;
+ TMemoryQualifier memoryQualifier;
+ TPrecision precision;
+ TQualifier qualifier;
+ bool invariant;
+ TSourceLoc line;
+};
+
+// TTypeQualifierBuilder contains all of the qualifiers when type_qualifier gets parsed.
+// It is to be used to validate the qualifier sequence and build a TTypeQualifier from it.
+class TTypeQualifierBuilder : angle::NonCopyable
+{
+ public:
+ using QualifierSequence = TVector<const TQualifierWrapperBase *>;
+
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TTypeQualifierBuilder(const TStorageQualifierWrapper *scope, int shaderVersion);
+ // Adds the passed qualifier to the end of the sequence.
+ void appendQualifier(const TQualifierWrapperBase *qualifier);
+ // Checks for the order of qualification and repeating qualifiers.
+ bool checkSequenceIsValid(TDiagnostics *diagnostics) const;
+ // Goes over the qualifier sequence and parses it to form a type qualifier for a function
+ // parameter.
+ // The returned object is initialized even if the parsing fails.
+ TTypeQualifier getParameterTypeQualifier(TDiagnostics *diagnostics) const;
+ // Goes over the qualifier sequence and parses it to form a type qualifier for a variable.
+ // The returned object is initialized even if the parsing fails.
+ TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics) const;
+
+ private:
+ QualifierSequence mQualifiers;
+ int mShaderVersion;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
diff --git a/gfx/angle/src/compiler/translator/RecordConstantPrecision.cpp b/gfx/angle/src/compiler/translator/RecordConstantPrecision.cpp
new file mode 100755
index 000000000..6b8515a2c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RecordConstantPrecision.cpp
@@ -0,0 +1,167 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been
+// folded may have had precision qualifiers, which should affect the precision of the consuming operation.
+// If the folded constant union nodes are written to output as such they won't have any precision qualifiers,
+// and their effect on the precision of the consuming operation is lost.
+//
+// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists
+// the constants outside the containing expression as precision qualified named variables in case that is
+// required for correct precision propagation.
+//
+
+#include "compiler/translator/RecordConstantPrecision.h"
+
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RecordConstantPrecisionTraverser : public TIntermTraverser
+{
+ public:
+ RecordConstantPrecisionTraverser();
+
+ void visitConstantUnion(TIntermConstantUnion *node) override;
+
+ void nextIteration();
+
+ bool foundHigherPrecisionConstant() const { return mFoundHigherPrecisionConstant; }
+ protected:
+ bool operandAffectsParentOperationPrecision(TIntermTyped *operand);
+
+ bool mFoundHigherPrecisionConstant;
+};
+
+RecordConstantPrecisionTraverser::RecordConstantPrecisionTraverser()
+ : TIntermTraverser(true, false, true),
+ mFoundHigherPrecisionConstant(false)
+{
+}
+
+bool RecordConstantPrecisionTraverser::operandAffectsParentOperationPrecision(TIntermTyped *operand)
+{
+ if (getParentNode()->getAsCaseNode() || getParentNode()->getAsBlock())
+ {
+ return false;
+ }
+
+ const TIntermBinary *parentAsBinary = getParentNode()->getAsBinaryNode();
+ if (parentAsBinary != nullptr)
+ {
+ // If the constant is assigned or is used to initialize a variable, or if it's an index,
+ // its precision has no effect.
+ switch (parentAsBinary->getOp())
+ {
+ case EOpInitialize:
+ case EOpAssign:
+ case EOpIndexDirect:
+ case EOpIndexDirectStruct:
+ case EOpIndexDirectInterfaceBlock:
+ case EOpIndexIndirect:
+ return false;
+ default:
+ break;
+ }
+
+ TIntermTyped *otherOperand = parentAsBinary->getRight();
+ if (otherOperand == operand)
+ {
+ otherOperand = parentAsBinary->getLeft();
+ }
+ // If the precision of the other child is at least as high as the precision of the constant, the precision of
+ // the constant has no effect.
+ if (otherOperand->getAsConstantUnion() == nullptr && otherOperand->getPrecision() >= operand->getPrecision())
+ {
+ return false;
+ }
+ }
+
+ TIntermAggregate *parentAsAggregate = getParentNode()->getAsAggregate();
+ if (parentAsAggregate != nullptr)
+ {
+ if (!parentAsAggregate->gotPrecisionFromChildren())
+ {
+ // This can be either:
+ // * a call to an user-defined function
+ // * a call to a texture function
+ // * some other kind of aggregate
+ // In any of these cases the constant precision has no effect.
+ return false;
+ }
+ if (parentAsAggregate->isConstructor() && parentAsAggregate->getBasicType() == EbtBool)
+ {
+ return false;
+ }
+ // If the precision of operands does affect the result, but the precision of any of the other children
+ // has a precision that's at least as high as the precision of the constant, the precision of the constant
+ // has no effect.
+ TIntermSequence *parameters = parentAsAggregate->getSequence();
+ for (TIntermNode *parameter : *parameters)
+ {
+ const TIntermTyped *typedParameter = parameter->getAsTyped();
+ if (parameter != operand && typedParameter != nullptr && parameter->getAsConstantUnion() == nullptr &&
+ typedParameter->getPrecision() >= operand->getPrecision())
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void RecordConstantPrecisionTraverser::visitConstantUnion(TIntermConstantUnion *node)
+{
+ if (mFoundHigherPrecisionConstant)
+ return;
+
+ // If the constant has lowp or undefined precision, it can't increase the precision of consuming operations.
+ if (node->getPrecision() < EbpMedium)
+ return;
+
+ // It's possible the node has no effect on the precision of the consuming expression, depending on the
+ // consuming expression, and the precision of the other parameters of the expression.
+ if (!operandAffectsParentOperationPrecision(node))
+ return;
+
+ // Make the constant a precision-qualified named variable to make sure it affects the precision of the consuming
+ // expression.
+ TIntermSequence insertions;
+ insertions.push_back(createTempInitDeclaration(node, EvqConst));
+ insertStatementsInParentBlock(insertions);
+ queueReplacement(node, createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
+ mFoundHigherPrecisionConstant = true;
+}
+
+void RecordConstantPrecisionTraverser::nextIteration()
+{
+ nextTemporaryIndex();
+ mFoundHigherPrecisionConstant = false;
+}
+
+} // namespace
+
+void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ RecordConstantPrecisionTraverser traverser;
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // Iterate as necessary, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundHigherPrecisionConstant())
+ traverser.updateTree();
+ }
+ while (traverser.foundHigherPrecisionConstant());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RecordConstantPrecision.h b/gfx/angle/src/compiler/translator/RecordConstantPrecision.h
new file mode 100755
index 000000000..a62831e22
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RecordConstantPrecision.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// During parsing, all constant expressions are folded to constant union nodes. The expressions that have been
+// folded may have had precision qualifiers, which should affect the precision of the consuming operation.
+// If the folded constant union nodes are written to output as such they won't have any precision qualifiers,
+// and their effect on the precision of the consuming operation is lost.
+//
+// RecordConstantPrecision is an AST traverser that inspects the precision qualifiers of constants and hoists
+// the constants outside the containing expression as precision qualified named variables in case that is
+// required for correct precision propagation.
+//
+
+#ifndef COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
+#define COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_
diff --git a/gfx/angle/src/compiler/translator/RegenerateStructNames.cpp b/gfx/angle/src/compiler/translator/RegenerateStructNames.cpp
new file mode 100755
index 000000000..a01d79abe
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RegenerateStructNames.cpp
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "common/debug.h"
+#include "compiler/translator/RegenerateStructNames.h"
+
+namespace sh
+{
+
+void RegenerateStructNames::visitSymbol(TIntermSymbol *symbol)
+{
+ ASSERT(symbol);
+ TType *type = symbol->getTypePointer();
+ ASSERT(type);
+ TStructure *userType = type->getStruct();
+ if (!userType)
+ return;
+
+ if (mSymbolTable.findBuiltIn(userType->name(), mShaderVersion))
+ {
+ // Built-in struct, do not touch it.
+ return;
+ }
+
+ int uniqueId = userType->uniqueId();
+
+ ASSERT(mScopeDepth > 0);
+ if (mScopeDepth == 1)
+ {
+ // If a struct is defined at global scope, we don't map its name.
+ // This is because at global level, the struct might be used to
+ // declare a uniform, so the same name needs to stay the same for
+ // vertex/fragment shaders. However, our mapping uses internal ID,
+ // which will be different for the same struct in vertex/fragment
+ // shaders.
+ // This is OK because names for any structs defined in other scopes
+ // will begin with "_webgl", which is reserved. So there will be
+ // no conflicts among unmapped struct names from global scope and
+ // mapped struct names from other scopes.
+ // However, we need to keep track of these global structs, so if a
+ // variable is used in a local scope, we don't try to modify the
+ // struct name through that variable.
+ mDeclaredGlobalStructs.insert(uniqueId);
+ return;
+ }
+ if (mDeclaredGlobalStructs.count(uniqueId) > 0)
+ return;
+ // Map {name} to _webgl_struct_{uniqueId}_{name}.
+ const char kPrefix[] = "_webgl_struct_";
+ if (userType->name().find(kPrefix) == 0)
+ {
+ // The name has already been regenerated.
+ return;
+ }
+ std::string id = Str(uniqueId);
+ TString tmp = kPrefix + TString(id.c_str());
+ tmp += "_" + userType->name();
+ userType->setName(tmp);
+}
+
+bool RegenerateStructNames::visitBlock(Visit, TIntermBlock *block)
+{
+ ++mScopeDepth;
+ TIntermSequence &sequence = *(block->getSequence());
+ for (TIntermNode *node : sequence)
+ {
+ node->traverse(this);
+ }
+ --mScopeDepth;
+ return false;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RegenerateStructNames.h b/gfx/angle/src/compiler/translator/RegenerateStructNames.h
new file mode 100755
index 000000000..86c5060b3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RegenerateStructNames.h
@@ -0,0 +1,46 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
+#define COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
+
+#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/SymbolTable.h"
+
+#include <set>
+
+namespace sh
+{
+
+class RegenerateStructNames : public TIntermTraverser
+{
+ public:
+ RegenerateStructNames(const TSymbolTable &symbolTable,
+ int shaderVersion)
+ : TIntermTraverser(true, false, false),
+ mSymbolTable(symbolTable),
+ mShaderVersion(shaderVersion),
+ mScopeDepth(0) {}
+
+ protected:
+ void visitSymbol(TIntermSymbol *) override;
+ bool visitBlock(Visit, TIntermBlock *block) override;
+
+ private:
+ const TSymbolTable &mSymbolTable;
+ int mShaderVersion;
+
+ // Indicating the depth of the current scope.
+ // The global scope is 1.
+ int mScopeDepth;
+
+ // If a struct's declared globally, push its ID in this set.
+ std::set<int> mDeclaredGlobalStructs;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_
diff --git a/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
new file mode 100755
index 000000000..31914dcf3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.cpp
@@ -0,0 +1,513 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
+// replacing them with calls to functions that choose which component to return or write.
+//
+
+#include "compiler/translator/RemoveDynamicIndexing.h"
+
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+TName GetIndexFunctionName(const TType &type, bool write)
+{
+ TInfoSinkBase nameSink;
+ nameSink << "dyn_index_";
+ if (write)
+ {
+ nameSink << "write_";
+ }
+ if (type.isMatrix())
+ {
+ nameSink << "mat" << type.getCols() << "x" << type.getRows();
+ }
+ else
+ {
+ switch (type.getBasicType())
+ {
+ case EbtInt:
+ nameSink << "ivec";
+ break;
+ case EbtBool:
+ nameSink << "bvec";
+ break;
+ case EbtUInt:
+ nameSink << "uvec";
+ break;
+ case EbtFloat:
+ nameSink << "vec";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ nameSink << type.getNominalSize();
+ }
+ TString nameString = TFunction::mangleName(nameSink.c_str());
+ TName name(nameString);
+ name.setInternal(true);
+ return name;
+}
+
+TIntermSymbol *CreateBaseSymbol(const TType &type, TQualifier qualifier)
+{
+ TIntermSymbol *symbol = new TIntermSymbol(0, "base", type);
+ symbol->setInternal(true);
+ symbol->getTypePointer()->setQualifier(qualifier);
+ return symbol;
+}
+
+TIntermSymbol *CreateIndexSymbol()
+{
+ TIntermSymbol *symbol = new TIntermSymbol(0, "index", TType(EbtInt, EbpHigh));
+ symbol->setInternal(true);
+ symbol->getTypePointer()->setQualifier(EvqIn);
+ return symbol;
+}
+
+TIntermSymbol *CreateValueSymbol(const TType &type)
+{
+ TIntermSymbol *symbol = new TIntermSymbol(0, "value", type);
+ symbol->setInternal(true);
+ symbol->getTypePointer()->setQualifier(EvqIn);
+ return symbol;
+}
+
+TIntermConstantUnion *CreateIntConstantNode(int i)
+{
+ TConstantUnion *constant = new TConstantUnion();
+ constant->setIConst(i);
+ return new TIntermConstantUnion(constant, TType(EbtInt, EbpHigh));
+}
+
+TIntermBinary *CreateIndexDirectBaseSymbolNode(const TType &indexedType,
+ const TType &fieldType,
+ const int index,
+ TQualifier baseQualifier)
+{
+ TIntermSymbol *baseSymbol = CreateBaseSymbol(indexedType, baseQualifier);
+ TIntermBinary *indexNode =
+ new TIntermBinary(EOpIndexDirect, baseSymbol, TIntermTyped::CreateIndexNode(index));
+ return indexNode;
+}
+
+TIntermBinary *CreateAssignValueSymbolNode(TIntermTyped *targetNode, const TType &assignedValueType)
+{
+ return new TIntermBinary(EOpAssign, targetNode, CreateValueSymbol(assignedValueType));
+}
+
+TIntermTyped *EnsureSignedInt(TIntermTyped *node)
+{
+ if (node->getBasicType() == EbtInt)
+ return node;
+
+ TIntermAggregate *convertedNode = new TIntermAggregate(EOpConstructInt);
+ convertedNode->setType(TType(EbtInt));
+ convertedNode->getSequence()->push_back(node);
+ convertedNode->setPrecisionFromChildren();
+ return convertedNode;
+}
+
+TType GetFieldType(const TType &indexedType)
+{
+ if (indexedType.isMatrix())
+ {
+ TType fieldType = TType(indexedType.getBasicType(), indexedType.getPrecision());
+ fieldType.setPrimarySize(static_cast<unsigned char>(indexedType.getRows()));
+ return fieldType;
+ }
+ else
+ {
+ return TType(indexedType.getBasicType(), indexedType.getPrecision());
+ }
+}
+
+// Generate a read or write function for one field in a vector/matrix.
+// Out-of-range indices are clamped. This is consistent with how ANGLE handles out-of-range
+// indices in other places.
+// Note that indices can be either int or uint. We create only int versions of the functions,
+// and convert uint indices to int at the call site.
+// read function example:
+// float dyn_index_vec2(in vec2 base, in int index)
+// {
+// switch(index)
+// {
+// case (0):
+// return base[0];
+// case (1):
+// return base[1];
+// default:
+// break;
+// }
+// if (index < 0)
+// return base[0];
+// return base[1];
+// }
+// write function example:
+// void dyn_index_write_vec2(inout vec2 base, in int index, in float value)
+// {
+// switch(index)
+// {
+// case (0):
+// base[0] = value;
+// return;
+// case (1):
+// base[1] = value;
+// return;
+// default:
+// break;
+// }
+// if (index < 0)
+// {
+// base[0] = value;
+// return;
+// }
+// base[1] = value;
+// }
+// Note that else is not used in above functions to avoid the RewriteElseBlocks transformation.
+TIntermFunctionDefinition *GetIndexFunctionDefinition(TType type, bool write)
+{
+ ASSERT(!type.isArray());
+ // Conservatively use highp here, even if the indexed type is not highp. That way the code can't
+ // end up using mediump version of an indexing function for a highp value, if both mediump and
+ // highp values are being indexed in the shader. For HLSL precision doesn't matter, but in
+ // principle this code could be used with multiple backends.
+ type.setPrecision(EbpHigh);
+
+ TType fieldType = GetFieldType(type);
+ int numCases = 0;
+ if (type.isMatrix())
+ {
+ numCases = type.getCols();
+ }
+ else
+ {
+ numCases = type.getNominalSize();
+ }
+
+ TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
+ TQualifier baseQualifier = EvqInOut;
+ if (!write)
+ baseQualifier = EvqIn;
+ TIntermSymbol *baseParam = CreateBaseSymbol(type, baseQualifier);
+ paramsNode->getSequence()->push_back(baseParam);
+ TIntermSymbol *indexParam = CreateIndexSymbol();
+ paramsNode->getSequence()->push_back(indexParam);
+ if (write)
+ {
+ TIntermSymbol *valueParam = CreateValueSymbol(fieldType);
+ paramsNode->getSequence()->push_back(valueParam);
+ }
+
+ TIntermBlock *statementList = new TIntermBlock();
+ for (int i = 0; i < numCases; ++i)
+ {
+ TIntermCase *caseNode = new TIntermCase(CreateIntConstantNode(i));
+ statementList->getSequence()->push_back(caseNode);
+
+ TIntermBinary *indexNode =
+ CreateIndexDirectBaseSymbolNode(type, fieldType, i, baseQualifier);
+ if (write)
+ {
+ TIntermBinary *assignNode = CreateAssignValueSymbolNode(indexNode, fieldType);
+ statementList->getSequence()->push_back(assignNode);
+ TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
+ statementList->getSequence()->push_back(returnNode);
+ }
+ else
+ {
+ TIntermBranch *returnNode = new TIntermBranch(EOpReturn, indexNode);
+ statementList->getSequence()->push_back(returnNode);
+ }
+ }
+
+ // Default case
+ TIntermCase *defaultNode = new TIntermCase(nullptr);
+ statementList->getSequence()->push_back(defaultNode);
+ TIntermBranch *breakNode = new TIntermBranch(EOpBreak, nullptr);
+ statementList->getSequence()->push_back(breakNode);
+
+ TIntermSwitch *switchNode = new TIntermSwitch(CreateIndexSymbol(), statementList);
+
+ TIntermBlock *bodyNode = new TIntermBlock();
+ bodyNode->getSequence()->push_back(switchNode);
+
+ TIntermBinary *cond =
+ new TIntermBinary(EOpLessThan, CreateIndexSymbol(), CreateIntConstantNode(0));
+ cond->setType(TType(EbtBool, EbpUndefined));
+
+ // Two blocks: one accesses (either reads or writes) the first element and returns,
+ // the other accesses the last element.
+ TIntermBlock *useFirstBlock = new TIntermBlock();
+ TIntermBlock *useLastBlock = new TIntermBlock();
+ TIntermBinary *indexFirstNode =
+ CreateIndexDirectBaseSymbolNode(type, fieldType, 0, baseQualifier);
+ TIntermBinary *indexLastNode =
+ CreateIndexDirectBaseSymbolNode(type, fieldType, numCases - 1, baseQualifier);
+ if (write)
+ {
+ TIntermBinary *assignFirstNode = CreateAssignValueSymbolNode(indexFirstNode, fieldType);
+ useFirstBlock->getSequence()->push_back(assignFirstNode);
+ TIntermBranch *returnNode = new TIntermBranch(EOpReturn, nullptr);
+ useFirstBlock->getSequence()->push_back(returnNode);
+
+ TIntermBinary *assignLastNode = CreateAssignValueSymbolNode(indexLastNode, fieldType);
+ useLastBlock->getSequence()->push_back(assignLastNode);
+ }
+ else
+ {
+ TIntermBranch *returnFirstNode = new TIntermBranch(EOpReturn, indexFirstNode);
+ useFirstBlock->getSequence()->push_back(returnFirstNode);
+
+ TIntermBranch *returnLastNode = new TIntermBranch(EOpReturn, indexLastNode);
+ useLastBlock->getSequence()->push_back(returnLastNode);
+ }
+ TIntermIfElse *ifNode = new TIntermIfElse(cond, useFirstBlock, nullptr);
+ bodyNode->getSequence()->push_back(ifNode);
+ bodyNode->getSequence()->push_back(useLastBlock);
+
+ TIntermFunctionDefinition *indexingFunction = nullptr;
+ if (write)
+ {
+ indexingFunction = new TIntermFunctionDefinition(TType(EbtVoid), paramsNode, bodyNode);
+ }
+ else
+ {
+ indexingFunction = new TIntermFunctionDefinition(fieldType, paramsNode, bodyNode);
+ }
+ indexingFunction->getFunctionSymbolInfo()->setNameObj(GetIndexFunctionName(type, write));
+ return indexingFunction;
+}
+
+class RemoveDynamicIndexingTraverser : public TLValueTrackingTraverser
+{
+ public:
+ RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable, int shaderVersion);
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+
+ void insertHelperDefinitions(TIntermNode *root);
+
+ void nextIteration();
+
+ bool usedTreeInsertion() const { return mUsedTreeInsertion; }
+
+ protected:
+ // Sets of types that are indexed. Note that these can not store multiple variants
+ // of the same type with different precisions - only one precision gets stored.
+ std::set<TType> mIndexedVecAndMatrixTypes;
+ std::set<TType> mWrittenVecAndMatrixTypes;
+
+ bool mUsedTreeInsertion;
+
+ // When true, the traverser will remove side effects from any indexing expression.
+ // This is done so that in code like
+ // V[j++][i]++.
+ // where V is an array of vectors, j++ will only be evaluated once.
+ bool mRemoveIndexSideEffectsInSubtree;
+};
+
+RemoveDynamicIndexingTraverser::RemoveDynamicIndexingTraverser(const TSymbolTable &symbolTable,
+ int shaderVersion)
+ : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
+ mUsedTreeInsertion(false),
+ mRemoveIndexSideEffectsInSubtree(false)
+{
+}
+
+void RemoveDynamicIndexingTraverser::insertHelperDefinitions(TIntermNode *root)
+{
+ TIntermBlock *rootBlock = root->getAsBlock();
+ ASSERT(rootBlock != nullptr);
+ TIntermSequence insertions;
+ for (TType type : mIndexedVecAndMatrixTypes)
+ {
+ insertions.push_back(GetIndexFunctionDefinition(type, false));
+ }
+ for (TType type : mWrittenVecAndMatrixTypes)
+ {
+ insertions.push_back(GetIndexFunctionDefinition(type, true));
+ }
+ mInsertions.push_back(NodeInsertMultipleEntry(rootBlock, 0, insertions, TIntermSequence()));
+}
+
+// Create a call to dyn_index_*() based on an indirect indexing op node
+TIntermAggregate *CreateIndexFunctionCall(TIntermBinary *node,
+ TIntermTyped *indexedNode,
+ TIntermTyped *index)
+{
+ ASSERT(node->getOp() == EOpIndexIndirect);
+ TIntermAggregate *indexingCall = new TIntermAggregate(EOpFunctionCall);
+ indexingCall->setLine(node->getLine());
+ indexingCall->setUserDefined();
+ indexingCall->getFunctionSymbolInfo()->setNameObj(
+ GetIndexFunctionName(indexedNode->getType(), false));
+ indexingCall->getSequence()->push_back(indexedNode);
+ indexingCall->getSequence()->push_back(index);
+
+ TType fieldType = GetFieldType(indexedNode->getType());
+ indexingCall->setType(fieldType);
+ return indexingCall;
+}
+
+TIntermAggregate *CreateIndexedWriteFunctionCall(TIntermBinary *node,
+ TIntermTyped *index,
+ TIntermTyped *writtenValue)
+{
+ // Deep copy the left node so that two pointers to the same node don't end up in the tree.
+ TIntermNode *leftCopy = node->getLeft()->deepCopy();
+ ASSERT(leftCopy != nullptr && leftCopy->getAsTyped() != nullptr);
+ TIntermAggregate *indexedWriteCall =
+ CreateIndexFunctionCall(node, leftCopy->getAsTyped(), index);
+ indexedWriteCall->getFunctionSymbolInfo()->setNameObj(
+ GetIndexFunctionName(node->getLeft()->getType(), true));
+ indexedWriteCall->setType(TType(EbtVoid));
+ indexedWriteCall->getSequence()->push_back(writtenValue);
+ return indexedWriteCall;
+}
+
+bool RemoveDynamicIndexingTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mUsedTreeInsertion)
+ return false;
+
+ if (node->getOp() == EOpIndexIndirect)
+ {
+ if (mRemoveIndexSideEffectsInSubtree)
+ {
+ ASSERT(node->getRight()->hasSideEffects());
+ // In case we're just removing index side effects, convert
+ // v_expr[index_expr]
+ // to this:
+ // int s0 = index_expr; v_expr[s0];
+ // Now v_expr[s0] can be safely executed several times without unintended side effects.
+
+ // Init the temp variable holding the index
+ TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
+ insertStatementInParentBlock(initIndex);
+ mUsedTreeInsertion = true;
+
+ // Replace the index with the temp variable
+ TIntermSymbol *tempIndex = createTempSymbol(node->getRight()->getType());
+ queueReplacementWithParent(node, node->getRight(), tempIndex, OriginalNode::IS_DROPPED);
+ }
+ else if (IntermNodePatternMatcher::IsDynamicIndexingOfVectorOrMatrix(node))
+ {
+ bool write = isLValueRequiredHere();
+
+#if defined(ANGLE_ENABLE_ASSERTS)
+ // Make sure that IntermNodePatternMatcher is consistent with the slightly differently
+ // implemented checks in this traverser.
+ IntermNodePatternMatcher matcher(
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue);
+ ASSERT(matcher.match(node, getParentNode(), isLValueRequiredHere()) == write);
+#endif
+
+ TType type = node->getLeft()->getType();
+ mIndexedVecAndMatrixTypes.insert(type);
+
+ if (write)
+ {
+ // Convert:
+ // v_expr[index_expr]++;
+ // to this:
+ // int s0 = index_expr; float s1 = dyn_index(v_expr, s0); s1++;
+ // dyn_index_write(v_expr, s0, s1);
+ // This works even if index_expr has some side effects.
+ if (node->getLeft()->hasSideEffects())
+ {
+ // If v_expr has side effects, those need to be removed before proceeding.
+ // Otherwise the side effects of v_expr would be evaluated twice.
+ // The only case where an l-value can have side effects is when it is
+ // indexing. For example, it can be V[j++] where V is an array of vectors.
+ mRemoveIndexSideEffectsInSubtree = true;
+ return true;
+ }
+ // TODO(oetuaho@nvidia.com): This is not optimal if the expression using the value
+ // only writes it and doesn't need the previous value. http://anglebug.com/1116
+
+ mWrittenVecAndMatrixTypes.insert(type);
+ TType fieldType = GetFieldType(type);
+
+ TIntermSequence insertionsBefore;
+ TIntermSequence insertionsAfter;
+
+ // Store the index in a temporary signed int variable.
+ TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
+ TIntermDeclaration *initIndex = createTempInitDeclaration(indexInitializer);
+ initIndex->setLine(node->getLine());
+ insertionsBefore.push_back(initIndex);
+
+ TIntermAggregate *indexingCall = CreateIndexFunctionCall(
+ node, node->getLeft(), createTempSymbol(indexInitializer->getType()));
+
+ // Create a node for referring to the index after the nextTemporaryIndex() call
+ // below.
+ TIntermSymbol *tempIndex = createTempSymbol(indexInitializer->getType());
+
+ nextTemporaryIndex(); // From now on, creating temporary symbols that refer to the
+ // field value.
+ insertionsBefore.push_back(createTempInitDeclaration(indexingCall));
+
+ TIntermAggregate *indexedWriteCall =
+ CreateIndexedWriteFunctionCall(node, tempIndex, createTempSymbol(fieldType));
+ insertionsAfter.push_back(indexedWriteCall);
+ insertStatementsInParentBlock(insertionsBefore, insertionsAfter);
+ queueReplacement(node, createTempSymbol(fieldType), OriginalNode::IS_DROPPED);
+ mUsedTreeInsertion = true;
+ }
+ else
+ {
+ // The indexed value is not being written, so we can simply convert
+ // v_expr[index_expr]
+ // into
+ // dyn_index(v_expr, index_expr)
+ // If the index_expr is unsigned, we'll convert it to signed.
+ ASSERT(!mRemoveIndexSideEffectsInSubtree);
+ TIntermAggregate *indexingCall = CreateIndexFunctionCall(
+ node, node->getLeft(), EnsureSignedInt(node->getRight()));
+ queueReplacement(node, indexingCall, OriginalNode::IS_DROPPED);
+ }
+ }
+ }
+ return !mUsedTreeInsertion;
+}
+
+void RemoveDynamicIndexingTraverser::nextIteration()
+{
+ mUsedTreeInsertion = false;
+ mRemoveIndexSideEffectsInSubtree = false;
+ nextTemporaryIndex();
+}
+
+} // namespace
+
+void RemoveDynamicIndexing(TIntermNode *root,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ RemoveDynamicIndexingTraverser traverser(symbolTable, shaderVersion);
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ traverser.updateTree();
+ } while (traverser.usedTreeInsertion());
+ traverser.insertHelperDefinitions(root);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.h b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.h
new file mode 100755
index 000000000..06305d0f8
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveDynamicIndexing.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveDynamicIndexing is an AST traverser to remove dynamic indexing of vectors and matrices,
+// replacing them with calls to functions that choose which component to return or write.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
+#define COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
+
+namespace sh
+{
+
+class TIntermNode;
+class TSymbolTable;
+
+void RemoveDynamicIndexing(TIntermNode *root,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_
diff --git a/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp b/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp
new file mode 100644
index 000000000..f6f016310
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/RemoveInvariantDeclaration.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that removes invariant declaration for input in fragment shader
+// when GLSL >= 4.20 and for output in vertex shader when GLSL < 4.2.
+class RemoveInvariantDeclarationTraverser : public TIntermTraverser
+{
+ public:
+ RemoveInvariantDeclarationTraverser() : TIntermTraverser(true, false, false) {}
+
+ private:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override
+ {
+ if (node->getOp() == EOpInvariantDeclaration)
+ {
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
+ node, emptyReplacement));
+ return false;
+ }
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+void RemoveInvariantDeclaration(TIntermNode *root)
+{
+ RemoveInvariantDeclarationTraverser traverser;
+ root->traverse(&traverser);
+ traverser.updateTree();
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.h b/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.h
new file mode 100644
index 000000000..cf9d4aa4c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveInvariantDeclaration.h
@@ -0,0 +1,18 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
+#define COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void RemoveInvariantDeclaration(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVEINVARIANTDECLARATION_H_
diff --git a/gfx/angle/src/compiler/translator/RemovePow.cpp b/gfx/angle/src/compiler/translator/RemovePow.cpp
new file mode 100755
index 000000000..192084c36
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemovePow.cpp
@@ -0,0 +1,99 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a
+// constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series
+// OpenGL drivers.
+//
+
+#include "compiler/translator/RemovePow.h"
+
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool IsProblematicPow(TIntermTyped *node)
+{
+ TIntermAggregate *agg = node->getAsAggregate();
+ if (agg != nullptr && agg->getOp() == EOpPow)
+ {
+ ASSERT(agg->getSequence()->size() == 2);
+ return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr;
+ }
+ return false;
+}
+
+// Traverser that converts all pow operations simultaneously.
+class RemovePowTraverser : public TIntermTraverser
+{
+ public:
+ RemovePowTraverser();
+
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ void nextIteration() { mNeedAnotherIteration = false; }
+ bool needAnotherIteration() const { return mNeedAnotherIteration; }
+
+ protected:
+ bool mNeedAnotherIteration;
+};
+
+RemovePowTraverser::RemovePowTraverser()
+ : TIntermTraverser(true, false, false),
+ mNeedAnotherIteration(false)
+{
+}
+
+bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (IsProblematicPow(node))
+ {
+ TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
+ TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
+
+ TIntermUnary *log = new TIntermUnary(EOpLog2, x);
+ log->setLine(node->getLine());
+
+ TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
+ TIntermBinary *mul = new TIntermBinary(op, y, log);
+ mul->setLine(node->getLine());
+
+ TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
+ exp->setLine(node->getLine());
+
+ queueReplacement(node, exp, OriginalNode::IS_DROPPED);
+
+ // If the x parameter also needs to be replaced, we need to do that in another traversal,
+ // since it's parent node will change in a way that's not handled correctly by updateTree().
+ if (IsProblematicPow(x))
+ {
+ mNeedAnotherIteration = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+void RemovePow(TIntermNode *root)
+{
+ RemovePowTraverser traverser;
+ // Iterate as necessary, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ traverser.updateTree();
+ }
+ while (traverser.needAnotherIteration());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RemovePow.h b/gfx/angle/src/compiler/translator/RemovePow.h
new file mode 100755
index 000000000..1e2f4e116
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemovePow.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a
+// constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series
+// OpenGL drivers.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVEPOW_H_
+#define COMPILER_TRANSLATOR_REMOVEPOW_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void RemovePow(TIntermNode *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_
diff --git a/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp b/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
new file mode 100755
index 000000000..dd995af47
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.cpp
@@ -0,0 +1,174 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/RemoveSwitchFallThrough.h"
+
+namespace sh
+{
+
+TIntermBlock *RemoveSwitchFallThrough::removeFallThrough(TIntermBlock *statementList)
+{
+ RemoveSwitchFallThrough rm(statementList);
+ ASSERT(statementList);
+ statementList->traverse(&rm);
+ bool lastStatementWasBreak = rm.mLastStatementWasBreak;
+ rm.mLastStatementWasBreak = true;
+ rm.handlePreviousCase();
+ if (!lastStatementWasBreak)
+ {
+ TIntermBranch *finalBreak = new TIntermBranch(EOpBreak, nullptr);
+ rm.mStatementListOut->getSequence()->push_back(finalBreak);
+ }
+ return rm.mStatementListOut;
+}
+
+RemoveSwitchFallThrough::RemoveSwitchFallThrough(TIntermBlock *statementList)
+ : TIntermTraverser(true, false, false),
+ mStatementList(statementList),
+ mLastStatementWasBreak(false),
+ mPreviousCase(nullptr)
+{
+ mStatementListOut = new TIntermBlock();
+}
+
+void RemoveSwitchFallThrough::visitSymbol(TIntermSymbol *node)
+{
+ // Note that this assumes that switch statements which don't begin by a case statement
+ // have already been weeded out in validation.
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+}
+
+void RemoveSwitchFallThrough::visitConstantUnion(TIntermConstantUnion *node)
+{
+ // Conditions of case labels are not traversed, so this is some other constant
+ // Could be just a statement like "0;"
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+}
+
+bool RemoveSwitchFallThrough::visitBinary(Visit, TIntermBinary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitUnary(Visit, TIntermUnary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitTernary(Visit, TIntermTernary *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitIfElse(Visit, TIntermIfElse *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitSwitch(Visit, TIntermSwitch *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ // Don't go into nested switch statements
+ return false;
+}
+
+void RemoveSwitchFallThrough::outputSequence(TIntermSequence *sequence, size_t startIndex)
+{
+ for (size_t i = startIndex; i < sequence->size(); ++i)
+ {
+ mStatementListOut->getSequence()->push_back(sequence->at(i));
+ }
+}
+
+void RemoveSwitchFallThrough::handlePreviousCase()
+{
+ if (mPreviousCase)
+ mCasesSharingBreak.push_back(mPreviousCase);
+ if (mLastStatementWasBreak)
+ {
+ bool labelsWithNoStatements = true;
+ for (size_t i = 0; i < mCasesSharingBreak.size(); ++i)
+ {
+ if (mCasesSharingBreak.at(i)->getSequence()->size() > 1)
+ {
+ labelsWithNoStatements = false;
+ }
+ if (labelsWithNoStatements)
+ {
+ // Fall-through is allowed in case the label has no statements.
+ outputSequence(mCasesSharingBreak.at(i)->getSequence(), 0);
+ }
+ else
+ {
+ // Include all the statements that this case can fall through under the same label.
+ for (size_t j = i; j < mCasesSharingBreak.size(); ++j)
+ {
+ size_t startIndex = j > i ? 1 : 0; // Add the label only from the first sequence.
+ outputSequence(mCasesSharingBreak.at(j)->getSequence(), startIndex);
+
+ }
+ }
+ }
+ mCasesSharingBreak.clear();
+ }
+ mLastStatementWasBreak = false;
+ mPreviousCase = nullptr;
+}
+
+bool RemoveSwitchFallThrough::visitCase(Visit, TIntermCase *node)
+{
+ handlePreviousCase();
+ mPreviousCase = new TIntermBlock();
+ mPreviousCase->getSequence()->push_back(node);
+ // Don't traverse the condition of the case statement
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitAggregate(Visit, TIntermAggregate *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitBlock(Visit, TIntermBlock *node)
+{
+ if (node != mStatementList)
+ {
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+ }
+ return true;
+}
+
+bool RemoveSwitchFallThrough::visitLoop(Visit, TIntermLoop *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ mLastStatementWasBreak = false;
+ return false;
+}
+
+bool RemoveSwitchFallThrough::visitBranch(Visit, TIntermBranch *node)
+{
+ mPreviousCase->getSequence()->push_back(node);
+ // TODO: Verify that accepting return or continue statements here doesn't cause problems.
+ mLastStatementWasBreak = true;
+ return false;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h b/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h
new file mode 100755
index 000000000..fd8bf4fa2
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RemoveSwitchFallThrough.h
@@ -0,0 +1,50 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
+#define COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+class RemoveSwitchFallThrough : public TIntermTraverser
+{
+ public:
+ // When given a statementList from a switch AST node, return an updated
+ // statementList that has fall-through removed.
+ static TIntermBlock *removeFallThrough(TIntermBlock *statementList);
+
+ private:
+ RemoveSwitchFallThrough(TIntermBlock *statementList);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ void visitConstantUnion(TIntermConstantUnion *node) override;
+ bool visitBinary(Visit, TIntermBinary *node) override;
+ bool visitUnary(Visit, TIntermUnary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit, TIntermSwitch *node) override;
+ bool visitCase(Visit, TIntermCase *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit, TIntermBlock *node) override;
+ bool visitLoop(Visit, TIntermLoop *node) override;
+ bool visitBranch(Visit, TIntermBranch *node) override;
+
+ void outputSequence(TIntermSequence *sequence, size_t startIndex);
+ void handlePreviousCase();
+
+ TIntermBlock *mStatementList;
+ TIntermBlock *mStatementListOut;
+ bool mLastStatementWasBreak;
+ TIntermBlock *mPreviousCase;
+ std::vector<TIntermBlock *> mCasesSharingBreak;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_
diff --git a/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp b/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
new file mode 100755
index 000000000..7999cbf49
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteDoWhile.cpp
@@ -0,0 +1,159 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// RewriteDoWhile.cpp: rewrites do-while loops using another equivalent
+// construct.
+
+#include "compiler/translator/RewriteDoWhile.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// An AST traverser that rewrites loops of the form
+// do {
+// CODE;
+// } while (CONDITION)
+//
+// to loops of the form
+// bool temp = false;
+// while (true) {
+// if (temp) {
+// if (!CONDITION) {
+// break;
+// }
+// }
+// temp = true;
+// CODE;
+// }
+//
+// The reason we don't use a simpler form, with for example just (temp && !CONDITION) in the
+// while condition, is that short-circuit is often badly supported by driver shader compiler.
+// The double if has the same effect, but forces shader compilers to behave.
+//
+// TODO(cwallez) when UnfoldShortCircuitIntoIf handles loops correctly, revisit this as we might
+// be able to use while (temp || CONDITION) with temp initially set to true then run
+// UnfoldShortCircuitIntoIf
+class DoWhileRewriter : public TIntermTraverser
+{
+ public:
+ DoWhileRewriter() : TIntermTraverser(true, false, false) {}
+
+ bool visitBlock(Visit, TIntermBlock *node) override
+ {
+ // A well-formed AST can only have do-while inside TIntermBlock. By doing a prefix traversal
+ // we are able to replace the do-while in the sequence directly as the content of the
+ // do-while will be traversed later.
+
+ TIntermSequence *statements = node->getSequence();
+
+ // The statements vector will have new statements inserted when we encounter a do-while,
+ // which prevents us from using a range-based for loop. Using the usual i++ works, as
+ // the (two) new statements inserted replace the statement at the current position.
+ for (size_t i = 0; i < statements->size(); i++)
+ {
+ TIntermNode *statement = (*statements)[i];
+ TIntermLoop *loop = statement->getAsLoopNode();
+
+ if (loop == nullptr || loop->getType() != ELoopDoWhile)
+ {
+ continue;
+ }
+
+ TType boolType = TType(EbtBool);
+
+ // bool temp = false;
+ TIntermDeclaration *tempDeclaration = nullptr;
+ {
+ TConstantUnion *falseConstant = new TConstantUnion();
+ falseConstant->setBConst(false);
+ TIntermTyped *falseValue = new TIntermConstantUnion(falseConstant, boolType);
+
+ tempDeclaration = createTempInitDeclaration(falseValue);
+ }
+
+ // temp = true;
+ TIntermBinary *assignTrue = nullptr;
+ {
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
+
+ assignTrue = createTempAssignment(trueValue);
+ }
+
+ // if (temp) {
+ // if (!CONDITION) {
+ // break;
+ // }
+ // }
+ TIntermIfElse *breakIf = nullptr;
+ {
+ TIntermBranch *breakStatement = new TIntermBranch(EOpBreak, nullptr);
+
+ TIntermBlock *breakBlock = new TIntermBlock();
+ breakBlock->getSequence()->push_back(breakStatement);
+
+ TIntermUnary *negatedCondition =
+ new TIntermUnary(EOpLogicalNot, loop->getCondition());
+
+ TIntermIfElse *innerIf = new TIntermIfElse(negatedCondition, breakBlock, nullptr);
+
+ TIntermBlock *innerIfBlock = new TIntermBlock();
+ innerIfBlock->getSequence()->push_back(innerIf);
+
+ breakIf = new TIntermIfElse(createTempSymbol(boolType), innerIfBlock, nullptr);
+ }
+
+ // Assemble the replacement loops, reusing the do-while loop's body and inserting our
+ // statements at the front.
+ TIntermLoop *newLoop = nullptr;
+ {
+ TConstantUnion *trueConstant = new TConstantUnion();
+ trueConstant->setBConst(true);
+ TIntermTyped *trueValue = new TIntermConstantUnion(trueConstant, boolType);
+
+ TIntermBlock *body = loop->getBody();
+ if (body == nullptr)
+ {
+ body = new TIntermBlock();
+ }
+ auto sequence = body->getSequence();
+ sequence->insert(sequence->begin(), assignTrue);
+ sequence->insert(sequence->begin(), breakIf);
+
+ newLoop = new TIntermLoop(ELoopWhile, nullptr, trueValue, nullptr, body);
+ }
+
+ TIntermSequence replacement;
+ replacement.push_back(tempDeclaration);
+ replacement.push_back(newLoop);
+
+ node->replaceChildNodeWithMultiple(loop, replacement);
+
+ nextTemporaryIndex();
+ }
+ return true;
+ }
+};
+
+} // anonymous namespace
+
+void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ ASSERT(temporaryIndex != 0);
+
+ DoWhileRewriter rewriter;
+ rewriter.useTemporaryIndex(temporaryIndex);
+
+ root->traverse(&rewriter);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/RewriteDoWhile.h b/gfx/angle/src/compiler/translator/RewriteDoWhile.h
new file mode 100755
index 000000000..91a7958c0
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteDoWhile.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// RewriteDoWhile.h: rewrite do-while loops as while loops to work around
+// driver bugs
+
+#ifndef COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
+#define COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
+
+namespace sh
+{
+class TIntermNode;
+void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITEDOWHILE_H_
diff --git a/gfx/angle/src/compiler/translator/RewriteElseBlocks.cpp b/gfx/angle/src/compiler/translator/RewriteElseBlocks.cpp
new file mode 100755
index 000000000..937de11cd
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteElseBlocks.cpp
@@ -0,0 +1,123 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteElseBlocks.cpp: Implementation for tree transform to change
+// all if-else blocks to if-if blocks.
+//
+
+#include "compiler/translator/RewriteElseBlocks.h"
+
+#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/NodeSearch.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class ElseBlockRewriter : public TIntermTraverser
+{
+ public:
+ ElseBlockRewriter();
+
+ protected:
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *aggregate) override;
+ bool visitBlock(Visit visit, TIntermBlock *block) override;
+
+ private:
+ const TType *mFunctionType;
+
+ TIntermNode *rewriteIfElse(TIntermIfElse *ifElse);
+};
+
+ElseBlockRewriter::ElseBlockRewriter()
+ : TIntermTraverser(true, false, true),
+ mFunctionType(NULL)
+{}
+
+bool ElseBlockRewriter::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ // Store the current function context (see comment below)
+ mFunctionType = ((visit == PreVisit) ? &node->getType() : nullptr);
+ return true;
+}
+
+bool ElseBlockRewriter::visitBlock(Visit visit, TIntermBlock *node)
+{
+ if (visit == PostVisit)
+ {
+ for (size_t statementIndex = 0; statementIndex != node->getSequence()->size();
+ statementIndex++)
+ {
+ TIntermNode *statement = (*node->getSequence())[statementIndex];
+ TIntermIfElse *ifElse = statement->getAsIfElseNode();
+ if (ifElse && ifElse->getFalseBlock() != nullptr)
+ {
+ (*node->getSequence())[statementIndex] = rewriteIfElse(ifElse);
+ }
+ }
+ }
+ return true;
+}
+
+TIntermNode *ElseBlockRewriter::rewriteIfElse(TIntermIfElse *ifElse)
+{
+ ASSERT(ifElse != nullptr);
+
+ nextTemporaryIndex();
+
+ TIntermDeclaration *storeCondition = createTempInitDeclaration(ifElse->getCondition());
+
+ TIntermBlock *falseBlock = nullptr;
+
+ TType boolType(EbtBool, EbpUndefined, EvqTemporary);
+
+ if (ifElse->getFalseBlock())
+ {
+ TIntermBlock *negatedElse = nullptr;
+ // crbug.com/346463
+ // D3D generates error messages claiming a function has no return value, when rewriting
+ // an if-else clause that returns something non-void in a function. By appending dummy
+ // returns (that are unreachable) we can silence this compile error.
+ if (mFunctionType && mFunctionType->getBasicType() != EbtVoid)
+ {
+ TString typeString = mFunctionType->getStruct() ? mFunctionType->getStruct()->name() :
+ mFunctionType->getBasicString();
+ TString rawText = "return (" + typeString + ")0";
+ TIntermRaw *returnNode = new TIntermRaw(*mFunctionType, rawText);
+ negatedElse = new TIntermBlock();
+ negatedElse->getSequence()->push_back(returnNode);
+ }
+
+ TIntermSymbol *conditionSymbolElse = createTempSymbol(boolType);
+ TIntermUnary *negatedCondition = new TIntermUnary(EOpLogicalNot, conditionSymbolElse);
+ TIntermIfElse *falseIfElse =
+ new TIntermIfElse(negatedCondition, ifElse->getFalseBlock(), negatedElse);
+ falseBlock = TIntermediate::EnsureBlock(falseIfElse);
+ }
+
+ TIntermSymbol *conditionSymbolSel = createTempSymbol(boolType);
+ TIntermIfElse *newIfElse =
+ new TIntermIfElse(conditionSymbolSel, ifElse->getTrueBlock(), falseBlock);
+
+ TIntermBlock *block = new TIntermBlock();
+ block->getSequence()->push_back(storeCondition);
+ block->getSequence()->push_back(newIfElse);
+
+ return block;
+}
+
+}
+
+void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex)
+{
+ ElseBlockRewriter rewriter;
+ rewriter.useTemporaryIndex(temporaryIndex);
+ node->traverse(&rewriter);
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/RewriteElseBlocks.h b/gfx/angle/src/compiler/translator/RewriteElseBlocks.h
new file mode 100755
index 000000000..24a425e66
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteElseBlocks.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RewriteElseBlocks.h: Prototype for tree transform to change
+// all if-else blocks to if-if blocks.
+//
+
+#ifndef COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
+#define COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+void RewriteElseBlocks(TIntermNode *node, unsigned int *temporaryIndex);
+
+}
+
+#endif // COMPILER_TRANSLATOR_REWRITEELSEBLOCKS_H_
diff --git a/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp b/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
new file mode 100755
index 000000000..487c90991
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.cpp
@@ -0,0 +1,170 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of texelFetchOffset translation issue workaround.
+// See header for more info.
+
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+
+#include "common/angleutils.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+ private:
+ Traverser(const TSymbolTable &symbolTable, int shaderVersion);
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ void nextIteration();
+
+ const TSymbolTable *symbolTable;
+ const int shaderVersion;
+ bool mFound = false;
+};
+
+Traverser::Traverser(const TSymbolTable &symbolTable, int shaderVersion)
+ : TIntermTraverser(true, false, false), symbolTable(&symbolTable), shaderVersion(shaderVersion)
+{
+}
+
+// static
+void Traverser::Apply(TIntermNode *root,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ Traverser traverser(symbolTable, shaderVersion);
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Decide if the node represents the call of texelFetchOffset.
+ if (node->getOp() != EOpFunctionCall || node->isUserDefined())
+ {
+ return true;
+ }
+
+ if (node->getFunctionSymbolInfo()->getName().compare(0, 16, "texelFetchOffset") != 0)
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround.
+ const TIntermSequence *sequence = node->getSequence();
+ ASSERT(sequence->size() == 4u);
+
+ // Decide if there is a 2DArray sampler.
+ bool is2DArray = node->getFunctionSymbolInfo()->getName().find("s2a1") != TString::npos;
+
+ // Create new argument list from node->getName().
+ // e.g. Get "(is2a1;vi3;i1;" from "texelFetchOffset(is2a1;vi3;i1;vi2;"
+ TString newArgs = node->getFunctionSymbolInfo()->getName().substr(
+ 16, node->getFunctionSymbolInfo()->getName().length() - 20);
+ TString newName = "texelFetch" + newArgs;
+ TSymbol *texelFetchSymbol = symbolTable->findBuiltIn(newName, shaderVersion);
+ ASSERT(texelFetchSymbol);
+ int uniqueId = texelFetchSymbol->getUniqueId();
+
+ // Create new node that represents the call of function texelFetch.
+ // Its argument list will be: texelFetch(sampler, Position+offset, lod).
+ TIntermAggregate *texelFetchNode = new TIntermAggregate(EOpFunctionCall);
+ texelFetchNode->getFunctionSymbolInfo()->setName(newName);
+ texelFetchNode->getFunctionSymbolInfo()->setId(uniqueId);
+ texelFetchNode->setType(node->getType());
+ texelFetchNode->setLine(node->getLine());
+
+ // sampler
+ texelFetchNode->getSequence()->push_back(sequence->at(0));
+
+ // Position
+ TIntermTyped *texCoordNode = sequence->at(1)->getAsTyped();
+ ASSERT(texCoordNode);
+
+ // offset
+ TIntermTyped *offsetNode = nullptr;
+ ASSERT(sequence->at(3)->getAsTyped());
+ if (is2DArray)
+ {
+ // For 2DArray samplers, Position is ivec3 and offset is ivec2;
+ // So offset must be converted into an ivec3 before being added to Position.
+ TIntermAggregate *constructIVec3Node = new TIntermAggregate(EOpConstructIVec3);
+ constructIVec3Node->setLine(texCoordNode->getLine());
+ constructIVec3Node->setType(texCoordNode->getType());
+
+ constructIVec3Node->getSequence()->push_back(sequence->at(3)->getAsTyped());
+
+ TConstantUnion *zero = new TConstantUnion();
+ zero->setIConst(0);
+ TType *intType = new TType(EbtInt);
+
+ TIntermConstantUnion *zeroNode = new TIntermConstantUnion(zero, *intType);
+ constructIVec3Node->getSequence()->push_back(zeroNode);
+
+ offsetNode = constructIVec3Node;
+ }
+ else
+ {
+ offsetNode = sequence->at(3)->getAsTyped();
+ }
+
+ // Position+offset
+ TIntermBinary *add = new TIntermBinary(EOpAdd, texCoordNode, offsetNode);
+ add->setLine(texCoordNode->getLine());
+ texelFetchNode->getSequence()->push_back(add);
+
+ // lod
+ texelFetchNode->getSequence()->push_back(sequence->at(2));
+
+ ASSERT(texelFetchNode->getSequence()->size() == 3u);
+
+ // Replace the old node by this new node.
+ queueReplacement(node, texelFetchNode, OriginalNode::IS_DROPPED);
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void RewriteTexelFetchOffset(TIntermNode *root,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ // texelFetchOffset is only valid in GLSL 3.0 and later.
+ if (shaderVersion < 300)
+ return;
+
+ Traverser::Apply(root, symbolTable, shaderVersion);
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.h b/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.h
new file mode 100755
index 000000000..4218f0b69
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteTexelFetchOffset.h
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around an issue on the translation
+// from texelFetchOffset into HLSL function Load on INTEL drivers. It
+// works by translating texelFetchOffset into texelFetch:
+//
+// - From: texelFetchOffset(sampler, Position, lod, offset)
+// - To: texelFetch(sampler, Position+offset, lod)
+//
+// See http://anglebug.com/1469
+
+#ifndef COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_
+#define COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_
+
+class TIntermNode;
+class TSymbolTable;
+
+namespace sh
+{
+
+void RewriteTexelFetchOffset(TIntermNode *root,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITE_TEXELFETCHOFFSET_H_ \ No newline at end of file
diff --git a/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp b/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp
new file mode 100644
index 000000000..ef708cb2e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp
@@ -0,0 +1,112 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Implementation of evaluating unary integer variable bug workaround.
+// See header for more info.
+
+#include "compiler/translator/RewriteUnaryMinusOperatorInt.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class Traverser : public TIntermTraverser
+{
+ public:
+ static void Apply(TIntermNode *root);
+
+ private:
+ Traverser();
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+ void nextIteration();
+
+ bool mFound = false;
+};
+
+// static
+void Traverser::Apply(TIntermNode *root)
+{
+ Traverser traverser;
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.mFound)
+ {
+ traverser.updateTree();
+ }
+ } while (traverser.mFound);
+}
+
+Traverser::Traverser() : TIntermTraverser(true, false, false)
+{
+}
+
+void Traverser::nextIteration()
+{
+ mFound = false;
+}
+
+bool Traverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (mFound)
+ {
+ return false;
+ }
+
+ // Decide if the current unary operator is unary minus.
+ if (node->getOp() != EOpNegative)
+ {
+ return true;
+ }
+
+ // Decide if the current operand is an integer variable.
+ TIntermTyped *opr = node->getOperand();
+ if (!opr->getType().isScalarInt())
+ {
+ return true;
+ }
+
+ // Potential problem case detected, apply workaround: -(int) -> ~(int) + 1.
+ // ~(int)
+ TIntermUnary *bitwiseNot = new TIntermUnary(EOpBitwiseNot, opr);
+ bitwiseNot->setLine(opr->getLine());
+
+ // Constant 1 (or 1u)
+ TConstantUnion *one = new TConstantUnion();
+ if (opr->getType().getBasicType() == EbtInt)
+ {
+ one->setIConst(1);
+ }
+ else
+ {
+ one->setUConst(1u);
+ }
+ TIntermConstantUnion *oneNode = new TIntermConstantUnion(one, opr->getType());
+ oneNode->getTypePointer()->setQualifier(EvqConst);
+ oneNode->setLine(opr->getLine());
+
+ // ~(int) + 1
+ TIntermBinary *add = new TIntermBinary(EOpAdd, bitwiseNot, oneNode);
+ add->setLine(opr->getLine());
+
+ queueReplacement(node, add, OriginalNode::IS_DROPPED);
+
+ mFound = true;
+ return false;
+}
+
+} // anonymous namespace
+
+void RewriteUnaryMinusOperatorInt(TIntermNode *root)
+{
+ Traverser::Apply(root);
+}
+
+} // namespace sh \ No newline at end of file
diff --git a/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h b/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h
new file mode 100644
index 000000000..50f0c442a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/RewriteUnaryMinusOperatorInt.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This mutating tree traversal works around a bug on evaluating unary
+// integer variable on Intel D3D driver. It works by rewriting -(int) to
+// ~(int) + 1 when evaluating unary integer variables.
+
+#ifndef COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_
+#define COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_
+
+class TIntermNode;
+namespace sh
+{
+
+void RewriteUnaryMinusOperatorInt(TIntermNode *root);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REWRITEUNARYMINUSOPERATORINT_H_ \ No newline at end of file
diff --git a/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
new file mode 100755
index 000000000..5afa0d308
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
@@ -0,0 +1,306 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Scalarize vector and matrix constructor args, so that vectors built from components don't have
+// matrix arguments, and matrices built from components don't have vector arguments. This avoids
+// driver bugs around vector and matrix constructors.
+//
+
+#include "common/debug.h"
+#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
+
+#include <algorithm>
+
+#include "angle_gl.h"
+#include "common/angleutils.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool ContainsMatrixNode(const TIntermSequence &sequence)
+{
+ for (size_t ii = 0; ii < sequence.size(); ++ii)
+ {
+ TIntermTyped *node = sequence[ii]->getAsTyped();
+ if (node && node->isMatrix())
+ return true;
+ }
+ return false;
+}
+
+bool ContainsVectorNode(const TIntermSequence &sequence)
+{
+ for (size_t ii = 0; ii < sequence.size(); ++ii)
+ {
+ TIntermTyped *node = sequence[ii]->getAsTyped();
+ if (node && node->isVector())
+ return true;
+ }
+ return false;
+}
+
+TIntermBinary *ConstructVectorIndexBinaryNode(TIntermSymbol *symbolNode, int index)
+{
+ return new TIntermBinary(EOpIndexDirect, symbolNode, TIntermTyped::CreateIndexNode(index));
+}
+
+TIntermBinary *ConstructMatrixIndexBinaryNode(
+ TIntermSymbol *symbolNode, int colIndex, int rowIndex)
+{
+ TIntermBinary *colVectorNode =
+ ConstructVectorIndexBinaryNode(symbolNode, colIndex);
+
+ return new TIntermBinary(EOpIndexDirect, colVectorNode,
+ TIntermTyped::CreateIndexNode(rowIndex));
+}
+
+class ScalarizeArgsTraverser : public TIntermTraverser
+{
+ public:
+ ScalarizeArgsTraverser(sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ unsigned int *temporaryIndex)
+ : TIntermTraverser(true, false, false),
+ mShaderType(shaderType),
+ mFragmentPrecisionHigh(fragmentPrecisionHigh)
+ {
+ useTemporaryIndex(temporaryIndex);
+ }
+
+ protected:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBlock(Visit visit, TIntermBlock *node) override;
+
+ private:
+ void scalarizeArgs(TIntermAggregate *aggregate, bool scalarizeVector, bool scalarizeMatrix);
+
+ // If we have the following code:
+ // mat4 m(0);
+ // vec4 v(1, m);
+ // We will rewrite to:
+ // mat4 m(0);
+ // mat4 s0 = m;
+ // vec4 v(1, s0[0][0], s0[0][1], s0[0][2]);
+ // This function is to create nodes for "mat4 s0 = m;" and insert it to the code sequence. This
+ // way the possible side effects of the constructor argument will only be evaluated once.
+ void createTempVariable(TIntermTyped *original);
+
+ std::vector<TIntermSequence> mBlockStack;
+
+ sh::GLenum mShaderType;
+ bool mFragmentPrecisionHigh;
+};
+
+bool ScalarizeArgsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (visit == PreVisit)
+ {
+ switch (node->getOp())
+ {
+ case EOpConstructVec2:
+ case EOpConstructVec3:
+ case EOpConstructVec4:
+ case EOpConstructBVec2:
+ case EOpConstructBVec3:
+ case EOpConstructBVec4:
+ case EOpConstructIVec2:
+ case EOpConstructIVec3:
+ case EOpConstructIVec4:
+ if (ContainsMatrixNode(*(node->getSequence())))
+ scalarizeArgs(node, false, true);
+ break;
+ case EOpConstructMat2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4:
+ if (ContainsVectorNode(*(node->getSequence())))
+ scalarizeArgs(node, true, false);
+ break;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+bool ScalarizeArgsTraverser::visitBlock(Visit visit, TIntermBlock *node)
+{
+ mBlockStack.push_back(TIntermSequence());
+ {
+ for (TIntermNode *child : *node->getSequence())
+ {
+ ASSERT(child != nullptr);
+ child->traverse(this);
+ mBlockStack.back().push_back(child);
+ }
+ }
+ if (mBlockStack.back().size() > node->getSequence()->size())
+ {
+ node->getSequence()->clear();
+ *(node->getSequence()) = mBlockStack.back();
+ }
+ mBlockStack.pop_back();
+ return false;
+}
+
+void ScalarizeArgsTraverser::scalarizeArgs(TIntermAggregate *aggregate,
+ bool scalarizeVector,
+ bool scalarizeMatrix)
+{
+ ASSERT(aggregate);
+ int size = 0;
+ switch (aggregate->getOp())
+ {
+ case EOpConstructVec2:
+ case EOpConstructBVec2:
+ case EOpConstructIVec2:
+ size = 2;
+ break;
+ case EOpConstructVec3:
+ case EOpConstructBVec3:
+ case EOpConstructIVec3:
+ size = 3;
+ break;
+ case EOpConstructVec4:
+ case EOpConstructBVec4:
+ case EOpConstructIVec4:
+ case EOpConstructMat2:
+ size = 4;
+ break;
+ case EOpConstructMat2x3:
+ case EOpConstructMat3x2:
+ size = 6;
+ break;
+ case EOpConstructMat2x4:
+ case EOpConstructMat4x2:
+ size = 8;
+ break;
+ case EOpConstructMat3:
+ size = 9;
+ break;
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x3:
+ size = 12;
+ break;
+ case EOpConstructMat4:
+ size = 16;
+ break;
+ default:
+ break;
+ }
+ TIntermSequence *sequence = aggregate->getSequence();
+ TIntermSequence original(*sequence);
+ sequence->clear();
+ for (size_t ii = 0; ii < original.size(); ++ii)
+ {
+ ASSERT(size > 0);
+ TIntermTyped *node = original[ii]->getAsTyped();
+ ASSERT(node);
+ createTempVariable(node);
+ if (node->isScalar())
+ {
+ sequence->push_back(createTempSymbol(node->getType()));
+ size--;
+ }
+ else if (node->isVector())
+ {
+ if (scalarizeVector)
+ {
+ int repeat = std::min(size, node->getNominalSize());
+ size -= repeat;
+ for (int index = 0; index < repeat; ++index)
+ {
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ TIntermBinary *newNode = ConstructVectorIndexBinaryNode(
+ symbolNode, index);
+ sequence->push_back(newNode);
+ }
+ }
+ else
+ {
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ sequence->push_back(symbolNode);
+ size -= node->getNominalSize();
+ }
+ }
+ else
+ {
+ ASSERT(node->isMatrix());
+ if (scalarizeMatrix)
+ {
+ int colIndex = 0, rowIndex = 0;
+ int repeat = std::min(size, node->getCols() * node->getRows());
+ size -= repeat;
+ while (repeat > 0)
+ {
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ TIntermBinary *newNode = ConstructMatrixIndexBinaryNode(
+ symbolNode, colIndex, rowIndex);
+ sequence->push_back(newNode);
+ rowIndex++;
+ if (rowIndex >= node->getRows())
+ {
+ rowIndex = 0;
+ colIndex++;
+ }
+ repeat--;
+ }
+ }
+ else
+ {
+ TIntermSymbol *symbolNode = createTempSymbol(node->getType());
+ sequence->push_back(symbolNode);
+ size -= node->getCols() * node->getRows();
+ }
+ }
+ }
+}
+
+void ScalarizeArgsTraverser::createTempVariable(TIntermTyped *original)
+{
+ ASSERT(original);
+ nextTemporaryIndex();
+ TIntermDeclaration *decl = createTempInitDeclaration(original);
+
+ TType type = original->getType();
+ if (mShaderType == GL_FRAGMENT_SHADER &&
+ type.getBasicType() == EbtFloat &&
+ type.getPrecision() == EbpUndefined)
+ {
+ // We use the highest available precision for the temporary variable
+ // to avoid computing the actual precision using the rules defined
+ // in GLSL ES 1.0 Section 4.5.2.
+ TIntermBinary *init = decl->getSequence()->at(0)->getAsBinaryNode();
+ init->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium);
+ init->getLeft()->getTypePointer()->setPrecision(mFragmentPrecisionHigh ? EbpHigh
+ : EbpMedium);
+ }
+
+ ASSERT(mBlockStack.size() > 0);
+ TIntermSequence &sequence = mBlockStack.back();
+ sequence.push_back(decl);
+}
+
+} // namespace anonymous
+
+void ScalarizeVecAndMatConstructorArgs(TIntermBlock *root,
+ sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ unsigned int *temporaryIndex)
+{
+ ScalarizeArgsTraverser scalarizer(shaderType, fragmentPrecisionHigh, temporaryIndex);
+ root->traverse(&scalarizer);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h b/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h
new file mode 100755
index 000000000..14bbbe13e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Scalarize vector and matrix constructor args, so that vectors built from components don't have
+// matrix arguments, and matrices built from components don't have vector arguments. This avoids
+// driver bugs around vector and matrix constructors.
+//
+
+#ifndef COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
+#define COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+class TIntermBlock;
+
+void ScalarizeVecAndMatConstructorArgs(TIntermBlock *root,
+ sh::GLenum shaderType,
+ bool fragmentPrecisionHigh,
+ unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_
diff --git a/gfx/angle/src/compiler/translator/SearchSymbol.cpp b/gfx/angle/src/compiler/translator/SearchSymbol.cpp
new file mode 100755
index 000000000..cccd4d3ff
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SearchSymbol.cpp
@@ -0,0 +1,39 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SearchSymbol is an AST traverser to detect the use of a given symbol name
+//
+
+#include "compiler/translator/SearchSymbol.h"
+
+#include "compiler/translator/InfoSink.h"
+
+namespace sh
+{
+SearchSymbol::SearchSymbol(const TString &symbol)
+ : TIntermTraverser(true, false, false),
+ mSymbol(symbol)
+{
+ match = false;
+}
+
+void SearchSymbol::traverse(TIntermNode *node)
+{
+ node->traverse(this);
+}
+
+void SearchSymbol::visitSymbol(TIntermSymbol *symbolNode)
+{
+ if (symbolNode->getSymbol() == mSymbol)
+ {
+ match = true;
+ }
+}
+
+bool SearchSymbol::foundMatch() const
+{
+ return match;
+}
+}
diff --git a/gfx/angle/src/compiler/translator/SearchSymbol.h b/gfx/angle/src/compiler/translator/SearchSymbol.h
new file mode 100755
index 000000000..1e5e1700d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SearchSymbol.h
@@ -0,0 +1,33 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SearchSymbol is an AST traverser to detect the use of a given symbol name
+//
+
+#ifndef COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
+#define COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
+class SearchSymbol : public TIntermTraverser
+{
+ public:
+ SearchSymbol(const TString &symbol);
+
+ void traverse(TIntermNode *node);
+ void visitSymbol(TIntermSymbol *symbolNode) override;
+
+ bool foundMatch() const;
+
+ protected:
+ const TString &mSymbol;
+ bool match;
+};
+}
+
+#endif // COMPILER_TRANSLATOR_SEARCHSYMBOL_H_
diff --git a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp
new file mode 100755
index 000000000..98e010a56
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayInitialization function splits each array initialization into a declaration and an assignment.
+// Example:
+// type[n] a = initializer;
+// will effectively become
+// type[n] a;
+// a = initializer;
+//
+// Note that if the array is declared as const, the initialization may still be split, making the
+// AST technically invalid. Because of that this transformation should only be used when subsequent
+// stages don't care about const qualifiers. However, the initialization will not be split if the
+// initializer can be written as a HLSL literal.
+
+#include "compiler/translator/SeparateArrayInitialization.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/OutputHLSL.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SeparateArrayInitTraverser : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermNode *root);
+ private:
+ SeparateArrayInitTraverser();
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+};
+
+void SeparateArrayInitTraverser::apply(TIntermNode *root)
+{
+ SeparateArrayInitTraverser separateInit;
+ root->traverse(&separateInit);
+ separateInit.updateTree();
+}
+
+SeparateArrayInitTraverser::SeparateArrayInitTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool SeparateArrayInitTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ TIntermBinary *initNode = sequence->back()->getAsBinaryNode();
+ if (initNode != nullptr && initNode->getOp() == EOpInitialize)
+ {
+ TIntermTyped *initializer = initNode->getRight();
+ if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
+ {
+ // We rely on that array declarations have been isolated to single declarations.
+ ASSERT(sequence->size() == 1);
+ TIntermTyped *symbol = initNode->getLeft();
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ ASSERT(parentBlock != nullptr);
+
+ TIntermSequence replacements;
+
+ TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
+ replacementDeclaration->appendDeclarator(symbol);
+ replacementDeclaration->setLine(symbol->getLine());
+ replacements.push_back(replacementDeclaration);
+
+ TIntermBinary *replacementAssignment =
+ new TIntermBinary(EOpAssign, symbol, initializer);
+ replacementAssignment->setLine(symbol->getLine());
+ replacements.push_back(replacementAssignment);
+
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void SeparateArrayInitialization(TIntermNode *root)
+{
+ SeparateArrayInitTraverser::apply(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h
new file mode 100755
index 000000000..038d38a61
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateArrayInitialization.h
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateArrayInitialization function splits each array initialization into a declaration and an assignment.
+// Example:
+// type[n] a = initializer;
+// will effectively become
+// type[n] a;
+// a = initializer;
+//
+// Note that if the array is declared as const, the initialization may still be split, making the
+// AST technically invalid. Because of that this transformation should only be used when subsequent
+// stages don't care about const qualifiers. However, the initialization will not be split if the
+// initializer can be written as a HLSL literal.
+
+#ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
+#define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void SeparateArrayInitialization(TIntermNode *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
diff --git a/gfx/angle/src/compiler/translator/SeparateDeclarations.cpp b/gfx/angle/src/compiler/translator/SeparateDeclarations.cpp
new file mode 100755
index 000000000..4d3835370
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateDeclarations.cpp
@@ -0,0 +1,78 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateDeclarations function processes declarations, so that in the end each declaration
+// contains only one declarator.
+// This is useful as an intermediate step when initialization needs to be separated from declaration,
+// or when things need to be unfolded out of the initializer.
+// Example:
+// int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+// int a[1] = int[1](1);
+// int b[1] = int[1](2);
+
+#include "compiler/translator/SeparateDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SeparateDeclarationsTraverser : private TIntermTraverser
+{
+ public:
+ static void apply(TIntermNode *root);
+ private:
+ SeparateDeclarationsTraverser();
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+};
+
+void SeparateDeclarationsTraverser::apply(TIntermNode *root)
+{
+ SeparateDeclarationsTraverser separateDecl;
+ root->traverse(&separateDecl);
+ separateDecl.updateTree();
+}
+
+SeparateDeclarationsTraverser::SeparateDeclarationsTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool SeparateDeclarationsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ TIntermSequence *sequence = node->getSequence();
+ if (sequence->size() > 1)
+ {
+ TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+ ASSERT(parentBlock != nullptr);
+
+ TIntermSequence replacementDeclarations;
+ for (size_t ii = 0; ii < sequence->size(); ++ii)
+ {
+ TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
+
+ replacementDeclaration->appendDeclarator(sequence->at(ii)->getAsTyped());
+ replacementDeclaration->setLine(sequence->at(ii)->getLine());
+ replacementDeclarations.push_back(replacementDeclaration);
+ }
+
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentBlock, node, replacementDeclarations));
+ }
+ return false;
+}
+
+} // namespace
+
+void SeparateDeclarations(TIntermNode *root)
+{
+ SeparateDeclarationsTraverser::apply(root);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SeparateDeclarations.h b/gfx/angle/src/compiler/translator/SeparateDeclarations.h
new file mode 100755
index 000000000..2c2611a49
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateDeclarations.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// The SeparateDeclarations function processes declarations, so that in the end each declaration
+// contains only one declarator.
+// This is useful as an intermediate step when initialization needs to be separated from declaration,
+// or when things need to be unfolded out of the initializer.
+// Example:
+// int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+// int a[1] = int[1](1);
+// int b[1] = int[1](2);
+
+#ifndef COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+#define COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void SeparateDeclarations(TIntermNode *root);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_
diff --git a/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp b/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
new file mode 100755
index 000000000..0f7c404d3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.cpp
@@ -0,0 +1,144 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex
+// expressions, assigning them to a temporary variable a#.
+// Examples where a, b and c are all arrays:
+// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
+// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
+
+#include "compiler/translator/SeparateExpressionsReturningArrays.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverser that separates one array expression into a statement at a time.
+class SeparateExpressionsTraverser : public TIntermTraverser
+{
+ public:
+ SeparateExpressionsTraverser();
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ void nextIteration();
+ bool foundArrayExpression() const { return mFoundArrayExpression; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of the expression has been found.
+ // After that, no more AST updates are performed on that traversal.
+ bool mFoundArrayExpression;
+
+ IntermNodePatternMatcher mPatternToSeparateMatcher;
+};
+
+SeparateExpressionsTraverser::SeparateExpressionsTraverser()
+ : TIntermTraverser(true, false, false),
+ mFoundArrayExpression(false),
+ mPatternToSeparateMatcher(IntermNodePatternMatcher::kExpressionReturningArray)
+{
+}
+
+// Performs a shallow copy of an assignment node.
+// These shallow copies are useful when a node gets inserted into an aggregate node
+// and also needs to be replaced in its original location by a different node.
+TIntermBinary *CopyAssignmentNode(TIntermBinary *node)
+{
+ return new TIntermBinary(node->getOp(), node->getLeft(), node->getRight());
+}
+
+// Performs a shallow copy of a constructor/function call node.
+TIntermAggregate *CopyAggregateNode(TIntermAggregate *node)
+{
+ TIntermAggregate *copyNode = new TIntermAggregate(node->getOp());
+ TIntermSequence *copySeq = copyNode->getSequence();
+ copySeq->insert(copySeq->begin(), node->getSequence()->begin(), node->getSequence()->end());
+ copyNode->setType(node->getType());
+ *copyNode->getFunctionSymbolInfo() = *node->getFunctionSymbolInfo();
+ if (node->isUserDefined())
+ {
+ copyNode->setUserDefined();
+ }
+ return copyNode;
+}
+
+bool SeparateExpressionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mFoundArrayExpression)
+ return false;
+
+ // Return if the expression is not an array or if we're not inside a complex expression.
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
+ return true;
+
+ ASSERT(node->getOp() == EOpAssign);
+
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(CopyAssignmentNode(node));
+ // TODO(oetuaho): In some cases it would be more optimal to not add the temporary node, but just
+ // use the original target of the assignment. Care must be taken so that this doesn't happen
+ // when the same array symbol is a target of assignment more than once in one expression.
+ insertions.push_back(createTempInitDeclaration(node->getLeft()));
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(node, createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+bool SeparateExpressionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundArrayExpression)
+ return false; // No need to traverse further
+
+ if (!mPatternToSeparateMatcher.match(node, getParentNode()))
+ return true;
+
+ ASSERT(node->isConstructor() || node->getOp() == EOpFunctionCall);
+
+ mFoundArrayExpression = true;
+
+ TIntermSequence insertions;
+ insertions.push_back(createTempInitDeclaration(CopyAggregateNode(node)));
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(node, createTempSymbol(node->getType()), OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+void SeparateExpressionsTraverser::nextIteration()
+{
+ mFoundArrayExpression = false;
+ nextTemporaryIndex();
+}
+
+} // namespace
+
+void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ SeparateExpressionsTraverser traverser;
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // Separate one expression at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundArrayExpression())
+ traverser.updateTree();
+ }
+ while (traverser.foundArrayExpression());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h b/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h
new file mode 100755
index 000000000..d0c73dc18
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SeparateExpressionsReturningArrays.h
@@ -0,0 +1,22 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SeparateExpressionsReturningArrays splits array-returning expressions that are not array names from more complex
+// expressions, assigning them to a temporary variable a#.
+// Examples where a, b and c are all arrays:
+// (a = b) == (a = c) is split into a = b; type[n] a1 = a; a = c; type[n] a2 = a; a1 == a2;
+// type d = type[n](...)[i]; is split into type[n] a1 = type[n](...); type d = a1[i];
+
+#ifndef COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+#define COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_
diff --git a/gfx/angle/src/compiler/translator/ShaderLang.cpp b/gfx/angle/src/compiler/translator/ShaderLang.cpp
new file mode 100755
index 000000000..b776ca50b
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ShaderLang.cpp
@@ -0,0 +1,533 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Implement the top-level of interface to the compiler,
+// as defined in ShaderLang.h
+//
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/translator/Compiler.h"
+#include "compiler/translator/InitializeDll.h"
+#include "compiler/translator/length_limits.h"
+#ifdef ANGLE_ENABLE_HLSL
+#include "compiler/translator/TranslatorHLSL.h"
+#endif // ANGLE_ENABLE_HLSL
+#include "compiler/translator/VariablePacker.h"
+#include "angle_gl.h"
+
+using namespace sh;
+
+namespace
+{
+
+bool isInitialized = false;
+
+//
+// This is the platform independent interface between an OGL driver
+// and the shading language compiler.
+//
+
+template <typename VarT>
+const std::vector<VarT> *GetVariableList(const TCompiler *compiler);
+
+template <>
+const std::vector<Uniform> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getUniforms();
+}
+
+template <>
+const std::vector<Varying> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getVaryings();
+}
+
+template <>
+const std::vector<Attribute> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getAttributes();
+}
+
+template <>
+const std::vector<OutputVariable> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getOutputVariables();
+}
+
+template <>
+const std::vector<InterfaceBlock> *GetVariableList(const TCompiler *compiler)
+{
+ return &compiler->getInterfaceBlocks();
+}
+
+template <typename VarT>
+const std::vector<VarT> *GetShaderVariables(const ShHandle handle)
+{
+ if (!handle)
+ {
+ return NULL;
+ }
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(handle);
+ TCompiler* compiler = base->getAsCompiler();
+ if (!compiler)
+ {
+ return NULL;
+ }
+
+ return GetVariableList<VarT>(compiler);
+}
+
+TCompiler *GetCompilerFromHandle(ShHandle handle)
+{
+ if (!handle)
+ return NULL;
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ return base->getAsCompiler();
+}
+
+#ifdef ANGLE_ENABLE_HLSL
+TranslatorHLSL *GetTranslatorHLSLFromHandle(ShHandle handle)
+{
+ if (!handle)
+ return NULL;
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ return base->getAsTranslatorHLSL();
+}
+#endif // ANGLE_ENABLE_HLSL
+
+} // anonymous namespace
+
+//
+// Driver must call this first, once, before doing any other compiler operations.
+// Subsequent calls to this function are no-op.
+//
+bool ShInitialize()
+{
+ if (!isInitialized)
+ {
+ isInitialized = InitProcess();
+ }
+ return isInitialized;
+}
+
+//
+// Cleanup symbol tables
+//
+bool ShFinalize()
+{
+ if (isInitialized)
+ {
+ DetachProcess();
+ isInitialized = false;
+ }
+ return true;
+}
+
+//
+// Initialize built-in resources with minimum expected values.
+//
+void ShInitBuiltInResources(ShBuiltInResources* resources)
+{
+ // Make comparable.
+ memset(resources, 0, sizeof(*resources));
+
+ // Constants.
+ resources->MaxVertexAttribs = 8;
+ resources->MaxVertexUniformVectors = 128;
+ resources->MaxVaryingVectors = 8;
+ resources->MaxVertexTextureImageUnits = 0;
+ resources->MaxCombinedTextureImageUnits = 8;
+ resources->MaxTextureImageUnits = 8;
+ resources->MaxFragmentUniformVectors = 16;
+ resources->MaxDrawBuffers = 1;
+
+ // Extensions.
+ resources->OES_standard_derivatives = 0;
+ resources->OES_EGL_image_external = 0;
+ resources->OES_EGL_image_external_essl3 = 0;
+ resources->NV_EGL_stream_consumer_external = 0;
+ resources->ARB_texture_rectangle = 0;
+ resources->EXT_blend_func_extended = 0;
+ resources->EXT_draw_buffers = 0;
+ resources->EXT_frag_depth = 0;
+ resources->EXT_shader_texture_lod = 0;
+ resources->WEBGL_debug_shader_precision = 0;
+ resources->EXT_shader_framebuffer_fetch = 0;
+ resources->NV_shader_framebuffer_fetch = 0;
+ resources->ARM_shader_framebuffer_fetch = 0;
+
+ resources->NV_draw_buffers = 0;
+
+ // Disable highp precision in fragment shader by default.
+ resources->FragmentPrecisionHigh = 0;
+
+ // GLSL ES 3.0 constants.
+ resources->MaxVertexOutputVectors = 16;
+ resources->MaxFragmentInputVectors = 15;
+ resources->MinProgramTexelOffset = -8;
+ resources->MaxProgramTexelOffset = 7;
+
+ // Extensions constants.
+ resources->MaxDualSourceDrawBuffers = 0;
+
+ // Disable name hashing by default.
+ resources->HashFunction = NULL;
+
+ resources->ArrayIndexClampingStrategy = SH_CLAMP_WITH_CLAMP_INTRINSIC;
+
+ resources->MaxExpressionComplexity = 256;
+ resources->MaxCallStackDepth = 256;
+ resources->MaxFunctionParameters = 1024;
+
+ // ES 3.1 Revision 4, 7.2 Built-in Constants
+ resources->MaxImageUnits = 4;
+ resources->MaxVertexImageUniforms = 0;
+ resources->MaxFragmentImageUniforms = 0;
+ resources->MaxComputeImageUniforms = 4;
+ resources->MaxCombinedImageUniforms = 4;
+
+ resources->MaxCombinedShaderOutputResources = 4;
+
+ resources->MaxComputeWorkGroupCount[0] = 65535;
+ resources->MaxComputeWorkGroupCount[1] = 65535;
+ resources->MaxComputeWorkGroupCount[2] = 65535;
+ resources->MaxComputeWorkGroupSize[0] = 128;
+ resources->MaxComputeWorkGroupSize[1] = 128;
+ resources->MaxComputeWorkGroupSize[2] = 64;
+ resources->MaxComputeUniformComponents = 512;
+ resources->MaxComputeTextureImageUnits = 16;
+
+ resources->MaxComputeAtomicCounters = 8;
+ resources->MaxComputeAtomicCounterBuffers = 1;
+
+ resources->MaxVertexAtomicCounters = 0;
+ resources->MaxFragmentAtomicCounters = 0;
+ resources->MaxCombinedAtomicCounters = 8;
+ resources->MaxAtomicCounterBindings = 1;
+
+ resources->MaxVertexAtomicCounterBuffers = 0;
+ resources->MaxFragmentAtomicCounterBuffers = 0;
+ resources->MaxCombinedAtomicCounterBuffers = 1;
+ resources->MaxAtomicCounterBufferSize = 32;
+}
+
+//
+// Driver calls these to create and destroy compiler objects.
+//
+ShHandle ShConstructCompiler(sh::GLenum type, ShShaderSpec spec,
+ ShShaderOutput output,
+ const ShBuiltInResources* resources)
+{
+ TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output));
+ if (base == nullptr)
+ {
+ return 0;
+ }
+
+ TCompiler* compiler = base->getAsCompiler();
+ if (compiler == nullptr)
+ {
+ return 0;
+ }
+
+ // Generate built-in symbol table.
+ if (!compiler->Init(*resources))
+ {
+ sh::Destruct(base);
+ return 0;
+ }
+
+ return reinterpret_cast<void*>(base);
+}
+
+void ShDestruct(ShHandle handle)
+{
+ if (handle == 0)
+ return;
+
+ TShHandleBase* base = static_cast<TShHandleBase*>(handle);
+
+ if (base->getAsCompiler())
+ DeleteCompiler(base->getAsCompiler());
+}
+
+const std::string &ShGetBuiltInResourcesString(const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+ return compiler->getBuiltInResourcesString();
+}
+
+//
+// Do an actual compile on the given strings. The result is left
+// in the given compile object.
+//
+// Return: The return value of ShCompile is really boolean, indicating
+// success or failure.
+//
+bool ShCompile(const ShHandle handle,
+ const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+
+ return compiler->compile(shaderStrings, numStrings, compileOptions);
+}
+
+void ShClearResults(const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+ compiler->clearResults();
+}
+
+int ShGetShaderVersion(const ShHandle handle)
+{
+ TCompiler* compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+ return compiler->getShaderVersion();
+}
+
+ShShaderOutput ShGetShaderOutputType(const ShHandle handle)
+{
+ TCompiler* compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+ return compiler->getOutputType();
+}
+
+//
+// Return any compiler log of messages for the application.
+//
+const std::string &ShGetInfoLog(const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+
+ TInfoSink &infoSink = compiler->getInfoSink();
+ return infoSink.info.str();
+}
+
+//
+// Return any object code.
+//
+const std::string &ShGetObjectCode(const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+
+ TInfoSink &infoSink = compiler->getInfoSink();
+ return infoSink.obj.str();
+}
+
+const std::map<std::string, std::string> *ShGetNameHashingMap(
+ const ShHandle handle)
+{
+ TCompiler *compiler = GetCompilerFromHandle(handle);
+ ASSERT(compiler);
+ return &(compiler->getNameMap());
+}
+
+const std::vector<Uniform> *ShGetUniforms(const ShHandle handle)
+{
+ return GetShaderVariables<Uniform>(handle);
+}
+
+const std::vector<Varying> *ShGetVaryings(const ShHandle handle)
+{
+ return GetShaderVariables<Varying>(handle);
+}
+
+const std::vector<Attribute> *ShGetAttributes(const ShHandle handle)
+{
+ return GetShaderVariables<Attribute>(handle);
+}
+
+const std::vector<OutputVariable> *ShGetOutputVariables(const ShHandle handle)
+{
+ return GetShaderVariables<OutputVariable>(handle);
+}
+
+const std::vector<InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handle)
+{
+ return GetShaderVariables<InterfaceBlock>(handle);
+}
+
+WorkGroupSize ShGetComputeShaderLocalGroupSize(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getComputeShaderLocalSize();
+}
+
+bool ShCheckVariablesWithinPackingLimits(int maxVectors,
+ const std::vector<ShaderVariable> &variables)
+{
+ VariablePacker packer;
+ return packer.CheckVariablesWithinPackingLimits(maxVectors, variables);
+}
+
+bool ShGetInterfaceBlockRegister(const ShHandle handle,
+ const std::string &interfaceBlockName,
+ unsigned int *indexOut)
+{
+#ifdef ANGLE_ENABLE_HLSL
+ ASSERT(indexOut);
+
+ TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle);
+ ASSERT(translator);
+
+ if (!translator->hasInterfaceBlock(interfaceBlockName))
+ {
+ return false;
+ }
+
+ *indexOut = translator->getInterfaceBlockRegister(interfaceBlockName);
+ return true;
+#else
+ return false;
+#endif // ANGLE_ENABLE_HLSL
+}
+
+const std::map<std::string, unsigned int> *ShGetUniformRegisterMap(const ShHandle handle)
+{
+#ifdef ANGLE_ENABLE_HLSL
+ TranslatorHLSL *translator = GetTranslatorHLSLFromHandle(handle);
+ ASSERT(translator);
+
+ return translator->getUniformRegisterMap();
+#else
+ return nullptr;
+#endif // ANGLE_ENABLE_HLSL
+}
+
+namespace sh
+{
+bool Initialize()
+{
+ return ShInitialize();
+}
+
+bool Finalize()
+{
+ return ShFinalize();
+}
+
+void InitBuiltInResources(ShBuiltInResources *resources)
+{
+ ShInitBuiltInResources(resources);
+}
+
+const std::string &GetBuiltInResourcesString(const ShHandle handle)
+{
+ return ShGetBuiltInResourcesString(handle);
+}
+
+ShHandle ConstructCompiler(sh::GLenum type,
+ ShShaderSpec spec,
+ ShShaderOutput output,
+ const ShBuiltInResources *resources)
+{
+ return ShConstructCompiler(type, spec, output, resources);
+}
+
+void Destruct(ShHandle handle)
+{
+ return ShDestruct(handle);
+}
+
+bool Compile(const ShHandle handle,
+ const char *const shaderStrings[],
+ size_t numStrings,
+ ShCompileOptions compileOptions)
+{
+ return ShCompile(handle, shaderStrings, numStrings, compileOptions);
+}
+
+void ClearResults(const ShHandle handle)
+{
+ return ShClearResults(handle);
+}
+
+int GetShaderVersion(const ShHandle handle)
+{
+ return ShGetShaderVersion(handle);
+}
+
+ShShaderOutput GetShaderOutputType(const ShHandle handle)
+{
+ return ShGetShaderOutputType(handle);
+}
+
+const std::string &GetInfoLog(const ShHandle handle)
+{
+ return ShGetInfoLog(handle);
+}
+
+const std::string &GetObjectCode(const ShHandle handle)
+{
+ return ShGetObjectCode(handle);
+}
+
+const std::map<std::string, std::string> *GetNameHashingMap(const ShHandle handle)
+{
+ return ShGetNameHashingMap(handle);
+}
+
+const std::vector<sh::Uniform> *GetUniforms(const ShHandle handle)
+{
+ return ShGetUniforms(handle);
+}
+const std::vector<sh::Varying> *GetVaryings(const ShHandle handle)
+{
+ return ShGetVaryings(handle);
+}
+const std::vector<sh::Attribute> *GetAttributes(const ShHandle handle)
+{
+ return ShGetAttributes(handle);
+}
+
+const std::vector<sh::OutputVariable> *GetOutputVariables(const ShHandle handle)
+{
+ return ShGetOutputVariables(handle);
+}
+const std::vector<sh::InterfaceBlock> *GetInterfaceBlocks(const ShHandle handle)
+{
+ return ShGetInterfaceBlocks(handle);
+}
+
+sh::WorkGroupSize GetComputeShaderLocalGroupSize(const ShHandle handle)
+{
+ return ShGetComputeShaderLocalGroupSize(handle);
+}
+
+bool CheckVariablesWithinPackingLimits(int maxVectors,
+ const std::vector<sh::ShaderVariable> &variables)
+{
+ return ShCheckVariablesWithinPackingLimits(maxVectors, variables);
+}
+
+bool GetInterfaceBlockRegister(const ShHandle handle,
+ const std::string &interfaceBlockName,
+ unsigned int *indexOut)
+{
+ return ShGetInterfaceBlockRegister(handle, interfaceBlockName, indexOut);
+}
+
+const std::map<std::string, unsigned int> *GetUniformRegisterMap(const ShHandle handle)
+{
+ return ShGetUniformRegisterMap(handle);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ShaderVars.cpp b/gfx/angle/src/compiler/translator/ShaderVars.cpp
new file mode 100755
index 000000000..8e217f1c0
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ShaderVars.cpp
@@ -0,0 +1,488 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ShaderVars.cpp:
+// Methods for GL variable types (varyings, uniforms, etc)
+//
+
+#include <GLSLANG/ShaderLang.h>
+
+#include "common/debug.h"
+
+namespace sh
+{
+
+namespace
+{
+
+InterpolationType GetNonAuxiliaryInterpolationType(InterpolationType interpolation)
+{
+ return (interpolation == INTERPOLATION_CENTROID ? INTERPOLATION_SMOOTH : interpolation);
+}
+
+}
+// The ES 3.0 spec is not clear on this point, but the ES 3.1 spec, and discussion
+// on Khronos.org, clarifies that a smooth/flat mismatch produces a link error,
+// but auxiliary qualifier mismatch (centroid) does not.
+bool InterpolationTypesMatch(InterpolationType a, InterpolationType b)
+{
+ return (GetNonAuxiliaryInterpolationType(a) == GetNonAuxiliaryInterpolationType(b));
+}
+
+ShaderVariable::ShaderVariable()
+ : type(0),
+ precision(0),
+ arraySize(0),
+ staticUse(false)
+{}
+
+ShaderVariable::ShaderVariable(GLenum typeIn, unsigned int arraySizeIn)
+ : type(typeIn),
+ precision(0),
+ arraySize(arraySizeIn),
+ staticUse(false)
+{}
+
+ShaderVariable::~ShaderVariable()
+{}
+
+ShaderVariable::ShaderVariable(const ShaderVariable &other)
+ : type(other.type),
+ precision(other.precision),
+ name(other.name),
+ mappedName(other.mappedName),
+ arraySize(other.arraySize),
+ staticUse(other.staticUse),
+ fields(other.fields),
+ structName(other.structName)
+{}
+
+ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
+{
+ type = other.type;
+ precision = other.precision;
+ name = other.name;
+ mappedName = other.mappedName;
+ arraySize = other.arraySize;
+ staticUse = other.staticUse;
+ fields = other.fields;
+ structName = other.structName;
+ return *this;
+}
+
+bool ShaderVariable::operator==(const ShaderVariable &other) const
+{
+ if (type != other.type ||
+ precision != other.precision ||
+ name != other.name ||
+ mappedName != other.mappedName ||
+ arraySize != other.arraySize ||
+ staticUse != other.staticUse ||
+ fields.size() != other.fields.size() ||
+ structName != other.structName)
+ {
+ return false;
+ }
+ for (size_t ii = 0; ii < fields.size(); ++ii)
+ {
+ if (fields[ii] != other.fields[ii])
+ return false;
+ }
+ return true;
+}
+
+bool ShaderVariable::findInfoByMappedName(
+ const std::string &mappedFullName,
+ const ShaderVariable **leafVar, std::string *originalFullName) const
+{
+ ASSERT(leafVar && originalFullName);
+ // There are three cases:
+ // 1) the top variable is of struct type;
+ // 2) the top variable is an array;
+ // 3) otherwise.
+ size_t pos = mappedFullName.find_first_of(".[");
+
+ if (pos == std::string::npos)
+ {
+ // Case 3.
+ if (mappedFullName != this->mappedName)
+ return false;
+ *originalFullName = this->name;
+ *leafVar = this;
+ return true;
+ }
+ else
+ {
+ std::string topName = mappedFullName.substr(0, pos);
+ if (topName != this->mappedName)
+ return false;
+ std::string originalName = this->name;
+ std::string remaining;
+ if (mappedFullName[pos] == '[')
+ {
+ // Case 2.
+ size_t closePos = mappedFullName.find_first_of(']');
+ if (closePos < pos || closePos == std::string::npos)
+ return false;
+ // Append '[index]'.
+ originalName += mappedFullName.substr(pos, closePos - pos + 1);
+ if (closePos + 1 == mappedFullName.size())
+ {
+ *originalFullName = originalName;
+ *leafVar = this;
+ return true;
+ }
+ else
+ {
+ // In the form of 'a[0].b', so after ']', '.' is expected.
+ if (mappedFullName[closePos + 1] != '.')
+ return false;
+ remaining = mappedFullName.substr(closePos + 2); // Skip "]."
+ }
+ }
+ else
+ {
+ // Case 1.
+ remaining = mappedFullName.substr(pos + 1); // Skip "."
+ }
+ for (size_t ii = 0; ii < this->fields.size(); ++ii)
+ {
+ const ShaderVariable *fieldVar = NULL;
+ std::string originalFieldName;
+ bool found = fields[ii].findInfoByMappedName(
+ remaining, &fieldVar, &originalFieldName);
+ if (found)
+ {
+ *originalFullName = originalName + "." + originalFieldName;
+ *leafVar = fieldVar;
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+bool ShaderVariable::isSameVariableAtLinkTime(
+ const ShaderVariable &other, bool matchPrecision) const
+{
+ if (type != other.type)
+ return false;
+ if (matchPrecision && precision != other.precision)
+ return false;
+ if (name != other.name)
+ return false;
+ ASSERT(mappedName == other.mappedName);
+ if (arraySize != other.arraySize)
+ return false;
+ if (fields.size() != other.fields.size())
+ return false;
+ for (size_t ii = 0; ii < fields.size(); ++ii)
+ {
+ if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii],
+ matchPrecision))
+ {
+ return false;
+ }
+ }
+ if (structName != other.structName)
+ return false;
+ return true;
+}
+
+Uniform::Uniform()
+{}
+
+Uniform::~Uniform()
+{}
+
+Uniform::Uniform(const Uniform &other)
+ : ShaderVariable(other)
+{}
+
+Uniform &Uniform::operator=(const Uniform &other)
+{
+ ShaderVariable::operator=(other);
+ return *this;
+}
+
+bool Uniform::operator==(const Uniform &other) const
+{
+ return ShaderVariable::operator==(other);
+}
+
+bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
+{
+ return ShaderVariable::isSameVariableAtLinkTime(other, true);
+}
+
+InterfaceVariable::InterfaceVariable() : location(-1)
+{}
+
+InterfaceVariable::~InterfaceVariable()
+{}
+
+InterfaceVariable::InterfaceVariable(const InterfaceVariable &other)
+ : ShaderVariable(other), location(other.location)
+{}
+
+InterfaceVariable &InterfaceVariable::operator=(const InterfaceVariable &other)
+{
+ ShaderVariable::operator=(other);
+ location = other.location;
+ return *this;
+}
+
+bool InterfaceVariable::operator==(const InterfaceVariable &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ location == other.location);
+}
+
+Attribute::Attribute()
+{
+}
+
+Attribute::~Attribute()
+{
+}
+
+Attribute::Attribute(const Attribute &other) : InterfaceVariable(other)
+{
+}
+
+Attribute &Attribute::operator=(const Attribute &other)
+{
+ InterfaceVariable::operator=(other);
+ return *this;
+}
+
+bool Attribute::operator==(const Attribute &other) const
+{
+ return InterfaceVariable::operator==(other);
+}
+
+OutputVariable::OutputVariable()
+{
+}
+
+OutputVariable::~OutputVariable()
+{
+}
+
+OutputVariable::OutputVariable(const OutputVariable &other) : InterfaceVariable(other)
+{
+}
+
+OutputVariable &OutputVariable::operator=(const OutputVariable &other)
+{
+ InterfaceVariable::operator=(other);
+ return *this;
+}
+
+bool OutputVariable::operator==(const OutputVariable &other) const
+{
+ return InterfaceVariable::operator==(other);
+}
+
+InterfaceBlockField::InterfaceBlockField()
+ : isRowMajorLayout(false)
+{}
+
+InterfaceBlockField::~InterfaceBlockField()
+{}
+
+InterfaceBlockField::InterfaceBlockField(const InterfaceBlockField &other)
+ : ShaderVariable(other),
+ isRowMajorLayout(other.isRowMajorLayout)
+{}
+
+InterfaceBlockField &InterfaceBlockField::operator=(const InterfaceBlockField &other)
+{
+ ShaderVariable::operator=(other);
+ isRowMajorLayout = other.isRowMajorLayout;
+ return *this;
+}
+
+bool InterfaceBlockField::operator==(const InterfaceBlockField &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ isRowMajorLayout == other.isRowMajorLayout);
+}
+
+bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
+ const InterfaceBlockField &other) const
+{
+ return (ShaderVariable::isSameVariableAtLinkTime(other, true) &&
+ isRowMajorLayout == other.isRowMajorLayout);
+}
+
+Varying::Varying()
+ : interpolation(INTERPOLATION_SMOOTH),
+ isInvariant(false)
+{}
+
+Varying::~Varying()
+{}
+
+Varying::Varying(const Varying &other)
+ : ShaderVariable(other),
+ interpolation(other.interpolation),
+ isInvariant(other.isInvariant)
+{}
+
+Varying &Varying::operator=(const Varying &other)
+{
+ ShaderVariable::operator=(other);
+ interpolation = other.interpolation;
+ isInvariant = other.isInvariant;
+ return *this;
+}
+
+bool Varying::operator==(const Varying &other) const
+{
+ return (ShaderVariable::operator==(other) &&
+ interpolation == other.interpolation &&
+ isInvariant == other.isInvariant);
+}
+
+bool Varying::isSameVaryingAtLinkTime(const Varying &other) const
+{
+ return isSameVaryingAtLinkTime(other, 100);
+}
+
+bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) const
+{
+ return (ShaderVariable::isSameVariableAtLinkTime(other, false) &&
+ InterpolationTypesMatch(interpolation, other.interpolation) &&
+ (shaderVersion >= 300 || isInvariant == other.isInvariant));
+}
+
+InterfaceBlock::InterfaceBlock()
+ : arraySize(0),
+ layout(BLOCKLAYOUT_PACKED),
+ isRowMajorLayout(false),
+ staticUse(false)
+{}
+
+InterfaceBlock::~InterfaceBlock()
+{}
+
+InterfaceBlock::InterfaceBlock(const InterfaceBlock &other)
+ : name(other.name),
+ mappedName(other.mappedName),
+ instanceName(other.instanceName),
+ arraySize(other.arraySize),
+ layout(other.layout),
+ isRowMajorLayout(other.isRowMajorLayout),
+ staticUse(other.staticUse),
+ fields(other.fields)
+{}
+
+InterfaceBlock &InterfaceBlock::operator=(const InterfaceBlock &other)
+{
+ name = other.name;
+ mappedName = other.mappedName;
+ instanceName = other.instanceName;
+ arraySize = other.arraySize;
+ layout = other.layout;
+ isRowMajorLayout = other.isRowMajorLayout;
+ staticUse = other.staticUse;
+ fields = other.fields;
+ return *this;
+}
+
+std::string InterfaceBlock::fieldPrefix() const
+{
+ return instanceName.empty() ? "" : name;
+}
+
+bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const
+{
+ if (name != other.name || mappedName != other.mappedName || arraySize != other.arraySize ||
+ layout != other.layout || isRowMajorLayout != other.isRowMajorLayout ||
+ fields.size() != other.fields.size())
+ {
+ return false;
+ }
+
+ for (size_t fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex)
+ {
+ if (!fields[fieldIndex].isSameInterfaceBlockFieldAtLinkTime(other.fields[fieldIndex]))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void WorkGroupSize::fill(int fillValue)
+{
+ localSizeQualifiers[0] = fillValue;
+ localSizeQualifiers[1] = fillValue;
+ localSizeQualifiers[2] = fillValue;
+}
+
+void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
+{
+ localSizeQualifiers[0] = localSizeX;
+ localSizeQualifiers[1] = localSizeY;
+ localSizeQualifiers[2] = localSizeZ;
+}
+
+// check that if one of them is less than 1, then all of them are.
+// Or if one is positive, then all of them are positive.
+bool WorkGroupSize::isLocalSizeValid() const
+{
+ return (
+ (localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
+ (localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
+}
+
+bool WorkGroupSize::isAnyValueSet() const
+{
+ return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
+}
+
+bool WorkGroupSize::isDeclared() const
+{
+ bool localSizeDeclared = localSizeQualifiers[0] > 0;
+ ASSERT(isLocalSizeValid());
+ return localSizeDeclared;
+}
+
+bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
+{
+ for (size_t i = 0u; i < size(); ++i)
+ {
+ bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
+ (localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
+ (localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
+ if (!result)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+int &WorkGroupSize::operator[](size_t index)
+{
+ ASSERT(index < size());
+ return localSizeQualifiers[index];
+}
+
+int WorkGroupSize::operator[](size_t index) const
+{
+ ASSERT(index < size());
+ return localSizeQualifiers[index];
+}
+
+size_t WorkGroupSize::size() const
+{
+ return 3u;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp b/gfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp
new file mode 100755
index 000000000..85c166e59
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SimplifyLoopConditions.cpp
@@ -0,0 +1,288 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SimplifyLoopConditions is an AST traverser that converts loop conditions and loop expressions
+// to regular statements inside the loop. This way further transformations that generate statements
+// from loop conditions and loop expressions work correctly.
+//
+
+#include "compiler/translator/SimplifyLoopConditions.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+namespace sh
+{
+
+namespace
+{
+
+TIntermConstantUnion *CreateBoolConstantNode(bool value)
+{
+ TConstantUnion *u = new TConstantUnion;
+ u->setBConst(value);
+ TIntermConstantUnion *node =
+ new TIntermConstantUnion(u, TType(EbtBool, EbpUndefined, EvqConst, 1));
+ return node;
+}
+
+class SimplifyLoopConditionsTraverser : public TLValueTrackingTraverser
+{
+ public:
+ SimplifyLoopConditionsTraverser(unsigned int conditionsToSimplifyMask,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+ void traverseLoop(TIntermLoop *node) override;
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+
+ void nextIteration();
+ bool foundLoopToChange() const { return mFoundLoopToChange; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of the expression has been
+ // found. After that, no more AST updates are performed on that traversal.
+ bool mFoundLoopToChange;
+ bool mInsideLoopConditionOrExpression;
+ IntermNodePatternMatcher mConditionsToSimplify;
+};
+
+SimplifyLoopConditionsTraverser::SimplifyLoopConditionsTraverser(
+ unsigned int conditionsToSimplifyMask,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+ : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
+ mFoundLoopToChange(false),
+ mInsideLoopConditionOrExpression(false),
+ mConditionsToSimplify(conditionsToSimplifyMask)
+{
+}
+
+void SimplifyLoopConditionsTraverser::nextIteration()
+{
+ mFoundLoopToChange = false;
+ mInsideLoopConditionOrExpression = false;
+ nextTemporaryIndex();
+}
+
+bool SimplifyLoopConditionsTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ // The visit functions operate in three modes:
+ // 1. If a matching expression has already been found, we return early since only one loop can
+ // be transformed on one traversal.
+ // 2. We try to find loops. In case a node is not inside a loop and can not contain loops, we
+ // stop traversing the subtree.
+ // 3. If we're inside a loop condition or expression, we check for expressions that should be
+ // moved out of the loop condition or expression. If one is found, the loop is processed.
+
+ if (mFoundLoopToChange)
+ return false;
+
+ if (!mInsideLoopConditionOrExpression)
+ return false;
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode(), isLValueRequiredHere());
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundLoopToChange)
+ return false;
+
+ // If we're outside a loop condition, we only need to traverse nodes that may contain loops.
+ if (!mInsideLoopConditionOrExpression)
+ return false;
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node, getParentNode());
+ return !mFoundLoopToChange;
+}
+
+bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (mFoundLoopToChange)
+ return false;
+
+ // Don't traverse ternary operators outside loop conditions.
+ if (!mInsideLoopConditionOrExpression)
+ return false;
+
+ mFoundLoopToChange = mConditionsToSimplify.match(node);
+ return !mFoundLoopToChange;
+}
+
+void SimplifyLoopConditionsTraverser::traverseLoop(TIntermLoop *node)
+{
+ if (mFoundLoopToChange)
+ return;
+
+ // Mark that we're inside a loop condition or expression, and transform the loop if needed.
+
+ incrementDepth(node);
+
+ // Note: No need to traverse the loop init node.
+
+ mInsideLoopConditionOrExpression = true;
+ TLoopType loopType = node->getType();
+
+ if (node->getCondition())
+ {
+ node->getCondition()->traverse(this);
+
+ if (mFoundLoopToChange)
+ {
+ // Replace the loop condition with a boolean variable that's updated on each iteration.
+ if (loopType == ELoopWhile)
+ {
+ // Transform:
+ // while (expr) { body; }
+ // into
+ // bool s0 = expr;
+ // while (s0) { { body; } s0 = expr; }
+ TIntermSequence tempInitSeq;
+ tempInitSeq.push_back(createTempInitDeclaration(node->getCondition()->deepCopy()));
+ insertStatementsInParentBlock(tempInitSeq);
+
+ TIntermBlock *newBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ newBody->getSequence()->push_back(node->getBody());
+ }
+ newBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+
+ // Can't use queueReplacement to replace old body, since it may have been nullptr.
+ // It's safe to do the replacements in place here - this node won't be traversed
+ // further.
+ node->setBody(newBody);
+ node->setCondition(createTempSymbol(node->getCondition()->getType()));
+ }
+ else if (loopType == ELoopDoWhile)
+ {
+ // Transform:
+ // do {
+ // body;
+ // } while (expr);
+ // into
+ // bool s0 = true;
+ // do {
+ // { body; }
+ // s0 = expr;
+ // while (s0);
+ TIntermSequence tempInitSeq;
+ tempInitSeq.push_back(createTempInitDeclaration(CreateBoolConstantNode(true)));
+ insertStatementsInParentBlock(tempInitSeq);
+
+ TIntermBlock *newBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ newBody->getSequence()->push_back(node->getBody());
+ }
+ newBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+
+ // Can't use queueReplacement to replace old body, since it may have been nullptr.
+ // It's safe to do the replacements in place here - this node won't be traversed
+ // further.
+ node->setBody(newBody);
+ node->setCondition(createTempSymbol(node->getCondition()->getType()));
+ }
+ else if (loopType == ELoopFor)
+ {
+ // Move the loop condition inside the loop.
+ // Transform:
+ // for (init; expr; exprB) { body; }
+ // into
+ // {
+ // init;
+ // bool s0 = expr;
+ // while (s0) { { body; } exprB; s0 = expr; }
+ // }
+ TIntermBlock *loopScope = new TIntermBlock();
+ if (node->getInit())
+ {
+ loopScope->getSequence()->push_back(node->getInit());
+ }
+ loopScope->getSequence()->push_back(
+ createTempInitDeclaration(node->getCondition()->deepCopy()));
+
+ TIntermBlock *whileLoopBody = new TIntermBlock();
+ if (node->getBody())
+ {
+ whileLoopBody->getSequence()->push_back(node->getBody());
+ }
+ if (node->getExpression())
+ {
+ whileLoopBody->getSequence()->push_back(node->getExpression());
+ }
+ whileLoopBody->getSequence()->push_back(
+ createTempAssignment(node->getCondition()->deepCopy()));
+ TIntermLoop *whileLoop = new TIntermLoop(
+ ELoopWhile, nullptr, createTempSymbol(node->getCondition()->getType()), nullptr,
+ whileLoopBody);
+ loopScope->getSequence()->push_back(whileLoop);
+ queueReplacementWithParent(getAncestorNode(1), node, loopScope,
+ OriginalNode::IS_DROPPED);
+ }
+ }
+ }
+
+ if (!mFoundLoopToChange && node->getExpression())
+ {
+ node->getExpression()->traverse(this);
+
+ if (mFoundLoopToChange)
+ {
+ ASSERT(loopType == ELoopFor);
+ // Move the loop expression to inside the loop.
+ // Transform:
+ // for (init; expr; exprB) { body; }
+ // into
+ // for (init; expr; ) { { body; } exprB; }
+ TIntermTyped *loopExpression = node->getExpression();
+ node->setExpression(nullptr);
+ TIntermBlock *oldBody = node->getBody();
+ node->setBody(new TIntermBlock());
+ if (oldBody != nullptr)
+ {
+ node->getBody()->getSequence()->push_back(oldBody);
+ }
+ node->getBody()->getSequence()->push_back(loopExpression);
+ }
+ }
+
+ mInsideLoopConditionOrExpression = false;
+
+ if (!mFoundLoopToChange && node->getBody())
+ node->getBody()->traverse(this);
+
+ decrementDepth();
+}
+
+} // namespace
+
+void SimplifyLoopConditions(TIntermNode *root,
+ unsigned int conditionsToSimplifyMask,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ SimplifyLoopConditionsTraverser traverser(conditionsToSimplifyMask, symbolTable, shaderVersion);
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // Process one loop at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundLoopToChange())
+ traverser.updateTree();
+ } while (traverser.foundLoopToChange());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SimplifyLoopConditions.h b/gfx/angle/src/compiler/translator/SimplifyLoopConditions.h
new file mode 100755
index 000000000..968089d54
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SimplifyLoopConditions.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SimplifyLoopConditions is an AST traverser that converts loop conditions and loop expressions
+// to regular statements inside the loop. This way further transformations that generate statements
+// from loop conditions and loop expressions work correctly.
+//
+
+#ifndef COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
+#define COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
+
+namespace sh
+{
+class TIntermNode;
+class TSymbolTable;
+
+void SimplifyLoopConditions(TIntermNode *root,
+ unsigned int conditionsToSimplify,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_
diff --git a/gfx/angle/src/compiler/translator/SplitSequenceOperator.cpp b/gfx/angle/src/compiler/translator/SplitSequenceOperator.cpp
new file mode 100755
index 000000000..4c63b59f7
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SplitSequenceOperator.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SplitSequenceOperator is an AST traverser that detects sequence operator expressions that
+// go through further AST transformations that generate statements, and splits them so that
+// possible side effects of earlier parts of the sequence operator expression are guaranteed to be
+// evaluated before the latter parts of the sequence operator expression are evaluated.
+//
+
+#include "compiler/translator/SplitSequenceOperator.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class SplitSequenceOperatorTraverser : public TLValueTrackingTraverser
+{
+ public:
+ SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+
+ void nextIteration();
+ bool foundExpressionToSplit() const { return mFoundExpressionToSplit; }
+
+ protected:
+ // Marked to true once an operation that needs to be hoisted out of the expression has been
+ // found. After that, no more AST updates are performed on that traversal.
+ bool mFoundExpressionToSplit;
+ int mInsideSequenceOperator;
+
+ IntermNodePatternMatcher mPatternToSplitMatcher;
+};
+
+SplitSequenceOperatorTraverser::SplitSequenceOperatorTraverser(unsigned int patternsToSplitMask,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+ : TLValueTrackingTraverser(true, false, true, symbolTable, shaderVersion),
+ mFoundExpressionToSplit(false),
+ mInsideSequenceOperator(0),
+ mPatternToSplitMatcher(patternsToSplitMask)
+{
+}
+
+void SplitSequenceOperatorTraverser::nextIteration()
+{
+ mFoundExpressionToSplit = false;
+ mInsideSequenceOperator = 0;
+ nextTemporaryIndex();
+}
+
+bool SplitSequenceOperatorTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit = mPatternToSplitMatcher.match(node, getParentNode());
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+bool SplitSequenceOperatorTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->getOp() == EOpComma)
+ {
+ if (visit == PreVisit)
+ {
+ if (mFoundExpressionToSplit)
+ {
+ return false;
+ }
+ mInsideSequenceOperator++;
+ }
+ else if (visit == PostVisit)
+ {
+ // Split sequence operators starting from the outermost one to preserve correct
+ // execution order.
+ if (mFoundExpressionToSplit && mInsideSequenceOperator == 1)
+ {
+ // Move the left side operand into a separate statement in the parent block.
+ TIntermSequence insertions;
+ insertions.push_back(node->getLeft());
+ insertStatementsInParentBlock(insertions);
+ // Replace the comma node with its right side operand.
+ queueReplacement(node, node->getRight(), OriginalNode::IS_DROPPED);
+ }
+ mInsideSequenceOperator--;
+ }
+ return true;
+ }
+
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit =
+ mPatternToSplitMatcher.match(node, getParentNode(), isLValueRequiredHere());
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+bool SplitSequenceOperatorTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (mFoundExpressionToSplit)
+ return false;
+
+ if (mInsideSequenceOperator > 0 && visit == PreVisit)
+ {
+ // Detect expressions that need to be simplified
+ mFoundExpressionToSplit = mPatternToSplitMatcher.match(node);
+ return !mFoundExpressionToSplit;
+ }
+
+ return true;
+}
+
+} // namespace
+
+void SplitSequenceOperator(TIntermNode *root,
+ int patternsToSplitMask,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion)
+{
+ SplitSequenceOperatorTraverser traverser(patternsToSplitMask, symbolTable, shaderVersion);
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // Separate one expression at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundExpressionToSplit())
+ traverser.updateTree();
+ } while (traverser.foundExpressionToSplit());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SplitSequenceOperator.h b/gfx/angle/src/compiler/translator/SplitSequenceOperator.h
new file mode 100755
index 000000000..6df9d458e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SplitSequenceOperator.h
@@ -0,0 +1,29 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// SplitSequenceOperator is an AST traverser that detects sequence operator expressions that
+// go through further AST transformations that generate statements, and splits them so that
+// possible side effects of earlier parts of the sequence operator expression are guaranteed to be
+// evaluated before the latter parts of the sequence operator expression are evaluated.
+//
+
+#ifndef COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
+#define COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
+
+namespace sh
+{
+
+class TIntermNode;
+class TSymbolTable;
+
+void SplitSequenceOperator(TIntermNode *root,
+ int patternsToSplitMask,
+ unsigned int *temporaryIndex,
+ const TSymbolTable &symbolTable,
+ int shaderVersion);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_
diff --git a/gfx/angle/src/compiler/translator/StructureHLSL.cpp b/gfx/angle/src/compiler/translator/StructureHLSL.cpp
new file mode 100755
index 000000000..93e0ba573
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/StructureHLSL.cpp
@@ -0,0 +1,539 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// StructureHLSL.cpp:
+// Definitions of methods for HLSL translation of GLSL structures.
+//
+
+#include "compiler/translator/StructureHLSL.h"
+#include "common/utilities.h"
+#include "compiler/translator/OutputHLSL.h"
+#include "compiler/translator/Types.h"
+#include "compiler/translator/util.h"
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+Std140PaddingHelper::Std140PaddingHelper(const std::map<TString, int> &structElementIndexes,
+ unsigned *uniqueCounter)
+ : mPaddingCounter(uniqueCounter),
+ mElementIndex(0),
+ mStructElementIndexes(&structElementIndexes)
+{}
+
+Std140PaddingHelper::Std140PaddingHelper(const Std140PaddingHelper &other)
+ : mPaddingCounter(other.mPaddingCounter),
+ mElementIndex(other.mElementIndex),
+ mStructElementIndexes(other.mStructElementIndexes)
+{}
+
+Std140PaddingHelper &Std140PaddingHelper::operator=(const Std140PaddingHelper &other)
+{
+ mPaddingCounter = other.mPaddingCounter;
+ mElementIndex = other.mElementIndex;
+ mStructElementIndexes = other.mStructElementIndexes;
+ return *this;
+}
+
+TString Std140PaddingHelper::next()
+{
+ unsigned value = (*mPaddingCounter)++;
+ return str(value);
+}
+
+int Std140PaddingHelper::prePadding(const TType &type)
+{
+ if (type.getBasicType() == EbtStruct || type.isMatrix() || type.isArray())
+ {
+ // no padding needed, HLSL will align the field to a new register
+ mElementIndex = 0;
+ return 0;
+ }
+
+ const GLenum glType = GLVariableType(type);
+ const int numComponents = gl::VariableComponentCount(glType);
+
+ if (numComponents >= 4)
+ {
+ // no padding needed, HLSL will align the field to a new register
+ mElementIndex = 0;
+ return 0;
+ }
+
+ if (mElementIndex + numComponents > 4)
+ {
+ // no padding needed, HLSL will align the field to a new register
+ mElementIndex = numComponents;
+ return 0;
+ }
+
+ const int alignment = numComponents == 3 ? 4 : numComponents;
+ const int paddingOffset = (mElementIndex % alignment);
+ const int paddingCount = (paddingOffset != 0 ? (alignment - paddingOffset) : 0);
+
+ mElementIndex += paddingCount;
+ mElementIndex += numComponents;
+ mElementIndex %= 4;
+
+ return paddingCount;
+}
+
+TString Std140PaddingHelper::prePaddingString(const TType &type)
+{
+ int paddingCount = prePadding(type);
+
+ TString padding;
+
+ for (int paddingIndex = 0; paddingIndex < paddingCount; paddingIndex++)
+ {
+ padding += " float pad_" + next() + ";\n";
+ }
+
+ return padding;
+}
+
+TString Std140PaddingHelper::postPaddingString(const TType &type, bool useHLSLRowMajorPacking)
+{
+ if (!type.isMatrix() && !type.isArray() && type.getBasicType() != EbtStruct)
+ {
+ return "";
+ }
+
+ int numComponents = 0;
+ TStructure *structure = type.getStruct();
+
+ if (type.isMatrix())
+ {
+ // This method can also be called from structureString, which does not use layout qualifiers.
+ // Thus, use the method parameter for determining the matrix packing.
+ //
+ // Note HLSL row major packing corresponds to GL API column-major, and vice-versa, since we
+ // wish to always transpose GL matrices to play well with HLSL's matrix array indexing.
+ //
+ const bool isRowMajorMatrix = !useHLSLRowMajorPacking;
+ const GLenum glType = GLVariableType(type);
+ numComponents = gl::MatrixComponentCount(glType, isRowMajorMatrix);
+ }
+ else if (structure)
+ {
+ const TString &structName = QualifiedStructNameString(*structure,
+ useHLSLRowMajorPacking, true);
+ numComponents = mStructElementIndexes->find(structName)->second;
+
+ if (numComponents == 0)
+ {
+ return "";
+ }
+ }
+ else
+ {
+ const GLenum glType = GLVariableType(type);
+ numComponents = gl::VariableComponentCount(glType);
+ }
+
+ TString padding;
+ for (int paddingOffset = numComponents; paddingOffset < 4; paddingOffset++)
+ {
+ padding += " float pad_" + next() + ";\n";
+ }
+ return padding;
+}
+
+StructureHLSL::StructureHLSL()
+ : mUniquePaddingCounter(0)
+{}
+
+Std140PaddingHelper StructureHLSL::getPaddingHelper()
+{
+ return Std140PaddingHelper(mStd140StructElementIndexes, &mUniquePaddingCounter);
+}
+
+TString StructureHLSL::defineQualified(const TStructure &structure, bool useHLSLRowMajorPacking, bool useStd140Packing)
+{
+ if (useStd140Packing)
+ {
+ Std140PaddingHelper padHelper = getPaddingHelper();
+ return define(structure, useHLSLRowMajorPacking, useStd140Packing, &padHelper);
+ }
+ else
+ {
+ return define(structure, useHLSLRowMajorPacking, useStd140Packing, NULL);
+ }
+}
+
+TString StructureHLSL::defineNameless(const TStructure &structure)
+{
+ return define(structure, false, false, NULL);
+}
+
+TString StructureHLSL::define(const TStructure &structure, bool useHLSLRowMajorPacking,
+ bool useStd140Packing, Std140PaddingHelper *padHelper)
+{
+ const TFieldList &fields = structure.fields();
+ const bool isNameless = (structure.name() == "");
+ const TString &structName = QualifiedStructNameString(structure, useHLSLRowMajorPacking,
+ useStd140Packing);
+ const TString declareString = (isNameless ? "struct" : "struct " + structName);
+
+ TString string;
+ string += declareString + "\n"
+ "{\n";
+
+ for (const TField *field : fields)
+ {
+ const TType &fieldType = *field->type();
+ if (!IsSampler(fieldType.getBasicType()))
+ {
+ const TStructure *fieldStruct = fieldType.getStruct();
+ const TString &fieldTypeString =
+ fieldStruct ? QualifiedStructNameString(*fieldStruct, useHLSLRowMajorPacking,
+ useStd140Packing)
+ : TypeString(fieldType);
+
+ if (padHelper)
+ {
+ string += padHelper->prePaddingString(fieldType);
+ }
+
+ string += " " + fieldTypeString + " " + DecorateField(field->name(), structure) +
+ ArrayString(fieldType) + ";\n";
+
+ if (padHelper)
+ {
+ string += padHelper->postPaddingString(fieldType, useHLSLRowMajorPacking);
+ }
+ }
+ }
+
+ // Nameless structs do not finish with a semicolon and newline, to leave room for an instance variable
+ string += (isNameless ? "} " : "};\n");
+
+ return string;
+}
+
+TString StructureHLSL::addConstructor(const TType &type,
+ const TString &name,
+ const TIntermSequence *parameters)
+{
+ if (name == "")
+ {
+ return TString(); // Nameless structures don't have constructors
+ }
+
+ if (type.getStruct() && mStructNames.find(name) != mStructNames.end())
+ {
+ return TString(name); // Already added
+ }
+
+ TType ctorType = type;
+ ctorType.clearArrayness();
+ ctorType.setPrecision(EbpHigh);
+ ctorType.setQualifier(EvqTemporary);
+
+ typedef std::vector<TType> ParameterArray;
+ ParameterArray ctorParameters;
+
+ TString constructorFunctionName;
+
+ const TStructure* structure = type.getStruct();
+ if (structure)
+ {
+ mStructNames.insert(name);
+
+ // Add element index
+ storeStd140ElementIndex(*structure, false);
+ storeStd140ElementIndex(*structure, true);
+
+ const TString &structString = defineQualified(*structure, false, false);
+
+ if (std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structString) == mStructDeclarations.end())
+ {
+ // Add row-major packed struct for interface blocks
+ TString rowMajorString = "#pragma pack_matrix(row_major)\n" +
+ defineQualified(*structure, true, false) +
+ "#pragma pack_matrix(column_major)\n";
+
+ TString std140String = defineQualified(*structure, false, true);
+ TString std140RowMajorString = "#pragma pack_matrix(row_major)\n" +
+ defineQualified(*structure, true, true) +
+ "#pragma pack_matrix(column_major)\n";
+
+ mStructDeclarations.push_back(structString);
+ mStructDeclarations.push_back(rowMajorString);
+ mStructDeclarations.push_back(std140String);
+ mStructDeclarations.push_back(std140RowMajorString);
+ }
+
+ const TFieldList &fields = structure->fields();
+ for (const TField *field : fields)
+ {
+ const TType *fieldType = field->type();
+ if (!IsSampler(fieldType->getBasicType()))
+ {
+ ctorParameters.push_back(*fieldType);
+ }
+ }
+ constructorFunctionName = TString(name);
+ }
+ else if (parameters)
+ {
+ for (auto parameter : *parameters)
+ {
+ const TType &paramType = parameter->getAsTyped()->getType();
+ ctorParameters.push_back(paramType);
+ }
+ constructorFunctionName = TString(name) + DisambiguateFunctionName(parameters);
+ }
+ else UNREACHABLE();
+
+ TString constructor;
+
+ if (ctorType.getStruct())
+ {
+ constructor += name + " " + name + "_ctor(";
+ }
+ else // Built-in type
+ {
+ constructor += TypeString(ctorType) + " " + constructorFunctionName + "(";
+ }
+
+ for (unsigned int parameter = 0; parameter < ctorParameters.size(); parameter++)
+ {
+ const TType &paramType = ctorParameters[parameter];
+
+ constructor += TypeString(paramType) + " x" + str(parameter) + ArrayString(paramType);
+
+ if (parameter < ctorParameters.size() - 1)
+ {
+ constructor += ", ";
+ }
+ }
+
+ constructor += ")\n"
+ "{\n";
+
+ if (ctorType.getStruct())
+ {
+ constructor += " " + name + " structure";
+ if (ctorParameters.empty())
+ {
+ constructor += ";\n";
+ }
+ else
+ {
+ constructor += " = { ";
+ }
+ }
+ else
+ {
+ constructor += " return " + TypeString(ctorType) + "(";
+ }
+
+ if (ctorType.isMatrix() && ctorParameters.size() == 1)
+ {
+ int rows = ctorType.getRows();
+ int cols = ctorType.getCols();
+ const TType &parameter = ctorParameters[0];
+
+ if (parameter.isScalar())
+ {
+ for (int col = 0; col < cols; col++)
+ {
+ for (int row = 0; row < rows; row++)
+ {
+ constructor += TString((row == col) ? "x0" : "0.0");
+
+ if (row < rows - 1 || col < cols - 1)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+ }
+ else if (parameter.isMatrix())
+ {
+ for (int col = 0; col < cols; col++)
+ {
+ for (int row = 0; row < rows; row++)
+ {
+ if (row < parameter.getRows() && col < parameter.getCols())
+ {
+ constructor += TString("x0") + "[" + str(col) + "][" + str(row) + "]";
+ }
+ else
+ {
+ constructor += TString((row == col) ? "1.0" : "0.0");
+ }
+
+ if (row < rows - 1 || col < cols - 1)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+ }
+ else
+ {
+ ASSERT(rows == 2 && cols == 2 && parameter.isVector() && parameter.getNominalSize() == 4);
+
+ constructor += "x0";
+ }
+ }
+ else
+ {
+ size_t remainingComponents = 0;
+ if (ctorType.getStruct())
+ {
+ remainingComponents = ctorParameters.size();
+ }
+ else
+ {
+ remainingComponents = ctorType.getObjectSize();
+ }
+ size_t parameterIndex = 0;
+
+ while (remainingComponents > 0)
+ {
+ const TType &parameter = ctorParameters[parameterIndex];
+ const size_t parameterSize = parameter.getObjectSize();
+ bool moreParameters = parameterIndex + 1 < ctorParameters.size();
+
+ constructor += "x" + str(parameterIndex);
+
+ if (ctorType.getStruct())
+ {
+ ASSERT(remainingComponents == 1 || moreParameters);
+
+ --remainingComponents;
+ }
+ else if (parameter.isScalar())
+ {
+ remainingComponents -= parameter.getObjectSize();
+ }
+ else if (parameter.isVector())
+ {
+ if (remainingComponents == parameterSize || moreParameters)
+ {
+ ASSERT(parameterSize <= remainingComponents);
+ remainingComponents -= parameterSize;
+ }
+ else if (remainingComponents < static_cast<size_t>(parameter.getNominalSize()))
+ {
+ switch (remainingComponents)
+ {
+ case 1: constructor += ".x"; break;
+ case 2: constructor += ".xy"; break;
+ case 3: constructor += ".xyz"; break;
+ case 4: constructor += ".xyzw"; break;
+ default: UNREACHABLE();
+ }
+
+ remainingComponents = 0;
+ }
+ else UNREACHABLE();
+ }
+ else if (parameter.isMatrix())
+ {
+ int column = 0;
+ while (remainingComponents > 0 && column < parameter.getCols())
+ {
+ constructor += "[" + str(column) + "]";
+
+ if (remainingComponents < static_cast<size_t>(parameter.getRows()))
+ {
+ switch (remainingComponents)
+ {
+ case 1: constructor += ".x"; break;
+ case 2: constructor += ".xy"; break;
+ case 3: constructor += ".xyz"; break;
+ default: UNREACHABLE();
+ }
+
+ remainingComponents = 0;
+ }
+ else
+ {
+ remainingComponents -= parameter.getRows();
+
+ if (remainingComponents > 0)
+ {
+ constructor += ", x" + str(parameterIndex);
+ }
+ }
+
+ column++;
+ }
+ }
+ else UNREACHABLE();
+
+ if (moreParameters)
+ {
+ parameterIndex++;
+ }
+
+ if (remainingComponents)
+ {
+ constructor += ", ";
+ }
+ }
+ }
+
+ if (ctorType.getStruct())
+ {
+ if (!ctorParameters.empty())
+ {
+ constructor += "};\n";
+ }
+ constructor +=
+ " return structure;\n"
+ "}\n";
+ }
+ else
+ {
+ constructor += ");\n"
+ "}\n";
+ }
+
+ mConstructors.insert(constructor);
+
+ return constructorFunctionName;
+}
+
+std::string StructureHLSL::structsHeader() const
+{
+ TInfoSinkBase out;
+
+ for (size_t structIndex = 0; structIndex < mStructDeclarations.size(); structIndex++)
+ {
+ out << mStructDeclarations[structIndex];
+ }
+
+ for (Constructors::const_iterator constructor = mConstructors.begin();
+ constructor != mConstructors.end();
+ constructor++)
+ {
+ out << *constructor;
+ }
+
+ return out.str();
+}
+
+void StructureHLSL::storeStd140ElementIndex(const TStructure &structure, bool useHLSLRowMajorPacking)
+{
+ Std140PaddingHelper padHelper = getPaddingHelper();
+ const TFieldList &fields = structure.fields();
+
+ for (unsigned int i = 0; i < fields.size(); i++)
+ {
+ padHelper.prePadding(*fields[i]->type());
+ }
+
+ // Add remaining element index to the global map, for use with nested structs in standard layouts
+ const TString &structName = QualifiedStructNameString(structure, useHLSLRowMajorPacking, true);
+ mStd140StructElementIndexes[structName] = padHelper.elementIndex();
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/StructureHLSL.h b/gfx/angle/src/compiler/translator/StructureHLSL.h
new file mode 100755
index 000000000..96139ec9a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/StructureHLSL.h
@@ -0,0 +1,85 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// StructureHLSL.h:
+// Interfaces of methods for HLSL translation of GLSL structures.
+//
+
+#ifndef COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
+#define COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
+
+#include "compiler/translator/Common.h"
+#include "compiler/translator/IntermNode.h"
+
+#include <set>
+
+class TInfoSinkBase;
+class TScopeBracket;
+
+namespace sh
+{
+
+// This helper class assists structure and interface block definitions in determining
+// how to pack std140 structs within HLSL's packing rules.
+class Std140PaddingHelper
+{
+ public:
+ explicit Std140PaddingHelper(const std::map<TString, int> &structElementIndexes,
+ unsigned int *uniqueCounter);
+ Std140PaddingHelper(const Std140PaddingHelper &other);
+ Std140PaddingHelper &operator=(const Std140PaddingHelper &other);
+
+ int elementIndex() const { return mElementIndex; }
+ int prePadding(const TType &type);
+ TString prePaddingString(const TType &type);
+ TString postPaddingString(const TType &type, bool useHLSLRowMajorPacking);
+
+ private:
+ TString next();
+
+ unsigned *mPaddingCounter;
+ int mElementIndex;
+ const std::map<TString, int> *mStructElementIndexes;
+};
+
+class StructureHLSL : angle::NonCopyable
+{
+ public:
+ StructureHLSL();
+
+ // Returns the name of the constructor function. "name" parameter is the name of the type being
+ // constructed.
+ TString addConstructor(const TType &type,
+ const TString &name,
+ const TIntermSequence *parameters);
+ std::string structsHeader() const;
+
+ TString defineQualified(const TStructure &structure, bool useHLSLRowMajorPacking, bool useStd140Packing);
+ static TString defineNameless(const TStructure &structure);
+
+ Std140PaddingHelper getPaddingHelper();
+
+ private:
+ unsigned mUniquePaddingCounter;
+
+ std::map<TString, int> mStd140StructElementIndexes;
+
+ typedef std::set<TString> StructNames;
+ StructNames mStructNames;
+
+ typedef std::set<TString> Constructors;
+ Constructors mConstructors;
+
+ typedef std::vector<TString> StructDeclarations;
+ StructDeclarations mStructDeclarations;
+
+ void storeStd140ElementIndex(const TStructure &structure, bool useHLSLRowMajorPacking);
+ static TString define(const TStructure &structure, bool useHLSLRowMajorPacking,
+ bool useStd140Packing, Std140PaddingHelper *padHelper);
+};
+
+}
+
+#endif // COMPILER_TRANSLATOR_STRUCTUREHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/SymbolTable.cpp b/gfx/angle/src/compiler/translator/SymbolTable.cpp
new file mode 100755
index 000000000..188f810e3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SymbolTable.cpp
@@ -0,0 +1,371 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// Symbol table for parsing. Most functionaliy and main ideas
+// are documented in the header file.
+//
+
+#if defined(_MSC_VER)
+#pragma warning(disable: 4718)
+#endif
+
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/Cache.h"
+
+#include <stdio.h>
+#include <algorithm>
+
+namespace sh
+{
+
+int TSymbolTable::uniqueIdCounter = 0;
+
+TSymbol::TSymbol(const TString *n) : uniqueId(TSymbolTable::nextUniqueId()), name(n)
+{
+}
+
+//
+// Functions have buried pointers to delete.
+//
+TFunction::~TFunction()
+{
+ clearParameters();
+}
+
+void TFunction::clearParameters()
+{
+ for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i)
+ delete (*i).type;
+ parameters.clear();
+ mangledName = nullptr;
+}
+
+void TFunction::swapParameters(const TFunction &parametersSource)
+{
+ clearParameters();
+ for (auto parameter : parametersSource.parameters)
+ {
+ addParameter(parameter);
+ }
+}
+
+const TString *TFunction::buildMangledName() const
+{
+ std::string newName = mangleName(getName()).c_str();
+
+ for (const auto &p : parameters)
+ {
+ newName += p.type->getMangledName().c_str();
+ }
+
+ return NewPoolTString(newName.c_str());
+}
+
+//
+// Symbol table levels are a map of pointers to symbols that have to be deleted.
+//
+TSymbolTableLevel::~TSymbolTableLevel()
+{
+ for (tLevel::iterator it = level.begin(); it != level.end(); ++it)
+ delete (*it).second;
+}
+
+bool TSymbolTableLevel::insert(TSymbol *symbol)
+{
+ // returning true means symbol was added to the table
+ tInsertResult result = level.insert(tLevelPair(symbol->getMangledName(), symbol));
+
+ return result.second;
+}
+
+bool TSymbolTableLevel::insertUnmangled(TFunction *function)
+{
+ // returning true means symbol was added to the table
+ tInsertResult result = level.insert(tLevelPair(function->getName(), function));
+
+ return result.second;
+}
+
+TSymbol *TSymbolTableLevel::find(const TString &name) const
+{
+ tLevel::const_iterator it = level.find(name);
+ if (it == level.end())
+ return 0;
+ else
+ return (*it).second;
+}
+
+TSymbol *TSymbolTable::find(const TString &name, int shaderVersion,
+ bool *builtIn, bool *sameScope) const
+{
+ int level = currentLevel();
+ TSymbol *symbol;
+
+ do
+ {
+ if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+ level--;
+ if (level == ESSL3_BUILTINS && shaderVersion < 300)
+ level--;
+ if (level == ESSL1_BUILTINS && shaderVersion != 100)
+ level--;
+
+ symbol = table[level]->find(name);
+ }
+ while (symbol == 0 && --level >= 0);
+
+ if (builtIn)
+ *builtIn = (level <= LAST_BUILTIN_LEVEL);
+ if (sameScope)
+ *sameScope = (level == currentLevel());
+
+ return symbol;
+}
+
+TSymbol *TSymbolTable::findGlobal(const TString &name) const
+{
+ ASSERT(table.size() > GLOBAL_LEVEL);
+ return table[GLOBAL_LEVEL]->find(name);
+}
+
+TSymbol *TSymbolTable::findBuiltIn(
+ const TString &name, int shaderVersion) const
+{
+ for (int level = LAST_BUILTIN_LEVEL; level >= 0; level--)
+ {
+ if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+ level--;
+ if (level == ESSL3_BUILTINS && shaderVersion < 300)
+ level--;
+ if (level == ESSL1_BUILTINS && shaderVersion != 100)
+ level--;
+
+ TSymbol *symbol = table[level]->find(name);
+
+ if (symbol)
+ return symbol;
+ }
+
+ return 0;
+}
+
+TSymbolTable::~TSymbolTable()
+{
+ while (table.size() > 0)
+ pop();
+}
+
+bool IsGenType(const TType *type)
+{
+ if (type)
+ {
+ TBasicType basicType = type->getBasicType();
+ return basicType == EbtGenType || basicType == EbtGenIType || basicType == EbtGenUType || basicType == EbtGenBType;
+ }
+
+ return false;
+}
+
+bool IsVecType(const TType *type)
+{
+ if (type)
+ {
+ TBasicType basicType = type->getBasicType();
+ return basicType == EbtVec || basicType == EbtIVec || basicType == EbtUVec || basicType == EbtBVec;
+ }
+
+ return false;
+}
+
+const TType *SpecificType(const TType *type, int size)
+{
+ ASSERT(size >= 1 && size <= 4);
+
+ if (!type)
+ {
+ return nullptr;
+ }
+
+ ASSERT(!IsVecType(type));
+
+ switch(type->getBasicType())
+ {
+ case EbtGenType: return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
+ case EbtGenIType: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
+ case EbtGenUType: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
+ case EbtGenBType: return TCache::getType(EbtBool, static_cast<unsigned char>(size));
+ default: return type;
+ }
+}
+
+const TType *VectorType(const TType *type, int size)
+{
+ ASSERT(size >= 2 && size <= 4);
+
+ if (!type)
+ {
+ return nullptr;
+ }
+
+ ASSERT(!IsGenType(type));
+
+ switch(type->getBasicType())
+ {
+ case EbtVec: return TCache::getType(EbtFloat, static_cast<unsigned char>(size));
+ case EbtIVec: return TCache::getType(EbtInt, static_cast<unsigned char>(size));
+ case EbtUVec: return TCache::getType(EbtUInt, static_cast<unsigned char>(size));
+ case EbtBVec: return TCache::getType(EbtBool, static_cast<unsigned char>(size));
+ default: return type;
+ }
+}
+
+void TSymbolTable::insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name,
+ const TType *ptype1, const TType *ptype2, const TType *ptype3, const TType *ptype4, const TType *ptype5)
+{
+ if (ptype1->getBasicType() == EbtGSampler2D)
+ {
+ insertUnmangledBuiltIn(name);
+ bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2D), ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (ptype1->getBasicType() == EbtGSampler3D)
+ {
+ insertUnmangledBuiltIn(name);
+ bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler3D), ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (ptype1->getBasicType() == EbtGSamplerCube)
+ {
+ insertUnmangledBuiltIn(name);
+ bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSamplerCube), ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (ptype1->getBasicType() == EbtGSampler2DArray)
+ {
+ insertUnmangledBuiltIn(name);
+ bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name, TCache::getType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtInt, 4) : rvalue, name, TCache::getType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, gvec4 ? TCache::getType(EbtUInt, 4) : rvalue, name, TCache::getType(EbtUSampler2DArray), ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (IsGImage(ptype1->getBasicType()))
+ {
+ insertUnmangledBuiltIn(name);
+
+ const TType *floatType = TCache::getType(EbtFloat, 4);
+ const TType *intType = TCache::getType(EbtInt, 4);
+ const TType *unsignedType = TCache::getType(EbtUInt, 4);
+
+ const TType *floatImage =
+ TCache::getType(convertGImageToFloatImage(ptype1->getBasicType()));
+ const TType *intImage = TCache::getType(convertGImageToIntImage(ptype1->getBasicType()));
+ const TType *unsignedImage =
+ TCache::getType(convertGImageToUnsignedImage(ptype1->getBasicType()));
+
+ // GLSL ES 3.10, Revision 4, 8.12 Image Functions
+ if (rvalue->getBasicType() == EbtGVec4)
+ {
+ // imageLoad
+ insertBuiltIn(level, floatType, name, floatImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, intType, name, intImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, unsignedType, name, unsignedImage, ptype2, ptype3, ptype4, ptype5);
+ }
+ else if (rvalue->getBasicType() == EbtVoid)
+ {
+ // imageStore
+ insertBuiltIn(level, rvalue, name, floatImage, ptype2, floatType, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, intImage, ptype2, intType, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, unsignedImage, ptype2, unsignedType, ptype4, ptype5);
+ }
+ else
+ {
+ // imageSize
+ insertBuiltIn(level, rvalue, name, floatImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, intImage, ptype2, ptype3, ptype4, ptype5);
+ insertBuiltIn(level, rvalue, name, unsignedImage, ptype2, ptype3, ptype4, ptype5);
+ }
+ }
+ else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3))
+ {
+ ASSERT(!ptype4 && !ptype5);
+ insertUnmangledBuiltIn(name);
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 1), name, SpecificType(ptype1, 1), SpecificType(ptype2, 1), SpecificType(ptype3, 1));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 2), name, SpecificType(ptype1, 2), SpecificType(ptype2, 2), SpecificType(ptype3, 2));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 3), name, SpecificType(ptype1, 3), SpecificType(ptype2, 3), SpecificType(ptype3, 3));
+ insertBuiltIn(level, op, ext, SpecificType(rvalue, 4), name, SpecificType(ptype1, 4), SpecificType(ptype2, 4), SpecificType(ptype3, 4));
+ }
+ else if (IsVecType(rvalue) || IsVecType(ptype1) || IsVecType(ptype2) || IsVecType(ptype3))
+ {
+ ASSERT(!ptype4 && !ptype5);
+ insertUnmangledBuiltIn(name);
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 2), name, VectorType(ptype1, 2), VectorType(ptype2, 2), VectorType(ptype3, 2));
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 3), name, VectorType(ptype1, 3), VectorType(ptype2, 3), VectorType(ptype3, 3));
+ insertBuiltIn(level, op, ext, VectorType(rvalue, 4), name, VectorType(ptype1, 4), VectorType(ptype2, 4), VectorType(ptype3, 4));
+ }
+ else
+ {
+ TFunction *function = new TFunction(NewPoolTString(name), rvalue, op, ext);
+
+ function->addParameter(TConstParameter(ptype1));
+
+ if (ptype2)
+ {
+ function->addParameter(TConstParameter(ptype2));
+ }
+
+ if (ptype3)
+ {
+ function->addParameter(TConstParameter(ptype3));
+ }
+
+ if (ptype4)
+ {
+ function->addParameter(TConstParameter(ptype4));
+ }
+
+ if (ptype5)
+ {
+ function->addParameter(TConstParameter(ptype5));
+ }
+
+ ASSERT(hasUnmangledBuiltIn(name));
+ insert(level, function);
+ }
+}
+
+TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
+{
+ if (!SupportsPrecision(type))
+ return EbpUndefined;
+
+ // unsigned integers use the same precision as signed
+ TBasicType baseType = (type == EbtUInt) ? EbtInt : type;
+
+ int level = static_cast<int>(precisionStack.size()) - 1;
+ assert(level >= 0); // Just to be safe. Should not happen.
+ // If we dont find anything we return this. Some types don't have predefined default precision.
+ TPrecision prec = EbpUndefined;
+ while (level >= 0)
+ {
+ PrecisionStackLevel::iterator it = precisionStack[level]->find(baseType);
+ if (it != precisionStack[level]->end())
+ {
+ prec = (*it).second;
+ break;
+ }
+ level--;
+ }
+ return prec;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/SymbolTable.h b/gfx/angle/src/compiler/translator/SymbolTable.h
new file mode 100755
index 000000000..7c4d3aab3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/SymbolTable.h
@@ -0,0 +1,549 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_SYMBOLTABLE_H_
+#define COMPILER_TRANSLATOR_SYMBOLTABLE_H_
+
+//
+// Symbol table for parsing. Has these design characteristics:
+//
+// * Same symbol table can be used to compile many shaders, to preserve
+// effort of creating and loading with the large numbers of built-in
+// symbols.
+//
+// * Name mangling will be used to give each function a unique name
+// so that symbol table lookups are never ambiguous. This allows
+// a simpler symbol table structure.
+//
+// * Pushing and popping of scope, so symbol table will really be a stack
+// of symbol tables. Searched from the top, with new inserts going into
+// the top.
+//
+// * Constants: Compile time constant symbols will keep their values
+// in the symbol table. The parser can substitute constants at parse
+// time, including doing constant folding and constant propagation.
+//
+// * No temporaries: Temporaries made from operations (+, --, .xy, etc.)
+// are tracked in the intermediate representation, not the symbol table.
+//
+
+#include <array>
+#include <assert.h>
+#include <set>
+
+#include "common/angleutils.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+// Symbol base class. (Can build functions or variables out of these...)
+class TSymbol : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TSymbol(const TString *n);
+
+ virtual ~TSymbol()
+ {
+ // don't delete name, it's from the pool
+ }
+
+ const TString &getName() const
+ {
+ return *name;
+ }
+ virtual const TString &getMangledName() const
+ {
+ return getName();
+ }
+ virtual bool isFunction() const
+ {
+ return false;
+ }
+ virtual bool isVariable() const
+ {
+ return false;
+ }
+ int getUniqueId() const
+ {
+ return uniqueId;
+ }
+ void relateToExtension(const TString &ext)
+ {
+ extension = ext;
+ }
+ const TString &getExtension() const
+ {
+ return extension;
+ }
+
+ private:
+ const int uniqueId;
+ const TString *name;
+ TString extension;
+};
+
+// Variable class, meaning a symbol that's not a function.
+//
+// There could be a separate class heirarchy for Constant variables;
+// Only one of int, bool, or float, (or none) is correct for
+// any particular use, but it's easy to do this way, and doesn't
+// seem worth having separate classes, and "getConst" can't simply return
+// different values for different types polymorphically, so this is
+// just simple and pragmatic.
+class TVariable : public TSymbol
+{
+ public:
+ TVariable(const TString *name, const TType &t, bool uT = false)
+ : TSymbol(name),
+ type(t),
+ userType(uT),
+ unionArray(0)
+ {
+ }
+ ~TVariable() override {}
+ bool isVariable() const override { return true; }
+ TType &getType()
+ {
+ return type;
+ }
+ const TType &getType() const
+ {
+ return type;
+ }
+ bool isUserType() const
+ {
+ return userType;
+ }
+ void setQualifier(TQualifier qualifier)
+ {
+ type.setQualifier(qualifier);
+ }
+
+ const TConstantUnion *getConstPointer() const { return unionArray; }
+
+ void shareConstPointer(const TConstantUnion *constArray) { unionArray = constArray; }
+
+ private:
+ TType type;
+ bool userType;
+ // we are assuming that Pool Allocator will free the memory
+ // allocated to unionArray when this object is destroyed.
+ const TConstantUnion *unionArray;
+};
+
+// Immutable version of TParameter.
+struct TConstParameter
+{
+ TConstParameter()
+ : name(nullptr),
+ type(nullptr)
+ {
+ }
+ explicit TConstParameter(const TString *n)
+ : name(n),
+ type(nullptr)
+ {
+ }
+ explicit TConstParameter(const TType *t)
+ : name(nullptr),
+ type(t)
+ {
+ }
+ TConstParameter(const TString *n, const TType *t)
+ : name(n),
+ type(t)
+ {
+ }
+
+ // Both constructor arguments must be const.
+ TConstParameter(TString *n, TType *t) = delete;
+ TConstParameter(const TString *n, TType *t) = delete;
+ TConstParameter(TString *n, const TType *t) = delete;
+
+ const TString *name;
+ const TType *type;
+};
+
+// The function sub-class of symbols and the parser will need to
+// share this definition of a function parameter.
+struct TParameter
+{
+ // Destructively converts to TConstParameter.
+ // This method resets name and type to nullptrs to make sure
+ // their content cannot be modified after the call.
+ TConstParameter turnToConst()
+ {
+ const TString *constName = name;
+ const TType *constType = type;
+ name = nullptr;
+ type = nullptr;
+ return TConstParameter(constName, constType);
+ }
+
+ TString *name;
+ TType *type;
+};
+
+// The function sub-class of a symbol.
+class TFunction : public TSymbol
+{
+ public:
+ TFunction(const TString *name,
+ const TType *retType,
+ TOperator tOp = EOpNull,
+ const char *ext = "")
+ : TSymbol(name),
+ returnType(retType),
+ mangledName(nullptr),
+ op(tOp),
+ defined(false),
+ mHasPrototypeDeclaration(false)
+ {
+ relateToExtension(ext);
+ }
+ ~TFunction() override;
+ bool isFunction() const override { return true; }
+
+ static TString mangleName(const TString &name)
+ {
+ return name + '(';
+ }
+ static TString unmangleName(const TString &mangledName)
+ {
+ return TString(mangledName.c_str(), mangledName.find_first_of('('));
+ }
+
+ void addParameter(const TConstParameter &p)
+ {
+ parameters.push_back(p);
+ mangledName = nullptr;
+ }
+
+ void swapParameters(const TFunction &parametersSource);
+
+ const TString &getMangledName() const override
+ {
+ if (mangledName == nullptr)
+ {
+ mangledName = buildMangledName();
+ }
+ return *mangledName;
+ }
+ const TType &getReturnType() const
+ {
+ return *returnType;
+ }
+
+ TOperator getBuiltInOp() const
+ {
+ return op;
+ }
+
+ void setDefined() { defined = true; }
+ bool isDefined() { return defined; }
+ void setHasPrototypeDeclaration() { mHasPrototypeDeclaration = true; }
+ bool hasPrototypeDeclaration() const { return mHasPrototypeDeclaration; }
+
+ size_t getParamCount() const
+ {
+ return parameters.size();
+ }
+ const TConstParameter &getParam(size_t i) const
+ {
+ return parameters[i];
+ }
+
+ private:
+ void clearParameters();
+
+ const TString *buildMangledName() const;
+
+ typedef TVector<TConstParameter> TParamList;
+ TParamList parameters;
+ const TType *returnType;
+ mutable const TString *mangledName;
+ TOperator op;
+ bool defined;
+ bool mHasPrototypeDeclaration;
+};
+
+// Interface block name sub-symbol
+class TInterfaceBlockName : public TSymbol
+{
+ public:
+ TInterfaceBlockName(const TString *name)
+ : TSymbol(name)
+ {
+ }
+
+ virtual ~TInterfaceBlockName()
+ {
+ }
+};
+
+class TSymbolTableLevel
+{
+ public:
+ typedef TMap<TString, TSymbol *> tLevel;
+ typedef tLevel::const_iterator const_iterator;
+ typedef const tLevel::value_type tLevelPair;
+ typedef std::pair<tLevel::iterator, bool> tInsertResult;
+
+ TSymbolTableLevel()
+ : mGlobalInvariant(false)
+ {
+ }
+ ~TSymbolTableLevel();
+
+ bool insert(TSymbol *symbol);
+
+ // Insert a function using its unmangled name as the key.
+ bool insertUnmangled(TFunction *function);
+
+ TSymbol *find(const TString &name) const;
+
+ void addInvariantVarying(const std::string &name)
+ {
+ mInvariantVaryings.insert(name);
+ }
+
+ bool isVaryingInvariant(const std::string &name)
+ {
+ return (mGlobalInvariant || mInvariantVaryings.count(name) > 0);
+ }
+
+ void setGlobalInvariant(bool invariant) { mGlobalInvariant = invariant; }
+
+ protected:
+ tLevel level;
+ std::set<std::string> mInvariantVaryings;
+ bool mGlobalInvariant;
+};
+
+// Define ESymbolLevel as int rather than an enum since level can go
+// above GLOBAL_LEVEL and cause atBuiltInLevel() to fail if the
+// compiler optimizes the >= of the last element to ==.
+typedef int ESymbolLevel;
+const int COMMON_BUILTINS = 0;
+const int ESSL1_BUILTINS = 1;
+const int ESSL3_BUILTINS = 2;
+const int ESSL3_1_BUILTINS = 3;
+const int LAST_BUILTIN_LEVEL = ESSL3_1_BUILTINS;
+const int GLOBAL_LEVEL = 4;
+
+class TSymbolTable : angle::NonCopyable
+{
+ public:
+ TSymbolTable()
+ {
+ // The symbol table cannot be used until push() is called, but
+ // the lack of an initial call to push() can be used to detect
+ // that the symbol table has not been preloaded with built-ins.
+ }
+
+ ~TSymbolTable();
+
+ // When the symbol table is initialized with the built-ins, there should
+ // 'push' calls, so that built-ins are at level 0 and the shader
+ // globals are at level 1.
+ bool isEmpty() const
+ {
+ return table.empty();
+ }
+ bool atBuiltInLevel() const
+ {
+ return currentLevel() <= LAST_BUILTIN_LEVEL;
+ }
+ bool atGlobalLevel() const
+ {
+ return currentLevel() == GLOBAL_LEVEL;
+ }
+ void push()
+ {
+ table.push_back(new TSymbolTableLevel);
+ precisionStack.push_back(new PrecisionStackLevel);
+ }
+
+ void pop()
+ {
+ delete table.back();
+ table.pop_back();
+
+ delete precisionStack.back();
+ precisionStack.pop_back();
+ }
+
+ bool declare(TSymbol *symbol)
+ {
+ return insert(currentLevel(), symbol);
+ }
+
+ bool insert(ESymbolLevel level, TSymbol *symbol)
+ {
+ return table[level]->insert(symbol);
+ }
+
+ bool insert(ESymbolLevel level, const char *ext, TSymbol *symbol)
+ {
+ symbol->relateToExtension(ext);
+ return table[level]->insert(symbol);
+ }
+
+ bool insertConstInt(ESymbolLevel level, const char *name, int value, TPrecision precision)
+ {
+ TVariable *constant =
+ new TVariable(NewPoolTString(name), TType(EbtInt, precision, EvqConst, 1));
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray[0].setIConst(value);
+ constant->shareConstPointer(unionArray);
+ return insert(level, constant);
+ }
+
+ bool insertConstIntExt(ESymbolLevel level, const char *ext, const char *name, int value)
+ {
+ TVariable *constant =
+ new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1));
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray[0].setIConst(value);
+ constant->shareConstPointer(unionArray);
+ return insert(level, ext, constant);
+ }
+
+ bool insertConstIvec3(ESymbolLevel level,
+ const char *name,
+ const std::array<int, 3> &values,
+ TPrecision precision)
+ {
+ TVariable *constantIvec3 =
+ new TVariable(NewPoolTString(name), TType(EbtInt, precision, EvqConst, 3));
+
+ TConstantUnion *unionArray = new TConstantUnion[3];
+ for (size_t index = 0u; index < 3u; ++index)
+ {
+ unionArray[index].setIConst(values[index]);
+ }
+ constantIvec3->shareConstPointer(unionArray);
+
+ return insert(level, constantIvec3);
+ }
+
+ void insertBuiltIn(ESymbolLevel level, TOperator op, const char *ext, const TType *rvalue, const char *name,
+ const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0);
+
+ void insertBuiltIn(ESymbolLevel level, const TType *rvalue, const char *name,
+ const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
+ {
+ insertUnmangledBuiltIn(name);
+ insertBuiltIn(level, EOpNull, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
+ }
+
+ void insertBuiltIn(ESymbolLevel level, const char *ext, const TType *rvalue, const char *name,
+ const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
+ {
+ insertUnmangledBuiltIn(name);
+ insertBuiltIn(level, EOpNull, ext, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
+ }
+
+ void insertBuiltIn(ESymbolLevel level, TOperator op, const TType *rvalue, const char *name,
+ const TType *ptype1, const TType *ptype2 = 0, const TType *ptype3 = 0, const TType *ptype4 = 0, const TType *ptype5 = 0)
+ {
+ insertUnmangledBuiltIn(name);
+ insertBuiltIn(level, op, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
+ }
+
+ TSymbol *find(const TString &name, int shaderVersion,
+ bool *builtIn = NULL, bool *sameScope = NULL) const;
+
+ TSymbol *findGlobal(const TString &name) const;
+
+ TSymbol *findBuiltIn(const TString &name, int shaderVersion) const;
+
+ TSymbolTableLevel *getOuterLevel()
+ {
+ assert(currentLevel() >= 1);
+ return table[currentLevel() - 1];
+ }
+
+ void dump(TInfoSink &infoSink) const;
+
+ bool setDefaultPrecision(const TPublicType &type, TPrecision prec)
+ {
+ if (!SupportsPrecision(type.getBasicType()))
+ return false;
+ if (type.getBasicType() == EbtUInt)
+ return false; // ESSL 3.00.4 section 4.5.4
+ if (type.isAggregate())
+ return false; // Not allowed to set for aggregate types
+ int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
+ // Uses map operator [], overwrites the current value
+ (*precisionStack[indexOfLastElement])[type.getBasicType()] = prec;
+ return true;
+ }
+
+ // Searches down the precisionStack for a precision qualifier
+ // for the specified TBasicType
+ TPrecision getDefaultPrecision(TBasicType type) const;
+
+ // This records invariant varyings declared through
+ // "invariant varying_name;".
+ void addInvariantVarying(const std::string &originalName)
+ {
+ ASSERT(atGlobalLevel());
+ table[currentLevel()]->addInvariantVarying(originalName);
+ }
+ // If this returns false, the varying could still be invariant
+ // if it is set as invariant during the varying variable
+ // declaration - this piece of information is stored in the
+ // variable's type, not here.
+ bool isVaryingInvariant(const std::string &originalName) const
+ {
+ ASSERT(atGlobalLevel());
+ return table[currentLevel()]->isVaryingInvariant(originalName);
+ }
+
+ void setGlobalInvariant(bool invariant)
+ {
+ ASSERT(atGlobalLevel());
+ table[currentLevel()]->setGlobalInvariant(invariant);
+ }
+
+ static int nextUniqueId()
+ {
+ return ++uniqueIdCounter;
+ }
+
+ bool hasUnmangledBuiltIn(const char *name)
+ {
+ return mUnmangledBuiltinNames.count(std::string(name)) > 0;
+ }
+
+ private:
+ ESymbolLevel currentLevel() const
+ {
+ return static_cast<ESymbolLevel>(table.size() - 1);
+ }
+
+ // Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00.
+ void insertUnmangledBuiltIn(const char *name)
+ {
+ mUnmangledBuiltinNames.insert(std::string(name));
+ }
+
+ std::vector<TSymbolTableLevel *> table;
+ typedef TMap<TBasicType, TPrecision> PrecisionStackLevel;
+ std::vector< PrecisionStackLevel *> precisionStack;
+
+ std::set<std::string> mUnmangledBuiltinNames;
+
+ static int uniqueIdCounter;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_SYMBOLTABLE_H_
diff --git a/gfx/angle/src/compiler/translator/TextureFunctionHLSL.cpp b/gfx/angle/src/compiler/translator/TextureFunctionHLSL.cpp
new file mode 100755
index 000000000..33d098531
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TextureFunctionHLSL.cpp
@@ -0,0 +1,1274 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
+// output. Some of the implementations are straightforward and just call the HLSL equivalent of the
+// ESSL texture function, others do more work to emulate ESSL texture sampling or size query
+// behavior.
+//
+
+#include "compiler/translator/TextureFunctionHLSL.h"
+
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void OutputIntTexCoordWrap(TInfoSinkBase &out,
+ const char *wrapMode,
+ const char *size,
+ const TString &texCoord,
+ const TString &texCoordOffset,
+ const char *texCoordOutName)
+{
+ // GLES 3.0.4 table 3.22 specifies how the wrap modes work. We don't use the formulas verbatim
+ // but rather use equivalent formulas that map better to HLSL.
+ out << "int " << texCoordOutName << ";\n";
+ out << "float " << texCoordOutName << "Offset = " << texCoord << " + float(" << texCoordOffset
+ << ") / " << size << ";\n";
+
+ // CLAMP_TO_EDGE
+ out << "if (" << wrapMode << " == 1)\n";
+ out << "{\n";
+ out << " " << texCoordOutName << " = clamp(int(floor(" << size << " * " << texCoordOutName
+ << "Offset)), 0, int(" << size << ") - 1);\n";
+ out << "}\n";
+
+ // MIRRORED_REPEAT
+ out << "else if (" << wrapMode << " == 3)\n";
+ out << "{\n";
+ out << " float coordWrapped = 1.0 - abs(frac(abs(" << texCoordOutName
+ << "Offset) * 0.5) * 2.0 - 1.0);\n";
+ out << " " << texCoordOutName << " = int(floor(" << size << " * coordWrapped));\n";
+ out << "}\n";
+
+ // REPEAT
+ out << "else\n";
+ out << "{\n";
+ out << " " << texCoordOutName << " = int(floor(" << size << " * frac(" << texCoordOutName
+ << "Offset)));\n";
+ out << "}\n";
+}
+
+void OutputIntTexCoordWraps(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ // Convert from normalized floating-point to integer
+ out << "int wrapS = samplerMetadata[samplerIndex].wrapModes & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "offset.x", "tix");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapS", "width", *texCoordX, "0", "tix");
+ }
+ *texCoordX = "tix";
+ out << "int wrapT = (samplerMetadata[samplerIndex].wrapModes >> 2) & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "offset.y", "tiy");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapT", "height", *texCoordY, "0", "tiy");
+ }
+ *texCoordY = "tiy";
+
+ if (IsSamplerArray(textureFunction.sampler))
+ {
+ *texCoordZ = "int(max(0, min(layers - 1, floor(0.5 + t.z))))";
+ }
+ else if (!IsSamplerCube(textureFunction.sampler) && !IsSampler2D(textureFunction.sampler))
+ {
+ out << "int wrapR = (samplerMetadata[samplerIndex].wrapModes >> 4) & 0x3;\n";
+ if (textureFunction.offset)
+ {
+ OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "offset.z", "tiz");
+ }
+ else
+ {
+ OutputIntTexCoordWrap(out, "wrapR", "depth", *texCoordZ, "0", "tiz");
+ }
+ *texCoordZ = "tiz";
+ }
+}
+
+void OutputHLSL4SampleFunctionPrefix(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const TString &textureReference,
+ const TString &samplerReference)
+{
+ out << textureReference;
+ if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ out << ".Load(";
+ return;
+ }
+
+ if (IsShadowSampler(textureFunction.sampler))
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ".SampleCmp(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ out << ".SampleCmpLevelZero(";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << ".Sample(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << ".SampleBias(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << ".SampleLevel(";
+ break;
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ out << ".SampleGrad(";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ out << samplerReference << ", ";
+}
+
+const char *GetSamplerCoordinateTypeString(
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ int hlslCoords)
+{
+ if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ switch (hlslCoords)
+ {
+ case 2:
+ return "int3";
+ case 3:
+ return "int4";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (hlslCoords)
+ {
+ case 2:
+ return "float2";
+ case 3:
+ return "float3";
+ case 4:
+ return "float4";
+ default:
+ UNREACHABLE();
+ }
+ }
+ return "";
+}
+
+int GetHLSLCoordCount(const TextureFunctionHLSL::TextureFunction &textureFunction,
+ ShShaderOutput outputType)
+{
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ int hlslCoords = 2;
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ hlslCoords = 2;
+ break;
+ case EbtSamplerCube:
+ hlslCoords = 3;
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ return hlslCoords;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ return 4;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ return 2;
+ case EbtSampler3D:
+ return 3;
+ case EbtSamplerCube:
+ return 3;
+ case EbtSampler2DArray:
+ return 3;
+ case EbtSamplerExternalOES:
+ return 2;
+ case EbtISampler2D:
+ return 2;
+ case EbtISampler3D:
+ return 3;
+ case EbtISamplerCube:
+ return 3;
+ case EbtISampler2DArray:
+ return 3;
+ case EbtUSampler2D:
+ return 2;
+ case EbtUSampler3D:
+ return 3;
+ case EbtUSamplerCube:
+ return 3;
+ case EbtUSampler2DArray:
+ return 3;
+ case EbtSampler2DShadow:
+ return 2;
+ case EbtSamplerCubeShadow:
+ return 3;
+ case EbtSampler2DArrayShadow:
+ return 3;
+ default:
+ UNREACHABLE();
+ }
+ }
+ return 0;
+}
+
+void OutputTextureFunctionArgumentList(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType)
+{
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ out << "sampler2D s";
+ break;
+ case EbtSamplerCube:
+ out << "samplerCUBE s";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else
+ {
+ if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ out << TextureString(textureFunction.sampler) << " x, "
+ << SamplerString(textureFunction.sampler) << " s";
+ }
+ else
+ {
+ ASSERT(outputType == SH_HLSL_4_1_OUTPUT);
+ out << "const uint samplerIndex";
+ }
+ }
+
+ if (textureFunction.method ==
+ TextureFunctionHLSL::TextureFunction::FETCH) // Integer coordinates
+ {
+ switch (textureFunction.coords)
+ {
+ case 2:
+ out << ", int2 t";
+ break;
+ case 3:
+ out << ", int3 t";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else // Floating-point coordinates (except textureSize)
+ {
+ switch (textureFunction.coords)
+ {
+ case 1:
+ out << ", int lod";
+ break; // textureSize()
+ case 2:
+ out << ", float2 t";
+ break;
+ case 3:
+ out << ", float3 t";
+ break;
+ case 4:
+ out << ", float4 t";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSamplerExternalOES:
+ out << ", float2 ddx, float2 ddy";
+ break;
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ out << ", float3 ddx, float3 ddy";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ break; // Comes after the offset parameter
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ", float lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ break; // Comes after the offset parameter
+ case TextureFunctionHLSL::TextureFunction::SIZE:
+ break;
+ case TextureFunctionHLSL::TextureFunction::FETCH:
+ out << ", int mip";
+ break;
+ case TextureFunctionHLSL::TextureFunction::GRAD:
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ if (textureFunction.offset)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ out << ", int3 offset";
+ break;
+ case EbtSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2D:
+ case EbtISampler2DArray:
+ case EbtUSampler2D:
+ case EbtUSampler2DArray:
+ case EbtSampler2DShadow:
+ case EbtSampler2DArrayShadow:
+ case EbtSamplerExternalOES:
+ out << ", int2 offset";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << ", float bias";
+ }
+}
+
+void GetTextureReference(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ TString *textureReference,
+ TString *samplerReference)
+{
+ if (outputType == SH_HLSL_4_1_OUTPUT)
+ {
+ TString suffix = TextureGroupSuffix(textureFunction.sampler);
+ if (TextureGroup(textureFunction.sampler) == HLSL_TEXTURE_2D)
+ {
+ *textureReference = TString("textures") + suffix + "[samplerIndex]";
+ *samplerReference = TString("samplers") + suffix + "[samplerIndex]";
+ }
+ else
+ {
+ out << " const uint textureIndex = samplerIndex - textureIndexOffset" << suffix
+ << ";\n";
+ *textureReference = TString("textures") + suffix + "[textureIndex]";
+ out << " const uint samplerArrayIndex = samplerIndex - samplerIndexOffset" << suffix
+ << ";\n";
+ *samplerReference = TString("samplers") + suffix + "[samplerArrayIndex]";
+ }
+ }
+ else
+ {
+ *textureReference = "x";
+ *samplerReference = "s";
+ }
+}
+
+void OutputTextureSizeFunctionBody(TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const TString &textureReference,
+ bool getDimensionsIgnoresBaseLevel)
+{
+ if (getDimensionsIgnoresBaseLevel)
+ {
+ out << "int baseLevel = samplerMetadata[samplerIndex].baseLevel;\n";
+ }
+ else
+ {
+ out << "int baseLevel = 0;\n";
+ }
+
+ if (IsSampler3D(textureFunction.sampler) || IsSamplerArray(textureFunction.sampler) ||
+ (IsIntegerSampler(textureFunction.sampler) && IsSamplerCube(textureFunction.sampler)))
+ {
+ // "depth" stores either the number of layers in an array texture or 3D depth
+ out << " uint width; uint height; uint depth; uint numberOfLevels;\n"
+ << " " << textureReference
+ << ".GetDimensions(baseLevel, width, height, depth, numberOfLevels);\n"
+ << " width = max(width >> lod, 1);\n"
+ << " height = max(height >> lod, 1);\n";
+
+ if (!IsSamplerArray(textureFunction.sampler))
+ {
+ out << " depth = max(depth >> lod, 1);\n";
+ }
+ }
+ else if (IsSampler2D(textureFunction.sampler) || IsSamplerCube(textureFunction.sampler))
+ {
+ out << " uint width; uint height; uint numberOfLevels;\n"
+ << " " << textureReference
+ << ".GetDimensions(baseLevel, width, height, numberOfLevels);\n"
+ << " width = max(width >> lod, 1);\n"
+ << " height = max(height >> lod, 1);\n";
+ }
+ else
+ UNREACHABLE();
+
+ if (strcmp(textureFunction.getReturnType(), "int3") == 0)
+ {
+ out << " return int3(width, height, depth);";
+ }
+ else
+ {
+ out << " return int2(width, height);";
+ }
+}
+
+void ProjectTextureCoordinates(const TextureFunctionHLSL::TextureFunction &textureFunction,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ if (textureFunction.proj)
+ {
+ TString proj("");
+ switch (textureFunction.coords)
+ {
+ case 3:
+ proj = " / t.z";
+ break;
+ case 4:
+ proj = " / t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ *texCoordX = "(" + *texCoordX + proj + ")";
+ *texCoordY = "(" + *texCoordY + proj + ")";
+ *texCoordZ = "(" + *texCoordZ + proj + ")";
+ }
+}
+
+void OutputIntegerTextureSampleFunctionComputations(
+ TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ const TString &textureReference,
+ TString *texCoordX,
+ TString *texCoordY,
+ TString *texCoordZ)
+{
+ if (!IsIntegerSampler(textureFunction.sampler))
+ {
+ return;
+ }
+ if (IsSamplerCube(textureFunction.sampler))
+ {
+ out << " float width; float height; float layers; float levels;\n";
+
+ out << " uint mip = 0;\n";
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+
+ out << " bool xMajor = abs(t.x) > abs(t.y) && abs(t.x) > abs(t.z);\n";
+ out << " bool yMajor = abs(t.y) > abs(t.z) && abs(t.y) > abs(t.x);\n";
+ out << " bool zMajor = abs(t.z) > abs(t.x) && abs(t.z) > abs(t.y);\n";
+ out << " bool negative = (xMajor && t.x < 0.0f) || (yMajor && t.y < 0.0f) || "
+ "(zMajor && t.z < 0.0f);\n";
+
+ // FACE_POSITIVE_X = 000b
+ // FACE_NEGATIVE_X = 001b
+ // FACE_POSITIVE_Y = 010b
+ // FACE_NEGATIVE_Y = 011b
+ // FACE_POSITIVE_Z = 100b
+ // FACE_NEGATIVE_Z = 101b
+ out << " int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
+
+ out << " float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
+ out << " float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
+ out << " float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
+
+ out << " t.x = (u * 0.5f / m) + 0.5f;\n";
+ out << " t.y = (v * 0.5f / m) + 0.5f;\n";
+
+ // Mip level computation.
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float2 dx = ddx(tSized);\n"
+ " float2 dy = ddy(tSized);\n"
+ " float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ // ESSL 3.00.6 spec section 8.8: "For the cube version, the partial
+ // derivatives of P are assumed to be in the coordinate system used before
+ // texture coordinates are projected onto the appropriate cube face."
+ // ddx[0] and ddy[0] are the derivatives of t.x passed into the function
+ // ddx[1] and ddy[1] are the derivatives of t.y passed into the function
+ // ddx[2] and ddy[2] are the derivatives of t.z passed into the function
+ // Determine the derivatives of u, v and m
+ out << " float dudx = xMajor ? ddx[2] : (yMajor && t.y < 0.0f ? -ddx[0] "
+ ": ddx[0]);\n"
+ " float dudy = xMajor ? ddy[2] : (yMajor && t.y < 0.0f ? -ddy[0] "
+ ": ddy[0]);\n"
+ " float dvdx = yMajor ? ddx[2] : (negative ? ddx[1] : -ddx[1]);\n"
+ " float dvdy = yMajor ? ddy[2] : (negative ? ddy[1] : -ddy[1]);\n"
+ " float dmdx = xMajor ? ddx[0] : (yMajor ? ddx[1] : ddx[2]);\n"
+ " float dmdy = xMajor ? ddy[0] : (yMajor ? ddy[1] : ddy[2]);\n";
+ // Now determine the derivatives of the face coordinates, using the
+ // derivatives calculated above.
+ // d / dx (u(x) * 0.5 / m(x) + 0.5)
+ // = 0.5 * (m(x) * u'(x) - u(x) * m'(x)) / m(x)^2
+ out << " float dfacexdx = 0.5f * (m * dudx - u * dmdx) / (m * m);\n"
+ " float dfaceydx = 0.5f * (m * dvdx - v * dmdx) / (m * m);\n"
+ " float dfacexdy = 0.5f * (m * dudy - u * dmdy) / (m * m);\n"
+ " float dfaceydy = 0.5f * (m * dvdy - v * dmdy) / (m * m);\n"
+ " float2 sizeVec = float2(width, height);\n"
+ " float2 faceddx = float2(dfacexdx, dfaceydx) * sizeVec;\n"
+ " float2 faceddy = float2(dfacexdy, dfaceydy) * sizeVec;\n";
+ // Optimization: instead of: log2(max(length(faceddx), length(faceddy)))
+ // we compute: log2(max(length(faceddx)^2, length(faceddy)^2)) / 2
+ out << " float lengthfaceddx2 = dot(faceddx, faceddx);\n"
+ " float lengthfaceddy2 = dot(faceddy, faceddy);\n"
+ " float lod = log2(max(lengthfaceddx2, lengthfaceddy2)) * 0.5f;\n";
+ }
+ out << " mip = uint(min(max(round(lod), 0), levels - 1));\n"
+ << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+ }
+
+ // Convert from normalized floating-point to integer
+ *texCoordX = "int(floor(width * frac(" + *texCoordX + ")))";
+ *texCoordY = "int(floor(height * frac(" + *texCoordY + ")))";
+ *texCoordZ = "face";
+ }
+ else if (textureFunction.method != TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ if (IsSampler2D(textureFunction.sampler))
+ {
+ if (IsSamplerArray(textureFunction.sampler))
+ {
+ out << " float width; float height; float layers; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, layers, levels);\n";
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float2 sizeVec = float2(width, height);\n"
+ " float2 sizeDdx = ddx * sizeVec;\n"
+ " float2 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), "
+ "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, layers, levels);\n";
+ }
+ else
+ {
+ out << " float width; float height; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, levels);\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float2 tSized = float2(t.x * width, t.y * height);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float2 sizeVec = float2(width, height);\n"
+ " float2 sizeDdx = ddx * sizeVec;\n"
+ " float2 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), "
+ "dot(sizeDdy, sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, levels);\n";
+ }
+ }
+ else if (IsSampler3D(textureFunction.sampler))
+ {
+ out << " float width; float height; float depth; float levels;\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0)
+ {
+ out << " uint mip = 0;\n";
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::LOD0BIAS)
+ {
+ out << " uint mip = bias;\n";
+ }
+ else
+ {
+ out << " " << textureReference
+ << ".GetDimensions(0, width, height, depth, levels);\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::IMPLICIT ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " float3 tSized = float3(t.x * width, t.y * height, t.z * depth);\n"
+ " float dx = length(ddx(tSized));\n"
+ " float dy = length(ddy(tSized));\n"
+ " float lod = log2(max(dx, dy));\n";
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::BIAS)
+ {
+ out << " lod += bias;\n";
+ }
+ }
+ else if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ out << " float3 sizeVec = float3(width, height, depth);\n"
+ " float3 sizeDdx = ddx * sizeVec;\n"
+ " float3 sizeDdy = ddy * sizeVec;\n"
+ " float lod = log2(max(dot(sizeDdx, sizeDdx), dot(sizeDdy, "
+ "sizeDdy))) * 0.5f;\n";
+ }
+
+ out << " uint mip = uint(min(max(round(lod), 0), levels - 1));\n";
+ }
+
+ out << " " << textureReference
+ << ".GetDimensions(mip, width, height, depth, levels);\n";
+ }
+ else
+ UNREACHABLE();
+
+ OutputIntTexCoordWraps(out, textureFunction, texCoordX, texCoordY, texCoordZ);
+ }
+}
+
+void OutputTextureSampleFunctionReturnStatement(
+ TInfoSinkBase &out,
+ const TextureFunctionHLSL::TextureFunction &textureFunction,
+ const ShShaderOutput outputType,
+ const TString &textureReference,
+ const TString &samplerReference,
+ const TString &texCoordX,
+ const TString &texCoordY,
+ const TString &texCoordZ)
+{
+ out << " return ";
+
+ // HLSL intrinsic
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ switch (textureFunction.sampler)
+ {
+ case EbtSampler2D:
+ case EbtSamplerExternalOES:
+ out << "tex2D";
+ break;
+ case EbtSamplerCube:
+ out << "texCUBE";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << "(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << "bias(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << "lod(" << samplerReference << ", ";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ OutputHLSL4SampleFunctionPrefix(out, textureFunction, textureReference, samplerReference);
+ }
+ else
+ UNREACHABLE();
+
+ const int hlslCoords = GetHLSLCoordCount(textureFunction, outputType);
+
+ out << GetSamplerCoordinateTypeString(textureFunction, hlslCoords) << "(" << texCoordX << ", "
+ << texCoordY;
+
+ if (outputType == SH_HLSL_3_0_OUTPUT)
+ {
+ if (hlslCoords >= 3)
+ {
+ if (textureFunction.coords < 3)
+ {
+ out << ", 0";
+ }
+ else
+ {
+ out << ", " << texCoordZ;
+ }
+ }
+
+ if (hlslCoords == 4)
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << ", bias";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << ", lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << ", 0";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << ", bias";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ out << ")";
+ }
+ else if (outputType == SH_HLSL_4_1_OUTPUT || outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ if (hlslCoords >= 3)
+ {
+ ASSERT(!IsIntegerSampler(textureFunction.sampler) ||
+ !IsSamplerCube(textureFunction.sampler) || texCoordZ == "face");
+ out << ", " << texCoordZ;
+ }
+
+ if (textureFunction.method == TextureFunctionHLSL::TextureFunction::GRAD)
+ {
+ if (IsIntegerSampler(textureFunction.sampler))
+ {
+ out << ", mip)";
+ }
+ else if (IsShadowSampler(textureFunction.sampler))
+ {
+ // Compare value
+ if (textureFunction.proj)
+ {
+ // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+ // The resulting third component of P' in the shadow forms is used as
+ // Dref
+ out << "), " << texCoordZ;
+ }
+ else
+ {
+ switch (textureFunction.coords)
+ {
+ case 3:
+ out << "), t.z";
+ break;
+ case 4:
+ out << "), t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ else
+ {
+ out << "), ddx, ddy";
+ }
+ }
+ else if (IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH)
+ {
+ out << ", mip)";
+ }
+ else if (IsShadowSampler(textureFunction.sampler))
+ {
+ // Compare value
+ if (textureFunction.proj)
+ {
+ // According to ESSL 3.00.4 sec 8.8 p95 on textureProj:
+ // The resulting third component of P' in the shadow forms is used as Dref
+ out << "), " << texCoordZ;
+ }
+ else
+ {
+ switch (textureFunction.coords)
+ {
+ case 3:
+ out << "), t.z";
+ break;
+ case 4:
+ out << "), t.w";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+ }
+ else
+ {
+ switch (textureFunction.method)
+ {
+ case TextureFunctionHLSL::TextureFunction::IMPLICIT:
+ out << ")";
+ break;
+ case TextureFunctionHLSL::TextureFunction::BIAS:
+ out << "), bias";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD:
+ out << "), lod";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0:
+ out << "), 0";
+ break;
+ case TextureFunctionHLSL::TextureFunction::LOD0BIAS:
+ out << "), bias";
+ break;
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ if (textureFunction.offset &&
+ (!IsIntegerSampler(textureFunction.sampler) ||
+ textureFunction.method == TextureFunctionHLSL::TextureFunction::FETCH))
+ {
+ out << ", offset";
+ }
+ }
+ else
+ UNREACHABLE();
+
+ out << ");\n"; // Close the sample function call and return statement
+}
+
+} // Anonymous namespace
+
+TString TextureFunctionHLSL::TextureFunction::name() const
+{
+ TString name = "gl_texture";
+
+ // We need to include full the sampler type in the function name to make the signature unique
+ // on D3D11, where samplers are passed to texture functions as indices.
+ name += TextureTypeSuffix(this->sampler);
+
+ if (proj)
+ {
+ name += "Proj";
+ }
+
+ if (offset)
+ {
+ name += "Offset";
+ }
+
+ switch (method)
+ {
+ case IMPLICIT:
+ break;
+ case BIAS:
+ break; // Extra parameter makes the signature unique
+ case LOD:
+ name += "Lod";
+ break;
+ case LOD0:
+ name += "Lod0";
+ break;
+ case LOD0BIAS:
+ name += "Lod0";
+ break; // Extra parameter makes the signature unique
+ case SIZE:
+ name += "Size";
+ break;
+ case FETCH:
+ name += "Fetch";
+ break;
+ case GRAD:
+ name += "Grad";
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ return name;
+}
+
+const char *TextureFunctionHLSL::TextureFunction::getReturnType() const
+{
+ if (method == TextureFunction::SIZE)
+ {
+ switch (sampler)
+ {
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DShadow:
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ case EbtSamplerCubeShadow:
+ case EbtSamplerExternalOES:
+ return "int2";
+ case EbtSampler3D:
+ case EbtISampler3D:
+ case EbtUSampler3D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ case EbtSampler2DArrayShadow:
+ return "int3";
+ default:
+ UNREACHABLE();
+ }
+ }
+ else // Sampling function
+ {
+ switch (sampler)
+ {
+ case EbtSampler2D:
+ case EbtSampler3D:
+ case EbtSamplerCube:
+ case EbtSampler2DArray:
+ case EbtSamplerExternalOES:
+ return "float4";
+ case EbtISampler2D:
+ case EbtISampler3D:
+ case EbtISamplerCube:
+ case EbtISampler2DArray:
+ return "int4";
+ case EbtUSampler2D:
+ case EbtUSampler3D:
+ case EbtUSamplerCube:
+ case EbtUSampler2DArray:
+ return "uint4";
+ case EbtSampler2DShadow:
+ case EbtSamplerCubeShadow:
+ case EbtSampler2DArrayShadow:
+ return "float";
+ default:
+ UNREACHABLE();
+ }
+ }
+ return "";
+}
+
+bool TextureFunctionHLSL::TextureFunction::operator<(const TextureFunction &rhs) const
+{
+ return std::tie(sampler, coords, proj, offset, method) <
+ std::tie(rhs.sampler, rhs.coords, rhs.proj, rhs.offset, rhs.method);
+}
+
+TString TextureFunctionHLSL::useTextureFunction(const TString &name,
+ TBasicType samplerType,
+ int coords,
+ size_t argumentCount,
+ bool lod0,
+ sh::GLenum shaderType)
+{
+ TextureFunction textureFunction;
+ textureFunction.sampler = samplerType;
+ textureFunction.coords = coords;
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.proj = false;
+ textureFunction.offset = false;
+
+ if (name == "texture2D" || name == "textureCube" || name == "texture")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ }
+ else if (name == "texture2DProj" || name == "textureProj")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.proj = true;
+ }
+ else if (name == "texture2DLod" || name == "textureCubeLod" || name == "textureLod" ||
+ name == "texture2DLodEXT" || name == "textureCubeLodEXT")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ }
+ else if (name == "texture2DProjLod" || name == "textureProjLod" ||
+ name == "texture2DProjLodEXT")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureSize")
+ {
+ textureFunction.method = TextureFunction::SIZE;
+ }
+ else if (name == "textureOffset")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjOffset")
+ {
+ textureFunction.method = TextureFunction::IMPLICIT;
+ textureFunction.offset = true;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureLodOffset")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjLodOffset")
+ {
+ textureFunction.method = TextureFunction::LOD;
+ textureFunction.proj = true;
+ textureFunction.offset = true;
+ }
+ else if (name == "texelFetch")
+ {
+ textureFunction.method = TextureFunction::FETCH;
+ }
+ else if (name == "texelFetchOffset")
+ {
+ textureFunction.method = TextureFunction::FETCH;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureGrad" || name == "texture2DGradEXT")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ }
+ else if (name == "textureGradOffset")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.offset = true;
+ }
+ else if (name == "textureProjGrad" || name == "texture2DProjGradEXT" ||
+ name == "textureCubeGradEXT")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.proj = true;
+ }
+ else if (name == "textureProjGradOffset")
+ {
+ textureFunction.method = TextureFunction::GRAD;
+ textureFunction.proj = true;
+ textureFunction.offset = true;
+ }
+ else
+ UNREACHABLE();
+
+ if (textureFunction.method ==
+ TextureFunction::IMPLICIT) // Could require lod 0 or have a bias argument
+ {
+ size_t mandatoryArgumentCount = 2; // All functions have sampler and coordinate arguments
+
+ if (textureFunction.offset)
+ {
+ mandatoryArgumentCount++;
+ }
+
+ bool bias = (argumentCount > mandatoryArgumentCount); // Bias argument is optional
+
+ if (lod0 || shaderType == GL_VERTEX_SHADER)
+ {
+ if (bias)
+ {
+ textureFunction.method = TextureFunction::LOD0BIAS;
+ }
+ else
+ {
+ textureFunction.method = TextureFunction::LOD0;
+ }
+ }
+ else if (bias)
+ {
+ textureFunction.method = TextureFunction::BIAS;
+ }
+ }
+
+ mUsesTexture.insert(textureFunction);
+ return textureFunction.name();
+}
+
+void TextureFunctionHLSL::textureFunctionHeader(TInfoSinkBase &out,
+ const ShShaderOutput outputType,
+ bool getDimensionsIgnoresBaseLevel)
+{
+ for (const TextureFunction &textureFunction : mUsesTexture)
+ {
+ // Function header
+ out << textureFunction.getReturnType() << " " << textureFunction.name() << "(";
+
+ OutputTextureFunctionArgumentList(out, textureFunction, outputType);
+
+ out << ")\n"
+ "{\n";
+
+ // In some cases we use a variable to store the texture/sampler objects, but to work around
+ // a D3D11 compiler bug related to discard inside a loop that is conditional on texture
+ // sampling we need to call the function directly on references to the texture and sampler
+ // arrays. The bug was found using dEQP-GLES3.functional.shaders.discard*loop_texture*
+ // tests.
+ TString textureReference;
+ TString samplerReference;
+ GetTextureReference(out, textureFunction, outputType, &textureReference, &samplerReference);
+
+ if (textureFunction.method == TextureFunction::SIZE)
+ {
+ OutputTextureSizeFunctionBody(out, textureFunction, textureReference,
+ getDimensionsIgnoresBaseLevel);
+ }
+ else
+ {
+ TString texCoordX("t.x");
+ TString texCoordY("t.y");
+ TString texCoordZ("t.z");
+ ProjectTextureCoordinates(textureFunction, &texCoordX, &texCoordY, &texCoordZ);
+ OutputIntegerTextureSampleFunctionComputations(out, textureFunction, outputType,
+ textureReference, &texCoordX, &texCoordY,
+ &texCoordZ);
+ OutputTextureSampleFunctionReturnStatement(out, textureFunction, outputType,
+ textureReference, samplerReference,
+ texCoordX, texCoordY, texCoordZ);
+ }
+
+ out << "}\n"
+ "\n";
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/TextureFunctionHLSL.h b/gfx/angle/src/compiler/translator/TextureFunctionHLSL.h
new file mode 100755
index 000000000..68bf8c089
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TextureFunctionHLSL.h
@@ -0,0 +1,76 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// TextureFunctionHLSL: Class for writing implementations of ESSL texture functions into HLSL
+// output. Some of the implementations are straightforward and just call the HLSL equivalent of the
+// ESSL texture function, others do more work to emulate ESSL texture sampling or size query
+// behavior.
+//
+
+#ifndef COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
+#define COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
+
+#include <set>
+
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+#include "compiler/translator/InfoSink.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace sh
+{
+
+class TextureFunctionHLSL final : angle::NonCopyable
+{
+ public:
+ struct TextureFunction
+ {
+ // See ESSL 3.00.6 section 8.8 for reference about what the different methods below do.
+ enum Method
+ {
+ IMPLICIT, // Mipmap LOD determined implicitly (standard lookup)
+ BIAS,
+ LOD,
+ LOD0,
+ LOD0BIAS,
+ SIZE, // textureSize()
+ FETCH,
+ GRAD
+ };
+
+ TString name() const;
+
+ bool operator<(const TextureFunction &rhs) const;
+
+ const char *getReturnType() const;
+
+ TBasicType sampler;
+ int coords;
+ bool proj;
+ bool offset;
+ Method method;
+ };
+
+ // Returns the name of the texture function implementation to call.
+ // The name that's passed in is the name of the GLSL texture function that it should implement.
+ TString useTextureFunction(const TString &name,
+ TBasicType samplerType,
+ int coords,
+ size_t argumentCount,
+ bool lod0,
+ sh::GLenum shaderType);
+
+ void textureFunctionHeader(TInfoSinkBase &out,
+ const ShShaderOutput outputType,
+ bool getDimensionsIgnoresBaseLevel);
+
+ private:
+ typedef std::set<TextureFunction> TextureFunctionSet;
+ TextureFunctionSet mUsesTexture;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TEXTUREFUNCTIONHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/TranslatorESSL.cpp b/gfx/angle/src/compiler/translator/TranslatorESSL.cpp
new file mode 100755
index 000000000..43bce74cc
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorESSL.cpp
@@ -0,0 +1,115 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/TranslatorESSL.h"
+
+#include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/RecordConstantPrecision.h"
+#include "compiler/translator/OutputESSL.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+TranslatorESSL::TranslatorESSL(sh::GLenum type, ShShaderSpec spec)
+ : TCompiler(type, spec, SH_ESSL_OUTPUT)
+{
+}
+
+void TranslatorESSL::translate(TIntermNode *root, ShCompileOptions compileOptions)
+{
+ TInfoSinkBase& sink = getInfoSink().obj;
+
+ int shaderVer = getShaderVersion();
+ if (shaderVer > 100)
+ {
+ sink << "#version " << shaderVer << " es\n";
+ }
+
+ // Write built-in extension behaviors.
+ writeExtensionBehavior();
+
+ // Write pragmas after extensions because some drivers consider pragmas
+ // like non-preprocessor tokens.
+ writePragma(compileOptions);
+
+ bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+
+ if (precisionEmulation)
+ {
+ EmulatePrecision emulatePrecision(getSymbolTable(), shaderVer);
+ root->traverse(&emulatePrecision);
+ emulatePrecision.updateTree();
+ emulatePrecision.writeEmulationHelpers(sink, shaderVer, SH_ESSL_OUTPUT);
+ }
+
+ RecordConstantPrecision(root, getTemporaryIndex());
+
+ // Write emulated built-in functions if needed.
+ if (!getBuiltInFunctionEmulator().IsOutputEmpty())
+ {
+ sink << "// BEGIN: Generated code for built-in function emulation\n\n";
+ if (getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ sink << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
+ << "#define webgl_emu_precision highp\n"
+ << "#else\n"
+ << "#define webgl_emu_precision mediump\n"
+ << "#endif\n\n";
+ }
+ else
+ {
+ sink << "#define webgl_emu_precision highp\n";
+ }
+
+ getBuiltInFunctionEmulator().OutputEmulatedFunctions(sink);
+ sink << "// END: Generated code for built-in function emulation\n\n";
+ }
+
+ // Write array bounds clamping emulation if needed.
+ getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
+ // Write translated shader.
+ TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
+ getSymbolTable(), getShaderType(), shaderVer, precisionEmulation,
+ compileOptions);
+ root->traverse(&outputESSL);
+}
+
+bool TranslatorESSL::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Not necessary when translating to ESSL.
+ return false;
+}
+
+void TranslatorESSL::writeExtensionBehavior() {
+ TInfoSinkBase& sink = getInfoSink().obj;
+ const TExtensionBehavior& extBehavior = getExtensionBehavior();
+ for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
+ iter != extBehavior.end(); ++iter) {
+ if (iter->second != EBhUndefined) {
+ if (getResources().NV_shader_framebuffer_fetch && iter->first == "GL_EXT_shader_framebuffer_fetch") {
+ sink << "#extension GL_NV_shader_framebuffer_fetch : "
+ << getBehaviorString(iter->second) << "\n";
+ } else if (getResources().NV_draw_buffers && iter->first == "GL_EXT_draw_buffers") {
+ sink << "#extension GL_NV_draw_buffers : "
+ << getBehaviorString(iter->second) << "\n";
+ } else {
+ sink << "#extension " << iter->first << " : "
+ << getBehaviorString(iter->second) << "\n";
+ }
+ }
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/TranslatorESSL.h b/gfx/angle/src/compiler/translator/TranslatorESSL.h
new file mode 100755
index 000000000..b7b46a65e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorESSL.h
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TRANSLATORESSL_H_
+#define COMPILER_TRANSLATOR_TRANSLATORESSL_H_
+
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+class TranslatorESSL : public TCompiler
+{
+ public:
+ TranslatorESSL(sh::GLenum type, ShShaderSpec spec);
+
+ protected:
+ void translate(TIntermNode *root, ShCompileOptions compileOptions) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
+
+ private:
+ void writeExtensionBehavior();
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TRANSLATORESSL_H_
diff --git a/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp b/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp
new file mode 100755
index 000000000..1d6582b02
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorGLSL.cpp
@@ -0,0 +1,298 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/TranslatorGLSL.h"
+
+#include "angle_gl.h"
+#include "compiler/translator/BuiltInFunctionEmulatorGLSL.h"
+#include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/ExtensionGLSL.h"
+#include "compiler/translator/OutputGLSL.h"
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+#include "compiler/translator/VersionGLSL.h"
+
+namespace sh
+{
+
+TranslatorGLSL::TranslatorGLSL(sh::GLenum type,
+ ShShaderSpec spec,
+ ShShaderOutput output)
+ : TCompiler(type, spec, output) {
+}
+
+void TranslatorGLSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions)
+{
+ if (compileOptions & SH_EMULATE_ABS_INT_FUNCTION)
+ {
+ InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(emu, getShaderType());
+ }
+
+ if (compileOptions & SH_EMULATE_ISNAN_FLOAT_FUNCTION)
+ {
+ InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(emu, getShaderVersion());
+ }
+
+ int targetGLSLVersion = ShaderOutputTypeToGLSLVersion(getOutputType());
+ InitBuiltInFunctionEmulatorForGLSLMissingFunctions(emu, getShaderType(), targetGLSLVersion);
+}
+
+void TranslatorGLSL::translate(TIntermNode *root, ShCompileOptions compileOptions)
+{
+ TInfoSinkBase& sink = getInfoSink().obj;
+
+ // Write GLSL version.
+ writeVersion(root);
+
+ // Write extension behaviour as needed
+ writeExtensionBehavior(root);
+
+ // Write pragmas after extensions because some drivers consider pragmas
+ // like non-preprocessor tokens.
+ writePragma(compileOptions);
+
+ // If flattening the global invariant pragma, write invariant declarations for built-in
+ // 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) && getPragma().stdgl.invariantAll)
+ {
+ ASSERT(wereVariablesCollected());
+
+ switch (getShaderType())
+ {
+ case GL_VERTEX_SHADER:
+ sink << "invariant gl_Position;\n";
+
+ // gl_PointSize should be declared invariant in both ESSL 1.00 and 3.00 fragment
+ // shaders if it's statically referenced.
+ conditionallyOutputInvariantDeclaration("gl_PointSize");
+ break;
+ case GL_FRAGMENT_SHADER:
+ // The preprocessor will reject this pragma if it's used in ESSL 3.00 fragment
+ // shaders, so we can use simple logic to determine whether to declare these
+ // variables invariant.
+ conditionallyOutputInvariantDeclaration("gl_FragCoord");
+ conditionallyOutputInvariantDeclaration("gl_PointCoord");
+ break;
+ default:
+ // Currently not reached, but leave this in for future expansion.
+ ASSERT(false);
+ break;
+ }
+ }
+
+ if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+ {
+ sh::RewriteTexelFetchOffset(root, getSymbolTable(), getShaderVersion());
+ }
+
+ bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+
+ if (precisionEmulation)
+ {
+ EmulatePrecision emulatePrecision(getSymbolTable(), getShaderVersion());
+ root->traverse(&emulatePrecision);
+ emulatePrecision.updateTree();
+ emulatePrecision.writeEmulationHelpers(sink, getShaderVersion(), getOutputType());
+ }
+
+ // Write emulated built-in functions if needed.
+ if (!getBuiltInFunctionEmulator().IsOutputEmpty())
+ {
+ sink << "// BEGIN: Generated code for built-in function emulation\n\n";
+ sink << "#define webgl_emu_precision\n\n";
+ getBuiltInFunctionEmulator().OutputEmulatedFunctions(sink);
+ sink << "// END: Generated code for built-in function emulation\n\n";
+ }
+
+ // Write array bounds clamping emulation if needed.
+ getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+
+ // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
+ // if it's core profile shaders and they are used.
+ if (getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ const bool mayHaveESSL1SecondaryOutputs =
+ IsExtensionEnabled(getExtensionBehavior(), "GL_EXT_blend_func_extended") &&
+ getShaderVersion() == 100;
+ const bool declareGLFragmentOutputs = IsGLSL130OrNewer(getOutputType());
+
+ bool hasGLFragColor = false;
+ bool hasGLFragData = false;
+ bool hasGLSecondaryFragColor = false;
+ bool hasGLSecondaryFragData = false;
+
+ for (const auto &outputVar : outputVariables)
+ {
+ if (declareGLFragmentOutputs)
+ {
+ if (outputVar.name == "gl_FragColor")
+ {
+ ASSERT(!hasGLFragColor);
+ hasGLFragColor = true;
+ continue;
+ }
+ else if (outputVar.name == "gl_FragData")
+ {
+ ASSERT(!hasGLFragData);
+ hasGLFragData = true;
+ continue;
+ }
+ }
+ if (mayHaveESSL1SecondaryOutputs)
+ {
+ if (outputVar.name == "gl_SecondaryFragColorEXT")
+ {
+ ASSERT(!hasGLSecondaryFragColor);
+ hasGLSecondaryFragColor = true;
+ continue;
+ }
+ else if (outputVar.name == "gl_SecondaryFragDataEXT")
+ {
+ ASSERT(!hasGLSecondaryFragData);
+ hasGLSecondaryFragData = true;
+ continue;
+ }
+ }
+ }
+ ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) &&
+ (hasGLFragData || hasGLSecondaryFragData)));
+ if (hasGLFragColor)
+ {
+ sink << "out vec4 webgl_FragColor;\n";
+ }
+ if (hasGLFragData)
+ {
+ sink << "out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
+ }
+ if (hasGLSecondaryFragColor)
+ {
+ sink << "out vec4 angle_SecondaryFragColor;\n";
+ }
+ if (hasGLSecondaryFragData)
+ {
+ sink << "out vec4 angle_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
+ << "];\n";
+ }
+ }
+
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
+ // Write translated shader.
+ TOutputGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
+ getSymbolTable(), getShaderType(), getShaderVersion(), getOutputType(),
+ compileOptions);
+ root->traverse(&outputGLSL);
+}
+
+bool TranslatorGLSL::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Required when outputting to any GLSL version greater than 1.20, but since ANGLE doesn't
+ // translate to that version, return true for the next higher version.
+ return IsGLSL130OrNewer(getOutputType());
+}
+
+bool TranslatorGLSL::shouldCollectVariables(ShCompileOptions compileOptions)
+{
+ return (compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) ||
+ TCompiler::shouldCollectVariables(compileOptions);
+}
+
+void TranslatorGLSL::writeVersion(TIntermNode *root)
+{
+ TVersionGLSL versionGLSL(getShaderType(), getPragma(), getOutputType());
+ root->traverse(&versionGLSL);
+ int version = versionGLSL.getVersion();
+ // We need to write version directive only if it is greater than 110.
+ // If there is no version directive in the shader, 110 is implied.
+ if (version > 110)
+ {
+ TInfoSinkBase& sink = getInfoSink().obj;
+ sink << "#version " << version << "\n";
+ }
+}
+
+void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
+{
+ TInfoSinkBase& sink = getInfoSink().obj;
+ const TExtensionBehavior& extBehavior = getExtensionBehavior();
+ for (const auto &iter : extBehavior)
+ {
+ if (iter.second == EBhUndefined)
+ {
+ continue;
+ }
+
+ if (getOutputType() == SH_GLSL_COMPATIBILITY_OUTPUT)
+ {
+ // For GLSL output, we don't need to emit most extensions explicitly,
+ // but some we need to translate in GL compatibility profile.
+ if (iter.first == "GL_EXT_shader_texture_lod")
+ {
+ sink << "#extension GL_ARB_shader_texture_lod : " << getBehaviorString(iter.second)
+ << "\n";
+ }
+
+ if (iter.first == "GL_EXT_draw_buffers")
+ {
+ sink << "#extension GL_ARB_draw_buffers : " << getBehaviorString(iter.second)
+ << "\n";
+ }
+ }
+ }
+
+ // GLSL ES 3 explicit location qualifiers need to use an extension before GLSL 330
+ if (getShaderVersion() >= 300 && getOutputType() < SH_GLSL_330_CORE_OUTPUT)
+ {
+ sink << "#extension GL_ARB_explicit_attrib_location : require\n";
+ }
+
+ // Need to enable gpu_shader5 to have index constant sampler array indexing
+ if (getOutputType() != SH_ESSL_OUTPUT && getOutputType() < SH_GLSL_400_CORE_OUTPUT)
+ {
+ sink << "#extension GL_ARB_gpu_shader5 : ";
+
+ // Don't use "require" on WebGL 1 to avoid breaking WebGL on drivers that silently
+ // support index constant sampler array indexing, but don't have the extension.
+ if (getShaderVersion() >= 300)
+ {
+ sink << "require\n";
+ }
+ else
+ {
+ sink << "enable\n";
+ }
+ }
+
+ TExtensionGLSL extensionGLSL(getOutputType());
+ root->traverse(&extensionGLSL);
+
+ for (const auto &ext : extensionGLSL.getEnabledExtensions())
+ {
+ sink << "#extension " << ext << " : enable\n";
+ }
+ for (const auto &ext : extensionGLSL.getRequiredExtensions())
+ {
+ sink << "#extension " << ext << " : require\n";
+ }
+}
+
+void TranslatorGLSL::conditionallyOutputInvariantDeclaration(const char *builtinVaryingName)
+{
+ if (isVaryingDefined(builtinVaryingName))
+ {
+ TInfoSinkBase &sink = getInfoSink().obj;
+ sink << "invariant " << builtinVaryingName << ";\n";
+ }
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/TranslatorGLSL.h b/gfx/angle/src/compiler/translator/TranslatorGLSL.h
new file mode 100755
index 000000000..d6f694824
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorGLSL.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TRANSLATORGLSL_H_
+#define COMPILER_TRANSLATOR_TRANSLATORGLSL_H_
+
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+class TranslatorGLSL : public TCompiler
+{
+ public:
+ TranslatorGLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
+
+ protected:
+ void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu,
+ ShCompileOptions compileOptions) override;
+
+ void translate(TIntermNode *root, ShCompileOptions compileOptions) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
+ bool shouldCollectVariables(ShCompileOptions compileOptions) override;
+
+ private:
+ void writeVersion(TIntermNode *root);
+ void writeExtensionBehavior(TIntermNode *root);
+ void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName);
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_
diff --git a/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp b/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
new file mode 100755
index 000000000..7ef1d4e36
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorHLSL.cpp
@@ -0,0 +1,148 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/TranslatorHLSL.h"
+
+#include "compiler/translator/AddDefaultReturnStatements.h"
+#include "compiler/translator/ArrayReturnValueToOutParameter.h"
+#include "compiler/translator/BreakVariableAliasingInInnerLoops.h"
+#include "compiler/translator/EmulatePrecision.h"
+#include "compiler/translator/ExpandIntegerPowExpressions.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+#include "compiler/translator/OutputHLSL.h"
+#include "compiler/translator/RemoveDynamicIndexing.h"
+#include "compiler/translator/RewriteElseBlocks.h"
+#include "compiler/translator/RewriteTexelFetchOffset.h"
+#include "compiler/translator/RewriteUnaryMinusOperatorInt.h"
+#include "compiler/translator/SeparateArrayInitialization.h"
+#include "compiler/translator/SeparateDeclarations.h"
+#include "compiler/translator/SeparateExpressionsReturningArrays.h"
+#include "compiler/translator/SimplifyLoopConditions.h"
+#include "compiler/translator/SplitSequenceOperator.h"
+#include "compiler/translator/UnfoldShortCircuitToIf.h"
+
+namespace sh
+{
+
+TranslatorHLSL::TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
+ : TCompiler(type, spec, output)
+{
+}
+
+void TranslatorHLSL::translate(TIntermNode *root, ShCompileOptions compileOptions)
+{
+ const ShBuiltInResources &resources = getResources();
+ int numRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1;
+
+ sh::AddDefaultReturnStatements(root);
+
+ SeparateDeclarations(root);
+
+ // Note that SimplifyLoopConditions needs to be run before any other AST transformations that
+ // may need to generate new statements from loop conditions or loop expressions.
+ SimplifyLoopConditions(root,
+ IntermNodePatternMatcher::kExpressionReturningArray |
+ IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
+ getTemporaryIndex(), getSymbolTable(), getShaderVersion());
+
+ SplitSequenceOperator(root,
+ IntermNodePatternMatcher::kExpressionReturningArray |
+ IntermNodePatternMatcher::kUnfoldedShortCircuitExpression |
+ IntermNodePatternMatcher::kDynamicIndexingOfVectorOrMatrixInLValue,
+ getTemporaryIndex(), getSymbolTable(), getShaderVersion());
+
+ // Note that SeparateDeclarations needs to be run before UnfoldShortCircuitToIf.
+ UnfoldShortCircuitToIf(root, getTemporaryIndex());
+
+ SeparateExpressionsReturningArrays(root, getTemporaryIndex());
+
+ // Note that SeparateDeclarations needs to be run before SeparateArrayInitialization.
+ SeparateArrayInitialization(root);
+
+ // HLSL doesn't support arrays as return values, we'll need to make functions that have an array
+ // as a return value to use an out parameter to transfer the array data instead.
+ ArrayReturnValueToOutParameter(root, getTemporaryIndex());
+
+ if (!shouldRunLoopAndIndexingValidation(compileOptions))
+ {
+ // HLSL doesn't support dynamic indexing of vectors and matrices.
+ RemoveDynamicIndexing(root, getTemporaryIndex(), getSymbolTable(), getShaderVersion());
+ }
+
+ // Work around D3D9 bug that would manifest in vertex shaders with selection blocks which
+ // use a vertex attribute as a condition, and some related computation in the else block.
+ if (getOutputType() == SH_HLSL_3_0_OUTPUT && getShaderType() == GL_VERTEX_SHADER)
+ {
+ sh::RewriteElseBlocks(root, getTemporaryIndex());
+ }
+
+ // Work around an HLSL compiler frontend aliasing optimization bug.
+ // TODO(cwallez) The date is 2016-08-25, Microsoft said the bug would be fixed
+ // in the next release of d3dcompiler.dll, it would be nice to detect the DLL
+ // version and only apply the workaround if it is too old.
+ sh::BreakVariableAliasingInInnerLoops(root);
+
+ bool precisionEmulation =
+ getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
+
+ if (precisionEmulation)
+ {
+ EmulatePrecision emulatePrecision(getSymbolTable(), getShaderVersion());
+ root->traverse(&emulatePrecision);
+ emulatePrecision.updateTree();
+ emulatePrecision.writeEmulationHelpers(getInfoSink().obj, getShaderVersion(),
+ getOutputType());
+ }
+
+ if ((compileOptions & SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS) != 0)
+ {
+ sh::ExpandIntegerPowExpressions(root, getTemporaryIndex());
+ }
+
+ if ((compileOptions & SH_REWRITE_TEXELFETCHOFFSET_TO_TEXELFETCH) != 0)
+ {
+ sh::RewriteTexelFetchOffset(root, getSymbolTable(), getShaderVersion());
+ }
+
+ if (((compileOptions & SH_REWRITE_INTEGER_UNARY_MINUS_OPERATOR) != 0) &&
+ getShaderType() == GL_VERTEX_SHADER)
+ {
+ sh::RewriteUnaryMinusOperatorInt(root);
+ }
+
+ sh::OutputHLSL outputHLSL(getShaderType(), getShaderVersion(), getExtensionBehavior(),
+ getSourcePath(), getOutputType(), numRenderTargets, getUniforms(), compileOptions);
+
+ outputHLSL.output(root, getInfoSink().obj);
+
+ mInterfaceBlockRegisterMap = outputHLSL.getInterfaceBlockRegisterMap();
+ mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
+}
+
+bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
+{
+ // Not necessary when translating to HLSL.
+ return false;
+}
+
+bool TranslatorHLSL::hasInterfaceBlock(const std::string &interfaceBlockName) const
+{
+ return (mInterfaceBlockRegisterMap.count(interfaceBlockName) > 0);
+}
+
+unsigned int TranslatorHLSL::getInterfaceBlockRegister(const std::string &interfaceBlockName) const
+{
+ ASSERT(hasInterfaceBlock(interfaceBlockName));
+ return mInterfaceBlockRegisterMap.find(interfaceBlockName)->second;
+}
+
+const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
+{
+ return &mUniformRegisterMap;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/TranslatorHLSL.h b/gfx/angle/src/compiler/translator/TranslatorHLSL.h
new file mode 100755
index 000000000..3bf64b2e6
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/TranslatorHLSL.h
@@ -0,0 +1,39 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TRANSLATORHLSL_H_
+#define COMPILER_TRANSLATOR_TRANSLATORHLSL_H_
+
+#include "compiler/translator/Compiler.h"
+
+namespace sh
+{
+
+class TranslatorHLSL : public TCompiler
+{
+ public:
+ TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
+ TranslatorHLSL *getAsTranslatorHLSL() override { return this; }
+
+ bool hasInterfaceBlock(const std::string &interfaceBlockName) const;
+ unsigned int getInterfaceBlockRegister(const std::string &interfaceBlockName) const;
+
+ const std::map<std::string, unsigned int> *getUniformRegisterMap() const;
+
+ protected:
+ void translate(TIntermNode *root, ShCompileOptions compileOptions) override;
+ bool shouldFlattenPragmaStdglInvariantAll() override;
+
+ // collectVariables needs to be run always so registers can be assigned.
+ bool shouldCollectVariables(ShCompileOptions compileOptions) override { return true; }
+
+ std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
+ std::map<std::string, unsigned int> mUniformRegisterMap;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TRANSLATORHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/Types.cpp b/gfx/angle/src/compiler/translator/Types.cpp
new file mode 100755
index 000000000..af79d3eea
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Types.cpp
@@ -0,0 +1,564 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#if defined(_MSC_VER)
+#pragma warning(disable: 4718)
+#endif
+
+#include "compiler/translator/Types.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+
+#include <algorithm>
+#include <climits>
+
+namespace sh
+{
+
+const char* getBasicString(TBasicType t)
+{
+ switch (t)
+ {
+ case EbtVoid: return "void";
+ case EbtFloat: return "float";
+ case EbtInt: return "int";
+ case EbtUInt: return "uint";
+ case EbtBool: return "bool";
+ case EbtSampler2D: return "sampler2D";
+ case EbtSampler3D: return "sampler3D";
+ case EbtSamplerCube: return "samplerCube";
+ case EbtSamplerExternalOES: return "samplerExternalOES";
+ case EbtSampler2DRect: return "sampler2DRect";
+ case EbtSampler2DArray: return "sampler2DArray";
+ case EbtISampler2D: return "isampler2D";
+ case EbtISampler3D: return "isampler3D";
+ case EbtISamplerCube: return "isamplerCube";
+ case EbtISampler2DArray: return "isampler2DArray";
+ case EbtUSampler2D: return "usampler2D";
+ case EbtUSampler3D: return "usampler3D";
+ case EbtUSamplerCube: return "usamplerCube";
+ case EbtUSampler2DArray: return "usampler2DArray";
+ case EbtSampler2DShadow: return "sampler2DShadow";
+ case EbtSamplerCubeShadow: return "samplerCubeShadow";
+ case EbtSampler2DArrayShadow: return "sampler2DArrayShadow";
+ case EbtStruct: return "structure";
+ case EbtInterfaceBlock: return "interface block";
+ case EbtImage2D:
+ return "image2D";
+ case EbtIImage2D:
+ return "iimage2D";
+ case EbtUImage2D:
+ return "uimage2D";
+ case EbtImage3D:
+ return "image3D";
+ case EbtIImage3D:
+ return "iimage3D";
+ case EbtUImage3D:
+ return "uimage3D";
+ case EbtImage2DArray:
+ return "image2DArray";
+ case EbtIImage2DArray:
+ return "iimage2DArray";
+ case EbtUImage2DArray:
+ return "uimage2DArray";
+ case EbtImageCube:
+ return "imageCube";
+ case EbtIImageCube:
+ return "iimageCube";
+ case EbtUImageCube:
+ return "uimageCube";
+ default: UNREACHABLE(); return "unknown type";
+ }
+}
+
+TType::TType(const TPublicType &p)
+ : type(p.getBasicType()),
+ precision(p.precision),
+ qualifier(p.qualifier),
+ invariant(p.invariant),
+ memoryQualifier(p.memoryQualifier),
+ layoutQualifier(p.layoutQualifier),
+ primarySize(p.getPrimarySize()),
+ secondarySize(p.getSecondarySize()),
+ array(p.array),
+ arraySize(p.arraySize),
+ interfaceBlock(0),
+ structure(0)
+{
+ if (p.getUserDef())
+ structure = p.getUserDef()->getStruct();
+}
+
+bool TStructure::equals(const TStructure &other) const
+{
+ return (uniqueId() == other.uniqueId());
+}
+
+const char *TType::getBuiltInTypeNameString() const
+{
+ if (isMatrix())
+ {
+ switch (getCols())
+ {
+ case 2:
+ switch (getRows())
+ {
+ case 2:
+ return "mat2";
+ case 3:
+ return "mat2x3";
+ case 4:
+ return "mat2x4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case 3:
+ switch (getRows())
+ {
+ case 2:
+ return "mat3x2";
+ case 3:
+ return "mat3";
+ case 4:
+ return "mat3x4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case 4:
+ switch (getRows())
+ {
+ case 2:
+ return "mat4x2";
+ case 3:
+ return "mat4x3";
+ case 4:
+ return "mat4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+ if (isVector())
+ {
+ switch (getBasicType())
+ {
+ case EbtFloat:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "vec2";
+ case 3:
+ return "vec3";
+ case 4:
+ return "vec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtInt:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "ivec2";
+ case 3:
+ return "ivec3";
+ case 4:
+ return "ivec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtBool:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "bvec2";
+ case 3:
+ return "bvec3";
+ case 4:
+ return "bvec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ case EbtUInt:
+ switch (getNominalSize())
+ {
+ case 2:
+ return "uvec2";
+ case 3:
+ return "uvec3";
+ case 4:
+ return "uvec4";
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ default:
+ UNREACHABLE();
+ return nullptr;
+ }
+ }
+ ASSERT(getBasicType() != EbtStruct);
+ ASSERT(getBasicType() != EbtInterfaceBlock);
+ return getBasicString();
+}
+
+TString TType::getCompleteString() const
+{
+ TStringStream stream;
+
+ if (invariant)
+ stream << "invariant ";
+ if (qualifier != EvqTemporary && qualifier != EvqGlobal)
+ stream << getQualifierString() << " ";
+ if (precision != EbpUndefined)
+ stream << getPrecisionString() << " ";
+ if (array)
+ stream << "array[" << getArraySize() << "] of ";
+ if (isMatrix())
+ stream << getCols() << "X" << getRows() << " matrix of ";
+ else if (isVector())
+ stream << getNominalSize() << "-component vector of ";
+
+ stream << getBasicString();
+ return stream.str();
+}
+
+//
+// Recursively generate mangled names.
+//
+TString TType::buildMangledName() const
+{
+ TString mangledName;
+ if (isMatrix())
+ mangledName += 'm';
+ else if (isVector())
+ mangledName += 'v';
+
+ switch (type)
+ {
+ case EbtFloat:
+ mangledName += 'f';
+ break;
+ case EbtInt:
+ mangledName += 'i';
+ break;
+ case EbtUInt:
+ mangledName += 'u';
+ break;
+ case EbtBool:
+ mangledName += 'b';
+ break;
+ case EbtSampler2D:
+ mangledName += "s2";
+ break;
+ case EbtSampler3D:
+ mangledName += "s3";
+ break;
+ case EbtSamplerCube:
+ mangledName += "sC";
+ break;
+ case EbtSampler2DArray:
+ mangledName += "s2a";
+ break;
+ case EbtSamplerExternalOES:
+ mangledName += "sext";
+ break;
+ case EbtSampler2DRect:
+ mangledName += "s2r";
+ break;
+ case EbtISampler2D:
+ mangledName += "is2";
+ break;
+ case EbtISampler3D:
+ mangledName += "is3";
+ break;
+ case EbtISamplerCube:
+ mangledName += "isC";
+ break;
+ case EbtISampler2DArray:
+ mangledName += "is2a";
+ break;
+ case EbtUSampler2D:
+ mangledName += "us2";
+ break;
+ case EbtUSampler3D:
+ mangledName += "us3";
+ break;
+ case EbtUSamplerCube:
+ mangledName += "usC";
+ break;
+ case EbtUSampler2DArray:
+ mangledName += "us2a";
+ break;
+ case EbtSampler2DShadow:
+ mangledName += "s2s";
+ break;
+ case EbtSamplerCubeShadow:
+ mangledName += "sCs";
+ break;
+ case EbtSampler2DArrayShadow:
+ mangledName += "s2as";
+ break;
+ case EbtImage2D:
+ mangledName += "im2";
+ break;
+ case EbtIImage2D:
+ mangledName += "iim2";
+ break;
+ case EbtUImage2D:
+ mangledName += "uim2";
+ break;
+ case EbtImage3D:
+ mangledName += "im3";
+ break;
+ case EbtIImage3D:
+ mangledName += "iim3";
+ break;
+ case EbtUImage3D:
+ mangledName += "uim3";
+ break;
+ case EbtImage2DArray:
+ mangledName += "im2a";
+ break;
+ case EbtIImage2DArray:
+ mangledName += "iim2a";
+ break;
+ case EbtUImage2DArray:
+ mangledName += "uim2a";
+ break;
+ case EbtImageCube:
+ mangledName += "imc";
+ break;
+ case EbtIImageCube:
+ mangledName += "iimc";
+ break;
+ case EbtUImageCube:
+ mangledName += "uimc";
+ break;
+ case EbtStruct:
+ mangledName += structure->mangledName();
+ break;
+ case EbtInterfaceBlock:
+ mangledName += interfaceBlock->mangledName();
+ break;
+ default:
+ // EbtVoid, EbtAddress and non types
+ break;
+ }
+
+ if (isMatrix())
+ {
+ mangledName += static_cast<char>('0' + getCols());
+ mangledName += static_cast<char>('x');
+ mangledName += static_cast<char>('0' + getRows());
+ }
+ else
+ {
+ mangledName += static_cast<char>('0' + getNominalSize());
+ }
+
+ if (isArray())
+ {
+ char buf[20];
+ snprintf(buf, sizeof(buf), "%d", arraySize);
+ mangledName += '[';
+ mangledName += buf;
+ mangledName += ']';
+ }
+ return mangledName;
+}
+
+size_t TType::getObjectSize() const
+{
+ size_t totalSize;
+
+ if (getBasicType() == EbtStruct)
+ totalSize = structure->objectSize();
+ else
+ totalSize = primarySize * secondarySize;
+
+ if (isArray())
+ {
+ if (totalSize == 0)
+ return 0;
+
+ size_t currentArraySize = getArraySize();
+ if (currentArraySize > INT_MAX / totalSize)
+ totalSize = INT_MAX;
+ else
+ totalSize *= currentArraySize;
+ }
+
+ return totalSize;
+}
+
+TStructure::TStructure(const TString *name, TFieldList *fields)
+ : TFieldListCollection(name, fields),
+ mDeepestNesting(0),
+ mUniqueId(TSymbolTable::nextUniqueId()),
+ mAtGlobalScope(false)
+{
+}
+
+bool TStructure::containsArrays() const
+{
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ const TType *fieldType = (*mFields)[i]->type();
+ if (fieldType->isArray() || fieldType->isStructureContainingArrays())
+ return true;
+ }
+ return false;
+}
+
+bool TStructure::containsType(TBasicType type) const
+{
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ const TType *fieldType = (*mFields)[i]->type();
+ if (fieldType->getBasicType() == type || fieldType->isStructureContainingType(type))
+ return true;
+ }
+ return false;
+}
+
+bool TStructure::containsSamplers() const
+{
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ const TType *fieldType = (*mFields)[i]->type();
+ if (IsSampler(fieldType->getBasicType()) || fieldType->isStructureContainingSamplers())
+ return true;
+ }
+ return false;
+}
+
+bool TStructure::containsImages() const
+{
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ const TType *fieldType = (*mFields)[i]->type();
+ if (IsImage(fieldType->getBasicType()) || fieldType->isStructureContainingImages())
+ return true;
+ }
+ return false;
+}
+
+void TStructure::createSamplerSymbols(const TString &structName,
+ const TString &structAPIName,
+ const unsigned int arrayOfStructsSize,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const
+{
+ for (auto &field : *mFields)
+ {
+ const TType *fieldType = field->type();
+ if (IsSampler(fieldType->getBasicType()))
+ {
+ if (arrayOfStructsSize > 0u)
+ {
+ for (unsigned int arrayIndex = 0u; arrayIndex < arrayOfStructsSize; ++arrayIndex)
+ {
+ TStringStream name;
+ name << structName << "_" << arrayIndex << "_" << field->name();
+ TIntermSymbol *symbol = new TIntermSymbol(0, name.str(), *fieldType);
+ outputSymbols->push_back(symbol);
+
+ if (outputSymbolsToAPINames)
+ {
+ TStringStream apiName;
+ apiName << structAPIName << "[" << arrayIndex << "]." << field->name();
+ (*outputSymbolsToAPINames)[symbol] = apiName.str();
+ }
+ }
+ }
+ else
+ {
+ TString symbolName = structName + "_" + field->name();
+ TIntermSymbol *symbol = new TIntermSymbol(0, symbolName, *fieldType);
+ outputSymbols->push_back(symbol);
+
+ if (outputSymbolsToAPINames)
+ {
+ TString apiName = structAPIName + "." + field->name();
+ (*outputSymbolsToAPINames)[symbol] = apiName;
+ }
+ }
+ }
+ else if (fieldType->isStructureContainingSamplers())
+ {
+ unsigned int nestedArrayOfStructsSize =
+ fieldType->isArray() ? fieldType->getArraySize() : 0u;
+ if (arrayOfStructsSize > 0)
+ {
+ for (unsigned int arrayIndex = 0u; arrayIndex < arrayOfStructsSize; ++arrayIndex)
+ {
+ TStringStream fieldName;
+ fieldName << structName << "_" << arrayIndex << "_" << field->name();
+ TStringStream fieldAPIName;
+ if (outputSymbolsToAPINames)
+ {
+ fieldAPIName << structAPIName << "[" << arrayIndex << "]." << field->name();
+ }
+ fieldType->createSamplerSymbols(fieldName.str(), fieldAPIName.str(),
+ nestedArrayOfStructsSize, outputSymbols,
+ outputSymbolsToAPINames);
+ }
+ }
+ else
+ {
+ fieldType->createSamplerSymbols(
+ structName + "_" + field->name(), structAPIName + "." + field->name(),
+ nestedArrayOfStructsSize, outputSymbols, outputSymbolsToAPINames);
+ }
+ }
+ }
+}
+
+TString TFieldListCollection::buildMangledName(const TString &mangledNamePrefix) const
+{
+ TString mangledName(mangledNamePrefix);
+ mangledName += *mName;
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ mangledName += '-';
+ mangledName += (*mFields)[i]->type()->getMangledName();
+ }
+ return mangledName;
+}
+
+size_t TFieldListCollection::calculateObjectSize() const
+{
+ size_t size = 0;
+ for (size_t i = 0; i < mFields->size(); ++i)
+ {
+ size_t fieldSize = (*mFields)[i]->type()->getObjectSize();
+ if (fieldSize > INT_MAX - size)
+ size = INT_MAX;
+ else
+ size += fieldSize;
+ }
+ return size;
+}
+
+int TStructure::calculateDeepestNesting() const
+{
+ int maxNesting = 0;
+ for (size_t i = 0; i < mFields->size(); ++i)
+ maxNesting = std::max(maxNesting, (*mFields)[i]->type()->getDeepestStructNesting());
+ return 1 + maxNesting;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/Types.h b/gfx/angle/src/compiler/translator/Types.h
new file mode 100755
index 000000000..fc26d5f59
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/Types.h
@@ -0,0 +1,773 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_TYPES_H_
+#define COMPILER_TRANSLATOR_TYPES_H_
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+
+#include "compiler/translator/BaseTypes.h"
+#include "compiler/translator/Common.h"
+
+namespace sh
+{
+
+struct TPublicType;
+class TType;
+class TSymbol;
+class TIntermSymbol;
+
+class TField : angle::NonCopyable
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TField(TType *type, TString *name, const TSourceLoc &line)
+ : mType(type),
+ mName(name),
+ mLine(line)
+ {
+ }
+
+ // TODO(alokp): We should only return const type.
+ // Fix it by tweaking grammar.
+ TType *type()
+ {
+ return mType;
+ }
+ const TType *type() const
+ {
+ return mType;
+ }
+
+ const TString &name() const
+ {
+ return *mName;
+ }
+ const TSourceLoc &line() const
+ {
+ return mLine;
+ }
+
+ private:
+ TType *mType;
+ TString *mName;
+ TSourceLoc mLine;
+};
+
+typedef TVector<TField *> TFieldList;
+inline TFieldList *NewPoolTFieldList()
+{
+ void *memory = GetGlobalPoolAllocator()->allocate(sizeof(TFieldList));
+ return new(memory) TFieldList;
+}
+
+class TFieldListCollection : angle::NonCopyable
+{
+ public:
+ const TString &name() const
+ {
+ return *mName;
+ }
+ const TFieldList &fields() const
+ {
+ return *mFields;
+ }
+
+ size_t objectSize() const
+ {
+ if (mObjectSize == 0)
+ mObjectSize = calculateObjectSize();
+ return mObjectSize;
+ };
+
+ protected:
+ TFieldListCollection(const TString *name, TFieldList *fields)
+ : mName(name),
+ mFields(fields),
+ mObjectSize(0)
+ {
+ }
+ TString buildMangledName(const TString &mangledNamePrefix) const;
+ size_t calculateObjectSize() const;
+
+ const TString *mName;
+ TFieldList *mFields;
+
+ mutable TString mMangledName;
+ mutable size_t mObjectSize;
+};
+
+// May also represent interface blocks
+class TStructure : public TFieldListCollection
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TStructure(const TString *name, TFieldList *fields);
+
+ int deepestNesting() const
+ {
+ if (mDeepestNesting == 0)
+ mDeepestNesting = calculateDeepestNesting();
+ return mDeepestNesting;
+ }
+ bool containsArrays() const;
+ bool containsType(TBasicType t) const;
+ bool containsSamplers() const;
+ bool containsImages() const;
+
+ void createSamplerSymbols(const TString &structName,
+ const TString &structAPIName,
+ const unsigned int arrayOfStructsSize,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const;
+
+ bool equals(const TStructure &other) const;
+
+ void setUniqueId(int uniqueId)
+ {
+ mUniqueId = uniqueId;
+ }
+
+ int uniqueId() const
+ {
+ ASSERT(mUniqueId != 0);
+ return mUniqueId;
+ }
+
+ void setAtGlobalScope(bool atGlobalScope)
+ {
+ mAtGlobalScope = atGlobalScope;
+ }
+
+ bool atGlobalScope() const
+ {
+ return mAtGlobalScope;
+ }
+
+ const TString &mangledName() const
+ {
+ if (mMangledName.empty())
+ mMangledName = buildMangledName("struct-");
+ return mMangledName;
+ }
+
+ private:
+ // TODO(zmo): Find a way to get rid of the const_cast in function
+ // setName(). At the moment keep this function private so only
+ // friend class RegenerateStructNames may call it.
+ friend class RegenerateStructNames;
+ void setName(const TString &name)
+ {
+ TString *mutableName = const_cast<TString *>(mName);
+ *mutableName = name;
+ }
+
+ int calculateDeepestNesting() const;
+
+ mutable int mDeepestNesting;
+ int mUniqueId;
+ bool mAtGlobalScope;
+};
+
+class TInterfaceBlock : public TFieldListCollection
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TInterfaceBlock(const TString *name, TFieldList *fields, const TString *instanceName,
+ int arraySize, const TLayoutQualifier &layoutQualifier)
+ : TFieldListCollection(name, fields),
+ mInstanceName(instanceName),
+ mArraySize(arraySize),
+ mBlockStorage(layoutQualifier.blockStorage),
+ mMatrixPacking(layoutQualifier.matrixPacking)
+ {
+ }
+
+ const TString &instanceName() const
+ {
+ return *mInstanceName;
+ }
+ bool hasInstanceName() const
+ {
+ return mInstanceName != NULL;
+ }
+ bool isArray() const
+ {
+ return mArraySize > 0;
+ }
+ int arraySize() const
+ {
+ return mArraySize;
+ }
+ TLayoutBlockStorage blockStorage() const
+ {
+ return mBlockStorage;
+ }
+ TLayoutMatrixPacking matrixPacking() const
+ {
+ return mMatrixPacking;
+ }
+ const TString &mangledName() const
+ {
+ if (mMangledName.empty())
+ mMangledName = buildMangledName("iblock-");
+ return mMangledName;
+ }
+
+ private:
+ const TString *mInstanceName; // for interface block instance names
+ int mArraySize; // 0 if not an array
+ TLayoutBlockStorage mBlockStorage;
+ TLayoutMatrixPacking mMatrixPacking;
+};
+
+//
+// Base class for things that have a type.
+//
+class TType
+{
+ public:
+ POOL_ALLOCATOR_NEW_DELETE();
+ TType()
+ : type(EbtVoid),
+ precision(EbpUndefined),
+ qualifier(EvqGlobal),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::create()),
+ layoutQualifier(TLayoutQualifier::create()),
+ primarySize(0),
+ secondarySize(0),
+ array(false),
+ arraySize(0),
+ interfaceBlock(nullptr),
+ structure(nullptr)
+ {
+ }
+ explicit TType(TBasicType t, unsigned char ps = 1, unsigned char ss = 1)
+ : type(t),
+ precision(EbpUndefined),
+ qualifier(EvqGlobal),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::create()),
+ layoutQualifier(TLayoutQualifier::create()),
+ primarySize(ps),
+ secondarySize(ss),
+ array(false),
+ arraySize(0),
+ interfaceBlock(0),
+ structure(0)
+ {
+ }
+ TType(TBasicType t,
+ TPrecision p,
+ TQualifier q = EvqTemporary,
+ unsigned char ps = 1,
+ unsigned char ss = 1,
+ bool a = false)
+ : type(t),
+ precision(p),
+ qualifier(q),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::create()),
+ layoutQualifier(TLayoutQualifier::create()),
+ primarySize(ps),
+ secondarySize(ss),
+ array(a),
+ arraySize(0),
+ interfaceBlock(0),
+ structure(0)
+ {
+ }
+ explicit TType(const TPublicType &p);
+ explicit TType(TStructure *userDef, TPrecision p = EbpUndefined)
+ : type(EbtStruct),
+ precision(p),
+ qualifier(EvqTemporary),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::create()),
+ layoutQualifier(TLayoutQualifier::create()),
+ primarySize(1),
+ secondarySize(1),
+ array(false),
+ arraySize(0),
+ interfaceBlock(0),
+ structure(userDef)
+ {
+ }
+ TType(TInterfaceBlock *interfaceBlockIn,
+ TQualifier qualifierIn,
+ TLayoutQualifier layoutQualifierIn,
+ int arraySizeIn)
+ : type(EbtInterfaceBlock),
+ precision(EbpUndefined),
+ qualifier(qualifierIn),
+ invariant(false),
+ memoryQualifier(TMemoryQualifier::create()),
+ layoutQualifier(layoutQualifierIn),
+ primarySize(1),
+ secondarySize(1),
+ array(arraySizeIn > 0),
+ arraySize(arraySizeIn),
+ interfaceBlock(interfaceBlockIn),
+ structure(0)
+ {
+ }
+
+ TType(const TType &) = default;
+ TType &operator=(const TType &) = default;
+
+ TBasicType getBasicType() const
+ {
+ return type;
+ }
+ void setBasicType(TBasicType t)
+ {
+ if (type != t)
+ {
+ type = t;
+ invalidateMangledName();
+ }
+ }
+
+ TPrecision getPrecision() const
+ {
+ return precision;
+ }
+ void setPrecision(TPrecision p)
+ {
+ precision = p;
+ }
+
+ TQualifier getQualifier() const
+ {
+ return qualifier;
+ }
+ void setQualifier(TQualifier q)
+ {
+ qualifier = q;
+ }
+
+ bool isInvariant() const
+ {
+ return invariant;
+ }
+
+ void setInvariant(bool i) { invariant = i; }
+
+ TMemoryQualifier getMemoryQualifier() const { return memoryQualifier; }
+ void setMemoryQualifier(const TMemoryQualifier &mq) { memoryQualifier = mq; }
+
+ TLayoutQualifier getLayoutQualifier() const
+ {
+ return layoutQualifier;
+ }
+ void setLayoutQualifier(TLayoutQualifier lq)
+ {
+ layoutQualifier = lq;
+ }
+
+ int getNominalSize() const
+ {
+ return primarySize;
+ }
+ int getSecondarySize() const
+ {
+ return secondarySize;
+ }
+ int getCols() const
+ {
+ ASSERT(isMatrix());
+ return primarySize;
+ }
+ int getRows() const
+ {
+ ASSERT(isMatrix());
+ return secondarySize;
+ }
+ void setPrimarySize(unsigned char ps)
+ {
+ if (primarySize != ps)
+ {
+ primarySize = ps;
+ invalidateMangledName();
+ }
+ }
+ void setSecondarySize(unsigned char ss)
+ {
+ if (secondarySize != ss)
+ {
+ secondarySize = ss;
+ invalidateMangledName();
+ }
+ }
+
+ // Full size of single instance of type
+ size_t getObjectSize() const;
+
+ bool isMatrix() const
+ {
+ return primarySize > 1 && secondarySize > 1;
+ }
+ bool isNonSquareMatrix() const
+ {
+ return isMatrix() && primarySize != secondarySize;
+ }
+ bool isArray() const
+ {
+ return array;
+ }
+ bool isUnsizedArray() const
+ {
+ return array && arraySize == 0u;
+ }
+ unsigned int getArraySize() const { return arraySize; }
+ void setArraySize(unsigned int s)
+ {
+ if (!array || arraySize != s)
+ {
+ array = true;
+ arraySize = s;
+ invalidateMangledName();
+ }
+ }
+ void clearArrayness()
+ {
+ if (array)
+ {
+ array = false;
+ arraySize = 0u;
+ invalidateMangledName();
+ }
+ }
+
+ TInterfaceBlock *getInterfaceBlock() const
+ {
+ return interfaceBlock;
+ }
+ void setInterfaceBlock(TInterfaceBlock *interfaceBlockIn)
+ {
+ if (interfaceBlock != interfaceBlockIn)
+ {
+ interfaceBlock = interfaceBlockIn;
+ invalidateMangledName();
+ }
+ }
+ bool isInterfaceBlock() const
+ {
+ return type == EbtInterfaceBlock;
+ }
+
+ bool isVector() const
+ {
+ return primarySize > 1 && secondarySize == 1;
+ }
+ bool isScalar() const
+ {
+ return primarySize == 1 && secondarySize == 1 && !structure;
+ }
+ bool isScalarInt() const
+ {
+ return isScalar() && (type == EbtInt || type == EbtUInt);
+ }
+
+ TStructure *getStruct() const
+ {
+ return structure;
+ }
+ void setStruct(TStructure *s)
+ {
+ if (structure != s)
+ {
+ structure = s;
+ invalidateMangledName();
+ }
+ }
+
+ const TString &getMangledName() const
+ {
+ if (mangled.empty())
+ {
+ mangled = buildMangledName();
+ mangled += ';';
+ }
+
+ return mangled;
+ }
+
+ bool sameElementType(const TType &right) const
+ {
+ return type == right.type &&
+ primarySize == right.primarySize &&
+ secondarySize == right.secondarySize &&
+ structure == right.structure;
+ }
+ bool operator==(const TType &right) const
+ {
+ return type == right.type &&
+ primarySize == right.primarySize &&
+ secondarySize == right.secondarySize &&
+ array == right.array && (!array || arraySize == right.arraySize) &&
+ structure == right.structure;
+ // don't check the qualifier, it's not ever what's being sought after
+ }
+ bool operator!=(const TType &right) const
+ {
+ return !operator==(right);
+ }
+ bool operator<(const TType &right) const
+ {
+ if (type != right.type)
+ return type < right.type;
+ if (primarySize != right.primarySize)
+ return primarySize < right.primarySize;
+ if (secondarySize != right.secondarySize)
+ return secondarySize < right.secondarySize;
+ if (array != right.array)
+ return array < right.array;
+ if (arraySize != right.arraySize)
+ return arraySize < right.arraySize;
+ if (structure != right.structure)
+ return structure < right.structure;
+
+ return false;
+ }
+
+ const char *getBasicString() const
+ {
+ return sh::getBasicString(type);
+ }
+
+ const char *getPrecisionString() const
+ {
+ return sh::getPrecisionString(precision);
+ }
+ const char *getQualifierString() const
+ {
+ return sh::getQualifierString(qualifier);
+ }
+
+ const char *getBuiltInTypeNameString() const;
+
+ TString getCompleteString() const;
+
+ // If this type is a struct, returns the deepest struct nesting of
+ // any field in the struct. For example:
+ // struct nesting1 {
+ // vec4 position;
+ // };
+ // struct nesting2 {
+ // nesting1 field1;
+ // vec4 field2;
+ // };
+ // For type "nesting2", this method would return 2 -- the number
+ // of structures through which indirection must occur to reach the
+ // deepest field (nesting2.field1.position).
+ int getDeepestStructNesting() const
+ {
+ return structure ? structure->deepestNesting() : 0;
+ }
+
+ bool isStructureContainingArrays() const
+ {
+ return structure ? structure->containsArrays() : false;
+ }
+
+ bool isStructureContainingType(TBasicType t) const
+ {
+ return structure ? structure->containsType(t) : false;
+ }
+
+ bool isStructureContainingSamplers() const
+ {
+ return structure ? structure->containsSamplers() : false;
+ }
+
+ bool isStructureContainingImages() const
+ {
+ return structure ? structure->containsImages() : false;
+ }
+
+ void createSamplerSymbols(const TString &structName,
+ const TString &structAPIName,
+ const unsigned int arrayOfStructsSize,
+ TVector<TIntermSymbol *> *outputSymbols,
+ TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const
+ {
+ ASSERT(structure != nullptr && structure->containsSamplers());
+ structure->createSamplerSymbols(structName, structAPIName, arrayOfStructsSize,
+ outputSymbols, outputSymbolsToAPINames);
+ }
+
+ // Initializes all lazily-initialized members.
+ void realize()
+ {
+ getMangledName();
+ }
+
+ private:
+ void invalidateMangledName() { mangled = ""; }
+ TString buildMangledName() const;
+ size_t getStructSize() const;
+
+ TBasicType type;
+ TPrecision precision;
+ TQualifier qualifier;
+ bool invariant;
+ TMemoryQualifier memoryQualifier;
+ TLayoutQualifier layoutQualifier;
+ unsigned char primarySize; // size of vector or cols matrix
+ unsigned char secondarySize; // rows of a matrix
+ bool array;
+ unsigned int arraySize;
+
+ // 0 unless this is an interface block, or interface block member variable
+ TInterfaceBlock *interfaceBlock;
+
+ // 0 unless this is a struct
+ TStructure *structure;
+
+ mutable TString mangled;
+};
+
+// TTypeSpecifierNonArray stores all of the necessary fields for type_specifier_nonarray from the
+// grammar
+struct TTypeSpecifierNonArray
+{
+ TBasicType type;
+ unsigned char primarySize; // size of vector or cols of matrix
+ unsigned char secondarySize; // rows of matrix
+ TType *userDef;
+ TSourceLoc line;
+
+ // true if the type was defined by a struct specifier rather than a reference to a type name.
+ bool isStructSpecifier;
+
+ void initialize(TBasicType bt, const TSourceLoc &ln)
+ {
+ type = bt;
+ primarySize = 1;
+ secondarySize = 1;
+ userDef = nullptr;
+ line = ln;
+ isStructSpecifier = false;
+ }
+
+ void setAggregate(unsigned char size)
+ {
+ primarySize = size;
+ }
+
+ void setMatrix(unsigned char columns, unsigned char rows)
+ {
+ ASSERT(columns > 1 && rows > 1 && columns <= 4 && rows <= 4);
+ primarySize = columns;
+ secondarySize = rows;
+ }
+
+ bool isMatrix() const { return primarySize > 1 && secondarySize > 1; }
+
+ bool isVector() const { return primarySize > 1 && secondarySize == 1; }
+};
+
+//
+// This is a workaround for a problem with the yacc stack, It can't have
+// types that it thinks have non-trivial constructors. It should
+// just be used while recognizing the grammar, not anything else. Pointers
+// could be used, but also trying to avoid lots of memory management overhead.
+//
+// Not as bad as it looks, there is no actual assumption that the fields
+// match up or are name the same or anything like that.
+//
+struct TPublicType
+{
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ TLayoutQualifier layoutQualifier;
+ TMemoryQualifier memoryQualifier;
+ TQualifier qualifier;
+ bool invariant;
+ TPrecision precision;
+ bool array;
+ int arraySize;
+
+ void initialize(const TTypeSpecifierNonArray &typeSpecifier, TQualifier q)
+ {
+ typeSpecifierNonArray = typeSpecifier;
+ layoutQualifier = TLayoutQualifier::create();
+ memoryQualifier = TMemoryQualifier::create();
+ qualifier = q;
+ invariant = false;
+ precision = EbpUndefined;
+ array = false;
+ arraySize = 0;
+ }
+
+ void initializeBasicType(TBasicType basicType)
+ {
+ typeSpecifierNonArray.type = basicType;
+ typeSpecifierNonArray.primarySize = 1;
+ typeSpecifierNonArray.secondarySize = 1;
+ layoutQualifier = TLayoutQualifier::create();
+ memoryQualifier = TMemoryQualifier::create();
+ qualifier = EvqTemporary;
+ invariant = false;
+ precision = EbpUndefined;
+ array = false;
+ arraySize = 0;
+ }
+
+ TBasicType getBasicType() const { return typeSpecifierNonArray.type; }
+ void setBasicType(TBasicType basicType) { typeSpecifierNonArray.type = basicType; }
+
+ unsigned char getPrimarySize() const { return typeSpecifierNonArray.primarySize; }
+ unsigned char getSecondarySize() const { return typeSpecifierNonArray.secondarySize; }
+
+ const TType *getUserDef() const { return typeSpecifierNonArray.userDef; }
+ const TSourceLoc &getLine() const { return typeSpecifierNonArray.line; }
+
+ bool isStructSpecifier() const { return typeSpecifierNonArray.isStructSpecifier; }
+
+ bool isStructureContainingArrays() const
+ {
+ if (!typeSpecifierNonArray.userDef)
+ {
+ return false;
+ }
+
+ return typeSpecifierNonArray.userDef->isStructureContainingArrays();
+ }
+
+ bool isStructureContainingType(TBasicType t) const
+ {
+ if (!typeSpecifierNonArray.userDef)
+ {
+ return false;
+ }
+
+ return typeSpecifierNonArray.userDef->isStructureContainingType(t);
+ }
+
+ bool isUnsizedArray() const { return array && arraySize == 0; }
+ void setArraySize(int s)
+ {
+ array = true;
+ arraySize = s;
+ }
+ void clearArrayness()
+ {
+ array = false;
+ arraySize = 0;
+ }
+
+ bool isAggregate() const
+ {
+ return array || typeSpecifierNonArray.isMatrix() || typeSpecifierNonArray.isVector();
+ }
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_TYPES_H_
diff --git a/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp b/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
new file mode 100755
index 000000000..b6a355a13
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.cpp
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/UnfoldShortCircuitAST.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// "x || y" is equivalent to "x ? true : y".
+TIntermTernary *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
+{
+ TConstantUnion *u = new TConstantUnion;
+ u->setBConst(true);
+ TIntermConstantUnion *trueNode = new TIntermConstantUnion(
+ u, TType(EbtBool, EbpUndefined, EvqConst, 1));
+ return new TIntermTernary(x, trueNode, y);
+}
+
+// "x && y" is equivalent to "x ? y : false".
+TIntermTernary *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
+{
+ TConstantUnion *u = new TConstantUnion;
+ u->setBConst(false);
+ TIntermConstantUnion *falseNode = new TIntermConstantUnion(
+ u, TType(EbtBool, EbpUndefined, EvqConst, 1));
+ return new TIntermTernary(x, y, falseNode);
+}
+
+} // namespace anonymous
+
+bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TIntermTernary *replacement = nullptr;
+
+ switch (node->getOp())
+ {
+ case EOpLogicalOr:
+ replacement = UnfoldOR(node->getLeft(), node->getRight());
+ break;
+ case EOpLogicalAnd:
+ replacement = UnfoldAND(node->getLeft(), node->getRight());
+ break;
+ default:
+ break;
+ }
+ if (replacement)
+ {
+ queueReplacement(node, replacement, OriginalNode::IS_DROPPED);
+ }
+ return true;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.h b/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.h
new file mode 100755
index 000000000..ac18bbf99
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UnfoldShortCircuitAST.h
@@ -0,0 +1,36 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UnfoldShortCircuitAST is an AST traverser to replace short-circuiting
+// operations with ternary operations.
+//
+
+#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_
+#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_
+
+#include "common/angleutils.h"
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+// This traverser identifies all the short circuit binary nodes that need to
+// be replaced, and creates the corresponding replacement nodes. However,
+// the actual replacements happen after the traverse through updateTree().
+
+class UnfoldShortCircuitAST : public TIntermTraverser
+{
+ public:
+ UnfoldShortCircuitAST()
+ : TIntermTraverser(true, false, false)
+ {
+ }
+
+ bool visitBinary(Visit visit, TIntermBinary *) override;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_
diff --git a/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
new file mode 100755
index 000000000..22fa54287
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.cpp
@@ -0,0 +1,187 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
+// The results are assigned to s# temporaries, which are used by the main translator instead of
+// the original expression.
+//
+
+#include "compiler/translator/UnfoldShortCircuitToIf.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverser that unfolds one short-circuiting operation at a time.
+class UnfoldShortCircuitTraverser : public TIntermTraverser
+{
+ public:
+ UnfoldShortCircuitTraverser();
+
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+
+ void nextIteration();
+ bool foundShortCircuit() const { return mFoundShortCircuit; }
+
+ protected:
+ // Marked to true once an operation that needs to be unfolded has been found.
+ // After that, no more unfolding is performed on that traversal.
+ bool mFoundShortCircuit;
+
+ IntermNodePatternMatcher mPatternToUnfoldMatcher;
+};
+
+UnfoldShortCircuitTraverser::UnfoldShortCircuitTraverser()
+ : TIntermTraverser(true, false, true),
+ mFoundShortCircuit(false),
+ mPatternToUnfoldMatcher(IntermNodePatternMatcher::kUnfoldedShortCircuitExpression)
+{
+}
+
+bool UnfoldShortCircuitTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (mFoundShortCircuit)
+ return false;
+
+ if (visit != PreVisit)
+ return true;
+
+ if (!mPatternToUnfoldMatcher.match(node, getParentNode()))
+ return true;
+
+ // If our right node doesn't have side effects, we know we don't need to unfold this
+ // expression: there will be no short-circuiting side effects to avoid
+ // (note: unfolding doesn't depend on the left node -- it will always be evaluated)
+ ASSERT(node->getRight()->hasSideEffects());
+
+ mFoundShortCircuit = true;
+
+ switch (node->getOp())
+ {
+ case EOpLogicalOr:
+ {
+ // "x || y" is equivalent to "x ? true : y", which unfolds to "bool s; if(x) s = true;
+ // else s = y;",
+ // and then further simplifies down to "bool s = x; if(!s) s = y;".
+
+ TIntermSequence insertions;
+ TType boolType(EbtBool, EbpUndefined, EvqTemporary);
+
+ ASSERT(node->getLeft()->getType() == boolType);
+ insertions.push_back(createTempInitDeclaration(node->getLeft()));
+
+ TIntermBlock *assignRightBlock = new TIntermBlock();
+ ASSERT(node->getRight()->getType() == boolType);
+ assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
+
+ TIntermUnary *notTempSymbol = new TIntermUnary(EOpLogicalNot, createTempSymbol(boolType));
+ TIntermIfElse *ifNode = new TIntermIfElse(notTempSymbol, assignRightBlock, nullptr);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
+ return false;
+ }
+ case EOpLogicalAnd:
+ {
+ // "x && y" is equivalent to "x ? y : false", which unfolds to "bool s; if(x) s = y;
+ // else s = false;",
+ // and then further simplifies down to "bool s = x; if(s) s = y;".
+ TIntermSequence insertions;
+ TType boolType(EbtBool, EbpUndefined, EvqTemporary);
+
+ ASSERT(node->getLeft()->getType() == boolType);
+ insertions.push_back(createTempInitDeclaration(node->getLeft()));
+
+ TIntermBlock *assignRightBlock = new TIntermBlock();
+ ASSERT(node->getRight()->getType() == boolType);
+ assignRightBlock->getSequence()->push_back(createTempAssignment(node->getRight()));
+
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(createTempSymbol(boolType), assignRightBlock, nullptr);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ queueReplacement(node, createTempSymbol(boolType), OriginalNode::IS_DROPPED);
+ return false;
+ }
+ default:
+ UNREACHABLE();
+ return true;
+ }
+}
+
+bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ if (mFoundShortCircuit)
+ return false;
+
+ if (visit != PreVisit)
+ return true;
+
+ if (!mPatternToUnfoldMatcher.match(node))
+ return true;
+
+ mFoundShortCircuit = true;
+
+ // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
+ TIntermSequence insertions;
+
+ TIntermDeclaration *tempDeclaration = createTempDeclaration(node->getType());
+ insertions.push_back(tempDeclaration);
+
+ TIntermBlock *trueBlock = new TIntermBlock();
+ TIntermBinary *trueAssignment = createTempAssignment(node->getTrueExpression());
+ trueBlock->getSequence()->push_back(trueAssignment);
+
+ TIntermBlock *falseBlock = new TIntermBlock();
+ TIntermBinary *falseAssignment = createTempAssignment(node->getFalseExpression());
+ falseBlock->getSequence()->push_back(falseAssignment);
+
+ TIntermIfElse *ifNode =
+ new TIntermIfElse(node->getCondition()->getAsTyped(), trueBlock, falseBlock);
+ insertions.push_back(ifNode);
+
+ insertStatementsInParentBlock(insertions);
+
+ TIntermSymbol *ternaryResult = createTempSymbol(node->getType());
+ queueReplacement(node, ternaryResult, OriginalNode::IS_DROPPED);
+
+ return false;
+}
+
+void UnfoldShortCircuitTraverser::nextIteration()
+{
+ mFoundShortCircuit = false;
+ nextTemporaryIndex();
+}
+
+} // namespace
+
+void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex)
+{
+ UnfoldShortCircuitTraverser traverser;
+ ASSERT(temporaryIndex != nullptr);
+ traverser.useTemporaryIndex(temporaryIndex);
+ // Unfold one operator at a time, and reset the traverser between iterations.
+ do
+ {
+ traverser.nextIteration();
+ root->traverse(&traverser);
+ if (traverser.foundShortCircuit())
+ traverser.updateTree();
+ }
+ while (traverser.foundShortCircuit());
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.h b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.h
new file mode 100755
index 000000000..24ff289a3
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UnfoldShortCircuitToIf.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UnfoldShortCircuitToIf is an AST traverser to convert short-circuiting operators to if-else statements.
+// The results are assigned to s# temporaries, which are used by the main translator instead of
+// the original expression.
+//
+
+#ifndef COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
+#define COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
+
+namespace sh
+{
+class TIntermNode;
+
+void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_
diff --git a/gfx/angle/src/compiler/translator/UniformHLSL.cpp b/gfx/angle/src/compiler/translator/UniformHLSL.cpp
new file mode 100755
index 000000000..3a85c60fc
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UniformHLSL.cpp
@@ -0,0 +1,473 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UniformHLSL.cpp:
+// Methods for GLSL to HLSL translation for uniforms and interface blocks.
+//
+
+#include "compiler/translator/UniformHLSL.h"
+
+#include "common/utilities.h"
+#include "compiler/translator/StructureHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+#include "compiler/translator/blocklayoutHLSL.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+static const char *UniformRegisterPrefix(const TType &type)
+{
+ if (IsSampler(type.getBasicType()))
+ {
+ return "s";
+ }
+ else
+ {
+ return "c";
+ }
+}
+
+static TString InterfaceBlockFieldTypeString(const TField &field, TLayoutBlockStorage blockStorage)
+{
+ const TType &fieldType = *field.type();
+ const TLayoutMatrixPacking matrixPacking = fieldType.getLayoutQualifier().matrixPacking;
+ ASSERT(matrixPacking != EmpUnspecified);
+ TStructure *structure = fieldType.getStruct();
+
+ if (fieldType.isMatrix())
+ {
+ // Use HLSL row-major packing for GLSL column-major matrices
+ const TString &matrixPackString = (matrixPacking == EmpRowMajor ? "column_major" : "row_major");
+ return matrixPackString + " " + TypeString(fieldType);
+ }
+ else if (structure)
+ {
+ // Use HLSL row-major packing for GLSL column-major matrices
+ return QualifiedStructNameString(*structure, matrixPacking == EmpColumnMajor,
+ blockStorage == EbsStd140);
+ }
+ else
+ {
+ return TypeString(fieldType);
+ }
+}
+
+static TString InterfaceBlockStructName(const TInterfaceBlock &interfaceBlock)
+{
+ return DecoratePrivate(interfaceBlock.name()) + "_type";
+}
+
+UniformHLSL::UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType, const std::vector<Uniform> &uniforms)
+ : mUniformRegister(0),
+ mInterfaceBlockRegister(0),
+ mSamplerRegister(0),
+ mStructureHLSL(structureHLSL),
+ mOutputType(outputType),
+ mUniforms(uniforms)
+{}
+
+void UniformHLSL::reserveUniformRegisters(unsigned int registerCount)
+{
+ mUniformRegister = registerCount;
+}
+
+void UniformHLSL::reserveInterfaceBlockRegisters(unsigned int registerCount)
+{
+ mInterfaceBlockRegister = registerCount;
+}
+
+const Uniform *UniformHLSL::findUniformByName(const TString &name) const
+{
+ for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); ++uniformIndex)
+ {
+ if (mUniforms[uniformIndex].name == name.c_str())
+ {
+ return &mUniforms[uniformIndex];
+ }
+ }
+
+ return nullptr;
+}
+
+unsigned int UniformHLSL::assignUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount)
+{
+ unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister);
+
+ const Uniform *uniform = findUniformByName(name);
+ ASSERT(uniform);
+
+ mUniformRegisterMap[uniform->name] = registerIndex;
+
+ unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType);
+
+ if (gl::IsSamplerType(uniform->type))
+ {
+ mSamplerRegister += registerCount;
+ }
+ else
+ {
+ mUniformRegister += registerCount;
+ }
+ if (outRegisterCount)
+ {
+ *outRegisterCount = registerCount;
+ }
+ return registerIndex;
+}
+
+unsigned int UniformHLSL::assignSamplerInStructUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount)
+{
+ // Sampler that is a field of a uniform structure.
+ ASSERT(IsSampler(type.getBasicType()));
+ unsigned int registerIndex = mSamplerRegister;
+ mUniformRegisterMap[std::string(name.c_str())] = registerIndex;
+ unsigned int registerCount = type.isArray() ? type.getArraySize() : 1u;
+ mSamplerRegister += registerCount;
+ if (outRegisterCount)
+ {
+ *outRegisterCount = registerCount;
+ }
+ return registerIndex;
+}
+
+void UniformHLSL::outputHLSLSamplerUniformGroup(
+ TInfoSinkBase &out,
+ const HLSLTextureSamplerGroup textureGroup,
+ const TVector<const TIntermSymbol *> &group,
+ const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
+ unsigned int *groupTextureRegisterIndex)
+{
+ if (group.empty())
+ {
+ return;
+ }
+ unsigned int groupRegisterCount = 0;
+ for (const TIntermSymbol *uniform : group)
+ {
+ const TType &type = uniform->getType();
+ const TString &name = uniform->getSymbol();
+ unsigned int registerCount;
+
+ // The uniform might be just a regular sampler or one extracted from a struct.
+ unsigned int samplerArrayIndex = 0u;
+ const Uniform *uniformByName = findUniformByName(name);
+ if (uniformByName)
+ {
+ samplerArrayIndex = assignUniformRegister(type, name, &registerCount);
+ }
+ else
+ {
+ ASSERT(samplerInStructSymbolsToAPINames.find(uniform) !=
+ samplerInStructSymbolsToAPINames.end());
+ samplerArrayIndex = assignSamplerInStructUniformRegister(
+ type, samplerInStructSymbolsToAPINames.at(uniform), &registerCount);
+ }
+ groupRegisterCount += registerCount;
+
+ if (type.isArray())
+ {
+ out << "static const uint " << DecorateIfNeeded(uniform->getName()) << ArrayString(type)
+ << " = {";
+ for (unsigned int i = 0u; i < type.getArraySize(); ++i)
+ {
+ if (i > 0u)
+ out << ", ";
+ out << (samplerArrayIndex + i);
+ }
+ out << "};\n";
+ }
+ else
+ {
+ out << "static const uint " << DecorateIfNeeded(uniform->getName()) << " = "
+ << samplerArrayIndex << ";\n";
+ }
+ }
+ TString suffix = TextureGroupSuffix(textureGroup);
+ // Since HLSL_TEXTURE_2D is the first group, it has a fixed offset of zero.
+ if (textureGroup != HLSL_TEXTURE_2D)
+ {
+ out << "static const uint textureIndexOffset" << suffix << " = "
+ << (*groupTextureRegisterIndex) << ";\n";
+ out << "static const uint samplerIndexOffset" << suffix << " = "
+ << (*groupTextureRegisterIndex) << ";\n";
+ }
+ out << "uniform " << TextureString(textureGroup) << " textures" << suffix << "["
+ << groupRegisterCount << "]"
+ << " : register(t" << (*groupTextureRegisterIndex) << ");\n";
+ out << "uniform " << SamplerString(textureGroup) << " samplers" << suffix << "["
+ << groupRegisterCount << "]"
+ << " : register(s" << (*groupTextureRegisterIndex) << ");\n";
+ *groupTextureRegisterIndex += groupRegisterCount;
+}
+
+void UniformHLSL::outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex)
+{
+ out << "uniform " << SamplerString(type.getBasicType()) << " sampler_"
+ << DecorateUniform(name, type) << ArrayString(type) << " : register(s" << str(registerIndex)
+ << ");\n";
+ out << "uniform " << TextureString(type.getBasicType()) << " texture_"
+ << DecorateUniform(name, type) << ArrayString(type) << " : register(t" << str(registerIndex)
+ << ");\n";
+}
+
+void UniformHLSL::outputUniform(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex)
+{
+ const TStructure *structure = type.getStruct();
+ // If this is a nameless struct, we need to use its full definition, rather than its (empty)
+ // name.
+ // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for
+ // nameless structs in ES, as nameless structs cannot be used anywhere that layout qualifiers
+ // are permitted.
+ const TString &typeName = ((structure && !structure->name().empty())
+ ? QualifiedStructNameString(*structure, false, false)
+ : TypeString(type));
+
+ const TString &registerString =
+ TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")";
+
+ out << "uniform " << typeName << " ";
+
+ out << DecorateUniform(name, type);
+
+ out << ArrayString(type) << " : " << registerString << ";\n";
+}
+
+void UniformHLSL::uniformsHeader(TInfoSinkBase &out,
+ ShShaderOutput outputType,
+ const ReferencedSymbols &referencedUniforms)
+{
+ if (!referencedUniforms.empty())
+ {
+ out << "// Uniforms\n\n";
+ }
+ // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is
+ // written. They are grouped based on the combination of the HLSL texture type and
+ // HLSL sampler type, enumerated in HLSLTextureSamplerGroup.
+ TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1);
+ TMap<const TIntermSymbol *, TString> samplerInStructSymbolsToAPINames;
+ for (auto &uniformIt : referencedUniforms)
+ {
+ // Output regular uniforms. Group sampler uniforms by type.
+ const TIntermSymbol &uniform = *uniformIt.second;
+ const TType &type = uniform.getType();
+ const TName &name = uniform.getName();
+
+ if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType()))
+ {
+ HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType());
+ groupedSamplerUniforms[group].push_back(&uniform);
+ }
+ else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType()))
+ {
+ unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+ outputHLSL4_0_FL9_3Sampler(out, type, name, registerIndex);
+ }
+ else
+ {
+ if (type.isStructureContainingSamplers())
+ {
+ TVector<TIntermSymbol *> samplerSymbols;
+ TMap<TIntermSymbol *, TString> symbolsToAPINames;
+ unsigned int arrayOfStructsSize = type.isArray() ? type.getArraySize() : 0u;
+ type.createSamplerSymbols("angle_" + name.getString(), name.getString(),
+ arrayOfStructsSize, &samplerSymbols, &symbolsToAPINames);
+ for (TIntermSymbol *sampler : samplerSymbols)
+ {
+ const TType &samplerType = sampler->getType();
+
+ // Will use angle_ prefix instead of regular prefix.
+ sampler->setInternal(true);
+ const TName &samplerName = sampler->getName();
+
+ if (outputType == SH_HLSL_4_1_OUTPUT)
+ {
+ HLSLTextureSamplerGroup group = TextureGroup(samplerType.getBasicType());
+ groupedSamplerUniforms[group].push_back(sampler);
+ samplerInStructSymbolsToAPINames[sampler] = symbolsToAPINames[sampler];
+ }
+ else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT)
+ {
+ unsigned int registerIndex = assignSamplerInStructUniformRegister(
+ samplerType, symbolsToAPINames[sampler], nullptr);
+ outputHLSL4_0_FL9_3Sampler(out, samplerType, samplerName, registerIndex);
+ }
+ else
+ {
+ ASSERT(outputType == SH_HLSL_3_0_OUTPUT);
+ unsigned int registerIndex = assignSamplerInStructUniformRegister(
+ samplerType, symbolsToAPINames[sampler], nullptr);
+ outputUniform(out, samplerType, samplerName, registerIndex);
+ }
+ }
+ }
+ unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr);
+ outputUniform(out, type, name, registerIndex);
+ }
+ }
+
+ if (outputType == SH_HLSL_4_1_OUTPUT)
+ {
+ unsigned int groupTextureRegisterIndex = 0;
+ // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case.
+ ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D);
+ for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId)
+ {
+ outputHLSLSamplerUniformGroup(
+ out, HLSLTextureSamplerGroup(groupId), groupedSamplerUniforms[groupId],
+ samplerInStructSymbolsToAPINames, &groupTextureRegisterIndex);
+ }
+ }
+}
+
+void UniformHLSL::samplerMetadataUniforms(TInfoSinkBase &out, const char *reg)
+{
+ // If mSamplerRegister is 0 the shader doesn't use any textures.
+ if (mSamplerRegister > 0)
+ {
+ out << " struct SamplerMetadata\n"
+ " {\n"
+ " int baseLevel;\n"
+ " int internalFormatBits;\n"
+ " int wrapModes;\n"
+ " int padding;\n"
+ " };\n"
+ " SamplerMetadata samplerMetadata["
+ << mSamplerRegister << "] : packoffset(" << reg << ");\n";
+ }
+}
+
+TString UniformHLSL::interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks)
+{
+ TString interfaceBlocks;
+
+ for (ReferencedSymbols::const_iterator interfaceBlockIt = referencedInterfaceBlocks.begin();
+ interfaceBlockIt != referencedInterfaceBlocks.end(); interfaceBlockIt++)
+ {
+ const TType &nodeType = interfaceBlockIt->second->getType();
+ const TInterfaceBlock &interfaceBlock = *nodeType.getInterfaceBlock();
+
+ unsigned int arraySize = static_cast<unsigned int>(interfaceBlock.arraySize());
+ unsigned int activeRegister = mInterfaceBlockRegister;
+
+ mInterfaceBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
+ mInterfaceBlockRegister += std::max(1u, arraySize);
+
+ // FIXME: interface block field names
+
+ if (interfaceBlock.hasInstanceName())
+ {
+ interfaceBlocks += interfaceBlockStructString(interfaceBlock);
+ }
+
+ if (arraySize > 0)
+ {
+ for (unsigned int arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
+ {
+ interfaceBlocks += interfaceBlockString(interfaceBlock, activeRegister + arrayIndex, arrayIndex);
+ }
+ }
+ else
+ {
+ interfaceBlocks += interfaceBlockString(interfaceBlock, activeRegister, GL_INVALID_INDEX);
+ }
+ }
+
+ return (interfaceBlocks.empty() ? "" : ("// Interface Blocks\n\n" + interfaceBlocks));
+}
+
+TString UniformHLSL::interfaceBlockString(const TInterfaceBlock &interfaceBlock, unsigned int registerIndex, unsigned int arrayIndex)
+{
+ const TString &arrayIndexString = (arrayIndex != GL_INVALID_INDEX ? Decorate(str(arrayIndex)) : "");
+ const TString &blockName = interfaceBlock.name() + arrayIndexString;
+ TString hlsl;
+
+ hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) + ")\n"
+ "{\n";
+
+ if (interfaceBlock.hasInstanceName())
+ {
+ hlsl += " " + InterfaceBlockStructName(interfaceBlock) + " " +
+ interfaceBlockInstanceString(interfaceBlock, arrayIndex) + ";\n";
+ }
+ else
+ {
+ const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
+ hlsl += interfaceBlockMembersString(interfaceBlock, blockStorage);
+ }
+
+ hlsl += "};\n\n";
+
+ return hlsl;
+}
+
+TString UniformHLSL::interfaceBlockInstanceString(const TInterfaceBlock& interfaceBlock, unsigned int arrayIndex)
+{
+ if (!interfaceBlock.hasInstanceName())
+ {
+ return "";
+ }
+ else if (interfaceBlock.isArray())
+ {
+ return DecoratePrivate(interfaceBlock.instanceName()) + "_" + str(arrayIndex);
+ }
+ else
+ {
+ return Decorate(interfaceBlock.instanceName());
+ }
+}
+
+TString UniformHLSL::interfaceBlockMembersString(const TInterfaceBlock &interfaceBlock, TLayoutBlockStorage blockStorage)
+{
+ TString hlsl;
+
+ Std140PaddingHelper padHelper = mStructureHLSL->getPaddingHelper();
+
+ for (unsigned int typeIndex = 0; typeIndex < interfaceBlock.fields().size(); typeIndex++)
+ {
+ const TField &field = *interfaceBlock.fields()[typeIndex];
+ const TType &fieldType = *field.type();
+
+ if (blockStorage == EbsStd140)
+ {
+ // 2 and 3 component vector types in some cases need pre-padding
+ hlsl += padHelper.prePaddingString(fieldType);
+ }
+
+ hlsl += " " + InterfaceBlockFieldTypeString(field, blockStorage) +
+ " " + Decorate(field.name()) + ArrayString(fieldType) + ";\n";
+
+ // must pad out after matrices and arrays, where HLSL usually allows itself room to pack stuff
+ if (blockStorage == EbsStd140)
+ {
+ const bool useHLSLRowMajorPacking = (fieldType.getLayoutQualifier().matrixPacking == EmpColumnMajor);
+ hlsl += padHelper.postPaddingString(fieldType, useHLSLRowMajorPacking);
+ }
+ }
+
+ return hlsl;
+}
+
+TString UniformHLSL::interfaceBlockStructString(const TInterfaceBlock &interfaceBlock)
+{
+ const TLayoutBlockStorage blockStorage = interfaceBlock.blockStorage();
+
+ return "struct " + InterfaceBlockStructName(interfaceBlock) + "\n"
+ "{\n" +
+ interfaceBlockMembersString(interfaceBlock, blockStorage) +
+ "};\n\n";
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/UniformHLSL.h b/gfx/angle/src/compiler/translator/UniformHLSL.h
new file mode 100755
index 000000000..c01b90fe0
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UniformHLSL.h
@@ -0,0 +1,92 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UniformHLSL.h:
+// Methods for GLSL to HLSL translation for uniforms and interface blocks.
+//
+
+#ifndef COMPILER_TRANSLATOR_UNIFORMHLSL_H_
+#define COMPILER_TRANSLATOR_UNIFORMHLSL_H_
+
+#include "compiler/translator/OutputHLSL.h"
+#include "compiler/translator/UtilsHLSL.h"
+
+namespace sh
+{
+class StructureHLSL;
+
+class UniformHLSL : angle::NonCopyable
+{
+ public:
+ UniformHLSL(StructureHLSL *structureHLSL, ShShaderOutput outputType, const std::vector<Uniform> &uniforms);
+
+ void reserveUniformRegisters(unsigned int registerCount);
+ void reserveInterfaceBlockRegisters(unsigned int registerCount);
+ void uniformsHeader(TInfoSinkBase &out,
+ ShShaderOutput outputType,
+ const ReferencedSymbols &referencedUniforms);
+
+ // Must be called after uniformsHeader
+ void samplerMetadataUniforms(TInfoSinkBase &out, const char *reg);
+
+ TString interfaceBlocksHeader(const ReferencedSymbols &referencedInterfaceBlocks);
+
+ // Used for direct index references
+ static TString interfaceBlockInstanceString(const TInterfaceBlock& interfaceBlock, unsigned int arrayIndex);
+
+ const std::map<std::string, unsigned int> &getInterfaceBlockRegisterMap() const
+ {
+ return mInterfaceBlockRegisterMap;
+ }
+ const std::map<std::string, unsigned int> &getUniformRegisterMap() const
+ {
+ return mUniformRegisterMap;
+ }
+
+ private:
+ TString interfaceBlockString(const TInterfaceBlock &interfaceBlock, unsigned int registerIndex, unsigned int arrayIndex);
+ TString interfaceBlockMembersString(const TInterfaceBlock &interfaceBlock, TLayoutBlockStorage blockStorage);
+ TString interfaceBlockStructString(const TInterfaceBlock &interfaceBlock);
+ const Uniform *findUniformByName(const TString &name) const;
+
+ void outputHLSL4_0_FL9_3Sampler(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+
+ void outputUniform(TInfoSinkBase &out,
+ const TType &type,
+ const TName &name,
+ const unsigned int registerIndex);
+
+ // Returns the uniform's register index
+ unsigned int assignUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount);
+ unsigned int assignSamplerInStructUniformRegister(const TType &type,
+ const TString &name,
+ unsigned int *outRegisterCount);
+
+ void outputHLSLSamplerUniformGroup(
+ TInfoSinkBase &out,
+ const HLSLTextureSamplerGroup textureGroup,
+ const TVector<const TIntermSymbol *> &group,
+ const TMap<const TIntermSymbol *, TString> &samplerInStructSymbolsToAPINames,
+ unsigned int *groupTextureRegisterIndex);
+
+ unsigned int mUniformRegister;
+ unsigned int mInterfaceBlockRegister;
+ unsigned int mSamplerRegister;
+ StructureHLSL *mStructureHLSL;
+ ShShaderOutput mOutputType;
+
+ const std::vector<Uniform> &mUniforms;
+ std::map<std::string, unsigned int> mInterfaceBlockRegisterMap;
+ std::map<std::string, unsigned int> mUniformRegisterMap;
+};
+
+}
+
+#endif // COMPILER_TRANSLATOR_UNIFORMHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.cpp b/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.cpp
new file mode 100644
index 000000000..390e2b092
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// UseInterfaceBlockFields.cpp: insert statements to reference all members in InterfaceBlock list at
+// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
+// uniform blocks as inactive.
+
+#include "compiler/translator/UseInterfaceBlockFields.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class UseUniformBlockMembers : public TIntermTraverser
+{
+ public:
+ UseUniformBlockMembers(const InterfaceBlockList &blocks, const TSymbolTable &symbolTable)
+ : TIntermTraverser(true, false, false),
+ mBlocks(blocks),
+ mSymbolTable(symbolTable),
+ mCodeInserted(false)
+ {
+ ASSERT(mSymbolTable.atGlobalLevel());
+ }
+
+ protected:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override { return !mCodeInserted; }
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+
+ private:
+ void insertUseCode(TIntermSequence *sequence);
+ void AddFieldUseStatements(const ShaderVariable &var, TIntermSequence *sequence);
+
+ const InterfaceBlockList &mBlocks;
+ const TSymbolTable &mSymbolTable;
+ bool mCodeInserted;
+};
+
+bool UseUniformBlockMembers::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ ASSERT(visit == PreVisit);
+ if (node->getFunctionSymbolInfo()->isMain())
+ {
+ TIntermBlock *body = node->getBody();
+ ASSERT(body);
+ insertUseCode(body->getSequence());
+ mCodeInserted = true;
+ return false;
+ }
+ return !mCodeInserted;
+}
+
+void UseUniformBlockMembers::AddFieldUseStatements(const ShaderVariable &var,
+ TIntermSequence *sequence)
+{
+ TString name = TString(var.name.c_str());
+ if (var.isArray())
+ {
+ size_t pos = name.find_last_of('[');
+ if (pos != TString::npos)
+ {
+ name = name.substr(0, pos);
+ }
+ }
+ const TType *type;
+ TType basicType;
+ if (var.isStruct())
+ {
+ TVariable *structInfo = reinterpret_cast<TVariable *>(mSymbolTable.findGlobal(name));
+ ASSERT(structInfo);
+ const TType &structType = structInfo->getType();
+ type = &structType;
+ }
+ else
+ {
+ basicType = sh::GetShaderVariableBasicType(var);
+ type = &basicType;
+ }
+ ASSERT(type);
+
+ TIntermSymbol *symbol = new TIntermSymbol(0, name, *type);
+ if (var.isArray())
+ {
+ for (unsigned int i = 0; i < var.arraySize; ++i)
+ {
+ TIntermBinary *element =
+ new TIntermBinary(EOpIndexDirect, symbol, TIntermTyped::CreateIndexNode(i));
+ sequence->insert(sequence->begin(), element);
+ }
+ }
+ else
+ {
+ sequence->insert(sequence->begin(), symbol);
+ }
+}
+
+void UseUniformBlockMembers::insertUseCode(TIntermSequence *sequence)
+{
+ for (const auto &block : mBlocks)
+ {
+ if (block.instanceName.empty())
+ {
+ for (const auto &var : block.fields)
+ {
+ AddFieldUseStatements(var, sequence);
+ }
+ }
+ else if (block.arraySize > 0)
+ {
+ TString name = TString(block.instanceName.c_str());
+ TVariable *ubInfo = reinterpret_cast<TVariable *>(mSymbolTable.findGlobal(name));
+ ASSERT(ubInfo);
+ TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, ubInfo->getType());
+ for (unsigned int i = 0; i < block.arraySize; ++i)
+ {
+ TIntermBinary *instanceSymbol = new TIntermBinary(EOpIndexDirect, arraySymbol,
+ TIntermTyped::CreateIndexNode(i));
+ for (unsigned int j = 0; j < block.fields.size(); ++j)
+ {
+ TIntermBinary *element =
+ new TIntermBinary(EOpIndexDirectInterfaceBlock, instanceSymbol,
+ TIntermTyped::CreateIndexNode(j));
+ sequence->insert(sequence->begin(), element);
+ }
+ }
+ }
+ else
+ {
+ TString name = TString(block.instanceName.c_str());
+ TVariable *ubInfo = reinterpret_cast<TVariable *>(mSymbolTable.findGlobal(name));
+ ASSERT(ubInfo);
+ TIntermSymbol *blockSymbol = new TIntermSymbol(0, name, ubInfo->getType());
+ for (unsigned int i = 0; i < block.fields.size(); ++i)
+ {
+ TIntermBinary *element = new TIntermBinary(
+ EOpIndexDirectInterfaceBlock, blockSymbol, TIntermTyped::CreateIndexNode(i));
+
+ sequence->insert(sequence->begin(), element);
+ }
+ }
+ }
+}
+
+} // namespace anonymous
+
+void UseInterfaceBlockFields(TIntermNode *root,
+ const InterfaceBlockList &blocks,
+ const TSymbolTable &symbolTable)
+{
+ UseUniformBlockMembers useUniformBlock(blocks, symbolTable);
+ root->traverse(&useUniformBlock);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.h b/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.h
new file mode 100644
index 000000000..a789bb7e9
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UseInterfaceBlockFields.h
@@ -0,0 +1,30 @@
+//
+// Copyright 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// UseInterfaceBlockFields.h: insert statements to reference all members in InterfaceBlock list at
+// the beginning of main. This is to work around a Mac driver that treats unused standard/shared
+// uniform blocks as inactive.
+
+#ifndef COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
+#define COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
+
+#include <GLSLANG/ShaderLang.h>
+
+class TIntermNode;
+namespace sh
+{
+
+class TSymbolTable;
+
+using InterfaceBlockList = std::vector<sh::InterfaceBlock>;
+
+void UseInterfaceBlockFields(TIntermNode *root,
+ const InterfaceBlockList &blocks,
+ const TSymbolTable &symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_
diff --git a/gfx/angle/src/compiler/translator/UtilsHLSL.cpp b/gfx/angle/src/compiler/translator/UtilsHLSL.cpp
new file mode 100755
index 000000000..221d5d9b5
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UtilsHLSL.cpp
@@ -0,0 +1,414 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UtilsHLSL.cpp:
+// Utility methods for GLSL to HLSL translation.
+//
+
+#include "compiler/translator/UtilsHLSL.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/StructureHLSL.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+TString SamplerString(const TBasicType type)
+{
+ if (IsShadowSampler(type))
+ {
+ return "SamplerComparisonState";
+ }
+ else
+ {
+ return "SamplerState";
+ }
+}
+
+TString SamplerString(HLSLTextureSamplerGroup type)
+{
+ if (type >= HLSL_COMPARISON_SAMPLER_GROUP_BEGIN && type <= HLSL_COMPARISON_SAMPLER_GROUP_END)
+ {
+ return "SamplerComparisonState";
+ }
+ else
+ {
+ return "SamplerState";
+ }
+}
+
+HLSLTextureSamplerGroup TextureGroup(const TBasicType type)
+{
+ switch (type)
+ {
+ case EbtSampler2D:
+ return HLSL_TEXTURE_2D;
+ case EbtSamplerCube:
+ return HLSL_TEXTURE_CUBE;
+ case EbtSamplerExternalOES:
+ return HLSL_TEXTURE_2D;
+ case EbtSampler2DArray:
+ return HLSL_TEXTURE_2D_ARRAY;
+ case EbtSampler3D:
+ return HLSL_TEXTURE_3D;
+ case EbtISampler2D:
+ return HLSL_TEXTURE_2D_INT4;
+ case EbtISampler3D:
+ return HLSL_TEXTURE_3D_INT4;
+ case EbtISamplerCube:
+ return HLSL_TEXTURE_2D_ARRAY_INT4;
+ case EbtISampler2DArray:
+ return HLSL_TEXTURE_2D_ARRAY_INT4;
+ case EbtUSampler2D:
+ return HLSL_TEXTURE_2D_UINT4;
+ case EbtUSampler3D:
+ return HLSL_TEXTURE_3D_UINT4;
+ case EbtUSamplerCube:
+ return HLSL_TEXTURE_2D_ARRAY_UINT4;
+ case EbtUSampler2DArray:
+ return HLSL_TEXTURE_2D_ARRAY_UINT4;
+ case EbtSampler2DShadow:
+ return HLSL_TEXTURE_2D_COMPARISON;
+ case EbtSamplerCubeShadow:
+ return HLSL_TEXTURE_CUBE_COMPARISON;
+ case EbtSampler2DArrayShadow:
+ return HLSL_TEXTURE_2D_ARRAY_COMPARISON;
+ default:
+ UNREACHABLE();
+ }
+ return HLSL_TEXTURE_UNKNOWN;
+}
+
+TString TextureString(const HLSLTextureSamplerGroup type)
+{
+ switch (type)
+ {
+ case HLSL_TEXTURE_2D:
+ return "Texture2D";
+ case HLSL_TEXTURE_CUBE:
+ return "TextureCube";
+ case HLSL_TEXTURE_2D_ARRAY:
+ return "Texture2DArray";
+ case HLSL_TEXTURE_3D:
+ return "Texture3D";
+ case HLSL_TEXTURE_2D_INT4:
+ return "Texture2D<int4>";
+ case HLSL_TEXTURE_3D_INT4:
+ return "Texture3D<int4>";
+ case HLSL_TEXTURE_2D_ARRAY_INT4:
+ return "Texture2DArray<int4>";
+ case HLSL_TEXTURE_2D_UINT4:
+ return "Texture2D<uint4>";
+ case HLSL_TEXTURE_3D_UINT4:
+ return "Texture3D<uint4>";
+ case HLSL_TEXTURE_2D_ARRAY_UINT4:
+ return "Texture2DArray<uint4>";
+ case HLSL_TEXTURE_2D_COMPARISON:
+ return "Texture2D";
+ case HLSL_TEXTURE_CUBE_COMPARISON:
+ return "TextureCube";
+ case HLSL_TEXTURE_2D_ARRAY_COMPARISON:
+ return "Texture2DArray";
+ default:
+ UNREACHABLE();
+ }
+
+ return "<unknown texture type>";
+}
+
+TString TextureString(const TBasicType type)
+{
+ return TextureString(TextureGroup(type));
+}
+
+TString TextureGroupSuffix(const HLSLTextureSamplerGroup type)
+{
+ switch (type)
+ {
+ case HLSL_TEXTURE_2D:
+ return "2D";
+ case HLSL_TEXTURE_CUBE:
+ return "Cube";
+ case HLSL_TEXTURE_2D_ARRAY:
+ return "2DArray";
+ case HLSL_TEXTURE_3D:
+ return "3D";
+ case HLSL_TEXTURE_2D_INT4:
+ return "2D_int4_";
+ case HLSL_TEXTURE_3D_INT4:
+ return "3D_int4_";
+ case HLSL_TEXTURE_2D_ARRAY_INT4:
+ return "2DArray_int4_";
+ case HLSL_TEXTURE_2D_UINT4:
+ return "2D_uint4_";
+ case HLSL_TEXTURE_3D_UINT4:
+ return "3D_uint4_";
+ case HLSL_TEXTURE_2D_ARRAY_UINT4:
+ return "2DArray_uint4_";
+ case HLSL_TEXTURE_2D_COMPARISON:
+ return "2D_comparison";
+ case HLSL_TEXTURE_CUBE_COMPARISON:
+ return "Cube_comparison";
+ case HLSL_TEXTURE_2D_ARRAY_COMPARISON:
+ return "2DArray_comparison";
+ default:
+ UNREACHABLE();
+ }
+
+ return "<unknown texture type>";
+}
+
+TString TextureGroupSuffix(const TBasicType type)
+{
+ return TextureGroupSuffix(TextureGroup(type));
+}
+
+TString TextureTypeSuffix(const TBasicType type)
+{
+ switch (type)
+ {
+ case EbtISamplerCube:
+ return "Cube_int4_";
+ case EbtUSamplerCube:
+ return "Cube_uint4_";
+ case EbtSamplerExternalOES:
+ return "_External";
+ default:
+ // All other types are identified by their group suffix
+ return TextureGroupSuffix(type);
+ }
+}
+
+TString DecorateUniform(const TName &name, const TType &type)
+{
+ return DecorateIfNeeded(name);
+}
+
+TString DecorateField(const TString &string, const TStructure &structure)
+{
+ if (structure.name().compare(0, 3, "gl_") != 0)
+ {
+ return Decorate(string);
+ }
+
+ return string;
+}
+
+TString DecoratePrivate(const TString &privateText)
+{
+ return "dx_" + privateText;
+}
+
+TString Decorate(const TString &string)
+{
+ if (string.compare(0, 3, "gl_") != 0)
+ {
+ return "_" + string;
+ }
+
+ return string;
+}
+
+TString DecorateIfNeeded(const TName &name)
+{
+ if (name.isInternal())
+ {
+ return name.getString();
+ }
+ else
+ {
+ return Decorate(name.getString());
+ }
+}
+
+TString DecorateFunctionIfNeeded(const TName &name)
+{
+ if (name.isInternal())
+ {
+ return TFunction::unmangleName(name.getString());
+ }
+ else
+ {
+ return Decorate(TFunction::unmangleName(name.getString()));
+ }
+}
+
+TString TypeString(const TType &type)
+{
+ const TStructure* structure = type.getStruct();
+ if (structure)
+ {
+ const TString& typeName = structure->name();
+ if (typeName != "")
+ {
+ return StructNameString(*structure);
+ }
+ else // Nameless structure, define in place
+ {
+ return StructureHLSL::defineNameless(*structure);
+ }
+ }
+ else if (type.isMatrix())
+ {
+ int cols = type.getCols();
+ int rows = type.getRows();
+ return "float" + str(cols) + "x" + str(rows);
+ }
+ else
+ {
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ switch (type.getNominalSize())
+ {
+ case 1: return "float";
+ case 2: return "float2";
+ case 3: return "float3";
+ case 4: return "float4";
+ }
+ case EbtInt:
+ switch (type.getNominalSize())
+ {
+ case 1: return "int";
+ case 2: return "int2";
+ case 3: return "int3";
+ case 4: return "int4";
+ }
+ case EbtUInt:
+ switch (type.getNominalSize())
+ {
+ case 1: return "uint";
+ case 2: return "uint2";
+ case 3: return "uint3";
+ case 4: return "uint4";
+ }
+ case EbtBool:
+ switch (type.getNominalSize())
+ {
+ case 1: return "bool";
+ case 2: return "bool2";
+ case 3: return "bool3";
+ case 4: return "bool4";
+ }
+ case EbtVoid:
+ return "void";
+ case EbtSampler2D:
+ case EbtISampler2D:
+ case EbtUSampler2D:
+ case EbtSampler2DArray:
+ case EbtISampler2DArray:
+ case EbtUSampler2DArray:
+ return "sampler2D";
+ case EbtSamplerCube:
+ case EbtISamplerCube:
+ case EbtUSamplerCube:
+ return "samplerCUBE";
+ case EbtSamplerExternalOES:
+ return "sampler2D";
+ default:
+ break;
+ }
+ }
+
+ UNREACHABLE();
+ return "<unknown type>";
+}
+
+TString StructNameString(const TStructure &structure)
+{
+ if (structure.name().empty())
+ {
+ return "";
+ }
+
+ // For structures at global scope we use a consistent
+ // translation so that we can link between shader stages.
+ if (structure.atGlobalScope())
+ {
+ return Decorate(structure.name());
+ }
+
+ return "ss" + str(structure.uniqueId()) + "_" + structure.name();
+}
+
+TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMajorPacking,
+ bool useStd140Packing)
+{
+ if (structure.name() == "")
+ {
+ return "";
+ }
+
+ TString prefix = "";
+
+ // Structs packed with row-major matrices in HLSL are prefixed with "rm"
+ // GLSL column-major maps to HLSL row-major, and the converse is true
+
+ if (useStd140Packing)
+ {
+ prefix += "std_";
+ }
+
+ if (useHLSLRowMajorPacking)
+ {
+ prefix += "rm_";
+ }
+
+ return prefix + StructNameString(structure);
+}
+
+TString InterpolationString(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqVaryingIn: return "";
+ case EvqFragmentIn: return "";
+ case EvqSmoothIn: return "linear";
+ case EvqFlatIn: return "nointerpolation";
+ case EvqCentroidIn: return "centroid";
+ case EvqVaryingOut: return "";
+ case EvqVertexOut: return "";
+ case EvqSmoothOut: return "linear";
+ case EvqFlatOut: return "nointerpolation";
+ case EvqCentroidOut: return "centroid";
+ default: UNREACHABLE();
+ }
+
+ return "";
+}
+
+TString QualifierString(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqIn: return "in";
+ case EvqOut: return "inout"; // 'out' results in an HLSL error if not all fields are written, for GLSL it's undefined
+ case EvqInOut: return "inout";
+ case EvqConstReadOnly: return "const";
+ default: UNREACHABLE();
+ }
+
+ return "";
+}
+
+TString DisambiguateFunctionName(const TIntermSequence *parameters)
+{
+ TString disambiguatingString;
+ for (auto parameter : *parameters)
+ {
+ const TType &paramType = parameter->getAsTyped()->getType();
+ // Disambiguation is needed for float2x2 and float4 parameters. These are the only parameter
+ // types that HLSL thinks are identical. float2x3 and float3x2 are different types, for
+ // example. Other parameter types are not added to function names to avoid making function
+ // names longer.
+ if (paramType.getObjectSize() == 4 && paramType.getBasicType() == EbtFloat)
+ {
+ disambiguatingString += "_" + TypeString(paramType);
+ }
+ }
+ return disambiguatingString;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/UtilsHLSL.h b/gfx/angle/src/compiler/translator/UtilsHLSL.h
new file mode 100755
index 000000000..748b3513e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/UtilsHLSL.h
@@ -0,0 +1,81 @@
+//
+// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// UtilsHLSL.h:
+// Utility methods for GLSL to HLSL translation.
+//
+
+#ifndef COMPILER_TRANSLATOR_UTILSHLSL_H_
+#define COMPILER_TRANSLATOR_UTILSHLSL_H_
+
+#include <vector>
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/Types.h"
+
+#include "angle_gl.h"
+
+class TName;
+
+namespace sh
+{
+
+// Unique combinations of HLSL Texture type and HLSL Sampler type.
+enum HLSLTextureSamplerGroup
+{
+ // Regular samplers
+ HLSL_TEXTURE_2D,
+ HLSL_TEXTURE_MIN = HLSL_TEXTURE_2D,
+
+ HLSL_TEXTURE_CUBE,
+ HLSL_TEXTURE_2D_ARRAY,
+ HLSL_TEXTURE_3D,
+ HLSL_TEXTURE_2D_INT4,
+ HLSL_TEXTURE_3D_INT4,
+ HLSL_TEXTURE_2D_ARRAY_INT4,
+ HLSL_TEXTURE_2D_UINT4,
+ HLSL_TEXTURE_3D_UINT4,
+ HLSL_TEXTURE_2D_ARRAY_UINT4,
+
+ // Comparison samplers
+
+ HLSL_TEXTURE_2D_COMPARISON,
+ HLSL_TEXTURE_CUBE_COMPARISON,
+ HLSL_TEXTURE_2D_ARRAY_COMPARISON,
+
+ HLSL_COMPARISON_SAMPLER_GROUP_BEGIN = HLSL_TEXTURE_2D_COMPARISON,
+ HLSL_COMPARISON_SAMPLER_GROUP_END = HLSL_TEXTURE_2D_ARRAY_COMPARISON,
+
+ HLSL_TEXTURE_UNKNOWN,
+ HLSL_TEXTURE_MAX = HLSL_TEXTURE_UNKNOWN
+};
+
+HLSLTextureSamplerGroup TextureGroup(const TBasicType type);
+TString TextureString(const HLSLTextureSamplerGroup type);
+TString TextureString(const TBasicType type);
+TString TextureGroupSuffix(const HLSLTextureSamplerGroup type);
+TString TextureGroupSuffix(const TBasicType type);
+TString TextureTypeSuffix(const TBasicType type);
+TString SamplerString(const TBasicType type);
+TString SamplerString(HLSLTextureSamplerGroup type);
+// Prepends an underscore to avoid naming clashes
+TString Decorate(const TString &string);
+TString DecorateIfNeeded(const TName &name);
+// Decorates and also unmangles the function name
+TString DecorateFunctionIfNeeded(const TName &name);
+TString DecorateUniform(const TName &name, const TType &type);
+TString DecorateField(const TString &string, const TStructure &structure);
+TString DecoratePrivate(const TString &privateText);
+TString TypeString(const TType &type);
+TString StructNameString(const TStructure &structure);
+TString QualifiedStructNameString(const TStructure &structure, bool useHLSLRowMajorPacking,
+ bool useStd140Packing);
+TString InterpolationString(TQualifier qualifier);
+TString QualifierString(TQualifier qualifier);
+// Parameters may need to be included in function names to disambiguate between overloaded
+// functions.
+TString DisambiguateFunctionName(const TIntermSequence *parameters);
+}
+
+#endif // COMPILER_TRANSLATOR_UTILSHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.cpp b/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.cpp
new file mode 100755
index 000000000..d5b1b3be4
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.cpp
@@ -0,0 +1,116 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ValidateGlobalInitializer.h"
+
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class ValidateGlobalInitializerTraverser : public TIntermTraverser
+{
+ public:
+ ValidateGlobalInitializerTraverser(const TParseContext *context);
+
+ void visitSymbol(TIntermSymbol *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *node) override;
+ bool visitUnary(Visit visit, TIntermUnary *node) override;
+
+ bool isValid() const { return mIsValid; }
+ bool issueWarning() const { return mIssueWarning; }
+
+ private:
+ const TParseContext *mContext;
+ bool mIsValid;
+ bool mIssueWarning;
+};
+
+void ValidateGlobalInitializerTraverser::visitSymbol(TIntermSymbol *node)
+{
+ const TSymbol *sym = mContext->symbolTable.find(node->getSymbol(), mContext->getShaderVersion());
+ if (sym->isVariable())
+ {
+ // ESSL 1.00 section 4.3 (or ESSL 3.00 section 4.3):
+ // Global initializers must be constant expressions.
+ const TVariable *var = static_cast<const TVariable *>(sym);
+ switch (var->getType().getQualifier())
+ {
+ case EvqConst:
+ break;
+ case EvqGlobal:
+ case EvqTemporary:
+ case EvqUniform:
+ // We allow these cases to be compatible with legacy ESSL 1.00 content.
+ // Implement stricter rules for ESSL 3.00 since there's no legacy content to deal with.
+ if (mContext->getShaderVersion() >= 300)
+ {
+ mIsValid = false;
+ }
+ else
+ {
+ mIssueWarning = true;
+ }
+ break;
+ default:
+ mIsValid = false;
+ }
+ }
+}
+
+bool ValidateGlobalInitializerTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ // Disallow calls to user-defined functions and texture lookup functions in global variable initializers.
+ // This is done simply by disabling all function calls - built-in math functions don't use EOpFunctionCall.
+ if (node->getOp() == EOpFunctionCall)
+ {
+ mIsValid = false;
+ }
+ return true;
+}
+
+bool ValidateGlobalInitializerTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ if (node->isAssignment())
+ {
+ mIsValid = false;
+ }
+ return true;
+}
+
+bool ValidateGlobalInitializerTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ if (node->isAssignment())
+ {
+ mIsValid = false;
+ }
+ return true;
+}
+
+ValidateGlobalInitializerTraverser::ValidateGlobalInitializerTraverser(const TParseContext *context)
+ : TIntermTraverser(true, false, false),
+ mContext(context),
+ mIsValid(true),
+ mIssueWarning(false)
+{
+}
+
+} // namespace
+
+bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning)
+{
+ ValidateGlobalInitializerTraverser validate(context);
+ initializer->traverse(&validate);
+ ASSERT(warning != nullptr);
+ *warning = validate.issueWarning();
+ return validate.isValid();
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.h b/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.h
new file mode 100755
index 000000000..89b4b1176
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateGlobalInitializer.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
+#define COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
+
+namespace sh
+{
+
+class TIntermTyped;
+class TParseContext;
+
+// Returns true if the initializer is valid.
+bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_
diff --git a/gfx/angle/src/compiler/translator/ValidateLimitations.cpp b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
new file mode 100755
index 000000000..75a0c5156
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
@@ -0,0 +1,502 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ValidateLimitations.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/InitializeParseContext.h"
+#include "compiler/translator/ParseContext.h"
+#include "angle_gl.h"
+
+namespace sh
+{
+
+namespace
+{
+
+// Traverses a node to check if it represents a constant index expression.
+// Definition:
+// constant-index-expressions are a superset of constant-expressions.
+// Constant-index-expressions can include loop indices as defined in
+// GLSL ES 1.0 spec, Appendix A, section 4.
+// The following are constant-index-expressions:
+// - Constant expressions
+// - Loop indices as defined in section 4
+// - Expressions composed of both of the above
+class ValidateConstIndexExpr : public TIntermTraverser
+{
+ public:
+ ValidateConstIndexExpr(TLoopStack& stack)
+ : TIntermTraverser(true, false, false),
+ mValid(true),
+ mLoopStack(stack)
+ {
+ }
+
+ // Returns true if the parsed node represents a constant index expression.
+ bool isValid() const { return mValid; }
+
+ void visitSymbol(TIntermSymbol *symbol) override
+ {
+ // Only constants and loop indices are allowed in a
+ // constant index expression.
+ if (mValid)
+ {
+ mValid = (symbol->getQualifier() == EvqConst) ||
+ (mLoopStack.findLoop(symbol));
+ }
+ }
+
+ private:
+ bool mValid;
+ TLoopStack& mLoopStack;
+};
+
+} // namespace anonymous
+
+ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink)
+ : TIntermTraverser(true, false, false),
+ mShaderType(shaderType),
+ mSink(sink),
+ mNumErrors(0),
+ mValidateIndexing(true),
+ mValidateInnerLoops(true)
+{
+}
+
+// static
+bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop)
+{
+ // The shader type doesn't matter in this case.
+ ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr);
+ validate.mValidateIndexing = false;
+ validate.mValidateInnerLoops = false;
+ if (!validate.validateLoopType(loop))
+ return false;
+ if (!validate.validateForLoopHeader(loop))
+ return false;
+ TIntermNode *body = loop->getBody();
+ if (body != nullptr)
+ {
+ validate.mLoopStack.push(loop);
+ body->traverse(&validate);
+ validate.mLoopStack.pop();
+ }
+ return (validate.mNumErrors == 0);
+}
+
+bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
+{
+ // Check if loop index is modified in the loop body.
+ validateOperation(node, node->getLeft());
+
+ // Check indexing.
+ switch (node->getOp())
+ {
+ case EOpIndexDirect:
+ case EOpIndexIndirect:
+ if (mValidateIndexing)
+ validateIndexing(node);
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool ValidateLimitations::visitUnary(Visit, TIntermUnary *node)
+{
+ // Check if loop index is modified in the loop body.
+ validateOperation(node, node->getOperand());
+
+ return true;
+}
+
+bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node)
+{
+ switch (node->getOp()) {
+ case EOpFunctionCall:
+ validateFunctionCall(node);
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
+{
+ if (!mValidateInnerLoops)
+ return true;
+
+ if (!validateLoopType(node))
+ return false;
+
+ if (!validateForLoopHeader(node))
+ return false;
+
+ TIntermNode *body = node->getBody();
+ if (body != NULL)
+ {
+ mLoopStack.push(node);
+ body->traverse(this);
+ mLoopStack.pop();
+ }
+
+ // The loop is fully processed - no need to visit children.
+ return false;
+}
+
+void ValidateLimitations::error(TSourceLoc loc,
+ const char *reason, const char *token)
+{
+ if (mSink)
+ {
+ mSink->prefix(EPrefixError);
+ mSink->location(loc);
+ (*mSink) << "'" << token << "' : " << reason << "\n";
+ }
+ ++mNumErrors;
+}
+
+bool ValidateLimitations::withinLoopBody() const
+{
+ return !mLoopStack.empty();
+}
+
+bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
+{
+ return mLoopStack.findLoop(symbol) != NULL;
+}
+
+bool ValidateLimitations::validateLoopType(TIntermLoop *node)
+{
+ TLoopType type = node->getType();
+ if (type == ELoopFor)
+ return true;
+
+ // Reject while and do-while loops.
+ error(node->getLine(),
+ "This type of loop is not allowed",
+ type == ELoopWhile ? "while" : "do");
+ return false;
+}
+
+bool ValidateLimitations::validateForLoopHeader(TIntermLoop *node)
+{
+ ASSERT(node->getType() == ELoopFor);
+
+ //
+ // The for statement has the form:
+ // for ( init-declaration ; condition ; expression ) statement
+ //
+ int indexSymbolId = validateForLoopInit(node);
+ if (indexSymbolId < 0)
+ return false;
+ if (!validateForLoopCond(node, indexSymbolId))
+ return false;
+ if (!validateForLoopExpr(node, indexSymbolId))
+ return false;
+
+ return true;
+}
+
+int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
+{
+ TIntermNode *init = node->getInit();
+ if (init == NULL)
+ {
+ error(node->getLine(), "Missing init declaration", "for");
+ return -1;
+ }
+
+ //
+ // init-declaration has the form:
+ // type-specifier identifier = constant-expression
+ //
+ TIntermDeclaration *decl = init->getAsDeclarationNode();
+ if (decl == nullptr)
+ {
+ error(init->getLine(), "Invalid init declaration", "for");
+ return -1;
+ }
+ // To keep things simple do not allow declaration list.
+ TIntermSequence *declSeq = decl->getSequence();
+ if (declSeq->size() != 1)
+ {
+ error(decl->getLine(), "Invalid init declaration", "for");
+ return -1;
+ }
+ TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
+ if ((declInit == NULL) || (declInit->getOp() != EOpInitialize))
+ {
+ error(decl->getLine(), "Invalid init declaration", "for");
+ return -1;
+ }
+ TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
+ if (symbol == NULL)
+ {
+ error(declInit->getLine(), "Invalid init declaration", "for");
+ return -1;
+ }
+ // The loop index has type int or float.
+ TBasicType type = symbol->getBasicType();
+ if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat)) {
+ error(symbol->getLine(),
+ "Invalid type for loop index", getBasicString(type));
+ return -1;
+ }
+ // The loop index is initialized with constant expression.
+ if (!isConstExpr(declInit->getRight()))
+ {
+ error(declInit->getLine(),
+ "Loop index cannot be initialized with non-constant expression",
+ symbol->getSymbol().c_str());
+ return -1;
+ }
+
+ return symbol->getId();
+}
+
+bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
+ int indexSymbolId)
+{
+ TIntermNode *cond = node->getCondition();
+ if (cond == NULL)
+ {
+ error(node->getLine(), "Missing condition", "for");
+ return false;
+ }
+ //
+ // condition has the form:
+ // loop_index relational_operator constant_expression
+ //
+ TIntermBinary *binOp = cond->getAsBinaryNode();
+ if (binOp == NULL)
+ {
+ error(node->getLine(), "Invalid condition", "for");
+ return false;
+ }
+ // Loop index should be to the left of relational operator.
+ TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
+ if (symbol == NULL)
+ {
+ error(binOp->getLine(), "Invalid condition", "for");
+ return false;
+ }
+ if (symbol->getId() != indexSymbolId)
+ {
+ error(symbol->getLine(),
+ "Expected loop index", symbol->getSymbol().c_str());
+ return false;
+ }
+ // Relational operator is one of: > >= < <= == or !=.
+ switch (binOp->getOp())
+ {
+ case EOpEqual:
+ case EOpNotEqual:
+ case EOpLessThan:
+ case EOpGreaterThan:
+ case EOpLessThanEqual:
+ case EOpGreaterThanEqual:
+ break;
+ default:
+ error(binOp->getLine(),
+ "Invalid relational operator",
+ GetOperatorString(binOp->getOp()));
+ break;
+ }
+ // Loop index must be compared with a constant.
+ if (!isConstExpr(binOp->getRight()))
+ {
+ error(binOp->getLine(),
+ "Loop index cannot be compared with non-constant expression",
+ symbol->getSymbol().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node,
+ int indexSymbolId)
+{
+ TIntermNode *expr = node->getExpression();
+ if (expr == NULL)
+ {
+ error(node->getLine(), "Missing expression", "for");
+ return false;
+ }
+
+ // for expression has one of the following forms:
+ // loop_index++
+ // loop_index--
+ // loop_index += constant_expression
+ // loop_index -= constant_expression
+ // ++loop_index
+ // --loop_index
+ // The last two forms are not specified in the spec, but I am assuming
+ // its an oversight.
+ TIntermUnary *unOp = expr->getAsUnaryNode();
+ TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
+
+ TOperator op = EOpNull;
+ TIntermSymbol *symbol = NULL;
+ if (unOp != NULL)
+ {
+ op = unOp->getOp();
+ symbol = unOp->getOperand()->getAsSymbolNode();
+ }
+ else if (binOp != NULL)
+ {
+ op = binOp->getOp();
+ symbol = binOp->getLeft()->getAsSymbolNode();
+ }
+
+ // The operand must be loop index.
+ if (symbol == NULL)
+ {
+ error(expr->getLine(), "Invalid expression", "for");
+ return false;
+ }
+ if (symbol->getId() != indexSymbolId)
+ {
+ error(symbol->getLine(),
+ "Expected loop index", symbol->getSymbol().c_str());
+ return false;
+ }
+
+ // The operator is one of: ++ -- += -=.
+ switch (op)
+ {
+ case EOpPostIncrement:
+ case EOpPostDecrement:
+ case EOpPreIncrement:
+ case EOpPreDecrement:
+ ASSERT((unOp != NULL) && (binOp == NULL));
+ break;
+ case EOpAddAssign:
+ case EOpSubAssign:
+ ASSERT((unOp == NULL) && (binOp != NULL));
+ break;
+ default:
+ error(expr->getLine(), "Invalid operator", GetOperatorString(op));
+ return false;
+ }
+
+ // Loop index must be incremented/decremented with a constant.
+ if (binOp != NULL)
+ {
+ if (!isConstExpr(binOp->getRight()))
+ {
+ error(binOp->getLine(),
+ "Loop index cannot be modified by non-constant expression",
+ symbol->getSymbol().c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node)
+{
+ ASSERT(node->getOp() == EOpFunctionCall);
+
+ // If not within loop body, there is nothing to check.
+ if (!withinLoopBody())
+ return true;
+
+ // List of param indices for which loop indices are used as argument.
+ typedef std::vector<size_t> ParamIndex;
+ ParamIndex pIndex;
+ TIntermSequence *params = node->getSequence();
+ for (TIntermSequence::size_type i = 0; i < params->size(); ++i)
+ {
+ TIntermSymbol *symbol = (*params)[i]->getAsSymbolNode();
+ if (symbol && isLoopIndex(symbol))
+ pIndex.push_back(i);
+ }
+ // If none of the loop indices are used as arguments,
+ // there is nothing to check.
+ if (pIndex.empty())
+ return true;
+
+ bool valid = true;
+ TSymbolTable& symbolTable = GetGlobalParseContext()->symbolTable;
+ TSymbol *symbol = symbolTable.find(node->getFunctionSymbolInfo()->getName(),
+ GetGlobalParseContext()->getShaderVersion());
+ ASSERT(symbol && symbol->isFunction());
+ TFunction *function = static_cast<TFunction *>(symbol);
+ for (ParamIndex::const_iterator i = pIndex.begin();
+ i != pIndex.end(); ++i)
+ {
+ const TConstParameter &param = function->getParam(*i);
+ TQualifier qual = param.type->getQualifier();
+ if ((qual == EvqOut) || (qual == EvqInOut))
+ {
+ error((*params)[*i]->getLine(),
+ "Loop index cannot be used as argument to a function out or inout parameter",
+ (*params)[*i]->getAsSymbolNode()->getSymbol().c_str());
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+bool ValidateLimitations::validateOperation(TIntermOperator *node,
+ TIntermNode* operand)
+{
+ // Check if loop index is modified in the loop body.
+ if (!withinLoopBody() || !node->isAssignment())
+ return true;
+
+ TIntermSymbol *symbol = operand->getAsSymbolNode();
+ if (symbol && isLoopIndex(symbol))
+ {
+ error(node->getLine(),
+ "Loop index cannot be statically assigned to within the body of the loop",
+ symbol->getSymbol().c_str());
+ }
+ return true;
+}
+
+bool ValidateLimitations::isConstExpr(TIntermNode *node)
+{
+ ASSERT(node != nullptr);
+ return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
+}
+
+bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
+{
+ ASSERT(node != NULL);
+
+ ValidateConstIndexExpr validate(mLoopStack);
+ node->traverse(&validate);
+ return validate.isValid();
+}
+
+bool ValidateLimitations::validateIndexing(TIntermBinary *node)
+{
+ ASSERT((node->getOp() == EOpIndexDirect) ||
+ (node->getOp() == EOpIndexIndirect));
+
+ bool valid = true;
+ TIntermTyped *index = node->getRight();
+ // The index expession must be a constant-index-expression unless
+ // the operand is a uniform in a vertex shader.
+ TIntermTyped *operand = node->getLeft();
+ bool skip = (mShaderType == GL_VERTEX_SHADER) &&
+ (operand->getQualifier() == EvqUniform);
+ if (!skip && !isConstIndexExpr(index))
+ {
+ error(index->getLine(), "Index expression must be constant", "[]");
+ valid = false;
+ }
+ return valid;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ValidateLimitations.h b/gfx/angle/src/compiler/translator/ValidateLimitations.h
new file mode 100755
index 000000000..4c84f9db7
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.h
@@ -0,0 +1,68 @@
+//
+// Copyright (c) 2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
+#define COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/LoopInfo.h"
+
+namespace sh
+{
+
+class TInfoSinkBase;
+
+// Traverses intermediate tree to ensure that the shader does not exceed the
+// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
+class ValidateLimitations : public TIntermTraverser
+{
+ public:
+ ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink);
+
+ int numErrors() const { return mNumErrors; }
+
+ bool visitBinary(Visit, TIntermBinary *) override;
+ bool visitUnary(Visit, TIntermUnary *) override;
+ bool visitAggregate(Visit, TIntermAggregate *) override;
+ bool visitLoop(Visit, TIntermLoop *) override;
+
+ static bool IsLimitedForLoop(TIntermLoop *node);
+
+ private:
+ void error(TSourceLoc loc, const char *reason, const char *token);
+
+ bool withinLoopBody() const;
+ bool isLoopIndex(TIntermSymbol *symbol);
+ bool validateLoopType(TIntermLoop *node);
+
+ bool validateForLoopHeader(TIntermLoop *node);
+ // If valid, return the index symbol id; Otherwise, return -1.
+ int validateForLoopInit(TIntermLoop *node);
+ bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
+ bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
+
+ // Returns true if none of the loop indices is used as the argument to
+ // the given function out or inout parameter.
+ bool validateFunctionCall(TIntermAggregate *node);
+ bool validateOperation(TIntermOperator *node, TIntermNode *operand);
+
+ // Returns true if indexing does not exceed the minimum functionality
+ // mandated in GLSL 1.0 spec, Appendix A, Section 5.
+ bool isConstExpr(TIntermNode *node);
+ bool isConstIndexExpr(TIntermNode *node);
+ bool validateIndexing(TIntermBinary *node);
+
+ sh::GLenum mShaderType;
+ TInfoSinkBase *mSink;
+ int mNumErrors;
+ TLoopStack mLoopStack;
+ bool mValidateIndexing;
+ bool mValidateInnerLoops;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
diff --git a/gfx/angle/src/compiler/translator/ValidateMaxParameters.cpp b/gfx/angle/src/compiler/translator/ValidateMaxParameters.cpp
new file mode 100755
index 000000000..f97f7a907
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateMaxParameters.cpp
@@ -0,0 +1,40 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#include "compiler/translator/ValidateMaxParameters.h"
+
+namespace sh
+{
+
+ValidateMaxParameters::ValidateMaxParameters(unsigned int maxParameters)
+ : TIntermTraverser(true, false, false), mMaxParameters(maxParameters), mValid(true)
+{
+}
+
+bool ValidateMaxParameters::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ if (!mValid)
+ {
+ return false;
+ }
+
+ if (node->getOp() == EOpParameters && node->getSequence()->size() > mMaxParameters)
+ {
+ mValid = false;
+ }
+
+ return mValid;
+}
+
+bool ValidateMaxParameters::validate(TIntermNode *root, unsigned int maxParameters)
+{
+ ValidateMaxParameters argsTraverser(maxParameters);
+ root->traverse(&argsTraverser);
+ return argsTraverser.mValid;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ValidateMaxParameters.h b/gfx/angle/src/compiler/translator/ValidateMaxParameters.h
new file mode 100755
index 000000000..fdd0b8d09
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateMaxParameters.h
@@ -0,0 +1,34 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// ValidateMaxParameters checks if function definitions have more than a set number of parameters.
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+#define COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+class ValidateMaxParameters : public TIntermTraverser
+{
+ public:
+ // Returns false if maxParameters is exceeded.
+ static bool validate(TIntermNode *root, unsigned int maxParameters);
+
+ protected:
+ bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+
+ private:
+ ValidateMaxParameters(unsigned int maxParameters);
+
+ unsigned int mMaxParameters;
+ bool mValid;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_
diff --git a/gfx/angle/src/compiler/translator/ValidateOutputs.cpp b/gfx/angle/src/compiler/translator/ValidateOutputs.cpp
new file mode 100755
index 000000000..e48da8a79
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateOutputs.cpp
@@ -0,0 +1,113 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ValidateOutputs.h"
+#include "compiler/translator/InfoSink.h"
+#include "compiler/translator/InitializeParseContext.h"
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
+
+namespace
+{
+void error(int *errorCount, TInfoSinkBase &sink, const TIntermSymbol &symbol, const char *reason)
+{
+ sink.prefix(EPrefixError);
+ sink.location(symbol.getLine());
+ sink << "'" << symbol.getSymbol() << "' : " << reason << "\n";
+ (*errorCount)++;
+}
+
+} // namespace
+
+ValidateOutputs::ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers)
+ : TIntermTraverser(true, false, false),
+ mMaxDrawBuffers(maxDrawBuffers),
+ mAllowUnspecifiedOutputLocationResolution(
+ IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended"))
+{
+}
+
+void ValidateOutputs::visitSymbol(TIntermSymbol *symbol)
+{
+ TString name = symbol->getSymbol();
+ TQualifier qualifier = symbol->getQualifier();
+
+ if (mVisitedSymbols.count(name.c_str()) == 1)
+ return;
+
+ mVisitedSymbols.insert(name.c_str());
+
+ if (qualifier == EvqFragmentOut)
+ {
+ if (symbol->getType().getLayoutQualifier().location == -1)
+ {
+ mUnspecifiedLocationOutputs.push_back(symbol);
+ }
+ else
+ {
+ mOutputs.push_back(symbol);
+ }
+ }
+}
+
+int ValidateOutputs::validateAndCountErrors(TInfoSinkBase &sink) const
+{
+ OutputVector validOutputs(mMaxDrawBuffers);
+ int errorCount = 0;
+
+ for (const auto &symbol : mOutputs)
+ {
+ const TType &type = symbol->getType();
+ const size_t elementCount = static_cast<size_t>(type.isArray() ? type.getArraySize() : 1u);
+ const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
+
+ ASSERT(type.getLayoutQualifier().location != -1);
+
+ if (location + elementCount <= validOutputs.size())
+ {
+ for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
+ {
+ const size_t offsetLocation = location + elementIndex;
+ if (validOutputs[offsetLocation])
+ {
+ std::stringstream strstr;
+ strstr << "conflicting output locations with previously defined output '"
+ << validOutputs[offsetLocation]->getSymbol() << "'";
+ error(&errorCount, sink, *symbol, strstr.str().c_str());
+ }
+ else
+ {
+ validOutputs[offsetLocation] = symbol;
+ }
+ }
+ }
+ else
+ {
+ if (elementCount > 0)
+ {
+ error(&errorCount, sink, *symbol,
+ elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
+ : "output location must be < MAX_DRAW_BUFFERS");
+ }
+ }
+ }
+
+ if (!mAllowUnspecifiedOutputLocationResolution &&
+ ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
+ mUnspecifiedLocationOutputs.size() > 1))
+ {
+ for (const auto &symbol : mUnspecifiedLocationOutputs)
+ {
+ error(&errorCount, sink, *symbol,
+ "must explicitly specify all locations when using multiple fragment outputs");
+ }
+ }
+ return errorCount;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ValidateOutputs.h b/gfx/angle/src/compiler/translator/ValidateOutputs.h
new file mode 100755
index 000000000..0788f4ed6
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateOutputs.h
@@ -0,0 +1,41 @@
+//
+// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
+#define COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
+
+#include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/IntermNode.h"
+
+#include <set>
+
+namespace sh
+{
+
+class TInfoSinkBase;
+
+class ValidateOutputs : public TIntermTraverser
+{
+ public:
+ ValidateOutputs(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
+
+ int validateAndCountErrors(TInfoSinkBase &sink) const;
+
+ void visitSymbol(TIntermSymbol *) override;
+
+ private:
+ int mMaxDrawBuffers;
+ bool mAllowUnspecifiedOutputLocationResolution;
+
+ typedef std::vector<TIntermSymbol *> OutputVector;
+ OutputVector mOutputs;
+ OutputVector mUnspecifiedLocationOutputs;
+ std::set<std::string> mVisitedSymbols;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_
diff --git a/gfx/angle/src/compiler/translator/ValidateSwitch.cpp b/gfx/angle/src/compiler/translator/ValidateSwitch.cpp
new file mode 100755
index 000000000..9bcd8f5fe
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateSwitch.cpp
@@ -0,0 +1,215 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/ValidateSwitch.h"
+
+#include "compiler/translator/ParseContext.h"
+
+namespace sh
+{
+
+bool ValidateSwitch::validate(TBasicType switchType,
+ TParseContext *context,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc)
+{
+ ValidateSwitch validate(switchType, context);
+ ASSERT(statementList);
+ statementList->traverse(&validate);
+ return validate.validateInternal(loc);
+}
+
+ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
+ : TIntermTraverser(true, false, true),
+ mSwitchType(switchType),
+ mContext(context),
+ mCaseTypeMismatch(false),
+ mFirstCaseFound(false),
+ mStatementBeforeCase(false),
+ mLastStatementWasCase(false),
+ mControlFlowDepth(0),
+ mCaseInsideControlFlow(false),
+ mDefaultCount(0),
+ mDuplicateCases(false)
+{}
+
+void ValidateSwitch::visitSymbol(TIntermSymbol *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+}
+
+void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
+{
+ // Conditions of case labels are not traversed, so this is some other constant
+ // Could be just a statement like "0;"
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+}
+
+bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
+{
+ if (visit == PreVisit)
+ ++mControlFlowDepth;
+ if (visit == PostVisit)
+ --mControlFlowDepth;
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ // Don't go into nested switch statements
+ return false;
+}
+
+bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
+{
+ const char *nodeStr = node->hasCondition() ? "case" : "default";
+ if (mControlFlowDepth > 0)
+ {
+ mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
+ mCaseInsideControlFlow = true;
+ }
+ mFirstCaseFound = true;
+ mLastStatementWasCase = true;
+ if (!node->hasCondition())
+ {
+ ++mDefaultCount;
+ if (mDefaultCount > 1)
+ {
+ mContext->error(node->getLine(), "duplicate default label", nodeStr);
+ }
+ }
+ else
+ {
+ TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
+ if (condition == nullptr)
+ {
+ // This can happen in error cases.
+ return false;
+ }
+ TBasicType conditionType = condition->getBasicType();
+ if (conditionType != mSwitchType)
+ {
+ mContext->error(condition->getLine(),
+ "case label type does not match switch init-expression type", nodeStr);
+ mCaseTypeMismatch = true;
+ }
+
+ if (conditionType == EbtInt)
+ {
+ int iConst = condition->getIConst(0);
+ if (mCasesSigned.find(iConst) != mCasesSigned.end())
+ {
+ mContext->error(condition->getLine(), "duplicate case label", nodeStr);
+ mDuplicateCases = true;
+ }
+ else
+ {
+ mCasesSigned.insert(iConst);
+ }
+ }
+ else if (conditionType == EbtUInt)
+ {
+ unsigned int uConst = condition->getUConst(0);
+ if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
+ {
+ mContext->error(condition->getLine(), "duplicate case label", nodeStr);
+ mDuplicateCases = true;
+ }
+ else
+ {
+ mCasesUnsigned.insert(uConst);
+ }
+ }
+ // Other types are possible only in error cases, where the error has already been generated
+ // when parsing the case statement.
+ }
+ // Don't traverse the condition of the case statement
+ return false;
+}
+
+bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
+{
+ if (getParentNode() != nullptr)
+ {
+ // This is not the statementList node, but some other node.
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ }
+ return true;
+}
+
+bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
+{
+ if (visit == PreVisit)
+ ++mControlFlowDepth;
+ if (visit == PostVisit)
+ --mControlFlowDepth;
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
+{
+ if (mStatementBeforeCase)
+ {
+ mContext->error(loc,
+ "statement before the first label", "switch");
+ }
+ if (mLastStatementWasCase)
+ {
+ mContext->error(loc,
+ "no statement between the last label and the end of the switch statement", "switch");
+ }
+ return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
+ !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/ValidateSwitch.h b/gfx/angle/src/compiler/translator/ValidateSwitch.h
new file mode 100755
index 000000000..feffbc03c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/ValidateSwitch.h
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATESWITCH_H_
+#define COMPILER_TRANSLATOR_VALIDATESWITCH_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+class TParseContext;
+
+class ValidateSwitch : public TIntermTraverser
+{
+ public:
+ // Check for errors and output messages any remaining errors on the context.
+ // Returns true if there are no errors.
+ static bool validate(TBasicType switchType,
+ TParseContext *context,
+ TIntermBlock *statementList,
+ const TSourceLoc &loc);
+
+ void visitSymbol(TIntermSymbol *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitBinary(Visit, TIntermBinary *) override;
+ bool visitUnary(Visit, TIntermUnary *) override;
+ bool visitTernary(Visit, TIntermTernary *) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *) override;
+ bool visitSwitch(Visit, TIntermSwitch *) override;
+ bool visitCase(Visit, TIntermCase *node) override;
+ bool visitAggregate(Visit, TIntermAggregate *) override;
+ bool visitLoop(Visit visit, TIntermLoop *) override;
+ bool visitBranch(Visit, TIntermBranch *) override;
+
+ private:
+ ValidateSwitch(TBasicType switchType, TParseContext *context);
+
+ bool validateInternal(const TSourceLoc &loc);
+
+ TBasicType mSwitchType;
+ TParseContext *mContext;
+ bool mCaseTypeMismatch;
+ bool mFirstCaseFound;
+ bool mStatementBeforeCase;
+ bool mLastStatementWasCase;
+ int mControlFlowDepth;
+ bool mCaseInsideControlFlow;
+ int mDefaultCount;
+ std::set<int> mCasesSigned;
+ std::set<unsigned int> mCasesUnsigned;
+ bool mDuplicateCases;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VALIDATESWITCH_H_
diff --git a/gfx/angle/src/compiler/translator/VariableInfo.cpp b/gfx/angle/src/compiler/translator/VariableInfo.cpp
new file mode 100755
index 000000000..07fe12e5d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VariableInfo.cpp
@@ -0,0 +1,682 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "angle_gl.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/VariableInfo.h"
+#include "compiler/translator/util.h"
+#include "common/utilities.h"
+
+namespace sh
+{
+
+namespace
+{
+
+BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
+{
+ switch (blockStorage)
+ {
+ case EbsPacked: return BLOCKLAYOUT_PACKED;
+ case EbsShared: return BLOCKLAYOUT_SHARED;
+ case EbsStd140: return BLOCKLAYOUT_STANDARD;
+ default: UNREACHABLE(); return BLOCKLAYOUT_SHARED;
+ }
+}
+
+void ExpandUserDefinedVariable(const ShaderVariable &variable,
+ const std::string &name,
+ const std::string &mappedName,
+ bool markStaticUse,
+ std::vector<ShaderVariable> *expanded)
+{
+ ASSERT(variable.isStruct());
+
+ const std::vector<ShaderVariable> &fields = variable.fields;
+
+ for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+ {
+ const ShaderVariable &field = fields[fieldIndex];
+ ExpandVariable(field,
+ name + "." + field.name,
+ mappedName + "." + field.mappedName,
+ markStaticUse,
+ expanded);
+ }
+}
+
+template <class VarT>
+VarT *FindVariable(const TString &name,
+ std::vector<VarT> *infoList)
+{
+ // TODO(zmo): optimize this function.
+ for (size_t ii = 0; ii < infoList->size(); ++ii)
+ {
+ if ((*infoList)[ii].name.c_str() == name)
+ return &((*infoList)[ii]);
+ }
+
+ return NULL;
+}
+
+}
+
+CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
+ std::vector<sh::OutputVariable> *outputVariables,
+ std::vector<sh::Uniform> *uniforms,
+ std::vector<sh::Varying> *varyings,
+ std::vector<sh::InterfaceBlock> *interfaceBlocks,
+ ShHashFunction64 hashFunction,
+ const TSymbolTable &symbolTable,
+ const TExtensionBehavior &extensionBehavior)
+ : TIntermTraverser(true, false, false),
+ mAttribs(attribs),
+ mOutputVariables(outputVariables),
+ mUniforms(uniforms),
+ mVaryings(varyings),
+ mInterfaceBlocks(interfaceBlocks),
+ mDepthRangeAdded(false),
+ mPointCoordAdded(false),
+ mFrontFacingAdded(false),
+ mFragCoordAdded(false),
+ mInstanceIDAdded(false),
+ mVertexIDAdded(false),
+ mPositionAdded(false),
+ mPointSizeAdded(false),
+ mLastFragDataAdded(false),
+ mFragColorAdded(false),
+ mFragDataAdded(false),
+ mFragDepthEXTAdded(false),
+ mFragDepthAdded(false),
+ mSecondaryFragColorEXTAdded(false),
+ mSecondaryFragDataEXTAdded(false),
+ mHashFunction(hashFunction),
+ mSymbolTable(symbolTable),
+ mExtensionBehavior(extensionBehavior)
+{
+}
+
+// We want to check whether a uniform/varying is statically used
+// because we only count the used ones in packing computing.
+// Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
+// toward varying counting if they are statically used in a fragment
+// shader.
+void CollectVariables::visitSymbol(TIntermSymbol *symbol)
+{
+ ASSERT(symbol != NULL);
+ ShaderVariable *var = NULL;
+ const TString &symbolName = symbol->getSymbol();
+
+ if (IsVarying(symbol->getQualifier()))
+ {
+ var = FindVariable(symbolName, mVaryings);
+ }
+ else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
+ {
+ UNREACHABLE();
+ }
+ else if (symbolName == "gl_DepthRange")
+ {
+ ASSERT(symbol->getQualifier() == EvqUniform);
+
+ if (!mDepthRangeAdded)
+ {
+ Uniform info;
+ const char kName[] = "gl_DepthRange";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_STRUCT_ANGLEX;
+ info.arraySize = 0;
+ info.precision = GL_NONE;
+ info.staticUse = true;
+
+ ShaderVariable nearInfo;
+ const char kNearName[] = "near";
+ nearInfo.name = kNearName;
+ nearInfo.mappedName = kNearName;
+ nearInfo.type = GL_FLOAT;
+ nearInfo.arraySize = 0;
+ nearInfo.precision = GL_HIGH_FLOAT;
+ nearInfo.staticUse = true;
+
+ ShaderVariable farInfo;
+ const char kFarName[] = "far";
+ farInfo.name = kFarName;
+ farInfo.mappedName = kFarName;
+ farInfo.type = GL_FLOAT;
+ farInfo.arraySize = 0;
+ farInfo.precision = GL_HIGH_FLOAT;
+ farInfo.staticUse = true;
+
+ ShaderVariable diffInfo;
+ const char kDiffName[] = "diff";
+ diffInfo.name = kDiffName;
+ diffInfo.mappedName = kDiffName;
+ diffInfo.type = GL_FLOAT;
+ diffInfo.arraySize = 0;
+ diffInfo.precision = GL_HIGH_FLOAT;
+ diffInfo.staticUse = true;
+
+ info.fields.push_back(nearInfo);
+ info.fields.push_back(farInfo);
+ info.fields.push_back(diffInfo);
+
+ mUniforms->push_back(info);
+ mDepthRangeAdded = true;
+ }
+ }
+ else
+ {
+ switch (symbol->getQualifier())
+ {
+ case EvqAttribute:
+ case EvqVertexIn:
+ var = FindVariable(symbolName, mAttribs);
+ break;
+ case EvqFragmentOut:
+ var = FindVariable(symbolName, mOutputVariables);
+ break;
+ case EvqUniform:
+ {
+ const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
+ if (interfaceBlock)
+ {
+ InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
+ ASSERT(namedBlock);
+ var = FindVariable(symbolName, &namedBlock->fields);
+
+ // Set static use on the parent interface block here
+ namedBlock->staticUse = true;
+ }
+ else
+ {
+ var = FindVariable(symbolName, mUniforms);
+ }
+
+ // It's an internal error to reference an undefined user uniform
+ ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
+ }
+ break;
+ case EvqFragCoord:
+ if (!mFragCoordAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_FragCoord";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ info.arraySize = 0;
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mFragCoordAdded = true;
+ }
+ return;
+ case EvqFrontFacing:
+ if (!mFrontFacingAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_FrontFacing";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_BOOL;
+ info.arraySize = 0;
+ info.precision = GL_NONE;
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mFrontFacingAdded = true;
+ }
+ return;
+ case EvqPointCoord:
+ if (!mPointCoordAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_PointCoord";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC2;
+ info.arraySize = 0;
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mPointCoordAdded = true;
+ }
+ return;
+ case EvqInstanceID:
+ if (!mInstanceIDAdded)
+ {
+ Attribute info;
+ const char kName[] = "gl_InstanceID";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_INT;
+ info.arraySize = 0;
+ info.precision = GL_HIGH_INT; // Defined by spec.
+ info.staticUse = true;
+ info.location = -1;
+ mAttribs->push_back(info);
+ mInstanceIDAdded = true;
+ }
+ return;
+ case EvqVertexID:
+ if (!mVertexIDAdded)
+ {
+ Attribute info;
+ const char kName[] = "gl_VertexID";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_INT;
+ info.arraySize = 0;
+ info.precision = GL_HIGH_INT; // Defined by spec.
+ info.staticUse = true;
+ info.location = -1;
+ mAttribs->push_back(info);
+ mVertexIDAdded = true;
+ }
+ return;
+ case EvqPosition:
+ if (!mPositionAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_Position";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ info.arraySize = 0;
+ info.precision = GL_HIGH_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mPositionAdded = true;
+ }
+ return;
+ case EvqPointSize:
+ if (!mPointSizeAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_PointSize";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT;
+ info.arraySize = 0;
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mPointSizeAdded = true;
+ }
+ return;
+ case EvqLastFragData:
+ if (!mLastFragDataAdded)
+ {
+ Varying info;
+ const char kName[] = "gl_LastFragData";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ info.arraySize = static_cast<const TVariable*>(mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))->getConstPointer()->getIConst();
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
+ mVaryings->push_back(info);
+ mLastFragDataAdded = true;
+ }
+ return;
+ case EvqFragColor:
+ if (!mFragColorAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_FragColor";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ info.arraySize = 0;
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mFragColorAdded = true;
+ }
+ return;
+ case EvqFragData:
+ if (!mFragDataAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_FragData";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ if (::IsExtensionEnabled(mExtensionBehavior, "GL_EXT_draw_buffers"))
+ {
+ info.arraySize = static_cast<const TVariable *>(
+ mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))
+ ->getConstPointer()
+ ->getIConst();
+ }
+ else
+ {
+ info.arraySize = 1;
+ }
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mFragDataAdded = true;
+ }
+ return;
+ case EvqFragDepthEXT:
+ if (!mFragDepthEXTAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_FragDepthEXT";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT;
+ info.arraySize = 0;
+ info.precision =
+ GLVariablePrecision(static_cast<const TVariable *>(
+ mSymbolTable.findBuiltIn("gl_FragDepthEXT", 100))
+ ->getType());
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mFragDepthEXTAdded = true;
+ }
+ return;
+ case EvqFragDepth:
+ if (!mFragDepthAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_FragDepth";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT;
+ info.arraySize = 0;
+ info.precision = GL_HIGH_FLOAT;
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mFragDepthAdded = true;
+ }
+ return;
+ case EvqSecondaryFragColorEXT:
+ if (!mSecondaryFragColorEXTAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_SecondaryFragColorEXT";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+ info.arraySize = 0;
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mSecondaryFragColorEXTAdded = true;
+ }
+ return;
+ case EvqSecondaryFragDataEXT:
+ if (!mSecondaryFragDataEXTAdded)
+ {
+ OutputVariable info;
+ const char kName[] = "gl_SecondaryFragDataEXT";
+ info.name = kName;
+ info.mappedName = kName;
+ info.type = GL_FLOAT_VEC4;
+
+ const TVariable *maxDualSourceDrawBuffersVar = static_cast<const TVariable *>(
+ mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100));
+ info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst();
+ info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
+ info.staticUse = true;
+ mOutputVariables->push_back(info);
+ mSecondaryFragDataEXTAdded = true;
+ }
+ return;
+ default:
+ break;
+ }
+ }
+ if (var)
+ {
+ var->staticUse = true;
+ }
+}
+
+class NameHashingTraverser : public GetVariableTraverser
+{
+ public:
+ NameHashingTraverser(ShHashFunction64 hashFunction,
+ const TSymbolTable &symbolTable)
+ : GetVariableTraverser(symbolTable),
+ mHashFunction(hashFunction)
+ {}
+
+ private:
+ void visitVariable(ShaderVariable *variable) override
+ {
+ TString stringName = TString(variable->name.c_str());
+ variable->mappedName = TIntermTraverser::hash(stringName, mHashFunction).c_str();
+ }
+
+ ShHashFunction64 mHashFunction;
+};
+
+// Attributes, which cannot have struct fields, are a special case
+template <>
+void CollectVariables::visitVariable(const TIntermSymbol *variable,
+ std::vector<Attribute> *infoList) const
+{
+ ASSERT(variable);
+ const TType &type = variable->getType();
+ ASSERT(!type.getStruct());
+
+ Attribute attribute;
+
+ attribute.type = GLVariableType(type);
+ attribute.precision = GLVariablePrecision(type);
+ attribute.name = variable->getSymbol().c_str();
+ attribute.arraySize = type.getArraySize();
+ attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
+ attribute.location = variable->getType().getLayoutQualifier().location;
+
+ infoList->push_back(attribute);
+}
+
+template <>
+void CollectVariables::visitVariable(const TIntermSymbol *variable,
+ std::vector<OutputVariable> *infoList) const
+{
+ ASSERT(variable);
+ const TType &type = variable->getType();
+ ASSERT(!type.getStruct());
+
+ OutputVariable attribute;
+
+ attribute.type = GLVariableType(type);
+ attribute.precision = GLVariablePrecision(type);
+ attribute.name = variable->getSymbol().c_str();
+ attribute.arraySize = type.getArraySize();
+ attribute.mappedName = TIntermTraverser::hash(variable->getSymbol(), mHashFunction).c_str();
+ attribute.location = variable->getType().getLayoutQualifier().location;
+
+ infoList->push_back(attribute);
+}
+
+template <>
+void CollectVariables::visitVariable(const TIntermSymbol *variable,
+ std::vector<InterfaceBlock> *infoList) const
+{
+ InterfaceBlock interfaceBlock;
+ const TInterfaceBlock *blockType = variable->getType().getInterfaceBlock();
+ ASSERT(blockType);
+
+ interfaceBlock.name = blockType->name().c_str();
+ interfaceBlock.mappedName =
+ TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str();
+ interfaceBlock.instanceName = (blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
+ interfaceBlock.arraySize = variable->getArraySize();
+ interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
+ interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
+
+ // Gather field information
+ for (const TField *field : blockType->fields())
+ {
+ const TType &fieldType = *field->type();
+
+ NameHashingTraverser traverser(mHashFunction, mSymbolTable);
+ traverser.traverse(fieldType, field->name(), &interfaceBlock.fields);
+
+ interfaceBlock.fields.back().isRowMajorLayout = (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
+ }
+
+ infoList->push_back(interfaceBlock);
+}
+
+template <typename VarT>
+void CollectVariables::visitVariable(const TIntermSymbol *variable,
+ std::vector<VarT> *infoList) const
+{
+ NameHashingTraverser traverser(mHashFunction, mSymbolTable);
+ traverser.traverse(variable->getType(), variable->getSymbol(), infoList);
+}
+
+template <typename VarT>
+void CollectVariables::visitInfoList(const TIntermSequence &sequence,
+ std::vector<VarT> *infoList) const
+{
+ for (size_t seqIndex = 0; seqIndex < sequence.size(); seqIndex++)
+ {
+ const TIntermSymbol *variable = sequence[seqIndex]->getAsSymbolNode();
+ // The only case in which the sequence will not contain a
+ // TIntermSymbol node is initialization. It will contain a
+ // TInterBinary node in that case. Since attributes, uniforms,
+ // and varyings cannot be initialized in a shader, we must have
+ // only TIntermSymbol nodes in the sequence.
+ ASSERT(variable != NULL);
+ visitVariable(variable, infoList);
+ }
+}
+
+bool CollectVariables::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ ASSERT(!sequence.empty());
+
+ const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
+ TQualifier qualifier = typedNode.getQualifier();
+
+ if (typedNode.getBasicType() == EbtInterfaceBlock)
+ {
+ visitInfoList(sequence, mInterfaceBlocks);
+ return false;
+ }
+ else if (qualifier == EvqAttribute || qualifier == EvqVertexIn || qualifier == EvqFragmentOut ||
+ qualifier == EvqUniform || IsVarying(qualifier))
+ {
+ switch (qualifier)
+ {
+ case EvqAttribute:
+ case EvqVertexIn:
+ visitInfoList(sequence, mAttribs);
+ break;
+ case EvqFragmentOut:
+ visitInfoList(sequence, mOutputVariables);
+ break;
+ case EvqUniform:
+ visitInfoList(sequence, mUniforms);
+ break;
+ default:
+ visitInfoList(sequence, mVaryings);
+ break;
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode)
+{
+ if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
+ {
+ // NOTE: we do not determine static use for individual blocks of an array
+ TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
+ ASSERT(blockNode);
+
+ TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
+ ASSERT(constantUnion);
+
+ const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
+ InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
+ ASSERT(namedBlock);
+ namedBlock->staticUse = true;
+
+ unsigned int fieldIndex = constantUnion->getUConst(0);
+ ASSERT(fieldIndex < namedBlock->fields.size());
+ namedBlock->fields[fieldIndex].staticUse = true;
+ return false;
+ }
+
+ return true;
+}
+
+void ExpandVariable(const ShaderVariable &variable,
+ const std::string &name,
+ const std::string &mappedName,
+ bool markStaticUse,
+ std::vector<ShaderVariable> *expanded)
+{
+ if (variable.isStruct())
+ {
+ if (variable.isArray())
+ {
+ for (unsigned int elementIndex = 0; elementIndex < variable.elementCount();
+ elementIndex++)
+ {
+ std::string lname = name + ::ArrayString(elementIndex);
+ std::string lmappedName = mappedName + ::ArrayString(elementIndex);
+ ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
+ }
+ }
+ else
+ {
+ ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
+ }
+ }
+ else
+ {
+ ShaderVariable expandedVar = variable;
+
+ expandedVar.name = name;
+ expandedVar.mappedName = mappedName;
+
+ // Mark all expanded fields as used if the parent is used
+ if (markStaticUse)
+ {
+ expandedVar.staticUse = true;
+ }
+
+ if (expandedVar.isArray())
+ {
+ expandedVar.name += "[0]";
+ expandedVar.mappedName += "[0]";
+ }
+
+ expanded->push_back(expandedVar);
+ }
+}
+
+void ExpandUniforms(const std::vector<Uniform> &compact,
+ std::vector<ShaderVariable> *expanded)
+{
+ for (size_t variableIndex = 0; variableIndex < compact.size(); variableIndex++)
+ {
+ const ShaderVariable &variable = compact[variableIndex];
+ ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse, expanded);
+ }
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/VariableInfo.h b/gfx/angle/src/compiler/translator/VariableInfo.h
new file mode 100755
index 000000000..6b2115656
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VariableInfo.h
@@ -0,0 +1,87 @@
+//
+// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VARIABLEINFO_H_
+#define COMPILER_TRANSLATOR_VARIABLEINFO_H_
+
+#include <GLSLANG/ShaderLang.h>
+
+#include "compiler/translator/ExtensionBehavior.h"
+#include "compiler/translator/IntermNode.h"
+
+class TSymbolTable;
+
+namespace sh
+{
+
+// Traverses intermediate tree to collect all attributes, uniforms, varyings.
+class CollectVariables : public TIntermTraverser
+{
+ public:
+ CollectVariables(std::vector<Attribute> *attribs,
+ std::vector<OutputVariable> *outputVariables,
+ std::vector<Uniform> *uniforms,
+ std::vector<Varying> *varyings,
+ std::vector<InterfaceBlock> *interfaceBlocks,
+ ShHashFunction64 hashFunction,
+ const TSymbolTable &symbolTable,
+ const TExtensionBehavior &extensionBehavior);
+
+ void visitSymbol(TIntermSymbol *symbol) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
+
+ private:
+ template <typename VarT>
+ void visitVariable(const TIntermSymbol *variable, std::vector<VarT> *infoList) const;
+
+ template <typename VarT>
+ void visitInfoList(const TIntermSequence &sequence, std::vector<VarT> *infoList) const;
+
+ std::vector<Attribute> *mAttribs;
+ std::vector<OutputVariable> *mOutputVariables;
+ std::vector<Uniform> *mUniforms;
+ std::vector<Varying> *mVaryings;
+ std::vector<InterfaceBlock> *mInterfaceBlocks;
+
+ std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
+
+ bool mDepthRangeAdded;
+ bool mPointCoordAdded;
+ bool mFrontFacingAdded;
+ bool mFragCoordAdded;
+
+ bool mInstanceIDAdded;
+ bool mVertexIDAdded;
+ bool mPositionAdded;
+ bool mPointSizeAdded;
+ bool mLastFragDataAdded;
+ bool mFragColorAdded;
+ bool mFragDataAdded;
+ bool mFragDepthEXTAdded;
+ bool mFragDepthAdded;
+ bool mSecondaryFragColorEXTAdded;
+ bool mSecondaryFragDataEXTAdded;
+
+ ShHashFunction64 mHashFunction;
+
+ const TSymbolTable &mSymbolTable;
+ const TExtensionBehavior &mExtensionBehavior;
+};
+
+void ExpandVariable(const ShaderVariable &variable,
+ const std::string &name,
+ const std::string &mappedName,
+ bool markStaticUse,
+ std::vector<ShaderVariable> *expanded);
+
+// Expand struct uniforms to flattened lists of split variables
+void ExpandUniforms(const std::vector<Uniform> &compact,
+ std::vector<ShaderVariable> *expanded);
+
+}
+
+#endif // COMPILER_TRANSLATOR_VARIABLEINFO_H_
diff --git a/gfx/angle/src/compiler/translator/VariablePacker.cpp b/gfx/angle/src/compiler/translator/VariablePacker.cpp
new file mode 100755
index 000000000..a981c8ae0
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VariablePacker.cpp
@@ -0,0 +1,269 @@
+//
+// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include <algorithm>
+
+#include "angle_gl.h"
+
+#include "compiler/translator/VariablePacker.h"
+#include "common/utilities.h"
+
+int VariablePacker::GetNumComponentsPerRow(sh::GLenum type)
+{
+ switch (type)
+ {
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_VEC4:
+ case GL_INT_VEC4:
+ case GL_BOOL_VEC4:
+ case GL_UNSIGNED_INT_VEC4:
+ return 4;
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_VEC3:
+ case GL_INT_VEC3:
+ case GL_BOOL_VEC3:
+ case GL_UNSIGNED_INT_VEC3:
+ return 3;
+ case GL_FLOAT_VEC2:
+ case GL_INT_VEC2:
+ case GL_BOOL_VEC2:
+ case GL_UNSIGNED_INT_VEC2:
+ return 2;
+ default:
+ ASSERT(gl::VariableComponentCount(type) == 1);
+ return 1;
+ }
+}
+
+int VariablePacker::GetNumRows(sh::GLenum type)
+{
+ switch (type)
+ {
+ case GL_FLOAT_MAT4:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x3:
+ case GL_FLOAT_MAT4x2:
+ return 4;
+ case GL_FLOAT_MAT3:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT3x2:
+ return 3;
+ case GL_FLOAT_MAT2:
+ return 2;
+ default:
+ ASSERT(gl::VariableRowCount(type) == 1);
+ return 1;
+ }
+}
+
+struct TVariableInfoComparer
+{
+ bool operator()(const sh::ShaderVariable &lhs, const sh::ShaderVariable &rhs) const
+ {
+ int lhsSortOrder = gl::VariableSortOrder(lhs.type);
+ int rhsSortOrder = gl::VariableSortOrder(rhs.type);
+ if (lhsSortOrder != rhsSortOrder) {
+ return lhsSortOrder < rhsSortOrder;
+ }
+ // Sort by largest first.
+ return lhs.arraySize > rhs.arraySize;
+ }
+};
+
+unsigned VariablePacker::makeColumnFlags(int column, int numComponentsPerRow)
+{
+ return ((kColumnMask << (kNumColumns - numComponentsPerRow)) &
+ kColumnMask) >> column;
+}
+
+void VariablePacker::fillColumns(int topRow, int numRows, int column, int numComponentsPerRow)
+{
+ unsigned columnFlags = makeColumnFlags(column, numComponentsPerRow);
+ for (int r = 0; r < numRows; ++r) {
+ int row = topRow + r;
+ ASSERT((rows_[row] & columnFlags) == 0);
+ rows_[row] |= columnFlags;
+ }
+}
+
+bool VariablePacker::searchColumn(int column, int numRows, int* destRow, int* destSize)
+{
+ ASSERT(destRow);
+
+ for (; topNonFullRow_ < maxRows_ && rows_[topNonFullRow_] == kColumnMask;
+ ++topNonFullRow_) {
+ }
+
+ for (; bottomNonFullRow_ >= 0 && rows_[bottomNonFullRow_] == kColumnMask;
+ --bottomNonFullRow_) {
+ }
+
+ if (bottomNonFullRow_ - topNonFullRow_ + 1 < numRows) {
+ return false;
+ }
+
+ unsigned columnFlags = makeColumnFlags(column, 1);
+ int topGoodRow = 0;
+ int smallestGoodTop = -1;
+ int smallestGoodSize = maxRows_ + 1;
+ int bottomRow = bottomNonFullRow_ + 1;
+ bool found = false;
+ for (int row = topNonFullRow_; row <= bottomRow; ++row) {
+ bool rowEmpty = row < bottomRow ? ((rows_[row] & columnFlags) == 0) : false;
+ if (rowEmpty) {
+ if (!found) {
+ topGoodRow = row;
+ found = true;
+ }
+ } else {
+ if (found) {
+ int size = row - topGoodRow;
+ if (size >= numRows && size < smallestGoodSize) {
+ smallestGoodSize = size;
+ smallestGoodTop = topGoodRow;
+ }
+ }
+ found = false;
+ }
+ }
+ if (smallestGoodTop < 0) {
+ return false;
+ }
+
+ *destRow = smallestGoodTop;
+ if (destSize) {
+ *destSize = smallestGoodSize;
+ }
+ return true;
+}
+
+bool VariablePacker::CheckVariablesWithinPackingLimits(
+ unsigned int maxVectors,
+ const std::vector<sh::ShaderVariable> &in_variables)
+{
+ ASSERT(maxVectors > 0);
+ maxRows_ = maxVectors;
+ topNonFullRow_ = 0;
+ bottomNonFullRow_ = maxRows_ - 1;
+ std::vector<sh::ShaderVariable> variables;
+
+ for (const auto &variable : in_variables)
+ {
+ ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse,
+ &variables);
+ }
+
+ // Check whether each variable fits in the available vectors.
+ for (size_t i = 0; i < variables.size(); i++) {
+ const sh::ShaderVariable &variable = variables[i];
+ if (variable.elementCount() > maxVectors / GetNumRows(variable.type)) {
+ return false;
+ }
+ }
+
+ // As per GLSL 1.017 Appendix A, Section 7 variables are packed in specific
+ // order by type, then by size of array, largest first.
+ std::sort(variables.begin(), variables.end(), TVariableInfoComparer());
+ rows_.clear();
+ rows_.resize(maxVectors, 0);
+
+ // Packs the 4 column variables.
+ size_t ii = 0;
+ for (; ii < variables.size(); ++ii) {
+ const sh::ShaderVariable &variable = variables[ii];
+ if (GetNumComponentsPerRow(variable.type) != 4) {
+ break;
+ }
+ topNonFullRow_ += GetNumRows(variable.type) * variable.elementCount();
+ }
+
+ if (topNonFullRow_ > maxRows_) {
+ return false;
+ }
+
+ // Packs the 3 column variables.
+ int num3ColumnRows = 0;
+ for (; ii < variables.size(); ++ii) {
+ const sh::ShaderVariable &variable = variables[ii];
+ if (GetNumComponentsPerRow(variable.type) != 3) {
+ break;
+ }
+ num3ColumnRows += GetNumRows(variable.type) * variable.elementCount();
+ }
+
+ if (topNonFullRow_ + num3ColumnRows > maxRows_) {
+ return false;
+ }
+
+ fillColumns(topNonFullRow_, num3ColumnRows, 0, 3);
+
+ // Packs the 2 column variables.
+ int top2ColumnRow = topNonFullRow_ + num3ColumnRows;
+ int twoColumnRowsAvailable = maxRows_ - top2ColumnRow;
+ int rowsAvailableInColumns01 = twoColumnRowsAvailable;
+ int rowsAvailableInColumns23 = twoColumnRowsAvailable;
+ for (; ii < variables.size(); ++ii) {
+ const sh::ShaderVariable &variable = variables[ii];
+ if (GetNumComponentsPerRow(variable.type) != 2) {
+ break;
+ }
+ int numRows = GetNumRows(variable.type) * variable.elementCount();
+ if (numRows <= rowsAvailableInColumns01) {
+ rowsAvailableInColumns01 -= numRows;
+ } else if (numRows <= rowsAvailableInColumns23) {
+ rowsAvailableInColumns23 -= numRows;
+ } else {
+ return false;
+ }
+ }
+
+ int numRowsUsedInColumns01 =
+ twoColumnRowsAvailable - rowsAvailableInColumns01;
+ int numRowsUsedInColumns23 =
+ twoColumnRowsAvailable - rowsAvailableInColumns23;
+ fillColumns(top2ColumnRow, numRowsUsedInColumns01, 0, 2);
+ fillColumns(maxRows_ - numRowsUsedInColumns23, numRowsUsedInColumns23,
+ 2, 2);
+
+ // Packs the 1 column variables.
+ for (; ii < variables.size(); ++ii) {
+ const sh::ShaderVariable &variable = variables[ii];
+ ASSERT(1 == GetNumComponentsPerRow(variable.type));
+ int numRows = GetNumRows(variable.type) * variable.elementCount();
+ int smallestColumn = -1;
+ int smallestSize = maxRows_ + 1;
+ int topRow = -1;
+ for (int column = 0; column < kNumColumns; ++column) {
+ int row = 0;
+ int size = 0;
+ if (searchColumn(column, numRows, &row, &size)) {
+ if (size < smallestSize) {
+ smallestSize = size;
+ smallestColumn = column;
+ topRow = row;
+ }
+ }
+ }
+
+ if (smallestColumn < 0) {
+ return false;
+ }
+
+ fillColumns(topRow, numRows, smallestColumn, 1);
+ }
+
+ ASSERT(variables.size() == ii);
+
+ return true;
+}
diff --git a/gfx/angle/src/compiler/translator/VariablePacker.h b/gfx/angle/src/compiler/translator/VariablePacker.h
new file mode 100755
index 000000000..5f38a0a98
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VariablePacker.h
@@ -0,0 +1,40 @@
+//
+// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VARIABLEPACKER_H_
+#define COMPILER_TRANSLATOR_VARIABLEPACKER_H_
+
+#include <vector>
+#include "compiler/translator/VariableInfo.h"
+
+class VariablePacker {
+ public:
+ // Returns true if the passed in variables pack in maxVectors following
+ // the packing rules from the GLSL 1.017 spec, Appendix A, section 7.
+ bool CheckVariablesWithinPackingLimits(unsigned int maxVectors,
+ const std::vector<sh::ShaderVariable> &in_variables);
+
+ // Gets how many components in a row a data type takes.
+ static int GetNumComponentsPerRow(sh::GLenum type);
+
+ // Gets how many rows a data type takes.
+ static int GetNumRows(sh::GLenum type);
+
+ private:
+ static const int kNumColumns = 4;
+ static const unsigned kColumnMask = (1 << kNumColumns) - 1;
+
+ unsigned makeColumnFlags(int column, int numComponentsPerRow);
+ void fillColumns(int topRow, int numRows, int column, int numComponentsPerRow);
+ bool searchColumn(int column, int numRows, int *destRow, int *destSize);
+
+ int topNonFullRow_;
+ int bottomNonFullRow_;
+ int maxRows_;
+ std::vector<unsigned> rows_;
+};
+
+#endif // COMPILER_TRANSLATOR_VARIABLEPACKER_H_
diff --git a/gfx/angle/src/compiler/translator/VersionGLSL.cpp b/gfx/angle/src/compiler/translator/VersionGLSL.cpp
new file mode 100755
index 000000000..9e593ac76
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VersionGLSL.cpp
@@ -0,0 +1,141 @@
+//
+// Copyright (c) 2002-2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/VersionGLSL.h"
+
+namespace sh
+{
+
+int ShaderOutputTypeToGLSLVersion(ShShaderOutput output)
+{
+ switch (output)
+ {
+ case SH_GLSL_130_OUTPUT: return GLSL_VERSION_130;
+ case SH_GLSL_140_OUTPUT: return GLSL_VERSION_140;
+ case SH_GLSL_150_CORE_OUTPUT: return GLSL_VERSION_150;
+ case SH_GLSL_330_CORE_OUTPUT: return GLSL_VERSION_330;
+ case SH_GLSL_400_CORE_OUTPUT: return GLSL_VERSION_400;
+ case SH_GLSL_410_CORE_OUTPUT: return GLSL_VERSION_410;
+ case SH_GLSL_420_CORE_OUTPUT: return GLSL_VERSION_420;
+ case SH_GLSL_430_CORE_OUTPUT: return GLSL_VERSION_430;
+ case SH_GLSL_440_CORE_OUTPUT: return GLSL_VERSION_440;
+ case SH_GLSL_450_CORE_OUTPUT: return GLSL_VERSION_450;
+ case SH_GLSL_COMPATIBILITY_OUTPUT: return GLSL_VERSION_110;
+ default: UNREACHABLE(); return 0;
+ }
+}
+
+// We need to scan for the following:
+// 1. "invariant" keyword: This can occur in both - vertex and fragment shaders
+// but only at the global scope.
+// 2. "gl_PointCoord" built-in variable: This can only occur in fragment shader
+// but inside any scope.
+// 3. Call to a matrix constructor with another matrix as argument.
+// (These constructors were reserved in GLSL version 1.10.)
+// 4. Arrays as "out" function parameters.
+// GLSL spec section 6.1.1: "When calling a function, expressions that do
+// not evaluate to l-values cannot be passed to parameters declared as
+// out or inout."
+// GLSL 1.1 section 5.8: "Other binary or unary expressions,
+// non-dereferenced arrays, function names, swizzles with repeated fields,
+// and constants cannot be l-values."
+// GLSL 1.2 relaxed the restriction on arrays, section 5.8: "Variables that
+// are built-in types, entire structures or arrays... are all l-values."
+//
+TVersionGLSL::TVersionGLSL(sh::GLenum type,
+ const TPragma &pragma,
+ ShShaderOutput output)
+ : TIntermTraverser(true, false, false)
+{
+ mVersion = ShaderOutputTypeToGLSLVersion(output);
+ if (pragma.stdgl.invariantAll)
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ }
+}
+
+void TVersionGLSL::visitSymbol(TIntermSymbol *node)
+{
+ if (node->getSymbol() == "gl_PointCoord")
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ }
+}
+
+bool TVersionGLSL::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ if (sequence.front()->getAsTyped()->getType().isInvariant())
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ }
+ return true;
+}
+
+bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node)
+{
+ bool visitChildren = true;
+
+ switch (node->getOp())
+ {
+ case EOpInvariantDeclaration:
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ break;
+ case EOpParameters:
+ {
+ const TIntermSequence &params = *(node->getSequence());
+ for (TIntermSequence::const_iterator iter = params.begin();
+ iter != params.end(); ++iter)
+ {
+ const TIntermTyped *param = (*iter)->getAsTyped();
+ if (param->isArray())
+ {
+ TQualifier qualifier = param->getQualifier();
+ if ((qualifier == EvqOut) || (qualifier == EvqInOut))
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ break;
+ }
+ }
+ }
+ // Fully processed. No need to visit children.
+ visitChildren = false;
+ break;
+ }
+ case EOpConstructMat2:
+ case EOpConstructMat2x3:
+ case EOpConstructMat2x4:
+ case EOpConstructMat3x2:
+ case EOpConstructMat3:
+ case EOpConstructMat3x4:
+ case EOpConstructMat4x2:
+ case EOpConstructMat4x3:
+ case EOpConstructMat4:
+ {
+ const TIntermSequence &sequence = *(node->getSequence());
+ if (sequence.size() == 1)
+ {
+ TIntermTyped *typed = sequence.front()->getAsTyped();
+ if (typed && typed->isMatrix())
+ {
+ ensureVersionIsAtLeast(GLSL_VERSION_120);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return visitChildren;
+}
+
+void TVersionGLSL::ensureVersionIsAtLeast(int version)
+{
+ mVersion = std::max(version, mVersion);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/VersionGLSL.h b/gfx/angle/src/compiler/translator/VersionGLSL.h
new file mode 100755
index 000000000..380349be0
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/VersionGLSL.h
@@ -0,0 +1,74 @@
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_VERSIONGLSL_H_
+#define COMPILER_TRANSLATOR_VERSIONGLSL_H_
+
+#include "compiler/translator/IntermNode.h"
+
+#include "compiler/translator/Pragma.h"
+
+namespace sh
+{
+
+static const int GLSL_VERSION_110 = 110;
+static const int GLSL_VERSION_120 = 120;
+static const int GLSL_VERSION_130 = 130;
+static const int GLSL_VERSION_140 = 140;
+static const int GLSL_VERSION_150 = 150;
+static const int GLSL_VERSION_330 = 330;
+static const int GLSL_VERSION_400 = 400;
+static const int GLSL_VERSION_410 = 410;
+static const int GLSL_VERSION_420 = 420;
+static const int GLSL_VERSION_430 = 430;
+static const int GLSL_VERSION_440 = 440;
+static const int GLSL_VERSION_450 = 450;
+
+int ShaderOutputTypeToGLSLVersion(ShShaderOutput output);
+
+// Traverses the intermediate tree to return the minimum GLSL version
+// required to legally access all built-in features used in the shader.
+// GLSL 1.1 which is mandated by OpenGL 2.0 provides:
+// - #version and #extension to declare version and extensions.
+// - built-in functions refract, exp, and log.
+// - updated step() to compare x < edge instead of x <= edge.
+// GLSL 1.2 which is mandated by OpenGL 2.1 provides:
+// - many changes to reduce differences when compared to the ES specification.
+// - invariant keyword and its support.
+// - c++ style name hiding rules.
+// - built-in variable gl_PointCoord for fragment shaders.
+// - matrix constructors taking matrix as argument.
+// - array as "out" function parameters
+//
+// TODO: ES3 equivalent versions of GLSL
+class TVersionGLSL : public TIntermTraverser
+{
+ public:
+ TVersionGLSL(sh::GLenum type, const TPragma &pragma, ShShaderOutput output);
+
+ // If output is core profile, returns 150.
+ // If output is legacy profile,
+ // Returns 120 if the following is used the shader:
+ // - "invariant",
+ // - "gl_PointCoord",
+ // - matrix/matrix constructors
+ // - array "out" parameters
+ // Else 110 is returned.
+ int getVersion() const { return mVersion; }
+
+ void visitSymbol(TIntermSymbol *) override;
+ bool visitAggregate(Visit, TIntermAggregate *) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *node) override;
+
+ private:
+ void ensureVersionIsAtLeast(int version);
+
+ int mVersion;
+};
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_VERSIONGLSL_H_
diff --git a/gfx/angle/src/compiler/translator/blocklayout.cpp b/gfx/angle/src/compiler/translator/blocklayout.cpp
new file mode 100755
index 000000000..ba6322848
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/blocklayout.cpp
@@ -0,0 +1,126 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// blocklayout.cpp:
+// Implementation for block layout classes and methods.
+//
+
+#include "compiler/translator/blocklayout.h"
+
+#include "common/mathutil.h"
+#include "common/utilities.h"
+
+namespace sh
+{
+
+BlockLayoutEncoder::BlockLayoutEncoder()
+ : mCurrentOffset(0)
+{
+}
+
+BlockMemberInfo BlockLayoutEncoder::encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix)
+{
+ int arrayStride;
+ int matrixStride;
+
+ getBlockLayoutInfo(type, arraySize, isRowMajorMatrix, &arrayStride, &matrixStride);
+
+ const BlockMemberInfo memberInfo(static_cast<int>(mCurrentOffset * BytesPerComponent),
+ static_cast<int>(arrayStride * BytesPerComponent),
+ static_cast<int>(matrixStride * BytesPerComponent),
+ isRowMajorMatrix);
+
+ advanceOffset(type, arraySize, isRowMajorMatrix, arrayStride, matrixStride);
+
+ return memberInfo;
+}
+
+// static
+size_t BlockLayoutEncoder::getBlockRegister(const BlockMemberInfo &info)
+{
+ return (info.offset / BytesPerComponent) / ComponentsPerRegister;
+}
+
+// static
+size_t BlockLayoutEncoder::getBlockRegisterElement(const BlockMemberInfo &info)
+{
+ return (info.offset / BytesPerComponent) % ComponentsPerRegister;
+}
+
+void BlockLayoutEncoder::nextRegister()
+{
+ mCurrentOffset = rx::roundUp<size_t>(mCurrentOffset, ComponentsPerRegister);
+}
+
+Std140BlockEncoder::Std140BlockEncoder()
+{
+}
+
+void Std140BlockEncoder::enterAggregateType()
+{
+ nextRegister();
+}
+
+void Std140BlockEncoder::exitAggregateType()
+{
+ nextRegister();
+}
+
+void Std140BlockEncoder::getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut)
+{
+ // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
+ ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == BytesPerComponent);
+
+ size_t baseAlignment = 0;
+ int matrixStride = 0;
+ int arrayStride = 0;
+
+ if (gl::IsMatrixType(type))
+ {
+ baseAlignment = ComponentsPerRegister;
+ matrixStride = ComponentsPerRegister;
+
+ if (arraySize > 0)
+ {
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ arrayStride = ComponentsPerRegister * numRegisters;
+ }
+ }
+ else if (arraySize > 0)
+ {
+ baseAlignment = ComponentsPerRegister;
+ arrayStride = ComponentsPerRegister;
+ }
+ else
+ {
+ const int numComponents = gl::VariableComponentCount(type);
+ baseAlignment = (numComponents == 3 ? 4u : static_cast<size_t>(numComponents));
+ }
+
+ mCurrentOffset = rx::roundUp(mCurrentOffset, baseAlignment);
+
+ *matrixStrideOut = matrixStride;
+ *arrayStrideOut = arrayStride;
+}
+
+void Std140BlockEncoder::advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride)
+{
+ if (arraySize > 0)
+ {
+ mCurrentOffset += arrayStride * arraySize;
+ }
+ else if (gl::IsMatrixType(type))
+ {
+ ASSERT(matrixStride == ComponentsPerRegister);
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ mCurrentOffset += ComponentsPerRegister * numRegisters;
+ }
+ else
+ {
+ mCurrentOffset += gl::VariableComponentCount(type);
+ }
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/blocklayout.h b/gfx/angle/src/compiler/translator/blocklayout.h
new file mode 100755
index 000000000..298a72c92
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/blocklayout.h
@@ -0,0 +1,103 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// blocklayout.h:
+// Methods and classes related to uniform layout and packing in GLSL and HLSL.
+//
+
+#ifndef COMMON_BLOCKLAYOUT_H_
+#define COMMON_BLOCKLAYOUT_H_
+
+#include <cstddef>
+#include <vector>
+
+#include "angle_gl.h"
+#include <GLSLANG/ShaderLang.h>
+
+namespace sh
+{
+struct ShaderVariable;
+struct InterfaceBlockField;
+struct Uniform;
+struct Varying;
+struct InterfaceBlock;
+
+struct BlockMemberInfo
+{
+ BlockMemberInfo() : offset(-1), arrayStride(-1), matrixStride(-1), isRowMajorMatrix(false) {}
+
+ BlockMemberInfo(int offset, int arrayStride, int matrixStride, bool isRowMajorMatrix)
+ : offset(offset),
+ arrayStride(arrayStride),
+ matrixStride(matrixStride),
+ isRowMajorMatrix(isRowMajorMatrix)
+ {}
+
+ static BlockMemberInfo getDefaultBlockInfo()
+ {
+ return BlockMemberInfo(-1, -1, -1, false);
+ }
+
+ int offset;
+ int arrayStride;
+ int matrixStride;
+ bool isRowMajorMatrix;
+};
+
+class BlockLayoutEncoder
+{
+ public:
+ BlockLayoutEncoder();
+ virtual ~BlockLayoutEncoder() {}
+
+ BlockMemberInfo encodeType(GLenum type, unsigned int arraySize, bool isRowMajorMatrix);
+
+ size_t getBlockSize() const { return mCurrentOffset * BytesPerComponent; }
+
+ virtual void enterAggregateType() = 0;
+ virtual void exitAggregateType() = 0;
+
+ static const size_t BytesPerComponent = 4u;
+ static const unsigned int ComponentsPerRegister = 4u;
+
+ static size_t getBlockRegister(const BlockMemberInfo &info);
+ static size_t getBlockRegisterElement(const BlockMemberInfo &info);
+
+ protected:
+ size_t mCurrentOffset;
+
+ void nextRegister();
+
+ virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut) = 0;
+ virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride) = 0;
+};
+
+// Block layout according to the std140 block layout
+// See "Standard Uniform Block Layout" in Section 2.11.6 of the OpenGL ES 3.0 specification
+
+class Std140BlockEncoder : public BlockLayoutEncoder
+{
+ public:
+ Std140BlockEncoder();
+
+ void enterAggregateType() override;
+ void exitAggregateType() override;
+
+ protected:
+ void getBlockLayoutInfo(GLenum type,
+ unsigned int arraySize,
+ bool isRowMajorMatrix,
+ int *arrayStrideOut,
+ int *matrixStrideOut) override;
+ void advanceOffset(GLenum type,
+ unsigned int arraySize,
+ bool isRowMajorMatrix,
+ int arrayStride,
+ int matrixStride) override;
+};
+
+}
+
+#endif // COMMON_BLOCKLAYOUT_H_
diff --git a/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp b/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp
new file mode 100755
index 000000000..43119248e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/blocklayoutHLSL.cpp
@@ -0,0 +1,171 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// blocklayout.cpp:
+// Implementation for block layout classes and methods.
+//
+
+#include "compiler/translator/blocklayoutHLSL.h"
+
+#include "common/mathutil.h"
+#include "common/utilities.h"
+
+namespace sh
+{
+
+HLSLBlockEncoder::HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy)
+ : mEncoderStrategy(strategy),
+ mTransposeMatrices(false)
+{
+}
+
+void HLSLBlockEncoder::enterAggregateType()
+{
+ nextRegister();
+}
+
+void HLSLBlockEncoder::exitAggregateType()
+{
+}
+
+void HLSLBlockEncoder::getBlockLayoutInfo(GLenum typeIn, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut)
+{
+ GLenum type = (mTransposeMatrices ? gl::TransposeMatrixType(typeIn) : typeIn);
+
+ // We assume we are only dealing with 4 byte components (no doubles or half-words currently)
+ ASSERT(gl::VariableComponentSize(gl::VariableComponentType(type)) == BytesPerComponent);
+
+ int matrixStride = 0;
+ int arrayStride = 0;
+
+ // if variables are not to be packed, or we're about to
+ // pack a matrix or array, skip to the start of the next
+ // register
+ if (!isPacked() ||
+ gl::IsMatrixType(type) ||
+ arraySize > 0)
+ {
+ nextRegister();
+ }
+
+ if (gl::IsMatrixType(type))
+ {
+ matrixStride = ComponentsPerRegister;
+
+ if (arraySize > 0)
+ {
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ arrayStride = ComponentsPerRegister * numRegisters;
+ }
+ }
+ else if (arraySize > 0)
+ {
+ arrayStride = ComponentsPerRegister;
+ }
+ else if (isPacked())
+ {
+ int numComponents = gl::VariableComponentCount(type);
+ if ((numComponents + (mCurrentOffset % ComponentsPerRegister)) > ComponentsPerRegister)
+ {
+ nextRegister();
+ }
+ }
+
+ *matrixStrideOut = matrixStride;
+ *arrayStrideOut = arrayStride;
+}
+
+void HLSLBlockEncoder::advanceOffset(GLenum typeIn, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride)
+{
+ GLenum type = (mTransposeMatrices ? gl::TransposeMatrixType(typeIn) : typeIn);
+
+ if (arraySize > 0)
+ {
+ mCurrentOffset += arrayStride * (arraySize - 1);
+ }
+
+ if (gl::IsMatrixType(type))
+ {
+ ASSERT(matrixStride == ComponentsPerRegister);
+ const int numRegisters = gl::MatrixRegisterCount(type, isRowMajorMatrix);
+ const int numComponents = gl::MatrixComponentCount(type, isRowMajorMatrix);
+ mCurrentOffset += ComponentsPerRegister * (numRegisters - 1);
+ mCurrentOffset += numComponents;
+ }
+ else if (isPacked())
+ {
+ mCurrentOffset += gl::VariableComponentCount(type);
+ }
+ else
+ {
+ mCurrentOffset += ComponentsPerRegister;
+ }
+}
+
+void HLSLBlockEncoder::skipRegisters(unsigned int numRegisters)
+{
+ mCurrentOffset += (numRegisters * ComponentsPerRegister);
+}
+
+HLSLBlockEncoder::HLSLBlockEncoderStrategy HLSLBlockEncoder::GetStrategyFor(ShShaderOutput outputType)
+{
+ switch (outputType)
+ {
+ case SH_HLSL_3_0_OUTPUT:
+ return ENCODE_LOOSE;
+ case SH_HLSL_4_1_OUTPUT:
+ case SH_HLSL_4_0_FL9_3_OUTPUT:
+ return ENCODE_PACKED;
+ default:
+ UNREACHABLE();
+ return ENCODE_PACKED;
+ }
+}
+
+template <class ShaderVarType>
+void HLSLVariableRegisterCount(const ShaderVarType &variable, HLSLBlockEncoder *encoder)
+{
+ if (variable.isStruct())
+ {
+ for (size_t arrayElement = 0; arrayElement < variable.elementCount(); arrayElement++)
+ {
+ encoder->enterAggregateType();
+
+ for (size_t fieldIndex = 0; fieldIndex < variable.fields.size(); fieldIndex++)
+ {
+ HLSLVariableRegisterCount(variable.fields[fieldIndex], encoder);
+ }
+
+ encoder->exitAggregateType();
+ }
+ }
+ else
+ {
+ // We operate only on varyings and uniforms, which do not have matrix layout qualifiers
+ encoder->encodeType(variable.type, variable.arraySize, false);
+ }
+}
+
+unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMatrices)
+{
+ HLSLBlockEncoder encoder(HLSLBlockEncoder::ENCODE_PACKED);
+ encoder.setTransposeMatrices(transposeMatrices);
+ HLSLVariableRegisterCount(variable, &encoder);
+
+ const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister);
+ return static_cast<unsigned int>(rx::roundUp<size_t>(encoder.getBlockSize(), registerBytes) / registerBytes);
+}
+
+unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType)
+{
+ HLSLBlockEncoder encoder(HLSLBlockEncoder::GetStrategyFor(outputType));
+ encoder.setTransposeMatrices(true);
+ HLSLVariableRegisterCount(variable, &encoder);
+
+ const size_t registerBytes = (encoder.BytesPerComponent * encoder.ComponentsPerRegister);
+ return static_cast<unsigned int>(rx::roundUp<size_t>(encoder.getBlockSize(), registerBytes) / registerBytes);
+}
+
+}
diff --git a/gfx/angle/src/compiler/translator/blocklayoutHLSL.h b/gfx/angle/src/compiler/translator/blocklayoutHLSL.h
new file mode 100755
index 000000000..8d98847bd
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/blocklayoutHLSL.h
@@ -0,0 +1,61 @@
+//
+// Copyright (c) 2013-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// blocklayout.h:
+// Methods and classes related to uniform layout and packing in GLSL and HLSL.
+//
+
+#ifndef COMMON_BLOCKLAYOUTHLSL_H_
+#define COMMON_BLOCKLAYOUTHLSL_H_
+
+#include <cstddef>
+#include <vector>
+
+#include "angle_gl.h"
+#include "blocklayout.h"
+#include <GLSLANG/ShaderLang.h>
+
+namespace sh
+{
+// Block layout packed according to the D3D9 or default D3D10+ register packing rules
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb509632(v=vs.85).aspx
+// The strategy should be ENCODE_LOOSE for D3D9 constant blocks, and ENCODE_PACKED
+// for everything else (D3D10+ constant blocks and all attributes/varyings).
+
+class HLSLBlockEncoder : public BlockLayoutEncoder
+{
+ public:
+ enum HLSLBlockEncoderStrategy
+ {
+ ENCODE_PACKED,
+ ENCODE_LOOSE
+ };
+
+ HLSLBlockEncoder(HLSLBlockEncoderStrategy strategy);
+
+ virtual void enterAggregateType();
+ virtual void exitAggregateType();
+ void skipRegisters(unsigned int numRegisters);
+
+ bool isPacked() const { return mEncoderStrategy == ENCODE_PACKED; }
+ void setTransposeMatrices(bool enabled) { mTransposeMatrices = enabled; }
+
+ static HLSLBlockEncoderStrategy GetStrategyFor(ShShaderOutput outputType);
+
+ protected:
+ virtual void getBlockLayoutInfo(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int *arrayStrideOut, int *matrixStrideOut);
+ virtual void advanceOffset(GLenum type, unsigned int arraySize, bool isRowMajorMatrix, int arrayStride, int matrixStride);
+
+ HLSLBlockEncoderStrategy mEncoderStrategy;
+ bool mTransposeMatrices;
+};
+
+// This method returns the number of used registers for a ShaderVariable. It is dependent on the HLSLBlockEncoder
+// class to count the number of used registers in a struct (which are individually packed according to the same rules).
+unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMatrices);
+unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType);
+}
+
+#endif // COMMON_BLOCKLAYOUTHLSL_H_
diff --git a/gfx/angle/src/compiler/translator/generate_parser.sh b/gfx/angle/src/compiler/translator/generate_parser.sh
new file mode 100755
index 000000000..61eecce73
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/generate_parser.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright (c) 2010 The ANGLE Project Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Generates GLSL ES parser - glslang_lex.cpp, glslang_tab.h, and glslang_tab.cpp
+
+run_flex()
+{
+input_file=./$1.l
+output_source=./$1_lex.cpp
+flex --noline --nounistd --outfile=$output_source $input_file
+}
+
+run_bison()
+{
+input_file=./$1.y
+output_header=./$1_tab.h
+output_source=./$1_tab.cpp
+bison --no-lines --skeleton=yacc.c --defines=$output_header --output=$output_source $input_file
+}
+
+script_dir=$(dirname $0)
+
+# Generate Parser
+cd $script_dir
+run_flex glslang
+run_bison glslang
+patch --silent --forward < 64bit-lexer-safety.patch
diff --git a/gfx/angle/src/compiler/translator/glslang.h b/gfx/angle/src/compiler/translator/glslang.h
new file mode 100755
index 000000000..aaa5dd119
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_GLSLANG_H_
+#define COMPILER_TRANSLATOR_GLSLANG_H_
+
+namespace sh
+{
+class TParseContext;
+}
+
+extern int glslang_initialize(sh::TParseContext *context);
+extern int glslang_finalize(sh::TParseContext *context);
+
+extern int glslang_scan(size_t count,
+ const char *const string[],
+ const int length[],
+ sh::TParseContext *context);
+extern int glslang_parse(sh::TParseContext *context);
+
+#endif // COMPILER_TRANSLATOR_GLSLANG_H_
diff --git a/gfx/angle/src/compiler/translator/glslang.l b/gfx/angle/src/compiler/translator/glslang.l
new file mode 100755
index 000000000..a30f2c33e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang.l
@@ -0,0 +1,623 @@
+/*
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Lex specification for GLSL ES.
+Based on ANSI C grammar, Lex specification:
+http://www.lysator.liu.se/c/ANSI-C-grammar-l.html
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
+WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp).
+*/
+
+%top{
+//
+// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+/* clang-format off */
+
+// Ignore errors in auto-generated code.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4005)
+#pragma warning(disable: 4065)
+#pragma warning(disable: 4189)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4505)
+#pragma warning(disable: 4701)
+#pragma warning(disable: 4702)
+#endif
+}
+
+%{
+#include "compiler/translator/glslang.h"
+#include "compiler/translator/ParseContext.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/translator/util.h"
+#include "compiler/translator/length_limits.h"
+
+using namespace sh;
+
+#include "glslang_tab.h"
+
+/* windows only pragma */
+#ifdef _MSC_VER
+#pragma warning(disable : 4102)
+#endif
+
+// Workaround for flex using the register keyword, deprecated in C++11.
+#ifdef __cplusplus
+#if __cplusplus > 199711L
+#define register
+#endif
+#endif
+
+#define YY_USER_ACTION \
+ yylloc->first_file = yylloc->last_file = yycolumn; \
+ yylloc->first_line = yylloc->last_line = yylineno;
+
+#define YY_INPUT(buf, result, max_size) \
+ result = string_input(buf, max_size, yyscanner);
+
+static yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner);
+static int check_type(yyscan_t yyscanner);
+static int reserved_word(yyscan_t yyscanner);
+static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
+static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
+static int ES2_ident_ES3_keyword(TParseContext *context, int token);
+static int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int uint_constant(TParseContext *context);
+static int int_constant(TParseContext *context);
+static int float_constant(yyscan_t yyscanner);
+static int floatsuffix_check(TParseContext* context);
+%}
+
+%option noyywrap nounput never-interactive
+%option yylineno reentrant bison-bridge bison-locations
+%option extra-type="TParseContext*"
+%x FIELDS
+
+D [0-9]
+L [a-zA-Z_]
+H [a-fA-F0-9]
+E [Ee][+-]?{D}+
+O [0-7]
+
+%%
+
+%{
+ TParseContext* context = yyextra;
+%}
+
+"invariant" { return INVARIANT; }
+"highp" { return HIGH_PRECISION; }
+"mediump" { return MEDIUM_PRECISION; }
+"lowp" { return LOW_PRECISION; }
+"precision" { return PRECISION; }
+
+"attribute" { return ES2_keyword_ES3_reserved(context, ATTRIBUTE); }
+"const" { return CONST_QUAL; }
+"uniform" { return UNIFORM; }
+"varying" { return ES2_keyword_ES3_reserved(context, VARYING); }
+
+"break" { return BREAK; }
+"continue" { return CONTINUE; }
+"do" { return DO; }
+"for" { return FOR; }
+"while" { return WHILE; }
+
+"if" { return IF; }
+"else" { return ELSE; }
+"switch" { return ES2_reserved_ES3_keyword(context, SWITCH); }
+"case" { return ES2_ident_ES3_keyword(context, CASE); }
+"default" { return ES2_reserved_ES3_keyword(context, DEFAULT); }
+
+"centroid" { return ES2_ident_ES3_keyword(context, CENTROID); }
+"flat" { return ES2_reserved_ES3_keyword(context, FLAT); }
+"smooth" { return ES2_ident_ES3_keyword(context, SMOOTH); }
+
+"in" { return IN_QUAL; }
+"out" { return OUT_QUAL; }
+"inout" { return INOUT_QUAL; }
+
+"float" { return FLOAT_TYPE; }
+"int" { return INT_TYPE; }
+"uint" { return ES2_ident_ES3_keyword(context, UINT_TYPE); }
+"void" { return VOID_TYPE; }
+"bool" { return BOOL_TYPE; }
+"true" { yylval->lex.b = true; return BOOLCONSTANT; }
+"false" { yylval->lex.b = false; return BOOLCONSTANT; }
+
+"discard" { return DISCARD; }
+"return" { return RETURN; }
+
+"mat2" { return MATRIX2; }
+"mat3" { return MATRIX3; }
+"mat4" { return MATRIX4; }
+
+"mat2x2" { return ES2_ident_ES3_keyword(context, MATRIX2); }
+"mat3x3" { return ES2_ident_ES3_keyword(context, MATRIX3); }
+"mat4x4" { return ES2_ident_ES3_keyword(context, MATRIX4); }
+
+"mat2x3" { return ES2_ident_ES3_keyword(context, MATRIX2x3); }
+"mat3x2" { return ES2_ident_ES3_keyword(context, MATRIX3x2); }
+"mat2x4" { return ES2_ident_ES3_keyword(context, MATRIX2x4); }
+"mat4x2" { return ES2_ident_ES3_keyword(context, MATRIX4x2); }
+"mat3x4" { return ES2_ident_ES3_keyword(context, MATRIX3x4); }
+"mat4x3" { return ES2_ident_ES3_keyword(context, MATRIX4x3); }
+
+"vec2" { return VEC2; }
+"vec3" { return VEC3; }
+"vec4" { return VEC4; }
+"ivec2" { return IVEC2; }
+"ivec3" { return IVEC3; }
+"ivec4" { return IVEC4; }
+"bvec2" { return BVEC2; }
+"bvec3" { return BVEC3; }
+"bvec4" { return BVEC4; }
+"uvec2" { return ES2_ident_ES3_keyword(context, UVEC2); }
+"uvec3" { return ES2_ident_ES3_keyword(context, UVEC3); }
+"uvec4" { return ES2_ident_ES3_keyword(context, UVEC4); }
+
+"sampler2D" { return SAMPLER2D; }
+"samplerCube" { return SAMPLERCUBE; }
+"samplerExternalOES" { return SAMPLER_EXTERNAL_OES; }
+"sampler3D" { return ES2_reserved_ES3_keyword(context, SAMPLER3D); }
+"sampler3DRect" { return ES2_reserved_ES3_keyword(context, SAMPLER3DRECT); }
+"sampler2DRect" { return SAMPLER2DRECT; }
+"sampler2DArray" { return ES2_ident_ES3_keyword(context, SAMPLER2DARRAY); }
+"isampler2D" { return ES2_ident_ES3_keyword(context, ISAMPLER2D); }
+"isampler3D" { return ES2_ident_ES3_keyword(context, ISAMPLER3D); }
+"isamplerCube" { return ES2_ident_ES3_keyword(context, ISAMPLERCUBE); }
+"isampler2DArray" { return ES2_ident_ES3_keyword(context, ISAMPLER2DARRAY); }
+"usampler2D" { return ES2_ident_ES3_keyword(context, USAMPLER2D); }
+"usampler3D" { return ES2_ident_ES3_keyword(context, USAMPLER3D); }
+"usamplerCube" { return ES2_ident_ES3_keyword(context, USAMPLERCUBE); }
+"usampler2DArray" { return ES2_ident_ES3_keyword(context, USAMPLER2DARRAY); }
+"sampler2DShadow" { return ES2_reserved_ES3_keyword(context, SAMPLER2DSHADOW); }
+"samplerCubeShadow" { return ES2_ident_ES3_keyword(context, SAMPLERCUBESHADOW); }
+"sampler2DArrayShadow" { return ES2_ident_ES3_keyword(context, SAMPLER2DARRAYSHADOW); }
+
+"struct" { return STRUCT; }
+
+"layout" { return ES2_ident_ES3_keyword(context, LAYOUT); }
+
+"image2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2D); }
+"iimage2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2D); }
+"uimage2D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2D); }
+"image2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2DARRAY); }
+"iimage2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2DARRAY); }
+"uimage2DArray" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2DARRAY); }
+"image3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE3D); }
+"uimage3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE3D); }
+"iimage3D" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE3D); }
+"iimageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGECUBE); }
+"uimageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGECUBE); }
+"imageCube" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGECUBE); }
+"readonly" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, READONLY); }
+"writeonly" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, WRITEONLY); }
+"coherent" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, COHERENT); }
+"restrict" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, RESTRICT); }
+"volatile" { return ES2_and_ES3_reserved_ES3_1_keyword(context, VOLATILE); }
+
+ /* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
+"resource" |
+"atomic_uint" |
+"noperspective" |
+"patch" |
+"sample" |
+"subroutine" |
+"common" |
+"partition" |
+"active" |
+
+"filter" |
+"image1D" |
+"iimage1D" |
+"uimage1D" |
+"image1DArray" |
+"iimage1DArray" |
+"uimage1DArray" |
+"image1DShadow" |
+"image2DShadow" |
+"image1DArrayShadow" |
+"image2DArrayShadow" |
+"imageBuffer" |
+"iimageBuffer" |
+"uimageBuffer" |
+
+"sampler1DArray" |
+"sampler1DArrayShadow" |
+"isampler1D" |
+"isampler1DArray" |
+"usampler1D" |
+"usampler1DArray" |
+"isampler2DRect" |
+"usampler2DRect" |
+"samplerBuffer" |
+"isamplerBuffer" |
+"usamplerBuffer" |
+"sampler2DMS" |
+"isampler2DMS" |
+"usampler2DMS" |
+"sampler2DMSArray" |
+"isampler2DMSArray" |
+"usampler2DMSArray" {
+ if (context->getShaderVersion() < 300) {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+ return reserved_word(yyscanner);
+}
+
+ /* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */
+"packed" {
+ if (context->getShaderVersion() >= 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return reserved_word(yyscanner);
+}
+
+ /* Reserved keywords */
+"asm" |
+
+"class" |
+"union" |
+"enum" |
+"typedef" |
+"template" |
+"this" |
+
+"goto" |
+
+"inline" |
+"noinline" |
+"public" |
+"static" |
+"extern" |
+"external" |
+"interface" |
+
+"long" |
+"short" |
+"double" |
+"half" |
+"fixed" |
+"unsigned" |
+"superp" |
+
+"input" |
+"output" |
+
+"hvec2" |
+"hvec3" |
+"hvec4" |
+"dvec2" |
+"dvec3" |
+"dvec4" |
+"fvec2" |
+"fvec3" |
+"fvec4" |
+
+"sampler1D" |
+"sampler1DShadow" |
+"sampler2DRectShadow" |
+
+"sizeof" |
+"cast" |
+
+"namespace" |
+"using" { return reserved_word(yyscanner); }
+
+{L}({L}|{D})* {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+}
+
+0[xX]{H}+ { return int_constant(context); }
+0{O}+ { return int_constant(context); }
+{D}+ { return int_constant(context); }
+
+0[xX]{H}+[uU] { return uint_constant(context); }
+0{O}+[uU] { return uint_constant(context); }
+{D}+[uU] { return uint_constant(context); }
+
+{D}+{E} { return float_constant(yyscanner); }
+{D}+"."{D}*({E})? { return float_constant(yyscanner); }
+"."{D}+({E})? { return float_constant(yyscanner); }
+
+{D}+{E}[fF] { return floatsuffix_check(context); }
+{D}+"."{D}*({E})?[fF] { return floatsuffix_check(context); }
+"."{D}+({E})?[fF] { return floatsuffix_check(context); }
+
+"+=" { return ADD_ASSIGN; }
+"-=" { return SUB_ASSIGN; }
+"*=" { return MUL_ASSIGN; }
+"/=" { return DIV_ASSIGN; }
+"%=" { return MOD_ASSIGN; }
+"<<=" { return LEFT_ASSIGN; }
+">>=" { return RIGHT_ASSIGN; }
+"&=" { return AND_ASSIGN; }
+"^=" { return XOR_ASSIGN; }
+"|=" { return OR_ASSIGN; }
+
+"++" { return INC_OP; }
+"--" { return DEC_OP; }
+"&&" { return AND_OP; }
+"||" { return OR_OP; }
+"^^" { return XOR_OP; }
+"<=" { return LE_OP; }
+">=" { return GE_OP; }
+"==" { return EQ_OP; }
+"!=" { return NE_OP; }
+"<<" { return LEFT_OP; }
+">>" { return RIGHT_OP; }
+";" { return SEMICOLON; }
+("{"|"<%") { return LEFT_BRACE; }
+("}"|"%>") { return RIGHT_BRACE; }
+"," { return COMMA; }
+":" { return COLON; }
+"=" { return EQUAL; }
+"(" { return LEFT_PAREN; }
+")" { return RIGHT_PAREN; }
+("["|"<:") { return LEFT_BRACKET; }
+("]"|":>") { return RIGHT_BRACKET; }
+"." { BEGIN(FIELDS); return DOT; }
+"!" { return BANG; }
+"-" { return DASH; }
+"~" { return TILDE; }
+"+" { return PLUS; }
+"*" { return STAR; }
+"/" { return SLASH; }
+"%" { return PERCENT; }
+"<" { return LEFT_ANGLE; }
+">" { return RIGHT_ANGLE; }
+"|" { return VERTICAL_BAR; }
+"^" { return CARET; }
+"&" { return AMPERSAND; }
+"?" { return QUESTION; }
+
+<FIELDS>{L}({L}|{D})* {
+ BEGIN(INITIAL);
+ yylval->lex.string = NewPoolTString(yytext);
+ return FIELD_SELECTION;
+}
+<FIELDS>[ \t\v\f\r] {}
+<FIELDS>. {
+ yyextra->error(*yylloc, "Illegal character at fieldname start", yytext, "");
+ return 0;
+}
+
+[ \t\v\n\f\r] { }
+<*><<EOF>> { yyterminate(); }
+<*>. { assert(false); return 0; }
+
+%%
+
+yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) {
+ pp::Token token;
+ yyget_extra(yyscanner)->getPreprocessor().lex(&token);
+ yy_size_t len = token.type == pp::Token::LAST ? 0 : token.text.size();
+ if (len < max_size)
+ memcpy(buf, token.text.c_str(), len);
+ yyset_column(token.location.file, yyscanner);
+ yyset_lineno(token.location.line, yyscanner);
+
+ if (len >= max_size)
+ YY_FATAL_ERROR("Input buffer overflow");
+ else if (len > 0)
+ buf[len++] = ' ';
+ return len;
+}
+
+int check_type(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ int token = IDENTIFIER;
+ TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->getShaderVersion());
+ if (symbol && symbol->isVariable()) {
+ TVariable* variable = static_cast<TVariable*>(symbol);
+ if (variable->isUserType()) {
+ token = TYPE_NAME;
+ }
+ }
+ yylval->lex.symbol = symbol;
+ return token;
+}
+
+int reserved_word(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ yyextra->error(*yylloc, "Illegal use of reserved word", yytext, "");
+ return 0;
+}
+
+int ES2_reserved_ES3_keyword(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_keyword_ES3_reserved(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() >= 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+ else if (context->getShaderVersion() == 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_ident_ES3_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
+ if (context->getShaderVersion() < 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 310)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int uint_constant(TParseContext *context)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext, "");
+ return 0;
+ }
+
+ if (!atoi_clamp(yytext, &(yylval->lex.u)))
+ yyextra->error(*yylloc, "Integer overflow", yytext, "");
+
+ return UINTCONSTANT;
+}
+
+int floatsuffix_check(TParseContext* context)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ context->error(*yylloc, "Floating-point suffix unsupported prior to GLSL ES 3.00", yytext);
+ return 0;
+ }
+
+ std::string text = yytext;
+ text.resize(text.size() - 1);
+ if (!strtof_clamp(text, &(yylval->lex.f)))
+ yyextra->warning(*yylloc, "Float overflow", yytext, "");
+
+ return(FLOATCONSTANT);
+}
+
+void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* reason) {
+ context->error(*lloc, reason, yyget_text(scanner));
+}
+
+int int_constant(TParseContext *context) {
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ unsigned int u;
+ if (!atoi_clamp(yytext, &u))
+ {
+ if (context->getShaderVersion() >= 300)
+ yyextra->error(*yylloc, "Integer overflow", yytext, "");
+ else
+ yyextra->warning(*yylloc, "Integer overflow", yytext, "");
+ }
+ yylval->lex.i = static_cast<int>(u);
+ return INTCONSTANT;
+}
+
+int float_constant(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ if (!strtof_clamp(yytext, &(yylval->lex.f)))
+ yyextra->warning(*yylloc, "Float overflow", yytext, "");
+ return FLOATCONSTANT;
+}
+
+int glslang_initialize(TParseContext* context) {
+ yyscan_t scanner = NULL;
+ if (yylex_init_extra(context, &scanner))
+ return 1;
+
+ context->setScanner(scanner);
+ return 0;
+}
+
+int glslang_finalize(TParseContext* context) {
+ yyscan_t scanner = context->getScanner();
+ if (scanner == NULL) return 0;
+
+ context->setScanner(NULL);
+ yylex_destroy(scanner);
+
+ return 0;
+}
+
+int glslang_scan(size_t count, const char* const string[], const int length[],
+ TParseContext* context) {
+ yyrestart(NULL, context->getScanner());
+ yyset_column(0, context->getScanner());
+ yyset_lineno(1, context->getScanner());
+
+ // Initialize preprocessor.
+ pp::Preprocessor *preprocessor = &context->getPreprocessor();
+
+ if (!preprocessor->init(count, string, length))
+ return 1;
+
+ // Define extension macros.
+ const TExtensionBehavior& extBehavior = context->extensionBehavior();
+ for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
+ iter != extBehavior.end(); ++iter) {
+ preprocessor->predefineMacro(iter->first.c_str(), 1);
+ }
+ if (context->getFragmentPrecisionHigh())
+ preprocessor->predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1);
+
+ preprocessor->setMaxTokenSize(sh::GetGlobalMaxTokenSize(context->getShaderSpec()));
+
+ return 0;
+}
diff --git a/gfx/angle/src/compiler/translator/glslang.y b/gfx/angle/src/compiler/translator/glslang.y
new file mode 100755
index 000000000..c15dd9d6e
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang.y
@@ -0,0 +1,1533 @@
+/*
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Yacc grammar for GLSL ES.
+Based on ANSI C Yacc grammar:
+http://www.lysator.liu.se/c/ANSI-C-grammar-y.html
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
+WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h).
+*/
+
+%{
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+// clang-format off
+
+// Ignore errors in auto-generated code.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4065)
+#pragma warning(disable: 4189)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4505)
+#pragma warning(disable: 4701)
+#pragma warning(disable: 4702)
+#endif
+
+#include "angle_gl.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/ParseContext.h"
+#include "GLSLANG/ShaderLang.h"
+
+#define YYENABLE_NLS 0
+
+using namespace sh;
+
+%}
+%expect 1 /* One shift reduce conflict because of if | else */
+%parse-param {TParseContext* context}
+%param {void *scanner}
+%define api.pure full
+%locations
+
+%code requires {
+#define YYLTYPE TSourceLoc
+#define YYLTYPE_IS_DECLARED 1
+}
+
+%union {
+ struct {
+ union {
+ TString *string;
+ float f;
+ int i;
+ unsigned int u;
+ bool b;
+ };
+ TSymbol* symbol;
+ } lex;
+ struct {
+ TOperator op;
+ union {
+ TIntermNode *intermNode;
+ TIntermNodePair nodePair;
+ TIntermTyped *intermTypedNode;
+ TIntermAggregate *intermAggregate;
+ TIntermBlock *intermBlock;
+ TIntermDeclaration *intermDeclaration;
+ TIntermSwitch *intermSwitch;
+ TIntermCase *intermCase;
+ };
+ union {
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ TPublicType type;
+ TPrecision precision;
+ TLayoutQualifier layoutQualifier;
+ TQualifier qualifier;
+ TFunction *function;
+ TParameter param;
+ TField *field;
+ TFieldList *fieldList;
+ TQualifierWrapperBase *qualifierWrapper;
+ TTypeQualifierBuilder *typeQualifierBuilder;
+ };
+ } interm;
+}
+
+%{
+extern int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, void* yyscanner);
+extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, const char* reason);
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do { \
+ if (N) { \
+ (Current).first_file = YYRHSLOC(Rhs, 1).first_file; \
+ (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
+ (Current).last_file = YYRHSLOC(Rhs, N).last_file; \
+ (Current).last_line = YYRHSLOC(Rhs, N).last_line; \
+ } \
+ else { \
+ (Current).first_file = YYRHSLOC(Rhs, 0).last_file; \
+ (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \
+ (Current).last_file = YYRHSLOC(Rhs, 0).last_file; \
+ (Current).last_line = YYRHSLOC(Rhs, 0).last_line; \
+ } \
+ } while (0)
+
+#define VERTEX_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER) { \
+ context->error(L, " supported in vertex shaders only ", S); \
+ } \
+}
+
+#define FRAG_ONLY(S, L) { \
+ if (context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in fragment shaders only ", S); \
+ } \
+}
+
+#define COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_COMPUTE_SHADER) { \
+ context->error(L, " supported in compute shaders only ", S); \
+ } \
+}
+
+#define NON_COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in vertex and fragment shaders only ", S); \
+ } \
+}
+
+#define ES2_ONLY(S, L) { \
+ if (context->getShaderVersion() != 100) { \
+ context->error(L, " supported in GLSL ES 1.00 only ", S); \
+ } \
+}
+
+#define ES3_OR_NEWER(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() < 300) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.00 and above only ", TOKEN); \
+ } \
+}
+
+#define ES3_1_ONLY(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() != 310) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.10 only ", TOKEN); \
+ } \
+}
+%}
+
+%token <lex> INVARIANT HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION
+%token <lex> ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE UINT_TYPE
+%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT
+%token <lex> BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 UVEC2 UVEC3 UVEC4
+%token <lex> MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING
+%token <lex> MATRIX2x3 MATRIX3x2 MATRIX2x4 MATRIX4x2 MATRIX3x4 MATRIX4x3
+%token <lex> CENTROID FLAT SMOOTH
+%token <lex> READONLY WRITEONLY COHERENT RESTRICT VOLATILE
+%token <lex> STRUCT VOID_TYPE WHILE
+%token <lex> SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT SAMPLER2DARRAY
+%token <lex> ISAMPLER2D ISAMPLER3D ISAMPLERCUBE ISAMPLER2DARRAY
+%token <lex> USAMPLER2D USAMPLER3D USAMPLERCUBE USAMPLER2DARRAY
+%token <lex> SAMPLER3D SAMPLER3DRECT SAMPLER2DSHADOW SAMPLERCUBESHADOW SAMPLER2DARRAYSHADOW
+%token <lex> IMAGE2D IIMAGE2D UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY
+%token <lex> IMAGECUBE IIMAGECUBE UIMAGECUBE
+%token <lex> LAYOUT
+
+%token <lex> IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT
+%token <lex> FIELD_SELECTION
+%token <lex> LEFT_OP RIGHT_OP
+%token <lex> INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
+%token <lex> AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN
+%token <lex> MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN
+%token <lex> SUB_ASSIGN
+
+%token <lex> LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT
+%token <lex> COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT
+%token <lex> LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION
+
+%type <lex> identifier
+%type <interm> assignment_operator unary_operator
+%type <interm.intermTypedNode> variable_identifier primary_expression postfix_expression
+%type <interm.intermTypedNode> expression integer_expression assignment_expression
+%type <interm.intermTypedNode> unary_expression multiplicative_expression additive_expression
+%type <interm.intermTypedNode> relational_expression equality_expression
+%type <interm.intermTypedNode> conditional_expression constant_expression
+%type <interm.intermTypedNode> logical_or_expression logical_xor_expression logical_and_expression
+%type <interm.intermTypedNode> shift_expression and_expression exclusive_or_expression inclusive_or_expression
+%type <interm.intermTypedNode> function_call initializer condition conditionopt
+
+%type <interm.intermBlock> translation_unit
+%type <interm.intermNode> function_definition statement simple_statement
+%type <interm.intermBlock> statement_list compound_statement compound_statement_no_new_scope
+%type <interm.intermNode> declaration_statement selection_statement expression_statement
+%type <interm.intermNode> declaration external_declaration
+%type <interm.intermNode> for_init_statement
+%type <interm.nodePair> selection_rest_statement for_rest_statement
+%type <interm.intermSwitch> switch_statement
+%type <interm.intermCase> case_label
+%type <interm.intermNode> iteration_statement jump_statement statement_no_new_scope statement_with_scope
+%type <interm> single_declaration init_declarator_list
+
+%type <interm> parameter_declaration parameter_declarator parameter_type_specifier
+%type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id
+
+%type <interm.type> fully_specified_type type_specifier
+
+%type <interm.precision> precision_qualifier
+%type <interm.layoutQualifier> layout_qualifier
+%type <interm.qualifier> interpolation_qualifier
+%type <interm.qualifierWrapper> storage_qualifier single_type_qualifier invariant_qualifier
+%type <interm.typeQualifierBuilder> type_qualifier
+
+%type <interm.typeSpecifierNonArray> type_specifier_nonarray struct_specifier
+%type <interm.type> type_specifier_no_prec
+%type <interm.field> struct_declarator
+%type <interm.fieldList> struct_declarator_list struct_declaration struct_declaration_list
+%type <interm.function> function_header function_declarator function_identifier
+%type <interm.function> function_header_with_parameters function_call_header
+%type <interm> function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype
+%type <interm> function_call_or_method
+
+%type <lex> enter_struct
+
+%start translation_unit
+%%
+
+identifier
+ : IDENTIFIER
+ | TYPE_NAME
+
+variable_identifier
+ : IDENTIFIER {
+ // The symbol table search was done in the lexical phase
+ $$ = context->parseVariableIdentifier(@1, $1.string, $1.symbol);
+
+ // don't delete $1.string, it's used by error recovery, and the pool
+ // pop will reclaim the memory
+ }
+ ;
+
+primary_expression
+ : variable_identifier {
+ $$ = $1;
+ }
+ | INTCONSTANT {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setIConst($1.i);
+ $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1);
+ }
+ | UINTCONSTANT {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setUConst($1.u);
+ $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtUInt, EbpUndefined, EvqConst), @1);
+ }
+ | FLOATCONSTANT {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setFConst($1.f);
+ $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1);
+ }
+ | BOOLCONSTANT {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setBConst($1.b);
+ $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @1);
+ }
+ | LEFT_PAREN expression RIGHT_PAREN {
+ $$ = $2;
+ }
+ ;
+
+postfix_expression
+ : primary_expression {
+ $$ = $1;
+ }
+ | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET {
+ $$ = context->addIndexExpression($1, @2, $3);
+ }
+ | function_call {
+ $$ = $1;
+ }
+ | postfix_expression DOT FIELD_SELECTION {
+ $$ = context->addFieldSelectionExpression($1, @2, *$3.string, @3);
+ }
+ | postfix_expression INC_OP {
+ $$ = context->addUnaryMathLValue(EOpPostIncrement, $1, @2);
+ }
+ | postfix_expression DEC_OP {
+ $$ = context->addUnaryMathLValue(EOpPostDecrement, $1, @2);
+ }
+ ;
+
+integer_expression
+ : expression {
+ context->checkIsScalarInteger($1, "[]");
+ $$ = $1;
+ }
+ ;
+
+function_call
+ : function_call_or_method {
+ bool fatalError = false;
+ $$ = context->addFunctionCallOrMethod($1.function, $1.nodePair.node1, $1.nodePair.node2, @1, &fatalError);
+ if (fatalError)
+ {
+ YYERROR;
+ }
+ }
+ ;
+
+function_call_or_method
+ : function_call_generic {
+ $$ = $1;
+ $$.nodePair.node2 = nullptr;
+ }
+ | postfix_expression DOT function_call_generic {
+ ES3_OR_NEWER("", @3, "methods");
+ $$ = $3;
+ $$.nodePair.node2 = $1;
+ }
+ ;
+
+function_call_generic
+ : function_call_header_with_parameters RIGHT_PAREN {
+ $$ = $1;
+ }
+ | function_call_header_no_parameters RIGHT_PAREN {
+ $$ = $1;
+ }
+ ;
+
+function_call_header_no_parameters
+ : function_call_header VOID_TYPE {
+ $$.function = $1;
+ $$.nodePair.node1 = nullptr;
+ }
+ | function_call_header {
+ $$.function = $1;
+ $$.nodePair.node1 = nullptr;
+ }
+ ;
+
+function_call_header_with_parameters
+ : function_call_header assignment_expression {
+ const TType *type = new TType($2->getType());
+ $1->addParameter(TConstParameter(type));
+ $$.function = $1;
+ $$.nodePair.node1 = TIntermediate::MakeAggregate($2, @2);
+ }
+ | function_call_header_with_parameters COMMA assignment_expression {
+ const TType *type = new TType($3->getType());
+ $1.function->addParameter(TConstParameter(type));
+ $$.function = $1.function;
+ $$.nodePair.node1 = context->intermediate.growAggregate($1.intermNode, $3, @2);
+ }
+ ;
+
+function_call_header
+ : function_identifier LEFT_PAREN {
+ $$ = $1;
+ }
+ ;
+
+// Grammar Note: Constructors look like functions, but are recognized as types.
+
+function_identifier
+ : type_specifier_no_prec {
+ if ($1.array) {
+ ES3_OR_NEWER("[]", @1, "array constructor");
+ }
+ $$ = context->addConstructorFunc($1);
+ }
+ | IDENTIFIER {
+ context->checkIsNotReserved(@1, *$1.string);
+ const TType *type = TCache::getType(EbtVoid, EbpUndefined);
+ TFunction *function = new TFunction($1.string, type);
+ $$ = function;
+ }
+ | FIELD_SELECTION {
+ context->checkIsNotReserved(@1, *$1.string);
+ const TType *type = TCache::getType(EbtVoid, EbpUndefined);
+ TFunction *function = new TFunction($1.string, type);
+ $$ = function;
+ }
+ ;
+
+unary_expression
+ : postfix_expression {
+ $$ = $1;
+ }
+ | INC_OP unary_expression {
+ $$ = context->addUnaryMathLValue(EOpPreIncrement, $2, @1);
+ }
+ | DEC_OP unary_expression {
+ $$ = context->addUnaryMathLValue(EOpPreDecrement, $2, @1);
+ }
+ | unary_operator unary_expression {
+ if ($1.op != EOpNull) {
+ $$ = context->addUnaryMath($1.op, $2, @1);
+ } else
+ $$ = $2;
+ }
+ ;
+// Grammar Note: No traditional style type casts.
+
+unary_operator
+ : PLUS { $$.op = EOpPositive; }
+ | DASH { $$.op = EOpNegative; }
+ | BANG { $$.op = EOpLogicalNot; }
+ | TILDE {
+ ES3_OR_NEWER("~", @$, "bit-wise operator");
+ $$.op = EOpBitwiseNot;
+ }
+ ;
+// Grammar Note: No '*' or '&' unary ops. Pointers are not supported.
+
+multiplicative_expression
+ : unary_expression { $$ = $1; }
+ | multiplicative_expression STAR unary_expression {
+ $$ = context->addBinaryMath(EOpMul, $1, $3, @2);
+ }
+ | multiplicative_expression SLASH unary_expression {
+ $$ = context->addBinaryMath(EOpDiv, $1, $3, @2);
+ }
+ | multiplicative_expression PERCENT unary_expression {
+ ES3_OR_NEWER("%", @2, "integer modulus operator");
+ $$ = context->addBinaryMath(EOpIMod, $1, $3, @2);
+ }
+ ;
+
+additive_expression
+ : multiplicative_expression { $$ = $1; }
+ | additive_expression PLUS multiplicative_expression {
+ $$ = context->addBinaryMath(EOpAdd, $1, $3, @2);
+ }
+ | additive_expression DASH multiplicative_expression {
+ $$ = context->addBinaryMath(EOpSub, $1, $3, @2);
+ }
+ ;
+
+shift_expression
+ : additive_expression { $$ = $1; }
+ | shift_expression LEFT_OP additive_expression {
+ ES3_OR_NEWER("<<", @2, "bit-wise operator");
+ $$ = context->addBinaryMath(EOpBitShiftLeft, $1, $3, @2);
+ }
+ | shift_expression RIGHT_OP additive_expression {
+ ES3_OR_NEWER(">>", @2, "bit-wise operator");
+ $$ = context->addBinaryMath(EOpBitShiftRight, $1, $3, @2);
+ }
+ ;
+
+relational_expression
+ : shift_expression { $$ = $1; }
+ | relational_expression LEFT_ANGLE shift_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpLessThan, $1, $3, @2);
+ }
+ | relational_expression RIGHT_ANGLE shift_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpGreaterThan, $1, $3, @2);
+ }
+ | relational_expression LE_OP shift_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpLessThanEqual, $1, $3, @2);
+ }
+ | relational_expression GE_OP shift_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpGreaterThanEqual, $1, $3, @2);
+ }
+ ;
+
+equality_expression
+ : relational_expression { $$ = $1; }
+ | equality_expression EQ_OP relational_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpEqual, $1, $3, @2);
+ }
+ | equality_expression NE_OP relational_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpNotEqual, $1, $3, @2);
+ }
+ ;
+
+and_expression
+ : equality_expression { $$ = $1; }
+ | and_expression AMPERSAND equality_expression {
+ ES3_OR_NEWER("&", @2, "bit-wise operator");
+ $$ = context->addBinaryMath(EOpBitwiseAnd, $1, $3, @2);
+ }
+ ;
+
+exclusive_or_expression
+ : and_expression { $$ = $1; }
+ | exclusive_or_expression CARET and_expression {
+ ES3_OR_NEWER("^", @2, "bit-wise operator");
+ $$ = context->addBinaryMath(EOpBitwiseXor, $1, $3, @2);
+ }
+ ;
+
+inclusive_or_expression
+ : exclusive_or_expression { $$ = $1; }
+ | inclusive_or_expression VERTICAL_BAR exclusive_or_expression {
+ ES3_OR_NEWER("|", @2, "bit-wise operator");
+ $$ = context->addBinaryMath(EOpBitwiseOr, $1, $3, @2);
+ }
+ ;
+
+logical_and_expression
+ : inclusive_or_expression { $$ = $1; }
+ | logical_and_expression AND_OP inclusive_or_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpLogicalAnd, $1, $3, @2);
+ }
+ ;
+
+logical_xor_expression
+ : logical_and_expression { $$ = $1; }
+ | logical_xor_expression XOR_OP logical_and_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpLogicalXor, $1, $3, @2);
+ }
+ ;
+
+logical_or_expression
+ : logical_xor_expression { $$ = $1; }
+ | logical_or_expression OR_OP logical_xor_expression {
+ $$ = context->addBinaryMathBooleanResult(EOpLogicalOr, $1, $3, @2);
+ }
+ ;
+
+conditional_expression
+ : logical_or_expression { $$ = $1; }
+ | logical_or_expression QUESTION expression COLON assignment_expression {
+ $$ = context->addTernarySelection($1, $3, $5, @2);
+ }
+ ;
+
+assignment_expression
+ : conditional_expression { $$ = $1; }
+ | unary_expression assignment_operator assignment_expression {
+ context->checkCanBeLValue(@2, "assign", $1);
+ $$ = context->addAssign($2.op, $1, $3, @2);
+ }
+ ;
+
+assignment_operator
+ : EQUAL { $$.op = EOpAssign; }
+ | MUL_ASSIGN { $$.op = EOpMulAssign; }
+ | DIV_ASSIGN { $$.op = EOpDivAssign; }
+ | MOD_ASSIGN {
+ ES3_OR_NEWER("%=", @$, "integer modulus operator");
+ $$.op = EOpIModAssign;
+ }
+ | ADD_ASSIGN { $$.op = EOpAddAssign; }
+ | SUB_ASSIGN { $$.op = EOpSubAssign; }
+ | LEFT_ASSIGN {
+ ES3_OR_NEWER("<<=", @$, "bit-wise operator");
+ $$.op = EOpBitShiftLeftAssign;
+ }
+ | RIGHT_ASSIGN {
+ ES3_OR_NEWER(">>=", @$, "bit-wise operator");
+ $$.op = EOpBitShiftRightAssign;
+ }
+ | AND_ASSIGN {
+ ES3_OR_NEWER("&=", @$, "bit-wise operator");
+ $$.op = EOpBitwiseAndAssign;
+ }
+ | XOR_ASSIGN {
+ ES3_OR_NEWER("^=", @$, "bit-wise operator");
+ $$.op = EOpBitwiseXorAssign;
+ }
+ | OR_ASSIGN {
+ ES3_OR_NEWER("|=", @$, "bit-wise operator");
+ $$.op = EOpBitwiseOrAssign;
+ }
+ ;
+
+expression
+ : assignment_expression {
+ $$ = $1;
+ }
+ | expression COMMA assignment_expression {
+ $$ = context->addComma($1, $3, @2);
+ }
+ ;
+
+constant_expression
+ : conditional_expression {
+ context->checkIsConst($1);
+ $$ = $1;
+ }
+ ;
+
+enter_struct
+ : IDENTIFIER LEFT_BRACE {
+ context->enterStructDeclaration(@1, *$1.string);
+ $$ = $1;
+ }
+ ;
+
+declaration
+ : function_prototype SEMICOLON {
+ $$ = context->addFunctionPrototypeDeclaration(*($1.function), @1);
+ }
+ | init_declarator_list SEMICOLON {
+ $$ = $1.intermDeclaration;
+ }
+ | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON {
+ if (($2 == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) {
+ context->error(@1, "precision is not supported in fragment shader", "highp");
+ }
+ if (!context->symbolTable.setDefaultPrecision( $3, $2 )) {
+ context->error(@1, "illegal type argument for default precision qualifier", getBasicString($3.getBasicType()));
+ }
+ $$ = 0;
+ }
+ | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON {
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, NULL, @$, NULL, @$);
+ }
+ | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON {
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, NULL, @$);
+ }
+ | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON {
+ ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks");
+ $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, $7, @6);
+ }
+ | type_qualifier SEMICOLON {
+ context->parseGlobalLayoutQualifier(*$1);
+ $$ = 0;
+ }
+ | type_qualifier IDENTIFIER SEMICOLON // e.g. to qualify an existing variable as invariant
+ {
+ $$ = context->parseInvariantDeclaration(*$1, @2, $2.string, $2.symbol);
+ }
+ ;
+
+function_prototype
+ : function_declarator RIGHT_PAREN {
+ $$.function = context->parseFunctionDeclarator(@2, $1);
+ context->exitFunctionDeclaration();
+ }
+ ;
+
+function_declarator
+ : function_header {
+ $$ = $1;
+ }
+ | function_header_with_parameters {
+ $$ = $1;
+ }
+ ;
+
+
+function_header_with_parameters
+ : function_header parameter_declaration {
+ // Add the parameter
+ $$ = $1;
+ if ($2.param.type->getBasicType() != EbtVoid)
+ $1->addParameter($2.param.turnToConst());
+ else
+ delete $2.param.type;
+ }
+ | function_header_with_parameters COMMA parameter_declaration {
+ //
+ // Only first parameter of one-parameter functions can be void
+ // The check for named parameters not being void is done in parameter_declarator
+ //
+ if ($3.param.type->getBasicType() == EbtVoid) {
+ //
+ // This parameter > first is void
+ //
+ context->error(@2, "cannot be an argument type except for '(void)'", "void");
+ delete $3.param.type;
+ } else {
+ // Add the parameter
+ $$ = $1;
+ $1->addParameter($3.param.turnToConst());
+ }
+ }
+ ;
+
+function_header
+ : fully_specified_type IDENTIFIER LEFT_PAREN {
+ $$ = context->parseFunctionHeader($1, $2.string, @2);
+
+ context->symbolTable.push();
+ context->enterFunctionDeclaration();
+ }
+ ;
+
+parameter_declarator
+ // Type + name
+ : type_specifier identifier {
+ if ($1.getBasicType() == EbtVoid) {
+ context->error(@2, "illegal use of type 'void'", $2.string->c_str());
+ }
+ context->checkIsNotReserved(@2, *$2.string);
+ TParameter param = {$2.string, new TType($1)};
+ $$.param = param;
+ }
+ | type_specifier identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ // Check that we can make an array out of this type
+ context->checkIsValidTypeForArray(@3, $1);
+
+ context->checkIsNotReserved(@2, *$2.string);
+
+ unsigned int size = context->checkIsValidArraySize(@3, $4);
+
+ $1.setArraySize(size);
+
+ TType* type = new TType($1);
+ TParameter param = { $2.string, type };
+ $$.param = param;
+ }
+ ;
+
+parameter_declaration
+ //
+ // The only parameter qualifier a parameter can have are
+ // IN_QUAL, OUT_QUAL, INOUT_QUAL, or CONST.
+ //
+
+ //
+ // Type + name
+ //
+ : type_qualifier parameter_declarator {
+ $$ = $2;
+ context->checkIsParameterQualifierValid(@2, *$1, $2.param.type);
+ }
+ | parameter_declarator {
+ $$ = $1;
+ $$.param.type->setQualifier(EvqIn);
+ }
+ | type_qualifier parameter_type_specifier {
+ $$ = $2;
+ context->checkIsParameterQualifierValid(@2, *$1, $2.param.type);
+ }
+ | parameter_type_specifier {
+ $$ = $1;
+ $$.param.type->setQualifier(EvqIn);
+ }
+ ;
+
+parameter_type_specifier
+ : type_specifier {
+ TParameter param = { 0, new TType($1) };
+ $$.param = param;
+ }
+ ;
+
+init_declarator_list
+ : single_declaration {
+ $$ = $1;
+ }
+ | init_declarator_list COMMA identifier {
+ $$ = $1;
+ context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration);
+ }
+ | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ $$ = $1;
+ context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
+ }
+ | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
+ ES3_OR_NEWER("[]", @3, "implicitly sized array");
+ $$ = $1;
+ context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, nullptr, @6, $7, $$.intermDeclaration);
+ }
+ | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
+ ES3_OR_NEWER("=", @7, "first-class arrays (array initializer)");
+ $$ = $1;
+ context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, $5, @7, $8, $$.intermDeclaration);
+ }
+ | init_declarator_list COMMA identifier EQUAL initializer {
+ $$ = $1;
+ context->parseInitDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
+ }
+ ;
+
+single_declaration
+ : fully_specified_type {
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, "");
+ }
+ | fully_specified_type identifier {
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
+ }
+ | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4);
+ }
+ | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
+ ES3_OR_NEWER("[]", @3, "implicitly sized array");
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6);
+ }
+ | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
+ ES3_OR_NEWER("=", @6, "first-class arrays (array initializer)");
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7);
+ }
+ | fully_specified_type identifier EQUAL initializer {
+ $$.type = $1;
+ $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
+ }
+ ;
+
+fully_specified_type
+ : type_specifier {
+ context->addFullySpecifiedType(&$1);
+ $$ = $1;
+ }
+ | type_qualifier type_specifier {
+ $$ = context->addFullySpecifiedType(*$1, $2);
+ }
+ ;
+
+interpolation_qualifier
+ : SMOOTH {
+ $$ = EvqSmooth;
+ }
+ | FLAT {
+ $$ = EvqFlat;
+ }
+ ;
+
+type_qualifier
+ : single_type_qualifier {
+ $$ = context->createTypeQualifierBuilder(@1);
+ $$->appendQualifier($1);
+ }
+ | type_qualifier single_type_qualifier {
+ $$ = $1;
+ $$->appendQualifier($2);
+ }
+ ;
+
+invariant_qualifier
+ : INVARIANT {
+ // empty
+ }
+ ;
+
+single_type_qualifier
+ : storage_qualifier {
+ context->checkLocalVariableConstStorageQualifier(*$1);
+ $$ = $1;
+ }
+ | layout_qualifier {
+ context->checkIsAtGlobalLevel(@1, "layout");
+ $$ = new TLayoutQualifierWrapper($1, @1);
+ }
+ | precision_qualifier {
+ $$ = new TPrecisionQualifierWrapper($1, @1);
+ }
+ | interpolation_qualifier {
+ $$ = new TInterpolationQualifierWrapper($1, @1);
+ }
+ | invariant_qualifier {
+ context->checkIsAtGlobalLevel(@1, "invariant");
+ $$ = new TInvariantQualifierWrapper(@1);
+ }
+ ;
+
+
+storage_qualifier
+ :
+ ATTRIBUTE {
+ VERTEX_ONLY("attribute", @1);
+ ES2_ONLY("attribute", @1);
+ context->checkIsAtGlobalLevel(@1, "attribute");
+ $$ = new TStorageQualifierWrapper(EvqAttribute, @1);
+ }
+ | VARYING {
+ ES2_ONLY("varying", @1);
+ context->checkIsAtGlobalLevel(@1, "varying");
+ if (context->getShaderType() == GL_VERTEX_SHADER)
+ $$ = new TStorageQualifierWrapper(EvqVaryingOut, @1);
+ else
+ $$ = new TStorageQualifierWrapper(EvqVaryingIn, @1);
+ }
+ | CONST_QUAL {
+ $$ = new TStorageQualifierWrapper(EvqConst, @1);
+ }
+ | IN_QUAL {
+ if (context->declaringFunction())
+ {
+ $$ = new TStorageQualifierWrapper(EvqIn, @1);
+ }
+ else if (context->getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ ES3_OR_NEWER("in", @1, "storage qualifier");
+ $$ = new TStorageQualifierWrapper(EvqFragmentIn, @1);
+ }
+ else if (context->getShaderType() == GL_VERTEX_SHADER)
+ {
+ ES3_OR_NEWER("in", @1, "storage qualifier");
+ $$ = new TStorageQualifierWrapper(EvqVertexIn, @1);
+ }
+ else
+ {
+ $$ = new TStorageQualifierWrapper(EvqComputeIn, @1);
+ }
+ }
+ | OUT_QUAL {
+ if (context->declaringFunction())
+ {
+ $$ = new TStorageQualifierWrapper(EvqOut, @1);
+ }
+ else
+ {
+ ES3_OR_NEWER("out", @1, "storage qualifier");
+ NON_COMPUTE_ONLY("out", @1);
+ if (context->getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ $$ = new TStorageQualifierWrapper(EvqFragmentOut, @1);
+ }
+ else
+ {
+ $$ = new TStorageQualifierWrapper(EvqVertexOut, @1);
+ }
+ }
+ }
+ | INOUT_QUAL {
+ if (!context->declaringFunction())
+ {
+ context->error(@1, "invalid inout qualifier", "'inout' can be only used with function parameters");
+ }
+ $$ = new TStorageQualifierWrapper(EvqInOut, @1);
+ }
+ | CENTROID {
+ ES3_OR_NEWER("centroid", @1, "storage qualifier");
+ $$ = new TStorageQualifierWrapper(EvqCentroid, @1);
+ }
+ | UNIFORM {
+ context->checkIsAtGlobalLevel(@1, "uniform");
+ $$ = new TStorageQualifierWrapper(EvqUniform, @1);
+ }
+ | READONLY {
+ $$ = new TMemoryQualifierWrapper(EvqReadOnly, @1);
+ }
+ | WRITEONLY {
+ $$ = new TMemoryQualifierWrapper(EvqWriteOnly, @1);
+ }
+ | COHERENT {
+ $$ = new TMemoryQualifierWrapper(EvqCoherent, @1);
+ }
+ | RESTRICT {
+ $$ = new TMemoryQualifierWrapper(EvqRestrict, @1);
+ }
+ | VOLATILE {
+ $$ = new TMemoryQualifierWrapper(EvqVolatile, @1);
+ }
+ ;
+
+type_specifier
+ : type_specifier_no_prec {
+ $$ = $1;
+ $$.precision = context->symbolTable.getDefaultPrecision($1.getBasicType());
+ }
+ ;
+
+precision_qualifier
+ : HIGH_PRECISION {
+ $$ = EbpHigh;
+ }
+ | MEDIUM_PRECISION {
+ $$ = EbpMedium;
+ }
+ | LOW_PRECISION {
+ $$ = EbpLow;
+ }
+ ;
+
+layout_qualifier
+ : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN {
+ ES3_OR_NEWER("layout", @1, "qualifier");
+ $$ = $3;
+ }
+ ;
+
+layout_qualifier_id_list
+ : layout_qualifier_id {
+ $$ = $1;
+ }
+ | layout_qualifier_id_list COMMA layout_qualifier_id {
+ $$ = context->joinLayoutQualifiers($1, $3, @3);
+ }
+ ;
+
+layout_qualifier_id
+ : IDENTIFIER {
+ $$ = context->parseLayoutQualifier(*$1.string, @1);
+ }
+ | IDENTIFIER EQUAL INTCONSTANT {
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
+ }
+ | IDENTIFIER EQUAL UINTCONSTANT {
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
+ }
+ ;
+
+type_specifier_no_prec
+ : type_specifier_nonarray {
+ $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ }
+ | type_specifier_nonarray LEFT_BRACKET RIGHT_BRACKET {
+ ES3_OR_NEWER("[]", @2, "implicitly sized array");
+ $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ $$.setArraySize(0);
+ }
+ | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ if (context->checkIsValidTypeForArray(@2, $$))
+ {
+ unsigned int size = context->checkIsValidArraySize(@2, $3);
+ $$.setArraySize(size);
+ }
+ }
+ ;
+
+type_specifier_nonarray
+ : VOID_TYPE {
+ $$.initialize(EbtVoid, @1);
+ }
+ | FLOAT_TYPE {
+ $$.initialize(EbtFloat, @1);
+ }
+ | INT_TYPE {
+ $$.initialize(EbtInt, @1);
+ }
+ | UINT_TYPE {
+ $$.initialize(EbtUInt, @1);
+ }
+ | BOOL_TYPE {
+ $$.initialize(EbtBool, @1);
+ }
+ | VEC2 {
+ $$.initialize(EbtFloat, @1);
+ $$.setAggregate(2);
+ }
+ | VEC3 {
+ $$.initialize(EbtFloat, @1);
+ $$.setAggregate(3);
+ }
+ | VEC4 {
+ $$.initialize(EbtFloat, @1);
+ $$.setAggregate(4);
+ }
+ | BVEC2 {
+ $$.initialize(EbtBool, @1);
+ $$.setAggregate(2);
+ }
+ | BVEC3 {
+ $$.initialize(EbtBool, @1);
+ $$.setAggregate(3);
+ }
+ | BVEC4 {
+ $$.initialize(EbtBool, @1);
+ $$.setAggregate(4);
+ }
+ | IVEC2 {
+ $$.initialize(EbtInt, @1);
+ $$.setAggregate(2);
+ }
+ | IVEC3 {
+ $$.initialize(EbtInt, @1);
+ $$.setAggregate(3);
+ }
+ | IVEC4 {
+ $$.initialize(EbtInt, @1);
+ $$.setAggregate(4);
+ }
+ | UVEC2 {
+ $$.initialize(EbtUInt, @1);
+ $$.setAggregate(2);
+ }
+ | UVEC3 {
+ $$.initialize(EbtUInt, @1);
+ $$.setAggregate(3);
+ }
+ | UVEC4 {
+ $$.initialize(EbtUInt, @1);
+ $$.setAggregate(4);
+ }
+ | MATRIX2 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(2, 2);
+ }
+ | MATRIX3 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(3, 3);
+ }
+ | MATRIX4 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(4, 4);
+ }
+ | MATRIX2x3 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(2, 3);
+ }
+ | MATRIX3x2 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(3, 2);
+ }
+ | MATRIX2x4 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(2, 4);
+ }
+ | MATRIX4x2 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(4, 2);
+ }
+ | MATRIX3x4 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(3, 4);
+ }
+ | MATRIX4x3 {
+ $$.initialize(EbtFloat, @1);
+ $$.setMatrix(4, 3);
+ }
+ | SAMPLER2D {
+ $$.initialize(EbtSampler2D, @1);
+ }
+ | SAMPLER3D {
+ $$.initialize(EbtSampler3D, @1);
+ }
+ | SAMPLERCUBE {
+ $$.initialize(EbtSamplerCube, @1);
+ }
+ | SAMPLER2DARRAY {
+ $$.initialize(EbtSampler2DArray, @1);
+ }
+ | ISAMPLER2D {
+ $$.initialize(EbtISampler2D, @1);
+ }
+ | ISAMPLER3D {
+ $$.initialize(EbtISampler3D, @1);
+ }
+ | ISAMPLERCUBE {
+ $$.initialize(EbtISamplerCube, @1);
+ }
+ | ISAMPLER2DARRAY {
+ $$.initialize(EbtISampler2DArray, @1);
+ }
+ | USAMPLER2D {
+ $$.initialize(EbtUSampler2D, @1);
+ }
+ | USAMPLER3D {
+ $$.initialize(EbtUSampler3D, @1);
+ }
+ | USAMPLERCUBE {
+ $$.initialize(EbtUSamplerCube, @1);
+ }
+ | USAMPLER2DARRAY {
+ $$.initialize(EbtUSampler2DArray, @1);
+ }
+ | SAMPLER2DSHADOW {
+ $$.initialize(EbtSampler2DShadow, @1);
+ }
+ | SAMPLERCUBESHADOW {
+ $$.initialize(EbtSamplerCubeShadow, @1);
+ }
+ | SAMPLER2DARRAYSHADOW {
+ $$.initialize(EbtSampler2DArrayShadow, @1);
+ }
+ | SAMPLER_EXTERNAL_OES {
+ if (!context->supportsExtension("GL_OES_EGL_image_external") &&
+ !context->supportsExtension("GL_NV_EGL_stream_consumer_external")) {
+ context->error(@1, "unsupported type", "samplerExternalOES");
+ }
+ $$.initialize(EbtSamplerExternalOES, @1);
+ }
+ | SAMPLER2DRECT {
+ if (!context->supportsExtension("GL_ARB_texture_rectangle")) {
+ context->error(@1, "unsupported type", "sampler2DRect");
+ }
+ $$.initialize(EbtSampler2DRect, @1);
+ }
+ | struct_specifier {
+ $$ = $1;
+ }
+ | IMAGE2D {
+ $$.initialize(EbtImage2D, @1);
+ }
+ | IIMAGE2D {
+ $$.initialize(EbtIImage2D, @1);
+ }
+ | UIMAGE2D {
+ $$.initialize(EbtUImage2D, @1);
+ }
+ | IMAGE3D {
+ $$.initialize(EbtImage3D, @1);
+ }
+ | IIMAGE3D {
+ $$.initialize(EbtIImage3D, @1);
+ }
+ | UIMAGE3D {
+ $$.initialize(EbtUImage3D, @1);
+ }
+ | IMAGE2DARRAY {
+ $$.initialize(EbtImage2DArray, @1);
+ }
+ | IIMAGE2DARRAY {
+ $$.initialize(EbtIImage2DArray, @1);
+ }
+ | UIMAGE2DARRAY {
+ $$.initialize(EbtUImage2DArray, @1);
+ }
+ | IMAGECUBE {
+ $$.initialize(EbtImageCube, @1);
+ }
+ | IIMAGECUBE {
+ $$.initialize(EbtIImageCube, @1);
+ }
+ | UIMAGECUBE {
+ $$.initialize(EbtUImageCube, @1);
+ }
+ | TYPE_NAME {
+ //
+ // This is for user defined type names. The lexical phase looked up the
+ // type.
+ //
+ TType& structure = static_cast<TVariable*>($1.symbol)->getType();
+ $$.initialize(EbtStruct, @1);
+ $$.userDef = &structure;
+ }
+ ;
+
+struct_specifier
+ : STRUCT identifier LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
+ $$ = context->addStructure(@1, @2, $2.string, $5);
+ }
+ | STRUCT LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE {
+ $$ = context->addStructure(@1, @$, NewPoolTString(""), $4);
+ }
+ ;
+
+struct_declaration_list
+ : struct_declaration {
+ $$ = $1;
+ }
+ | struct_declaration_list struct_declaration {
+ $$ = $1;
+ for (size_t i = 0; i < $2->size(); ++i) {
+ TField* field = (*$2)[i];
+ for (size_t j = 0; j < $$->size(); ++j) {
+ if ((*$$)[j]->name() == field->name()) {
+ context->error(@2, "duplicate field name in structure:", "struct", field->name().c_str());
+ }
+ }
+ $$->push_back(field);
+ }
+ }
+ ;
+
+struct_declaration
+ : type_specifier struct_declarator_list SEMICOLON {
+ $$ = context->addStructDeclaratorList($1, $2);
+ }
+ | type_qualifier type_specifier struct_declarator_list SEMICOLON {
+ // ES3 Only, but errors should be handled elsewhere
+ $$ = context->addStructDeclaratorListWithQualifiers(*$1, &$2, $3);
+ }
+ ;
+
+struct_declarator_list
+ : struct_declarator {
+ $$ = NewPoolTFieldList();
+ $$->push_back($1);
+ }
+ | struct_declarator_list COMMA struct_declarator {
+ $$->push_back($3);
+ }
+ ;
+
+struct_declarator
+ : identifier {
+ context->checkIsNotReserved(@1, *$1.string);
+
+ TType* type = new TType(EbtVoid, EbpUndefined);
+ $$ = new TField(type, $1.string, @1);
+ }
+ | identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
+ context->checkIsNotReserved(@1, *$1.string);
+
+ TType* type = new TType(EbtVoid, EbpUndefined);
+ unsigned int size = context->checkIsValidArraySize(@3, $3);
+ type->setArraySize(size);
+
+ $$ = new TField(type, $1.string, @1);
+ }
+ ;
+
+initializer
+ : assignment_expression { $$ = $1; }
+ ;
+
+declaration_statement
+ : declaration { $$ = $1; }
+ ;
+
+statement
+ : compound_statement { $$ = $1; }
+ | simple_statement { $$ = $1; }
+ ;
+
+// Grammar Note: Labeled statements for SWITCH only; 'goto' is not supported.
+
+simple_statement
+ : declaration_statement { $$ = $1; }
+ | expression_statement { $$ = $1; }
+ | selection_statement { $$ = $1; }
+ | switch_statement { $$ = $1; }
+ | case_label { $$ = $1; }
+ | iteration_statement { $$ = $1; }
+ | jump_statement { $$ = $1; }
+ ;
+
+compound_statement
+ : LEFT_BRACE RIGHT_BRACE { $$ = 0; }
+ | LEFT_BRACE { context->symbolTable.push(); } statement_list { context->symbolTable.pop(); } RIGHT_BRACE {
+ if ($3 != 0) {
+ $3->setLine(@$);
+ }
+ $$ = $3;
+ }
+ ;
+
+statement_no_new_scope
+ : compound_statement_no_new_scope { $$ = $1; }
+ | simple_statement { $$ = $1; }
+ ;
+
+statement_with_scope
+ : { context->symbolTable.push(); } compound_statement_no_new_scope { context->symbolTable.pop(); $$ = $2; }
+ | { context->symbolTable.push(); } simple_statement { context->symbolTable.pop(); $$ = $2; }
+ ;
+
+compound_statement_no_new_scope
+ // Statement that doesn't create a new scope, for selection_statement, iteration_statement
+ : LEFT_BRACE RIGHT_BRACE {
+ $$ = 0;
+ }
+ | LEFT_BRACE statement_list RIGHT_BRACE {
+ if ($2) {
+ $2->setLine(@$);
+ }
+ $$ = $2;
+ }
+ ;
+
+statement_list
+ : statement {
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
+ $$->appendStatement($1);
+ }
+ | statement_list statement {
+ $$ = $1;
+ $$->appendStatement($2);
+ }
+ ;
+
+expression_statement
+ : SEMICOLON { $$ = 0; }
+ | expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); }
+ ;
+
+selection_statement
+ : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement {
+ context->checkIsScalarBool(@1, $3);
+ $$ = context->intermediate.addIfElse($3, $5, @1);
+ }
+ ;
+
+selection_rest_statement
+ : statement_with_scope ELSE statement_with_scope {
+ $$.node1 = $1;
+ $$.node2 = $3;
+ }
+ | statement_with_scope {
+ $$.node1 = $1;
+ $$.node2 = 0;
+ }
+ ;
+
+switch_statement
+ : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement {
+ $$ = context->addSwitch($3, $6, @1);
+ context->decrSwitchNestingLevel();
+ }
+ ;
+
+case_label
+ : CASE constant_expression COLON {
+ $$ = context->addCase($2, @1);
+ }
+ | DEFAULT COLON {
+ $$ = context->addDefault(@1);
+ }
+ ;
+
+condition
+ // In 1996 c++ draft, conditions can include single declarations
+ : expression {
+ $$ = $1;
+ context->checkIsScalarBool($1->getLine(), $1);
+ }
+ | fully_specified_type identifier EQUAL initializer {
+ TIntermBinary *initNode = nullptr;
+ context->checkIsScalarBool(@2, $1);
+
+ if (!context->executeInitializer(@2, *$2.string, $1, $4, &initNode))
+ $$ = $4;
+ else {
+ $$ = 0;
+ }
+ }
+ ;
+
+iteration_statement
+ : WHILE LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } condition RIGHT_PAREN statement_no_new_scope {
+ context->symbolTable.pop();
+ $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1);
+ context->decrLoopNestingLevel();
+ }
+ | DO { context->incrLoopNestingLevel(); } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON {
+ context->checkIsScalarBool(@8, $6);
+
+ $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4);
+ context->decrLoopNestingLevel();
+ }
+ | FOR LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope {
+ context->symbolTable.pop();
+ $$ = context->intermediate.addLoop(ELoopFor, $4, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), $7, @1);
+ context->decrLoopNestingLevel();
+ }
+ ;
+
+for_init_statement
+ : expression_statement {
+ $$ = $1;
+ }
+ | declaration_statement {
+ $$ = $1;
+ }
+ ;
+
+conditionopt
+ : condition {
+ $$ = $1;
+ }
+ | /* May be null */ {
+ $$ = 0;
+ }
+ ;
+
+for_rest_statement
+ : conditionopt SEMICOLON {
+ $$.node1 = $1;
+ $$.node2 = 0;
+ }
+ | conditionopt SEMICOLON expression {
+ $$.node1 = $1;
+ $$.node2 = $3;
+ }
+ ;
+
+jump_statement
+ : CONTINUE SEMICOLON {
+ $$ = context->addBranch(EOpContinue, @1);
+ }
+ | BREAK SEMICOLON {
+ $$ = context->addBranch(EOpBreak, @1);
+ }
+ | RETURN SEMICOLON {
+ $$ = context->addBranch(EOpReturn, @1);
+ }
+ | RETURN expression SEMICOLON {
+ $$ = context->addBranch(EOpReturn, $2, @1);
+ }
+ | DISCARD SEMICOLON {
+ FRAG_ONLY("discard", @1);
+ $$ = context->addBranch(EOpKill, @1);
+ }
+ ;
+
+// Grammar Note: No 'goto'. Gotos are not supported.
+
+translation_unit
+ : external_declaration {
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
+ $$->appendStatement($1);
+ context->setTreeRoot($$);
+ }
+ | translation_unit external_declaration {
+ $$->appendStatement($2);
+ }
+ ;
+
+external_declaration
+ : function_definition {
+ $$ = $1;
+ }
+ | declaration {
+ $$ = $1;
+ }
+ ;
+
+function_definition
+ : function_prototype {
+ context->parseFunctionDefinitionHeader(@1, &($1.function), &$1.intermAggregate);
+ }
+ compound_statement_no_new_scope {
+ $$ = context->addFunctionDefinition(*($1.function), $1.intermAggregate, $3, @1);
+ }
+ ;
+
+%%
+
+int glslang_parse(TParseContext* context) {
+ return yyparse(context, context->getScanner());
+}
diff --git a/gfx/angle/src/compiler/translator/glslang_lex.cpp b/gfx/angle/src/compiler/translator/glslang_lex.cpp
new file mode 100755
index 000000000..2dcc1bc0f
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang_lex.cpp
@@ -0,0 +1,3513 @@
+//
+// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+/* clang-format off */
+
+// Ignore errors in auto-generated code.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4005)
+#pragma warning(disable: 4065)
+#pragma warning(disable: 4189)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4505)
+#pragma warning(disable: 4701)
+#pragma warning(disable: 4702)
+#endif
+
+
+
+#line 30 "./glslang_lex.cpp"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 39
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+ are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+ * access to the local variable yy_act. Since yyless() is a macro, it would break
+ * existing scanners that call yyless() from OUTSIDE yylex.
+ * One obvious solution it to make yy_act a global. I tried that, and saw
+ * a 5% performance hit in a non-yylineno scanner, because yy_act is
+ * normally declared as a register variable-- so it is not worth it.
+ */
+ #define YY_LESS_LINENO(n) \
+ do { \
+ yy_size_t yyl;\
+ for ( yyl = n; yyl < static_cast<yy_site_t>(yyleng); ++yyl )\
+ if ( yytext[yyl] == '\n' )\
+ --yylineno;\
+ }while(0)
+ #define YY_LINENO_REWIND_TO(dst) \
+ do {\
+ const char *p;\
+ for ( p = yy_cp-1; p >= (dst); --p)\
+ if ( *p == '\n' )\
+ --yylineno;\
+ }while(0)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+void yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void yypop_buffer_state (yyscan_t yyscanner );
+
+static void yyensure_buffer_stack (yyscan_t yyscanner );
+static void yy_load_buffer_state (yyscan_t yyscanner );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void yyfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap(yyscanner) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 240
+#define YY_END_OF_BUFFER 241
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[820] =
+ { 0,
+ 0, 0, 0, 0, 241, 239, 238, 238, 222, 228,
+ 233, 217, 218, 226, 225, 214, 223, 221, 227, 180,
+ 180, 215, 211, 229, 216, 230, 234, 177, 219, 220,
+ 232, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 212, 231, 213, 224, 237, 236, 240, 235, 208,
+ 194, 213, 202, 197, 192, 200, 190, 201, 191, 186,
+ 193, 185, 179, 180, 0, 183, 0, 220, 212, 219,
+ 209, 205, 207, 206, 210, 177, 198, 204, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+
+ 12, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 15, 177, 177, 23, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 199, 203, 235, 0, 189, 185, 0, 188, 182,
+ 0, 184, 178, 195, 196, 177, 137, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 13, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+
+ 177, 27, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 24, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 0, 186,
+ 0, 185, 187, 181, 177, 177, 177, 30, 177, 177,
+ 18, 174, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 16, 140, 177, 177, 177, 177, 21, 177,
+ 177, 144, 155, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 152, 4, 35, 36, 37,
+
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 143, 31, 177, 177, 28, 177,
+ 177, 177, 177, 177, 177, 177, 47, 48, 49, 29,
+ 177, 177, 177, 177, 177, 177, 10, 53, 54, 55,
+ 177, 138, 177, 177, 7, 177, 177, 177, 177, 164,
+ 165, 166, 177, 32, 177, 156, 26, 167, 168, 169,
+ 2, 161, 162, 163, 177, 177, 177, 25, 159, 177,
+ 177, 177, 50, 51, 52, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 99, 177, 177, 177,
+
+ 177, 177, 177, 177, 153, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 139, 177, 177, 176,
+ 56, 57, 58, 177, 177, 14, 177, 104, 177, 177,
+ 177, 177, 102, 177, 177, 177, 154, 149, 105, 177,
+ 177, 177, 177, 177, 177, 145, 177, 177, 177, 78,
+ 38, 41, 43, 42, 39, 45, 44, 46, 40, 177,
+ 177, 177, 177, 160, 136, 177, 177, 147, 177, 177,
+ 177, 34, 100, 173, 22, 148, 77, 177, 158, 17,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 19, 33, 177, 177, 177, 177,
+
+ 177, 177, 106, 79, 85, 177, 177, 177, 177, 177,
+ 3, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 141, 177, 177, 177, 177, 177, 8, 177,
+ 177, 9, 177, 177, 177, 177, 20, 93, 11, 150,
+ 107, 80, 87, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 146, 177, 177, 177, 91,
+ 96, 94, 177, 177, 177, 177, 177, 177, 177, 142,
+ 108, 81, 86, 177, 177, 157, 177, 95, 177, 177,
+ 6, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 90, 151, 1, 177, 177, 177, 177, 177, 175, 177,
+
+ 103, 5, 170, 59, 62, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 177, 92, 177,
+ 177, 177, 177, 88, 177, 177, 177, 177, 177, 121,
+ 66, 67, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 101, 177, 177, 177, 89,
+ 123, 70, 71, 177, 177, 97, 177, 177, 177, 177,
+ 177, 177, 177, 116, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 130, 177, 177, 177, 177, 60,
+ 177, 177, 177, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 117, 109, 177, 82, 177, 177, 177, 131,
+
+ 177, 177, 68, 177, 177, 177, 177, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 118, 177, 177, 132,
+ 177, 177, 72, 110, 83, 177, 112, 177, 113, 177,
+ 177, 177, 177, 177, 98, 177, 177, 177, 177, 64,
+ 177, 63, 127, 177, 177, 111, 84, 177, 177, 177,
+ 177, 177, 177, 177, 177, 177, 177, 125, 128, 119,
+ 177, 65, 177, 177, 177, 177, 177, 177, 177, 177,
+ 126, 129, 177, 177, 122, 69, 177, 177, 171, 177,
+ 177, 177, 74, 177, 177, 124, 73, 177, 177, 177,
+ 177, 177, 177, 133, 177, 177, 177, 177, 177, 177,
+
+ 134, 177, 177, 177, 75, 177, 135, 114, 115, 177,
+ 177, 177, 61, 177, 177, 172, 120, 76, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 1, 1, 1, 5, 6, 1, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 20, 20, 21, 21, 22, 23, 24,
+ 25, 26, 27, 1, 28, 29, 30, 31, 32, 33,
+ 34, 34, 34, 34, 34, 34, 35, 34, 36, 34,
+ 34, 37, 38, 34, 39, 34, 34, 40, 34, 34,
+ 41, 1, 42, 43, 44, 1, 45, 46, 47, 48,
+
+ 49, 50, 51, 52, 53, 34, 54, 55, 56, 57,
+ 58, 59, 34, 60, 61, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[73] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 1, 1, 1, 1, 1, 1, 3, 3, 3,
+ 3, 2, 2, 4, 4, 4, 4, 4, 4, 4,
+ 1, 1, 1, 4, 3, 3, 3, 3, 2, 2,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 1, 1,
+ 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[825] =
+ { 0,
+ 0, 0, 72, 0, 1016, 1017, 1017, 1017, 990, 120,
+ 141, 1017, 1017, 989, 138, 1017, 137, 135, 988, 154,
+ 208, 986, 1017, 154, 986, 132, 1017, 0, 1017, 1017,
+ 139, 130, 123, 140, 147, 133, 177, 952, 186, 151,
+ 139, 116, 161, 946, 173, 959, 193, 199, 208, 215,
+ 108, 1017, 184, 1017, 1017, 1017, 1017, 1017, 0, 1017,
+ 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 230,
+ 1017, 235, 235, 0, 271, 1017, 0, 1017, 1017, 1017,
+ 982, 1017, 1017, 1017, 981, 0, 1017, 1017, 943, 948,
+ 152, 945, 953, 952, 939, 942, 953, 243, 947, 935,
+
+ 932, 945, 932, 929, 929, 935, 147, 248, 929, 939,
+ 925, 931, 934, 935, 0, 927, 937, 249, 936, 931,
+ 912, 177, 916, 929, 920, 184, 913, 250, 925, 927,
+ 257, 916, 913, 902, 911, 249, 257, 915, 911, 913,
+ 902, 905, 196, 217, 269, 914, 902, 914, 262, 907,
+ 906, 1017, 1017, 0, 311, 1017, 292, 328, 1017, 1017,
+ 335, 342, 257, 1017, 1017, 905, 0, 901, 896, 900,
+ 909, 906, 315, 890, 890, 901, 893, 215, 903, 900,
+ 900, 898, 895, 887, 893, 880, 878, 890, 876, 892,
+ 0, 889, 877, 884, 881, 885, 886, 879, 876, 865,
+
+ 864, 877, 880, 868, 876, 864, 870, 861, 316, 866,
+ 869, 860, 867, 856, 860, 851, 865, 864, 855, 861,
+ 307, 845, 848, 846, 856, 846, 841, 839, 841, 851,
+ 837, 839, 836, 847, 846, 849, 831, 316, 839, 835,
+ 833, 842, 821, 353, 839, 841, 830, 822, 363, 370,
+ 378, 389, 1017, 1017, 819, 829, 828, 0, 826, 383,
+ 0, 0, 819, 817, 817, 818, 813, 821, 810, 827,
+ 816, 394, 0, 0, 810, 820, 819, 819, 0, 804,
+ 397, 0, 0, 806, 400, 813, 814, 805, 799, 798,
+ 799, 798, 798, 406, 793, 0, 0, 789, 788, 787,
+
+ 789, 790, 795, 789, 785, 798, 793, 793, 791, 790,
+ 784, 778, 780, 779, 783, 775, 778, 773, 781, 786,
+ 774, 771, 783, 774, 0, 0, 780, 776, 0, 768,
+ 768, 773, 764, 771, 409, 768, 0, 0, 0, 0,
+ 758, 770, 769, 768, 769, 769, 0, 0, 0, 0,
+ 756, 0, 764, 755, 0, 754, 755, 749, 759, 0,
+ 0, 0, 750, 0, 746, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 756, 413, 755, 0, 0, 753,
+ 749, 746, 0, 0, 0, 738, 415, 418, 427, 743,
+ 739, 744, 735, 733, 746, 731, 0, 731, 744, 733,
+
+ 729, 735, 730, 737, 0, 735, 732, 736, 720, 718,
+ 721, 727, 733, 728, 727, 715, 0, 717, 718, 0,
+ 0, 0, 0, 715, 718, 0, 712, 0, 725, 705,
+ 714, 709, 0, 702, 702, 715, 0, 717, 0, 431,
+ 730, 729, 728, 695, 694, 0, 711, 710, 705, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 694,
+ 707, 694, 691, 0, 0, 696, 695, 0, 692, 699,
+ 698, 0, 684, 0, 0, 0, 0, 681, 0, 0,
+ 680, 691, 434, 684, 690, 689, 686, 681, 678, 671,
+ 671, 684, 669, 681, 0, 0, 674, 697, 696, 695,
+
+ 662, 661, 427, 428, 0, 673, 676, 674, 663, 659,
+ 0, 671, 668, 667, 657, 656, 646, 663, 649, 441,
+ 657, 660, 0, 677, 676, 675, 642, 641, 0, 655,
+ 642, 0, 652, 645, 646, 649, 0, 0, 0, 0,
+ 669, 668, 0, 645, 648, 633, 640, 631, 638, 639,
+ 639, 638, 624, 451, 636, 0, 637, 626, 625, 0,
+ 0, 0, 650, 649, 648, 615, 614, 610, 618, 0,
+ 646, 645, 0, 622, 625, 0, 458, 0, 603, 612,
+ 0, 608, 607, 616, 616, 604, 618, 602, 616, 611,
+ 0, 0, 0, 628, 627, 626, 593, 592, 0, 592,
+
+ 0, 0, 434, 454, 616, 602, 605, 588, 600, 588,
+ 587, 596, 596, 613, 612, 611, 578, 577, 0, 577,
+ 578, 577, 587, 0, 590, 586, 588, 584, 571, 602,
+ 449, 0, 579, 582, 574, 566, 573, 564, 585, 573,
+ 569, 571, 569, 569, 568, 0, 556, 555, 565, 0,
+ 585, 462, 0, 562, 565, 0, 565, 564, 548, 540,
+ 548, 538, 546, 0, 543, 542, 563, 551, 549, 549,
+ 533, 536, 550, 534, 565, 545, 546, 543, 540, 550,
+ 527, 541, 540, 524, 523, 522, 543, 531, 529, 529,
+ 510, 509, 0, 537, 509, 535, 507, 511, 510, 541,
+
+ 521, 518, 0, 517, 520, 516, 518, 502, 499, 512,
+ 497, 498, 505, 499, 488, 487, 0, 493, 492, 523,
+ 503, 500, 0, 0, 0, 496, 0, 495, 0, 501,
+ 500, 484, 481, 482, 0, 474, 482, 472, 478, 499,
+ 478, 0, 0, 490, 489, 0, 0, 488, 487, 471,
+ 468, 469, 483, 482, 459, 458, 464, 0, 0, 485,
+ 457, 483, 475, 467, 453, 132, 161, 177, 215, 245,
+ 0, 0, 288, 289, 0, 0, 294, 315, 0, 316,
+ 306, 331, 0, 363, 402, 0, 0, 395, 383, 395,
+ 387, 433, 434, 0, 435, 420, 461, 427, 430, 431,
+
+ 0, 450, 452, 443, 0, 464, 0, 0, 0, 445,
+ 446, 440, 0, 441, 442, 0, 0, 0, 1017, 506,
+ 509, 512, 513, 514
+ } ;
+
+static yyconst flex_int16_t yy_def[825] =
+ { 0,
+ 819, 1, 819, 3, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 820, 819, 819,
+ 819, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 819, 819, 819, 819, 819, 819, 819, 821, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 822,
+ 819, 823, 20, 21, 819, 819, 824, 819, 819, 819,
+ 819, 819, 819, 819, 819, 820, 819, 819, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 819, 819, 821, 819, 819, 823, 819, 819, 819,
+ 819, 819, 824, 819, 819, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 819, 819,
+ 819, 819, 819, 819, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+
+ 820, 820, 820, 820, 820, 820, 820, 820, 820, 820,
+ 820, 820, 820, 820, 820, 820, 820, 820, 0, 819,
+ 819, 819, 819, 819
+ } ;
+
+static yyconst flex_int16_t yy_nxt[1090] =
+ { 0,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 21, 21, 21, 21,
+ 21, 22, 23, 24, 25, 26, 27, 28, 28, 28,
+ 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 29, 30, 31, 28, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 28, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 28, 28, 28, 52, 53,
+ 54, 55, 56, 57, 58, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
+ 56, 56, 56, 56, 56, 56, 56, 56, 56, 59,
+
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 56, 56, 56, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+ 56, 56, 56, 56, 61, 62, 63, 66, 68, 70,
+ 70, 70, 70, 70, 70, 70, 84, 85, 79, 150,
+ 123, 69, 67, 87, 124, 64, 72, 151, 73, 73,
+ 73, 73, 73, 73, 74, 80, 89, 81, 82, 784,
+ 92, 88, 93, 121, 95, 75, 94, 103, 96, 104,
+ 90, 91, 76, 77, 97, 99, 122, 98, 105, 100,
+
+ 115, 187, 75, 116, 101, 125, 117, 118, 152, 168,
+ 102, 119, 188, 169, 120, 785, 76, 128, 126, 77,
+ 72, 106, 74, 74, 74, 74, 74, 74, 74, 107,
+ 112, 108, 129, 207, 109, 130, 212, 132, 113, 75,
+ 110, 208, 213, 786, 133, 134, 76, 139, 135, 114,
+ 140, 236, 237, 153, 136, 137, 75, 138, 141, 147,
+ 143, 155, 156, 148, 144, 142, 158, 159, 145, 238,
+ 76, 146, 149, 160, 819, 267, 268, 239, 155, 156,
+ 161, 787, 161, 158, 159, 162, 162, 162, 162, 162,
+ 162, 162, 189, 227, 176, 254, 215, 160, 177, 178,
+
+ 819, 220, 229, 199, 788, 190, 200, 201, 228, 216,
+ 202, 217, 203, 240, 245, 230, 246, 221, 222, 254,
+ 249, 241, 249, 158, 159, 250, 250, 250, 250, 250,
+ 250, 250, 298, 299, 300, 789, 790, 251, 791, 251,
+ 158, 159, 252, 252, 252, 252, 252, 252, 252, 162,
+ 162, 162, 162, 162, 162, 162, 162, 162, 162, 162,
+ 162, 162, 162, 261, 312, 330, 792, 793, 313, 337,
+ 338, 339, 794, 331, 253, 795, 262, 250, 250, 250,
+ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
+ 250, 253, 252, 252, 252, 252, 252, 252, 252, 348,
+
+ 349, 350, 156, 252, 252, 252, 252, 252, 252, 252,
+ 360, 361, 362, 368, 369, 370, 372, 373, 374, 156,
+ 796, 159, 383, 384, 385, 421, 422, 423, 441, 442,
+ 443, 451, 452, 453, 454, 455, 456, 797, 159, 798,
+ 799, 444, 445, 457, 458, 459, 498, 499, 500, 524,
+ 525, 526, 800, 801, 546, 548, 563, 564, 565, 501,
+ 502, 636, 527, 528, 547, 549, 594, 595, 596, 566,
+ 567, 637, 568, 614, 615, 616, 666, 802, 803, 597,
+ 598, 638, 804, 667, 805, 668, 617, 618, 639, 686,
+ 640, 641, 806, 807, 808, 809, 687, 810, 688, 811,
+
+ 812, 813, 814, 815, 816, 817, 818, 86, 86, 86,
+ 154, 154, 154, 70, 157, 163, 163, 783, 782, 781,
+ 780, 779, 778, 777, 776, 775, 774, 773, 772, 771,
+ 770, 769, 768, 767, 766, 765, 764, 763, 762, 761,
+ 760, 759, 758, 757, 756, 755, 754, 753, 752, 751,
+ 750, 749, 748, 747, 746, 745, 744, 743, 742, 741,
+ 740, 739, 738, 737, 736, 735, 734, 733, 732, 731,
+ 730, 729, 728, 727, 726, 725, 724, 723, 722, 721,
+ 720, 719, 718, 717, 716, 715, 714, 713, 712, 711,
+ 710, 709, 708, 707, 706, 705, 704, 703, 702, 701,
+
+ 700, 699, 698, 697, 696, 695, 694, 693, 692, 691,
+ 690, 689, 685, 684, 683, 682, 681, 680, 679, 678,
+ 677, 676, 675, 674, 673, 672, 671, 670, 669, 665,
+ 664, 663, 662, 661, 660, 659, 658, 657, 656, 655,
+ 654, 653, 652, 651, 650, 649, 648, 647, 646, 645,
+ 644, 643, 642, 635, 634, 633, 632, 631, 630, 629,
+ 628, 627, 626, 625, 624, 623, 622, 621, 620, 619,
+ 613, 612, 611, 610, 609, 608, 607, 606, 605, 604,
+ 603, 602, 601, 600, 599, 593, 592, 591, 590, 589,
+ 588, 587, 586, 585, 584, 583, 582, 581, 580, 579,
+
+ 578, 577, 576, 575, 574, 573, 572, 571, 570, 569,
+ 562, 561, 560, 559, 558, 557, 556, 555, 554, 553,
+ 552, 551, 550, 545, 544, 543, 542, 541, 540, 539,
+ 538, 537, 536, 535, 534, 533, 532, 531, 530, 529,
+ 523, 522, 521, 520, 519, 518, 517, 516, 515, 514,
+ 513, 512, 511, 510, 509, 508, 507, 506, 505, 504,
+ 503, 497, 496, 495, 494, 493, 492, 491, 490, 489,
+ 488, 487, 486, 485, 484, 483, 482, 481, 480, 479,
+ 478, 477, 476, 475, 474, 473, 472, 471, 470, 469,
+ 468, 467, 466, 465, 464, 463, 462, 461, 460, 450,
+
+ 449, 448, 447, 446, 440, 439, 438, 437, 436, 435,
+ 434, 433, 432, 431, 430, 429, 428, 427, 426, 425,
+ 424, 420, 419, 418, 417, 416, 415, 414, 413, 412,
+ 411, 410, 409, 408, 407, 406, 405, 404, 403, 402,
+ 401, 400, 399, 398, 397, 396, 395, 394, 393, 392,
+ 391, 390, 389, 388, 387, 386, 382, 381, 380, 379,
+ 378, 377, 376, 375, 371, 367, 366, 365, 364, 363,
+ 359, 358, 357, 356, 355, 354, 353, 352, 351, 347,
+ 346, 345, 344, 343, 342, 341, 340, 336, 335, 334,
+ 333, 332, 329, 328, 327, 326, 325, 324, 323, 322,
+
+ 321, 320, 319, 318, 317, 316, 315, 314, 311, 310,
+ 309, 308, 307, 306, 305, 304, 303, 302, 301, 297,
+ 296, 295, 294, 293, 292, 291, 290, 289, 288, 287,
+ 286, 285, 284, 283, 282, 281, 280, 279, 278, 277,
+ 276, 275, 274, 273, 272, 271, 270, 269, 266, 265,
+ 264, 263, 260, 259, 258, 257, 256, 255, 248, 247,
+ 244, 243, 242, 235, 234, 233, 232, 231, 226, 225,
+ 224, 223, 219, 218, 214, 211, 210, 209, 206, 205,
+ 204, 198, 197, 196, 195, 194, 193, 192, 191, 186,
+ 185, 184, 183, 182, 181, 180, 179, 175, 174, 173,
+
+ 172, 171, 170, 167, 166, 165, 164, 131, 127, 111,
+ 83, 78, 71, 65, 60, 819, 5, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819
+ } ;
+
+static yyconst flex_int16_t yy_chk[1090] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 10, 10, 11, 15, 17, 18,
+ 18, 18, 18, 18, 18, 18, 26, 26, 24, 51,
+ 42, 17, 15, 31, 42, 11, 20, 51, 20, 20,
+ 20, 20, 20, 20, 20, 24, 32, 24, 24, 766,
+ 33, 31, 33, 41, 34, 20, 33, 36, 34, 36,
+ 32, 32, 20, 20, 34, 35, 41, 34, 36, 35,
+
+ 40, 107, 20, 40, 35, 43, 40, 40, 53, 91,
+ 35, 40, 107, 91, 40, 767, 20, 45, 43, 20,
+ 21, 37, 21, 21, 21, 21, 21, 21, 21, 37,
+ 39, 37, 45, 122, 37, 45, 126, 47, 39, 21,
+ 37, 122, 126, 768, 47, 47, 21, 48, 47, 39,
+ 48, 143, 143, 53, 47, 47, 21, 47, 48, 50,
+ 49, 70, 70, 50, 49, 48, 72, 72, 49, 144,
+ 21, 49, 50, 73, 73, 178, 178, 144, 70, 70,
+ 75, 769, 75, 72, 72, 75, 75, 75, 75, 75,
+ 75, 75, 108, 136, 98, 163, 128, 73, 98, 98,
+
+ 73, 131, 137, 118, 770, 108, 118, 118, 136, 128,
+ 118, 128, 118, 145, 149, 137, 149, 131, 131, 163,
+ 155, 145, 155, 157, 157, 155, 155, 155, 155, 155,
+ 155, 155, 209, 209, 209, 773, 774, 158, 777, 158,
+ 157, 157, 158, 158, 158, 158, 158, 158, 158, 161,
+ 161, 161, 161, 161, 161, 161, 162, 162, 162, 162,
+ 162, 162, 162, 173, 221, 238, 778, 780, 221, 244,
+ 244, 244, 781, 238, 162, 782, 173, 249, 249, 249,
+ 249, 249, 249, 249, 250, 250, 250, 250, 250, 250,
+ 250, 162, 251, 251, 251, 251, 251, 251, 251, 260,
+
+ 260, 260, 250, 252, 252, 252, 252, 252, 252, 252,
+ 272, 272, 272, 281, 281, 281, 285, 285, 285, 250,
+ 784, 252, 294, 294, 294, 335, 335, 335, 376, 376,
+ 376, 387, 387, 387, 388, 388, 388, 785, 252, 788,
+ 789, 376, 376, 389, 389, 389, 440, 440, 440, 483,
+ 483, 483, 790, 791, 503, 504, 520, 520, 520, 440,
+ 440, 603, 483, 483, 503, 504, 554, 554, 554, 520,
+ 520, 603, 520, 577, 577, 577, 631, 792, 793, 554,
+ 554, 604, 795, 631, 796, 631, 577, 577, 604, 652,
+ 604, 604, 797, 798, 799, 800, 652, 802, 652, 803,
+
+ 804, 806, 810, 811, 812, 814, 815, 820, 820, 820,
+ 821, 821, 821, 822, 823, 824, 824, 765, 764, 763,
+ 762, 761, 760, 757, 756, 755, 754, 753, 752, 751,
+ 750, 749, 748, 745, 744, 741, 740, 739, 738, 737,
+ 736, 734, 733, 732, 731, 730, 728, 726, 722, 721,
+ 720, 719, 718, 716, 715, 714, 713, 712, 711, 710,
+ 709, 708, 707, 706, 705, 704, 702, 701, 700, 699,
+ 698, 697, 696, 695, 694, 692, 691, 690, 689, 688,
+ 687, 686, 685, 684, 683, 682, 681, 680, 679, 678,
+ 677, 676, 675, 674, 673, 672, 671, 670, 669, 668,
+
+ 667, 666, 665, 663, 662, 661, 660, 659, 658, 657,
+ 655, 654, 651, 649, 648, 647, 645, 644, 643, 642,
+ 641, 640, 639, 638, 637, 636, 635, 634, 633, 630,
+ 629, 628, 627, 626, 625, 623, 622, 621, 620, 618,
+ 617, 616, 615, 614, 613, 612, 611, 610, 609, 608,
+ 607, 606, 605, 600, 598, 597, 596, 595, 594, 590,
+ 589, 588, 587, 586, 585, 584, 583, 582, 580, 579,
+ 575, 574, 572, 571, 569, 568, 567, 566, 565, 564,
+ 563, 559, 558, 557, 555, 553, 552, 551, 550, 549,
+ 548, 547, 546, 545, 544, 542, 541, 536, 535, 534,
+
+ 533, 531, 530, 528, 527, 526, 525, 524, 522, 521,
+ 519, 518, 517, 516, 515, 514, 513, 512, 510, 509,
+ 508, 507, 506, 502, 501, 500, 499, 498, 497, 494,
+ 493, 492, 491, 490, 489, 488, 487, 486, 485, 484,
+ 482, 481, 478, 473, 471, 470, 469, 467, 466, 463,
+ 462, 461, 460, 449, 448, 447, 445, 444, 443, 442,
+ 441, 438, 436, 435, 434, 432, 431, 430, 429, 427,
+ 425, 424, 419, 418, 416, 415, 414, 413, 412, 411,
+ 410, 409, 408, 407, 406, 404, 403, 402, 401, 400,
+ 399, 398, 396, 395, 394, 393, 392, 391, 390, 386,
+
+ 382, 381, 380, 377, 375, 365, 363, 359, 358, 357,
+ 356, 354, 353, 351, 346, 345, 344, 343, 342, 341,
+ 336, 334, 333, 332, 331, 330, 328, 327, 324, 323,
+ 322, 321, 320, 319, 318, 317, 316, 315, 314, 313,
+ 312, 311, 310, 309, 308, 307, 306, 305, 304, 303,
+ 302, 301, 300, 299, 298, 295, 293, 292, 291, 290,
+ 289, 288, 287, 286, 284, 280, 278, 277, 276, 275,
+ 271, 270, 269, 268, 267, 266, 265, 264, 263, 259,
+ 257, 256, 255, 248, 247, 246, 245, 243, 242, 241,
+ 240, 239, 237, 236, 235, 234, 233, 232, 231, 230,
+
+ 229, 228, 227, 226, 225, 224, 223, 222, 220, 219,
+ 218, 217, 216, 215, 214, 213, 212, 211, 210, 208,
+ 207, 206, 205, 204, 203, 202, 201, 200, 199, 198,
+ 197, 196, 195, 194, 193, 192, 190, 189, 188, 187,
+ 186, 185, 184, 183, 182, 181, 180, 179, 177, 176,
+ 175, 174, 172, 171, 170, 169, 168, 166, 151, 150,
+ 148, 147, 146, 142, 141, 140, 139, 138, 135, 134,
+ 133, 132, 130, 129, 127, 125, 124, 123, 121, 120,
+ 119, 117, 116, 114, 113, 112, 111, 110, 109, 106,
+ 105, 104, 103, 102, 101, 100, 99, 97, 96, 95,
+
+ 94, 93, 92, 90, 89, 85, 81, 46, 44, 38,
+ 25, 22, 19, 14, 9, 5, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819, 819,
+ 819, 819, 819, 819, 819, 819, 819, 819, 819
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[241] =
+ { 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
+ 0, };
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+/*
+//
+// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+This file contains the Lex specification for GLSL ES.
+Based on ANSI C grammar, Lex specification:
+http://www.lysator.liu.se/c/ANSI-C-grammar-l.html
+
+IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh,
+WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp).
+*/
+
+#include "compiler/translator/glslang.h"
+#include "compiler/translator/ParseContext.h"
+#include "compiler/preprocessor/Token.h"
+#include "compiler/translator/util.h"
+#include "compiler/translator/length_limits.h"
+
+using namespace sh;
+
+#include "glslang_tab.h"
+
+/* windows only pragma */
+#ifdef _MSC_VER
+#pragma warning(disable : 4102)
+#endif
+
+// Workaround for flex using the register keyword, deprecated in C++11.
+#ifdef __cplusplus
+#if __cplusplus > 199711L
+#define register
+#endif
+#endif
+
+#define YY_USER_ACTION \
+ yylloc->first_file = yylloc->last_file = yycolumn; \
+ yylloc->first_line = yylloc->last_line = yylineno;
+
+#define YY_INPUT(buf, result, max_size) \
+ result = string_input(buf, max_size, yyscanner);
+
+static yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner);
+static int check_type(yyscan_t yyscanner);
+static int reserved_word(yyscan_t yyscanner);
+static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
+static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
+static int ES2_ident_ES3_keyword(TParseContext *context, int token);
+static int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
+static int uint_constant(TParseContext *context);
+static int int_constant(TParseContext *context);
+static int float_constant(yyscan_t yyscanner);
+static int floatsuffix_check(TParseContext* context);
+
+#define INITIAL 0
+#define FIELDS 1
+
+#define YY_EXTRA_TYPE TParseContext*
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ yy_size_t yy_n_chars;
+ yy_size_t yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ YYSTYPE * yylval_r;
+
+ YYLTYPE * yylloc_r;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+ /* This must go here because YYSTYPE and YYLTYPE are included
+ * from bison output in section 1.*/
+ # define yylval yyg->yylval_r
+
+ # define yylloc yyg->yylloc_r
+
+int yylex_init (yyscan_t* scanner);
+
+int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (yyscan_t yyscanner );
+
+int yyget_debug (yyscan_t yyscanner );
+
+void yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *yyget_in (yyscan_t yyscanner );
+
+void yyset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *yyget_out (yyscan_t yyscanner );
+
+void yyset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t yyget_leng (yyscan_t yyscanner );
+
+char *yyget_text (yyscan_t yyscanner );
+
+int yyget_lineno (yyscan_t yyscanner );
+
+void yyset_lineno (int line_number ,yyscan_t yyscanner );
+
+int yyget_column (yyscan_t yyscanner );
+
+void yyset_column (int column_no ,yyscan_t yyscanner );
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner );
+
+void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
+
+ YYLTYPE *yyget_lloc (yyscan_t yyscanner );
+
+ void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (yyscan_t yyscanner );
+#else
+extern int yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex \
+ (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
+
+#define YY_DECL int yylex \
+ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yylval = yylval_param;
+
+ yylloc = yylloc_param;
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_load_buffer_state(yyscanner );
+ }
+
+ {
+
+ TParseContext* context = yyextra;
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 820 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 819 );
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+ if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+ {
+ yy_size_t yyl;
+ for ( yyl = 0; yyl < static_cast<yy_size_t>(yyleng); ++yyl )
+ if ( yytext[yyl] == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+ }
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+{ return INVARIANT; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+{ return HIGH_PRECISION; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+{ return MEDIUM_PRECISION; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+{ return LOW_PRECISION; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+{ return PRECISION; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+{ return ES2_keyword_ES3_reserved(context, ATTRIBUTE); }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+{ return CONST_QUAL; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+{ return UNIFORM; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+{ return ES2_keyword_ES3_reserved(context, VARYING); }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+{ return BREAK; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+{ return CONTINUE; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+{ return DO; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+{ return FOR; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+{ return WHILE; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+{ return IF; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+{ return ELSE; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, SWITCH); }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, CASE); }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, DEFAULT); }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, CENTROID); }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, FLAT); }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, SMOOTH); }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+{ return IN_QUAL; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+{ return OUT_QUAL; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+{ return INOUT_QUAL; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+{ return FLOAT_TYPE; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+{ return INT_TYPE; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, UINT_TYPE); }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+{ return VOID_TYPE; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+{ return BOOL_TYPE; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+{ yylval->lex.b = true; return BOOLCONSTANT; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+{ yylval->lex.b = false; return BOOLCONSTANT; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+{ return DISCARD; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+{ return RETURN; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+{ return MATRIX2; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+{ return MATRIX3; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+{ return MATRIX4; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX2); }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX3); }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX4); }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX2x3); }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX3x2); }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX2x4); }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX4x2); }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX3x4); }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, MATRIX4x3); }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+{ return VEC2; }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+{ return VEC3; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+{ return VEC4; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+{ return IVEC2; }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+{ return IVEC3; }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+{ return IVEC4; }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+{ return BVEC2; }
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+{ return BVEC3; }
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+{ return BVEC4; }
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, UVEC2); }
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, UVEC3); }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, UVEC4); }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+{ return SAMPLER2D; }
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+{ return SAMPLERCUBE; }
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+{ return SAMPLER_EXTERNAL_OES; }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, SAMPLER3D); }
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, SAMPLER3DRECT); }
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+{ return SAMPLER2DRECT; }
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, SAMPLER2DARRAY); }
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, ISAMPLER2D); }
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, ISAMPLER3D); }
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, ISAMPLERCUBE); }
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, ISAMPLER2DARRAY); }
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, USAMPLER2D); }
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, USAMPLER3D); }
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, USAMPLERCUBE); }
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, USAMPLER2DARRAY); }
+ YY_BREAK
+case 74:
+YY_RULE_SETUP
+{ return ES2_reserved_ES3_keyword(context, SAMPLER2DSHADOW); }
+ YY_BREAK
+case 75:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, SAMPLERCUBESHADOW); }
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, SAMPLER2DARRAYSHADOW); }
+ YY_BREAK
+case 77:
+YY_RULE_SETUP
+{ return STRUCT; }
+ YY_BREAK
+case 78:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_keyword(context, LAYOUT); }
+ YY_BREAK
+case 79:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2D); }
+ YY_BREAK
+case 80:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2D); }
+ YY_BREAK
+case 81:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2D); }
+ YY_BREAK
+case 82:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE2DARRAY); }
+ YY_BREAK
+case 83:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE2DARRAY); }
+ YY_BREAK
+case 84:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE2DARRAY); }
+ YY_BREAK
+case 85:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGE3D); }
+ YY_BREAK
+case 86:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGE3D); }
+ YY_BREAK
+case 87:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGE3D); }
+ YY_BREAK
+case 88:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IIMAGECUBE); }
+ YY_BREAK
+case 89:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, UIMAGECUBE); }
+ YY_BREAK
+case 90:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, IMAGECUBE); }
+ YY_BREAK
+case 91:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, READONLY); }
+ YY_BREAK
+case 92:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, WRITEONLY); }
+ YY_BREAK
+case 93:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, COHERENT); }
+ YY_BREAK
+case 94:
+YY_RULE_SETUP
+{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, RESTRICT); }
+ YY_BREAK
+case 95:
+YY_RULE_SETUP
+{ return ES2_and_ES3_reserved_ES3_1_keyword(context, VOLATILE); }
+ YY_BREAK
+/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
+case 96:
+case 97:
+case 98:
+case 99:
+case 100:
+case 101:
+case 102:
+case 103:
+case 104:
+case 105:
+case 106:
+case 107:
+case 108:
+case 109:
+case 110:
+case 111:
+case 112:
+case 113:
+case 114:
+case 115:
+case 116:
+case 117:
+case 118:
+case 119:
+case 120:
+case 121:
+case 122:
+case 123:
+case 124:
+case 125:
+case 126:
+case 127:
+case 128:
+case 129:
+case 130:
+case 131:
+case 132:
+case 133:
+case 134:
+case 135:
+YY_RULE_SETUP
+{
+ if (context->getShaderVersion() < 300) {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+ return reserved_word(yyscanner);
+}
+ YY_BREAK
+/* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */
+case 136:
+YY_RULE_SETUP
+{
+ if (context->getShaderVersion() >= 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return reserved_word(yyscanner);
+}
+ YY_BREAK
+/* Reserved keywords */
+case 137:
+case 138:
+case 139:
+case 140:
+case 141:
+case 142:
+case 143:
+case 144:
+case 145:
+case 146:
+case 147:
+case 148:
+case 149:
+case 150:
+case 151:
+case 152:
+case 153:
+case 154:
+case 155:
+case 156:
+case 157:
+case 158:
+case 159:
+case 160:
+case 161:
+case 162:
+case 163:
+case 164:
+case 165:
+case 166:
+case 167:
+case 168:
+case 169:
+case 170:
+case 171:
+case 172:
+case 173:
+case 174:
+case 175:
+case 176:
+YY_RULE_SETUP
+{ return reserved_word(yyscanner); }
+ YY_BREAK
+case 177:
+YY_RULE_SETUP
+{
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+}
+ YY_BREAK
+case 178:
+YY_RULE_SETUP
+{ return int_constant(context); }
+ YY_BREAK
+case 179:
+YY_RULE_SETUP
+{ return int_constant(context); }
+ YY_BREAK
+case 180:
+YY_RULE_SETUP
+{ return int_constant(context); }
+ YY_BREAK
+case 181:
+YY_RULE_SETUP
+{ return uint_constant(context); }
+ YY_BREAK
+case 182:
+YY_RULE_SETUP
+{ return uint_constant(context); }
+ YY_BREAK
+case 183:
+YY_RULE_SETUP
+{ return uint_constant(context); }
+ YY_BREAK
+case 184:
+YY_RULE_SETUP
+{ return float_constant(yyscanner); }
+ YY_BREAK
+case 185:
+YY_RULE_SETUP
+{ return float_constant(yyscanner); }
+ YY_BREAK
+case 186:
+YY_RULE_SETUP
+{ return float_constant(yyscanner); }
+ YY_BREAK
+case 187:
+YY_RULE_SETUP
+{ return floatsuffix_check(context); }
+ YY_BREAK
+case 188:
+YY_RULE_SETUP
+{ return floatsuffix_check(context); }
+ YY_BREAK
+case 189:
+YY_RULE_SETUP
+{ return floatsuffix_check(context); }
+ YY_BREAK
+case 190:
+YY_RULE_SETUP
+{ return ADD_ASSIGN; }
+ YY_BREAK
+case 191:
+YY_RULE_SETUP
+{ return SUB_ASSIGN; }
+ YY_BREAK
+case 192:
+YY_RULE_SETUP
+{ return MUL_ASSIGN; }
+ YY_BREAK
+case 193:
+YY_RULE_SETUP
+{ return DIV_ASSIGN; }
+ YY_BREAK
+case 194:
+YY_RULE_SETUP
+{ return MOD_ASSIGN; }
+ YY_BREAK
+case 195:
+YY_RULE_SETUP
+{ return LEFT_ASSIGN; }
+ YY_BREAK
+case 196:
+YY_RULE_SETUP
+{ return RIGHT_ASSIGN; }
+ YY_BREAK
+case 197:
+YY_RULE_SETUP
+{ return AND_ASSIGN; }
+ YY_BREAK
+case 198:
+YY_RULE_SETUP
+{ return XOR_ASSIGN; }
+ YY_BREAK
+case 199:
+YY_RULE_SETUP
+{ return OR_ASSIGN; }
+ YY_BREAK
+case 200:
+YY_RULE_SETUP
+{ return INC_OP; }
+ YY_BREAK
+case 201:
+YY_RULE_SETUP
+{ return DEC_OP; }
+ YY_BREAK
+case 202:
+YY_RULE_SETUP
+{ return AND_OP; }
+ YY_BREAK
+case 203:
+YY_RULE_SETUP
+{ return OR_OP; }
+ YY_BREAK
+case 204:
+YY_RULE_SETUP
+{ return XOR_OP; }
+ YY_BREAK
+case 205:
+YY_RULE_SETUP
+{ return LE_OP; }
+ YY_BREAK
+case 206:
+YY_RULE_SETUP
+{ return GE_OP; }
+ YY_BREAK
+case 207:
+YY_RULE_SETUP
+{ return EQ_OP; }
+ YY_BREAK
+case 208:
+YY_RULE_SETUP
+{ return NE_OP; }
+ YY_BREAK
+case 209:
+YY_RULE_SETUP
+{ return LEFT_OP; }
+ YY_BREAK
+case 210:
+YY_RULE_SETUP
+{ return RIGHT_OP; }
+ YY_BREAK
+case 211:
+YY_RULE_SETUP
+{ return SEMICOLON; }
+ YY_BREAK
+case 212:
+YY_RULE_SETUP
+{ return LEFT_BRACE; }
+ YY_BREAK
+case 213:
+YY_RULE_SETUP
+{ return RIGHT_BRACE; }
+ YY_BREAK
+case 214:
+YY_RULE_SETUP
+{ return COMMA; }
+ YY_BREAK
+case 215:
+YY_RULE_SETUP
+{ return COLON; }
+ YY_BREAK
+case 216:
+YY_RULE_SETUP
+{ return EQUAL; }
+ YY_BREAK
+case 217:
+YY_RULE_SETUP
+{ return LEFT_PAREN; }
+ YY_BREAK
+case 218:
+YY_RULE_SETUP
+{ return RIGHT_PAREN; }
+ YY_BREAK
+case 219:
+YY_RULE_SETUP
+{ return LEFT_BRACKET; }
+ YY_BREAK
+case 220:
+YY_RULE_SETUP
+{ return RIGHT_BRACKET; }
+ YY_BREAK
+case 221:
+YY_RULE_SETUP
+{ BEGIN(FIELDS); return DOT; }
+ YY_BREAK
+case 222:
+YY_RULE_SETUP
+{ return BANG; }
+ YY_BREAK
+case 223:
+YY_RULE_SETUP
+{ return DASH; }
+ YY_BREAK
+case 224:
+YY_RULE_SETUP
+{ return TILDE; }
+ YY_BREAK
+case 225:
+YY_RULE_SETUP
+{ return PLUS; }
+ YY_BREAK
+case 226:
+YY_RULE_SETUP
+{ return STAR; }
+ YY_BREAK
+case 227:
+YY_RULE_SETUP
+{ return SLASH; }
+ YY_BREAK
+case 228:
+YY_RULE_SETUP
+{ return PERCENT; }
+ YY_BREAK
+case 229:
+YY_RULE_SETUP
+{ return LEFT_ANGLE; }
+ YY_BREAK
+case 230:
+YY_RULE_SETUP
+{ return RIGHT_ANGLE; }
+ YY_BREAK
+case 231:
+YY_RULE_SETUP
+{ return VERTICAL_BAR; }
+ YY_BREAK
+case 232:
+YY_RULE_SETUP
+{ return CARET; }
+ YY_BREAK
+case 233:
+YY_RULE_SETUP
+{ return AMPERSAND; }
+ YY_BREAK
+case 234:
+YY_RULE_SETUP
+{ return QUESTION; }
+ YY_BREAK
+case 235:
+YY_RULE_SETUP
+{
+ BEGIN(INITIAL);
+ yylval->lex.string = NewPoolTString(yytext);
+ return FIELD_SELECTION;
+}
+ YY_BREAK
+case 236:
+YY_RULE_SETUP
+{}
+ YY_BREAK
+case 237:
+YY_RULE_SETUP
+{
+ yyextra->error(*yylloc, "Illegal character at fieldname start", yytext, "");
+ return 0;
+}
+ YY_BREAK
+case 238:
+/* rule 238 can match eol */
+YY_RULE_SETUP
+{ }
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(FIELDS):
+{ yyterminate(); }
+ YY_BREAK
+case 239:
+YY_RULE_SETUP
+{ assert(false); return 0; }
+ YY_BREAK
+case 240:
+YY_RULE_SETUP
+ECHO;
+ YY_BREAK
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ yy_size_t new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ static_cast<int>(number_to_move) - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ size_t result = 0;
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ result, num_to_read );
+ yyg->yy_n_chars = static_cast<int>(result);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 820 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 820 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 819);
+
+ (void)yyg;
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ if ( c == '\n' )
+
+ do{ yylineno++;
+ yycolumn=0;
+ }while(0)
+;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void yy_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf ,yyscanner );
+
+ yyfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack(yyscanner);
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void yypop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (yyscan_t yyscanner)
+{
+ yy_size_t num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = static_cast<int>(yyg->yy_buffer_stack_max + grow_size);
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = static_cast<int>(size) - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ yy_size_t i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < static_cast<yy_size_t>(_yybytes_len); ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int yyget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *yyget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+yy_size_t yyget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *yyget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_lineno called with no buffer" );
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void yyset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ YY_FATAL_ERROR( "yyset_column called with no buffer" );
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int yyget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+YYSTYPE * yyget_lval (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylval;
+}
+
+void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylval = yylval_param;
+}
+
+YYLTYPE *yyget_lloc (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yylloc;
+}
+
+void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yylloc = yylloc_param;
+}
+
+/* User-visible API */
+
+/* yylex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int yylex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* yylex_init_extra has the same functionality as yylex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to yyalloc in
+ * the yyextra field.
+ */
+
+int yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ yyset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ yyset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ yyfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ yyfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ yyfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) {
+ pp::Token token;
+ yyget_extra(yyscanner)->getPreprocessor().lex(&token);
+ yy_size_t len = token.type == pp::Token::LAST ? 0 : token.text.size();
+ if (len < max_size)
+ memcpy(buf, token.text.c_str(), len);
+ yyset_column(token.location.file,yyscanner);
+ yyset_lineno(token.location.line,yyscanner);
+
+ if (len >= max_size)
+ YY_FATAL_ERROR("Input buffer overflow");
+ else if (len > 0)
+ buf[len++] = ' ';
+ return len;
+}
+
+int check_type(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ int token = IDENTIFIER;
+ TSymbol* symbol = yyextra->symbolTable.find(yytext, yyextra->getShaderVersion());
+ if (symbol && symbol->isVariable()) {
+ TVariable* variable = static_cast<TVariable*>(symbol);
+ if (variable->isUserType()) {
+ token = TYPE_NAME;
+ }
+ }
+ yylval->lex.symbol = symbol;
+ return token;
+}
+
+int reserved_word(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ yyextra->error(*yylloc, "Illegal use of reserved word", yytext, "");
+ return 0;
+}
+
+int ES2_reserved_ES3_keyword(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_keyword_ES3_reserved(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() >= 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_ident_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+ else if (context->getShaderVersion() == 300)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_ident_ES3_keyword(TParseContext *context, int token)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
+ if (context->getShaderVersion() < 300)
+ {
+ yylval->lex.string = NewPoolTString(yytext);
+ return check_type(yyscanner);
+ }
+
+ return token;
+}
+
+int ES2_and_ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
+{
+ yyscan_t yyscanner = (yyscan_t) context->getScanner();
+
+ if (context->getShaderVersion() < 310)
+ {
+ return reserved_word(yyscanner);
+ }
+
+ return token;
+}
+
+int uint_constant(TParseContext *context)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext, "");
+ return 0;
+ }
+
+ if (!atoi_clamp(yytext, &(yylval->lex.u)))
+ yyextra->error(*yylloc, "Integer overflow", yytext, "");
+
+ return UINTCONSTANT;
+}
+
+int floatsuffix_check(TParseContext* context)
+{
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ if (context->getShaderVersion() < 300)
+ {
+ context->error(*yylloc, "Floating-point suffix unsupported prior to GLSL ES 3.00", yytext);
+ return 0;
+ }
+
+ std::string text = yytext;
+ text.resize(text.size() - 1);
+ if (!strtof_clamp(text, &(yylval->lex.f)))
+ yyextra->warning(*yylloc, "Float overflow", yytext, "");
+
+ return(FLOATCONSTANT);
+}
+
+void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* reason) {
+ context->error(*lloc, reason, yyget_text(scanner));
+}
+
+int int_constant(TParseContext *context) {
+ struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
+
+ unsigned int u;
+ if (!atoi_clamp(yytext, &u))
+ {
+ if (context->getShaderVersion() >= 300)
+ yyextra->error(*yylloc, "Integer overflow", yytext, "");
+ else
+ yyextra->warning(*yylloc, "Integer overflow", yytext, "");
+ }
+ yylval->lex.i = static_cast<int>(u);
+ return INTCONSTANT;
+}
+
+int float_constant(yyscan_t yyscanner) {
+ struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
+
+ if (!strtof_clamp(yytext, &(yylval->lex.f)))
+ yyextra->warning(*yylloc, "Float overflow", yytext, "");
+ return FLOATCONSTANT;
+}
+
+int glslang_initialize(TParseContext* context) {
+ yyscan_t scanner = NULL;
+ if (yylex_init_extra(context,&scanner))
+ return 1;
+
+ context->setScanner(scanner);
+ return 0;
+}
+
+int glslang_finalize(TParseContext* context) {
+ yyscan_t scanner = context->getScanner();
+ if (scanner == NULL) return 0;
+
+ context->setScanner(NULL);
+ yylex_destroy(scanner);
+
+ return 0;
+}
+
+int glslang_scan(size_t count, const char* const string[], const int length[],
+ TParseContext* context) {
+ yyrestart(NULL,context->getScanner());
+ yyset_column(0,context->getScanner());
+ yyset_lineno(1,context->getScanner());
+
+ // Initialize preprocessor.
+ pp::Preprocessor *preprocessor = &context->getPreprocessor();
+
+ if (!preprocessor->init(count, string, length))
+ return 1;
+
+ // Define extension macros.
+ const TExtensionBehavior& extBehavior = context->extensionBehavior();
+ for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
+ iter != extBehavior.end(); ++iter) {
+ preprocessor->predefineMacro(iter->first.c_str(), 1);
+ }
+ if (context->getFragmentPrecisionHigh())
+ preprocessor->predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1);
+
+ preprocessor->setMaxTokenSize(sh::GetGlobalMaxTokenSize(context->getShaderSpec()));
+
+ return 0;
+}
+
diff --git a/gfx/angle/src/compiler/translator/glslang_tab.cpp b/gfx/angle/src/compiler/translator/glslang_tab.cpp
new file mode 100755
index 000000000..63a3371dc
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang_tab.cpp
@@ -0,0 +1,5153 @@
+/* A Bison parser, made by GNU Bison 3.0.4. */
+
+/* Bison implementation for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "3.0.4"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 2
+
+/* Push parsers. */
+#define YYPUSH 0
+
+/* Pull parsers. */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations. */
+
+
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// This file is auto-generated by generate_parser.sh. DO NOT EDIT!
+
+// clang-format off
+
+// Ignore errors in auto-generated code.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#pragma GCC diagnostic ignored "-Wswitch-enum"
+#elif defined(_MSC_VER)
+#pragma warning(disable: 4065)
+#pragma warning(disable: 4189)
+#pragma warning(disable: 4244)
+#pragma warning(disable: 4505)
+#pragma warning(disable: 4701)
+#pragma warning(disable: 4702)
+#endif
+
+#include "angle_gl.h"
+#include "compiler/translator/Cache.h"
+#include "compiler/translator/SymbolTable.h"
+#include "compiler/translator/ParseContext.h"
+#include "GLSLANG/ShaderLang.h"
+
+#define YYENABLE_NLS 0
+
+using namespace sh;
+
+
+
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus && 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+ by #include "glslang_tab.h". */
+#ifndef YY_YY_GLSLANG_TAB_H_INCLUDED
+# define YY_YY_GLSLANG_TAB_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+/* "%code requires" blocks. */
+
+
+#define YYLTYPE TSourceLoc
+#define YYLTYPE_IS_DECLARED 1
+
+
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ enum yytokentype
+ {
+ INVARIANT = 258,
+ HIGH_PRECISION = 259,
+ MEDIUM_PRECISION = 260,
+ LOW_PRECISION = 261,
+ PRECISION = 262,
+ ATTRIBUTE = 263,
+ CONST_QUAL = 264,
+ BOOL_TYPE = 265,
+ FLOAT_TYPE = 266,
+ INT_TYPE = 267,
+ UINT_TYPE = 268,
+ BREAK = 269,
+ CONTINUE = 270,
+ DO = 271,
+ ELSE = 272,
+ FOR = 273,
+ IF = 274,
+ DISCARD = 275,
+ RETURN = 276,
+ SWITCH = 277,
+ CASE = 278,
+ DEFAULT = 279,
+ BVEC2 = 280,
+ BVEC3 = 281,
+ BVEC4 = 282,
+ IVEC2 = 283,
+ IVEC3 = 284,
+ IVEC4 = 285,
+ VEC2 = 286,
+ VEC3 = 287,
+ VEC4 = 288,
+ UVEC2 = 289,
+ UVEC3 = 290,
+ UVEC4 = 291,
+ MATRIX2 = 292,
+ MATRIX3 = 293,
+ MATRIX4 = 294,
+ IN_QUAL = 295,
+ OUT_QUAL = 296,
+ INOUT_QUAL = 297,
+ UNIFORM = 298,
+ VARYING = 299,
+ MATRIX2x3 = 300,
+ MATRIX3x2 = 301,
+ MATRIX2x4 = 302,
+ MATRIX4x2 = 303,
+ MATRIX3x4 = 304,
+ MATRIX4x3 = 305,
+ CENTROID = 306,
+ FLAT = 307,
+ SMOOTH = 308,
+ READONLY = 309,
+ WRITEONLY = 310,
+ COHERENT = 311,
+ RESTRICT = 312,
+ VOLATILE = 313,
+ STRUCT = 314,
+ VOID_TYPE = 315,
+ WHILE = 316,
+ SAMPLER2D = 317,
+ SAMPLERCUBE = 318,
+ SAMPLER_EXTERNAL_OES = 319,
+ SAMPLER2DRECT = 320,
+ SAMPLER2DARRAY = 321,
+ ISAMPLER2D = 322,
+ ISAMPLER3D = 323,
+ ISAMPLERCUBE = 324,
+ ISAMPLER2DARRAY = 325,
+ USAMPLER2D = 326,
+ USAMPLER3D = 327,
+ USAMPLERCUBE = 328,
+ USAMPLER2DARRAY = 329,
+ SAMPLER3D = 330,
+ SAMPLER3DRECT = 331,
+ SAMPLER2DSHADOW = 332,
+ SAMPLERCUBESHADOW = 333,
+ SAMPLER2DARRAYSHADOW = 334,
+ IMAGE2D = 335,
+ IIMAGE2D = 336,
+ UIMAGE2D = 337,
+ IMAGE3D = 338,
+ IIMAGE3D = 339,
+ UIMAGE3D = 340,
+ IMAGE2DARRAY = 341,
+ IIMAGE2DARRAY = 342,
+ UIMAGE2DARRAY = 343,
+ IMAGECUBE = 344,
+ IIMAGECUBE = 345,
+ UIMAGECUBE = 346,
+ LAYOUT = 347,
+ IDENTIFIER = 348,
+ TYPE_NAME = 349,
+ FLOATCONSTANT = 350,
+ INTCONSTANT = 351,
+ UINTCONSTANT = 352,
+ BOOLCONSTANT = 353,
+ FIELD_SELECTION = 354,
+ LEFT_OP = 355,
+ RIGHT_OP = 356,
+ INC_OP = 357,
+ DEC_OP = 358,
+ LE_OP = 359,
+ GE_OP = 360,
+ EQ_OP = 361,
+ NE_OP = 362,
+ AND_OP = 363,
+ OR_OP = 364,
+ XOR_OP = 365,
+ MUL_ASSIGN = 366,
+ DIV_ASSIGN = 367,
+ ADD_ASSIGN = 368,
+ MOD_ASSIGN = 369,
+ LEFT_ASSIGN = 370,
+ RIGHT_ASSIGN = 371,
+ AND_ASSIGN = 372,
+ XOR_ASSIGN = 373,
+ OR_ASSIGN = 374,
+ SUB_ASSIGN = 375,
+ LEFT_PAREN = 376,
+ RIGHT_PAREN = 377,
+ LEFT_BRACKET = 378,
+ RIGHT_BRACKET = 379,
+ LEFT_BRACE = 380,
+ RIGHT_BRACE = 381,
+ DOT = 382,
+ COMMA = 383,
+ COLON = 384,
+ EQUAL = 385,
+ SEMICOLON = 386,
+ BANG = 387,
+ DASH = 388,
+ TILDE = 389,
+ PLUS = 390,
+ STAR = 391,
+ SLASH = 392,
+ PERCENT = 393,
+ LEFT_ANGLE = 394,
+ RIGHT_ANGLE = 395,
+ VERTICAL_BAR = 396,
+ CARET = 397,
+ AMPERSAND = 398,
+ QUESTION = 399
+ };
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+union YYSTYPE
+{
+
+
+ struct {
+ union {
+ TString *string;
+ float f;
+ int i;
+ unsigned int u;
+ bool b;
+ };
+ TSymbol* symbol;
+ } lex;
+ struct {
+ TOperator op;
+ union {
+ TIntermNode *intermNode;
+ TIntermNodePair nodePair;
+ TIntermTyped *intermTypedNode;
+ TIntermAggregate *intermAggregate;
+ TIntermBlock *intermBlock;
+ TIntermDeclaration *intermDeclaration;
+ TIntermSwitch *intermSwitch;
+ TIntermCase *intermCase;
+ };
+ union {
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ TPublicType type;
+ TPrecision precision;
+ TLayoutQualifier layoutQualifier;
+ TQualifier qualifier;
+ TFunction *function;
+ TParameter param;
+ TField *field;
+ TFieldList *fieldList;
+ TQualifierWrapperBase *qualifierWrapper;
+ TTypeQualifierBuilder *typeQualifierBuilder;
+ };
+ } interm;
+
+
+};
+
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+/* Location type. */
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE YYLTYPE;
+struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+};
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+int yyparse (TParseContext* context, void *scanner);
+
+#endif /* !YY_YY_GLSLANG_TAB_H_INCLUDED */
+
+/* Copy the second part of user declarations. */
+
+
+extern int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, void* yyscanner);
+extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, const char* reason);
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do { \
+ if (N) { \
+ (Current).first_file = YYRHSLOC(Rhs, 1).first_file; \
+ (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \
+ (Current).last_file = YYRHSLOC(Rhs, N).last_file; \
+ (Current).last_line = YYRHSLOC(Rhs, N).last_line; \
+ } \
+ else { \
+ (Current).first_file = YYRHSLOC(Rhs, 0).last_file; \
+ (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \
+ (Current).last_file = YYRHSLOC(Rhs, 0).last_file; \
+ (Current).last_line = YYRHSLOC(Rhs, 0).last_line; \
+ } \
+ } while (0)
+
+#define VERTEX_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER) { \
+ context->error(L, " supported in vertex shaders only ", S); \
+ } \
+}
+
+#define FRAG_ONLY(S, L) { \
+ if (context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in fragment shaders only ", S); \
+ } \
+}
+
+#define COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_COMPUTE_SHADER) { \
+ context->error(L, " supported in compute shaders only ", S); \
+ } \
+}
+
+#define NON_COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in vertex and fragment shaders only ", S); \
+ } \
+}
+
+#define ES2_ONLY(S, L) { \
+ if (context->getShaderVersion() != 100) { \
+ context->error(L, " supported in GLSL ES 1.00 only ", S); \
+ } \
+}
+
+#define ES3_OR_NEWER(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() < 300) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.00 and above only ", TOKEN); \
+ } \
+}
+
+#define ES3_1_ONLY(TOKEN, LINE, REASON) { \
+ if (context->getShaderVersion() != 310) { \
+ context->error(LINE, REASON " supported in GLSL ES 3.10 only ", TOKEN); \
+ } \
+}
+
+
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__ \
+ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \
+ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+# define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+# define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+# define _Noreturn __declspec (noreturn)
+# else
+# define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+ /* Use EXIT_SUCCESS as a witness for stdlib.h. */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's 'empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined EXIT_SUCCESS \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+ && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss_alloc;
+ YYSTYPE yyvs_alloc;
+ YYLTYPE yyls_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ + 2 * YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \
+ Stack = &yyptr->Stack_alloc; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(Dst, Src, Count) \
+ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+# else
+# define YYCOPY(Dst, Src, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (Dst)[yyi] = (Src)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 126
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 2825
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 145
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 94
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 286
+/* YYNSTATES -- Number of states. */
+#define YYNSTATES 422
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+ by yylex, with out-of-bounds checking. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 399
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+ as returned by yylex, without out-of-bounds checking. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
+ 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144
+};
+
+#if YYDEBUG
+ /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 243, 243, 244, 247, 257, 260, 265, 270, 275,
+ 280, 286, 289, 292, 295, 298, 301, 307, 314, 325,
+ 329, 337, 340, 346, 350, 357, 363, 372, 380, 386,
+ 392, 401, 404, 407, 410, 420, 421, 422, 423, 431,
+ 432, 435, 438, 445, 446, 449, 455, 456, 460, 467,
+ 468, 471, 474, 477, 483, 484, 487, 493, 494, 501,
+ 502, 509, 510, 517, 518, 524, 525, 531, 532, 538,
+ 539, 545, 546, 553, 554, 555, 556, 560, 561, 562,
+ 566, 570, 574, 578, 585, 588, 594, 601, 608, 611,
+ 614, 623, 627, 631, 635, 639, 646, 653, 656, 663,
+ 671, 691, 701, 709, 734, 738, 742, 746, 753, 760,
+ 763, 767, 771, 776, 781, 788, 792, 796, 800, 805,
+ 810, 817, 821, 827, 830, 836, 840, 847, 853, 857,
+ 861, 864, 867, 876, 882, 890, 893, 913, 932, 939,
+ 943, 947, 950, 953, 956, 959, 965, 972, 975, 978,
+ 984, 991, 994, 1000, 1003, 1006, 1012, 1015, 1020, 1031,
+ 1034, 1037, 1040, 1043, 1046, 1050, 1054, 1058, 1062, 1066,
+ 1070, 1074, 1078, 1082, 1086, 1090, 1094, 1098, 1102, 1106,
+ 1110, 1114, 1118, 1122, 1126, 1130, 1133, 1136, 1139, 1142,
+ 1145, 1148, 1151, 1154, 1157, 1160, 1163, 1166, 1169, 1172,
+ 1175, 1182, 1188, 1191, 1194, 1197, 1200, 1203, 1206, 1209,
+ 1212, 1215, 1218, 1221, 1224, 1227, 1239, 1239, 1242, 1242,
+ 1248, 1251, 1266, 1269, 1276, 1280, 1286, 1292, 1304, 1308,
+ 1312, 1313, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1329,
+ 1330, 1330, 1330, 1339, 1340, 1344, 1344, 1345, 1345, 1350,
+ 1353, 1362, 1367, 1374, 1375, 1379, 1386, 1390, 1397, 1397,
+ 1404, 1407, 1414, 1418, 1431, 1431, 1436, 1436, 1442, 1442,
+ 1450, 1453, 1459, 1462, 1468, 1472, 1479, 1482, 1485, 1488,
+ 1491, 1500, 1506, 1512, 1515, 1521, 1521
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "INVARIANT", "HIGH_PRECISION",
+ "MEDIUM_PRECISION", "LOW_PRECISION", "PRECISION", "ATTRIBUTE",
+ "CONST_QUAL", "BOOL_TYPE", "FLOAT_TYPE", "INT_TYPE", "UINT_TYPE",
+ "BREAK", "CONTINUE", "DO", "ELSE", "FOR", "IF", "DISCARD", "RETURN",
+ "SWITCH", "CASE", "DEFAULT", "BVEC2", "BVEC3", "BVEC4", "IVEC2", "IVEC3",
+ "IVEC4", "VEC2", "VEC3", "VEC4", "UVEC2", "UVEC3", "UVEC4", "MATRIX2",
+ "MATRIX3", "MATRIX4", "IN_QUAL", "OUT_QUAL", "INOUT_QUAL", "UNIFORM",
+ "VARYING", "MATRIX2x3", "MATRIX3x2", "MATRIX2x4", "MATRIX4x2",
+ "MATRIX3x4", "MATRIX4x3", "CENTROID", "FLAT", "SMOOTH", "READONLY",
+ "WRITEONLY", "COHERENT", "RESTRICT", "VOLATILE", "STRUCT", "VOID_TYPE",
+ "WHILE", "SAMPLER2D", "SAMPLERCUBE", "SAMPLER_EXTERNAL_OES",
+ "SAMPLER2DRECT", "SAMPLER2DARRAY", "ISAMPLER2D", "ISAMPLER3D",
+ "ISAMPLERCUBE", "ISAMPLER2DARRAY", "USAMPLER2D", "USAMPLER3D",
+ "USAMPLERCUBE", "USAMPLER2DARRAY", "SAMPLER3D", "SAMPLER3DRECT",
+ "SAMPLER2DSHADOW", "SAMPLERCUBESHADOW", "SAMPLER2DARRAYSHADOW",
+ "IMAGE2D", "IIMAGE2D", "UIMAGE2D", "IMAGE3D", "IIMAGE3D", "UIMAGE3D",
+ "IMAGE2DARRAY", "IIMAGE2DARRAY", "UIMAGE2DARRAY", "IMAGECUBE",
+ "IIMAGECUBE", "UIMAGECUBE", "LAYOUT", "IDENTIFIER", "TYPE_NAME",
+ "FLOATCONSTANT", "INTCONSTANT", "UINTCONSTANT", "BOOLCONSTANT",
+ "FIELD_SELECTION", "LEFT_OP", "RIGHT_OP", "INC_OP", "DEC_OP", "LE_OP",
+ "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "XOR_OP", "MUL_ASSIGN",
+ "DIV_ASSIGN", "ADD_ASSIGN", "MOD_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN",
+ "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "SUB_ASSIGN", "LEFT_PAREN",
+ "RIGHT_PAREN", "LEFT_BRACKET", "RIGHT_BRACKET", "LEFT_BRACE",
+ "RIGHT_BRACE", "DOT", "COMMA", "COLON", "EQUAL", "SEMICOLON", "BANG",
+ "DASH", "TILDE", "PLUS", "STAR", "SLASH", "PERCENT", "LEFT_ANGLE",
+ "RIGHT_ANGLE", "VERTICAL_BAR", "CARET", "AMPERSAND", "QUESTION",
+ "$accept", "identifier", "variable_identifier", "primary_expression",
+ "postfix_expression", "integer_expression", "function_call",
+ "function_call_or_method", "function_call_generic",
+ "function_call_header_no_parameters",
+ "function_call_header_with_parameters", "function_call_header",
+ "function_identifier", "unary_expression", "unary_operator",
+ "multiplicative_expression", "additive_expression", "shift_expression",
+ "relational_expression", "equality_expression", "and_expression",
+ "exclusive_or_expression", "inclusive_or_expression",
+ "logical_and_expression", "logical_xor_expression",
+ "logical_or_expression", "conditional_expression",
+ "assignment_expression", "assignment_operator", "expression",
+ "constant_expression", "enter_struct", "declaration",
+ "function_prototype", "function_declarator",
+ "function_header_with_parameters", "function_header",
+ "parameter_declarator", "parameter_declaration",
+ "parameter_type_specifier", "init_declarator_list", "single_declaration",
+ "fully_specified_type", "interpolation_qualifier", "type_qualifier",
+ "invariant_qualifier", "single_type_qualifier", "storage_qualifier",
+ "type_specifier", "precision_qualifier", "layout_qualifier",
+ "layout_qualifier_id_list", "layout_qualifier_id",
+ "type_specifier_no_prec", "type_specifier_nonarray", "struct_specifier",
+ "$@1", "$@2", "struct_declaration_list", "struct_declaration",
+ "struct_declarator_list", "struct_declarator", "initializer",
+ "declaration_statement", "statement", "simple_statement",
+ "compound_statement", "$@3", "$@4", "statement_no_new_scope",
+ "statement_with_scope", "$@5", "$@6", "compound_statement_no_new_scope",
+ "statement_list", "expression_statement", "selection_statement",
+ "selection_rest_statement", "switch_statement", "$@7", "case_label",
+ "condition", "iteration_statement", "$@8", "$@9", "$@10",
+ "for_init_statement", "conditionopt", "for_rest_statement",
+ "jump_statement", "translation_unit", "external_declaration",
+ "function_definition", "$@11", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+ (internal) symbol number NUM (which must be that of a token). */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 319, 320, 321, 322, 323, 324,
+ 325, 326, 327, 328, 329, 330, 331, 332, 333, 334,
+ 335, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 350, 351, 352, 353, 354,
+ 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
+ 365, 366, 367, 368, 369, 370, 371, 372, 373, 374,
+ 375, 376, 377, 378, 379, 380, 381, 382, 383, 384,
+ 385, 386, 387, 388, 389, 390, 391, 392, 393, 394,
+ 395, 396, 397, 398, 399
+};
+# endif
+
+#define YYPACT_NINF -363
+
+#define yypact_value_is_default(Yystate) \
+ (!!((Yystate) == (-363)))
+
+#define YYTABLE_NINF -246
+
+#define yytable_value_is_error(Yytable_value) \
+ 0
+
+ /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+static const yytype_int16 yypact[] =
+{
+ 2469, -363, -363, -363, -363, 101, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -68, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, -363, -88, -363, -363,
+ -77, -35, -13, 2561, -51, -363, 27, -363, 1301, -363,
+ -363, -363, -363, -363, -363, -363, 12, -363, 2377, -363,
+ -363, 2731, -363, -363, -363, 15, 49, -363, 22, -363,
+ 2561, -363, -363, -363, 2561, 51, 51, -363, 16, -103,
+ -87, -363, 2561, -363, -363, 1388, -363, -363, 20, 2561,
+ -363, 19, -82, -363, 408, -363, -363, -363, -363, 39,
+ -91, -363, 1503, 1845, -363, -363, 2561, 51, 2071, -363,
+ 47, -363, -363, -363, -363, -363, 1845, 1845, 1845, -363,
+ -363, -363, -363, -363, -363, -363, -41, -363, -363, -363,
+ 55, -55, 1957, 59, -363, 1845, -5, -90, 64, -81,
+ -42, 33, 45, 48, 78, 80, -93, -363, 71, -363,
+ -363, 2173, 2561, 70, -363, 49, 57, 63, -363, 79,
+ 81, 68, 1618, 83, 1845, 72, 84, 82, -363, -363,
+ 41, -363, -363, -36, -363, -77, 85, -363, -363, -363,
+ -363, 541, -363, -363, -363, -363, -363, -363, 1845, 1730,
+ 1845, 77, 88, -363, -363, 51, 86, -34, -363, -78,
+ -363, -363, -363, -50, -363, -363, 1845, 2646, -363, -363,
+ 1845, 92, -363, -363, -363, 1845, 1845, 1845, 1845, 1845,
+ 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845,
+ 1845, 1845, 1845, 1845, 1845, -363, -363, 2275, -363, -363,
+ -363, -363, -363, 90, -363, 1845, -363, -363, -29, 1845,
+ 87, -363, -363, -363, 674, -363, -363, -363, -363, -363,
+ -363, -363, -363, -363, -363, -363, 1845, 1845, -363, -363,
+ -363, 93, 89, 94, -363, 1845, 95, -1, 1845, 51,
+ -363, -94, -363, -363, 96, 98, -363, 102, -363, -363,
+ -363, -363, -363, -5, -5, -90, -90, 64, 64, 64,
+ 64, -81, -81, -42, 33, 45, 48, 78, 80, 44,
+ -363, 161, 22, 940, 1073, -47, -363, -37, -363, 1187,
+ 674, -363, -363, -363, 1845, 103, -363, 1845, -363, 100,
+ -363, 1845, -363, -363, 1845, 107, -363, -363, -363, -363,
+ 1187, 90, -363, 98, 51, 2561, 109, 106, -363, 1845,
+ -363, -363, 111, -363, 1845, -363, 105, 116, 222, -363,
+ 115, 112, 807, -363, -363, 110, -32, 1845, 807, 90,
+ -363, 1845, -363, -363, -363, -363, 113, 98, -363, -363,
+ -363, -363
+};
+
+ /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ Performed when YYTABLE does not specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint16 yydefact[] =
+{
+ 0, 127, 147, 148, 149, 0, 133, 135, 163, 160,
+ 161, 162, 167, 168, 169, 170, 171, 172, 164, 165,
+ 166, 173, 174, 175, 176, 177, 178, 136, 137, 138,
+ 140, 134, 179, 180, 181, 182, 183, 184, 139, 124,
+ 123, 141, 142, 143, 144, 145, 0, 159, 185, 187,
+ 200, 201, 188, 189, 190, 191, 192, 193, 194, 195,
+ 196, 186, 197, 198, 199, 203, 204, 205, 206, 207,
+ 208, 209, 210, 211, 212, 213, 214, 0, 215, 284,
+ 285, 0, 98, 97, 0, 109, 115, 131, 0, 132,
+ 125, 128, 121, 130, 129, 146, 156, 202, 0, 281,
+ 283, 0, 2, 3, 218, 0, 0, 88, 0, 96,
+ 0, 105, 99, 107, 0, 108, 0, 89, 2, 116,
+ 0, 94, 0, 126, 122, 0, 1, 282, 0, 0,
+ 216, 153, 0, 151, 0, 286, 100, 104, 106, 102,
+ 110, 101, 0, 0, 87, 95, 0, 0, 0, 220,
+ 4, 8, 6, 7, 9, 30, 0, 0, 0, 157,
+ 37, 36, 38, 35, 5, 11, 31, 13, 18, 19,
+ 0, 0, 24, 0, 39, 0, 43, 46, 49, 54,
+ 57, 59, 61, 63, 65, 67, 69, 86, 0, 28,
+ 90, 0, 0, 0, 150, 0, 0, 0, 266, 0,
+ 0, 0, 0, 0, 0, 0, 0, 240, 249, 253,
+ 39, 71, 84, 0, 229, 0, 146, 232, 251, 231,
+ 230, 0, 233, 234, 235, 236, 237, 238, 0, 0,
+ 0, 0, 0, 228, 120, 0, 226, 0, 224, 0,
+ 221, 32, 33, 0, 15, 16, 0, 0, 22, 21,
+ 0, 159, 25, 27, 34, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 158, 219, 0, 154, 155,
+ 152, 277, 276, 247, 268, 0, 280, 278, 0, 0,
+ 0, 261, 264, 239, 0, 74, 75, 77, 76, 79,
+ 80, 81, 82, 83, 78, 73, 0, 0, 254, 250,
+ 252, 0, 0, 0, 114, 0, 117, 0, 0, 0,
+ 222, 0, 91, 10, 0, 17, 29, 14, 20, 26,
+ 40, 41, 42, 45, 44, 47, 48, 52, 53, 50,
+ 51, 55, 56, 58, 60, 62, 64, 66, 68, 0,
+ 217, 0, 0, 0, 0, 0, 279, 0, 260, 0,
+ 241, 72, 85, 103, 0, 111, 118, 0, 223, 0,
+ 225, 0, 92, 12, 0, 0, 246, 248, 271, 270,
+ 273, 247, 258, 262, 0, 0, 0, 0, 112, 0,
+ 119, 227, 0, 70, 0, 272, 0, 0, 257, 255,
+ 0, 0, 0, 242, 113, 0, 0, 274, 0, 247,
+ 259, 0, 244, 265, 243, 93, 0, 275, 269, 256,
+ 263, 267
+};
+
+ /* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -363, -45, -363, -363, -363, -363, -363, -363, -4, -363,
+ -363, -363, -363, 54, -363, -84, -79, -139, -83, -23,
+ -22, -21, -18, -17, -16, -363, -120, -137, -363, -146,
+ -125, -363, 18, 21, -363, -363, -363, 136, 146, 145,
+ -363, -363, -328, -363, -74, -363, -86, -363, -80, 255,
+ -363, -363, 67, 0, -363, -363, -363, -363, -116, -141,
+ 28, -54, -226, -85, -210, -339, -136, -363, -363, -142,
+ -362, -363, -363, -98, -27, -76, -363, -363, -363, -363,
+ -363, -112, -363, -363, -363, -363, -363, -363, -363, -363,
+ -363, 172, -363, -363
+};
+
+ /* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 236, 164, 165, 166, 324, 167, 168, 169, 170,
+ 171, 172, 173, 210, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 211, 212, 306, 213,
+ 188, 122, 214, 215, 81, 82, 83, 111, 112, 113,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 132, 133, 189, 96, 97, 192, 129, 148, 149,
+ 237, 238, 234, 217, 218, 219, 220, 294, 387, 413,
+ 351, 352, 353, 414, 221, 222, 223, 399, 224, 400,
+ 225, 386, 226, 359, 283, 354, 380, 396, 397, 227,
+ 98, 99, 100, 108
+};
+
+ /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule whose
+ number is the opposite. If YYTABLE_NINF, syntax error. */
+static const yytype_int16 yytable[] =
+{
+ 95, 105, 123, 115, 314, 187, 233, 240, 124, 114,
+ 135, 310, 243, 191, 377, 321, 273, 232, 79, 398,
+ 142, 80, 187, 262, 263, 102, 103, 143, 123, 371,
+ 115, 384, 229, 106, 115, 252, 114, 372, 144, 230,
+ 194, 119, 147, 258, 145, 259, 195, 419, 146, 147,
+ 240, 274, 384, 322, 107, 146, 288, 104, 264, 265,
+ 123, 244, 245, 412, 266, 267, 235, 249, 147, 412,
+ 139, 140, 323, 250, 146, 381, 277, 116, 307, 290,
+ 117, 307, 246, 95, 187, 382, 247, 109, 95, 366,
+ 416, 307, 307, 233, 319, 308, 307, 320, 95, 307,
+ 325, 128, 356, 311, 313, 2, 3, 4, 187, 187,
+ 95, 147, 147, 329, 95, 110, 79, 146, 146, 80,
+ 118, 103, 95, 337, 338, 339, 340, 319, 349, 95,
+ 368, 255, 256, 257, 216, 125, 240, 141, 388, 355,
+ 130, 390, 131, 357, 102, 103, 95, 134, 95, 193,
+ 310, 190, 295, 296, 297, 298, 299, 300, 301, 302,
+ 303, 304, 228, 404, 260, 261, 278, 279, -29, 361,
+ 362, 305, 307, 374, 333, 334, 268, 248, 233, 174,
+ 253, 335, 336, 341, 342, 420, 271, 269, 281, 270,
+ 272, 95, 95, 369, 282, 275, 174, 147, 187, 286,
+ 284, 291, 285, 146, 289, 292, -28, 315, 293, 318,
+ 241, 242, 316, 383, -23, -245, 358, 363, 365, 364,
+ 373, 216, 375, -30, 391, 367, 307, 233, 394, 254,
+ 233, 402, 403, 389, 383, 405, 407, 393, 408, 409,
+ 207, 415, 411, 328, 421, 343, 392, 344, 406, 345,
+ 137, 187, 233, 346, 376, 347, 136, 348, 174, 138,
+ 101, 417, 280, 317, 410, 370, 418, 360, 395, 378,
+ 127, 0, 0, 0, 233, 0, 0, 95, 379, 0,
+ 0, 0, 174, 174, 0, 385, 0, 0, 0, 0,
+ 0, 0, 0, 0, 216, 0, 0, 0, 0, 123,
+ 0, 0, 0, 0, 0, 124, 385, 0, 0, 330,
+ 331, 332, 174, 174, 174, 174, 174, 174, 174, 174,
+ 174, 174, 174, 174, 174, 174, 174, 174, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 401,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 216, 216, 0, 0, 0, 0, 216,
+ 216, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 174, 0, 0, 0, 0, 0, 0, 0,
+ 216, 0, 0, 0, 0, 95, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 216, 0, 0, 0, 0, 0, 216, 0,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 196, 197, 198, 174, 199, 200, 201, 202,
+ 203, 204, 205, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 206,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 0, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 150, 78, 151, 152, 153, 154, 155, 0, 0,
+ 156, 157, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 158,
+ 0, 0, 0, 207, 208, 0, 0, 0, 0, 209,
+ 160, 161, 162, 163, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 196, 197, 198, 0, 199,
+ 200, 201, 202, 203, 204, 205, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 206, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 0, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 150, 78, 151, 152, 153, 154,
+ 155, 0, 0, 156, 157, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 158, 0, 0, 0, 207, 309, 0, 0,
+ 0, 0, 209, 160, 161, 162, 163, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 196, 197,
+ 198, 0, 199, 200, 201, 202, 203, 204, 205, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 206, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 0, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, 76, 77, 150, 78, 151,
+ 152, 153, 154, 155, 0, 0, 156, 157, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 158, 0, 0, 0, 207,
+ 0, 0, 0, 0, 0, 209, 160, 161, 162, 163,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 196, 197, 198, 0, 199, 200, 201, 202, 203,
+ 204, 205, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 206, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 0, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 150, 78, 151, 152, 153, 154, 155, 0, 0, 156,
+ 157, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 158, 0,
+ 0, 0, 134, 0, 0, 0, 0, 0, 209, 160,
+ 161, 162, 163, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 196, 197, 198, 0, 199, 200,
+ 201, 202, 203, 204, 205, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 206, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 0, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 150, 78, 151, 152, 153, 154, 155,
+ 0, 0, 156, 157, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 158, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 209, 160, 161, 162, 163, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 0, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 150, 78, 151, 152,
+ 153, 154, 155, 0, 0, 156, 157, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 2, 3, 4, 158, 6, 7, 8, 9, 10,
+ 11, 0, 0, 0, 209, 160, 161, 162, 163, 0,
+ 0, 0, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 0, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 0, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 150, 78, 151, 152, 153, 154, 155, 0, 0, 156,
+ 157, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2, 3, 4, 158, 6,
+ 7, 8, 9, 10, 11, 0, 0, 0, 0, 160,
+ 161, 162, 163, 0, 0, 0, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 0, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 0, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 120, 78, 0, 0, 8, 9,
+ 10, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 0, 0,
+ 0, 0, 121, 32, 33, 34, 35, 36, 37, 0,
+ 0, 0, 0, 0, 0, 0, 0, 46, 47, 0,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 0, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 0, 150, 78, 151, 152, 153, 154, 155, 0, 0,
+ 156, 157, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 158,
+ 0, 0, 159, 8, 9, 10, 11, 0, 0, 0,
+ 160, 161, 162, 163, 0, 0, 0, 0, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 0, 0, 0, 0, 0, 32, 33,
+ 34, 35, 36, 37, 0, 0, 0, 0, 0, 0,
+ 0, 0, 46, 47, 0, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 0, 150, 78, 151, 152,
+ 153, 154, 155, 0, 0, 156, 157, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 158, 0, 0, 231, 8, 9,
+ 10, 11, 0, 0, 0, 160, 161, 162, 163, 0,
+ 0, 0, 0, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 0, 0,
+ 0, 0, 0, 32, 33, 34, 35, 36, 37, 0,
+ 0, 0, 0, 0, 0, 0, 0, 46, 47, 0,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 0, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 0, 150, 78, 151, 152, 153, 154, 155, 0, 0,
+ 156, 157, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 158,
+ 8, 9, 10, 11, 0, 0, 0, 0, 0, 287,
+ 160, 161, 162, 163, 0, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 0, 0, 0, 0, 0, 32, 33, 34, 35, 36,
+ 37, 0, 0, 0, 0, 0, 0, 0, 0, 46,
+ 47, 0, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 0, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 0, 150, 78, 151, 152, 153, 154, 155,
+ 0, 0, 156, 157, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 158, 0, 0, 312, 8, 9, 10, 11, 0,
+ 0, 0, 160, 161, 162, 163, 0, 0, 0, 0,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 0, 0, 0, 0, 0,
+ 32, 33, 34, 35, 36, 37, 0, 0, 0, 0,
+ 0, 0, 0, 0, 46, 47, 0, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 0, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 0, 150, 78,
+ 151, 152, 153, 154, 155, 0, 0, 156, 157, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 158, 8, 9, 10,
+ 11, 0, 0, 0, 0, 0, 0, 160, 161, 162,
+ 163, 0, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 0, 0, 0,
+ 0, 0, 32, 33, 34, 35, 36, 37, 0, 0,
+ 0, 0, 0, 0, 0, 0, 46, 251, 0, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 0, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 0,
+ 150, 78, 151, 152, 153, 154, 155, 0, 0, 156,
+ 157, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 2, 3, 4, 158, 6,
+ 7, 8, 9, 10, 11, 0, 0, 0, 0, 160,
+ 161, 162, 163, 0, 0, 0, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 0, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 0, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 0, 78, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 1, 2, 3, 4,
+ 0, 6, 7, 8, 9, 10, 11, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 239, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 0, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 0,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 77, 0, 78, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2,
+ 3, 4, 0, 6, 7, 8, 9, 10, 11, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 276,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 0, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, 0, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, 76, 77, 0, 78,
+ 0, 0, 0, 0, 0, 0, 0, 126, 0, 0,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 350, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 0, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 0, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 0, 78, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 0, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 0, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 0, 78, 1, 2, 3, 4, 0, 6,
+ 7, 8, 9, 10, 11, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 0, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 0, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 0, 78, 8, 9, 10, 11,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 0, 0, 0, 0,
+ 0, 32, 33, 34, 35, 36, 37, 0, 0, 0,
+ 0, 0, 0, 0, 0, 46, 47, 0, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 0, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, 76, 0, 326,
+ 78, 8, 9, 10, 11, 327, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 0, 0, 0, 0, 0, 32, 33, 34, 35,
+ 36, 37, 0, 0, 0, 0, 0, 0, 0, 0,
+ 46, 47, 0, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 0, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 0, 0, 78
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 0, 46, 88, 83, 230, 125, 143, 148, 88, 83,
+ 108, 221, 158, 129, 353, 93, 109, 142, 0, 381,
+ 123, 0, 142, 104, 105, 93, 94, 130, 114, 123,
+ 110, 359, 123, 121, 114, 172, 110, 131, 125, 130,
+ 122, 86, 122, 133, 131, 135, 128, 409, 122, 129,
+ 191, 144, 380, 131, 131, 129, 202, 125, 139, 140,
+ 146, 102, 103, 402, 106, 107, 146, 122, 148, 408,
+ 115, 116, 122, 128, 148, 122, 192, 128, 128, 204,
+ 131, 128, 123, 83, 204, 122, 127, 122, 88, 315,
+ 122, 128, 128, 230, 128, 131, 128, 131, 98, 128,
+ 246, 101, 131, 228, 229, 4, 5, 6, 228, 229,
+ 110, 191, 192, 250, 114, 128, 98, 191, 192, 98,
+ 93, 94, 122, 262, 263, 264, 265, 128, 274, 129,
+ 131, 136, 137, 138, 134, 123, 277, 121, 364, 285,
+ 125, 367, 93, 289, 93, 94, 146, 125, 148, 130,
+ 360, 131, 111, 112, 113, 114, 115, 116, 117, 118,
+ 119, 120, 123, 389, 100, 101, 96, 97, 121, 306,
+ 307, 130, 128, 129, 258, 259, 143, 122, 315, 125,
+ 121, 260, 261, 266, 267, 411, 108, 142, 131, 141,
+ 110, 191, 192, 318, 131, 124, 142, 277, 318, 131,
+ 121, 129, 121, 277, 121, 121, 121, 130, 126, 123,
+ 156, 157, 124, 359, 122, 125, 129, 124, 124, 130,
+ 124, 221, 61, 121, 124, 130, 128, 364, 121, 175,
+ 367, 122, 126, 130, 380, 124, 131, 374, 122, 17,
+ 125, 131, 130, 247, 131, 268, 371, 269, 394, 270,
+ 114, 371, 389, 271, 352, 272, 110, 273, 204, 114,
+ 5, 407, 195, 235, 400, 319, 408, 294, 380, 354,
+ 98, -1, -1, -1, 411, -1, -1, 277, 354, -1,
+ -1, -1, 228, 229, -1, 359, -1, -1, -1, -1,
+ -1, -1, -1, -1, 294, -1, -1, -1, -1, 385,
+ -1, -1, -1, -1, -1, 385, 380, -1, -1, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 264, 265,
+ 266, 267, 268, 269, 270, 271, 272, 273, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 384,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 353, 354, -1, -1, -1, -1, 359,
+ 360, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 318, -1, -1, -1, -1, -1, -1, -1,
+ 380, -1, -1, -1, -1, 385, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 402, -1, -1, -1, -1, -1, 408, -1,
+ -1, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 371, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
+ 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, -1, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99, -1, -1,
+ 102, 103, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 121,
+ -1, -1, -1, 125, 126, -1, -1, -1, -1, 131,
+ 132, 133, 134, 135, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16, -1, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, -1, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
+ 99, -1, -1, 102, 103, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 121, -1, -1, -1, 125, 126, -1, -1,
+ -1, -1, 131, 132, 133, 134, 135, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, -1, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
+ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ -1, 77, 78, 79, 80, 81, 82, 83, 84, 85,
+ 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, -1, -1, 102, 103, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 121, -1, -1, -1, 125,
+ -1, -1, -1, -1, -1, 131, 132, 133, 134, 135,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, -1, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, -1, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, -1, -1, 102,
+ 103, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 121, -1,
+ -1, -1, 125, -1, -1, -1, -1, -1, 131, 132,
+ 133, 134, 135, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, -1, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
+ -1, -1, 102, 103, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 121, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 131, 132, 133, 134, 135, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, -1, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, -1,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, -1, -1, 102, 103, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 4, 5, 6, 121, 8, 9, 10, 11, 12,
+ 13, -1, -1, -1, 131, 132, 133, 134, 135, -1,
+ -1, -1, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, -1, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, -1, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, -1, -1, 102,
+ 103, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 121, 8,
+ 9, 10, 11, 12, 13, -1, -1, -1, -1, 132,
+ 133, 134, 135, -1, -1, -1, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, -1, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, -1, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, 93, 94, -1, -1, 10, 11,
+ 12, 13, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, -1, -1,
+ -1, -1, 131, 45, 46, 47, 48, 49, 50, -1,
+ -1, -1, -1, -1, -1, -1, -1, 59, 60, -1,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, -1, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ -1, 93, 94, 95, 96, 97, 98, 99, -1, -1,
+ 102, 103, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 121,
+ -1, -1, 124, 10, 11, 12, 13, -1, -1, -1,
+ 132, 133, 134, 135, -1, -1, -1, -1, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, -1, -1, -1, -1, -1, 45, 46,
+ 47, 48, 49, 50, -1, -1, -1, -1, -1, -1,
+ -1, -1, 59, 60, -1, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, -1,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, -1, 93, 94, 95, 96,
+ 97, 98, 99, -1, -1, 102, 103, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 121, -1, -1, 124, 10, 11,
+ 12, 13, -1, -1, -1, 132, 133, 134, 135, -1,
+ -1, -1, -1, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, -1, -1,
+ -1, -1, -1, 45, 46, 47, 48, 49, 50, -1,
+ -1, -1, -1, -1, -1, -1, -1, 59, 60, -1,
+ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, -1, 77, 78, 79, 80, 81,
+ 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ -1, 93, 94, 95, 96, 97, 98, 99, -1, -1,
+ 102, 103, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 121,
+ 10, 11, 12, 13, -1, -1, -1, -1, -1, 131,
+ 132, 133, 134, 135, -1, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ -1, -1, -1, -1, -1, 45, 46, 47, 48, 49,
+ 50, -1, -1, -1, -1, -1, -1, -1, -1, 59,
+ 60, -1, 62, 63, 64, 65, 66, 67, 68, 69,
+ 70, 71, 72, 73, 74, 75, -1, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, -1, 93, 94, 95, 96, 97, 98, 99,
+ -1, -1, 102, 103, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 121, -1, -1, 124, 10, 11, 12, 13, -1,
+ -1, -1, 132, 133, 134, 135, -1, -1, -1, -1,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, -1, -1, -1, -1, -1,
+ 45, 46, 47, 48, 49, 50, -1, -1, -1, -1,
+ -1, -1, -1, -1, 59, 60, -1, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, -1, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, -1, 93, 94,
+ 95, 96, 97, 98, 99, -1, -1, 102, 103, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 121, 10, 11, 12,
+ 13, -1, -1, -1, -1, -1, -1, 132, 133, 134,
+ 135, -1, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, -1, -1, -1,
+ -1, -1, 45, 46, 47, 48, 49, 50, -1, -1,
+ -1, -1, -1, -1, -1, -1, 59, 60, -1, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, -1, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, -1,
+ 93, 94, 95, 96, 97, 98, 99, -1, -1, 102,
+ 103, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 3, 4, 5, 6, 121, 8,
+ 9, 10, 11, 12, 13, -1, -1, -1, -1, 132,
+ 133, 134, 135, -1, -1, -1, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, -1, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, -1, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, -1, 94, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 3, 4, 5, 6,
+ -1, 8, 9, 10, 11, 12, 13, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 126, 25, 26,
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, -1, 62, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, -1,
+ 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, -1, 94, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 3, 4,
+ 5, 6, -1, 8, 9, 10, 11, 12, 13, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 126,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, -1, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, -1, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, -1, 94,
+ -1, -1, -1, -1, -1, -1, -1, 0, -1, -1,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 126, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, -1, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 74, 75, -1, 77, 78, 79, 80, 81, 82,
+ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
+ -1, 94, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ -1, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 73, 74, 75, -1, 77, 78, 79, 80,
+ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,
+ 91, 92, -1, 94, 3, 4, 5, 6, -1, 8,
+ 9, 10, 11, 12, 13, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
+ 59, 60, -1, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, -1, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, 92, -1, 94, 10, 11, 12, 13,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, -1, -1, -1, -1,
+ -1, 45, 46, 47, 48, 49, 50, -1, -1, -1,
+ -1, -1, -1, -1, -1, 59, 60, -1, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, -1, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, -1, 93,
+ 94, 10, 11, 12, 13, 99, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 25, 26, 27, 28,
+ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, -1, -1, -1, -1, -1, 45, 46, 47, 48,
+ 49, 50, -1, -1, -1, -1, -1, -1, -1, -1,
+ 59, 60, -1, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 74, 75, -1, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 89, 90, 91, -1, -1, 94
+};
+
+ /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 57, 58, 59, 60, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 77, 78, 79, 80, 81, 82, 83, 84,
+ 85, 86, 87, 88, 89, 90, 91, 92, 94, 177,
+ 178, 179, 180, 181, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 198, 199, 200, 235, 236,
+ 237, 194, 93, 94, 125, 146, 121, 131, 238, 122,
+ 128, 182, 183, 184, 189, 193, 128, 131, 93, 146,
+ 93, 131, 176, 191, 193, 123, 0, 236, 198, 202,
+ 125, 93, 196, 197, 125, 218, 183, 182, 184, 146,
+ 146, 121, 123, 130, 125, 131, 189, 193, 203, 204,
+ 93, 95, 96, 97, 98, 99, 102, 103, 121, 124,
+ 132, 133, 134, 135, 147, 148, 149, 151, 152, 153,
+ 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
+ 164, 165, 166, 167, 168, 169, 170, 171, 175, 198,
+ 131, 203, 201, 130, 122, 128, 14, 15, 16, 18,
+ 19, 20, 21, 22, 23, 24, 61, 125, 126, 131,
+ 158, 171, 172, 174, 177, 178, 198, 208, 209, 210,
+ 211, 219, 220, 221, 223, 225, 227, 234, 123, 123,
+ 130, 124, 175, 172, 207, 193, 146, 205, 206, 126,
+ 204, 158, 158, 174, 102, 103, 123, 127, 122, 122,
+ 128, 60, 172, 121, 158, 136, 137, 138, 133, 135,
+ 100, 101, 104, 105, 139, 140, 106, 107, 143, 142,
+ 141, 108, 110, 109, 144, 124, 126, 203, 96, 97,
+ 197, 131, 131, 229, 121, 121, 131, 131, 174, 121,
+ 175, 129, 121, 126, 212, 111, 112, 113, 114, 115,
+ 116, 117, 118, 119, 120, 130, 173, 128, 131, 126,
+ 209, 175, 124, 175, 207, 130, 124, 205, 123, 128,
+ 131, 93, 131, 122, 150, 174, 93, 99, 153, 172,
+ 158, 158, 158, 160, 160, 161, 161, 162, 162, 162,
+ 162, 163, 163, 164, 165, 166, 167, 168, 169, 174,
+ 126, 215, 216, 217, 230, 174, 131, 174, 129, 228,
+ 219, 172, 172, 124, 130, 124, 207, 130, 131, 175,
+ 206, 123, 131, 124, 129, 61, 218, 210, 208, 220,
+ 231, 122, 122, 174, 187, 189, 226, 213, 207, 130,
+ 207, 124, 175, 172, 121, 226, 232, 233, 215, 222,
+ 224, 146, 122, 126, 207, 124, 174, 131, 122, 17,
+ 211, 130, 210, 214, 218, 131, 122, 174, 214, 215,
+ 207, 131
+};
+
+ /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 145, 146, 146, 147, 148, 148, 148, 148, 148,
+ 148, 149, 149, 149, 149, 149, 149, 150, 151, 152,
+ 152, 153, 153, 154, 154, 155, 155, 156, 157, 157,
+ 157, 158, 158, 158, 158, 159, 159, 159, 159, 160,
+ 160, 160, 160, 161, 161, 161, 162, 162, 162, 163,
+ 163, 163, 163, 163, 164, 164, 164, 165, 165, 166,
+ 166, 167, 167, 168, 168, 169, 169, 170, 170, 171,
+ 171, 172, 172, 173, 173, 173, 173, 173, 173, 173,
+ 173, 173, 173, 173, 174, 174, 175, 176, 177, 177,
+ 177, 177, 177, 177, 177, 177, 178, 179, 179, 180,
+ 180, 181, 182, 182, 183, 183, 183, 183, 184, 185,
+ 185, 185, 185, 185, 185, 186, 186, 186, 186, 186,
+ 186, 187, 187, 188, 188, 189, 189, 190, 191, 191,
+ 191, 191, 191, 192, 192, 192, 192, 192, 192, 192,
+ 192, 192, 192, 192, 192, 192, 193, 194, 194, 194,
+ 195, 196, 196, 197, 197, 197, 198, 198, 198, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 199, 199, 199, 199,
+ 199, 199, 199, 199, 199, 199, 201, 200, 202, 200,
+ 203, 203, 204, 204, 205, 205, 206, 206, 207, 208,
+ 209, 209, 210, 210, 210, 210, 210, 210, 210, 211,
+ 212, 213, 211, 214, 214, 216, 215, 217, 215, 218,
+ 218, 219, 219, 220, 220, 221, 222, 222, 224, 223,
+ 225, 225, 226, 226, 228, 227, 229, 227, 230, 227,
+ 231, 231, 232, 232, 233, 233, 234, 234, 234, 234,
+ 234, 235, 235, 236, 236, 238, 237
+};
+
+ /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 1, 4, 1, 3, 2, 2, 1, 1, 1,
+ 3, 2, 2, 2, 1, 2, 3, 2, 1, 1,
+ 1, 1, 2, 2, 2, 1, 1, 1, 1, 1,
+ 3, 3, 3, 1, 3, 3, 1, 3, 3, 1,
+ 3, 3, 3, 3, 1, 3, 3, 1, 3, 1,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 5, 1, 3, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 3, 1, 2, 2, 2,
+ 4, 5, 6, 9, 2, 3, 2, 1, 1, 2,
+ 3, 3, 2, 5, 2, 1, 2, 1, 1, 1,
+ 3, 6, 7, 8, 5, 1, 2, 5, 6, 7,
+ 4, 1, 2, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 4, 1, 3, 1, 3, 3, 1, 3, 4, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 6, 0, 5,
+ 1, 2, 3, 4, 1, 3, 1, 4, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 0, 0, 5, 1, 1, 0, 2, 0, 2, 2,
+ 3, 1, 2, 1, 2, 5, 3, 1, 0, 6,
+ 3, 2, 1, 4, 0, 6, 0, 8, 0, 7,
+ 1, 1, 1, 0, 2, 3, 2, 2, 2, 3,
+ 2, 1, 2, 1, 1, 0, 3
+};
+
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ YYPOPSTACK (yylen); \
+ yystate = *yyssp; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (&yylloc, context, scanner, YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (0)
+
+/* Error token number */
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+
+/* Print *YYLOCP on YYO. Private, do not rely on its existence. */
+
+YY_ATTRIBUTE_UNUSED
+static unsigned
+yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp)
+{
+ unsigned res = 0;
+ int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0;
+ if (0 <= yylocp->first_line)
+ {
+ res += YYFPRINTF (yyo, "%d", yylocp->first_line);
+ if (0 <= yylocp->first_column)
+ res += YYFPRINTF (yyo, ".%d", yylocp->first_column);
+ }
+ if (0 <= yylocp->last_line)
+ {
+ if (yylocp->first_line < yylocp->last_line)
+ {
+ res += YYFPRINTF (yyo, "-%d", yylocp->last_line);
+ if (0 <= end_col)
+ res += YYFPRINTF (yyo, ".%d", end_col);
+ }
+ else if (0 <= end_col && yylocp->first_column < end_col)
+ res += YYFPRINTF (yyo, "-%d", end_col);
+ }
+ return res;
+ }
+
+# define YY_LOCATION_PRINT(File, Loc) \
+ yy_location_print_ (File, &(Loc))
+
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value, Location, context, scanner); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT. |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, TParseContext* context, void *scanner)
+{
+ FILE *yyo = yyoutput;
+ YYUSE (yyo);
+ YYUSE (yylocationp);
+ YYUSE (context);
+ YYUSE (scanner);
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, TParseContext* context, void *scanner)
+{
+ YYFPRINTF (yyoutput, "%s %s (",
+ yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+ YY_LOCATION_PRINT (yyoutput, *yylocationp);
+ YYFPRINTF (yyoutput, ": ");
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, context, scanner);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; yybottom <= yytop; yybottom++)
+ {
+ int yybot = *yybottom;
+ YYFPRINTF (stderr, " %d", yybot);
+ }
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, TParseContext* context, void *scanner)
+{
+ unsigned long int yylno = yyrline[yyrule];
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ YYFPRINTF (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr,
+ yystos[yyssp[yyi + 1 - yynrhs]],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ , &(yylsp[(yyi + 1) - (yynrhs)]) , context, scanner);
+ YYFPRINTF (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyssp, yyvsp, yylsp, Rule, context, scanner); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+ about the unexpected token YYTOKEN for the state stack whose top is
+ YYSSP.
+
+ Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is
+ not large enough to hold the message. In that case, also set
+ *YYMSG_ALLOC to the required number of bytes. Return 2 if the
+ required number of bytes is too large to store. */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+ yytype_int16 *yyssp, int yytoken)
+{
+ YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+ YYSIZE_T yysize = yysize0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ /* Internationalized format string. */
+ const char *yyformat = YY_NULLPTR;
+ /* Arguments of yyformat. */
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ /* Number of reported tokens (one for the "unexpected", one per
+ "expected"). */
+ int yycount = 0;
+
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yychar) is if
+ this state is a consistent state with a default action. Thus,
+ detecting the absence of a lookahead is sufficient to determine
+ that there is no unexpected or expected token to report. In that
+ case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is a
+ consistent state with a default action. There might have been a
+ previous inconsistent state, consistent state with a non-default
+ action, or user semantic action that manipulated yychar.
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+ if (yytoken != YYEMPTY)
+ {
+ int yyn = yypact[*yyssp];
+ yyarg[yycount++] = yytname[yytoken];
+ if (!yypact_value_is_default (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yyx;
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+ && !yytable_value_is_error (yytable[yyx + yyn]))
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ {
+ YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+ if (! (yysize <= yysize1
+ && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+ }
+ }
+ }
+
+ switch (yycount)
+ {
+# define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ YYCASE_(0, YY_("syntax error"));
+ YYCASE_(1, YY_("syntax error, unexpected %s"));
+ YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+ }
+
+ {
+ YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+ if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+ return 2;
+ yysize = yysize1;
+ }
+
+ if (*yymsg_alloc < yysize)
+ {
+ *yymsg_alloc = 2 * yysize;
+ if (! (yysize <= *yymsg_alloc
+ && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+ *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+ return 1;
+ }
+
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ {
+ char *yyp = *yymsg;
+ int yyi = 0;
+ while ((*yyp = *yyformat) != '\0')
+ if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyformat += 2;
+ }
+ else
+ {
+ yyp++;
+ yyformat++;
+ }
+ }
+ return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, TParseContext* context, void *scanner)
+{
+ YYUSE (yyvaluep);
+ YYUSE (yylocationp);
+ YYUSE (context);
+ YYUSE (scanner);
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ YYUSE (yytype);
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+int
+yyparse (TParseContext* context, void *scanner)
+{
+/* The lookahead symbol. */
+int yychar;
+
+
+/* The semantic value of the lookahead symbol. */
+/* Default value used for initialization, for pacifying older GCCs
+ or non-GCC compilers. */
+YY_INITIAL_VALUE (static YYSTYPE yyval_default;)
+YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);
+
+/* Location data for the lookahead symbol. */
+static YYLTYPE yyloc_default
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+ = { 1, 1, 1, 1 }
+# endif
+;
+YYLTYPE yylloc = yyloc_default;
+
+ /* Number of syntax errors so far. */
+ int yynerrs;
+
+ int yystate;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+
+ /* The stacks and their tools:
+ 'yyss': related to states.
+ 'yyvs': related to semantic values.
+ 'yyls': related to locations.
+
+ Refer to the stacks through separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs;
+ YYSTYPE *yyvsp;
+
+ /* The location stack. */
+ YYLTYPE yylsa[YYINITDEPTH];
+ YYLTYPE *yyls;
+ YYLTYPE *yylsp;
+
+ /* The locations where the error started and ended. */
+ YYLTYPE yyerror_range[3];
+
+ YYSIZE_T yystacksize;
+
+ int yyn;
+ int yyresult;
+ /* Lookahead token as an internal (translated) token number. */
+ int yytoken = 0;
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+ YYLTYPE yyloc;
+
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ yyssp = yyss = yyssa;
+ yyvsp = yyvs = yyvsa;
+ yylsp = yyls = yylsa;
+ yystacksize = YYINITDEPTH;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+ yylsp[0] = yylloc;
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+ YYLTYPE *yyls1 = yyls;
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+ &yyls1, yysize * sizeof (*yylsp),
+ &yystacksize);
+
+ yyls = yyls1;
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss_alloc, yyss);
+ YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+ YYSTACK_RELOCATE (yyls_alloc, yyls);
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+ yylsp = yyls + yysize - 1;
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ lookahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to lookahead token. */
+ yyn = yypact[yystate];
+ if (yypact_value_is_default (yyn))
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = yylex (&yylval, &yylloc, scanner);
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yytable_value_is_error (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the lookahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token. */
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+ *++yylsp = yylloc;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ '$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+ /* Default location. */
+ YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+
+ {
+ // The symbol table search was done in the lexical phase
+ (yyval.interm.intermTypedNode) = context->parseVariableIdentifier((yylsp[0]), (yyvsp[0].lex).string, (yyvsp[0].lex).symbol);
+
+ // don't delete $1.string, it's used by error recovery, and the pool
+ // pop will reclaim the memory
+ }
+
+ break;
+
+ case 5:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 6:
+
+ {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setIConst((yyvsp[0].lex).i);
+ (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), (yylsp[0]));
+ }
+
+ break;
+
+ case 7:
+
+ {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setUConst((yyvsp[0].lex).u);
+ (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(unionArray, TType(EbtUInt, EbpUndefined, EvqConst), (yylsp[0]));
+ }
+
+ break;
+
+ case 8:
+
+ {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setFConst((yyvsp[0].lex).f);
+ (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), (yylsp[0]));
+ }
+
+ break;
+
+ case 9:
+
+ {
+ TConstantUnion *unionArray = new TConstantUnion[1];
+ unionArray->setBConst((yyvsp[0].lex).b);
+ (yyval.interm.intermTypedNode) = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), (yylsp[0]));
+ }
+
+ break;
+
+ case 10:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 11:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 12:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addIndexExpression((yyvsp[-3].interm.intermTypedNode), (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 13:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 14:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addFieldSelectionExpression((yyvsp[-2].interm.intermTypedNode), (yylsp[-1]), *(yyvsp[0].lex).string, (yylsp[0]));
+ }
+
+ break;
+
+ case 15:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addUnaryMathLValue(EOpPostIncrement, (yyvsp[-1].interm.intermTypedNode), (yylsp[0]));
+ }
+
+ break;
+
+ case 16:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addUnaryMathLValue(EOpPostDecrement, (yyvsp[-1].interm.intermTypedNode), (yylsp[0]));
+ }
+
+ break;
+
+ case 17:
+
+ {
+ context->checkIsScalarInteger((yyvsp[0].interm.intermTypedNode), "[]");
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 18:
+
+ {
+ bool fatalError = false;
+ (yyval.interm.intermTypedNode) = context->addFunctionCallOrMethod((yyvsp[0].interm).function, (yyvsp[0].interm).nodePair.node1, (yyvsp[0].interm).nodePair.node2, (yylsp[0]), &fatalError);
+ if (fatalError)
+ {
+ YYERROR;
+ }
+ }
+
+ break;
+
+ case 19:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ (yyval.interm).nodePair.node2 = nullptr;
+ }
+
+ break;
+
+ case 20:
+
+ {
+ ES3_OR_NEWER("", (yylsp[0]), "methods");
+ (yyval.interm) = (yyvsp[0].interm);
+ (yyval.interm).nodePair.node2 = (yyvsp[-2].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 21:
+
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ }
+
+ break;
+
+ case 22:
+
+ {
+ (yyval.interm) = (yyvsp[-1].interm);
+ }
+
+ break;
+
+ case 23:
+
+ {
+ (yyval.interm).function = (yyvsp[-1].interm.function);
+ (yyval.interm).nodePair.node1 = nullptr;
+ }
+
+ break;
+
+ case 24:
+
+ {
+ (yyval.interm).function = (yyvsp[0].interm.function);
+ (yyval.interm).nodePair.node1 = nullptr;
+ }
+
+ break;
+
+ case 25:
+
+ {
+ const TType *type = new TType((yyvsp[0].interm.intermTypedNode)->getType());
+ (yyvsp[-1].interm.function)->addParameter(TConstParameter(type));
+ (yyval.interm).function = (yyvsp[-1].interm.function);
+ (yyval.interm).nodePair.node1 = TIntermediate::MakeAggregate((yyvsp[0].interm.intermTypedNode), (yylsp[0]));
+ }
+
+ break;
+
+ case 26:
+
+ {
+ const TType *type = new TType((yyvsp[0].interm.intermTypedNode)->getType());
+ (yyvsp[-2].interm).function->addParameter(TConstParameter(type));
+ (yyval.interm).function = (yyvsp[-2].interm).function;
+ (yyval.interm).nodePair.node1 = context->intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 27:
+
+ {
+ (yyval.interm.function) = (yyvsp[-1].interm.function);
+ }
+
+ break;
+
+ case 28:
+
+ {
+ if ((yyvsp[0].interm.type).array) {
+ ES3_OR_NEWER("[]", (yylsp[0]), "array constructor");
+ }
+ (yyval.interm.function) = context->addConstructorFunc((yyvsp[0].interm.type));
+ }
+
+ break;
+
+ case 29:
+
+ {
+ context->checkIsNotReserved((yylsp[0]), *(yyvsp[0].lex).string);
+ const TType *type = TCache::getType(EbtVoid, EbpUndefined);
+ TFunction *function = new TFunction((yyvsp[0].lex).string, type);
+ (yyval.interm.function) = function;
+ }
+
+ break;
+
+ case 30:
+
+ {
+ context->checkIsNotReserved((yylsp[0]), *(yyvsp[0].lex).string);
+ const TType *type = TCache::getType(EbtVoid, EbpUndefined);
+ TFunction *function = new TFunction((yyvsp[0].lex).string, type);
+ (yyval.interm.function) = function;
+ }
+
+ break;
+
+ case 31:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 32:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addUnaryMathLValue(EOpPreIncrement, (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 33:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addUnaryMathLValue(EOpPreDecrement, (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 34:
+
+ {
+ if ((yyvsp[-1].interm).op != EOpNull) {
+ (yyval.interm.intermTypedNode) = context->addUnaryMath((yyvsp[-1].interm).op, (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ } else
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 35:
+
+ { (yyval.interm).op = EOpPositive; }
+
+ break;
+
+ case 36:
+
+ { (yyval.interm).op = EOpNegative; }
+
+ break;
+
+ case 37:
+
+ { (yyval.interm).op = EOpLogicalNot; }
+
+ break;
+
+ case 38:
+
+ {
+ ES3_OR_NEWER("~", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitwiseNot;
+ }
+
+ break;
+
+ case 39:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 40:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpMul, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 41:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpDiv, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 42:
+
+ {
+ ES3_OR_NEWER("%", (yylsp[-1]), "integer modulus operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpIMod, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 43:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 44:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpAdd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 45:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpSub, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 46:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 47:
+
+ {
+ ES3_OR_NEWER("<<", (yylsp[-1]), "bit-wise operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpBitShiftLeft, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 48:
+
+ {
+ ES3_OR_NEWER(">>", (yylsp[-1]), "bit-wise operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpBitShiftRight, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 49:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 50:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpLessThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 51:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpGreaterThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 52:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpLessThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 53:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpGreaterThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 54:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 55:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 56:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpNotEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 57:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 58:
+
+ {
+ ES3_OR_NEWER("&", (yylsp[-1]), "bit-wise operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpBitwiseAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 59:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 60:
+
+ {
+ ES3_OR_NEWER("^", (yylsp[-1]), "bit-wise operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpBitwiseXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 61:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 62:
+
+ {
+ ES3_OR_NEWER("|", (yylsp[-1]), "bit-wise operator");
+ (yyval.interm.intermTypedNode) = context->addBinaryMath(EOpBitwiseOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 63:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 64:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpLogicalAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 65:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 66:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpLogicalXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 67:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 68:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addBinaryMathBooleanResult(EOpLogicalOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 69:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 70:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addTernarySelection((yyvsp[-4].interm.intermTypedNode), (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-3]));
+ }
+
+ break;
+
+ case 71:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 72:
+
+ {
+ context->checkCanBeLValue((yylsp[-1]), "assign", (yyvsp[-2].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = context->addAssign((yyvsp[-1].interm).op, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 73:
+
+ { (yyval.interm).op = EOpAssign; }
+
+ break;
+
+ case 74:
+
+ { (yyval.interm).op = EOpMulAssign; }
+
+ break;
+
+ case 75:
+
+ { (yyval.interm).op = EOpDivAssign; }
+
+ break;
+
+ case 76:
+
+ {
+ ES3_OR_NEWER("%=", (yyloc), "integer modulus operator");
+ (yyval.interm).op = EOpIModAssign;
+ }
+
+ break;
+
+ case 77:
+
+ { (yyval.interm).op = EOpAddAssign; }
+
+ break;
+
+ case 78:
+
+ { (yyval.interm).op = EOpSubAssign; }
+
+ break;
+
+ case 79:
+
+ {
+ ES3_OR_NEWER("<<=", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitShiftLeftAssign;
+ }
+
+ break;
+
+ case 80:
+
+ {
+ ES3_OR_NEWER(">>=", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitShiftRightAssign;
+ }
+
+ break;
+
+ case 81:
+
+ {
+ ES3_OR_NEWER("&=", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitwiseAndAssign;
+ }
+
+ break;
+
+ case 82:
+
+ {
+ ES3_OR_NEWER("^=", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitwiseXorAssign;
+ }
+
+ break;
+
+ case 83:
+
+ {
+ ES3_OR_NEWER("|=", (yyloc), "bit-wise operator");
+ (yyval.interm).op = EOpBitwiseOrAssign;
+ }
+
+ break;
+
+ case 84:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 85:
+
+ {
+ (yyval.interm.intermTypedNode) = context->addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yylsp[-1]));
+ }
+
+ break;
+
+ case 86:
+
+ {
+ context->checkIsConst((yyvsp[0].interm.intermTypedNode));
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 87:
+
+ {
+ context->enterStructDeclaration((yylsp[-1]), *(yyvsp[-1].lex).string);
+ (yyval.lex) = (yyvsp[-1].lex);
+ }
+
+ break;
+
+ case 88:
+
+ {
+ (yyval.interm.intermNode) = context->addFunctionPrototypeDeclaration(*((yyvsp[-1].interm).function), (yylsp[-1]));
+ }
+
+ break;
+
+ case 89:
+
+ {
+ (yyval.interm.intermNode) = (yyvsp[-1].interm).intermDeclaration;
+ }
+
+ break;
+
+ case 90:
+
+ {
+ if (((yyvsp[-2].interm.precision) == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) {
+ context->error((yylsp[-3]), "precision is not supported in fragment shader", "highp");
+ }
+ if (!context->symbolTable.setDefaultPrecision( (yyvsp[-1].interm.type), (yyvsp[-2].interm.precision) )) {
+ context->error((yylsp[-3]), "illegal type argument for default precision qualifier", getBasicString((yyvsp[-1].interm.type).getBasicType()));
+ }
+ (yyval.interm.intermNode) = 0;
+ }
+
+ break;
+
+ case 91:
+
+ {
+ ES3_OR_NEWER((yyvsp[-3].lex).string->c_str(), (yylsp[-4]), "interface blocks");
+ (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-4].interm.typeQualifierBuilder), (yylsp[-3]), *(yyvsp[-3].lex).string, (yyvsp[-2].interm.fieldList), NULL, (yyloc), NULL, (yyloc));
+ }
+
+ break;
+
+ case 92:
+
+ {
+ ES3_OR_NEWER((yyvsp[-4].lex).string->c_str(), (yylsp[-5]), "interface blocks");
+ (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-5].interm.typeQualifierBuilder), (yylsp[-4]), *(yyvsp[-4].lex).string, (yyvsp[-3].interm.fieldList), (yyvsp[-1].lex).string, (yylsp[-1]), NULL, (yyloc));
+ }
+
+ break;
+
+ case 93:
+
+ {
+ ES3_OR_NEWER((yyvsp[-7].lex).string->c_str(), (yylsp[-8]), "interface blocks");
+ (yyval.interm.intermNode) = context->addInterfaceBlock(*(yyvsp[-8].interm.typeQualifierBuilder), (yylsp[-7]), *(yyvsp[-7].lex).string, (yyvsp[-6].interm.fieldList), (yyvsp[-4].lex).string, (yylsp[-4]), (yyvsp[-2].interm.intermTypedNode), (yylsp[-3]));
+ }
+
+ break;
+
+ case 94:
+
+ {
+ context->parseGlobalLayoutQualifier(*(yyvsp[-1].interm.typeQualifierBuilder));
+ (yyval.interm.intermNode) = 0;
+ }
+
+ break;
+
+ case 95:
+
+ {
+ (yyval.interm.intermNode) = context->parseInvariantDeclaration(*(yyvsp[-2].interm.typeQualifierBuilder), (yylsp[-1]), (yyvsp[-1].lex).string, (yyvsp[-1].lex).symbol);
+ }
+
+ break;
+
+ case 96:
+
+ {
+ (yyval.interm).function = context->parseFunctionDeclarator((yylsp[0]), (yyvsp[-1].interm.function));
+ context->exitFunctionDeclaration();
+ }
+
+ break;
+
+ case 97:
+
+ {
+ (yyval.interm.function) = (yyvsp[0].interm.function);
+ }
+
+ break;
+
+ case 98:
+
+ {
+ (yyval.interm.function) = (yyvsp[0].interm.function);
+ }
+
+ break;
+
+ case 99:
+
+ {
+ // Add the parameter
+ (yyval.interm.function) = (yyvsp[-1].interm.function);
+ if ((yyvsp[0].interm).param.type->getBasicType() != EbtVoid)
+ (yyvsp[-1].interm.function)->addParameter((yyvsp[0].interm).param.turnToConst());
+ else
+ delete (yyvsp[0].interm).param.type;
+ }
+
+ break;
+
+ case 100:
+
+ {
+ //
+ // Only first parameter of one-parameter functions can be void
+ // The check for named parameters not being void is done in parameter_declarator
+ //
+ if ((yyvsp[0].interm).param.type->getBasicType() == EbtVoid) {
+ //
+ // This parameter > first is void
+ //
+ context->error((yylsp[-1]), "cannot be an argument type except for '(void)'", "void");
+ delete (yyvsp[0].interm).param.type;
+ } else {
+ // Add the parameter
+ (yyval.interm.function) = (yyvsp[-2].interm.function);
+ (yyvsp[-2].interm.function)->addParameter((yyvsp[0].interm).param.turnToConst());
+ }
+ }
+
+ break;
+
+ case 101:
+
+ {
+ (yyval.interm.function) = context->parseFunctionHeader((yyvsp[-2].interm.type), (yyvsp[-1].lex).string, (yylsp[-1]));
+
+ context->symbolTable.push();
+ context->enterFunctionDeclaration();
+ }
+
+ break;
+
+ case 102:
+
+ {
+ if ((yyvsp[-1].interm.type).getBasicType() == EbtVoid) {
+ context->error((yylsp[0]), "illegal use of type 'void'", (yyvsp[0].lex).string->c_str());
+ }
+ context->checkIsNotReserved((yylsp[0]), *(yyvsp[0].lex).string);
+ TParameter param = {(yyvsp[0].lex).string, new TType((yyvsp[-1].interm.type))};
+ (yyval.interm).param = param;
+ }
+
+ break;
+
+ case 103:
+
+ {
+ // Check that we can make an array out of this type
+ context->checkIsValidTypeForArray((yylsp[-2]), (yyvsp[-4].interm.type));
+
+ context->checkIsNotReserved((yylsp[-3]), *(yyvsp[-3].lex).string);
+
+ unsigned int size = context->checkIsValidArraySize((yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+
+ (yyvsp[-4].interm.type).setArraySize(size);
+
+ TType* type = new TType((yyvsp[-4].interm.type));
+ TParameter param = { (yyvsp[-3].lex).string, type };
+ (yyval.interm).param = param;
+ }
+
+ break;
+
+ case 104:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ context->checkIsParameterQualifierValid((yylsp[0]), *(yyvsp[-1].interm.typeQualifierBuilder), (yyvsp[0].interm).param.type);
+ }
+
+ break;
+
+ case 105:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ (yyval.interm).param.type->setQualifier(EvqIn);
+ }
+
+ break;
+
+ case 106:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ context->checkIsParameterQualifierValid((yylsp[0]), *(yyvsp[-1].interm.typeQualifierBuilder), (yyvsp[0].interm).param.type);
+ }
+
+ break;
+
+ case 107:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ (yyval.interm).param.type->setQualifier(EvqIn);
+ }
+
+ break;
+
+ case 108:
+
+ {
+ TParameter param = { 0, new TType((yyvsp[0].interm.type)) };
+ (yyval.interm).param = param;
+ }
+
+ break;
+
+ case 109:
+
+ {
+ (yyval.interm) = (yyvsp[0].interm);
+ }
+
+ break;
+
+ case 110:
+
+ {
+ (yyval.interm) = (yyvsp[-2].interm);
+ context->parseDeclarator((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string, (yyval.interm).intermDeclaration);
+ }
+
+ break;
+
+ case 111:
+
+ {
+ (yyval.interm) = (yyvsp[-5].interm);
+ context->parseArrayDeclarator((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+ }
+
+ break;
+
+ case 112:
+
+ {
+ ES3_OR_NEWER("[]", (yylsp[-4]), "implicitly sized array");
+ (yyval.interm) = (yyvsp[-6].interm);
+ context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+ }
+
+ break;
+
+ case 113:
+
+ {
+ ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)");
+ (yyval.interm) = (yyvsp[-7].interm);
+ context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+ }
+
+ break;
+
+ case 114:
+
+ {
+ (yyval.interm) = (yyvsp[-4].interm);
+ context->parseInitDeclarator((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
+ }
+
+ break;
+
+ case 115:
+
+ {
+ (yyval.interm).type = (yyvsp[0].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), "");
+ }
+
+ break;
+
+ case 116:
+
+ {
+ (yyval.interm).type = (yyvsp[-1].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string);
+ }
+
+ break;
+
+ case 117:
+
+ {
+ (yyval.interm).type = (yyvsp[-4].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleArrayDeclaration((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 118:
+
+ {
+ ES3_OR_NEWER("[]", (yylsp[-3]), "implicitly sized array");
+ (yyval.interm).type = (yyvsp[-5].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 119:
+
+ {
+ ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)");
+ (yyval.interm).type = (yyvsp[-6].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 120:
+
+ {
+ (yyval.interm).type = (yyvsp[-3].interm.type);
+ (yyval.interm).intermDeclaration = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 121:
+
+ {
+ context->addFullySpecifiedType(&(yyvsp[0].interm.type));
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ }
+
+ break;
+
+ case 122:
+
+ {
+ (yyval.interm.type) = context->addFullySpecifiedType(*(yyvsp[-1].interm.typeQualifierBuilder), (yyvsp[0].interm.type));
+ }
+
+ break;
+
+ case 123:
+
+ {
+ (yyval.interm.qualifier) = EvqSmooth;
+ }
+
+ break;
+
+ case 124:
+
+ {
+ (yyval.interm.qualifier) = EvqFlat;
+ }
+
+ break;
+
+ case 125:
+
+ {
+ (yyval.interm.typeQualifierBuilder) = context->createTypeQualifierBuilder((yylsp[0]));
+ (yyval.interm.typeQualifierBuilder)->appendQualifier((yyvsp[0].interm.qualifierWrapper));
+ }
+
+ break;
+
+ case 126:
+
+ {
+ (yyval.interm.typeQualifierBuilder) = (yyvsp[-1].interm.typeQualifierBuilder);
+ (yyval.interm.typeQualifierBuilder)->appendQualifier((yyvsp[0].interm.qualifierWrapper));
+ }
+
+ break;
+
+ case 127:
+
+ {
+ // empty
+ }
+
+ break;
+
+ case 128:
+
+ {
+ context->checkLocalVariableConstStorageQualifier(*(yyvsp[0].interm.qualifierWrapper));
+ (yyval.interm.qualifierWrapper) = (yyvsp[0].interm.qualifierWrapper);
+ }
+
+ break;
+
+ case 129:
+
+ {
+ context->checkIsAtGlobalLevel((yylsp[0]), "layout");
+ (yyval.interm.qualifierWrapper) = new TLayoutQualifierWrapper((yyvsp[0].interm.layoutQualifier), (yylsp[0]));
+ }
+
+ break;
+
+ case 130:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TPrecisionQualifierWrapper((yyvsp[0].interm.precision), (yylsp[0]));
+ }
+
+ break;
+
+ case 131:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TInterpolationQualifierWrapper((yyvsp[0].interm.qualifier), (yylsp[0]));
+ }
+
+ break;
+
+ case 132:
+
+ {
+ context->checkIsAtGlobalLevel((yylsp[0]), "invariant");
+ (yyval.interm.qualifierWrapper) = new TInvariantQualifierWrapper((yylsp[0]));
+ }
+
+ break;
+
+ case 133:
+
+ {
+ VERTEX_ONLY("attribute", (yylsp[0]));
+ ES2_ONLY("attribute", (yylsp[0]));
+ context->checkIsAtGlobalLevel((yylsp[0]), "attribute");
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqAttribute, (yylsp[0]));
+ }
+
+ break;
+
+ case 134:
+
+ {
+ ES2_ONLY("varying", (yylsp[0]));
+ context->checkIsAtGlobalLevel((yylsp[0]), "varying");
+ if (context->getShaderType() == GL_VERTEX_SHADER)
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqVaryingOut, (yylsp[0]));
+ else
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqVaryingIn, (yylsp[0]));
+ }
+
+ break;
+
+ case 135:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqConst, (yylsp[0]));
+ }
+
+ break;
+
+ case 136:
+
+ {
+ if (context->declaringFunction())
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqIn, (yylsp[0]));
+ }
+ else if (context->getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqFragmentIn, (yylsp[0]));
+ }
+ else if (context->getShaderType() == GL_VERTEX_SHADER)
+ {
+ ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqVertexIn, (yylsp[0]));
+ }
+ else
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqComputeIn, (yylsp[0]));
+ }
+ }
+
+ break;
+
+ case 137:
+
+ {
+ if (context->declaringFunction())
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqOut, (yylsp[0]));
+ }
+ else
+ {
+ ES3_OR_NEWER("out", (yylsp[0]), "storage qualifier");
+ NON_COMPUTE_ONLY("out", (yylsp[0]));
+ if (context->getShaderType() == GL_FRAGMENT_SHADER)
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqFragmentOut, (yylsp[0]));
+ }
+ else
+ {
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqVertexOut, (yylsp[0]));
+ }
+ }
+ }
+
+ break;
+
+ case 138:
+
+ {
+ if (!context->declaringFunction())
+ {
+ context->error((yylsp[0]), "invalid inout qualifier", "'inout' can be only used with function parameters");
+ }
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqInOut, (yylsp[0]));
+ }
+
+ break;
+
+ case 139:
+
+ {
+ ES3_OR_NEWER("centroid", (yylsp[0]), "storage qualifier");
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqCentroid, (yylsp[0]));
+ }
+
+ break;
+
+ case 140:
+
+ {
+ context->checkIsAtGlobalLevel((yylsp[0]), "uniform");
+ (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqUniform, (yylsp[0]));
+ }
+
+ break;
+
+ case 141:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TMemoryQualifierWrapper(EvqReadOnly, (yylsp[0]));
+ }
+
+ break;
+
+ case 142:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TMemoryQualifierWrapper(EvqWriteOnly, (yylsp[0]));
+ }
+
+ break;
+
+ case 143:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TMemoryQualifierWrapper(EvqCoherent, (yylsp[0]));
+ }
+
+ break;
+
+ case 144:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TMemoryQualifierWrapper(EvqRestrict, (yylsp[0]));
+ }
+
+ break;
+
+ case 145:
+
+ {
+ (yyval.interm.qualifierWrapper) = new TMemoryQualifierWrapper(EvqVolatile, (yylsp[0]));
+ }
+
+ break;
+
+ case 146:
+
+ {
+ (yyval.interm.type) = (yyvsp[0].interm.type);
+ (yyval.interm.type).precision = context->symbolTable.getDefaultPrecision((yyvsp[0].interm.type).getBasicType());
+ }
+
+ break;
+
+ case 147:
+
+ {
+ (yyval.interm.precision) = EbpHigh;
+ }
+
+ break;
+
+ case 148:
+
+ {
+ (yyval.interm.precision) = EbpMedium;
+ }
+
+ break;
+
+ case 149:
+
+ {
+ (yyval.interm.precision) = EbpLow;
+ }
+
+ break;
+
+ case 150:
+
+ {
+ ES3_OR_NEWER("layout", (yylsp[-3]), "qualifier");
+ (yyval.interm.layoutQualifier) = (yyvsp[-1].interm.layoutQualifier);
+ }
+
+ break;
+
+ case 151:
+
+ {
+ (yyval.interm.layoutQualifier) = (yyvsp[0].interm.layoutQualifier);
+ }
+
+ break;
+
+ case 152:
+
+ {
+ (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier), (yylsp[0]));
+ }
+
+ break;
+
+ case 153:
+
+ {
+ (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[0].lex).string, (yylsp[0]));
+ }
+
+ break;
+
+ case 154:
+
+ {
+ (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
+ }
+
+ break;
+
+ case 155:
+
+ {
+ (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
+ }
+
+ break;
+
+ case 156:
+
+ {
+ (yyval.interm.type).initialize((yyvsp[0].interm.typeSpecifierNonArray), (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ }
+
+ break;
+
+ case 157:
+
+ {
+ ES3_OR_NEWER("[]", (yylsp[-1]), "implicitly sized array");
+ (yyval.interm.type).initialize((yyvsp[-2].interm.typeSpecifierNonArray), (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ (yyval.interm.type).setArraySize(0);
+ }
+
+ break;
+
+ case 158:
+
+ {
+ (yyval.interm.type).initialize((yyvsp[-3].interm.typeSpecifierNonArray), (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary));
+ if (context->checkIsValidTypeForArray((yylsp[-2]), (yyval.interm.type)))
+ {
+ unsigned int size = context->checkIsValidArraySize((yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+ (yyval.interm.type).setArraySize(size);
+ }
+ }
+
+ break;
+
+ case 159:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtVoid, (yylsp[0]));
+ }
+
+ break;
+
+ case 160:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ }
+
+ break;
+
+ case 161:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0]));
+ }
+
+ break;
+
+ case 162:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0]));
+ }
+
+ break;
+
+ case 163:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0]));
+ }
+
+ break;
+
+ case 164:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(2);
+ }
+
+ break;
+
+ case 165:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(3);
+ }
+
+ break;
+
+ case 166:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(4);
+ }
+
+ break;
+
+ case 167:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(2);
+ }
+
+ break;
+
+ case 168:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(3);
+ }
+
+ break;
+
+ case 169:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(4);
+ }
+
+ break;
+
+ case 170:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(2);
+ }
+
+ break;
+
+ case 171:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(3);
+ }
+
+ break;
+
+ case 172:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(4);
+ }
+
+ break;
+
+ case 173:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(2);
+ }
+
+ break;
+
+ case 174:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(3);
+ }
+
+ break;
+
+ case 175:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setAggregate(4);
+ }
+
+ break;
+
+ case 176:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(2, 2);
+ }
+
+ break;
+
+ case 177:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(3, 3);
+ }
+
+ break;
+
+ case 178:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(4, 4);
+ }
+
+ break;
+
+ case 179:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(2, 3);
+ }
+
+ break;
+
+ case 180:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(3, 2);
+ }
+
+ break;
+
+ case 181:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(2, 4);
+ }
+
+ break;
+
+ case 182:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(4, 2);
+ }
+
+ break;
+
+ case 183:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(3, 4);
+ }
+
+ break;
+
+ case 184:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).setMatrix(4, 3);
+ }
+
+ break;
+
+ case 185:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 186:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 187:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 188:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 189:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 190:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 191:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtISamplerCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 192:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 193:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 194:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 195:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUSamplerCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 196:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 197:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DShadow, (yylsp[0]));
+ }
+
+ break;
+
+ case 198:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerCubeShadow, (yylsp[0]));
+ }
+
+ break;
+
+ case 199:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DArrayShadow, (yylsp[0]));
+ }
+
+ break;
+
+ case 200:
+
+ {
+ if (!context->supportsExtension("GL_OES_EGL_image_external") &&
+ !context->supportsExtension("GL_NV_EGL_stream_consumer_external")) {
+ context->error((yylsp[0]), "unsupported type", "samplerExternalOES");
+ }
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerExternalOES, (yylsp[0]));
+ }
+
+ break;
+
+ case 201:
+
+ {
+ if (!context->supportsExtension("GL_ARB_texture_rectangle")) {
+ context->error((yylsp[0]), "unsupported type", "sampler2DRect");
+ }
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DRect, (yylsp[0]));
+ }
+
+ break;
+
+ case 202:
+
+ {
+ (yyval.interm.typeSpecifierNonArray) = (yyvsp[0].interm.typeSpecifierNonArray);
+ }
+
+ break;
+
+ case 203:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtImage2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 204:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtIImage2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 205:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUImage2D, (yylsp[0]));
+ }
+
+ break;
+
+ case 206:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtImage3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 207:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtIImage3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 208:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUImage3D, (yylsp[0]));
+ }
+
+ break;
+
+ case 209:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtImage2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 210:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtIImage2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 211:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUImage2DArray, (yylsp[0]));
+ }
+
+ break;
+
+ case 212:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtImageCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 213:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtIImageCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 214:
+
+ {
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtUImageCube, (yylsp[0]));
+ }
+
+ break;
+
+ case 215:
+
+ {
+ //
+ // This is for user defined type names. The lexical phase looked up the
+ // type.
+ //
+ TType& structure = static_cast<TVariable*>((yyvsp[0].lex).symbol)->getType();
+ (yyval.interm.typeSpecifierNonArray).initialize(EbtStruct, (yylsp[0]));
+ (yyval.interm.typeSpecifierNonArray).userDef = &structure;
+ }
+
+ break;
+
+ case 216:
+
+ { context->enterStructDeclaration((yylsp[-1]), *(yyvsp[-1].lex).string); }
+
+ break;
+
+ case 217:
+
+ {
+ (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-5]), (yylsp[-4]), (yyvsp[-4].lex).string, (yyvsp[-1].interm.fieldList));
+ }
+
+ break;
+
+ case 218:
+
+ { context->enterStructDeclaration((yylsp[0]), *(yyvsp[0].lex).string); }
+
+ break;
+
+ case 219:
+
+ {
+ (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-4]), (yyloc), NewPoolTString(""), (yyvsp[-1].interm.fieldList));
+ }
+
+ break;
+
+ case 220:
+
+ {
+ (yyval.interm.fieldList) = (yyvsp[0].interm.fieldList);
+ }
+
+ break;
+
+ case 221:
+
+ {
+ (yyval.interm.fieldList) = (yyvsp[-1].interm.fieldList);
+ for (size_t i = 0; i < (yyvsp[0].interm.fieldList)->size(); ++i) {
+ TField* field = (*(yyvsp[0].interm.fieldList))[i];
+ for (size_t j = 0; j < (yyval.interm.fieldList)->size(); ++j) {
+ if ((*(yyval.interm.fieldList))[j]->name() == field->name()) {
+ context->error((yylsp[0]), "duplicate field name in structure:", "struct", field->name().c_str());
+ }
+ }
+ (yyval.interm.fieldList)->push_back(field);
+ }
+ }
+
+ break;
+
+ case 222:
+
+ {
+ (yyval.interm.fieldList) = context->addStructDeclaratorList((yyvsp[-2].interm.type), (yyvsp[-1].interm.fieldList));
+ }
+
+ break;
+
+ case 223:
+
+ {
+ // ES3 Only, but errors should be handled elsewhere
+ (yyval.interm.fieldList) = context->addStructDeclaratorListWithQualifiers(*(yyvsp[-3].interm.typeQualifierBuilder), &(yyvsp[-2].interm.type), (yyvsp[-1].interm.fieldList));
+ }
+
+ break;
+
+ case 224:
+
+ {
+ (yyval.interm.fieldList) = NewPoolTFieldList();
+ (yyval.interm.fieldList)->push_back((yyvsp[0].interm.field));
+ }
+
+ break;
+
+ case 225:
+
+ {
+ (yyval.interm.fieldList)->push_back((yyvsp[0].interm.field));
+ }
+
+ break;
+
+ case 226:
+
+ {
+ context->checkIsNotReserved((yylsp[0]), *(yyvsp[0].lex).string);
+
+ TType* type = new TType(EbtVoid, EbpUndefined);
+ (yyval.interm.field) = new TField(type, (yyvsp[0].lex).string, (yylsp[0]));
+ }
+
+ break;
+
+ case 227:
+
+ {
+ context->checkIsNotReserved((yylsp[-3]), *(yyvsp[-3].lex).string);
+
+ TType* type = new TType(EbtVoid, EbpUndefined);
+ unsigned int size = context->checkIsValidArraySize((yylsp[-1]), (yyvsp[-1].interm.intermTypedNode));
+ type->setArraySize(size);
+
+ (yyval.interm.field) = new TField(type, (yyvsp[-3].lex).string, (yylsp[-3]));
+ }
+
+ break;
+
+ case 228:
+
+ { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); }
+
+ break;
+
+ case 229:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 230:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); }
+
+ break;
+
+ case 231:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 232:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 233:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 234:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 235:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermSwitch); }
+
+ break;
+
+ case 236:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermCase); }
+
+ break;
+
+ case 237:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 238:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 239:
+
+ { (yyval.interm.intermBlock) = 0; }
+
+ break;
+
+ case 240:
+
+ { context->symbolTable.push(); }
+
+ break;
+
+ case 241:
+
+ { context->symbolTable.pop(); }
+
+ break;
+
+ case 242:
+
+ {
+ if ((yyvsp[-2].interm.intermBlock) != 0) {
+ (yyvsp[-2].interm.intermBlock)->setLine((yyloc));
+ }
+ (yyval.interm.intermBlock) = (yyvsp[-2].interm.intermBlock);
+ }
+
+ break;
+
+ case 243:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); }
+
+ break;
+
+ case 244:
+
+ { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 245:
+
+ { context->symbolTable.push(); }
+
+ break;
+
+ case 246:
+
+ { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); }
+
+ break;
+
+ case 247:
+
+ { context->symbolTable.push(); }
+
+ break;
+
+ case 248:
+
+ { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); }
+
+ break;
+
+ case 249:
+
+ {
+ (yyval.interm.intermBlock) = 0;
+ }
+
+ break;
+
+ case 250:
+
+ {
+ if ((yyvsp[-1].interm.intermBlock)) {
+ (yyvsp[-1].interm.intermBlock)->setLine((yyloc));
+ }
+ (yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock);
+ }
+
+ break;
+
+ case 251:
+
+ {
+ (yyval.interm.intermBlock) = new TIntermBlock();
+ (yyval.interm.intermBlock)->setLine((yyloc));
+ (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode));
+ }
+
+ break;
+
+ case 252:
+
+ {
+ (yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock);
+ (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode));
+ }
+
+ break;
+
+ case 253:
+
+ { (yyval.interm.intermNode) = 0; }
+
+ break;
+
+ case 254:
+
+ { (yyval.interm.intermNode) = static_cast<TIntermNode*>((yyvsp[-1].interm.intermTypedNode)); }
+
+ break;
+
+ case 255:
+
+ {
+ context->checkIsScalarBool((yylsp[-4]), (yyvsp[-2].interm.intermTypedNode));
+ (yyval.interm.intermNode) = context->intermediate.addIfElse((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.nodePair), (yylsp[-4]));
+ }
+
+ break;
+
+ case 256:
+
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode);
+ (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode);
+ }
+
+ break;
+
+ case 257:
+
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode);
+ (yyval.interm.nodePair).node2 = 0;
+ }
+
+ break;
+
+ case 258:
+
+ { context->incrSwitchNestingLevel(); }
+
+ break;
+
+ case 259:
+
+ {
+ (yyval.interm.intermSwitch) = context->addSwitch((yyvsp[-3].interm.intermTypedNode), (yyvsp[0].interm.intermBlock), (yylsp[-5]));
+ context->decrSwitchNestingLevel();
+ }
+
+ break;
+
+ case 260:
+
+ {
+ (yyval.interm.intermCase) = context->addCase((yyvsp[-1].interm.intermTypedNode), (yylsp[-2]));
+ }
+
+ break;
+
+ case 261:
+
+ {
+ (yyval.interm.intermCase) = context->addDefault((yylsp[-1]));
+ }
+
+ break;
+
+ case 262:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ context->checkIsScalarBool((yyvsp[0].interm.intermTypedNode)->getLine(), (yyvsp[0].interm.intermTypedNode));
+ }
+
+ break;
+
+ case 263:
+
+ {
+ TIntermBinary *initNode = nullptr;
+ context->checkIsScalarBool((yylsp[-2]), (yyvsp[-3].interm.type));
+
+ if (!context->executeInitializer((yylsp[-2]), *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), (yyvsp[0].interm.intermTypedNode), &initNode))
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ else {
+ (yyval.interm.intermTypedNode) = 0;
+ }
+ }
+
+ break;
+
+ case 264:
+
+ { context->symbolTable.push(); context->incrLoopNestingLevel(); }
+
+ break;
+
+ case 265:
+
+ {
+ context->symbolTable.pop();
+ (yyval.interm.intermNode) = context->intermediate.addLoop(ELoopWhile, 0, (yyvsp[-2].interm.intermTypedNode), 0, (yyvsp[0].interm.intermNode), (yylsp[-5]));
+ context->decrLoopNestingLevel();
+ }
+
+ break;
+
+ case 266:
+
+ { context->incrLoopNestingLevel(); }
+
+ break;
+
+ case 267:
+
+ {
+ context->checkIsScalarBool((yylsp[0]), (yyvsp[-2].interm.intermTypedNode));
+
+ (yyval.interm.intermNode) = context->intermediate.addLoop(ELoopDoWhile, 0, (yyvsp[-2].interm.intermTypedNode), 0, (yyvsp[-5].interm.intermNode), (yylsp[-4]));
+ context->decrLoopNestingLevel();
+ }
+
+ break;
+
+ case 268:
+
+ { context->symbolTable.push(); context->incrLoopNestingLevel(); }
+
+ break;
+
+ case 269:
+
+ {
+ context->symbolTable.pop();
+ (yyval.interm.intermNode) = context->intermediate.addLoop(ELoopFor, (yyvsp[-3].interm.intermNode), reinterpret_cast<TIntermTyped*>((yyvsp[-2].interm.nodePair).node1), reinterpret_cast<TIntermTyped*>((yyvsp[-2].interm.nodePair).node2), (yyvsp[0].interm.intermNode), (yylsp[-6]));
+ context->decrLoopNestingLevel();
+ }
+
+ break;
+
+ case 270:
+
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+
+ break;
+
+ case 271:
+
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+
+ break;
+
+ case 272:
+
+ {
+ (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 273:
+
+ {
+ (yyval.interm.intermTypedNode) = 0;
+ }
+
+ break;
+
+ case 274:
+
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode);
+ (yyval.interm.nodePair).node2 = 0;
+ }
+
+ break;
+
+ case 275:
+
+ {
+ (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode);
+ (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode);
+ }
+
+ break;
+
+ case 276:
+
+ {
+ (yyval.interm.intermNode) = context->addBranch(EOpContinue, (yylsp[-1]));
+ }
+
+ break;
+
+ case 277:
+
+ {
+ (yyval.interm.intermNode) = context->addBranch(EOpBreak, (yylsp[-1]));
+ }
+
+ break;
+
+ case 278:
+
+ {
+ (yyval.interm.intermNode) = context->addBranch(EOpReturn, (yylsp[-1]));
+ }
+
+ break;
+
+ case 279:
+
+ {
+ (yyval.interm.intermNode) = context->addBranch(EOpReturn, (yyvsp[-1].interm.intermTypedNode), (yylsp[-2]));
+ }
+
+ break;
+
+ case 280:
+
+ {
+ FRAG_ONLY("discard", (yylsp[-1]));
+ (yyval.interm.intermNode) = context->addBranch(EOpKill, (yylsp[-1]));
+ }
+
+ break;
+
+ case 281:
+
+ {
+ (yyval.interm.intermBlock) = new TIntermBlock();
+ (yyval.interm.intermBlock)->setLine((yyloc));
+ (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode));
+ context->setTreeRoot((yyval.interm.intermBlock));
+ }
+
+ break;
+
+ case 282:
+
+ {
+ (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode));
+ }
+
+ break;
+
+ case 283:
+
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+
+ break;
+
+ case 284:
+
+ {
+ (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode);
+ }
+
+ break;
+
+ case 285:
+
+ {
+ context->parseFunctionDefinitionHeader((yylsp[0]), &((yyvsp[0].interm).function), &(yyvsp[0].interm).intermAggregate);
+ }
+
+ break;
+
+ case 286:
+
+ {
+ (yyval.interm.intermNode) = context->addFunctionDefinition(*((yyvsp[-2].interm).function), (yyvsp[-2].interm).intermAggregate, (yyvsp[0].interm.intermBlock), (yylsp[-2]));
+ }
+
+ break;
+
+
+
+ default: break;
+ }
+ /* User semantic actions sometimes alter yychar, and that requires
+ that yytoken be updated with the new translation. We take the
+ approach of translating immediately before every use of yytoken.
+ One alternative is translating here after every semantic action,
+ but that translation would be missed if the semantic action invokes
+ YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+ if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an
+ incorrect destructor might then be invoked immediately. In the
+ case of YYERROR or YYBACKUP, subsequent parser actions might lead
+ to an incorrect destructor call or verbose syntax error message
+ before the lookahead is translated. */
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+ *++yylsp = yyloc;
+
+ /* Now 'shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error. |
+`--------------------------------------*/
+yyerrlab:
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (&yylloc, context, scanner, YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+ yyssp, yytoken)
+ {
+ char const *yymsgp = YY_("syntax error");
+ int yysyntax_error_status;
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ if (yysyntax_error_status == 0)
+ yymsgp = yymsg;
+ else if (yysyntax_error_status == 1)
+ {
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+ if (!yymsg)
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ yysyntax_error_status = 2;
+ }
+ else
+ {
+ yysyntax_error_status = YYSYNTAX_ERROR;
+ yymsgp = yymsg;
+ }
+ }
+ yyerror (&yylloc, context, scanner, yymsgp);
+ if (yysyntax_error_status == 2)
+ goto yyexhaustedlab;
+ }
+# undef YYSYNTAX_ERROR
+#endif
+ }
+
+ yyerror_range[1] = yylloc;
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval, &yylloc, context, scanner);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse lookahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ yyerror_range[1] = yylsp[1-yylen];
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (!yypact_value_is_default (yyn))
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+ yyerror_range[1] = *yylsp;
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp, yylsp, context, scanner);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+ *++yyvsp = yylval;
+ YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+ yyerror_range[2] = yylloc;
+ /* Using YYLLOC is tempting, but would change the location of
+ the lookahead. YYLOC is available though. */
+ YYLLOC_DEFAULT (yyloc, yyerror_range, 2);
+ *++yylsp = yyloc;
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (&yylloc, context, scanner, YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEMPTY)
+ {
+ /* Make sure we have latest lookahead translation. See comments at
+ user semantic actions for why this is necessary. */
+ yytoken = YYTRANSLATE (yychar);
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval, &yylloc, context, scanner);
+ }
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp, yylsp, context, scanner);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ return yyresult;
+}
+
+
+
+int glslang_parse(TParseContext* context) {
+ return yyparse(context, context->getScanner());
+}
diff --git a/gfx/angle/src/compiler/translator/glslang_tab.h b/gfx/angle/src/compiler/translator/glslang_tab.h
new file mode 100755
index 000000000..8b043c01d
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/glslang_tab.h
@@ -0,0 +1,270 @@
+/* A Bison parser, made by GNU Bison 3.0.4. */
+
+/* Bison interface for Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+#ifndef YY_YY_GLSLANG_TAB_H_INCLUDED
+# define YY_YY_GLSLANG_TAB_H_INCLUDED
+/* Debug traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+/* "%code requires" blocks. */
+
+
+#define YYLTYPE TSourceLoc
+#define YYLTYPE_IS_DECLARED 1
+
+
+
+/* Token type. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+enum yytokentype
+{
+ INVARIANT = 258,
+ HIGH_PRECISION = 259,
+ MEDIUM_PRECISION = 260,
+ LOW_PRECISION = 261,
+ PRECISION = 262,
+ ATTRIBUTE = 263,
+ CONST_QUAL = 264,
+ BOOL_TYPE = 265,
+ FLOAT_TYPE = 266,
+ INT_TYPE = 267,
+ UINT_TYPE = 268,
+ BREAK = 269,
+ CONTINUE = 270,
+ DO = 271,
+ ELSE = 272,
+ FOR = 273,
+ IF = 274,
+ DISCARD = 275,
+ RETURN = 276,
+ SWITCH = 277,
+ CASE = 278,
+ DEFAULT = 279,
+ BVEC2 = 280,
+ BVEC3 = 281,
+ BVEC4 = 282,
+ IVEC2 = 283,
+ IVEC3 = 284,
+ IVEC4 = 285,
+ VEC2 = 286,
+ VEC3 = 287,
+ VEC4 = 288,
+ UVEC2 = 289,
+ UVEC3 = 290,
+ UVEC4 = 291,
+ MATRIX2 = 292,
+ MATRIX3 = 293,
+ MATRIX4 = 294,
+ IN_QUAL = 295,
+ OUT_QUAL = 296,
+ INOUT_QUAL = 297,
+ UNIFORM = 298,
+ VARYING = 299,
+ MATRIX2x3 = 300,
+ MATRIX3x2 = 301,
+ MATRIX2x4 = 302,
+ MATRIX4x2 = 303,
+ MATRIX3x4 = 304,
+ MATRIX4x3 = 305,
+ CENTROID = 306,
+ FLAT = 307,
+ SMOOTH = 308,
+ READONLY = 309,
+ WRITEONLY = 310,
+ COHERENT = 311,
+ RESTRICT = 312,
+ VOLATILE = 313,
+ STRUCT = 314,
+ VOID_TYPE = 315,
+ WHILE = 316,
+ SAMPLER2D = 317,
+ SAMPLERCUBE = 318,
+ SAMPLER_EXTERNAL_OES = 319,
+ SAMPLER2DRECT = 320,
+ SAMPLER2DARRAY = 321,
+ ISAMPLER2D = 322,
+ ISAMPLER3D = 323,
+ ISAMPLERCUBE = 324,
+ ISAMPLER2DARRAY = 325,
+ USAMPLER2D = 326,
+ USAMPLER3D = 327,
+ USAMPLERCUBE = 328,
+ USAMPLER2DARRAY = 329,
+ SAMPLER3D = 330,
+ SAMPLER3DRECT = 331,
+ SAMPLER2DSHADOW = 332,
+ SAMPLERCUBESHADOW = 333,
+ SAMPLER2DARRAYSHADOW = 334,
+ IMAGE2D = 335,
+ IIMAGE2D = 336,
+ UIMAGE2D = 337,
+ IMAGE3D = 338,
+ IIMAGE3D = 339,
+ UIMAGE3D = 340,
+ IMAGE2DARRAY = 341,
+ IIMAGE2DARRAY = 342,
+ UIMAGE2DARRAY = 343,
+ IMAGECUBE = 344,
+ IIMAGECUBE = 345,
+ UIMAGECUBE = 346,
+ LAYOUT = 347,
+ IDENTIFIER = 348,
+ TYPE_NAME = 349,
+ FLOATCONSTANT = 350,
+ INTCONSTANT = 351,
+ UINTCONSTANT = 352,
+ BOOLCONSTANT = 353,
+ FIELD_SELECTION = 354,
+ LEFT_OP = 355,
+ RIGHT_OP = 356,
+ INC_OP = 357,
+ DEC_OP = 358,
+ LE_OP = 359,
+ GE_OP = 360,
+ EQ_OP = 361,
+ NE_OP = 362,
+ AND_OP = 363,
+ OR_OP = 364,
+ XOR_OP = 365,
+ MUL_ASSIGN = 366,
+ DIV_ASSIGN = 367,
+ ADD_ASSIGN = 368,
+ MOD_ASSIGN = 369,
+ LEFT_ASSIGN = 370,
+ RIGHT_ASSIGN = 371,
+ AND_ASSIGN = 372,
+ XOR_ASSIGN = 373,
+ OR_ASSIGN = 374,
+ SUB_ASSIGN = 375,
+ LEFT_PAREN = 376,
+ RIGHT_PAREN = 377,
+ LEFT_BRACKET = 378,
+ RIGHT_BRACKET = 379,
+ LEFT_BRACE = 380,
+ RIGHT_BRACE = 381,
+ DOT = 382,
+ COMMA = 383,
+ COLON = 384,
+ EQUAL = 385,
+ SEMICOLON = 386,
+ BANG = 387,
+ DASH = 388,
+ TILDE = 389,
+ PLUS = 390,
+ STAR = 391,
+ SLASH = 392,
+ PERCENT = 393,
+ LEFT_ANGLE = 394,
+ RIGHT_ANGLE = 395,
+ VERTICAL_BAR = 396,
+ CARET = 397,
+ AMPERSAND = 398,
+ QUESTION = 399
+};
+#endif
+
+/* Value type. */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+union YYSTYPE
+{
+
+
+ struct {
+ union {
+ TString *string;
+ float f;
+ int i;
+ unsigned int u;
+ bool b;
+ };
+ TSymbol* symbol;
+ } lex;
+ struct {
+ TOperator op;
+ union {
+ TIntermNode *intermNode;
+ TIntermNodePair nodePair;
+ TIntermTyped *intermTypedNode;
+ TIntermAggregate *intermAggregate;
+ TIntermBlock *intermBlock;
+ TIntermDeclaration *intermDeclaration;
+ TIntermSwitch *intermSwitch;
+ TIntermCase *intermCase;
+ };
+ union {
+ TTypeSpecifierNonArray typeSpecifierNonArray;
+ TPublicType type;
+ TPrecision precision;
+ TLayoutQualifier layoutQualifier;
+ TQualifier qualifier;
+ TFunction *function;
+ TParameter param;
+ TField *field;
+ TFieldList *fieldList;
+ TQualifierWrapperBase *qualifierWrapper;
+ TTypeQualifierBuilder *typeQualifierBuilder;
+ };
+ } interm;
+
+
+};
+
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+/* Location type. */
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE YYLTYPE;
+struct YYLTYPE
+{
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+};
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+int yyparse (TParseContext* context, void *scanner);
+
+#endif /* !YY_YY_GLSLANG_TAB_H_INCLUDED */
diff --git a/gfx/angle/src/compiler/translator/intermOut.cpp b/gfx/angle/src/compiler/translator/intermOut.cpp
new file mode 100755
index 000000000..53ee1d02a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/intermOut.cpp
@@ -0,0 +1,738 @@
+//
+// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/Intermediate.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void OutputFunction(TInfoSinkBase &out, const char *str, TFunctionSymbolInfo *info)
+{
+ const char *internal = info->getNameObj().isInternal() ? " (internal function)" : "";
+ out << str << internal << ": " << info->getNameObj().getString() << " (symbol id "
+ << info->getId() << ")";
+}
+
+//
+// Two purposes:
+// 1. Show an example of how to iterate tree. Functions can
+// also directly call Traverse() on children themselves to
+// have finer grained control over the process than shown here.
+// See the last function for how to get started.
+// 2. Print out a text based description of the tree.
+//
+
+//
+// Use this class to carry along data from node to node in
+// the traversal
+//
+class TOutputTraverser : public TIntermTraverser
+{
+ public:
+ TOutputTraverser(TInfoSinkBase &i)
+ : TIntermTraverser(true, false, false),
+ sink(i)
+ {
+ }
+ TInfoSinkBase& sink;
+
+ protected:
+ void visitSymbol(TIntermSymbol *) override;
+ void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
+ bool visitBinary(Visit visit, TIntermBinary *) override;
+ bool visitUnary(Visit visit, TIntermUnary *) override;
+ bool visitTernary(Visit visit, TIntermTernary *node) override;
+ bool visitIfElse(Visit visit, TIntermIfElse *node) override;
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+ bool visitCase(Visit visit, TIntermCase *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+ bool visitAggregate(Visit visit, TIntermAggregate *) override;
+ bool visitBlock(Visit visit, TIntermBlock *) override;
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitLoop(Visit visit, TIntermLoop *) override;
+ bool visitBranch(Visit visit, TIntermBranch *) override;
+};
+
+//
+// Helper functions for printing, not part of traversing.
+//
+void OutputTreeText(TInfoSinkBase &sink, TIntermNode *node, const int depth)
+{
+ int i;
+
+ sink.location(node->getLine());
+
+ for (i = 0; i < depth; ++i)
+ sink << " ";
+}
+
+} // namespace anonymous
+
+//
+// The rest of the file are the traversal functions. The last one
+// is the one that starts the traversal.
+//
+// Return true from interior nodes to have the external traversal
+// continue on to children. If you process children yourself,
+// return false.
+//
+
+void TOutputTraverser::visitSymbol(TIntermSymbol *node)
+{
+ OutputTreeText(sink, node, mDepth);
+
+ sink << "'" << node->getSymbol() << "' ";
+ sink << "(" << node->getCompleteString() << ")\n";
+}
+
+bool TOutputTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
+{
+ TInfoSinkBase &out = sink;
+ OutputTreeText(out, node, mDepth);
+ out << "vector swizzle";
+ return true;
+}
+
+bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node)
+{
+ TInfoSinkBase& out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ switch (node->getOp())
+ {
+ case EOpComma:
+ out << "comma";
+ break;
+ case EOpAssign:
+ out << "move second child to first child";
+ break;
+ case EOpInitialize:
+ out << "initialize first child with second child";
+ break;
+ case EOpAddAssign:
+ out << "add second child into first child";
+ break;
+ case EOpSubAssign:
+ out << "subtract second child into first child";
+ break;
+ case EOpMulAssign:
+ out << "multiply second child into first child";
+ break;
+ case EOpVectorTimesMatrixAssign:
+ out << "matrix mult second child into first child";
+ break;
+ case EOpVectorTimesScalarAssign:
+ out << "vector scale second child into first child";
+ break;
+ case EOpMatrixTimesScalarAssign:
+ out << "matrix scale second child into first child";
+ break;
+ case EOpMatrixTimesMatrixAssign:
+ out << "matrix mult second child into first child";
+ break;
+ case EOpDivAssign:
+ out << "divide second child into first child";
+ break;
+ case EOpIModAssign:
+ out << "modulo second child into first child";
+ break;
+ case EOpBitShiftLeftAssign:
+ out << "bit-wise shift first child left by second child";
+ break;
+ case EOpBitShiftRightAssign:
+ out << "bit-wise shift first child right by second child";
+ break;
+ case EOpBitwiseAndAssign:
+ out << "bit-wise and second child into first child";
+ break;
+ case EOpBitwiseXorAssign:
+ out << "bit-wise xor second child into first child";
+ break;
+ case EOpBitwiseOrAssign:
+ out << "bit-wise or second child into first child";
+ break;
+
+ case EOpIndexDirect:
+ out << "direct index";
+ break;
+ case EOpIndexIndirect:
+ out << "indirect index";
+ break;
+ case EOpIndexDirectStruct:
+ out << "direct index for structure";
+ break;
+ case EOpIndexDirectInterfaceBlock:
+ out << "direct index for interface block";
+ break;
+
+ case EOpAdd:
+ out << "add";
+ break;
+ case EOpSub:
+ out << "subtract";
+ break;
+ case EOpMul:
+ out << "component-wise multiply";
+ break;
+ case EOpDiv:
+ out << "divide";
+ break;
+ case EOpIMod:
+ out << "modulo";
+ break;
+ case EOpBitShiftLeft:
+ out << "bit-wise shift left";
+ break;
+ case EOpBitShiftRight:
+ out << "bit-wise shift right";
+ break;
+ case EOpBitwiseAnd:
+ out << "bit-wise and";
+ break;
+ case EOpBitwiseXor:
+ out << "bit-wise xor";
+ break;
+ case EOpBitwiseOr:
+ out << "bit-wise or";
+ break;
+
+ case EOpEqual:
+ out << "Compare Equal";
+ break;
+ case EOpNotEqual:
+ out << "Compare Not Equal";
+ break;
+ case EOpLessThan:
+ out << "Compare Less Than";
+ break;
+ case EOpGreaterThan:
+ out << "Compare Greater Than";
+ break;
+ case EOpLessThanEqual:
+ out << "Compare Less Than or Equal";
+ break;
+ case EOpGreaterThanEqual:
+ out << "Compare Greater Than or Equal";
+ break;
+
+ case EOpVectorTimesScalar:
+ out << "vector-scale";
+ break;
+ case EOpVectorTimesMatrix:
+ out << "vector-times-matrix";
+ break;
+ case EOpMatrixTimesVector:
+ out << "matrix-times-vector";
+ break;
+ case EOpMatrixTimesScalar:
+ out << "matrix-scale";
+ break;
+ case EOpMatrixTimesMatrix:
+ out << "matrix-multiply";
+ break;
+
+ case EOpLogicalOr:
+ out << "logical-or";
+ break;
+ case EOpLogicalXor:
+ out << "logical-xor";
+ break;
+ case EOpLogicalAnd:
+ out << "logical-and";
+ break;
+ default:
+ out << "<unknown op>";
+ }
+
+ out << " (" << node->getCompleteString() << ")";
+
+ out << "\n";
+
+ // Special handling for direct indexes. Because constant
+ // unions are not aware they are struct indexes, treat them
+ // here where we have that contextual knowledge.
+ if (node->getOp() == EOpIndexDirectStruct ||
+ node->getOp() == EOpIndexDirectInterfaceBlock)
+ {
+ mDepth++;
+ node->getLeft()->traverse(this);
+ mDepth--;
+
+ TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion();
+ ASSERT(intermConstantUnion);
+
+ OutputTreeText(out, intermConstantUnion, mDepth + 1);
+
+ // The following code finds the field name from the constant union
+ const TConstantUnion *constantUnion = intermConstantUnion->getUnionArrayPointer();
+ const TStructure *structure = node->getLeft()->getType().getStruct();
+ const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
+ ASSERT(structure || interfaceBlock);
+
+ const TFieldList &fields = structure ? structure->fields() : interfaceBlock->fields();
+
+ const TField *field = fields[constantUnion->getIConst()];
+
+ out << constantUnion->getIConst() << " (field '" << field->name() << "')";
+
+ return false;
+ }
+
+ return true;
+}
+
+bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node)
+{
+ TInfoSinkBase& out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ switch (node->getOp())
+ {
+ case EOpNegative: out << "Negate value"; break;
+ case EOpPositive: out << "Positive sign"; break;
+ case EOpVectorLogicalNot:
+ case EOpLogicalNot: out << "Negate conditional"; break;
+ case EOpBitwiseNot: out << "bit-wise not"; break;
+
+ case EOpPostIncrement: out << "Post-Increment"; break;
+ case EOpPostDecrement: out << "Post-Decrement"; break;
+ case EOpPreIncrement: out << "Pre-Increment"; break;
+ case EOpPreDecrement: out << "Pre-Decrement"; break;
+
+ case EOpRadians: out << "radians"; break;
+ case EOpDegrees: out << "degrees"; break;
+ case EOpSin: out << "sine"; break;
+ case EOpCos: out << "cosine"; break;
+ case EOpTan: out << "tangent"; break;
+ case EOpAsin: out << "arc sine"; break;
+ case EOpAcos: out << "arc cosine"; break;
+ case EOpAtan: out << "arc tangent"; break;
+
+ case EOpSinh: out << "hyperbolic sine"; break;
+ case EOpCosh: out << "hyperbolic cosine"; break;
+ case EOpTanh: out << "hyperbolic tangent"; break;
+ case EOpAsinh: out << "arc hyperbolic sine"; break;
+ case EOpAcosh: out << "arc hyperbolic cosine"; break;
+ case EOpAtanh: out << "arc hyperbolic tangent"; break;
+
+ case EOpExp: out << "exp"; break;
+ case EOpLog: out << "log"; break;
+ case EOpExp2: out << "exp2"; break;
+ case EOpLog2: out << "log2"; break;
+ case EOpSqrt: out << "sqrt"; break;
+ case EOpInverseSqrt: out << "inverse sqrt"; break;
+
+ case EOpAbs: out << "Absolute value"; break;
+ case EOpSign: out << "Sign"; break;
+ case EOpFloor: out << "Floor"; break;
+ case EOpTrunc: out << "Truncate"; break;
+ case EOpRound: out << "Round"; break;
+ case EOpRoundEven: out << "Round half even"; break;
+ case EOpCeil: out << "Ceiling"; break;
+ case EOpFract: out << "Fraction"; break;
+ case EOpIsNan: out << "Is not a number"; break;
+ case EOpIsInf: out << "Is infinity"; break;
+
+ case EOpFloatBitsToInt: out << "float bits to int"; break;
+ case EOpFloatBitsToUint: out << "float bits to uint"; break;
+ case EOpIntBitsToFloat: out << "int bits to float"; break;
+ case EOpUintBitsToFloat: out << "uint bits to float"; break;
+
+ case EOpPackSnorm2x16: out << "pack Snorm 2x16"; break;
+ case EOpPackUnorm2x16: out << "pack Unorm 2x16"; break;
+ case EOpPackHalf2x16: out << "pack half 2x16"; break;
+
+ case EOpUnpackSnorm2x16: out << "unpack Snorm 2x16"; break;
+ case EOpUnpackUnorm2x16: out << "unpack Unorm 2x16"; break;
+ case EOpUnpackHalf2x16: out << "unpack half 2x16"; break;
+
+ case EOpLength: out << "length"; break;
+ case EOpNormalize: out << "normalize"; break;
+ // case EOpDPdx: out << "dPdx"; break;
+ // case EOpDPdy: out << "dPdy"; break;
+ // case EOpFwidth: out << "fwidth"; break;
+
+ case EOpDeterminant: out << "determinant"; break;
+ case EOpTranspose: out << "transpose"; break;
+ case EOpInverse: out << "inverse"; break;
+
+ case EOpAny: out << "any"; break;
+ case EOpAll: out << "all"; break;
+
+ default:
+ out.prefix(EPrefixError);
+ out << "Bad unary op";
+ }
+
+ out << " (" << node->getCompleteString() << ")";
+
+ out << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
+{
+ TInfoSinkBase &out = sink;
+ OutputTreeText(out, node, mDepth);
+ OutputFunction(out, "Function Definition", node->getFunctionSymbolInfo());
+ out << "\n";
+ return true;
+}
+
+bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ if (node->getOp() == EOpNull)
+ {
+ out.prefix(EPrefixError);
+ out << "node is still EOpNull!\n";
+ return true;
+ }
+
+
+ switch (node->getOp())
+ {
+ case EOpFunctionCall:
+ OutputFunction(out, "Function Call", node->getFunctionSymbolInfo());
+ break;
+ case EOpParameters: out << "Function Parameters: "; break;
+ case EOpPrototype:
+ OutputFunction(out, "Function Prototype", node->getFunctionSymbolInfo());
+ break;
+
+ case EOpConstructFloat: out << "Construct float"; break;
+ case EOpConstructVec2: out << "Construct vec2"; break;
+ case EOpConstructVec3: out << "Construct vec3"; break;
+ case EOpConstructVec4: out << "Construct vec4"; break;
+ case EOpConstructBool: out << "Construct bool"; break;
+ case EOpConstructBVec2: out << "Construct bvec2"; break;
+ case EOpConstructBVec3: out << "Construct bvec3"; break;
+ case EOpConstructBVec4: out << "Construct bvec4"; break;
+ case EOpConstructInt: out << "Construct int"; break;
+ case EOpConstructIVec2: out << "Construct ivec2"; break;
+ case EOpConstructIVec3: out << "Construct ivec3"; break;
+ case EOpConstructIVec4: out << "Construct ivec4"; break;
+ case EOpConstructUInt: out << "Construct uint"; break;
+ case EOpConstructUVec2: out << "Construct uvec2"; break;
+ case EOpConstructUVec3: out << "Construct uvec3"; break;
+ case EOpConstructUVec4: out << "Construct uvec4"; break;
+ case EOpConstructMat2: out << "Construct mat2"; break;
+ case EOpConstructMat2x3: out << "Construct mat2x3"; break;
+ case EOpConstructMat2x4: out << "Construct mat2x4"; break;
+ case EOpConstructMat3x2: out << "Construct mat3x2"; break;
+ case EOpConstructMat3: out << "Construct mat3"; break;
+ case EOpConstructMat3x4: out << "Construct mat3x4"; break;
+ case EOpConstructMat4x2: out << "Construct mat4x2"; break;
+ case EOpConstructMat4x3: out << "Construct mat4x3"; break;
+ case EOpConstructMat4: out << "Construct mat4"; break;
+ case EOpConstructStruct: out << "Construct structure"; break;
+
+ case EOpLessThan: out << "Compare Less Than"; break;
+ case EOpGreaterThan: out << "Compare Greater Than"; break;
+ case EOpLessThanEqual: out << "Compare Less Than or Equal"; break;
+ case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break;
+ case EOpVectorEqual: out << "Equal"; break;
+ case EOpVectorNotEqual: out << "NotEqual"; break;
+
+ case EOpMod: out << "mod"; break;
+ case EOpModf: out << "modf"; break;
+ case EOpPow: out << "pow"; break;
+
+ case EOpAtan: out << "arc tangent"; break;
+
+ case EOpMin: out << "min"; break;
+ case EOpMax: out << "max"; break;
+ case EOpClamp: out << "clamp"; break;
+ case EOpMix: out << "mix"; break;
+ case EOpStep: out << "step"; break;
+ case EOpSmoothStep: out << "smoothstep"; break;
+
+ case EOpDistance: out << "distance"; break;
+ case EOpDot: out << "dot-product"; break;
+ case EOpCross: out << "cross-product"; break;
+ case EOpFaceForward: out << "face-forward"; break;
+ case EOpReflect: out << "reflect"; break;
+ case EOpRefract: out << "refract"; break;
+ case EOpMul: out << "component-wise multiply"; break;
+
+ case EOpOuterProduct: out << "outer product"; break;
+
+ case EOpInvariantDeclaration: out << "Invariant Declaration: "; break;
+
+ default:
+ out.prefix(EPrefixError);
+ out << "Bad aggregation op";
+ }
+
+ if (node->getOp() != EOpParameters)
+ out << " (" << node->getCompleteString() << ")";
+
+ out << "\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+ out << "Code block\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+ out << "Declaration\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ out << "Ternary selection";
+ out << " (" << node->getCompleteString() << ")\n";
+
+ ++mDepth;
+
+ OutputTreeText(sink, node, mDepth);
+ out << "Condition\n";
+ node->getCondition()->traverse(this);
+
+ OutputTreeText(sink, node, mDepth);
+ if (node->getTrueExpression())
+ {
+ out << "true case\n";
+ node->getTrueExpression()->traverse(this);
+ }
+ if (node->getFalseExpression())
+ {
+ OutputTreeText(sink, node, mDepth);
+ out << "false case\n";
+ node->getFalseExpression()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ out << "If test\n";
+
+ ++mDepth;
+
+ OutputTreeText(sink, node, mDepth);
+ out << "Condition\n";
+ node->getCondition()->traverse(this);
+
+ OutputTreeText(sink, node, mDepth);
+ if (node->getTrueBlock())
+ {
+ out << "true case\n";
+ node->getTrueBlock()->traverse(this);
+ }
+ else
+ {
+ out << "true case is null\n";
+ }
+
+ if (node->getFalseBlock())
+ {
+ OutputTreeText(sink, node, mDepth);
+ out << "false case\n";
+ node->getFalseBlock()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ out << "Switch\n";
+
+ return true;
+}
+
+bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ if (node->getCondition() == nullptr)
+ {
+ out << "Default\n";
+ }
+ else
+ {
+ out << "Case\n";
+ }
+
+ return true;
+}
+
+void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node)
+{
+ TInfoSinkBase &out = sink;
+
+ size_t size = node->getType().getObjectSize();
+
+ for (size_t i = 0; i < size; i++)
+ {
+ OutputTreeText(out, node, mDepth);
+ switch (node->getUnionArrayPointer()[i].getType())
+ {
+ case EbtBool:
+ if (node->getUnionArrayPointer()[i].getBConst())
+ out << "true";
+ else
+ out << "false";
+
+ out << " (" << "const bool" << ")";
+ out << "\n";
+ break;
+ case EbtFloat:
+ out << node->getUnionArrayPointer()[i].getFConst();
+ out << " (const float)\n";
+ break;
+ case EbtInt:
+ out << node->getUnionArrayPointer()[i].getIConst();
+ out << " (const int)\n";
+ break;
+ case EbtUInt:
+ out << node->getUnionArrayPointer()[i].getUConst();
+ out << " (const uint)\n";
+ break;
+ default:
+ out.message(EPrefixInternalError, node->getLine(), "Unknown constant");
+ break;
+ }
+ }
+}
+
+bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ out << "Loop with condition ";
+ if (node->getType() == ELoopDoWhile)
+ out << "not ";
+ out << "tested first\n";
+
+ ++mDepth;
+
+ OutputTreeText(sink, node, mDepth);
+ if (node->getCondition())
+ {
+ out << "Loop Condition\n";
+ node->getCondition()->traverse(this);
+ }
+ else
+ {
+ out << "No loop condition\n";
+ }
+
+ OutputTreeText(sink, node, mDepth);
+ if (node->getBody())
+ {
+ out << "Loop Body\n";
+ node->getBody()->traverse(this);
+ }
+ else
+ {
+ out << "No loop body\n";
+ }
+
+ if (node->getExpression())
+ {
+ OutputTreeText(sink, node, mDepth);
+ out << "Loop Terminal Expression\n";
+ node->getExpression()->traverse(this);
+ }
+
+ --mDepth;
+
+ return false;
+}
+
+bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
+{
+ TInfoSinkBase &out = sink;
+
+ OutputTreeText(out, node, mDepth);
+
+ switch (node->getFlowOp())
+ {
+ case EOpKill: out << "Branch: Kill"; break;
+ case EOpBreak: out << "Branch: Break"; break;
+ case EOpContinue: out << "Branch: Continue"; break;
+ case EOpReturn: out << "Branch: Return"; break;
+ default: out << "Branch: Unknown Branch"; break;
+ }
+
+ if (node->getExpression())
+ {
+ out << " with expression\n";
+ ++mDepth;
+ node->getExpression()->traverse(this);
+ --mDepth;
+ }
+ else
+ {
+ out << "\n";
+ }
+
+ return false;
+}
+
+//
+// This function is the one to call externally to start the traversal.
+// Individual functions can be initialized to 0 to skip processing of that
+// type of node. Its children will still be processed.
+//
+void TIntermediate::outputTree(TIntermNode *root, TInfoSinkBase &infoSink)
+{
+ TOutputTraverser it(infoSink);
+
+ ASSERT(root);
+
+ root->traverse(&it);
+}
+
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/length_limits.h b/gfx/angle/src/compiler/translator/length_limits.h
new file mode 100755
index 000000000..607157f4a
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/length_limits.h
@@ -0,0 +1,26 @@
+//
+// Copyright (c) 2011-2014 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+//
+// length_limits.h
+//
+
+#ifndef COMPILER_TRANSLATOR_LENGTHLIMITS_H_
+#define COMPILER_TRANSLATOR_LENGTHLIMITS_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+// These constants are factored out from the rest of the headers to
+// make it easier to reference them from the compiler sources.
+
+namespace sh
+{
+
+size_t GetGlobalMaxTokenSize(ShShaderSpec spec);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_LENGTHLIMITS_H_
diff --git a/gfx/angle/src/compiler/translator/util.cpp b/gfx/angle/src/compiler/translator/util.cpp
new file mode 100755
index 000000000..89f237ceb
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/util.cpp
@@ -0,0 +1,620 @@
+//
+// Copyright (c) 2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/translator/util.h"
+
+#include <limits>
+
+#include "compiler/preprocessor/numeric_lex.h"
+#include "compiler/translator/SymbolTable.h"
+#include "common/utilities.h"
+
+bool strtof_clamp(const std::string &str, float *value)
+{
+ bool success = pp::numeric_lex_float(str, value);
+ if (!success)
+ *value = std::numeric_limits<float>::max();
+ return success;
+}
+
+bool atoi_clamp(const char *str, unsigned int *value)
+{
+ bool success = pp::numeric_lex_int(str, value);
+ if (!success)
+ *value = std::numeric_limits<unsigned int>::max();
+ return success;
+}
+
+namespace sh
+{
+
+GLenum GLVariableType(const TType &type)
+{
+ if (type.getBasicType() == EbtFloat)
+ {
+ if (type.isScalar())
+ {
+ return GL_FLOAT;
+ }
+ else if (type.isVector())
+ {
+ switch (type.getNominalSize())
+ {
+ case 2: return GL_FLOAT_VEC2;
+ case 3: return GL_FLOAT_VEC3;
+ case 4: return GL_FLOAT_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else if (type.isMatrix())
+ {
+ switch (type.getCols())
+ {
+ case 2:
+ switch (type.getRows())
+ {
+ case 2: return GL_FLOAT_MAT2;
+ case 3: return GL_FLOAT_MAT2x3;
+ case 4: return GL_FLOAT_MAT2x4;
+ default: UNREACHABLE();
+ }
+
+ case 3:
+ switch (type.getRows())
+ {
+ case 2: return GL_FLOAT_MAT3x2;
+ case 3: return GL_FLOAT_MAT3;
+ case 4: return GL_FLOAT_MAT3x4;
+ default: UNREACHABLE();
+ }
+
+ case 4:
+ switch (type.getRows())
+ {
+ case 2: return GL_FLOAT_MAT4x2;
+ case 3: return GL_FLOAT_MAT4x3;
+ case 4: return GL_FLOAT_MAT4;
+ default: UNREACHABLE();
+ }
+
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtInt)
+ {
+ if (type.isScalar())
+ {
+ return GL_INT;
+ }
+ else if (type.isVector())
+ {
+ switch (type.getNominalSize())
+ {
+ case 2: return GL_INT_VEC2;
+ case 3: return GL_INT_VEC3;
+ case 4: return GL_INT_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtUInt)
+ {
+ if (type.isScalar())
+ {
+ return GL_UNSIGNED_INT;
+ }
+ else if (type.isVector())
+ {
+ switch (type.getNominalSize())
+ {
+ case 2: return GL_UNSIGNED_INT_VEC2;
+ case 3: return GL_UNSIGNED_INT_VEC3;
+ case 4: return GL_UNSIGNED_INT_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+ else if (type.getBasicType() == EbtBool)
+ {
+ if (type.isScalar())
+ {
+ return GL_BOOL;
+ }
+ else if (type.isVector())
+ {
+ switch (type.getNominalSize())
+ {
+ case 2: return GL_BOOL_VEC2;
+ case 3: return GL_BOOL_VEC3;
+ case 4: return GL_BOOL_VEC4;
+ default: UNREACHABLE();
+ }
+ }
+ else UNREACHABLE();
+ }
+
+ switch (type.getBasicType())
+ {
+ case EbtSampler2D: return GL_SAMPLER_2D;
+ case EbtSampler3D: return GL_SAMPLER_3D;
+ case EbtSamplerCube: return GL_SAMPLER_CUBE;
+ case EbtSamplerExternalOES: return GL_SAMPLER_EXTERNAL_OES;
+ case EbtSampler2DRect: return GL_SAMPLER_2D_RECT_ARB;
+ case EbtSampler2DArray: return GL_SAMPLER_2D_ARRAY;
+ case EbtISampler2D: return GL_INT_SAMPLER_2D;
+ case EbtISampler3D: return GL_INT_SAMPLER_3D;
+ case EbtISamplerCube: return GL_INT_SAMPLER_CUBE;
+ case EbtISampler2DArray: return GL_INT_SAMPLER_2D_ARRAY;
+ case EbtUSampler2D: return GL_UNSIGNED_INT_SAMPLER_2D;
+ case EbtUSampler3D: return GL_UNSIGNED_INT_SAMPLER_3D;
+ case EbtUSamplerCube: return GL_UNSIGNED_INT_SAMPLER_CUBE;
+ case EbtUSampler2DArray: return GL_UNSIGNED_INT_SAMPLER_2D_ARRAY;
+ case EbtSampler2DShadow: return GL_SAMPLER_2D_SHADOW;
+ case EbtSamplerCubeShadow: return GL_SAMPLER_CUBE_SHADOW;
+ case EbtSampler2DArrayShadow: return GL_SAMPLER_2D_ARRAY_SHADOW;
+ case EbtImage2D:
+ return GL_IMAGE_2D;
+ case EbtIImage2D:
+ return GL_INT_IMAGE_2D;
+ case EbtUImage2D:
+ return GL_UNSIGNED_INT_IMAGE_2D;
+ case EbtImage2DArray:
+ return GL_IMAGE_2D_ARRAY;
+ case EbtIImage2DArray:
+ return GL_INT_IMAGE_2D_ARRAY;
+ case EbtUImage2DArray:
+ return GL_UNSIGNED_INT_IMAGE_2D_ARRAY;
+ case EbtImage3D:
+ return GL_IMAGE_3D;
+ case EbtIImage3D:
+ return GL_INT_IMAGE_3D;
+ case EbtUImage3D:
+ return GL_UNSIGNED_INT_IMAGE_3D;
+ case EbtImageCube:
+ return GL_IMAGE_CUBE;
+ case EbtIImageCube:
+ return GL_INT_IMAGE_CUBE;
+ case EbtUImageCube:
+ return GL_UNSIGNED_INT_IMAGE_CUBE;
+ default: UNREACHABLE();
+ }
+
+ return GL_NONE;
+}
+
+GLenum GLVariablePrecision(const TType &type)
+{
+ if (type.getBasicType() == EbtFloat)
+ {
+ switch (type.getPrecision())
+ {
+ case EbpHigh:
+ return GL_HIGH_FLOAT;
+ case EbpMedium:
+ return GL_MEDIUM_FLOAT;
+ case EbpLow:
+ return GL_LOW_FLOAT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default:
+ UNREACHABLE();
+ }
+ }
+ else if (type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt)
+ {
+ switch (type.getPrecision())
+ {
+ case EbpHigh:
+ return GL_HIGH_INT;
+ case EbpMedium:
+ return GL_MEDIUM_INT;
+ case EbpLow:
+ return GL_LOW_INT;
+ case EbpUndefined:
+ // Should be defined as the default precision by the parser
+ default:
+ UNREACHABLE();
+ }
+ }
+
+ // Other types (boolean, sampler) don't have a precision
+ return GL_NONE;
+}
+
+TString ArrayString(const TType &type)
+{
+ if (!type.isArray())
+ {
+ return "";
+ }
+
+ return "[" + str(type.getArraySize()) + "]";
+}
+
+bool IsVaryingOut(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqVaryingOut:
+ case EvqSmoothOut:
+ case EvqFlatOut:
+ case EvqCentroidOut:
+ case EvqVertexOut:
+ return true;
+
+ default: break;
+ }
+
+ return false;
+}
+
+bool IsVaryingIn(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqVaryingIn:
+ case EvqSmoothIn:
+ case EvqFlatIn:
+ case EvqCentroidIn:
+ case EvqFragmentIn:
+ return true;
+
+ default: break;
+ }
+
+ return false;
+}
+
+bool IsVarying(TQualifier qualifier)
+{
+ return IsVaryingIn(qualifier) || IsVaryingOut(qualifier);
+}
+
+InterpolationType GetInterpolationType(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqFlatIn:
+ case EvqFlatOut:
+ return INTERPOLATION_FLAT;
+
+ case EvqSmoothIn:
+ case EvqSmoothOut:
+ case EvqVertexOut:
+ case EvqFragmentIn:
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ return INTERPOLATION_SMOOTH;
+
+ case EvqCentroidIn:
+ case EvqCentroidOut:
+ return INTERPOLATION_CENTROID;
+
+ default: UNREACHABLE();
+ return INTERPOLATION_SMOOTH;
+ }
+}
+
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var)
+{
+ switch (var.type)
+ {
+ case GL_BOOL:
+ return TType(EbtBool);
+ case GL_BOOL_VEC2:
+ return TType(EbtBool, 2);
+ case GL_BOOL_VEC3:
+ return TType(EbtBool, 3);
+ case GL_BOOL_VEC4:
+ return TType(EbtBool, 4);
+ case GL_FLOAT:
+ return TType(EbtFloat);
+ case GL_FLOAT_VEC2:
+ return TType(EbtFloat, 2);
+ case GL_FLOAT_VEC3:
+ return TType(EbtFloat, 3);
+ case GL_FLOAT_VEC4:
+ return TType(EbtFloat, 4);
+ case GL_FLOAT_MAT2:
+ return TType(EbtFloat, 2, 2);
+ case GL_FLOAT_MAT3:
+ return TType(EbtFloat, 3, 3);
+ case GL_FLOAT_MAT4:
+ return TType(EbtFloat, 4, 4);
+ case GL_FLOAT_MAT2x3:
+ return TType(EbtFloat, 2, 3);
+ case GL_FLOAT_MAT2x4:
+ return TType(EbtFloat, 2, 4);
+ case GL_FLOAT_MAT3x2:
+ return TType(EbtFloat, 3, 2);
+ case GL_FLOAT_MAT3x4:
+ return TType(EbtFloat, 3, 4);
+ case GL_FLOAT_MAT4x2:
+ return TType(EbtFloat, 4, 2);
+ case GL_FLOAT_MAT4x3:
+ return TType(EbtFloat, 4, 3);
+ case GL_INT:
+ return TType(EbtInt);
+ case GL_INT_VEC2:
+ return TType(EbtInt, 2);
+ case GL_INT_VEC3:
+ return TType(EbtInt, 3);
+ case GL_INT_VEC4:
+ return TType(EbtInt, 4);
+ case GL_UNSIGNED_INT:
+ return TType(EbtUInt);
+ case GL_UNSIGNED_INT_VEC2:
+ return TType(EbtUInt, 2);
+ case GL_UNSIGNED_INT_VEC3:
+ return TType(EbtUInt, 3);
+ case GL_UNSIGNED_INT_VEC4:
+ return TType(EbtUInt, 4);
+ default:
+ UNREACHABLE();
+ return TType();
+ }
+}
+
+TOperator TypeToConstructorOperator(const TType &type)
+{
+ switch (type.getBasicType())
+ {
+ case EbtFloat:
+ if (type.isMatrix())
+ {
+ switch (type.getCols())
+ {
+ case 2:
+ switch (type.getRows())
+ {
+ case 2:
+ return EOpConstructMat2;
+ case 3:
+ return EOpConstructMat2x3;
+ case 4:
+ return EOpConstructMat2x4;
+ default:
+ break;
+ }
+ break;
+
+ case 3:
+ switch (type.getRows())
+ {
+ case 2:
+ return EOpConstructMat3x2;
+ case 3:
+ return EOpConstructMat3;
+ case 4:
+ return EOpConstructMat3x4;
+ default:
+ break;
+ }
+ break;
+
+ case 4:
+ switch (type.getRows())
+ {
+ case 2:
+ return EOpConstructMat4x2;
+ case 3:
+ return EOpConstructMat4x3;
+ case 4:
+ return EOpConstructMat4;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (type.getNominalSize())
+ {
+ case 1:
+ return EOpConstructFloat;
+ case 2:
+ return EOpConstructVec2;
+ case 3:
+ return EOpConstructVec3;
+ case 4:
+ return EOpConstructVec4;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case EbtInt:
+ switch (type.getNominalSize())
+ {
+ case 1:
+ return EOpConstructInt;
+ case 2:
+ return EOpConstructIVec2;
+ case 3:
+ return EOpConstructIVec3;
+ case 4:
+ return EOpConstructIVec4;
+ default:
+ break;
+ }
+ break;
+
+ case EbtUInt:
+ switch (type.getNominalSize())
+ {
+ case 1:
+ return EOpConstructUInt;
+ case 2:
+ return EOpConstructUVec2;
+ case 3:
+ return EOpConstructUVec3;
+ case 4:
+ return EOpConstructUVec4;
+ default:
+ break;
+ }
+ break;
+
+ case EbtBool:
+ switch (type.getNominalSize())
+ {
+ case 1:
+ return EOpConstructBool;
+ case 2:
+ return EOpConstructBVec2;
+ case 3:
+ return EOpConstructBVec3;
+ case 4:
+ return EOpConstructBVec4;
+ default:
+ break;
+ }
+ break;
+
+ case EbtStruct:
+ return EOpConstructStruct;
+
+ default:
+ break;
+ }
+
+ return EOpNull;
+}
+
+GetVariableTraverser::GetVariableTraverser(const TSymbolTable &symbolTable)
+ : mSymbolTable(symbolTable)
+{
+}
+
+template void GetVariableTraverser::setTypeSpecificInfo(
+ const TType &type, const TString& name, InterfaceBlockField *variable);
+template void GetVariableTraverser::setTypeSpecificInfo(
+ const TType &type, const TString& name, ShaderVariable *variable);
+template void GetVariableTraverser::setTypeSpecificInfo(
+ const TType &type, const TString& name, Uniform *variable);
+
+template<>
+void GetVariableTraverser::setTypeSpecificInfo(
+ const TType &type, const TString& name, Varying *variable)
+{
+ ASSERT(variable);
+ switch (type.getQualifier())
+ {
+ case EvqVaryingIn:
+ case EvqVaryingOut:
+ case EvqVertexOut:
+ case EvqSmoothOut:
+ case EvqFlatOut:
+ case EvqCentroidOut:
+ if (mSymbolTable.isVaryingInvariant(std::string(name.c_str())) || type.isInvariant())
+ {
+ variable->isInvariant = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ variable->interpolation = GetInterpolationType(type.getQualifier());
+}
+
+template <typename VarT>
+void GetVariableTraverser::traverse(const TType &type,
+ const TString &name,
+ std::vector<VarT> *output)
+{
+ const TStructure *structure = type.getStruct();
+
+ VarT variable;
+ variable.name = name.c_str();
+ variable.arraySize = type.getArraySize();
+
+ if (!structure)
+ {
+ variable.type = GLVariableType(type);
+ variable.precision = GLVariablePrecision(type);
+ }
+ else
+ {
+ // Note: this enum value is not exposed outside ANGLE
+ variable.type = GL_STRUCT_ANGLEX;
+ variable.structName = structure->name().c_str();
+
+ const TFieldList &fields = structure->fields();
+
+ for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
+ {
+ TField *field = fields[fieldIndex];
+ traverse(*field->type(), field->name(), &variable.fields);
+ }
+ }
+ setTypeSpecificInfo(type, name, &variable);
+ visitVariable(&variable);
+
+ ASSERT(output);
+ output->push_back(variable);
+}
+
+template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<InterfaceBlockField> *);
+template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<ShaderVariable> *);
+template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Uniform> *);
+template void GetVariableTraverser::traverse(const TType &, const TString &, std::vector<Varying> *);
+
+// GLSL ES 1.0.17 4.6.1 The Invariant Qualifier
+bool CanBeInvariantESSL1(TQualifier qualifier)
+{
+ return IsVaryingIn(qualifier) || IsVaryingOut(qualifier) ||
+ IsBuiltinOutputVariable(qualifier) ||
+ (IsBuiltinFragmentInputVariable(qualifier) && qualifier != EvqFrontFacing);
+}
+
+// GLSL ES 3.00 Revision 6, 4.6.1 The Invariant Qualifier
+// GLSL ES 3.10 Revision 4, 4.8.1 The Invariant Qualifier
+bool CanBeInvariantESSL3OrGreater(TQualifier qualifier)
+{
+ return IsVaryingOut(qualifier) || qualifier == EvqFragmentOut ||
+ IsBuiltinOutputVariable(qualifier);
+}
+
+bool IsBuiltinOutputVariable(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqPosition:
+ case EvqPointSize:
+ case EvqFragDepth:
+ case EvqFragDepthEXT:
+ case EvqFragColor:
+ case EvqSecondaryFragColorEXT:
+ case EvqFragData:
+ case EvqSecondaryFragDataEXT:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool IsBuiltinFragmentInputVariable(TQualifier qualifier)
+{
+ switch (qualifier)
+ {
+ case EvqFragCoord:
+ case EvqPointCoord:
+ case EvqFrontFacing:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+} // namespace sh
diff --git a/gfx/angle/src/compiler/translator/util.h b/gfx/angle/src/compiler/translator/util.h
new file mode 100755
index 000000000..bfad5fb4c
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/util.h
@@ -0,0 +1,73 @@
+//
+// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TRANSLATOR_UTIL_H_
+#define COMPILER_TRANSLATOR_UTIL_H_
+
+#include <stack>
+
+#include "angle_gl.h"
+#include <GLSLANG/ShaderLang.h>
+
+#include "compiler/translator/Operator.h"
+#include "compiler/translator/Types.h"
+
+// strtof_clamp is like strtof but
+// 1. it forces C locale, i.e. forcing '.' as decimal point.
+// 2. it clamps the value to -FLT_MAX or FLT_MAX if overflow happens.
+// Return false if overflow happens.
+bool strtof_clamp(const std::string &str, float *value);
+
+// If overflow happens, clamp the value to UINT_MIN or UINT_MAX.
+// Return false if overflow happens.
+bool atoi_clamp(const char *str, unsigned int *value);
+
+namespace sh
+{
+class TSymbolTable;
+
+GLenum GLVariableType(const TType &type);
+GLenum GLVariablePrecision(const TType &type);
+bool IsVaryingIn(TQualifier qualifier);
+bool IsVaryingOut(TQualifier qualifier);
+bool IsVarying(TQualifier qualifier);
+InterpolationType GetInterpolationType(TQualifier qualifier);
+TString ArrayString(const TType &type);
+
+TType GetShaderVariableBasicType(const sh::ShaderVariable &var);
+
+TOperator TypeToConstructorOperator(const TType &type);
+
+class GetVariableTraverser : angle::NonCopyable
+{
+ public:
+ GetVariableTraverser(const TSymbolTable &symbolTable);
+ virtual ~GetVariableTraverser() {}
+
+ template <typename VarT>
+ void traverse(const TType &type, const TString &name, std::vector<VarT> *output);
+
+ protected:
+ // May be overloaded
+ virtual void visitVariable(ShaderVariable *newVar) {}
+
+ private:
+ // Helper function called by traverse() to fill specific fields
+ // for attributes/varyings/uniforms.
+ template <typename VarT>
+ void setTypeSpecificInfo(
+ const TType &type, const TString &name, VarT *variable) {}
+
+ const TSymbolTable &mSymbolTable;
+};
+
+bool IsBuiltinOutputVariable(TQualifier qualifier);
+bool IsBuiltinFragmentInputVariable(TQualifier qualifier);
+bool CanBeInvariantESSL1(TQualifier qualifier);
+bool CanBeInvariantESSL3OrGreater(TQualifier qualifier);
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_UTIL_H_