diff options
Diffstat (limited to 'Source/ThirdParty/ANGLE/src/compiler')
296 files changed, 52975 insertions, 20965 deletions
diff --git a/Source/ThirdParty/ANGLE/src/compiler/BaseTypes.h b/Source/ThirdParty/ANGLE/src/compiler/BaseTypes.h deleted file mode 100644 index 1631f4f77..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/BaseTypes.h +++ /dev/null @@ -1,148 +0,0 @@ -// -// 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 _BASICTYPES_INCLUDED_ -#define _BASICTYPES_INCLUDED_ - -// -// Precision qualifiers -// -enum TPrecision -{ - // These need to be kept sorted - EbpUndefined, - EbpLow, - EbpMedium, - EbpHigh -}; - -inline const char* getPrecisionString(TPrecision p) -{ - switch(p) - { - case EbpHigh: return "highp"; break; - case EbpMedium: return "mediump"; break; - case EbpLow: return "lowp"; break; - default: return "mediump"; break; // Safest fallback - } -} - -// -// Basic type. Arrays, vectors, etc., are orthogonal to this. -// -enum TBasicType -{ - EbtVoid, - EbtFloat, - EbtInt, - EbtBool, - EbtGuardSamplerBegin, // non type: see implementation of IsSampler() - EbtSampler2D, - EbtSamplerCube, - EbtSamplerExternalOES, // Only valid if OES_EGL_image_external exists. - EbtSampler2DRect, // Only valid if GL_ARB_texture_rectangle exists. - EbtGuardSamplerEnd, // non type: see implementation of IsSampler() - EbtStruct, - EbtAddress, // should be deprecated?? - EbtInvariant // used as a type when qualifying a previously declared variable as being invariant -}; - -inline const char* getBasicString(TBasicType t) -{ - switch (t) - { - case EbtVoid: return "void"; break; - case EbtFloat: return "float"; break; - case EbtInt: return "int"; break; - case EbtBool: return "bool"; break; - case EbtSampler2D: return "sampler2D"; break; - case EbtSamplerCube: return "samplerCube"; break; - case EbtSamplerExternalOES: return "samplerExternalOES"; break; - case EbtSampler2DRect: return "sampler2DRect"; break; - case EbtStruct: return "structure"; break; - default: return "unknown type"; - } -} - -inline bool IsSampler(TBasicType type) -{ - return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd; -} - -// -// 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 - EvqInvariantVaryingIn, // readonly, fragment shaders only - EvqInvariantVaryingOut, // vertex shaders only read/write - EvqUniform, // Readonly, vertex and fragment - - // parameters - EvqIn, - EvqOut, - EvqInOut, - EvqConstReadOnly, - - // 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, - - // end of list - EvqLast -}; - -// -// This is just for debug print out, carried along with the definitions above. -// -inline const char* getQualifierString(TQualifier q) -{ - switch(q) - { - case EvqTemporary: return "Temporary"; break; - case EvqGlobal: return "Global"; break; - case EvqConst: return "const"; break; - case EvqConstReadOnly: return "const"; break; - case EvqAttribute: return "attribute"; break; - case EvqVaryingIn: return "varying"; break; - case EvqVaryingOut: return "varying"; break; - case EvqInvariantVaryingIn: return "invariant varying"; break; - case EvqInvariantVaryingOut:return "invariant varying"; break; - case EvqUniform: return "uniform"; break; - case EvqIn: return "in"; break; - case EvqOut: return "out"; break; - case EvqInOut: return "inout"; break; - case EvqPosition: return "Position"; break; - case EvqPointSize: return "PointSize"; break; - case EvqFragCoord: return "FragCoord"; break; - case EvqFrontFacing: return "FrontFacing"; break; - case EvqFragColor: return "FragColor"; break; - case EvqFragData: return "FragData"; break; - case EvqFragDepth: return "FragDepth"; break; - default: return "unknown qualifier"; - } -} - -#endif // _BASICTYPES_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.cpp b/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.cpp deleted file mode 100644 index 1c4b25f13..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// -// 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 "compiler/BuiltInFunctionEmulator.h" - -#include "compiler/SymbolTable.h" - -namespace { - -// we use macros here instead of function definitions to work around more GLSL -// compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are -// problematic because if the argument has side-effects they will be repeatedly -// evaluated. This is unlikely to show up in real shaders, but is something to -// consider. -const char* kFunctionEmulationVertexSource[] = { - "#error no emulation for cos(float)", - "#error no emulation for cos(vec2)", - "#error no emulation for cos(vec3)", - "#error no emulation for cos(vec4)", - - "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))", - "#error no emulation for distance(vec2, vec2)", - "#error no emulation for distance(vec3, vec3)", - "#error no emulation for distance(vec4, vec4)", - - "#define webgl_dot_emu(x, y) ((x) * (y))", - "#error no emulation for dot(vec2, vec2)", - "#error no emulation for dot(vec3, vec3)", - "#error no emulation for dot(vec4, vec4)", - - "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", - "#error no emulation for length(vec2)", - "#error no emulation for length(vec3)", - "#error no emulation for length(vec4)", - - "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", - "#error no emulation for normalize(vec2)", - "#error no emulation for normalize(vec3)", - "#error no emulation for normalize(vec4)", - - "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", - "#error no emulation for reflect(vec2, vec2)", - "#error no emulation for reflect(vec3, vec3)", - "#error no emulation for reflect(vec4, vec4)" -}; - -const char* kFunctionEmulationFragmentSource[] = { - "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }", - "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }", - "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }", - "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }", - - "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))", - "#error no emulation for distance(vec2, vec2)", - "#error no emulation for distance(vec3, vec3)", - "#error no emulation for distance(vec4, vec4)", - - "#define webgl_dot_emu(x, y) ((x) * (y))", - "#error no emulation for dot(vec2, vec2)", - "#error no emulation for dot(vec3, vec3)", - "#error no emulation for dot(vec4, vec4)", - - "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", - "#error no emulation for length(vec2)", - "#error no emulation for length(vec3)", - "#error no emulation for length(vec4)", - - "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", - "#error no emulation for normalize(vec2)", - "#error no emulation for normalize(vec3)", - "#error no emulation for normalize(vec4)", - - "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", - "#error no emulation for reflect(vec2, vec2)", - "#error no emulation for reflect(vec3, vec3)", - "#error no emulation for reflect(vec4, vec4)" -}; - -const bool kFunctionEmulationVertexMask[] = { -#if defined(__APPLE__) - // Work around ATI driver bugs in Mac. - false, // TFunctionCos1 - false, // TFunctionCos2 - false, // TFunctionCos3 - false, // TFunctionCos4 - true, // TFunctionDistance1_1 - false, // TFunctionDistance2_2 - false, // TFunctionDistance3_3 - false, // TFunctionDistance4_4 - true, // TFunctionDot1_1 - false, // TFunctionDot2_2 - false, // TFunctionDot3_3 - false, // TFunctionDot4_4 - true, // TFunctionLength1 - false, // TFunctionLength2 - false, // TFunctionLength3 - false, // TFunctionLength4 - true, // TFunctionNormalize1 - false, // TFunctionNormalize2 - false, // TFunctionNormalize3 - false, // TFunctionNormalize4 - true, // TFunctionReflect1_1 - false, // TFunctionReflect2_2 - false, // TFunctionReflect3_3 - false, // TFunctionReflect4_4 -#else - // Work around D3D driver bug in Win. - false, // TFunctionCos1 - false, // TFunctionCos2 - false, // TFunctionCos3 - false, // TFunctionCos4 - false, // TFunctionDistance1_1 - false, // TFunctionDistance2_2 - false, // TFunctionDistance3_3 - false, // TFunctionDistance4_4 - false, // TFunctionDot1_1 - false, // TFunctionDot2_2 - false, // TFunctionDot3_3 - false, // TFunctionDot4_4 - false, // TFunctionLength1 - false, // TFunctionLength2 - false, // TFunctionLength3 - false, // TFunctionLength4 - false, // TFunctionNormalize1 - false, // TFunctionNormalize2 - false, // TFunctionNormalize3 - false, // TFunctionNormalize4 - false, // TFunctionReflect1_1 - false, // TFunctionReflect2_2 - false, // TFunctionReflect3_3 - false, // TFunctionReflect4_4 -#endif - false // TFunctionUnknown -}; - -const bool kFunctionEmulationFragmentMask[] = { -#if defined(__APPLE__) - // Work around ATI driver bugs in Mac. - true, // TFunctionCos1 - true, // TFunctionCos2 - true, // TFunctionCos3 - true, // TFunctionCos4 - true, // TFunctionDistance1_1 - false, // TFunctionDistance2_2 - false, // TFunctionDistance3_3 - false, // TFunctionDistance4_4 - true, // TFunctionDot1_1 - false, // TFunctionDot2_2 - false, // TFunctionDot3_3 - false, // TFunctionDot4_4 - true, // TFunctionLength1 - false, // TFunctionLength2 - false, // TFunctionLength3 - false, // TFunctionLength4 - true, // TFunctionNormalize1 - false, // TFunctionNormalize2 - false, // TFunctionNormalize3 - false, // TFunctionNormalize4 - true, // TFunctionReflect1_1 - false, // TFunctionReflect2_2 - false, // TFunctionReflect3_3 - false, // TFunctionReflect4_4 -#else - // Work around D3D driver bug in Win. - false, // TFunctionCos1 - false, // TFunctionCos2 - false, // TFunctionCos3 - false, // TFunctionCos4 - false, // TFunctionDistance1_1 - false, // TFunctionDistance2_2 - false, // TFunctionDistance3_3 - false, // TFunctionDistance4_4 - false, // TFunctionDot1_1 - false, // TFunctionDot2_2 - false, // TFunctionDot3_3 - false, // TFunctionDot4_4 - false, // TFunctionLength1 - false, // TFunctionLength2 - false, // TFunctionLength3 - false, // TFunctionLength4 - false, // TFunctionNormalize1 - false, // TFunctionNormalize2 - false, // TFunctionNormalize3 - false, // TFunctionNormalize4 - false, // TFunctionReflect1_1 - false, // TFunctionReflect2_2 - false, // TFunctionReflect3_3 - false, // TFunctionReflect4_4 -#endif - false // TFunctionUnknown -}; - -class BuiltInFunctionEmulationMarker : public TIntermTraverser { -public: - BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator) - : mEmulator(emulator) - { - } - - virtual bool visitUnary(Visit visit, TIntermUnary* node) - { - if (visit == PreVisit) { - bool needToEmulate = mEmulator.SetFunctionCalled( - node->getOp(), node->getOperand()->getType()); - if (needToEmulate) - node->setUseEmulatedFunction(); - } - return true; - } - - virtual bool visitAggregate(Visit visit, TIntermAggregate* node) - { - 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 EOpMul: - break; - default: - return true; - }; - const TIntermSequence& sequence = node->getSequence(); - // Right now we only handle built-in functions with two parameters. - if (sequence.size() != 2) - return true; - TIntermTyped* param1 = sequence[0]->getAsTyped(); - TIntermTyped* param2 = sequence[1]->getAsTyped(); - if (!param1 || !param2) - return true; - bool needToEmulate = mEmulator.SetFunctionCalled( - node->getOp(), param1->getType(), param2->getType()); - if (needToEmulate) - node->setUseEmulatedFunction(); - } - return true; - } - -private: - BuiltInFunctionEmulator& mEmulator; -}; - -} // anonymous namepsace - -BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType) -{ - if (shaderType == SH_FRAGMENT_SHADER) { - mFunctionMask = kFunctionEmulationFragmentMask; - mFunctionSource = kFunctionEmulationFragmentSource; - } else { - mFunctionMask = kFunctionEmulationVertexMask; - mFunctionSource = kFunctionEmulationVertexSource; - } -} - -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param) -{ - TBuiltInFunction function = IdentifyFunction(op, param); - return SetFunctionCalled(function); -} - -bool BuiltInFunctionEmulator::SetFunctionCalled( - TOperator op, const TType& param1, const TType& param2) -{ - TBuiltInFunction function = IdentifyFunction(op, param1, param2); - return SetFunctionCalled(function); -} - -bool BuiltInFunctionEmulator::SetFunctionCalled( - BuiltInFunctionEmulator::TBuiltInFunction function) { - if (function == TFunctionUnknown || mFunctionMask[function] == false) - return false; - for (size_t i = 0; i < mFunctions.size(); ++i) { - if (mFunctions[i] == function) - return true; - } - mFunctions.push_back(function); - return true; -} - -void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition( - TInfoSinkBase& out, bool withPrecision) const -{ - if (mFunctions.size() == 0) - return; - out << "// BEGIN: Generated code for built-in function emulation\n\n"; - if (withPrecision) { - out << "#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 { - out << "#define webgl_emu_precision\n\n"; - } - for (size_t i = 0; i < mFunctions.size(); ++i) { - out << mFunctionSource[mFunctions[i]] << "\n\n"; - } - out << "// END: Generated code for built-in function emulation\n\n"; -} - -BuiltInFunctionEmulator::TBuiltInFunction -BuiltInFunctionEmulator::IdentifyFunction( - TOperator op, const TType& param) -{ - if (param.getNominalSize() > 4) - return TFunctionUnknown; - unsigned int function = TFunctionUnknown; - switch (op) { - case EOpCos: - function = TFunctionCos1; - break; - case EOpLength: - function = TFunctionLength1; - break; - case EOpNormalize: - function = TFunctionNormalize1; - break; - default: - break; - } - if (function == TFunctionUnknown) - return TFunctionUnknown; - if (param.isVector()) - function += param.getNominalSize() - 1; - return static_cast<TBuiltInFunction>(function); -} - -BuiltInFunctionEmulator::TBuiltInFunction -BuiltInFunctionEmulator::IdentifyFunction( - TOperator op, const TType& param1, const TType& param2) -{ - // Right now for all the emulated functions with two parameters, the two - // parameters have the same type. - if (param1.isVector() != param2.isVector() || - param1.getNominalSize() != param2.getNominalSize() || - param1.getNominalSize() > 4) - return TFunctionUnknown; - - unsigned int function = TFunctionUnknown; - switch (op) { - case EOpDistance: - function = TFunctionDistance1_1; - break; - case EOpDot: - function = TFunctionDot1_1; - break; - case EOpReflect: - function = TFunctionReflect1_1; - break; - default: - break; - } - if (function == TFunctionUnknown) - return TFunctionUnknown; - if (param1.isVector()) - function += param1.getNominalSize() - 1; - return static_cast<TBuiltInFunction>(function); -} - -void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation( - TIntermNode* root) -{ - ASSERT(root); - - 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("; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.h b/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.h deleted file mode 100644 index 0d904f41d..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/BuiltInFunctionEmulator.h +++ /dev/null @@ -1,93 +0,0 @@ -// -// 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 COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_ -#define COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_ - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/InfoSink.h" -#include "compiler/intermediate.h" - -// -// This class decides which built-in functions need to be replaced with the -// emulated ones. -// It's only a workaround for OpenGL driver bugs, and isn't needed in general. -// -class BuiltInFunctionEmulator { -public: - BuiltInFunctionEmulator(ShShaderType shaderType); - // Records that a function is called by the shader and might needs to be - // emulated. If the function's group is not in mFunctionGroupFilter, this - // becomes an 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); - - // Output function emulation definition. This should be before any other - // shader source. - void OutputEmulatedFunctionDefinition(TInfoSinkBase& out, bool withPrecision) const; - - void MarkBuiltInFunctionsForEmulation(TIntermNode* root); - - void Cleanup(); - - // "name(" becomes "webgl_name_emu(". - static TString GetEmulatedFunctionName(const TString& name); - -private: - // - // Built-in functions. - // - enum TBuiltInFunction { - TFunctionCos1 = 0, // float cos(float); - TFunctionCos2, // vec2 cos(vec2); - TFunctionCos3, // vec3 cos(vec3); - TFunctionCos4, // vec4 cos(vec4); - - TFunctionDistance1_1, // float distance(float, float); - TFunctionDistance2_2, // vec2 distance(vec2, vec2); - TFunctionDistance3_3, // vec3 distance(vec3, vec3); - TFunctionDistance4_4, // vec4 distance(vec4, vec4); - - TFunctionDot1_1, // float dot(float, float); - TFunctionDot2_2, // vec2 dot(vec2, vec2); - TFunctionDot3_3, // vec3 dot(vec3, vec3); - TFunctionDot4_4, // vec4 dot(vec4, vec4); - - TFunctionLength1, // float length(float); - TFunctionLength2, // float length(vec2); - TFunctionLength3, // float length(vec3); - TFunctionLength4, // float length(vec4); - - TFunctionNormalize1, // float normalize(float); - TFunctionNormalize2, // vec2 normalize(vec2); - TFunctionNormalize3, // vec3 normalize(vec3); - TFunctionNormalize4, // vec4 normalize(vec4); - - TFunctionReflect1_1, // float reflect(float, float); - TFunctionReflect2_2, // vec2 reflect(vec2, vec2); - TFunctionReflect3_3, // vec3 reflect(vec3, vec3); - TFunctionReflect4_4, // vec4 reflect(vec4, vec4); - - TFunctionUnknown - }; - - TBuiltInFunction IdentifyFunction(TOperator op, const TType& param); - TBuiltInFunction IdentifyFunction( - TOperator op, const TType& param1, const TType& param2); - - bool SetFunctionCalled(TBuiltInFunction function); - - std::vector<TBuiltInFunction> mFunctions; - - const bool* mFunctionMask; // a boolean flag for each function. - const char** mFunctionSource; -}; - -#endif // COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/CodeGen.cpp b/Source/ThirdParty/ANGLE/src/compiler/CodeGen.cpp deleted file mode 100644 index 24f21b876..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/CodeGen.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// -// 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/TranslatorESSL.h" -#include "compiler/TranslatorGLSL.h" -#include "compiler/TranslatorHLSL.h" - -// -// This function must be provided to create the actual -// compile object used by higher level code. It returns -// a subclass of TCompiler. -// -TCompiler* ConstructCompiler( - ShShaderType type, ShShaderSpec spec, ShShaderOutput output) -{ - switch (output) { - case SH_ESSL_OUTPUT: - return new TranslatorESSL(type, spec); - case SH_GLSL_OUTPUT: - return new TranslatorGLSL(type, spec); - case SH_HLSL9_OUTPUT: - case SH_HLSL11_OUTPUT: - return new TranslatorHLSL(type, spec, output); - default: - return NULL; - } -} - -// -// Delete the compiler made by ConstructCompiler -// -void DeleteCompiler(TCompiler* compiler) -{ - delete compiler; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/Compiler.cpp b/Source/ThirdParty/ANGLE/src/compiler/Compiler.cpp deleted file mode 100644 index c2b4ef602..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Compiler.cpp +++ /dev/null @@ -1,452 +0,0 @@ -// -// 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/BuiltInFunctionEmulator.h" -#include "compiler/DetectCallDepth.h" -#include "compiler/ForLoopUnroll.h" -#include "compiler/Initialize.h" -#include "compiler/InitializeGLPosition.h" -#include "compiler/InitializeParseContext.h" -#include "compiler/MapLongVariableNames.h" -#include "compiler/ParseContext.h" -#include "compiler/RenameFunction.h" -#include "compiler/ShHandle.h" -#include "compiler/UnfoldShortCircuitAST.h" -#include "compiler/ValidateLimitations.h" -#include "compiler/VariablePacker.h" -#include "compiler/depgraph/DependencyGraph.h" -#include "compiler/depgraph/DependencyGraphOutput.h" -#include "compiler/timing/RestrictFragmentShaderTiming.h" -#include "compiler/timing/RestrictVertexShaderTiming.h" -#include "third_party/compiler/ArrayBoundsClamper.h" - -bool isWebGLBasedSpec(ShShaderSpec spec) -{ - return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC; -} - -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; -}; -} // namespace - -TShHandleBase::TShHandleBase() { - allocator.push(); - SetGlobalPoolAllocator(&allocator); -} - -TShHandleBase::~TShHandleBase() { - SetGlobalPoolAllocator(NULL); - allocator.popAll(); -} - -TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec) - : shaderType(type), - shaderSpec(spec), - maxUniformVectors(0), - maxVaryingVectors(0), - maxExpressionComplexity(0), - maxCallStackDepth(0), - fragmentPrecisionHigh(false), - clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), - builtInFunctionEmulator(type) -{ - longNameMap = LongNameMap::GetInstance(); -} - -TCompiler::~TCompiler() -{ - ASSERT(longNameMap); - longNameMap->Release(); -} - -bool TCompiler::Init(const ShBuiltInResources& resources) -{ - maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ? - resources.MaxVertexUniformVectors : - resources.MaxFragmentUniformVectors; - maxVaryingVectors = resources.MaxVaryingVectors; - maxExpressionComplexity = resources.MaxExpressionComplexity; - maxCallStackDepth = resources.MaxCallStackDepth; - - 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; -} - -bool TCompiler::compile(const char* const shaderStrings[], - size_t numStrings, - int compileOptions) -{ - TScopedPoolAllocator scopedAlloc(&allocator); - clearResults(); - - if (numStrings == 0) - return true; - - // If compiling for WebGL, validate loop and indexing as well. - if (isWebGLBasedSpec(shaderSpec)) - compileOptions |= SH_VALIDATE_LOOP_INDEXING; - - // First string is path of source file if flag is set. The actual source follows. - const char* sourcePath = NULL; - size_t firstSource = 0; - if (compileOptions & SH_SOURCE_PATH) - { - sourcePath = shaderStrings[0]; - ++firstSource; - } - - TIntermediate intermediate(infoSink); - TParseContext parseContext(symbolTable, extensionBehavior, intermediate, - shaderType, shaderSpec, compileOptions, true, - sourcePath, infoSink); - parseContext.fragmentPrecisionHigh = 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], NULL, &parseContext) == 0) && - (parseContext.treeRoot != NULL); - if (success) { - TIntermNode* root = parseContext.treeRoot; - success = intermediate.postProcess(root); - - if (success) - success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0); - - if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) - success = validateLimitations(root); - - if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) - success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0); - - if (success && shaderSpec == SH_CSS_SHADERS_SPEC) - rewriteCSSShader(root); - - // Unroll for-loop markup needs to happen after validateLimitations pass. - if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) - ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root); - - // Built-in function emulation needs to happen after validateLimitations pass. - if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)) - builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root); - - // Clamping uniform array bounds needs to happen after validateLimitations pass. - if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) - arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); - - // Disallow expressions deemed too complex. - if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) - success = limitExpressionComplexity(root); - - // Call mapLongVariableNames() before collectAttribsUniforms() so in - // collectAttribsUniforms() we already have the mapped symbol names and - // we could composite mapped and original variable names. - // Also, if we hash all the names, then no need to do this for long names. - if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL) - mapLongVariableNames(root); - - if (success && shaderType == SH_VERTEX_SHADER && (compileOptions & SH_INIT_GL_POSITION)) { - InitializeGLPosition initGLPosition; - root->traverse(&initGLPosition); - } - - if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT)) { - UnfoldShortCircuitAST unfoldShortCircuit; - root->traverse(&unfoldShortCircuit); - unfoldShortCircuit.updateTree(); - } - - if (success && (compileOptions & SH_VARIABLES)) { - collectVariables(root); - if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) - success = enforcePackingRestrictions(); - } - - if (success && (compileOptions & SH_INTERMEDIATE_TREE)) - intermediate.outputTree(root); - - if (success && (compileOptions & SH_OBJECT_CODE)) - translate(root); - } - - // Cleanup memory. - intermediate.remove(parseContext.treeRoot); - - return success; -} - -bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) -{ - compileResources = resources; - - assert(symbolTable.isEmpty()); - symbolTable.push(); - - TPublicType integer; - integer.type = EbtInt; - integer.size = 1; - integer.matrix = false; - integer.array = false; - - TPublicType floatingPoint; - floatingPoint.type = EbtFloat; - floatingPoint.size = 1; - floatingPoint.matrix = false; - floatingPoint.array = false; - - TPublicType sampler; - sampler.size = 1; - sampler.matrix = false; - sampler.array = false; - - switch(shaderType) - { - case SH_FRAGMENT_SHADER: - symbolTable.setDefaultPrecision(integer, EbpMedium); - break; - case SH_VERTEX_SHADER: - symbolTable.setDefaultPrecision(integer, EbpHigh); - symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); - break; - default: assert(false && "Language not supported"); - } - // We set defaults for all the sampler types, even those that are - // only available if an extension exists. - for (int samplerType = EbtGuardSamplerBegin + 1; - samplerType < EbtGuardSamplerEnd; ++samplerType) { - sampler.type = static_cast<TBasicType>(samplerType); - symbolTable.setDefaultPrecision(sampler, EbpLow); - } - - InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); - - IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable); - - return true; -} - -void TCompiler::clearResults() -{ - arrayBoundsClamper.Cleanup(); - infoSink.info.erase(); - infoSink.obj.erase(); - infoSink.debug.erase(); - - attribs.clear(); - uniforms.clear(); - varyings.clear(); - - builtInFunctionEmulator.Cleanup(); - - nameMap.clear(); -} - -bool TCompiler::detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth) -{ - DetectCallDepth detect(infoSink, limitCallStackDepth, maxCallStackDepth); - root->traverse(&detect); - switch (detect.detectCallDepth()) { - case DetectCallDepth::kErrorNone: - return true; - case DetectCallDepth::kErrorMissingMain: - infoSink.info.prefix(EPrefixError); - infoSink.info << "Missing main()"; - return false; - case DetectCallDepth::kErrorRecursion: - infoSink.info.prefix(EPrefixError); - infoSink.info << "Function recursion detected"; - return false; - case DetectCallDepth::kErrorMaxDepthExceeded: - infoSink.info.prefix(EPrefixError); - infoSink.info << "Function call stack too deep"; - return false; - default: - UNREACHABLE(); - return false; - } -} - -void TCompiler::rewriteCSSShader(TIntermNode* root) -{ - RenameFunction renamer("main(", "css_main("); - root->traverse(&renamer); -} - -bool TCompiler::validateLimitations(TIntermNode* root) { - ValidateLimitations validate(shaderType, infoSink.info); - root->traverse(&validate); - return validate.numErrors() == 0; -} - -bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph) -{ - if (shaderSpec != SH_WEBGL_SPEC) { - infoSink.info << "Timing restrictions must be enforced under the WebGL spec."; - return false; - } - - if (shaderType == SH_FRAGMENT_SHADER) { - TDependencyGraph graph(root); - - // Output any errors first. - bool success = enforceFragmentShaderTimingRestrictions(graph); - - // Then, output the dependency graph. - if (outputGraph) { - TDependencyGraphOutput output(infoSink.info); - output.outputAllSpanningTrees(graph); - } - - return success; - } - else { - return enforceVertexShaderTimingRestrictions(root); - } -} - -bool TCompiler::limitExpressionComplexity(TIntermNode* root) -{ - TIntermTraverser traverser; - root->traverse(&traverser); - TDependencyGraph graph(root); - - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) - { - TGraphFunctionCall* samplerSymbol = *iter; - TDependencyGraphTraverser graphTraverser; - samplerSymbol->traverse(&graphTraverser); - } - - if (traverser.getMaxDepth() > maxExpressionComplexity) { - infoSink.info << "Expression too complex."; - return false; - } - return true; -} - -bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph) -{ - RestrictFragmentShaderTiming restrictor(infoSink.info); - restrictor.enforceRestrictions(graph); - return restrictor.numErrors() == 0; -} - -bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root) -{ - RestrictVertexShaderTiming restrictor(infoSink.info); - restrictor.enforceRestrictions(root); - return restrictor.numErrors() == 0; -} - -void TCompiler::collectVariables(TIntermNode* root) -{ - CollectVariables collect(attribs, uniforms, varyings, hashFunction); - root->traverse(&collect); -} - -bool TCompiler::enforcePackingRestrictions() -{ - VariablePacker packer; - bool success = packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms); - if (!success) { - infoSink.info.prefix(EPrefixError); - infoSink.info << "too many uniforms"; - return false; - } - - success = packer.CheckVariablesWithinPackingLimits(maxVaryingVectors, varyings); - - if (!success) { - infoSink.info.prefix(EPrefixError); - infoSink.info << "too many varyings"; - return false; - } - - return true; -} - -void TCompiler::mapLongVariableNames(TIntermNode* root) -{ - ASSERT(longNameMap); - MapLongVariableNames map(longNameMap); - root->traverse(&map); -} - -int TCompiler::getMappedNameMaxLength() const -{ - return MAX_SHORTENED_IDENTIFIER_SIZE + 1; -} - -const TExtensionBehavior& TCompiler::getExtensionBehavior() const -{ - return extensionBehavior; -} - -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; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/ConstantUnion.h b/Source/ThirdParty/ANGLE/src/compiler/ConstantUnion.h deleted file mode 100644 index b1e37885f..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ConstantUnion.h +++ /dev/null @@ -1,257 +0,0 @@ -// -// 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 _CONSTANT_UNION_INCLUDED_ -#define _CONSTANT_UNION_INCLUDED_ - -#include <assert.h> - -class ConstantUnion { -public: - POOL_ALLOCATOR_NEW_DELETE(); - ConstantUnion() - { - iConst = 0; - type = EbtVoid; - } - - void setIConst(int i) {iConst = i; type = EbtInt; } - void setFConst(float f) {fConst = f; type = EbtFloat; } - void setBConst(bool b) {bConst = b; type = EbtBool; } - - int getIConst() { return iConst; } - float getFConst() { return fConst; } - bool getBConst() { return bConst; } - int getIConst() const { return iConst; } - float getFConst() const { return fConst; } - bool getBConst() const { return bConst; } - - bool operator==(const int i) const - { - return i == iConst; - } - - bool operator==(const float f) const - { - return f == fConst; - } - - bool operator==(const bool b) const - { - return b == bConst; - } - - bool operator==(const ConstantUnion& constant) const - { - if (constant.type != type) - return false; - - switch (type) { - case EbtInt: - return constant.iConst == iConst; - case EbtFloat: - return constant.fConst == fConst; - case EbtBool: - return constant.bConst == bConst; - default: - return false; - } - } - - bool operator!=(const int i) const - { - return !operator==(i); - } - - bool operator!=(const float f) const - { - return !operator==(f); - } - - bool operator!=(const bool b) const - { - return !operator==(b); - } - - bool operator!=(const ConstantUnion& constant) const - { - return !operator==(constant); - } - - bool operator>(const ConstantUnion& constant) const - { - assert(type == constant.type); - switch (type) { - case EbtInt: - return iConst > constant.iConst; - case EbtFloat: - return fConst > constant.fConst; - default: - return false; // Invalid operation, handled at semantic analysis - } - } - - bool operator<(const ConstantUnion& constant) const - { - assert(type == constant.type); - switch (type) { - case EbtInt: - return iConst < constant.iConst; - case EbtFloat: - return fConst < constant.fConst; - default: - return false; // Invalid operation, handled at semantic analysis - } - } - - ConstantUnion operator+(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; - case EbtFloat: returnValue.setFConst(fConst + constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator-(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; - case EbtFloat: returnValue.setFConst(fConst - constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator*(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; - case EbtFloat: returnValue.setFConst(fConst * constant.fConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator%(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator>>(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator<<(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator&(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator|(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator^(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator&&(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - ConstantUnion operator||(const ConstantUnion& constant) const - { - ConstantUnion returnValue; - assert(type == constant.type); - switch (type) { - case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; - default: assert(false && "Default missing"); - } - - return returnValue; - } - - TBasicType getType() const { return type; } -private: - - union { - int iConst; // used for ivec, scalar ints - bool bConst; // used for bvec, scalar bools - float fConst; // used for vec, mat, scalar floats - } ; - - TBasicType type; -}; - -#endif // _CONSTANT_UNION_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.cpp b/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.cpp deleted file mode 100644 index 60df52c71..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// -// 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 "compiler/DetectCallDepth.h" -#include "compiler/InfoSink.h" - -DetectCallDepth::FunctionNode::FunctionNode(const TString& fname) - : name(fname), - visit(PreVisit) -{ -} - -const TString& DetectCallDepth::FunctionNode::getName() const -{ - return name; -} - -void DetectCallDepth::FunctionNode::addCallee( - DetectCallDepth::FunctionNode* callee) -{ - for (size_t i = 0; i < callees.size(); ++i) { - if (callees[i] == callee) - return; - } - callees.push_back(callee); -} - -int DetectCallDepth::FunctionNode::detectCallDepth(DetectCallDepth* detectCallDepth, int depth) -{ - ASSERT(visit == PreVisit); - ASSERT(detectCallDepth); - - int maxDepth = depth; - visit = InVisit; - for (size_t i = 0; i < callees.size(); ++i) { - switch (callees[i]->visit) { - case InVisit: - // cycle detected, i.e., recursion detected. - return kInfiniteCallDepth; - case PostVisit: - break; - case PreVisit: { - // Check before we recurse so we don't go too depth - if (detectCallDepth->checkExceedsMaxDepth(depth)) - return depth; - int callDepth = callees[i]->detectCallDepth(detectCallDepth, depth + 1); - // Check after we recurse so we can exit immediately and provide info. - if (detectCallDepth->checkExceedsMaxDepth(callDepth)) { - detectCallDepth->getInfoSink().info << "<-" << callees[i]->getName(); - return callDepth; - } - maxDepth = std::max(callDepth, maxDepth); - break; - } - default: - UNREACHABLE(); - break; - } - } - visit = PostVisit; - return maxDepth; -} - -void DetectCallDepth::FunctionNode::reset() -{ - visit = PreVisit; -} - -DetectCallDepth::DetectCallDepth(TInfoSink& infoSink, bool limitCallStackDepth, int maxCallStackDepth) - : TIntermTraverser(true, false, true, false), - currentFunction(NULL), - infoSink(infoSink), - maxDepth(limitCallStackDepth ? maxCallStackDepth : FunctionNode::kInfiniteCallDepth) -{ -} - -DetectCallDepth::~DetectCallDepth() -{ - for (size_t i = 0; i < functions.size(); ++i) - delete functions[i]; -} - -bool DetectCallDepth::visitAggregate(Visit visit, TIntermAggregate* node) -{ - switch (node->getOp()) - { - case EOpPrototype: - // Function declaration. - // Don't add FunctionNode here because node->getName() is the - // unmangled function name. - break; - case EOpFunction: { - // Function definition. - if (visit == PreVisit) { - currentFunction = findFunctionByName(node->getName()); - if (currentFunction == NULL) { - currentFunction = new FunctionNode(node->getName()); - functions.push_back(currentFunction); - } - } else if (visit == PostVisit) { - currentFunction = NULL; - } - break; - } - case EOpFunctionCall: { - // Function call. - if (visit == PreVisit) { - FunctionNode* func = findFunctionByName(node->getName()); - if (func == NULL) { - func = new FunctionNode(node->getName()); - functions.push_back(func); - } - if (currentFunction) - currentFunction->addCallee(func); - } - break; - } - default: - break; - } - return true; -} - -bool DetectCallDepth::checkExceedsMaxDepth(int depth) -{ - return depth >= maxDepth; -} - -void DetectCallDepth::resetFunctionNodes() -{ - for (size_t i = 0; i < functions.size(); ++i) { - functions[i]->reset(); - } -} - -DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepthForFunction(FunctionNode* func) -{ - currentFunction = NULL; - resetFunctionNodes(); - - int maxCallDepth = func->detectCallDepth(this, 1); - - if (maxCallDepth == FunctionNode::kInfiniteCallDepth) - return kErrorRecursion; - - if (maxCallDepth >= maxDepth) - return kErrorMaxDepthExceeded; - - return kErrorNone; -} - -DetectCallDepth::ErrorCode DetectCallDepth::detectCallDepth() -{ - if (maxDepth != FunctionNode::kInfiniteCallDepth) { - // Check all functions because the driver may fail on them - // TODO: Before detectingRecursion, strip unused functions. - for (size_t i = 0; i < functions.size(); ++i) { - ErrorCode error = detectCallDepthForFunction(functions[i]); - if (error != kErrorNone) - return error; - } - } else { - FunctionNode* main = findFunctionByName("main("); - if (main == NULL) - return kErrorMissingMain; - - return detectCallDepthForFunction(main); - } - - return kErrorNone; -} - -DetectCallDepth::FunctionNode* DetectCallDepth::findFunctionByName( - const TString& name) -{ - for (size_t i = 0; i < functions.size(); ++i) { - if (functions[i]->getName() == name) - return functions[i]; - } - return NULL; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.h b/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.h deleted file mode 100644 index 89e85f88f..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DetectCallDepth.h +++ /dev/null @@ -1,80 +0,0 @@ -// -// 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_DETECT_RECURSION_H_ -#define COMPILER_DETECT_RECURSION_H_ - -#include "GLSLANG/ShaderLang.h" - -#include <limits.h> -#include "compiler/intermediate.h" -#include "compiler/VariableInfo.h" - -class TInfoSink; - -// Traverses intermediate tree to detect function recursion. -class DetectCallDepth : public TIntermTraverser { -public: - enum ErrorCode { - kErrorMissingMain, - kErrorRecursion, - kErrorMaxDepthExceeded, - kErrorNone - }; - - DetectCallDepth(TInfoSink& infoSync, bool limitCallStackDepth, int maxCallStackDepth); - ~DetectCallDepth(); - - virtual bool visitAggregate(Visit, TIntermAggregate*); - - bool checkExceedsMaxDepth(int depth); - - ErrorCode detectCallDepth(); - -private: - class FunctionNode { - public: - static const int kInfiniteCallDepth = INT_MAX; - - FunctionNode(const TString& fname); - - const TString& getName() const; - - // If a function is already in the callee list, this becomes a no-op. - void addCallee(FunctionNode* callee); - - // Returns kInifinityCallDepth if recursive function calls are detected. - int detectCallDepth(DetectCallDepth* detectCallDepth, int depth); - - // Reset state. - void reset(); - - private: - // mangled function name is unique. - TString name; - - // functions that are directly called by this function. - TVector<FunctionNode*> callees; - - Visit visit; - }; - - ErrorCode detectCallDepthForFunction(FunctionNode* func); - FunctionNode* findFunctionByName(const TString& name); - void resetFunctionNodes(); - - TInfoSink& getInfoSink() { return infoSink; } - - TVector<FunctionNode*> functions; - FunctionNode* currentFunction; - TInfoSink& infoSink; - int maxDepth; - - DetectCallDepth(const DetectCallDepth&); - void operator=(const DetectCallDepth&); -}; - -#endif // COMPILER_DETECT_RECURSION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.cpp b/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.cpp deleted file mode 100644 index 8cfe49ba2..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// -// 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. -// -// Contains analysis utilities for dealing with HLSL's lack of support for -// the use of intrinsic functions which (implicitly or explicitly) compute -// gradients of functions with discontinuities. -// - -#include "compiler/DetectDiscontinuity.h" - -#include "compiler/ParseContext.h" - -namespace sh -{ -bool DetectLoopDiscontinuity::traverse(TIntermNode *node) -{ - mLoopDepth = 0; - mLoopDiscontinuity = false; - node->traverse(this); - return mLoopDiscontinuity; -} - -bool DetectLoopDiscontinuity::visitLoop(Visit visit, TIntermLoop *loop) -{ - if (visit == PreVisit) - { - ++mLoopDepth; - } - else if (visit == PostVisit) - { - --mLoopDepth; - } - - return true; -} - -bool DetectLoopDiscontinuity::visitBranch(Visit visit, TIntermBranch *node) -{ - if (mLoopDiscontinuity) - { - return false; - } - - if (!mLoopDepth) - { - return true; - } - - switch (node->getFlowOp()) - { - case EOpKill: - break; - case EOpBreak: - case EOpContinue: - case EOpReturn: - mLoopDiscontinuity = true; - break; - default: UNREACHABLE(); - } - - return !mLoopDiscontinuity; -} - -bool DetectLoopDiscontinuity::visitAggregate(Visit visit, TIntermAggregate *node) -{ - return !mLoopDiscontinuity; -} - -bool containsLoopDiscontinuity(TIntermNode *node) -{ - DetectLoopDiscontinuity detectLoopDiscontinuity; - return detectLoopDiscontinuity.traverse(node); -} - -bool DetectGradientOperation::traverse(TIntermNode *node) -{ - mGradientOperation = false; - node->traverse(this); - return mGradientOperation; -} - -bool DetectGradientOperation::visitUnary(Visit visit, TIntermUnary *node) -{ - if (mGradientOperation) - { - return false; - } - - switch (node->getOp()) - { - case EOpDFdx: - case EOpDFdy: - mGradientOperation = true; - default: - break; - } - - return !mGradientOperation; -} - -bool DetectGradientOperation::visitAggregate(Visit visit, TIntermAggregate *node) -{ - if (mGradientOperation) - { - return false; - } - - if (node->getOp() == EOpFunctionCall) - { - if (!node->isUserDefined()) - { - TString name = TFunction::unmangleName(node->getName()); - - if (name == "texture2D" || - name == "texture2DProj" || - name == "textureCube") - { - mGradientOperation = true; - } - } - else - { - // When a user defined function is called, we have to - // conservatively assume it to contain gradient operations - mGradientOperation = true; - } - } - - return !mGradientOperation; -} - -bool containsGradientOperation(TIntermNode *node) -{ - DetectGradientOperation detectGradientOperation; - return detectGradientOperation.traverse(node); -} -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.h b/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.h deleted file mode 100644 index e5520bd5b..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DetectDiscontinuity.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// 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. -// -// Contains analysis utilities for dealing with HLSL's lack of support for -// the use of intrinsic functions which (implicitly or explicitly) compute -// gradients of functions with discontinuities. -// - -#ifndef COMPILER_DETECTDISCONTINUITY_H_ -#define COMPILER_DETECTDISCONTINUITY_H_ - -#include "compiler/intermediate.h" - -namespace sh -{ -// Checks whether a loop can run for a variable number of iterations -class DetectLoopDiscontinuity : public TIntermTraverser -{ - public: - bool traverse(TIntermNode *node); - - protected: - bool visitBranch(Visit visit, TIntermBranch *node); - bool visitLoop(Visit visit, TIntermLoop *loop); - bool visitAggregate(Visit visit, TIntermAggregate *node); - - int mLoopDepth; - bool mLoopDiscontinuity; -}; - -bool containsLoopDiscontinuity(TIntermNode *node); - -// Checks for intrinsic functions which compute gradients -class DetectGradientOperation : public TIntermTraverser -{ - public: - bool traverse(TIntermNode *node); - - protected: - bool visitUnary(Visit visit, TIntermUnary *node); - bool visitAggregate(Visit visit, TIntermAggregate *node); - - bool mGradientOperation; -}; - -bool containsGradientOperation(TIntermNode *node); - -} - -#endif // COMPILER_DETECTDISCONTINUITY_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.cpp b/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.cpp deleted file mode 100644 index d1f6ab3af..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// -// 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/DirectiveHandler.h" - -#include <sstream> - -#include "compiler/debug.h" -#include "compiler/Diagnostics.h" - -static TBehavior getBehavior(const std::string& str) -{ - static const std::string kRequire("require"); - static const std::string kEnable("enable"); - static const std::string kDisable("disable"); - static const std::string 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) - : mExtensionBehavior(extBehavior), - mDiagnostics(diagnostics) -{ -} - -TDirectiveHandler::~TDirectiveHandler() -{ -} - -void TDirectiveHandler::handleError(const pp::SourceLocation& loc, - const std::string& msg) -{ - mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc, msg, "", ""); -} - -void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, - const std::string& name, - const std::string& value) -{ - static const std::string kSTDGL("STDGL"); - static const std::string kOptimize("optimize"); - static const std::string kDebug("debug"); - static const std::string kOn("on"); - static const std::string kOff("off"); - - bool invalidValue = false; - if (name == kSTDGL) - { - // The STDGL pragma is used to reserve pragmas for use by future - // revisions of GLSL. Ignore it. - return; - } - else 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 - { - mDiagnostics.report(pp::Diagnostics::UNRECOGNIZED_PRAGMA, loc, name); - return; - } - - if (invalidValue) - mDiagnostics.writeInfo(pp::Diagnostics::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) -{ - static const std::string kExtAll("all"); - - TBehavior behaviorVal = getBehavior(behavior); - if (behaviorVal == EBhUndefined) - { - mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc, - "behavior", name, "invalid"); - return; - } - - if (name == kExtAll) - { - if (behaviorVal == EBhRequire) - { - mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc, - "extension", name, - "cannot have 'require' behavior"); - } - else if (behaviorVal == EBhEnable) - { - mDiagnostics.writeInfo(pp::Diagnostics::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::ERROR; - switch (behaviorVal) { - case EBhRequire: - severity = pp::Diagnostics::ERROR; - break; - case EBhEnable: - case EBhWarn: - case EBhDisable: - severity = pp::Diagnostics::WARNING; - break; - default: - UNREACHABLE(); - break; - } - mDiagnostics.writeInfo(severity, loc, - "extension", name, "is not supported"); -} - -void TDirectiveHandler::handleVersion(const pp::SourceLocation& loc, - int version) -{ - static const int kVersion = 100; - - if (version != kVersion) - { - std::stringstream stream; - stream << version; - std::string str = stream.str(); - mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc, - "version number", str, "not supported"); - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.h b/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.h deleted file mode 100644 index 95ca59d6f..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/DirectiveHandler.h +++ /dev/null @@ -1,46 +0,0 @@ -// -// 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_DIRECTIVE_HANDLER_H_ -#define COMPILER_DIRECTIVE_HANDLER_H_ - -#include "compiler/ExtensionBehavior.h" -#include "compiler/Pragma.h" -#include "compiler/preprocessor/DirectiveHandlerBase.h" - -class TDiagnostics; - -class TDirectiveHandler : public pp::DirectiveHandler -{ - public: - TDirectiveHandler(TExtensionBehavior& extBehavior, - TDiagnostics& diagnostics); - virtual ~TDirectiveHandler(); - - const TPragma& pragma() const { return mPragma; } - const TExtensionBehavior& extensionBehavior() const { return mExtensionBehavior; } - - virtual void handleError(const pp::SourceLocation& loc, - const std::string& msg); - - virtual void handlePragma(const pp::SourceLocation& loc, - const std::string& name, - const std::string& value); - - virtual void handleExtension(const pp::SourceLocation& loc, - const std::string& name, - const std::string& behavior); - - virtual void handleVersion(const pp::SourceLocation& loc, - int version); - - private: - TPragma mPragma; - TExtensionBehavior& mExtensionBehavior; - TDiagnostics& mDiagnostics; -}; - -#endif // COMPILER_DIRECTIVE_HANDLER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.cpp b/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.cpp deleted file mode 100644 index 27a13eaba..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.cpp +++ /dev/null @@ -1,215 +0,0 @@ -// -// 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/ForLoopUnroll.h" - -namespace { - -class IntegerForLoopUnrollMarker : public TIntermTraverser { -public: - - virtual bool visitLoop(Visit, TIntermLoop* node) - { - // This is called after ValidateLimitations pass, so all the ASSERT - // should never fail. - // See ValidateLimitations::validateForLoopInit(). - ASSERT(node); - ASSERT(node->getType() == ELoopFor); - ASSERT(node->getInit()); - TIntermAggregate* decl = node->getInit()->getAsAggregate(); - ASSERT(decl && decl->getOp() == EOpDeclaration); - TIntermSequence& declSeq = decl->getSequence(); - ASSERT(declSeq.size() == 1); - TIntermBinary* declInit = declSeq[0]->getAsBinaryNode(); - ASSERT(declInit && declInit->getOp() == EOpInitialize); - ASSERT(declInit->getLeft()); - TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode(); - ASSERT(symbol); - TBasicType type = symbol->getBasicType(); - ASSERT(type == EbtInt || type == EbtFloat); - if (type == EbtInt) - node->setUnrollFlag(true); - return true; - } - -}; - -} // anonymous namepsace - -void ForLoopUnroll::FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info) -{ - ASSERT(node->getType() == ELoopFor); - ASSERT(node->getUnrollFlag()); - - TIntermNode* init = node->getInit(); - ASSERT(init != NULL); - TIntermAggregate* decl = init->getAsAggregate(); - ASSERT((decl != NULL) && (decl->getOp() == EOpDeclaration)); - TIntermSequence& declSeq = decl->getSequence(); - ASSERT(declSeq.size() == 1); - TIntermBinary* declInit = declSeq[0]->getAsBinaryNode(); - ASSERT((declInit != NULL) && (declInit->getOp() == EOpInitialize)); - TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode(); - ASSERT(symbol != NULL); - ASSERT(symbol->getBasicType() == EbtInt); - - info.id = symbol->getId(); - - ASSERT(declInit->getRight() != NULL); - TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion(); - ASSERT(initNode != NULL); - - info.initValue = evaluateIntConstant(initNode); - info.currentValue = info.initValue; - - TIntermNode* cond = node->getCondition(); - ASSERT(cond != NULL); - TIntermBinary* binOp = cond->getAsBinaryNode(); - ASSERT(binOp != NULL); - ASSERT(binOp->getRight() != NULL); - ASSERT(binOp->getRight()->getAsConstantUnion() != NULL); - - info.incrementValue = getLoopIncrement(node); - info.stopValue = evaluateIntConstant( - binOp->getRight()->getAsConstantUnion()); - info.op = binOp->getOp(); -} - -void ForLoopUnroll::Step() -{ - ASSERT(mLoopIndexStack.size() > 0); - TLoopIndexInfo& info = mLoopIndexStack[mLoopIndexStack.size() - 1]; - info.currentValue += info.incrementValue; -} - -bool ForLoopUnroll::SatisfiesLoopCondition() -{ - ASSERT(mLoopIndexStack.size() > 0); - TLoopIndexInfo& info = mLoopIndexStack[mLoopIndexStack.size() - 1]; - // Relational operator is one of: > >= < <= == or !=. - switch (info.op) { - case EOpEqual: - return (info.currentValue == info.stopValue); - case EOpNotEqual: - return (info.currentValue != info.stopValue); - case EOpLessThan: - return (info.currentValue < info.stopValue); - case EOpGreaterThan: - return (info.currentValue > info.stopValue); - case EOpLessThanEqual: - return (info.currentValue <= info.stopValue); - case EOpGreaterThanEqual: - return (info.currentValue >= info.stopValue); - default: - UNREACHABLE(); - } - return false; -} - -bool ForLoopUnroll::NeedsToReplaceSymbolWithValue(TIntermSymbol* symbol) -{ - for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin(); - i != mLoopIndexStack.end(); - ++i) { - if (i->id == symbol->getId()) - return true; - } - return false; -} - -int ForLoopUnroll::GetLoopIndexValue(TIntermSymbol* symbol) -{ - for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin(); - i != mLoopIndexStack.end(); - ++i) { - if (i->id == symbol->getId()) - return i->currentValue; - } - UNREACHABLE(); - return false; -} - -void ForLoopUnroll::Push(TLoopIndexInfo& info) -{ - mLoopIndexStack.push_back(info); -} - -void ForLoopUnroll::Pop() -{ - mLoopIndexStack.pop_back(); -} - -// static -void ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling( - TIntermNode* root) -{ - ASSERT(root); - - IntegerForLoopUnrollMarker marker; - root->traverse(&marker); -} - -int ForLoopUnroll::getLoopIncrement(TIntermLoop* node) -{ - TIntermNode* expr = node->getExpression(); - ASSERT(expr != NULL); - // 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 != NULL) { - op = unOp->getOp(); - } else if (binOp != NULL) { - op = binOp->getOp(); - ASSERT(binOp->getRight() != NULL); - incrementNode = binOp->getRight()->getAsConstantUnion(); - ASSERT(incrementNode != NULL); - } - - int increment = 0; - // The operator is one of: ++ -- += -=. - switch (op) { - case EOpPostIncrement: - case EOpPreIncrement: - ASSERT((unOp != NULL) && (binOp == NULL)); - increment = 1; - break; - case EOpPostDecrement: - case EOpPreDecrement: - ASSERT((unOp != NULL) && (binOp == NULL)); - increment = -1; - break; - case EOpAddAssign: - ASSERT((unOp == NULL) && (binOp != NULL)); - increment = evaluateIntConstant(incrementNode); - break; - case EOpSubAssign: - ASSERT((unOp == NULL) && (binOp != NULL)); - increment = - evaluateIntConstant(incrementNode); - break; - default: - ASSERT(false); - } - - return increment; -} - -int ForLoopUnroll::evaluateIntConstant(TIntermConstantUnion* node) -{ - ASSERT((node != NULL) && (node->getUnionArrayPointer() != NULL)); - return node->getIConst(0); -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.h b/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.h deleted file mode 100644 index e800e25b1..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ForLoopUnroll.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// 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/intermediate.h" - -struct TLoopIndexInfo { - int id; - int initValue; - int stopValue; - int incrementValue; - TOperator op; - int currentValue; -}; - -class ForLoopUnroll { -public: - ForLoopUnroll() { } - - void FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info); - - // Update the info.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(TLoopIndexInfo& info); - void Pop(); - - static void MarkForLoopsWithIntegerIndicesForUnrolling(TIntermNode* root); - -private: - int getLoopIncrement(TIntermLoop* node); - - int evaluateIntConstant(TIntermConstantUnion* node); - - TVector<TLoopIndexInfo> mLoopIndexStack; -}; - diff --git a/Source/ThirdParty/ANGLE/src/compiler/Initialize.cpp b/Source/ThirdParty/ANGLE/src/compiler/Initialize.cpp deleted file mode 100644 index 2cdbe17aa..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Initialize.cpp +++ /dev/null @@ -1,564 +0,0 @@ -// -// 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. -// - -// -// Create strings 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/Initialize.h" - -#include "compiler/intermediate.h" - -void InsertBuiltInFunctions(ShShaderType type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable) -{ - TType *float1 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 1); - TType *float2 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 2); - TType *float3 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 3); - TType *float4 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 4); - - TType *int2 = new TType(EbtInt, EbpUndefined, EvqGlobal, 2); - TType *int3 = new TType(EbtInt, EbpUndefined, EvqGlobal, 3); - TType *int4 = new TType(EbtInt, EbpUndefined, EvqGlobal, 4); - - // - // Angle and Trigonometric Functions. - // - symbolTable.insertBuiltIn(float1, "radians", float1); - symbolTable.insertBuiltIn(float2, "radians", float2); - symbolTable.insertBuiltIn(float3, "radians", float3); - symbolTable.insertBuiltIn(float4, "radians", float4); - - symbolTable.insertBuiltIn(float1, "degrees", float1); - symbolTable.insertBuiltIn(float2, "degrees", float2); - symbolTable.insertBuiltIn(float3, "degrees", float3); - symbolTable.insertBuiltIn(float4, "degrees", float4); - - symbolTable.insertBuiltIn(float1, "sin", float1); - symbolTable.insertBuiltIn(float2, "sin", float2); - symbolTable.insertBuiltIn(float3, "sin", float3); - symbolTable.insertBuiltIn(float4, "sin", float4); - - symbolTable.insertBuiltIn(float1, "cos", float1); - symbolTable.insertBuiltIn(float2, "cos", float2); - symbolTable.insertBuiltIn(float3, "cos", float3); - symbolTable.insertBuiltIn(float4, "cos", float4); - - symbolTable.insertBuiltIn(float1, "tan", float1); - symbolTable.insertBuiltIn(float2, "tan", float2); - symbolTable.insertBuiltIn(float3, "tan", float3); - symbolTable.insertBuiltIn(float4, "tan", float4); - - symbolTable.insertBuiltIn(float1, "asin", float1); - symbolTable.insertBuiltIn(float2, "asin", float2); - symbolTable.insertBuiltIn(float3, "asin", float3); - symbolTable.insertBuiltIn(float4, "asin", float4); - - symbolTable.insertBuiltIn(float1, "acos", float1); - symbolTable.insertBuiltIn(float2, "acos", float2); - symbolTable.insertBuiltIn(float3, "acos", float3); - symbolTable.insertBuiltIn(float4, "acos", float4); - - symbolTable.insertBuiltIn(float1, "atan", float1, float1); - symbolTable.insertBuiltIn(float2, "atan", float2, float2); - symbolTable.insertBuiltIn(float3, "atan", float3, float3); - symbolTable.insertBuiltIn(float4, "atan", float4, float4); - - symbolTable.insertBuiltIn(float1, "atan", float1); - symbolTable.insertBuiltIn(float2, "atan", float2); - symbolTable.insertBuiltIn(float3, "atan", float3); - symbolTable.insertBuiltIn(float4, "atan", float4); - - // - // Exponential Functions. - // - symbolTable.insertBuiltIn(float1, "pow", float1, float1); - symbolTable.insertBuiltIn(float2, "pow", float2, float2); - symbolTable.insertBuiltIn(float3, "pow", float3, float3); - symbolTable.insertBuiltIn(float4, "pow", float4, float4); - - symbolTable.insertBuiltIn(float1, "exp", float1); - symbolTable.insertBuiltIn(float2, "exp", float2); - symbolTable.insertBuiltIn(float3, "exp", float3); - symbolTable.insertBuiltIn(float4, "exp", float4); - - symbolTable.insertBuiltIn(float1, "log", float1); - symbolTable.insertBuiltIn(float2, "log", float2); - symbolTable.insertBuiltIn(float3, "log", float3); - symbolTable.insertBuiltIn(float4, "log", float4); - - symbolTable.insertBuiltIn(float1, "exp2", float1); - symbolTable.insertBuiltIn(float2, "exp2", float2); - symbolTable.insertBuiltIn(float3, "exp2", float3); - symbolTable.insertBuiltIn(float4, "exp2", float4); - - symbolTable.insertBuiltIn(float1, "log2", float1); - symbolTable.insertBuiltIn(float2, "log2", float2); - symbolTable.insertBuiltIn(float3, "log2", float3); - symbolTable.insertBuiltIn(float4, "log2", float4); - - symbolTable.insertBuiltIn(float1, "sqrt", float1); - symbolTable.insertBuiltIn(float2, "sqrt", float2); - symbolTable.insertBuiltIn(float3, "sqrt", float3); - symbolTable.insertBuiltIn(float4, "sqrt", float4); - - symbolTable.insertBuiltIn(float1, "inversesqrt", float1); - symbolTable.insertBuiltIn(float2, "inversesqrt", float2); - symbolTable.insertBuiltIn(float3, "inversesqrt", float3); - symbolTable.insertBuiltIn(float4, "inversesqrt", float4); - - // - // Common Functions. - // - symbolTable.insertBuiltIn(float1, "abs", float1); - symbolTable.insertBuiltIn(float2, "abs", float2); - symbolTable.insertBuiltIn(float3, "abs", float3); - symbolTable.insertBuiltIn(float4, "abs", float4); - - symbolTable.insertBuiltIn(float1, "sign", float1); - symbolTable.insertBuiltIn(float2, "sign", float2); - symbolTable.insertBuiltIn(float3, "sign", float3); - symbolTable.insertBuiltIn(float4, "sign", float4); - - symbolTable.insertBuiltIn(float1, "floor", float1); - symbolTable.insertBuiltIn(float2, "floor", float2); - symbolTable.insertBuiltIn(float3, "floor", float3); - symbolTable.insertBuiltIn(float4, "floor", float4); - - symbolTable.insertBuiltIn(float1, "ceil", float1); - symbolTable.insertBuiltIn(float2, "ceil", float2); - symbolTable.insertBuiltIn(float3, "ceil", float3); - symbolTable.insertBuiltIn(float4, "ceil", float4); - - symbolTable.insertBuiltIn(float1, "fract", float1); - symbolTable.insertBuiltIn(float2, "fract", float2); - symbolTable.insertBuiltIn(float3, "fract", float3); - symbolTable.insertBuiltIn(float4, "fract", float4); - - symbolTable.insertBuiltIn(float1, "mod", float1, float1); - symbolTable.insertBuiltIn(float2, "mod", float2, float1); - symbolTable.insertBuiltIn(float3, "mod", float3, float1); - symbolTable.insertBuiltIn(float4, "mod", float4, float1); - symbolTable.insertBuiltIn(float2, "mod", float2, float2); - symbolTable.insertBuiltIn(float3, "mod", float3, float3); - symbolTable.insertBuiltIn(float4, "mod", float4, float4); - - symbolTable.insertBuiltIn(float1, "min", float1, float1); - symbolTable.insertBuiltIn(float2, "min", float2, float1); - symbolTable.insertBuiltIn(float3, "min", float3, float1); - symbolTable.insertBuiltIn(float4, "min", float4, float1); - symbolTable.insertBuiltIn(float2, "min", float2, float2); - symbolTable.insertBuiltIn(float3, "min", float3, float3); - symbolTable.insertBuiltIn(float4, "min", float4, float4); - - symbolTable.insertBuiltIn(float1, "max", float1, float1); - symbolTable.insertBuiltIn(float2, "max", float2, float1); - symbolTable.insertBuiltIn(float3, "max", float3, float1); - symbolTable.insertBuiltIn(float4, "max", float4, float1); - symbolTable.insertBuiltIn(float2, "max", float2, float2); - symbolTable.insertBuiltIn(float3, "max", float3, float3); - symbolTable.insertBuiltIn(float4, "max", float4, float4); - - symbolTable.insertBuiltIn(float1, "clamp", float1, float1, float1); - symbolTable.insertBuiltIn(float2, "clamp", float2, float1, float1); - symbolTable.insertBuiltIn(float3, "clamp", float3, float1, float1); - symbolTable.insertBuiltIn(float4, "clamp", float4, float1, float1); - symbolTable.insertBuiltIn(float2, "clamp", float2, float2, float2); - symbolTable.insertBuiltIn(float3, "clamp", float3, float3, float3); - symbolTable.insertBuiltIn(float4, "clamp", float4, float4, float4); - - symbolTable.insertBuiltIn(float1, "mix", float1, float1, float1); - symbolTable.insertBuiltIn(float2, "mix", float2, float2, float1); - symbolTable.insertBuiltIn(float3, "mix", float3, float3, float1); - symbolTable.insertBuiltIn(float4, "mix", float4, float4, float1); - symbolTable.insertBuiltIn(float2, "mix", float2, float2, float2); - symbolTable.insertBuiltIn(float3, "mix", float3, float3, float3); - symbolTable.insertBuiltIn(float4, "mix", float4, float4, float4); - - symbolTable.insertBuiltIn(float1, "step", float1, float1); - symbolTable.insertBuiltIn(float2, "step", float2, float2); - symbolTable.insertBuiltIn(float3, "step", float3, float3); - symbolTable.insertBuiltIn(float4, "step", float4, float4); - symbolTable.insertBuiltIn(float2, "step", float1, float2); - symbolTable.insertBuiltIn(float3, "step", float1, float3); - symbolTable.insertBuiltIn(float4, "step", float1, float4); - - symbolTable.insertBuiltIn(float1, "smoothstep", float1, float1, float1); - symbolTable.insertBuiltIn(float2, "smoothstep", float2, float2, float2); - symbolTable.insertBuiltIn(float3, "smoothstep", float3, float3, float3); - symbolTable.insertBuiltIn(float4, "smoothstep", float4, float4, float4); - symbolTable.insertBuiltIn(float2, "smoothstep", float1, float1, float2); - symbolTable.insertBuiltIn(float3, "smoothstep", float1, float1, float3); - symbolTable.insertBuiltIn(float4, "smoothstep", float1, float1, float4); - - // - // Geometric Functions. - // - symbolTable.insertBuiltIn(float1, "length", float1); - symbolTable.insertBuiltIn(float1, "length", float2); - symbolTable.insertBuiltIn(float1, "length", float3); - symbolTable.insertBuiltIn(float1, "length", float4); - - symbolTable.insertBuiltIn(float1, "distance", float1, float1); - symbolTable.insertBuiltIn(float1, "distance", float2, float2); - symbolTable.insertBuiltIn(float1, "distance", float3, float3); - symbolTable.insertBuiltIn(float1, "distance", float4, float4); - - symbolTable.insertBuiltIn(float1, "dot", float1, float1); - symbolTable.insertBuiltIn(float1, "dot", float2, float2); - symbolTable.insertBuiltIn(float1, "dot", float3, float3); - symbolTable.insertBuiltIn(float1, "dot", float4, float4); - - symbolTable.insertBuiltIn(float3, "cross", float3, float3); - symbolTable.insertBuiltIn(float1, "normalize", float1); - symbolTable.insertBuiltIn(float2, "normalize", float2); - symbolTable.insertBuiltIn(float3, "normalize", float3); - symbolTable.insertBuiltIn(float4, "normalize", float4); - - symbolTable.insertBuiltIn(float1, "faceforward", float1, float1, float1); - symbolTable.insertBuiltIn(float2, "faceforward", float2, float2, float2); - symbolTable.insertBuiltIn(float3, "faceforward", float3, float3, float3); - symbolTable.insertBuiltIn(float4, "faceforward", float4, float4, float4); - - symbolTable.insertBuiltIn(float1, "reflect", float1, float1); - symbolTable.insertBuiltIn(float2, "reflect", float2, float2); - symbolTable.insertBuiltIn(float3, "reflect", float3, float3); - symbolTable.insertBuiltIn(float4, "reflect", float4, float4); - - symbolTable.insertBuiltIn(float1, "refract", float1, float1, float1); - symbolTable.insertBuiltIn(float2, "refract", float2, float2, float1); - symbolTable.insertBuiltIn(float3, "refract", float3, float3, float1); - symbolTable.insertBuiltIn(float4, "refract", float4, float4, float1); - - TType *mat2 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 2, true); - TType *mat3 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 3, true); - TType *mat4 = new TType(EbtFloat, EbpUndefined, EvqGlobal, 4, true); - - // - // Matrix Functions. - // - symbolTable.insertBuiltIn(mat2, "matrixCompMult", mat2, mat2); - symbolTable.insertBuiltIn(mat3, "matrixCompMult", mat3, mat3); - symbolTable.insertBuiltIn(mat4, "matrixCompMult", mat4, mat4); - - TType *bool1 = new TType(EbtBool, EbpUndefined, EvqGlobal, 1); - TType *bool2 = new TType(EbtBool, EbpUndefined, EvqGlobal, 2); - TType *bool3 = new TType(EbtBool, EbpUndefined, EvqGlobal, 3); - TType *bool4 = new TType(EbtBool, EbpUndefined, EvqGlobal, 4); - - // - // Vector relational functions. - // - symbolTable.insertBuiltIn(bool2, "lessThan", float2, float2); - symbolTable.insertBuiltIn(bool3, "lessThan", float3, float3); - symbolTable.insertBuiltIn(bool4, "lessThan", float4, float4); - - symbolTable.insertBuiltIn(bool2, "lessThan", int2, int2); - symbolTable.insertBuiltIn(bool3, "lessThan", int3, int3); - symbolTable.insertBuiltIn(bool4, "lessThan", int4, int4); - - symbolTable.insertBuiltIn(bool2, "lessThanEqual", float2, float2); - symbolTable.insertBuiltIn(bool3, "lessThanEqual", float3, float3); - symbolTable.insertBuiltIn(bool4, "lessThanEqual", float4, float4); - - symbolTable.insertBuiltIn(bool2, "lessThanEqual", int2, int2); - symbolTable.insertBuiltIn(bool3, "lessThanEqual", int3, int3); - symbolTable.insertBuiltIn(bool4, "lessThanEqual", int4, int4); - - symbolTable.insertBuiltIn(bool2, "greaterThan", float2, float2); - symbolTable.insertBuiltIn(bool3, "greaterThan", float3, float3); - symbolTable.insertBuiltIn(bool4, "greaterThan", float4, float4); - - symbolTable.insertBuiltIn(bool2, "greaterThan", int2, int2); - symbolTable.insertBuiltIn(bool3, "greaterThan", int3, int3); - symbolTable.insertBuiltIn(bool4, "greaterThan", int4, int4); - - symbolTable.insertBuiltIn(bool2, "greaterThanEqual", float2, float2); - symbolTable.insertBuiltIn(bool3, "greaterThanEqual", float3, float3); - symbolTable.insertBuiltIn(bool4, "greaterThanEqual", float4, float4); - - symbolTable.insertBuiltIn(bool2, "greaterThanEqual", int2, int2); - symbolTable.insertBuiltIn(bool3, "greaterThanEqual", int3, int3); - symbolTable.insertBuiltIn(bool4, "greaterThanEqual", int4, int4); - - symbolTable.insertBuiltIn(bool2, "equal", float2, float2); - symbolTable.insertBuiltIn(bool3, "equal", float3, float3); - symbolTable.insertBuiltIn(bool4, "equal", float4, float4); - - symbolTable.insertBuiltIn(bool2, "equal", int2, int2); - symbolTable.insertBuiltIn(bool3, "equal", int3, int3); - symbolTable.insertBuiltIn(bool4, "equal", int4, int4); - - symbolTable.insertBuiltIn(bool2, "equal", bool2, bool2); - symbolTable.insertBuiltIn(bool3, "equal", bool3, bool3); - symbolTable.insertBuiltIn(bool4, "equal", bool4, bool4); - - symbolTable.insertBuiltIn(bool2, "notEqual", float2, float2); - symbolTable.insertBuiltIn(bool3, "notEqual", float3, float3); - symbolTable.insertBuiltIn(bool4, "notEqual", float4, float4); - - symbolTable.insertBuiltIn(bool2, "notEqual", int2, int2); - symbolTable.insertBuiltIn(bool3, "notEqual", int3, int3); - symbolTable.insertBuiltIn(bool4, "notEqual", int4, int4); - - symbolTable.insertBuiltIn(bool2, "notEqual", bool2, bool2); - symbolTable.insertBuiltIn(bool3, "notEqual", bool3, bool3); - symbolTable.insertBuiltIn(bool4, "notEqual", bool4, bool4); - - symbolTable.insertBuiltIn(bool1, "any", bool2); - symbolTable.insertBuiltIn(bool1, "any", bool3); - symbolTable.insertBuiltIn(bool1, "any", bool4); - - symbolTable.insertBuiltIn(bool1, "all", bool2); - symbolTable.insertBuiltIn(bool1, "all", bool3); - symbolTable.insertBuiltIn(bool1, "all", bool4); - - symbolTable.insertBuiltIn(bool2, "not", bool2); - symbolTable.insertBuiltIn(bool3, "not", bool3); - symbolTable.insertBuiltIn(bool4, "not", bool4); - - TType *sampler2D = new TType(EbtSampler2D, EbpUndefined, EvqGlobal, 1); - TType *samplerCube = new TType(EbtSamplerCube, EbpUndefined, EvqGlobal, 1); - - // - // Texture Functions for GLSL ES 1.0 - // - symbolTable.insertBuiltIn(float4, "texture2D", sampler2D, float2); - symbolTable.insertBuiltIn(float4, "texture2DProj", sampler2D, float3); - symbolTable.insertBuiltIn(float4, "texture2DProj", sampler2D, float4); - symbolTable.insertBuiltIn(float4, "textureCube", samplerCube, float3); - - if (resources.OES_EGL_image_external) - { - TType *samplerExternalOES = new TType(EbtSamplerExternalOES, EbpUndefined, EvqGlobal, 1); - - symbolTable.insertBuiltIn(float4, "texture2D", samplerExternalOES, float2); - symbolTable.insertBuiltIn(float4, "texture2DProj", samplerExternalOES, float3); - symbolTable.insertBuiltIn(float4, "texture2DProj", samplerExternalOES, float4); - } - - if (resources.ARB_texture_rectangle) - { - TType *sampler2DRect = new TType(EbtSampler2DRect, EbpUndefined, EvqGlobal, 1); - - symbolTable.insertBuiltIn(float4, "texture2DRect", sampler2DRect, float2); - symbolTable.insertBuiltIn(float4, "texture2DRectProj", sampler2DRect, float3); - symbolTable.insertBuiltIn(float4, "texture2DRectProj", sampler2DRect, float4); - } - - if (type == SH_FRAGMENT_SHADER) - { - symbolTable.insertBuiltIn(float4, "texture2D", sampler2D, float2, float1); - symbolTable.insertBuiltIn(float4, "texture2DProj", sampler2D, float3, float1); - symbolTable.insertBuiltIn(float4, "texture2DProj", sampler2D, float4, float1); - symbolTable.insertBuiltIn(float4, "textureCube", samplerCube, float3, float1); - - if (resources.OES_standard_derivatives) - { - symbolTable.insertBuiltIn(float1, "dFdx", float1); - symbolTable.insertBuiltIn(float2, "dFdx", float2); - symbolTable.insertBuiltIn(float3, "dFdx", float3); - symbolTable.insertBuiltIn(float4, "dFdx", float4); - - symbolTable.insertBuiltIn(float1, "dFdy", float1); - symbolTable.insertBuiltIn(float2, "dFdy", float2); - symbolTable.insertBuiltIn(float3, "dFdy", float3); - symbolTable.insertBuiltIn(float4, "dFdy", float4); - - symbolTable.insertBuiltIn(float1, "fwidth", float1); - symbolTable.insertBuiltIn(float2, "fwidth", float2); - symbolTable.insertBuiltIn(float3, "fwidth", float3); - symbolTable.insertBuiltIn(float4, "fwidth", float4); - } - } - - if(type == SH_VERTEX_SHADER) - { - symbolTable.insertBuiltIn(float4, "texture2DLod", sampler2D, float2, float1); - symbolTable.insertBuiltIn(float4, "texture2DProjLod", sampler2D, float3, float1); - symbolTable.insertBuiltIn(float4, "texture2DProjLod", sampler2D, float4, float1); - symbolTable.insertBuiltIn(float4, "textureCubeLod", samplerCube, float3, float1); - } - - // - // Depth range in window coordinates - // - TFieldList *fields = NewPoolTFieldList(); - TField *near = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("near")); - TField *far = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("far")); - TField *diff = new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1), NewPoolTString("diff")); - 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(), depthRangeStruct, true); - symbolTable.insert(*depthRangeParameters); - TVariable *depthRange = new TVariable(NewPoolTString("gl_DepthRange"), TType(depthRangeStruct)); - depthRange->setQualifier(EvqUniform); - symbolTable.insert(*depthRange); - - // - // Implementation dependent built-in constants. - // - symbolTable.insertConstInt("gl_MaxVertexAttribs", resources.MaxVertexAttribs); - symbolTable.insertConstInt("gl_MaxVertexUniformVectors", resources.MaxVertexUniformVectors); - symbolTable.insertConstInt("gl_MaxVaryingVectors", resources.MaxVaryingVectors); - symbolTable.insertConstInt("gl_MaxVertexTextureImageUnits", resources.MaxVertexTextureImageUnits); - symbolTable.insertConstInt("gl_MaxCombinedTextureImageUnits", resources.MaxCombinedTextureImageUnits); - symbolTable.insertConstInt("gl_MaxTextureImageUnits", resources.MaxTextureImageUnits); - symbolTable.insertConstInt("gl_MaxFragmentUniformVectors", resources.MaxFragmentUniformVectors); - - if (spec != SH_CSS_SHADERS_SPEC) - { - symbolTable.insertConstInt("gl_MaxDrawBuffers", resources.MaxDrawBuffers); - } -} - -void IdentifyBuiltIns(ShShaderType type, ShShaderSpec spec, - const ShBuiltInResources &resources, - TSymbolTable &symbolTable) -{ - // - // First, insert some special built-in variables that are not in - // the built-in header files. - // - switch(type) { - case SH_FRAGMENT_SHADER: - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragCoord"), TType(EbtFloat, EbpMedium, EvqFragCoord, 4))); - symbolTable.insert(*new TVariable(NewPoolTString("gl_FrontFacing"), TType(EbtBool, EbpUndefined, EvqFrontFacing, 1))); - symbolTable.insert(*new TVariable(NewPoolTString("gl_PointCoord"), TType(EbtFloat, EbpMedium, EvqPointCoord, 2))); - - // - // In CSS Shaders, gl_FragColor, gl_FragData, and gl_MaxDrawBuffers are not available. - // Instead, css_MixColor and css_ColorMatrix are available. - // - if (spec != SH_CSS_SHADERS_SPEC) { - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragColor"), TType(EbtFloat, EbpMedium, EvqFragColor, 4))); - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData[gl_MaxDrawBuffers]"), TType(EbtFloat, EbpMedium, EvqFragData, 4))); - if (resources.EXT_frag_depth) { - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragDepthEXT"), TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, EvqFragDepth, 1))); - symbolTable.relateToExtension("gl_FragDepthEXT", "GL_EXT_frag_depth"); - } - } else { - symbolTable.insert(*new TVariable(NewPoolTString("css_MixColor"), TType(EbtFloat, EbpMedium, EvqGlobal, 4))); - symbolTable.insert(*new TVariable(NewPoolTString("css_ColorMatrix"), TType(EbtFloat, EbpMedium, EvqGlobal, 4, true))); - } - - break; - - case SH_VERTEX_SHADER: - symbolTable.insert(*new TVariable(NewPoolTString("gl_Position"), TType(EbtFloat, EbpHigh, EvqPosition, 4))); - symbolTable.insert(*new TVariable(NewPoolTString("gl_PointSize"), TType(EbtFloat, EbpMedium, EvqPointSize, 1))); - break; - - default: assert(false && "Language not supported"); - } - - // - // Next, identify which built-ins from the already loaded headers have - // a mapping to an operator. Those that are not identified as such are - // expected to be resolved through a library of functions, versus as - // operations. - // - symbolTable.relateToOperator("matrixCompMult", EOpMul); - - symbolTable.relateToOperator("equal", EOpVectorEqual); - symbolTable.relateToOperator("notEqual", EOpVectorNotEqual); - symbolTable.relateToOperator("lessThan", EOpLessThan); - symbolTable.relateToOperator("greaterThan", EOpGreaterThan); - symbolTable.relateToOperator("lessThanEqual", EOpLessThanEqual); - symbolTable.relateToOperator("greaterThanEqual", EOpGreaterThanEqual); - - symbolTable.relateToOperator("radians", EOpRadians); - symbolTable.relateToOperator("degrees", EOpDegrees); - symbolTable.relateToOperator("sin", EOpSin); - symbolTable.relateToOperator("cos", EOpCos); - symbolTable.relateToOperator("tan", EOpTan); - symbolTable.relateToOperator("asin", EOpAsin); - symbolTable.relateToOperator("acos", EOpAcos); - symbolTable.relateToOperator("atan", EOpAtan); - - symbolTable.relateToOperator("pow", EOpPow); - symbolTable.relateToOperator("exp2", EOpExp2); - symbolTable.relateToOperator("log", EOpLog); - symbolTable.relateToOperator("exp", EOpExp); - symbolTable.relateToOperator("log2", EOpLog2); - symbolTable.relateToOperator("sqrt", EOpSqrt); - symbolTable.relateToOperator("inversesqrt", EOpInverseSqrt); - - symbolTable.relateToOperator("abs", EOpAbs); - symbolTable.relateToOperator("sign", EOpSign); - symbolTable.relateToOperator("floor", EOpFloor); - symbolTable.relateToOperator("ceil", EOpCeil); - symbolTable.relateToOperator("fract", EOpFract); - symbolTable.relateToOperator("mod", EOpMod); - symbolTable.relateToOperator("min", EOpMin); - symbolTable.relateToOperator("max", EOpMax); - symbolTable.relateToOperator("clamp", EOpClamp); - symbolTable.relateToOperator("mix", EOpMix); - symbolTable.relateToOperator("step", EOpStep); - symbolTable.relateToOperator("smoothstep", EOpSmoothStep); - - symbolTable.relateToOperator("length", EOpLength); - symbolTable.relateToOperator("distance", EOpDistance); - symbolTable.relateToOperator("dot", EOpDot); - symbolTable.relateToOperator("cross", EOpCross); - symbolTable.relateToOperator("normalize", EOpNormalize); - symbolTable.relateToOperator("faceforward", EOpFaceForward); - symbolTable.relateToOperator("reflect", EOpReflect); - symbolTable.relateToOperator("refract", EOpRefract); - - symbolTable.relateToOperator("any", EOpAny); - symbolTable.relateToOperator("all", EOpAll); - symbolTable.relateToOperator("not", EOpVectorLogicalNot); - - // Map language-specific operators. - switch(type) { - case SH_VERTEX_SHADER: - break; - case SH_FRAGMENT_SHADER: - if (resources.OES_standard_derivatives) { - symbolTable.relateToOperator("dFdx", EOpDFdx); - symbolTable.relateToOperator("dFdy", EOpDFdy); - symbolTable.relateToOperator("fwidth", EOpFwidth); - - symbolTable.relateToExtension("dFdx", "GL_OES_standard_derivatives"); - symbolTable.relateToExtension("dFdy", "GL_OES_standard_derivatives"); - symbolTable.relateToExtension("fwidth", "GL_OES_standard_derivatives"); - } - break; - default: break; - } - - // Finally add resource-specific variables. - switch(type) { - case SH_FRAGMENT_SHADER: - if (spec != SH_CSS_SHADERS_SPEC) { - // Set up gl_FragData. The array size. - TType fragData(EbtFloat, EbpMedium, EvqFragData, 4, false, true); - fragData.setArraySize(resources.MaxDrawBuffers); - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); - } - break; - default: break; - } -} - -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.ARB_texture_rectangle) - extBehavior["GL_ARB_texture_rectangle"] = EBhUndefined; - if (resources.EXT_draw_buffers) - extBehavior["GL_EXT_draw_buffers"] = EBhUndefined; - if (resources.EXT_frag_depth) - extBehavior["GL_EXT_frag_depth"] = EBhUndefined; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/Initialize.h b/Source/ThirdParty/ANGLE/src/compiler/Initialize.h deleted file mode 100644 index 4aa13466a..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Initialize.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// 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 _INITIALIZE_INCLUDED_ -#define _INITIALIZE_INCLUDED_ - -#include "compiler/Common.h" -#include "compiler/ShHandle.h" -#include "compiler/SymbolTable.h" - -void InsertBuiltInFunctions(ShShaderType type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &table); - -void IdentifyBuiltIns(ShShaderType type, ShShaderSpec spec, - const ShBuiltInResources& resources, - TSymbolTable& symbolTable); - -void InitExtensionBehavior(const ShBuiltInResources& resources, - TExtensionBehavior& extensionBehavior); - -#endif // _INITIALIZE_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.cpp b/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.cpp deleted file mode 100644 index e0193e39d..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// 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/InitializeGLPosition.h" -#include "compiler/debug.h" - -bool InitializeGLPosition::visitAggregate(Visit visit, TIntermAggregate* node) -{ - bool visitChildren = !mCodeInserted; - switch (node->getOp()) - { - case EOpSequence: break; - case EOpFunction: - { - // Function definition. - ASSERT(visit == PreVisit); - if (node->getName() == "main(") - { - TIntermSequence &sequence = node->getSequence(); - ASSERT((sequence.size() == 1) || (sequence.size() == 2)); - TIntermAggregate *body = NULL; - if (sequence.size() == 1) - { - body = new TIntermAggregate(EOpSequence); - sequence.push_back(body); - } - else - { - body = sequence[1]->getAsAggregate(); - } - ASSERT(body); - insertCode(body->getSequence()); - mCodeInserted = true; - } - break; - } - default: visitChildren = false; break; - } - return visitChildren; -} - -void InitializeGLPosition::insertCode(TIntermSequence& sequence) -{ - TIntermBinary *binary = new TIntermBinary(EOpAssign); - sequence.insert(sequence.begin(), binary); - - TIntermSymbol *left = new TIntermSymbol( - 0, "gl_Position", TType(EbtFloat, EbpUndefined, EvqPosition, 4)); - binary->setLeft(left); - - ConstantUnion *u = new ConstantUnion[4]; - for (int ii = 0; ii < 3; ++ii) - u[ii].setFConst(0.0f); - u[3].setFConst(1.0f); - TIntermConstantUnion *right = new TIntermConstantUnion( - u, TType(EbtFloat, EbpUndefined, EvqConst, 4)); - binary->setRight(right); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.h b/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.h deleted file mode 100644 index 1b11075a1..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeGLPosition.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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_INITIALIZE_GL_POSITION_H_ -#define COMPILER_INITIALIZE_GL_POSITION_H_ - -#include "compiler/intermediate.h" - -class InitializeGLPosition : public TIntermTraverser -{ -public: - InitializeGLPosition() : mCodeInserted(false) { } - -protected: - virtual bool visitBinary(Visit visit, TIntermBinary* node) { return false; } - virtual bool visitUnary(Visit visit, TIntermUnary* node) { return false; } - virtual bool visitSelection(Visit visit, TIntermSelection* node) { return false; } - virtual bool visitLoop(Visit visit, TIntermLoop* node) { return false; } - virtual bool visitBranch(Visit visit, TIntermBranch* node) { return false; } - - virtual bool visitAggregate(Visit visit, TIntermAggregate* node); - -private: - // Insert AST node in the beginning of main() for "gl_Position = vec4(0.0, 0.0, 0.0, 1.0);". - void insertCode(TIntermSequence& sequence); - - bool mCodeInserted; -}; - -#endif // COMPILER_INITIALIZE_GL_POSITION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeParseContext.cpp b/Source/ThirdParty/ANGLE/src/compiler/InitializeParseContext.cpp deleted file mode 100644 index dfab02733..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeParseContext.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// -// 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/InitializeParseContext.h" - -#include "compiler/osinclude.h" - -OS_TLSIndex GlobalParseContextIndex = OS_INVALID_TLS_INDEX; - -bool InitializeParseContextIndex() -{ - assert(GlobalParseContextIndex == OS_INVALID_TLS_INDEX); - - GlobalParseContextIndex = OS_AllocTLSIndex(); - return GlobalParseContextIndex != OS_INVALID_TLS_INDEX; -} - -void FreeParseContextIndex() -{ - assert(GlobalParseContextIndex != OS_INVALID_TLS_INDEX); - - OS_FreeTLSIndex(GlobalParseContextIndex); - GlobalParseContextIndex = OS_INVALID_TLS_INDEX; -} - -void SetGlobalParseContext(TParseContext* context) -{ - assert(GlobalParseContextIndex != OS_INVALID_TLS_INDEX); - OS_SetTLSValue(GlobalParseContextIndex, context); -} - -TParseContext* GetGlobalParseContext() -{ - assert(GlobalParseContextIndex != OS_INVALID_TLS_INDEX); - return static_cast<TParseContext*>(OS_GetTLSValue(GlobalParseContextIndex)); -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/IntermTraverse.cpp b/Source/ThirdParty/ANGLE/src/compiler/IntermTraverse.cpp deleted file mode 100644 index 9a691da71..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/IntermTraverse.cpp +++ /dev/null @@ -1,259 +0,0 @@ -// -// 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/intermediate.h" - -// -// 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. -// -// preVisit, postVisit, and rightToLeft control what order -// nodes are visited in. -// - -// -// Traversal functions for terminals are straighforward.... -// -void TIntermSymbol::traverse(TIntermTraverser *it) -{ - it->visitSymbol(this); -} - -void TIntermConstantUnion::traverse(TIntermTraverser *it) -{ - it->visitConstantUnion(this); -} - -// -// Traverse a binary node. -// -void TIntermBinary::traverse(TIntermTraverser *it) -{ - bool visit = true; - - // - // visit the node before children if pre-visiting. - // - if (it->preVisit) - visit = it->visitBinary(PreVisit, this); - - // - // Visit the children, in the right order. - // - if (visit) - { - it->incrementDepth(this); - - if (it->rightToLeft) - { - if (right) - right->traverse(it); - - if (it->inVisit) - visit = it->visitBinary(InVisit, this); - - if (visit && left) - left->traverse(it); - } - else - { - if (left) - left->traverse(it); - - if (it->inVisit) - visit = it->visitBinary(InVisit, this); - - if (visit && right) - right->traverse(it); - } - - it->decrementDepth(); - } - - // - // Visit the node after the children, if requested and the traversal - // hasn't been cancelled yet. - // - if (visit && it->postVisit) - it->visitBinary(PostVisit, this); -} - -// -// Traverse a unary node. Same comments in binary node apply here. -// -void TIntermUnary::traverse(TIntermTraverser *it) -{ - bool visit = true; - - if (it->preVisit) - visit = it->visitUnary(PreVisit, this); - - if (visit) { - it->incrementDepth(this); - operand->traverse(it); - it->decrementDepth(); - } - - if (visit && it->postVisit) - it->visitUnary(PostVisit, this); -} - -// -// Traverse an aggregate node. Same comments in binary node apply here. -// -void TIntermAggregate::traverse(TIntermTraverser *it) -{ - bool visit = true; - - if (it->preVisit) - visit = it->visitAggregate(PreVisit, this); - - if (visit) - { - it->incrementDepth(this); - - if (it->rightToLeft) - { - for (TIntermSequence::reverse_iterator sit = sequence.rbegin(); sit != sequence.rend(); sit++) - { - (*sit)->traverse(it); - - if (visit && it->inVisit) - { - if (*sit != sequence.front()) - visit = it->visitAggregate(InVisit, this); - } - } - } - else - { - for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) - { - (*sit)->traverse(it); - - if (visit && it->inVisit) - { - if (*sit != sequence.back()) - visit = it->visitAggregate(InVisit, this); - } - } - } - - it->decrementDepth(); - } - - if (visit && it->postVisit) - it->visitAggregate(PostVisit, this); -} - -// -// Traverse a selection node. Same comments in binary node apply here. -// -void TIntermSelection::traverse(TIntermTraverser *it) -{ - bool visit = true; - - if (it->preVisit) - visit = it->visitSelection(PreVisit, this); - - if (visit) { - it->incrementDepth(this); - if (it->rightToLeft) { - if (falseBlock) - falseBlock->traverse(it); - if (trueBlock) - trueBlock->traverse(it); - condition->traverse(it); - } else { - condition->traverse(it); - if (trueBlock) - trueBlock->traverse(it); - if (falseBlock) - falseBlock->traverse(it); - } - it->decrementDepth(); - } - - if (visit && it->postVisit) - it->visitSelection(PostVisit, this); -} - -// -// Traverse a loop node. Same comments in binary node apply here. -// -void TIntermLoop::traverse(TIntermTraverser *it) -{ - bool visit = true; - - if (it->preVisit) - visit = it->visitLoop(PreVisit, this); - - if (visit) - { - it->incrementDepth(this); - - if (it->rightToLeft) - { - if (expr) - expr->traverse(it); - - if (body) - body->traverse(it); - - if (cond) - cond->traverse(it); - - if (init) - init->traverse(it); - } - else - { - if (init) - init->traverse(it); - - if (cond) - cond->traverse(it); - - if (body) - body->traverse(it); - - if (expr) - expr->traverse(it); - } - - it->decrementDepth(); - } - - if (visit && it->postVisit) - it->visitLoop(PostVisit, this); -} - -// -// Traverse a branch node. Same comments in binary node apply here. -// -void TIntermBranch::traverse(TIntermTraverser *it) -{ - bool visit = true; - - if (it->preVisit) - visit = it->visitBranch(PreVisit, this); - - if (visit && expression) { - it->incrementDepth(this); - expression->traverse(it); - it->decrementDepth(); - } - - if (visit && it->postVisit) - it->visitBranch(PostVisit, this); -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/Intermediate.cpp b/Source/ThirdParty/ANGLE/src/compiler/Intermediate.cpp deleted file mode 100644 index 47e4e3951..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Intermediate.cpp +++ /dev/null @@ -1,1500 +0,0 @@ -// -// 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. -// - -// -// Build the intermediate representation. -// - -#include <float.h> -#include <limits.h> -#include <algorithm> - -#include "compiler/HashNames.h" -#include "compiler/localintermediate.h" -#include "compiler/QualifierAlive.h" -#include "compiler/RemoveTree.h" - -bool CompareStructure(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray); - -static TPrecision GetHigherPrecision(TPrecision left, TPrecision right) -{ - return left > right ? left : right; -} - -const char* getOperatorString(TOperator op) -{ - switch (op) { - case EOpInitialize: return "="; - case EOpAssign: return "="; - case EOpAddAssign: return "+="; - case EOpSubAssign: return "-="; - case EOpDivAssign: return "/="; - - // Fall-through. - case EOpMulAssign: - case EOpVectorTimesMatrixAssign: - case EOpVectorTimesScalarAssign: - case EOpMatrixTimesScalarAssign: - case EOpMatrixTimesMatrixAssign: return "*="; - - // Fall-through. - case EOpIndexDirect: - case EOpIndexIndirect: return "[]"; - - case EOpIndexDirectStruct: return "."; - case EOpVectorSwizzle: return "."; - case EOpAdd: return "+"; - case EOpSub: return "-"; - case EOpMul: return "*"; - case EOpDiv: return "/"; - case EOpMod: UNIMPLEMENTED(); break; - case EOpEqual: return "=="; - case EOpNotEqual: return "!="; - case EOpLessThan: return "<"; - case EOpGreaterThan: return ">"; - case EOpLessThanEqual: return "<="; - case EOpGreaterThanEqual: return ">="; - - // Fall-through. - case EOpVectorTimesScalar: - case EOpVectorTimesMatrix: - case EOpMatrixTimesVector: - case EOpMatrixTimesScalar: - case EOpMatrixTimesMatrix: return "*"; - - case EOpLogicalOr: return "||"; - case EOpLogicalXor: return "^^"; - case EOpLogicalAnd: return "&&"; - case EOpNegative: return "-"; - case EOpVectorLogicalNot: return "not"; - case EOpLogicalNot: return "!"; - case EOpPostIncrement: return "++"; - case EOpPostDecrement: return "--"; - case EOpPreIncrement: return "++"; - case EOpPreDecrement: return "--"; - - // Fall-through. - case EOpConvIntToBool: - case EOpConvFloatToBool: return "bool"; - - // Fall-through. - case EOpConvBoolToFloat: - case EOpConvIntToFloat: return "float"; - - // Fall-through. - case EOpConvFloatToInt: - case EOpConvBoolToInt: return "int"; - - 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 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 EOpCeil: return "ceil"; - case EOpFract: return "fract"; - case EOpLength: return "length"; - case EOpNormalize: return "normalize"; - case EOpDFdx: return "dFdx"; - case EOpDFdy: return "dFdy"; - case EOpFwidth: return "fwidth"; - case EOpAny: return "any"; - case EOpAll: return "all"; - - default: break; - } - return ""; -} - -//////////////////////////////////////////////////////////////////////////// -// -// 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 with a new parent that does a binary operation on the nodes. -// -// Returns the added node. -// -TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& line, TSymbolTable& symbolTable) -{ - switch (op) { - case EOpEqual: - case EOpNotEqual: - if (left->isArray()) - return 0; - break; - case EOpLessThan: - case EOpGreaterThan: - case EOpLessThanEqual: - case EOpGreaterThanEqual: - if (left->isMatrix() || left->isArray() || left->isVector() || left->getBasicType() == EbtStruct) { - return 0; - } - break; - case EOpLogicalOr: - case EOpLogicalXor: - case EOpLogicalAnd: - if (left->getBasicType() != EbtBool || left->isMatrix() || left->isArray() || left->isVector()) { - return 0; - } - break; - case EOpAdd: - case EOpSub: - case EOpDiv: - case EOpMul: - if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool) - return 0; - default: break; - } - - // - // First try converting the children to compatible types. - // - if (left->getType().getStruct() && right->getType().getStruct()) { - if (left->getType() != right->getType()) - return 0; - } else { - TIntermTyped* child = addConversion(op, left->getType(), right); - if (child) - right = child; - else { - child = addConversion(op, right->getType(), left); - if (child) - left = child; - else - return 0; - } - } - - // - // Need a new node holding things together then. Make - // one and promote it to the right type. - // - TIntermBinary* node = new TIntermBinary(op); - node->setLine(line); - - node->setLeft(left); - node->setRight(right); - if (!node->promote(infoSink)) - return 0; - - // - // See if we can fold constants. - // - TIntermTyped* typedReturnNode = 0; - TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion(); - TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion(); - if (leftTempConstant && rightTempConstant) { - typedReturnNode = leftTempConstant->fold(node->getOp(), rightTempConstant, infoSink); - - if (typedReturnNode) - return typedReturnNode; - } - - return node; -} - -// -// Connect two nodes through an assignment. -// -// Returns the added node. -// -TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& line) -{ - // - // Like adding binary math, except the conversion can only go - // from right to left. - // - TIntermBinary* node = new TIntermBinary(op); - node->setLine(line); - - TIntermTyped* child = addConversion(op, left->getType(), right); - if (child == 0) - return 0; - - node->setLeft(left); - node->setRight(child); - if (! node->promote(infoSink)) - return 0; - - 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) -{ - TIntermBinary* node = new TIntermBinary(op); - node->setLine(line); - node->setLeft(base); - node->setRight(index); - - // caller should set the type - - return node; -} - -// -// Add one node as the parent of another that it operates on. -// -// Returns the added node. -// -TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermNode* childNode, const TSourceLoc& line, TSymbolTable& symbolTable) -{ - TIntermUnary* node; - TIntermTyped* child = childNode->getAsTyped(); - - if (child == 0) { - infoSink.info.message(EPrefixInternalError, line, "Bad type in AddUnaryMath"); - return 0; - } - - switch (op) { - case EOpLogicalNot: - if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { - return 0; - } - break; - - case EOpPostIncrement: - case EOpPreIncrement: - case EOpPostDecrement: - case EOpPreDecrement: - case EOpNegative: - if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) - return 0; - default: break; - } - - // - // Do we need to promote the operand? - // - // Note: Implicit promotions were removed from the language. - // - TBasicType newType = EbtVoid; - switch (op) { - case EOpConstructInt: newType = EbtInt; break; - case EOpConstructBool: newType = EbtBool; break; - case EOpConstructFloat: newType = EbtFloat; break; - default: break; - } - - if (newType != EbtVoid) { - child = addConversion(op, TType(newType, child->getPrecision(), EvqTemporary, - child->getNominalSize(), - child->isMatrix(), - child->isArray()), - child); - if (child == 0) - return 0; - } - - // - // For constructors, we are now done, it's all in the conversion. - // - switch (op) { - case EOpConstructInt: - case EOpConstructBool: - case EOpConstructFloat: - return child; - default: break; - } - - TIntermConstantUnion *childTempConstant = 0; - if (child->getAsConstantUnion()) - childTempConstant = child->getAsConstantUnion(); - - // - // Make a new node for the operator. - // - node = new TIntermUnary(op); - node->setLine(line); - node->setOperand(child); - - if (! node->promote(infoSink)) - return 0; - - if (childTempConstant) { - TIntermTyped* newChild = childTempConstant->fold(op, 0, infoSink); - - if (newChild) - return newChild; - } - - return node; -} - -// -// This is the safe way to change the operator on an aggregate, as it -// does lots of error checking and fixing. Especially for establishing -// a function call's operation on it's set of parameters. Sequences -// of instructions are also aggregates, but they just direnctly set -// their operator to EOpSequence. -// -// 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 == 0 || 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; -} - -// -// Convert one type to another. -// -// Returns the node representing the conversion, which could be the same -// node passed in if no conversion was needed. -// -// Return 0 if a conversion can't be done. -// -TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) -{ - // - // Does the base type allow operation? - // - switch (node->getBasicType()) { - case EbtVoid: - case EbtSampler2D: - case EbtSamplerCube: - return 0; - default: break; - } - - // - // Otherwise, if types are identical, no problem - // - if (type == node->getType()) - return node; - - // - // If one's a structure, then no conversions. - // - if (type.getStruct() || node->getType().getStruct()) - return 0; - - // - // If one's an array, then no conversions. - // - if (type.isArray() || node->getType().isArray()) - return 0; - - TBasicType promoteTo; - - switch (op) { - // - // Explicit conversions - // - case EOpConstructBool: - promoteTo = EbtBool; - break; - case EOpConstructFloat: - promoteTo = EbtFloat; - break; - case EOpConstructInt: - promoteTo = EbtInt; - break; - default: - // - // implicit conversions were removed from the language. - // - if (type.getBasicType() != node->getType().getBasicType()) - return 0; - // - // Size and structure could still differ, but that's - // handled by operator promotion. - // - return node; - } - - if (node->getAsConstantUnion()) { - - return (promoteConstantUnion(promoteTo, node->getAsConstantUnion())); - } else { - - // - // Add a new newNode for the conversion. - // - TIntermUnary* newNode = 0; - - TOperator newOp = EOpNull; - switch (promoteTo) { - case EbtFloat: - switch (node->getBasicType()) { - case EbtInt: newOp = EOpConvIntToFloat; break; - case EbtBool: newOp = EOpConvBoolToFloat; break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Bad promotion node"); - return 0; - } - break; - case EbtBool: - switch (node->getBasicType()) { - case EbtInt: newOp = EOpConvIntToBool; break; - case EbtFloat: newOp = EOpConvFloatToBool; break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Bad promotion node"); - return 0; - } - break; - case EbtInt: - switch (node->getBasicType()) { - case EbtBool: newOp = EOpConvBoolToInt; break; - case EbtFloat: newOp = EOpConvFloatToInt; break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Bad promotion node"); - return 0; - } - break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Bad promotion type"); - return 0; - } - - TType type(promoteTo, node->getPrecision(), EvqTemporary, node->getNominalSize(), node->isMatrix(), node->isArray()); - newNode = new TIntermUnary(newOp, type); - newNode->setLine(node->getLine()); - newNode->setOperand(node); - - return newNode; - } -} - -// -// 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 == 0 && right == 0) - return 0; - - TIntermAggregate* aggNode = 0; - 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 0 was passed in for the existing node. -// -TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& line) -{ - if (node == 0) - return 0; - - TIntermAggregate* aggNode = new TIntermAggregate; - aggNode->getSequence().push_back(node); - aggNode->setLine(line); - - return aggNode; -} - -// -// 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 selection node created. -// -TIntermNode* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& line) -{ - // - // For compile time constant selections, prune the code and - // test now. - // - - if (cond->getAsTyped() && cond->getAsTyped()->getAsConstantUnion()) { - if (cond->getAsConstantUnion()->getBConst(0) == true) - return nodePair.node1 ? setAggregateOperator(nodePair.node1, EOpSequence, nodePair.node1->getLine()) : NULL; - else - return nodePair.node2 ? setAggregateOperator(nodePair.node2, EOpSequence, nodePair.node2->getLine()) : NULL; - } - - TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); - node->setLine(line); - - return node; -} - - -TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& line) -{ - if (left->getType().getQualifier() == EvqConst && right->getType().getQualifier() == EvqConst) { - return right; - } else { - TIntermTyped *commaAggregate = growAggregate(left, right, line); - commaAggregate->getAsAggregate()->setOp(EOpComma); - commaAggregate->setType(right->getType()); - commaAggregate->getTypePointer()->setQualifier(EvqTemporary); - return commaAggregate; - } -} - -// -// For "?:" test nodes. There are three children; a condition, -// a true path, and a false path. The two paths are specified -// as separate parameters. -// -// Returns the selection node created, or 0 if one could not be. -// -TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& line) -{ - // - // Get compatible types. - // - TIntermTyped* child = addConversion(EOpSequence, trueBlock->getType(), falseBlock); - if (child) - falseBlock = child; - else { - child = addConversion(EOpSequence, falseBlock->getType(), trueBlock); - if (child) - trueBlock = child; - else - return 0; - } - - // - // See if all the operands are constant, then fold it otherwise not. - // - - if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) { - if (cond->getAsConstantUnion()->getBConst(0)) - return trueBlock; - else - return falseBlock; - } - - // - // Make a selection node. - // - TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); - node->getTypePointer()->setQualifier(EvqTemporary); - 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(ConstantUnion* unionArrayPointer, const TType& t, const TSourceLoc& line) -{ - TIntermConstantUnion* node = new TIntermConstantUnion(unionArrayPointer, t); - node->setLine(line); - - return node; -} - -TIntermTyped* TIntermediate::addSwizzle(TVectorFields& fields, const TSourceLoc& line) -{ - - TIntermAggregate* node = new TIntermAggregate(EOpSequence); - - node->setLine(line); - TIntermConstantUnion* constIntNode; - TIntermSequence &sequenceVector = node->getSequence(); - ConstantUnion* unionArray; - - for (int i = 0; i < fields.num; i++) { - unionArray = new ConstantUnion[1]; - unionArray->setIConst(fields.offsets[i]); - constIntNode = addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), line); - sequenceVector.push_back(constIntNode); - } - - 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, 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; -} - -// -// This is to be executed once the final root is put on top by the parsing -// process. -// -bool TIntermediate::postProcess(TIntermNode* root) -{ - if (root == 0) - return true; - - // - // First, finish off the top level sequence, if any - // - TIntermAggregate* aggRoot = root->getAsAggregate(); - if (aggRoot && aggRoot->getOp() == EOpNull) - aggRoot->setOp(EOpSequence); - - return true; -} - -// -// This deletes the tree. -// -void TIntermediate::remove(TIntermNode* root) -{ - if (root) - RemoveAllTreeNodes(root); -} - -//////////////////////////////////////////////////////////////// -// -// Member functions of the nodes used for building the tree. -// -//////////////////////////////////////////////////////////////// - -#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) -{ - REPLACE_IF_IS(init, TIntermNode, original, replacement); - REPLACE_IF_IS(cond, TIntermTyped, original, replacement); - REPLACE_IF_IS(expr, TIntermTyped, original, replacement); - REPLACE_IF_IS(body, TIntermNode, original, replacement); - return false; -} - -bool TIntermBranch::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) -{ - REPLACE_IF_IS(expression, TIntermTyped, original, replacement); - return false; -} - -bool TIntermBinary::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) -{ - REPLACE_IF_IS(left, TIntermTyped, original, replacement); - REPLACE_IF_IS(right, TIntermTyped, original, replacement); - return false; -} - -bool TIntermUnary::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) -{ - REPLACE_IF_IS(operand, TIntermTyped, original, replacement); - return false; -} - -bool TIntermAggregate::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) -{ - for (size_t ii = 0; ii < sequence.size(); ++ii) - { - REPLACE_IF_IS(sequence[ii], TIntermNode, original, replacement); - } - return false; -} - -bool TIntermSelection::replaceChildNode( - TIntermNode *original, TIntermNode *replacement) -{ - REPLACE_IF_IS(condition, TIntermTyped, original, replacement); - REPLACE_IF_IS(trueBlock, TIntermNode, original, replacement); - REPLACE_IF_IS(falseBlock, TIntermNode, original, replacement); - return false; -} - -// -// Say whether or not an operation node changes the value of a variable. -// -bool TIntermOperator::isAssignment() const -{ - 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: - return true; - default: - return false; - } -} - -// -// returns true if the operator is for one of the constructors -// -bool TIntermOperator::isConstructor() const -{ - switch (op) { - case EOpConstructVec2: - case EOpConstructVec3: - case EOpConstructVec4: - case EOpConstructMat2: - case EOpConstructMat3: - case EOpConstructMat4: - case EOpConstructFloat: - case EOpConstructIVec2: - case EOpConstructIVec3: - case EOpConstructIVec4: - case EOpConstructInt: - case EOpConstructBVec2: - case EOpConstructBVec3: - case EOpConstructBVec4: - case EOpConstructBool: - case EOpConstructStruct: - return true; - default: - return false; - } -} - -// -// Make sure the type of a unary operator is appropriate for its -// combination of operation and operand type. -// -// Returns false in nothing makes sense. -// -bool TIntermUnary::promote(TInfoSink&) -{ - switch (op) { - case EOpLogicalNot: - if (operand->getBasicType() != EbtBool) - return false; - break; - case EOpNegative: - case EOpPostIncrement: - case EOpPostDecrement: - case EOpPreIncrement: - case EOpPreDecrement: - if (operand->getBasicType() == EbtBool) - return false; - break; - - // operators for built-ins are already type checked against their prototype - case EOpAny: - case EOpAll: - case EOpVectorLogicalNot: - return true; - - default: - if (operand->getBasicType() != EbtFloat) - return false; - } - - setType(operand->getType()); - type.setQualifier(EvqTemporary); - - return true; -} - -// -// Establishes the type of the resultant operation, as well as -// makes the operator the correct one for the operands. -// -// Returns false if operator can't work on operands. -// -bool TIntermBinary::promote(TInfoSink& infoSink) -{ - // This function only handles scalars, vectors, and matrices. - if (left->isArray() || right->isArray()) { - infoSink.info.message(EPrefixInternalError, getLine(), "Invalid operation for arrays"); - return false; - } - - // GLSL ES 2.0 does not support implicit type casting. - // So the basic type should always match. - if (left->getBasicType() != right->getBasicType()) - return false; - - // - // Base assumption: just make the type the same as the left - // operand. Then only deviations from this need be coded. - // - setType(left->getType()); - - // The result gets promoted to the highest precision. - TPrecision higherPrecision = GetHigherPrecision(left->getPrecision(), right->getPrecision()); - getTypePointer()->setPrecision(higherPrecision); - - // Binary operations results in temporary variables unless both - // operands are const. - if (left->getQualifier() != EvqConst || right->getQualifier() != EvqConst) { - getTypePointer()->setQualifier(EvqTemporary); - } - - int size = std::max(left->getNominalSize(), right->getNominalSize()); - - // - // All scalars. Code after this test assumes this case is removed! - // - if (size == 1) { - switch (op) { - // - // Promote to conditional - // - case EOpEqual: - case EOpNotEqual: - case EOpLessThan: - case EOpGreaterThan: - case EOpLessThanEqual: - case EOpGreaterThanEqual: - setType(TType(EbtBool, EbpUndefined)); - break; - - // - // And and Or operate on conditionals - // - case EOpLogicalAnd: - case EOpLogicalOr: - // Both operands must be of type bool. - if (left->getBasicType() != EbtBool || right->getBasicType() != EbtBool) - return false; - setType(TType(EbtBool, EbpUndefined)); - break; - - default: - break; - } - return true; - } - - // If we reach here, at least one of the operands is vector or matrix. - // The other operand could be a scalar, vector, or matrix. - // Are the sizes compatible? - // - if (left->getNominalSize() != right->getNominalSize()) { - // If the nominal size of operands do not match: - // One of them must be scalar. - if (left->getNominalSize() != 1 && right->getNominalSize() != 1) - return false; - // Operator cannot be of type pure assignment. - if (op == EOpAssign || op == EOpInitialize) - return false; - } - - // - // Can these two operands be combined? - // - TBasicType basicType = left->getBasicType(); - switch (op) { - case EOpMul: - if (!left->isMatrix() && right->isMatrix()) { - if (left->isVector()) - op = EOpVectorTimesMatrix; - else { - op = EOpMatrixTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, size, true)); - } - } else if (left->isMatrix() && !right->isMatrix()) { - if (right->isVector()) { - op = EOpMatrixTimesVector; - setType(TType(basicType, higherPrecision, EvqTemporary, size, false)); - } else { - op = EOpMatrixTimesScalar; - } - } else if (left->isMatrix() && right->isMatrix()) { - op = EOpMatrixTimesMatrix; - } else if (!left->isMatrix() && !right->isMatrix()) { - if (left->isVector() && right->isVector()) { - // leave as component product - } else if (left->isVector() || right->isVector()) { - op = EOpVectorTimesScalar; - setType(TType(basicType, higherPrecision, EvqTemporary, size, false)); - } - } else { - infoSink.info.message(EPrefixInternalError, getLine(), "Missing elses"); - return false; - } - break; - case EOpMulAssign: - if (!left->isMatrix() && right->isMatrix()) { - if (left->isVector()) - op = EOpVectorTimesMatrixAssign; - else { - return false; - } - } else if (left->isMatrix() && !right->isMatrix()) { - if (right->isVector()) { - return false; - } else { - op = EOpMatrixTimesScalarAssign; - } - } else if (left->isMatrix() && right->isMatrix()) { - op = EOpMatrixTimesMatrixAssign; - } else if (!left->isMatrix() && !right->isMatrix()) { - if (left->isVector() && right->isVector()) { - // leave as component product - } else if (left->isVector() || right->isVector()) { - if (! left->isVector()) - return false; - op = EOpVectorTimesScalarAssign; - setType(TType(basicType, higherPrecision, EvqTemporary, size, false)); - } - } else { - infoSink.info.message(EPrefixInternalError, getLine(), "Missing elses"); - return false; - } - break; - - case EOpAssign: - case EOpInitialize: - case EOpAdd: - case EOpSub: - case EOpDiv: - case EOpAddAssign: - case EOpSubAssign: - case EOpDivAssign: - if ((left->isMatrix() && right->isVector()) || - (left->isVector() && right->isMatrix())) - return false; - setType(TType(basicType, higherPrecision, EvqTemporary, size, left->isMatrix() || right->isMatrix())); - break; - - case EOpEqual: - case EOpNotEqual: - case EOpLessThan: - case EOpGreaterThan: - case EOpLessThanEqual: - case EOpGreaterThanEqual: - if ((left->isMatrix() && right->isVector()) || - (left->isVector() && right->isMatrix())) - return false; - setType(TType(EbtBool, EbpUndefined)); - break; - - default: - return false; - } - - return true; -} - -bool CompareStruct(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray) -{ - const TFieldList& fields = leftNodeType.getStruct()->fields(); - - size_t structSize = fields.size(); - size_t index = 0; - - for (size_t j = 0; j < structSize; j++) { - size_t size = fields[j]->type()->getObjectSize(); - for (size_t i = 0; i < size; i++) { - if (fields[j]->type()->getBasicType() == EbtStruct) { - if (!CompareStructure(*(fields[j]->type()), &rightUnionArray[index], &leftUnionArray[index])) - return false; - } else { - if (leftUnionArray[index] != rightUnionArray[index]) - return false; - index++; - } - } - } - return true; -} - -bool CompareStructure(const TType& leftNodeType, ConstantUnion* rightUnionArray, ConstantUnion* leftUnionArray) -{ - if (leftNodeType.isArray()) { - TType typeWithoutArrayness = leftNodeType; - typeWithoutArrayness.clearArrayness(); - - size_t arraySize = leftNodeType.getArraySize(); - - for (size_t i = 0; i < arraySize; ++i) { - size_t offset = typeWithoutArrayness.getObjectSize() * i; - if (!CompareStruct(typeWithoutArrayness, &rightUnionArray[offset], &leftUnionArray[offset])) - return false; - } - } else - return CompareStruct(leftNodeType, rightUnionArray, leftUnionArray); - - return true; -} - -// -// The fold functions see if an operation on a constant can be done in place, -// without generating run-time code. -// -// Returns the node to keep using, which may or may not be the node passed in. -// - -TIntermTyped* TIntermConstantUnion::fold(TOperator op, TIntermTyped* constantNode, TInfoSink& infoSink) -{ - ConstantUnion *unionArray = getUnionArrayPointer(); - size_t objectSize = getType().getObjectSize(); - - if (constantNode) { // binary operations - TIntermConstantUnion *node = constantNode->getAsConstantUnion(); - ConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - TType returnType = getType(); - - // for a case like float f = 1.2 + vec4(2,3,4,5); - if (constantNode->getType().getObjectSize() == 1 && objectSize > 1) { - rightUnionArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; ++i) - rightUnionArray[i] = *node->getUnionArrayPointer(); - returnType = getType(); - } else if (constantNode->getType().getObjectSize() > 1 && objectSize == 1) { - // for a case like float f = vec4(2,3,4,5) + 1.2; - unionArray = new ConstantUnion[constantNode->getType().getObjectSize()]; - for (size_t i = 0; i < constantNode->getType().getObjectSize(); ++i) - unionArray[i] = *getUnionArrayPointer(); - returnType = node->getType(); - objectSize = constantNode->getType().getObjectSize(); - } - - ConstantUnion* tempConstArray = 0; - TIntermConstantUnion *tempNode; - - bool boolNodeFlag = false; - switch(op) { - case EOpAdd: - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] + rightUnionArray[i]; - } - break; - case EOpSub: - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] - rightUnionArray[i]; - } - break; - - case EOpMul: - case EOpVectorTimesScalar: - case EOpMatrixTimesScalar: - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] * rightUnionArray[i]; - } - break; - case EOpMatrixTimesMatrix: - if (getType().getBasicType() != EbtFloat || node->getBasicType() != EbtFloat) { - infoSink.info.message(EPrefixInternalError, getLine(), "Constant Folding cannot be done for matrix multiply"); - return 0; - } - {// support MSVC++6.0 - int size = getNominalSize(); - tempConstArray = new ConstantUnion[size*size]; - for (int row = 0; row < size; row++) { - for (int column = 0; column < size; column++) { - tempConstArray[size * column + row].setFConst(0.0f); - for (int i = 0; i < size; i++) { - tempConstArray[size * column + row].setFConst(tempConstArray[size * column + row].getFConst() + unionArray[i * size + row].getFConst() * (rightUnionArray[column * size + i].getFConst())); - } - } - } - } - break; - case EOpDiv: - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) { - switch (getType().getBasicType()) { - case EbtFloat: - if (rightUnionArray[i] == 0.0f) { - infoSink.info.message(EPrefixWarning, getLine(), "Divide by zero error during constant folding"); - tempConstArray[i].setFConst(unionArray[i].getFConst() < 0 ? -FLT_MAX : FLT_MAX); - } else - tempConstArray[i].setFConst(unionArray[i].getFConst() / rightUnionArray[i].getFConst()); - break; - - case EbtInt: - if (rightUnionArray[i] == 0) { - infoSink.info.message(EPrefixWarning, getLine(), "Divide by zero error during constant folding"); - tempConstArray[i].setIConst(INT_MAX); - } else - tempConstArray[i].setIConst(unionArray[i].getIConst() / rightUnionArray[i].getIConst()); - break; - default: - infoSink.info.message(EPrefixInternalError, getLine(), "Constant folding cannot be done for \"/\""); - return 0; - } - } - } - break; - - case EOpMatrixTimesVector: - if (node->getBasicType() != EbtFloat) { - infoSink.info.message(EPrefixInternalError, getLine(), "Constant Folding cannot be done for matrix times vector"); - return 0; - } - tempConstArray = new ConstantUnion[getNominalSize()]; - - {// support MSVC++6.0 - for (int size = getNominalSize(), i = 0; i < size; i++) { - tempConstArray[i].setFConst(0.0f); - for (int j = 0; j < size; j++) { - tempConstArray[i].setFConst(tempConstArray[i].getFConst() + ((unionArray[j*size + i].getFConst()) * rightUnionArray[j].getFConst())); - } - } - } - - tempNode = new TIntermConstantUnion(tempConstArray, node->getType()); - tempNode->setLine(getLine()); - - return tempNode; - - case EOpVectorTimesMatrix: - if (getType().getBasicType() != EbtFloat) { - infoSink.info.message(EPrefixInternalError, getLine(), "Constant Folding cannot be done for vector times matrix"); - return 0; - } - - tempConstArray = new ConstantUnion[getNominalSize()]; - {// support MSVC++6.0 - for (int size = getNominalSize(), i = 0; i < size; i++) { - tempConstArray[i].setFConst(0.0f); - for (int j = 0; j < size; j++) { - tempConstArray[i].setFConst(tempConstArray[i].getFConst() + ((unionArray[j].getFConst()) * rightUnionArray[i*size + j].getFConst())); - } - } - } - break; - - case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] && rightUnionArray[i]; - } - break; - - case EOpLogicalOr: // this code is written for possible future use, will not get executed currently - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - tempConstArray[i] = unionArray[i] || rightUnionArray[i]; - } - break; - - case EOpLogicalXor: - tempConstArray = new ConstantUnion[objectSize]; - {// support MSVC++6.0 - for (size_t i = 0; i < objectSize; i++) - switch (getType().getBasicType()) { - case EbtBool: tempConstArray[i].setBConst((unionArray[i] == rightUnionArray[i]) ? false : true); break; - default: assert(false && "Default missing"); - } - } - break; - - case EOpLessThan: - assert(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray < *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; - case EOpGreaterThan: - assert(objectSize == 1); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(*unionArray > *rightUnionArray); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; - case EOpLessThanEqual: - { - assert(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray > *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; - } - case EOpGreaterThanEqual: - { - assert(objectSize == 1); - ConstantUnion constant; - constant.setBConst(*unionArray < *rightUnionArray); - tempConstArray = new ConstantUnion[1]; - tempConstArray->setBConst(!constant.getBConst()); - returnType = TType(EbtBool, EbpUndefined, EvqConst); - break; - } - - case EOpEqual: - if (getType().getBasicType() == EbtStruct) { - if (!CompareStructure(node->getType(), node->getUnionArrayPointer(), unionArray)) - boolNodeFlag = true; - } else { - for (size_t i = 0; i < objectSize; i++) { - if (unionArray[i] != rightUnionArray[i]) { - boolNodeFlag = true; - break; // break out of for loop - } - } - } - - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) { - tempConstArray->setBConst(true); - } - else { - tempConstArray->setBConst(false); - } - - tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); - - return tempNode; - - case EOpNotEqual: - if (getType().getBasicType() == EbtStruct) { - if (CompareStructure(node->getType(), node->getUnionArrayPointer(), unionArray)) - boolNodeFlag = true; - } else { - for (size_t i = 0; i < objectSize; i++) { - if (unionArray[i] == rightUnionArray[i]) { - boolNodeFlag = true; - break; // break out of for loop - } - } - } - - tempConstArray = new ConstantUnion[1]; - if (!boolNodeFlag) { - tempConstArray->setBConst(true); - } - else { - tempConstArray->setBConst(false); - } - - tempNode = new TIntermConstantUnion(tempConstArray, TType(EbtBool, EbpUndefined, EvqConst)); - tempNode->setLine(getLine()); - - return tempNode; - - default: - infoSink.info.message(EPrefixInternalError, getLine(), "Invalid operator for constant folding"); - return 0; - } - tempNode = new TIntermConstantUnion(tempConstArray, returnType); - tempNode->setLine(getLine()); - - return tempNode; - } else { - // - // Do unary operations - // - TIntermConstantUnion *newNode = 0; - ConstantUnion* tempConstArray = new ConstantUnion[objectSize]; - for (size_t i = 0; i < objectSize; i++) { - switch(op) { - case EOpNegative: - switch (getType().getBasicType()) { - case EbtFloat: tempConstArray[i].setFConst(-unionArray[i].getFConst()); break; - case EbtInt: tempConstArray[i].setIConst(-unionArray[i].getIConst()); break; - default: - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return 0; - } - break; - case EOpLogicalNot: // this code is written for possible future use, will not get executed currently - switch (getType().getBasicType()) { - case EbtBool: tempConstArray[i].setBConst(!unionArray[i].getBConst()); break; - default: - infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant"); - return 0; - } - break; - default: - return 0; - } - } - newNode = new TIntermConstantUnion(tempConstArray, getType()); - newNode->setLine(getLine()); - return newNode; - } -} - -TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) -{ - size_t size = node->getType().getObjectSize(); - - ConstantUnion *leftUnionArray = new ConstantUnion[size]; - - for (size_t i = 0; i < size; i++) { - int index = static_cast<int>(i); - switch (promoteTo) { - case EbtFloat: - switch (node->getType().getBasicType()) { - case EbtInt: - leftUnionArray[i].setFConst(static_cast<float>(node->getIConst(index))); - break; - case EbtBool: - leftUnionArray[i].setFConst(static_cast<float>(node->getBConst(index))); - break; - case EbtFloat: - leftUnionArray[i].setFConst(static_cast<float>(node->getFConst(index))); - break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Cannot promote"); - return 0; - } - break; - case EbtInt: - switch (node->getType().getBasicType()) { - case EbtInt: - leftUnionArray[i].setIConst(static_cast<int>(node->getIConst(index))); - break; - case EbtBool: - leftUnionArray[i].setIConst(static_cast<int>(node->getBConst(index))); - break; - case EbtFloat: - leftUnionArray[i].setIConst(static_cast<int>(node->getFConst(index))); - break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Cannot promote"); - return 0; - } - break; - case EbtBool: - switch (node->getType().getBasicType()) { - case EbtInt: - leftUnionArray[i].setBConst(node->getIConst(index) != 0); - break; - case EbtBool: - leftUnionArray[i].setBConst(node->getBConst(index)); - break; - case EbtFloat: - leftUnionArray[i].setBConst(node->getFConst(index) != 0.0f); - break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Cannot promote"); - return 0; - } - - break; - default: - infoSink.info.message(EPrefixInternalError, node->getLine(), "Incorrect data type found"); - return 0; - } - - } - - const TType& t = node->getType(); - - return addConstantUnion(leftUnionArray, TType(promoteTo, t.getPrecision(), t.getQualifier(), t.getNominalSize(), t.isMatrix(), t.isArray()), node->getLine()); -} - -// 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; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.cpp b/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.cpp deleted file mode 100644 index 077ef5d72..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// -// 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/MapLongVariableNames.h" - -namespace { - -TString mapLongName(size_t id, const TString& name, bool isGlobal) -{ - ASSERT(name.size() > MAX_SHORTENED_IDENTIFIER_SIZE); - TStringStream stream; - stream << "webgl_"; - if (isGlobal) - stream << "g"; - stream << id; - if (name[0] != '_') - stream << "_"; - stream << name.substr(0, MAX_SHORTENED_IDENTIFIER_SIZE - stream.str().size()); - return stream.str(); -} - -LongNameMap* gLongNameMapInstance = NULL; - -} // anonymous namespace - -LongNameMap::LongNameMap() - : refCount(0) -{ -} - -LongNameMap::~LongNameMap() -{ -} - -// static -LongNameMap* LongNameMap::GetInstance() -{ - if (gLongNameMapInstance == NULL) - gLongNameMapInstance = new LongNameMap; - gLongNameMapInstance->refCount++; - return gLongNameMapInstance; -} - -void LongNameMap::Release() -{ - ASSERT(gLongNameMapInstance == this); - ASSERT(refCount > 0); - refCount--; - if (refCount == 0) { - delete gLongNameMapInstance; - gLongNameMapInstance = NULL; - } -} - -const char* LongNameMap::Find(const char* originalName) const -{ - std::map<std::string, std::string>::const_iterator it = mLongNameMap.find( - originalName); - if (it != mLongNameMap.end()) - return (*it).second.c_str(); - return NULL; -} - -void LongNameMap::Insert(const char* originalName, const char* mappedName) -{ - mLongNameMap.insert(std::map<std::string, std::string>::value_type( - originalName, mappedName)); -} - -size_t LongNameMap::Size() const -{ - return mLongNameMap.size(); -} - -MapLongVariableNames::MapLongVariableNames(LongNameMap* globalMap) -{ - ASSERT(globalMap); - mGlobalMap = globalMap; -} - -void MapLongVariableNames::visitSymbol(TIntermSymbol* symbol) -{ - ASSERT(symbol != NULL); - if (symbol->getSymbol().size() > MAX_SHORTENED_IDENTIFIER_SIZE) { - switch (symbol->getQualifier()) { - case EvqVaryingIn: - case EvqVaryingOut: - case EvqInvariantVaryingIn: - case EvqInvariantVaryingOut: - case EvqUniform: - symbol->setSymbol( - mapGlobalLongName(symbol->getSymbol())); - break; - default: - symbol->setSymbol( - mapLongName(symbol->getId(), symbol->getSymbol(), false)); - break; - }; - } -} - -TString MapLongVariableNames::mapGlobalLongName(const TString& name) -{ - ASSERT(mGlobalMap); - const char* mappedName = mGlobalMap->Find(name.c_str()); - if (mappedName != NULL) - return mappedName; - size_t id = mGlobalMap->Size(); - TString rt = mapLongName(id, name, true); - mGlobalMap->Insert(name.c_str(), rt.c_str()); - return rt; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.h b/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.h deleted file mode 100644 index fd2ff8261..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/MapLongVariableNames.h +++ /dev/null @@ -1,58 +0,0 @@ -// -// 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_MAP_LONG_VARIABLE_NAMES_H_ -#define COMPILER_MAP_LONG_VARIABLE_NAMES_H_ - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/intermediate.h" -#include "compiler/VariableInfo.h" - -// This size does not include '\0' in the end. -#define MAX_SHORTENED_IDENTIFIER_SIZE 32 - -// This is a ref-counted singleton. GetInstance() returns a pointer to the -// singleton, and after use, call Release(). GetInstance() and Release() should -// be paired. -class LongNameMap { -public: - static LongNameMap* GetInstance(); - void Release(); - - // Return the mapped name if <originalName, mappedName> is in the map; - // otherwise, return NULL. - const char* Find(const char* originalName) const; - - // Insert a pair into the map. - void Insert(const char* originalName, const char* mappedName); - - // Return the number of entries in the map. - size_t Size() const; - -private: - LongNameMap(); - ~LongNameMap(); - - size_t refCount; - std::map<std::string, std::string> mLongNameMap; -}; - -// Traverses intermediate tree to map attributes and uniforms names that are -// longer than MAX_SHORTENED_IDENTIFIER_SIZE to MAX_SHORTENED_IDENTIFIER_SIZE. -class MapLongVariableNames : public TIntermTraverser { -public: - MapLongVariableNames(LongNameMap* globalMap); - - virtual void visitSymbol(TIntermSymbol*); - -private: - TString mapGlobalLongName(const TString& name); - - LongNameMap* mGlobalMap; -}; - -#endif // COMPILER_MAP_LONG_VARIABLE_NAMES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.cpp deleted file mode 100644 index c2048f1ce..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// 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 "compiler/OutputESSL.h" - -TOutputESSL::TOutputESSL(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable) - : TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable) -{ -} - -bool TOutputESSL::writeVariablePrecision(TPrecision precision) -{ - if (precision == EbpUndefined) - return false; - - TInfoSinkBase& out = objSink(); - out << getPrecisionString(precision); - return true; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.h b/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.h deleted file mode 100644 index 05db96e49..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputESSL.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// 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 CROSSCOMPILERGLSL_OUTPUTESSL_H_ -#define CROSSCOMPILERGLSL_OUTPUTESSL_H_ - -#include "compiler/OutputGLSLBase.h" - -class TOutputESSL : public TOutputGLSLBase -{ -public: - TOutputESSL(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable); - -protected: - virtual bool writeVariablePrecision(TPrecision precision); -}; - -#endif // CROSSCOMPILERGLSL_OUTPUTESSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp deleted file mode 100644 index 10a451c0d..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// 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 "compiler/OutputGLSL.h" - -TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable) - : TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable) -{ -} - -bool TOutputGLSL::writeVariablePrecision(TPrecision) -{ - return false; -} - -void TOutputGLSL::visitSymbol(TIntermSymbol* node) -{ - TInfoSinkBase& out = objSink(); - - if (node->getSymbol() == "gl_FragDepthEXT") - { - out << "gl_FragDepth"; - } - else - { - TOutputGLSLBase::visitSymbol(node); - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.h deleted file mode 100644 index fa68ac810..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSL.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// 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 CROSSCOMPILERGLSL_OUTPUTGLSL_H_ -#define CROSSCOMPILERGLSL_OUTPUTGLSL_H_ - -#include "compiler/OutputGLSLBase.h" - -class TOutputGLSL : public TOutputGLSLBase -{ -public: - TOutputGLSL(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable); - -protected: - virtual bool writeVariablePrecision(TPrecision); - virtual void visitSymbol(TIntermSymbol* node); -}; - -#endif // CROSSCOMPILERGLSL_OUTPUTGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.cpp b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.cpp deleted file mode 100644 index b90bd67ec..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.cpp +++ /dev/null @@ -1,817 +0,0 @@ -// -// 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 "compiler/OutputGLSLBase.h" -#include "compiler/debug.h" - -#include <cfloat> - -namespace -{ -TString arrayBrackets(const TType& type) -{ - ASSERT(type.isArray()); - TInfoSinkBase out; - out << "[" << type.getArraySize() << "]"; - return TString(out.c_str()); -} - -bool isSingleStatement(TIntermNode* node) { - if (const TIntermAggregate* aggregate = node->getAsAggregate()) - { - return (aggregate->getOp() != EOpFunction) && - (aggregate->getOp() != EOpSequence); - } - else if (const TIntermSelection* selection = node->getAsSelectionNode()) - { - // Ternary operators are usually part of an assignment operator. - // This handles those rare cases in which they are all by themselves. - return selection->usesTernaryOperator(); - } - else if (node->getAsLoopNode()) - { - return false; - } - return true; -} -} // namespace - -TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable) - : TIntermTraverser(true, true, true), - mObjSink(objSink), - mDeclaringVariables(false), - mClampingStrategy(clampingStrategy), - mHashFunction(hashFunction), - mNameMap(nameMap), - mSymbolTable(symbolTable) -{ -} - -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::writeVariableType(const TType& type) -{ - TInfoSinkBase& out = objSink(); - TQualifier qualifier = type.getQualifier(); - // TODO(alokp): Validate qualifier for variable declarations. - if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal)) - out << type.getQualifierString() << " "; - // Declare the struct if we have not done so already. - if ((type.getBasicType() == EbtStruct) && !structDeclared(type.getStruct())) - { - declareStruct(type.getStruct()); - } - 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); - - const TString& name = arg->getSymbol(); - if (!name.empty()) - out << " " << hashName(name); - if (type.isArray()) - out << arrayBrackets(type); - - // Put a comma if this is not the last argument. - if (iter != args.end() - 1) - out << ", "; - } -} - -const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type, - const ConstantUnion* pConstUnion) -{ - TInfoSinkBase& out = objSink(); - - if (type.getBasicType() == EbtStruct) - { - const TStructure* structure = type.getStruct(); - out << hashName(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 EbtBool: out << pConstUnion->getBConst(); break; - default: UNREACHABLE(); - } - if (i != size - 1) out << ", "; - } - if (writeType) out << ")"; - } - return pConstUnion; -} - -void TOutputGLSLBase::visitSymbol(TIntermSymbol* node) -{ - TInfoSinkBase& out = objSink(); - if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node)) - out << mLoopUnroll.GetLoopIndexValue(node); - else - out << hashVariableName(node->getSymbol()); - - if (mDeclaringVariables && node->getType().isArray()) - out << arrayBrackets(node->getType()); -} - -void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node) -{ - writeConstantUnion(node->getType(), node->getUnionArrayPointer()); -} - -bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node) -{ - bool visitChildren = true; - TInfoSinkBase& out = objSink(); - switch (node->getOp()) - { - 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; - // Notice the fall-through. - case EOpMulAssign: - case EOpVectorTimesMatrixAssign: - case EOpVectorTimesScalarAssign: - case EOpMatrixTimesScalarAssign: - case EOpMatrixTimesMatrixAssign: - 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 = 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())) - fieldName = hashName(fieldName); - - out << fieldName; - visitChildren = false; - } - break; - case EOpVectorSwizzle: - if (visit == InVisit) - { - out << "."; - TIntermAggregate* rightChild = node->getRight()->getAsAggregate(); - TIntermSequence& sequence = rightChild->getSequence(); - for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit) - { - TIntermConstantUnion* element = (*sit)->getAsConstantUnion(); - ASSERT(element->getBasicType() == EbtInt); - ASSERT(element->getNominalSize() == 1); - const ConstantUnion& data = element->getUnionArrayPointer()[0]; - ASSERT(data.getType() == EbtInt); - switch (data.getIConst()) - { - case 0: out << "x"; break; - case 1: out << "y"; break; - case 2: out << "z"; break; - case 3: out << "w"; break; - default: UNREACHABLE(); break; - } - } - visitChildren = false; - } - break; - - case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break; - case EOpSub: writeTriplet(visit, "(", " - ", ")"); break; - case EOpMul: writeTriplet(visit, "(", " * ", ")"); break; - case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break; - case EOpMod: UNIMPLEMENTED(); 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(); break; - } - - return visitChildren; -} - -bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node) -{ - TString preString; - TString postString = ")"; - - switch (node->getOp()) - { - case EOpNegative: preString = "(-"; break; - case EOpVectorLogicalNot: preString = "not("; break; - case EOpLogicalNot: preString = "(!"; break; - - case EOpPostIncrement: preString = "("; postString = "++)"; break; - case EOpPostDecrement: preString = "("; postString = "--)"; break; - case EOpPreIncrement: preString = "(++"; break; - case EOpPreDecrement: preString = "(--"; break; - - case EOpConvIntToBool: - case EOpConvFloatToBool: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: preString = "bool("; break; - case 2: preString = "bvec2("; break; - case 3: preString = "bvec3("; break; - case 4: preString = "bvec4("; break; - default: UNREACHABLE(); - } - break; - case EOpConvBoolToFloat: - case EOpConvIntToFloat: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: preString = "float("; break; - case 2: preString = "vec2("; break; - case 3: preString = "vec3("; break; - case 4: preString = "vec4("; break; - default: UNREACHABLE(); - } - break; - case EOpConvFloatToInt: - case EOpConvBoolToInt: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: preString = "int("; break; - case 2: preString = "ivec2("; break; - case 3: preString = "ivec3("; break; - case 4: preString = "ivec4("; break; - default: UNREACHABLE(); - } - 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 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 EOpCeil: preString = "ceil("; break; - case EOpFract: preString = "fract("; 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 EOpAny: preString = "any("; break; - case EOpAll: preString = "all("; break; - - default: UNREACHABLE(); break; - } - - if (visit == PreVisit && node->getUseEmulatedFunction()) - preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString); - writeTriplet(visit, preString.c_str(), NULL, postString.c_str()); - - return true; -} - -bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node) -{ - TInfoSinkBase& out = objSink(); - - if (node->usesTernaryOperator()) - { - // Notice two brackets at the beginning and end. The outer ones - // encapsulate the whole ternary expression. This preserves the - // order of precedence when ternary expressions are used in a - // compound expression, i.e., c = 2 * (a < b ? 1 : 2). - out << "(("; - node->getCondition()->traverse(this); - out << ") ? ("; - node->getTrueBlock()->traverse(this); - out << ") : ("; - node->getFalseBlock()->traverse(this); - out << "))"; - } - else - { - out << "if ("; - node->getCondition()->traverse(this); - out << ")\n"; - - incrementDepth(node); - visitCodeBlock(node->getTrueBlock()); - - if (node->getFalseBlock()) - { - out << "else\n"; - visitCodeBlock(node->getFalseBlock()); - } - decrementDepth(); - } - return false; -} - -bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node) -{ - bool visitChildren = true; - TInfoSinkBase& out = objSink(); - TString preString; - bool delayedWrite = false; - switch (node->getOp()) - { - case EOpSequence: { - // Scope the sequences except when at the global scope. - if (depth > 0) out << "{\n"; - - incrementDepth(node); - const TIntermSequence& sequence = node->getSequence(); - for (TIntermSequence::const_iterator iter = sequence.begin(); - iter != sequence.end(); ++iter) - { - TIntermNode* node = *iter; - ASSERT(node != NULL); - node->traverse(this); - - if (isSingleStatement(node)) - out << ";\n"; - } - decrementDepth(); - - // Scope the sequences except when at the global scope. - if (depth > 0) out << "}\n"; - visitChildren = false; - break; - } - case EOpPrototype: { - // Function declaration. - ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashName(node->getName()); - - out << "("; - writeFunctionParameters(node->getSequence()); - out << ")"; - - visitChildren = false; - break; - } - case EOpFunction: { - // Function definition. - ASSERT(visit == PreVisit); - writeVariableType(node->getType()); - out << " " << hashFunctionName(node->getName()); - - incrementDepth(node); - // Function definition node contains one or two children nodes - // representing function parameters and function body. The latter - // is not present in case of empty function bodies. - const TIntermSequence& sequence = node->getSequence(); - ASSERT((sequence.size() == 1) || (sequence.size() == 2)); - TIntermSequence::const_iterator seqIter = sequence.begin(); - - // Traverse function parameters. - TIntermAggregate* params = (*seqIter)->getAsAggregate(); - ASSERT(params != NULL); - ASSERT(params->getOp() == EOpParameters); - params->traverse(this); - - // Traverse function body. - TIntermAggregate* body = ++seqIter != sequence.end() ? - (*seqIter)->getAsAggregate() : NULL; - visitCodeBlock(body); - decrementDepth(); - - // Fully processed; no need to visit children. - visitChildren = false; - break; - } - case EOpFunctionCall: - // Function call. - if (visit == PreVisit) - { - out << hashFunctionName(node->getName()) << "("; - } - else if (visit == InVisit) - { - out << ", "; - } - else - { - out << ")"; - } - break; - case EOpParameters: { - // Function parameters. - ASSERT(visit == PreVisit); - out << "("; - writeFunctionParameters(node->getSequence()); - out << ")"; - visitChildren = false; - break; - } - case EOpDeclaration: { - // Variable declaration. - if (visit == PreVisit) - { - const TIntermSequence& sequence = node->getSequence(); - const TIntermTyped* variable = sequence.front()->getAsTyped(); - writeVariableType(variable->getType()); - out << " "; - mDeclaringVariables = true; - } - else if (visit == InVisit) - { - out << ", "; - mDeclaringVariables = true; - } - else - { - mDeclaringVariables = false; - } - break; - } - case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break; - case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break; - case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break; - case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break; - case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break; - case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break; - case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break; - case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break; - case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break; - case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break; - case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break; - case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break; - case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break; - case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break; - case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break; - case EOpConstructStruct: - if (visit == PreVisit) - { - const TType& type = node->getType(); - ASSERT(type.getBasicType() == EbtStruct); - out << hashName(type.getStruct()->name()) << "("; - } - else if (visit == InVisit) - { - out << ", "; - } - else - { - out << ")"; - } - break; - - case EOpLessThan: preString = "lessThan("; delayedWrite = true; break; - case EOpGreaterThan: preString = "greaterThan("; delayedWrite = true; break; - case EOpLessThanEqual: preString = "lessThanEqual("; delayedWrite = true; break; - case EOpGreaterThanEqual: preString = "greaterThanEqual("; delayedWrite = true; break; - case EOpVectorEqual: preString = "equal("; delayedWrite = true; break; - case EOpVectorNotEqual: preString = "notEqual("; delayedWrite = true; break; - case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break; - - case EOpMod: preString = "mod("; delayedWrite = true; break; - case EOpPow: preString = "pow("; delayedWrite = true; break; - case EOpAtan: preString = "atan("; delayedWrite = true; break; - case EOpMin: preString = "min("; delayedWrite = true; break; - case EOpMax: preString = "max("; delayedWrite = true; break; - case EOpClamp: preString = "clamp("; delayedWrite = true; break; - case EOpMix: preString = "mix("; delayedWrite = true; break; - case EOpStep: preString = "step("; delayedWrite = true; break; - case EOpSmoothStep: preString = "smoothstep("; delayedWrite = true; break; - - case EOpDistance: preString = "distance("; delayedWrite = true; break; - case EOpDot: preString = "dot("; delayedWrite = true; break; - case EOpCross: preString = "cross("; delayedWrite = true; break; - case EOpFaceForward: preString = "faceforward("; delayedWrite = true; break; - case EOpReflect: preString = "reflect("; delayedWrite = true; break; - case EOpRefract: preString = "refract("; delayedWrite = true; break; - case EOpMul: preString = "matrixCompMult("; delayedWrite = true; break; - - default: UNREACHABLE(); break; - } - if (delayedWrite && visit == PreVisit && node->getUseEmulatedFunction()) - preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString); - if (delayedWrite) - writeTriplet(visit, preString.c_str(), ", ", ")"); - return visitChildren; -} - -bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node) -{ - TInfoSinkBase& out = objSink(); - - incrementDepth(node); - // Loop header. - TLoopType loopType = node->getType(); - 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"; - } - } - else if (loopType == ELoopWhile) // while loop - { - out << "while ("; - ASSERT(node->getCondition() != NULL); - node->getCondition()->traverse(this); - out << ")\n"; - } - else // do-while loop - { - ASSERT(loopType == ELoopDoWhile); - out << "do\n"; - } - - // Loop body. - if (node->getUnrollFlag()) - { - TLoopIndexInfo indexInfo; - mLoopUnroll.FillLoopIndexInfo(node, indexInfo); - mLoopUnroll.Push(indexInfo); - while (mLoopUnroll.SatisfiesLoopCondition()) - { - visitCodeBlock(node->getBody()); - mLoopUnroll.Step(); - } - mLoopUnroll.Pop(); - } - else - { - visitCodeBlock(node->getBody()); - } - - // Loop footer. - if (loopType == ELoopDoWhile) // do-while loop - { - 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(); break; - } - - return true; -} - -void TOutputGLSLBase::visitCodeBlock(TIntermNode* 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) -{ - TInfoSinkBase out; - if (type.isMatrix()) - { - out << "mat"; - out << type.getNominalSize(); - } - else if (type.isVector()) - { - switch (type.getBasicType()) - { - case EbtFloat: out << "vec"; break; - case EbtInt: out << "ivec"; break; - case EbtBool: out << "bvec"; break; - default: UNREACHABLE(); break; - } - out << type.getNominalSize(); - } - else - { - if (type.getBasicType() == EbtStruct) - out << hashName(type.getStruct()->name()); - else - out << type.getBasicString(); - } - return TString(out.c_str()); -} - -TString TOutputGLSLBase::hashName(const TString& name) -{ - if (mHashFunction == NULL || name.empty()) - return name; - NameMap::const_iterator it = mNameMap.find(name.c_str()); - if (it != mNameMap.end()) - return it->second.c_str(); - TString hashedName = TIntermTraverser::hash(name, mHashFunction); - mNameMap[name.c_str()] = hashedName.c_str(); - return hashedName; -} - -TString TOutputGLSLBase::hashVariableName(const TString& name) -{ - if (mSymbolTable.findBuiltIn(name) != NULL) - return name; - return hashName(name); -} - -TString TOutputGLSLBase::hashFunctionName(const TString& mangled_name) -{ - TString name = TFunction::unmangleName(mangled_name); - if (mSymbolTable.findBuiltIn(mangled_name) != NULL || name == "main") - return name; - return hashName(name); -} - -bool TOutputGLSLBase::structDeclared(const TStructure* structure) const -{ - return mDeclaredStructs.find(structure->name()) != mDeclaredStructs.end(); -} - -void TOutputGLSLBase::declareStruct(const TStructure* structure) -{ - TInfoSinkBase& out = objSink(); - - out << "struct " << hashName(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(field->name()); - if (field->type()->isArray()) - out << arrayBrackets(*field->type()); - out << ";\n"; - } - out << "}"; - - mDeclaredStructs.insert(structure->name()); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.h b/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.h deleted file mode 100644 index 69868c09c..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputGLSLBase.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// 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 CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_ -#define CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_ - -#include <set> - -#include "compiler/ForLoopUnroll.h" -#include "compiler/intermediate.h" -#include "compiler/ParseContext.h" - -class TOutputGLSLBase : public TIntermTraverser -{ -public: - TOutputGLSLBase(TInfoSinkBase& objSink, - ShArrayIndexClampingStrategy clampingStrategy, - ShHashFunction64 hashFunction, - NameMap& nameMap, - TSymbolTable& symbolTable); - -protected: - TInfoSinkBase& objSink() { return mObjSink; } - void writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr); - void writeVariableType(const TType& type); - virtual bool writeVariablePrecision(TPrecision precision) = 0; - void writeFunctionParameters(const TIntermSequence& args); - const ConstantUnion* writeConstantUnion(const TType& type, const ConstantUnion* pConstUnion); - TString getTypeName(const TType& type); - - virtual void visitSymbol(TIntermSymbol* node); - virtual void visitConstantUnion(TIntermConstantUnion* node); - virtual bool visitBinary(Visit visit, TIntermBinary* node); - virtual bool visitUnary(Visit visit, TIntermUnary* node); - virtual bool visitSelection(Visit visit, TIntermSelection* node); - virtual bool visitAggregate(Visit visit, TIntermAggregate* node); - virtual bool visitLoop(Visit visit, TIntermLoop* node); - virtual bool visitBranch(Visit visit, TIntermBranch* node); - - void visitCodeBlock(TIntermNode* node); - - - // Return the original name if hash function pointer is NULL; - // otherwise return the hashed name. - TString hashName(const TString& name); - // Same as hashName(), but without hashing built-in variables. - TString hashVariableName(const TString& name); - // Same as hashName(), but without hashing built-in functions. - TString hashFunctionName(const TString& mangled_name); - -private: - bool structDeclared(const TStructure* structure) const; - void declareStruct(const TStructure* structure); - - TInfoSinkBase& mObjSink; - bool mDeclaringVariables; - - // Structs are declared as the tree is traversed. This set contains all - // the structs already declared. It is maintained so that a struct is - // declared only once. - typedef std::set<TString> DeclaredStructs; - DeclaredStructs mDeclaredStructs; - - ForLoopUnroll mLoopUnroll; - - ShArrayIndexClampingStrategy mClampingStrategy; - - // name hashing. - ShHashFunction64 mHashFunction; - - NameMap& mNameMap; - - TSymbolTable& mSymbolTable; -}; - -#endif // CROSSCOMPILERGLSL_OUTPUTGLSLBASE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.cpp deleted file mode 100644 index a715f229c..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.cpp +++ /dev/null @@ -1,3133 +0,0 @@ -// -// 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/OutputHLSL.h" - -#include "common/angleutils.h" -#include "compiler/debug.h" -#include "compiler/DetectDiscontinuity.h" -#include "compiler/InfoSink.h" -#include "compiler/SearchSymbol.h" -#include "compiler/UnfoldShortCircuit.h" -#include "compiler/NodeSearch.h" - -#include <algorithm> -#include <cfloat> -#include <stdio.h> - -namespace sh -{ -// Integer to TString conversion -TString str(int i) -{ - char buffer[20]; - snprintf(buffer, sizeof(buffer), "%d", i); - return buffer; -} - -OutputHLSL::OutputHLSL(TParseContext &context, const ShBuiltInResources& resources, ShShaderOutput outputType) - : TIntermTraverser(true, true, true), mContext(context), mOutputType(outputType) -{ - mUnfoldShortCircuit = new UnfoldShortCircuit(context, this); - mInsideFunction = false; - - mUsesTexture2D = false; - mUsesTexture2D_bias = false; - mUsesTexture2DProj = false; - mUsesTexture2DProj_bias = false; - mUsesTexture2DProjLod = false; - mUsesTexture2DLod = false; - mUsesTextureCube = false; - mUsesTextureCube_bias = false; - mUsesTextureCubeLod = false; - mUsesTexture2DLod0 = false; - mUsesTexture2DLod0_bias = false; - mUsesTexture2DProjLod0 = false; - mUsesTexture2DProjLod0_bias = false; - mUsesTextureCubeLod0 = false; - mUsesTextureCubeLod0_bias = false; - mUsesFragColor = false; - mUsesFragData = false; - mUsesDepthRange = false; - mUsesFragCoord = false; - mUsesPointCoord = false; - mUsesFrontFacing = false; - mUsesPointSize = false; - mUsesFragDepth = false; - mUsesXor = false; - mUsesMod1 = false; - mUsesMod2v = false; - mUsesMod2f = false; - mUsesMod3v = false; - mUsesMod3f = false; - mUsesMod4v = false; - mUsesMod4f = false; - mUsesFaceforward1 = false; - mUsesFaceforward2 = false; - mUsesFaceforward3 = false; - mUsesFaceforward4 = false; - mUsesAtan2_1 = false; - mUsesAtan2_2 = false; - mUsesAtan2_3 = false; - mUsesAtan2_4 = false; - mUsesDiscardRewriting = false; - - mNumRenderTargets = resources.EXT_draw_buffers ? resources.MaxDrawBuffers : 1; - - mScopeDepth = 0; - - mUniqueIndex = 0; - - mContainsLoopDiscontinuity = false; - mOutputLod0Function = false; - mInsideDiscontinuousLoop = false; - - mExcessiveLoopIndex = NULL; - - if (mOutputType == SH_HLSL9_OUTPUT) - { - if (mContext.shaderType == SH_FRAGMENT_SHADER) - { - mUniformRegister = 3; // Reserve registers for dx_DepthRange, dx_ViewCoords and dx_DepthFront - } - else - { - mUniformRegister = 2; // Reserve registers for dx_DepthRange and dx_ViewAdjust - } - } - else - { - mUniformRegister = 0; - } - - mSamplerRegister = 0; -} - -OutputHLSL::~OutputHLSL() -{ - delete mUnfoldShortCircuit; -} - -void OutputHLSL::output() -{ - mContainsLoopDiscontinuity = mContext.shaderType == SH_FRAGMENT_SHADER && containsLoopDiscontinuity(mContext.treeRoot); - - mContext.treeRoot->traverse(this); // Output the body first to determine what has to go in the header - header(); - - mContext.infoSink().obj << mHeader.c_str(); - mContext.infoSink().obj << mBody.c_str(); -} - -TInfoSinkBase &OutputHLSL::getBodyStream() -{ - return mBody; -} - -const ActiveUniforms &OutputHLSL::getUniforms() -{ - return mActiveUniforms; -} - -int OutputHLSL::vectorSize(const TType &type) const -{ - int elementSize = type.isMatrix() ? type.getNominalSize() : 1; - int arraySize = type.isArray() ? type.getArraySize() : 1; - - return elementSize * arraySize; -} - -void OutputHLSL::header() -{ - ShShaderType shaderType = mContext.shaderType; - TInfoSinkBase &out = mHeader; - - for (StructDeclarations::iterator structDeclaration = mStructDeclarations.begin(); structDeclaration != mStructDeclarations.end(); structDeclaration++) - { - out << *structDeclaration; - } - - for (Constructors::iterator constructor = mConstructors.begin(); constructor != mConstructors.end(); constructor++) - { - out << *constructor; - } - - TString uniforms; - TString varyings; - TString attributes; - - for (ReferencedSymbols::const_iterator uniform = mReferencedUniforms.begin(); uniform != mReferencedUniforms.end(); uniform++) - { - const TType &type = uniform->second->getType(); - const TString &name = uniform->second->getSymbol(); - - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) // Also declare the texture - { - int index = samplerRegister(mReferencedUniforms[name]); - - uniforms += "uniform SamplerState sampler_" + decorateUniform(name, type) + arrayString(type) + - " : register(s" + str(index) + ");\n"; - - uniforms += "uniform " + textureString(type) + " texture_" + decorateUniform(name, type) + arrayString(type) + - " : register(t" + str(index) + ");\n"; - } - else - { - uniforms += "uniform " + typeString(type) + " " + decorateUniform(name, type) + arrayString(type) + - " : register(" + registerString(mReferencedUniforms[name]) + ");\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 " + 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"; - } - - if (mUsesDiscardRewriting) - { - out << "#define ANGLE_USES_DISCARD_REWRITING" << "\n"; - } - - if (shaderType == SH_FRAGMENT_SHADER) - { - TExtensionBehavior::const_iterator iter = mContext.extensionBehavior().find("GL_EXT_draw_buffers"); - const bool usingMRTExtension = (iter != mContext.extensionBehavior().end() && (iter->second == EBhEnable || iter->second == EBhRequire)); - - const unsigned int numColorValues = usingMRTExtension ? mNumRenderTargets : 1; - - out << "// Varyings\n"; - out << varyings; - out << "\n" - "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_HLSL11_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"; - } - - 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"; - } - - out << uniforms; - out << "\n"; - - if (mUsesTexture2D) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2D(sampler2D s, float2 t)\n" - "{\n" - " return tex2D(s, t);\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv)\n" - "{\n" - " return t.Sample(s, uv);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2D_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2D(sampler2D s, float2 t, float bias)\n" - "{\n" - " return tex2Dbias(s, float4(t.x, t.y, 0, bias));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv, float bias)\n" - "{\n" - " return t.SampleBias(s, uv, bias);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProj) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProj(sampler2D s, float3 t)\n" - "{\n" - " return tex2Dproj(s, float4(t.x, t.y, 0, t.z));\n" - "}\n" - "\n" - "float4 gl_texture2DProj(sampler2D s, float4 t)\n" - "{\n" - " return tex2Dproj(s, t);\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.Sample(s, float2(uvw.x / uvw.z, uvw.y / uvw.z));\n" - "}\n" - "\n" - "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw)\n" - "{\n" - " return t.Sample(s, float2(uvw.x / uvw.w, uvw.y / uvw.w));\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProj_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProj(sampler2D s, float3 t, float bias)\n" - "{\n" - " return tex2Dbias(s, float4(t.x / t.z, t.y / t.z, 0, bias));\n" - "}\n" - "\n" - "float4 gl_texture2DProj(sampler2D s, float4 t, float bias)\n" - "{\n" - " return tex2Dbias(s, float4(t.x / t.w, t.y / t.w, 0, bias));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw, float bias)\n" - "{\n" - " return t.SampleBias(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), bias);\n" - "}\n" - "\n" - "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw, float bias)\n" - "{\n" - " return t.SampleBias(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), bias);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCube) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCube(samplerCUBE s, float3 t)\n" - "{\n" - " return texCUBE(s, t);\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.Sample(s, uvw);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCube_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCube(samplerCUBE s, float3 t, float bias)\n" - "{\n" - " return texCUBEbias(s, float4(t.x, t.y, t.z, bias));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw, float bias)\n" - "{\n" - " return t.SampleBias(s, uvw, bias);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - // These *Lod0 intrinsics are not available in GL fragment shaders. - // They are used to sample using discontinuous texture coordinates. - if (mUsesTexture2DLod0) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DLod0(sampler2D s, float2 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DLod0(Texture2D t, SamplerState s, float2 uv)\n" - "{\n" - " return t.SampleLevel(s, uv, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DLod0_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DLod0(sampler2D s, float2 t, float bias)\n" - "{\n" - " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DLod0(Texture2D t, SamplerState s, float2 uv, float bias)\n" - "{\n" - " return t.SampleLevel(s, uv, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProjLod0) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProjLod0(sampler2D s, float3 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n" - "}\n" - "\n" - "float4 gl_texture2DProjLod(sampler2D s, float4 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProjLod0(Texture2D t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n" - "}\n" - "\n" - "float4 gl_texture2DProjLod0(Texture2D t, SamplerState s, float4 uvw)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProjLod0_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProjLod0_bias(sampler2D s, float3 t, float bias)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n" - "}\n" - "\n" - "float4 gl_texture2DProjLod_bias(sampler2D s, float4 t, float bias)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProjLod_bias(Texture2D t, SamplerState s, float3 uvw, float bias)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n" - "}\n" - "\n" - "float4 gl_texture2DProjLod_bias(Texture2D t, SamplerState s, float4 uvw, float bias)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCubeLod0) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCubeLod0(samplerCUBE s, float3 t)\n" - "{\n" - " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCubeLod0(TextureCube t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.SampleLevel(s, uvw, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCubeLod0_bias) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCubeLod0(samplerCUBE s, float3 t, float bias)\n" - "{\n" - " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCubeLod0(TextureCube t, SamplerState s, float3 uvw, float bias)\n" - "{\n" - " return t.SampleLevel(s, uvw, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - 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"; - } - - 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_HLSL11_OUTPUT) - { - if (mUsesDepthRange) - { - out << "cbuffer DriverConstants : register(b1)\n" - "{\n" - " float3 dx_DepthRange : packoffset(c0);\n" - "};\n" - "\n"; - } - } - else - { - if (mUsesDepthRange) - { - out << "uniform float3 dx_DepthRange : register(c0);\n"; - } - - out << "uniform float4 dx_ViewAdjust : register(c1);\n" - "\n"; - } - - if (mUsesDepthRange) - { - out << "static gl_DepthRangeParameters gl_DepthRange = {dx_DepthRange.x, dx_DepthRange.y, dx_DepthRange.z};\n" - "\n"; - } - - out << uniforms; - out << "\n"; - - if (mUsesTexture2D) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2D(sampler2D s, float2 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x, t.y, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2D(Texture2D t, SamplerState s, float2 uv)\n" - "{\n" - " return t.SampleLevel(s, uv, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DLod) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DLod(sampler2D s, float2 t, float lod)\n" - "{\n" - " return tex2Dlod(s, float4(t.x, t.y, 0, lod));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DLod(Texture2D t, SamplerState s, float2 uv, float lod)\n" - "{\n" - " return t.SampleLevel(s, uv, lod);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProj) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProj(sampler2D s, float3 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, 0));\n" - "}\n" - "\n" - "float4 gl_texture2DProj(sampler2D s, float4 t)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), 0);\n" - "}\n" - "\n" - "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTexture2DProjLod) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_texture2DProjLod(sampler2D s, float3 t, float lod)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.z, t.y / t.z, 0, lod));\n" - "}\n" - "\n" - "float4 gl_texture2DProjLod(sampler2D s, float4 t, float lod)\n" - "{\n" - " return tex2Dlod(s, float4(t.x / t.w, t.y / t.w, 0, lod));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_texture2DProj(Texture2D t, SamplerState s, float3 uvw, float lod)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.z, uvw.y / uvw.z), lod);\n" - "}\n" - "\n" - "float4 gl_texture2DProj(Texture2D t, SamplerState s, float4 uvw)\n" - "{\n" - " return t.SampleLevel(s, float2(uvw.x / uvw.w, uvw.y / uvw.w), lod);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCube) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCube(samplerCUBE s, float3 t)\n" - "{\n" - " return texCUBElod(s, float4(t.x, t.y, t.z, 0));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCube(TextureCube t, SamplerState s, float3 uvw)\n" - "{\n" - " return t.SampleLevel(s, uvw, 0);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - - if (mUsesTextureCubeLod) - { - if (mOutputType == SH_HLSL9_OUTPUT) - { - out << "float4 gl_textureCubeLod(samplerCUBE s, float3 t, float lod)\n" - "{\n" - " return texCUBElod(s, float4(t.x, t.y, t.z, lod));\n" - "}\n" - "\n"; - } - else if (mOutputType == SH_HLSL11_OUTPUT) - { - out << "float4 gl_textureCubeLod(TextureCube t, SamplerState s, float3 uvw, float lod)\n" - "{\n" - " return t.SampleLevel(s, uvw, lod);\n" - "}\n" - "\n"; - } - else UNREACHABLE(); - } - } - - 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"; - } - - if (mUsesMod1) - { - out << "float mod(float x, float y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod2v) - { - out << "float2 mod(float2 x, float2 y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod2f) - { - out << "float2 mod(float2 x, float y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod3v) - { - out << "float3 mod(float3 x, float3 y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod3f) - { - out << "float3 mod(float3 x, float y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod4v) - { - out << "float4 mod(float4 x, float4 y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesMod4f) - { - out << "float4 mod(float4 x, float y)\n" - "{\n" - " return x - y * floor(x / y);\n" - "}\n" - "\n"; - } - - if (mUsesFaceforward1) - { - out << "float faceforward(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"; - } - - if (mUsesFaceforward2) - { - out << "float2 faceforward(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"; - } - - if (mUsesFaceforward3) - { - out << "float3 faceforward(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"; - } - - if (mUsesFaceforward4) - { - out << "float4 faceforward(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"; - } - - if (mUsesAtan2_1) - { - out << "float atanyx(float y, float x)\n" - "{\n" - " if(x == 0 && y == 0) x = 1;\n" // Avoid producing a NaN - " return atan2(y, x);\n" - "}\n"; - } - - if (mUsesAtan2_2) - { - out << "float2 atanyx(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"; - } - - if (mUsesAtan2_3) - { - out << "float3 atanyx(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"; - } - - if (mUsesAtan2_4) - { - out << "float4 atanyx(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"; - } -} - -void OutputHLSL::visitSymbol(TIntermSymbol *node) -{ - TInfoSinkBase &out = mBody; - - TString name = node->getSymbol(); - - if (name == "gl_FragColor") - { - out << "gl_Color[0]"; - mUsesFragColor = true; - } - else if (name == "gl_FragData") - { - out << "gl_Color"; - mUsesFragData = true; - } - else if (name == "gl_DepthRange") - { - mUsesDepthRange = true; - out << name; - } - else if (name == "gl_FragCoord") - { - mUsesFragCoord = true; - out << name; - } - else if (name == "gl_PointCoord") - { - mUsesPointCoord = true; - out << name; - } - else if (name == "gl_FrontFacing") - { - mUsesFrontFacing = true; - out << name; - } - else if (name == "gl_PointSize") - { - mUsesPointSize = true; - out << name; - } - else if (name == "gl_FragDepthEXT") - { - mUsesFragDepth = true; - out << "gl_Depth"; - } - else - { - TQualifier qualifier = node->getQualifier(); - - if (qualifier == EvqUniform) - { - mReferencedUniforms[name] = node; - out << decorateUniform(name, node->getType()); - } - else if (qualifier == EvqAttribute) - { - mReferencedAttributes[name] = node; - out << decorate(name); - } - else if (qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingOut || qualifier == EvqVaryingIn || qualifier == EvqInvariantVaryingIn) - { - mReferencedVaryings[name] = node; - out << decorate(name); - } - else - { - out << decorate(name); - } - } -} - -bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node) -{ - TInfoSinkBase &out = mBody; - - switch (node->getOp()) - { - case EOpAssign: outputTriplet(visit, "(", " = ", ")"); break; - case EOpInitialize: - if (visit == PreVisit) - { - // 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;". - - TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode(); - TIntermTyped *expression = node->getRight(); - - sh::SearchSymbol searchSymbol(symbolNode->getSymbol()); - expression->traverse(&searchSymbol); - bool sameSymbol = searchSymbol.foundMatch(); - - if (sameSymbol) - { - // Type already printed - out << "t" + str(mUniqueIndex) + " = "; - expression->traverse(this); - out << ", "; - symbolNode->traverse(this); - out << " = t" + str(mUniqueIndex); - - mUniqueIndex++; - return false; - } - } - else if (visit == InVisit) - { - out << " = "; - } - break; - case EOpAddAssign: outputTriplet(visit, "(", " += ", ")"); break; - case EOpSubAssign: outputTriplet(visit, "(", " -= ", ")"); break; - case EOpMulAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")"); break; - case EOpMatrixTimesScalarAssign: outputTriplet(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 << " = mul("; - node->getLeft()->traverse(this); - out << ", "; - } - else - { - out << "))"; - } - break; - case EOpDivAssign: outputTriplet(visit, "(", " /= ", ")"); break; - case EOpIndexDirect: outputTriplet(visit, "", "[", "]"); break; - case EOpIndexIndirect: outputTriplet(visit, "", "[", "]"); break; - case EOpIndexDirectStruct: - if (visit == InVisit) - { - const TStructure* structure = node->getLeft()->getType().getStruct(); - const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion(); - const TField* field = structure->fields()[index->getIConst(0)]; - out << "." + decorateField(field->name(), node->getLeft()->getType()); - - return false; - } - break; - case EOpVectorSwizzle: - if (visit == InVisit) - { - out << "."; - - TIntermAggregate *swizzle = node->getRight()->getAsAggregate(); - - if (swizzle) - { - TIntermSequence &sequence = swizzle->getSequence(); - - for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) - { - TIntermConstantUnion *element = (*sit)->getAsConstantUnion(); - - if (element) - { - int i = element->getIConst(0); - - switch (i) - { - case 0: out << "x"; break; - case 1: out << "y"; break; - case 2: out << "z"; break; - case 3: out << "w"; break; - default: UNREACHABLE(); - } - } - else UNREACHABLE(); - } - } - else UNREACHABLE(); - - return false; // Fully processed - } - break; - case EOpAdd: outputTriplet(visit, "(", " + ", ")"); break; - case EOpSub: outputTriplet(visit, "(", " - ", ")"); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; - case EOpDiv: outputTriplet(visit, "(", " / ", ")"); break; - case EOpEqual: - case EOpNotEqual: - if (node->getLeft()->isScalar()) - { - if (node->getOp() == EOpEqual) - { - outputTriplet(visit, "(", " == ", ")"); - } - else - { - outputTriplet(visit, "(", " != ", ")"); - } - } - else if (node->getLeft()->getBasicType() == EbtStruct) - { - if (node->getOp() == EOpEqual) - { - out << "("; - } - else - { - out << "!("; - } - - const TFieldList &fields = node->getLeft()->getType().getStruct()->fields(); - - for (size_t i = 0; i < fields.size(); i++) - { - const TField *field = fields[i]; - - node->getLeft()->traverse(this); - out << "." + decorateField(field->name(), node->getLeft()->getType()) + " == "; - node->getRight()->traverse(this); - out << "." + decorateField(field->name(), node->getLeft()->getType()); - - if (i < fields.size() - 1) - { - out << " && "; - } - } - - out << ")"; - - return false; - } - else - { - ASSERT(node->getLeft()->isMatrix() || node->getLeft()->isVector()); - - if (node->getOp() == EOpEqual) - { - outputTriplet(visit, "all(", " == ", ")"); - } - else - { - outputTriplet(visit, "!all(", " == ", ")"); - } - } - break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")"); break; - case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break; - case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break; - case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); break; - case EOpLogicalOr: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " || ", ")"); - return true; - } - case EOpLogicalXor: - mUsesXor = true; - outputTriplet(visit, "xor(", ", ", ")"); - break; - case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - return false; - } - else - { - outputTriplet(visit, "(", " && ", ")"); - return true; - } - default: UNREACHABLE(); - } - - return true; -} - -bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node) -{ - switch (node->getOp()) - { - case EOpNegative: outputTriplet(visit, "(-", "", ")"); break; - case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpLogicalNot: outputTriplet(visit, "(!", "", ")"); break; - case EOpPostIncrement: outputTriplet(visit, "(", "", "++)"); break; - case EOpPostDecrement: outputTriplet(visit, "(", "", "--)"); break; - case EOpPreIncrement: outputTriplet(visit, "(++", "", ")"); break; - case EOpPreDecrement: outputTriplet(visit, "(--", "", ")"); break; - case EOpConvIntToBool: - case EOpConvFloatToBool: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: outputTriplet(visit, "bool(", "", ")"); break; - case 2: outputTriplet(visit, "bool2(", "", ")"); break; - case 3: outputTriplet(visit, "bool3(", "", ")"); break; - case 4: outputTriplet(visit, "bool4(", "", ")"); break; - default: UNREACHABLE(); - } - break; - case EOpConvBoolToFloat: - case EOpConvIntToFloat: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: outputTriplet(visit, "float(", "", ")"); break; - case 2: outputTriplet(visit, "float2(", "", ")"); break; - case 3: outputTriplet(visit, "float3(", "", ")"); break; - case 4: outputTriplet(visit, "float4(", "", ")"); break; - default: UNREACHABLE(); - } - break; - case EOpConvFloatToInt: - case EOpConvBoolToInt: - switch (node->getOperand()->getType().getNominalSize()) - { - case 1: outputTriplet(visit, "int(", "", ")"); break; - case 2: outputTriplet(visit, "int2(", "", ")"); break; - case 3: outputTriplet(visit, "int3(", "", ")"); break; - case 4: outputTriplet(visit, "int4(", "", ")"); break; - default: UNREACHABLE(); - } - break; - case EOpRadians: outputTriplet(visit, "radians(", "", ")"); break; - case EOpDegrees: outputTriplet(visit, "degrees(", "", ")"); break; - case EOpSin: outputTriplet(visit, "sin(", "", ")"); break; - case EOpCos: outputTriplet(visit, "cos(", "", ")"); break; - case EOpTan: outputTriplet(visit, "tan(", "", ")"); break; - case EOpAsin: outputTriplet(visit, "asin(", "", ")"); break; - case EOpAcos: outputTriplet(visit, "acos(", "", ")"); break; - case EOpAtan: outputTriplet(visit, "atan(", "", ")"); break; - case EOpExp: outputTriplet(visit, "exp(", "", ")"); break; - case EOpLog: outputTriplet(visit, "log(", "", ")"); break; - case EOpExp2: outputTriplet(visit, "exp2(", "", ")"); break; - case EOpLog2: outputTriplet(visit, "log2(", "", ")"); break; - case EOpSqrt: outputTriplet(visit, "sqrt(", "", ")"); break; - case EOpInverseSqrt: outputTriplet(visit, "rsqrt(", "", ")"); break; - case EOpAbs: outputTriplet(visit, "abs(", "", ")"); break; - case EOpSign: outputTriplet(visit, "sign(", "", ")"); break; - case EOpFloor: outputTriplet(visit, "floor(", "", ")"); break; - case EOpCeil: outputTriplet(visit, "ceil(", "", ")"); break; - case EOpFract: outputTriplet(visit, "frac(", "", ")"); break; - case EOpLength: outputTriplet(visit, "length(", "", ")"); break; - case EOpNormalize: outputTriplet(visit, "normalize(", "", ")"); break; - case EOpDFdx: - if(mInsideDiscontinuousLoop || mOutputLod0Function) - { - outputTriplet(visit, "(", "", ", 0.0)"); - } - else - { - outputTriplet(visit, "ddx(", "", ")"); - } - break; - case EOpDFdy: - if(mInsideDiscontinuousLoop || mOutputLod0Function) - { - outputTriplet(visit, "(", "", ", 0.0)"); - } - else - { - outputTriplet(visit, "ddy(", "", ")"); - } - break; - case EOpFwidth: - if(mInsideDiscontinuousLoop || mOutputLod0Function) - { - outputTriplet(visit, "(", "", ", 0.0)"); - } - else - { - outputTriplet(visit, "fwidth(", "", ")"); - } - break; - case EOpAny: outputTriplet(visit, "any(", "", ")"); break; - case EOpAll: outputTriplet(visit, "all(", "", ")"); break; - default: UNREACHABLE(); - } - - return true; -} - -bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) -{ - TInfoSinkBase &out = mBody; - - switch (node->getOp()) - { - case EOpSequence: - { - if (mInsideFunction) - { - outputLineDirective(node->getLine().first_line); - out << "{\n"; - - mScopeDepth++; - - if (mScopeBracket.size() < mScopeDepth) - { - mScopeBracket.push_back(0); // New scope level - } - else - { - mScopeBracket[mScopeDepth - 1]++; // New scope at existing level - } - } - - for (TIntermSequence::iterator sit = node->getSequence().begin(); sit != node->getSequence().end(); sit++) - { - outputLineDirective((*sit)->getLine().first_line); - - traverseStatements(*sit); - - out << ";\n"; - } - - if (mInsideFunction) - { - outputLineDirective(node->getLine().last_line); - out << "}\n"; - - mScopeDepth--; - } - - return false; - } - case EOpDeclaration: - if (visit == PreVisit) - { - TIntermSequence &sequence = node->getSequence(); - TIntermTyped *variable = sequence[0]->getAsTyped(); - - if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal)) - { - if (variable->getType().getStruct()) - { - addConstructor(variable->getType(), scopedStruct(variable->getType().getStruct()->name()), NULL); - } - - if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getSymbol() != "") // Variable declaration - { - if (!mInsideFunction) - { - out << "static "; - } - - out << typeString(variable->getType()) + " "; - - for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) - { - TIntermSymbol *symbol = (*sit)->getAsSymbolNode(); - - if (symbol) - { - symbol->traverse(this); - out << arrayString(symbol->getType()); - out << " = " + initializer(variable->getType()); - } - else - { - (*sit)->traverse(this); - } - - if (*sit != sequence.back()) - { - out << ", "; - } - } - } - else if (variable->getAsSymbolNode() && variable->getAsSymbolNode()->getSymbol() == "") // Type (struct) declaration - { - // Already added to constructor map - } - else UNREACHABLE(); - } - else if (variable && (variable->getQualifier() == EvqVaryingOut || variable->getQualifier() == EvqInvariantVaryingOut)) - { - 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; - } - else if (visit == InVisit) - { - out << ", "; - } - break; - case EOpPrototype: - if (visit == PreVisit) - { - out << typeString(node->getType()) << " " << decorate(node->getName()) << (mOutputLod0Function ? "Lod0(" : "("); - - TIntermSequence &arguments = node->getSequence(); - - 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 - if (mContainsLoopDiscontinuity && !mOutputLod0Function) - { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } - - return false; - } - break; - case EOpComma: outputTriplet(visit, "(", ", ", ")"); break; - case EOpFunction: - { - TString name = TFunction::unmangleName(node->getName()); - - out << typeString(node->getType()) << " "; - - if (name == "main") - { - out << "gl_main("; - } - else - { - out << decorate(name) << (mOutputLod0Function ? "Lod0(" : "("); - } - - TIntermSequence &sequence = node->getSequence(); - TIntermSequence &arguments = sequence[0]->getAsAggregate()->getSequence(); - - for (unsigned int i = 0; i < arguments.size(); i++) - { - TIntermSymbol *symbol = arguments[i]->getAsSymbolNode(); - - if (symbol) - { - if (symbol->getType().getStruct()) - { - addConstructor(symbol->getType(), scopedStruct(symbol->getType().getStruct()->name()), NULL); - } - - out << argumentString(symbol); - - if (i < arguments.size() - 1) - { - out << ", "; - } - } - else UNREACHABLE(); - } - - out << ")\n" - "{\n"; - - if (sequence.size() > 1) - { - mInsideFunction = true; - sequence[1]->traverse(this); - mInsideFunction = false; - } - - out << "}\n"; - - if (mContainsLoopDiscontinuity && !mOutputLod0Function) - { - if (name != "main") - { - mOutputLod0Function = true; - node->traverse(this); - mOutputLod0Function = false; - } - } - - return false; - } - break; - case EOpFunctionCall: - { - TString name = TFunction::unmangleName(node->getName()); - bool lod0 = mInsideDiscontinuousLoop || mOutputLod0Function; - - if (node->isUserDefined()) - { - out << decorate(name) << (lod0 ? "Lod0(" : "("); - } - else - { - if (name == "texture2D") - { - if (!lod0) - { - if (node->getSequence().size() == 2) - { - mUsesTexture2D = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTexture2D_bias = true; - } - else UNREACHABLE(); - - out << "gl_texture2D("; - } - else - { - if (node->getSequence().size() == 2) - { - mUsesTexture2DLod0 = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTexture2DLod0_bias = true; - } - else UNREACHABLE(); - - out << "gl_texture2DLod0("; - } - } - else if (name == "texture2DProj") - { - if (!lod0) - { - if (node->getSequence().size() == 2) - { - mUsesTexture2DProj = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTexture2DProj_bias = true; - } - else UNREACHABLE(); - - out << "gl_texture2DProj("; - } - else - { - if (node->getSequence().size() == 2) - { - mUsesTexture2DProjLod0 = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTexture2DProjLod0_bias = true; - } - else UNREACHABLE(); - - out << "gl_texture2DProjLod0("; - } - } - else if (name == "textureCube") - { - if (!lod0) - { - if (node->getSequence().size() == 2) - { - mUsesTextureCube = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTextureCube_bias = true; - } - else UNREACHABLE(); - - out << "gl_textureCube("; - } - else - { - if (node->getSequence().size() == 2) - { - mUsesTextureCubeLod0 = true; - } - else if (node->getSequence().size() == 3) - { - mUsesTextureCubeLod0_bias = true; - } - else UNREACHABLE(); - - out << "gl_textureCubeLod0("; - } - } - else if (name == "texture2DLod") - { - if (node->getSequence().size() == 3) - { - mUsesTexture2DLod = true; - } - else UNREACHABLE(); - - out << "gl_texture2DLod("; - } - else if (name == "texture2DProjLod") - { - if (node->getSequence().size() == 3) - { - mUsesTexture2DProjLod = true; - } - else UNREACHABLE(); - - out << "gl_texture2DProjLod("; - } - else if (name == "textureCubeLod") - { - if (node->getSequence().size() == 3) - { - mUsesTextureCubeLod = true; - } - else UNREACHABLE(); - - out << "gl_textureCubeLod("; - } - else UNREACHABLE(); - } - - TIntermSequence &arguments = node->getSequence(); - - for (TIntermSequence::iterator arg = arguments.begin(); arg != arguments.end(); arg++) - { - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler((*arg)->getAsTyped()->getBasicType())) - { - out << "texture_"; - (*arg)->traverse(this); - out << ", sampler_"; - } - - (*arg)->traverse(this); - - if (arg < arguments.end() - 1) - { - out << ", "; - } - } - - out << ")"; - - return false; - } - break; - case EOpParameters: outputTriplet(visit, "(", ", ", ")\n{\n"); break; - case EOpConstructFloat: - addConstructor(node->getType(), "vec1", &node->getSequence()); - outputTriplet(visit, "vec1(", "", ")"); - break; - case EOpConstructVec2: - addConstructor(node->getType(), "vec2", &node->getSequence()); - outputTriplet(visit, "vec2(", ", ", ")"); - break; - case EOpConstructVec3: - addConstructor(node->getType(), "vec3", &node->getSequence()); - outputTriplet(visit, "vec3(", ", ", ")"); - break; - case EOpConstructVec4: - addConstructor(node->getType(), "vec4", &node->getSequence()); - outputTriplet(visit, "vec4(", ", ", ")"); - break; - case EOpConstructBool: - addConstructor(node->getType(), "bvec1", &node->getSequence()); - outputTriplet(visit, "bvec1(", "", ")"); - break; - case EOpConstructBVec2: - addConstructor(node->getType(), "bvec2", &node->getSequence()); - outputTriplet(visit, "bvec2(", ", ", ")"); - break; - case EOpConstructBVec3: - addConstructor(node->getType(), "bvec3", &node->getSequence()); - outputTriplet(visit, "bvec3(", ", ", ")"); - break; - case EOpConstructBVec4: - addConstructor(node->getType(), "bvec4", &node->getSequence()); - outputTriplet(visit, "bvec4(", ", ", ")"); - break; - case EOpConstructInt: - addConstructor(node->getType(), "ivec1", &node->getSequence()); - outputTriplet(visit, "ivec1(", "", ")"); - break; - case EOpConstructIVec2: - addConstructor(node->getType(), "ivec2", &node->getSequence()); - outputTriplet(visit, "ivec2(", ", ", ")"); - break; - case EOpConstructIVec3: - addConstructor(node->getType(), "ivec3", &node->getSequence()); - outputTriplet(visit, "ivec3(", ", ", ")"); - break; - case EOpConstructIVec4: - addConstructor(node->getType(), "ivec4", &node->getSequence()); - outputTriplet(visit, "ivec4(", ", ", ")"); - break; - case EOpConstructMat2: - addConstructor(node->getType(), "mat2", &node->getSequence()); - outputTriplet(visit, "mat2(", ", ", ")"); - break; - case EOpConstructMat3: - addConstructor(node->getType(), "mat3", &node->getSequence()); - outputTriplet(visit, "mat3(", ", ", ")"); - break; - case EOpConstructMat4: - addConstructor(node->getType(), "mat4", &node->getSequence()); - outputTriplet(visit, "mat4(", ", ", ")"); - break; - case EOpConstructStruct: - addConstructor(node->getType(), scopedStruct(node->getType().getStruct()->name()), &node->getSequence()); - outputTriplet(visit, structLookup(node->getType().getStruct()->name()) + "_ctor(", ", ", ")"); - break; - case EOpLessThan: outputTriplet(visit, "(", " < ", ")"); break; - case EOpGreaterThan: outputTriplet(visit, "(", " > ", ")"); break; - case EOpLessThanEqual: outputTriplet(visit, "(", " <= ", ")"); break; - case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")"); break; - case EOpVectorEqual: outputTriplet(visit, "(", " == ", ")"); break; - case EOpVectorNotEqual: outputTriplet(visit, "(", " != ", ")"); break; - case EOpMod: - { - // We need to look at the number of components in both arguments - switch (node->getSequence()[0]->getAsTyped()->getNominalSize() * 10 - + node->getSequence()[1]->getAsTyped()->getNominalSize()) - { - case 11: mUsesMod1 = true; break; - case 22: mUsesMod2v = true; break; - case 21: mUsesMod2f = true; break; - case 33: mUsesMod3v = true; break; - case 31: mUsesMod3f = true; break; - case 44: mUsesMod4v = true; break; - case 41: mUsesMod4f = true; break; - default: UNREACHABLE(); - } - - outputTriplet(visit, "mod(", ", ", ")"); - } - break; - case EOpPow: outputTriplet(visit, "pow(", ", ", ")"); break; - case EOpAtan: - ASSERT(node->getSequence().size() == 2); // atan(x) is a unary operator - switch (node->getSequence()[0]->getAsTyped()->getNominalSize()) - { - case 1: mUsesAtan2_1 = true; break; - case 2: mUsesAtan2_2 = true; break; - case 3: mUsesAtan2_3 = true; break; - case 4: mUsesAtan2_4 = true; break; - default: UNREACHABLE(); - } - outputTriplet(visit, "atanyx(", ", ", ")"); - break; - case EOpMin: outputTriplet(visit, "min(", ", ", ")"); break; - case EOpMax: outputTriplet(visit, "max(", ", ", ")"); break; - case EOpClamp: outputTriplet(visit, "clamp(", ", ", ")"); break; - case EOpMix: outputTriplet(visit, "lerp(", ", ", ")"); break; - case EOpStep: outputTriplet(visit, "step(", ", ", ")"); break; - case EOpSmoothStep: outputTriplet(visit, "smoothstep(", ", ", ")"); break; - case EOpDistance: outputTriplet(visit, "distance(", ", ", ")"); break; - case EOpDot: outputTriplet(visit, "dot(", ", ", ")"); break; - case EOpCross: outputTriplet(visit, "cross(", ", ", ")"); break; - case EOpFaceForward: - { - switch (node->getSequence()[0]->getAsTyped()->getNominalSize()) // Number of components in the first argument - { - case 1: mUsesFaceforward1 = true; break; - case 2: mUsesFaceforward2 = true; break; - case 3: mUsesFaceforward3 = true; break; - case 4: mUsesFaceforward4 = true; break; - default: UNREACHABLE(); - } - - outputTriplet(visit, "faceforward(", ", ", ")"); - } - break; - case EOpReflect: outputTriplet(visit, "reflect(", ", ", ")"); break; - case EOpRefract: outputTriplet(visit, "refract(", ", ", ")"); break; - case EOpMul: outputTriplet(visit, "(", " * ", ")"); break; - default: UNREACHABLE(); - } - - return true; -} - -bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node) -{ - TInfoSinkBase &out = mBody; - - if (node->usesTernaryOperator()) - { - out << "s" << mUnfoldShortCircuit->getNextTemporaryIndex(); - } - else // if/else statement - { - mUnfoldShortCircuit->traverse(node->getCondition()); - - out << "if ("; - - node->getCondition()->traverse(this); - - out << ")\n"; - - outputLineDirective(node->getLine().first_line); - out << "{\n"; - - bool discard = false; - - if (node->getTrueBlock()) - { - traverseStatements(node->getTrueBlock()); - - // Detect true discard - discard = (discard || FindDiscard::search(node->getTrueBlock())); - } - - outputLineDirective(node->getLine().first_line); - out << ";\n}\n"; - - if (node->getFalseBlock()) - { - out << "else\n"; - - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << "{\n"; - - outputLineDirective(node->getFalseBlock()->getLine().first_line); - traverseStatements(node->getFalseBlock()); - - outputLineDirective(node->getFalseBlock()->getLine().first_line); - out << ";\n}\n"; - - // Detect false discard - discard = (discard || FindDiscard::search(node->getFalseBlock())); - } - - // ANGLE issue 486: Detect problematic conditional discard - if (discard && FindSideEffectRewriting::search(node)) - { - mUsesDiscardRewriting = true; - } - } - - return false; -} - -void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node) -{ - writeConstantUnion(node->getType(), node->getUnionArrayPointer()); -} - -bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node) -{ - bool wasDiscontinuous = mInsideDiscontinuousLoop; - - if (mContainsLoopDiscontinuity && !mInsideDiscontinuousLoop) - { - mInsideDiscontinuousLoop = containsLoopDiscontinuity(node); - } - - if (mOutputType == SH_HLSL9_OUTPUT) - { - if (handleExcessiveLoop(node)) - { - return false; - } - } - - TInfoSinkBase &out = mBody; - - if (node->getType() == ELoopDoWhile) - { - out << "{do\n"; - - outputLineDirective(node->getLine().first_line); - out << "{\n"; - } - else - { - 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"; - - outputLineDirective(node->getLine().first_line); - out << "{\n"; - } - - if (node->getBody()) - { - traverseStatements(node->getBody()); - } - - outputLineDirective(node->getLine().first_line); - out << ";}\n"; - - if (node->getType() == ELoopDoWhile) - { - outputLineDirective(node->getCondition()->getLine().first_line); - out << "while(\n"; - - node->getCondition()->traverse(this); - - out << ");"; - } - - out << "}\n"; - - mInsideDiscontinuousLoop = wasDiscontinuous; - - return false; -} - -bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node) -{ - TInfoSinkBase &out = mBody; - - switch (node->getFlowOp()) - { - case EOpKill: - outputTriplet(visit, "discard;\n", "", ""); - break; - case EOpBreak: - if (visit == PreVisit) - { - if (mExcessiveLoopIndex) - { - out << "{Break"; - mExcessiveLoopIndex->traverse(this); - out << " = true; break;}\n"; - } - else - { - out << "break;\n"; - } - } - break; - case EOpContinue: outputTriplet(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; -} - -void OutputHLSL::traverseStatements(TIntermNode *node) -{ - if (isSingleStatement(node)) - { - mUnfoldShortCircuit->traverse(node); - } - - node->traverse(this); -} - -bool OutputHLSL::isSingleStatement(TIntermNode *node) -{ - TIntermAggregate *aggregate = node->getAsAggregate(); - - if (aggregate) - { - if (aggregate->getOp() == EOpSequence) - { - return false; - } - else - { - for (TIntermSequence::iterator sit = aggregate->getSequence().begin(); sit != aggregate->getSequence().end(); sit++) - { - if (!isSingleStatement(*sit)) - { - return false; - } - } - - return true; - } - } - - 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(TIntermLoop *node) -{ - const int MAX_LOOP_ITERATIONS = 254; - TInfoSinkBase &out = mBody; - - // 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()) - { - TIntermAggregate *init = node->getInit()->getAsAggregate(); - - 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->getNominalSize() == 1) - { - 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->getNominalSize() == 1) - { - 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->getNominalSize() == 1) - { - 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) - - out << "for("; - index->traverse(this); - out << " = "; - out << initial; - - out << "; "; - index->traverse(this); - out << " < "; - out << clampedLimit; - - out << "; "; - index->traverse(this); - out << " += "; - out << increment; - out << ")\n"; - - outputLineDirective(node->getLine().first_line); - out << "{\n"; - - if (node->getBody()) - { - node->getBody()->traverse(this); - } - - outputLineDirective(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(Visit visit, const TString &preString, const TString &inString, const TString &postString) -{ - TInfoSinkBase &out = mBody; - - if (visit == PreVisit) - { - out << preString; - } - else if (visit == InVisit) - { - out << inString; - } - else if (visit == PostVisit) - { - out << postString; - } -} - -void OutputHLSL::outputLineDirective(int line) -{ - if ((mContext.compileOptions & SH_LINE_DIRECTIVES) && (line > 0)) - { - mBody << "\n"; - mBody << "#line " << line; - - if (mContext.sourcePath) - { - mBody << " \"" << mContext.sourcePath << "\""; - } - - mBody << "\n"; - } -} - -TString OutputHLSL::argumentString(const TIntermSymbol *symbol) -{ - TQualifier qualifier = symbol->getQualifier(); - const TType &type = symbol->getType(); - TString name = symbol->getSymbol(); - - if (name.empty()) // HLSL demands named arguments, also for prototypes - { - name = "x" + str(mUniqueIndex++); - } - else - { - name = decorate(name); - } - - if (mOutputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) - { - return qualifierString(qualifier) + " " + textureString(type) + " texture_" + name + arrayString(type) + ", " + - qualifierString(qualifier) + " SamplerState sampler_" + name + arrayString(type); - } - - return qualifierString(qualifier) + " " + typeString(type) + " " + name + arrayString(type); -} - -TString OutputHLSL::qualifierString(TQualifier qualifier) -{ - switch(qualifier) - { - case EvqIn: return "in"; - case EvqOut: return "out"; - case EvqInOut: return "inout"; - case EvqConstReadOnly: return "const"; - default: UNREACHABLE(); - } - - return ""; -} - -TString OutputHLSL::typeString(const TType &type) -{ - if (type.getBasicType() == EbtStruct) - { - const TString& typeName = type.getStruct()->name(); - if (typeName != "") - { - return structLookup(typeName); - } - else // Nameless structure, define in place - { - const TFieldList &fields = type.getStruct()->fields(); - - TString string = "struct\n" - "{\n"; - - for (unsigned int i = 0; i < fields.size(); i++) - { - const TField *field = fields[i]; - - string += " " + typeString(*field->type()) + " " + decorate(field->name()) + arrayString(*field->type()) + ";\n"; - } - - string += "} "; - - return string; - } - } - else if (type.isMatrix()) - { - switch (type.getNominalSize()) - { - case 2: return "float2x2"; - case 3: return "float3x3"; - case 4: return "float4x4"; - } - } - 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 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: - return "sampler2D"; - case EbtSamplerCube: - return "samplerCUBE"; - case EbtSamplerExternalOES: - return "sampler2D"; - default: - break; - } - } - - UNREACHABLE(); - return "<unknown type>"; -} - -TString OutputHLSL::textureString(const TType &type) -{ - switch (type.getBasicType()) - { - case EbtSampler2D: - return "Texture2D"; - case EbtSamplerCube: - return "TextureCube"; - case EbtSamplerExternalOES: - return "Texture2D"; - default: - break; - } - - UNREACHABLE(); - return "<unknown texture type>"; -} - -TString OutputHLSL::arrayString(const TType &type) -{ - if (!type.isArray()) - { - return ""; - } - - return "[" + str(type.getArraySize()) + "]"; -} - -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::addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters) -{ - if (name == "") - { - return; // Nameless structures don't have constructors - } - - if (type.getStruct() && mStructNames.find(decorate(name)) != mStructNames.end()) - { - return; // Already added - } - - TType ctorType = type; - ctorType.clearArrayness(); - ctorType.setPrecision(EbpHigh); - ctorType.setQualifier(EvqTemporary); - - TString ctorName = type.getStruct() ? decorate(name) : name; - - typedef std::vector<TType> ParameterArray; - ParameterArray ctorParameters; - - if (type.getStruct()) - { - mStructNames.insert(decorate(name)); - - TString structure; - structure += "struct " + decorate(name) + "\n" - "{\n"; - - const TFieldList &fields = type.getStruct()->fields(); - - for (unsigned int i = 0; i < fields.size(); i++) - { - const TField *field = fields[i]; - - structure += " " + typeString(*field->type()) + " " + decorateField(field->name(), type) + arrayString(*field->type()) + ";\n"; - } - - structure += "};\n"; - - if (std::find(mStructDeclarations.begin(), mStructDeclarations.end(), structure) == mStructDeclarations.end()) - { - mStructDeclarations.push_back(structure); - } - - for (unsigned int i = 0; i < fields.size(); i++) - { - ctorParameters.push_back(*fields[i]->type()); - } - } - else if (parameters) - { - for (TIntermSequence::const_iterator parameter = parameters->begin(); parameter != parameters->end(); parameter++) - { - ctorParameters.push_back((*parameter)->getAsTyped()->getType()); - } - } - else UNREACHABLE(); - - TString constructor; - - if (ctorType.getStruct()) - { - constructor += ctorName + " " + ctorName + "_ctor("; - } - else // Built-in type - { - constructor += typeString(ctorType) + " " + ctorName + "("; - } - - for (unsigned int parameter = 0; parameter < ctorParameters.size(); parameter++) - { - const TType &type = ctorParameters[parameter]; - - constructor += typeString(type) + " x" + str(parameter) + arrayString(type); - - if (parameter < ctorParameters.size() - 1) - { - constructor += ", "; - } - } - - constructor += ")\n" - "{\n"; - - if (ctorType.getStruct()) - { - constructor += " " + ctorName + " structure = {"; - } - else - { - constructor += " return " + typeString(ctorType) + "("; - } - - if (ctorType.isMatrix() && ctorParameters.size() == 1) - { - int dim = ctorType.getNominalSize(); - const TType ¶meter = ctorParameters[0]; - - if (parameter.isScalar()) - { - for (int row = 0; row < dim; row++) - { - for (int col = 0; col < dim; col++) - { - constructor += TString((row == col) ? "x0" : "0.0"); - - if (row < dim - 1 || col < dim - 1) - { - constructor += ", "; - } - } - } - } - else if (parameter.isMatrix()) - { - for (int row = 0; row < dim; row++) - { - for (int col = 0; col < dim; col++) - { - if (row < parameter.getNominalSize() && col < parameter.getNominalSize()) - { - constructor += TString("x0") + "[" + str(row) + "]" + "[" + str(col) + "]"; - } - else - { - constructor += TString((row == col) ? "1.0" : "0.0"); - } - - if (row < dim - 1 || col < dim - 1) - { - constructor += ", "; - } - } - } - } - else UNREACHABLE(); - } - else - { - size_t remainingComponents = ctorType.getObjectSize(); - size_t parameterIndex = 0; - - while (remainingComponents > 0) - { - const TType ¶meter = ctorParameters[parameterIndex]; - const size_t parameterSize = parameter.getObjectSize(); - bool moreParameters = parameterIndex + 1 < ctorParameters.size(); - - constructor += "x" + str(static_cast<int>(parameterIndex)); - - if (parameter.isScalar()) - { - ASSERT(parameterSize <= remainingComponents); - remainingComponents -= parameterSize; - } - 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() || parameter.getStruct()) - { - ASSERT(remainingComponents == parameterSize || moreParameters); - ASSERT(parameterSize <= remainingComponents); - - remainingComponents -= parameterSize; - } - else UNREACHABLE(); - - if (moreParameters) - { - parameterIndex++; - } - - if (remainingComponents) - { - constructor += ", "; - } - } - } - - if (ctorType.getStruct()) - { - constructor += "};\n" - " return structure;\n" - "}\n"; - } - else - { - constructor += ");\n" - "}\n"; - } - - mConstructors.insert(constructor); -} - -const ConstantUnion *OutputHLSL::writeConstantUnion(const TType &type, const ConstantUnion *constUnion) -{ - TInfoSinkBase &out = mBody; - - if (type.getBasicType() == EbtStruct) - { - out << structLookup(type.getStruct()->name()) + "_ctor("; - - const TFieldList &fields = type.getStruct()->fields(); - - for (size_t i = 0; i < fields.size(); i++) - { - const TType *fieldType = fields[i]->type(); - - constUnion = writeConstantUnion(*fieldType, constUnion); - - if (i != fields.size() - 1) - { - out << ", "; - } - } - - out << ")"; - } - else - { - size_t size = type.getObjectSize(); - bool writeType = size > 1; - - if (writeType) - { - out << typeString(type) << "("; - } - - for (size_t i = 0; i < size; i++, constUnion++) - { - switch (constUnion->getType()) - { - case EbtFloat: out << std::min(FLT_MAX, std::max(-FLT_MAX, constUnion->getFConst())); break; - case EbtInt: out << constUnion->getIConst(); break; - case EbtBool: out << constUnion->getBConst(); break; - default: UNREACHABLE(); - } - - if (i != size - 1) - { - out << ", "; - } - } - - if (writeType) - { - out << ")"; - } - } - - return constUnion; -} - -TString OutputHLSL::scopeString(unsigned int depthLimit) -{ - TString string; - - for (unsigned int i = 0; i < mScopeBracket.size() && i < depthLimit; i++) - { - string += "_" + str(i); - } - - return string; -} - -TString OutputHLSL::scopedStruct(const TString &typeName) -{ - if (typeName == "") - { - return typeName; - } - - return typeName + scopeString(mScopeDepth); -} - -TString OutputHLSL::structLookup(const TString &typeName) -{ - for (int depth = mScopeDepth; depth >= 0; depth--) - { - TString scopedName = decorate(typeName + scopeString(depth)); - - for (StructNames::iterator structName = mStructNames.begin(); structName != mStructNames.end(); structName++) - { - if (*structName == scopedName) - { - return scopedName; - } - } - } - - UNREACHABLE(); // Should have found a matching constructor - - return typeName; -} - -TString OutputHLSL::decorate(const TString &string) -{ - if (string.compare(0, 3, "gl_") != 0 && string.compare(0, 3, "dx_") != 0) - { - return "_" + string; - } - - return string; -} - -TString OutputHLSL::decorateUniform(const TString &string, const TType &type) -{ - if (type.getBasicType() == EbtSamplerExternalOES) - { - return "ex_" + string; - } - - return decorate(string); -} - -TString OutputHLSL::decorateField(const TString &string, const TType &structure) -{ - if (structure.getStruct()->name().compare(0, 3, "gl_") != 0) - { - return decorate(string); - } - - return string; -} - -TString OutputHLSL::registerString(TIntermSymbol *operand) -{ - ASSERT(operand->getQualifier() == EvqUniform); - - if (IsSampler(operand->getBasicType())) - { - return "s" + str(samplerRegister(operand)); - } - - return "c" + str(uniformRegister(operand)); -} - -int OutputHLSL::samplerRegister(TIntermSymbol *sampler) -{ - const TType &type = sampler->getType(); - ASSERT(IsSampler(type.getBasicType())); - - int index = mSamplerRegister; - mSamplerRegister += sampler->totalRegisterCount(); - - declareUniform(type, sampler->getSymbol(), index); - - return index; -} - -int OutputHLSL::uniformRegister(TIntermSymbol *uniform) -{ - const TType &type = uniform->getType(); - ASSERT(!IsSampler(type.getBasicType())); - - int index = mUniformRegister; - mUniformRegister += uniform->totalRegisterCount(); - - declareUniform(type, uniform->getSymbol(), index); - - return index; -} - -void OutputHLSL::declareUniform(const TType &type, const TString &name, int index) -{ - TStructure *structure = type.getStruct(); - - if (!structure) - { - mActiveUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(), index)); - } - else - { - const TFieldList &fields = structure->fields(); - - if (type.isArray()) - { - int elementIndex = index; - - for (int i = 0; i < type.getArraySize(); i++) - { - for (size_t j = 0; j < fields.size(); j++) - { - const TType &fieldType = *fields[j]->type(); - const TString uniformName = name + "[" + str(i) + "]." + fields[j]->name(); - declareUniform(fieldType, uniformName, elementIndex); - elementIndex += fieldType.totalRegisterCount(); - } - } - } - else - { - int fieldIndex = index; - - for (size_t i = 0; i < fields.size(); i++) - { - const TType &fieldType = *fields[i]->type(); - const TString uniformName = name + "." + fields[i]->name(); - declareUniform(fieldType, uniformName, fieldIndex); - fieldIndex += fieldType.totalRegisterCount(); - } - } - } -} - -GLenum OutputHLSL::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.getNominalSize()) - { - case 2: return GL_FLOAT_MAT2; - case 3: return GL_FLOAT_MAT3; - case 4: return GL_FLOAT_MAT4; - 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() == 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(); - } - else if (type.getBasicType() == EbtSampler2D) - { - return GL_SAMPLER_2D; - } - else if (type.getBasicType() == EbtSamplerCube) - { - return GL_SAMPLER_CUBE; - } - else UNREACHABLE(); - - return GL_NONE; -} - -GLenum OutputHLSL::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) - { - 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; -} - -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.h deleted file mode 100644 index 586a76fb9..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/OutputHLSL.h +++ /dev/null @@ -1,167 +0,0 @@ -// -// 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_OUTPUTHLSL_H_ -#define COMPILER_OUTPUTHLSL_H_ - -#include <list> -#include <set> -#include <map> - -#define GL_APICALL -#include <GLES2/gl2.h> - -#include "compiler/intermediate.h" -#include "compiler/ParseContext.h" -#include "compiler/Uniform.h" - -namespace sh -{ -class UnfoldShortCircuit; - -class OutputHLSL : public TIntermTraverser -{ - public: - OutputHLSL(TParseContext &context, const ShBuiltInResources& resources, ShShaderOutput outputType); - ~OutputHLSL(); - - void output(); - - TInfoSinkBase &getBodyStream(); - const ActiveUniforms &getUniforms(); - - TString typeString(const TType &type); - TString textureString(const TType &type); - static TString qualifierString(TQualifier qualifier); - static TString arrayString(const TType &type); - static TString initializer(const TType &type); - static TString decorate(const TString &string); // Prepends an underscore to avoid naming clashes - static TString decorateUniform(const TString &string, const TType &type); - static TString decorateField(const TString &string, const TType &structure); - - protected: - void header(); - - // Visit AST nodes and output their code to the body stream - void visitSymbol(TIntermSymbol*); - void visitConstantUnion(TIntermConstantUnion*); - bool visitBinary(Visit visit, TIntermBinary*); - bool visitUnary(Visit visit, TIntermUnary*); - bool visitSelection(Visit visit, TIntermSelection*); - bool visitAggregate(Visit visit, TIntermAggregate*); - bool visitLoop(Visit visit, TIntermLoop*); - bool visitBranch(Visit visit, TIntermBranch*); - - void traverseStatements(TIntermNode *node); - bool isSingleStatement(TIntermNode *node); - bool handleExcessiveLoop(TIntermLoop *node); - void outputTriplet(Visit visit, const TString &preString, const TString &inString, const TString &postString); - void outputLineDirective(int line); - TString argumentString(const TIntermSymbol *symbol); - int vectorSize(const TType &type) const; - - void addConstructor(const TType &type, const TString &name, const TIntermSequence *parameters); - const ConstantUnion *writeConstantUnion(const TType &type, const ConstantUnion *constUnion); - - TString scopeString(unsigned int depthLimit); - TString scopedStruct(const TString &typeName); - TString structLookup(const TString &typeName); - - TParseContext &mContext; - const ShShaderOutput mOutputType; - UnfoldShortCircuit *mUnfoldShortCircuit; - bool mInsideFunction; - - // Output streams - TInfoSinkBase mHeader; - TInfoSinkBase mBody; - TInfoSinkBase mFooter; - - typedef std::map<TString, TIntermSymbol*> ReferencedSymbols; - ReferencedSymbols mReferencedUniforms; - ReferencedSymbols mReferencedAttributes; - ReferencedSymbols mReferencedVaryings; - - // Parameters determining what goes in the header output - bool mUsesTexture2D; - bool mUsesTexture2D_bias; - bool mUsesTexture2DLod; - bool mUsesTexture2DProj; - bool mUsesTexture2DProj_bias; - bool mUsesTexture2DProjLod; - bool mUsesTextureCube; - bool mUsesTextureCube_bias; - bool mUsesTextureCubeLod; - bool mUsesTexture2DLod0; - bool mUsesTexture2DLod0_bias; - bool mUsesTexture2DProjLod0; - bool mUsesTexture2DProjLod0_bias; - bool mUsesTextureCubeLod0; - bool mUsesTextureCubeLod0_bias; - bool mUsesFragColor; - bool mUsesFragData; - bool mUsesDepthRange; - bool mUsesFragCoord; - bool mUsesPointCoord; - bool mUsesFrontFacing; - bool mUsesPointSize; - bool mUsesFragDepth; - bool mUsesXor; - bool mUsesMod1; - bool mUsesMod2v; - bool mUsesMod2f; - bool mUsesMod3v; - bool mUsesMod3f; - bool mUsesMod4v; - bool mUsesMod4f; - bool mUsesFaceforward1; - bool mUsesFaceforward2; - bool mUsesFaceforward3; - bool mUsesFaceforward4; - bool mUsesAtan2_1; - bool mUsesAtan2_2; - bool mUsesAtan2_3; - bool mUsesAtan2_4; - bool mUsesDiscardRewriting; - - int mNumRenderTargets; - - typedef std::set<TString> Constructors; - Constructors mConstructors; - - typedef std::set<TString> StructNames; - StructNames mStructNames; - - typedef std::list<TString> StructDeclarations; - StructDeclarations mStructDeclarations; - - typedef std::vector<int> ScopeBracket; - ScopeBracket mScopeBracket; - unsigned int mScopeDepth; - - int mUniqueIndex; // For creating unique names - - bool mContainsLoopDiscontinuity; - bool mOutputLod0Function; - bool mInsideDiscontinuousLoop; - - TIntermSymbol *mExcessiveLoopIndex; - - int mUniformRegister; - int mSamplerRegister; - - TString registerString(TIntermSymbol *operand); - int samplerRegister(TIntermSymbol *sampler); - int uniformRegister(TIntermSymbol *uniform); - void declareUniform(const TType &type, const TString &name, int index); - static GLenum glVariableType(const TType &type); - static GLenum glVariablePrecision(const TType &type); - - ActiveUniforms mActiveUniforms; -}; -} - -#endif // COMPILER_OUTPUTHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/ParseContext.cpp b/Source/ThirdParty/ANGLE/src/compiler/ParseContext.cpp deleted file mode 100644 index 9a279523b..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ParseContext.cpp +++ /dev/null @@ -1,1602 +0,0 @@ -// -// 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/ParseContext.h" - -#include <stdarg.h> -#include <stdio.h> - -#include "compiler/glslang.h" -#include "compiler/preprocessor/SourceLocation.h" - -/////////////////////////////////////////////////////////////////////// -// -// Sub- vector and matrix fields -// -//////////////////////////////////////////////////////////////////////// - -// -// 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; -} - - -// -// Look at a '.' field selector string and change it into offsets -// for a matrix. -// -bool TParseContext::parseMatrixFields(const TString& compString, int matSize, TMatrixFields& fields, const TSourceLoc& line) -{ - fields.wholeRow = false; - fields.wholeCol = false; - fields.row = -1; - fields.col = -1; - - if (compString.size() != 2) { - error(line, "illegal length of matrix field selection", compString.c_str()); - return false; - } - - if (compString[0] == '_') { - if (compString[1] < '0' || compString[1] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.wholeCol = true; - fields.col = compString[1] - '0'; - } else if (compString[1] == '_') { - if (compString[0] < '0' || compString[0] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.wholeRow = true; - fields.row = compString[0] - '0'; - } else { - if (compString[0] < '0' || compString[0] > '3' || - compString[1] < '0' || compString[1] > '3') { - error(line, "illegal matrix field selection", compString.c_str()); - return false; - } - fields.row = compString[0] - '0'; - fields.col = compString[1] - '0'; - } - - if (fields.row >= matSize || fields.col >= matSize) { - error(line, "matrix field selection out of range", compString.c_str()); - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////// -// -// Errors -// -//////////////////////////////////////////////////////////////////////// - -// -// Track whether errors have occurred. -// -void TParseContext::recover() -{ -} - -// -// 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) -{ - pp::SourceLocation srcLoc; - srcLoc.file = loc.first_file; - srcLoc.line = loc.first_line; - diagnostics.writeInfo(pp::Diagnostics::ERROR, - srcLoc, reason, token, extraInfo); - -} - -void TParseContext::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; - diagnostics.writeInfo(pp::Diagnostics::WARNING, - srcLoc, reason, token, extraInfo); -} - -void TParseContext::trace(const char* str) -{ - diagnostics.writeDebug(str); -} - -// -// 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()); -} - -bool TParseContext::precisionErrorCheck(const TSourceLoc& line, TPrecision precision, TBasicType type){ - if (!checksPrecisionErrors) - return false; - switch( type ){ - case EbtFloat: - if( precision == EbpUndefined ){ - error( line, "No precision specified for (float)", "" ); - return true; - } - break; - case EbtInt: - if( precision == EbpUndefined ){ - error( line, "No precision specified (int)", "" ); - return true; - } - break; - default: - return false; - } - return false; -} - -// -// 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. -// -// Returns true if the was an error. -// -bool TParseContext::lValueErrorCheck(const TSourceLoc& line, const char* op, TIntermTyped* node) -{ - TIntermSymbol* symNode = node->getAsSymbolNode(); - TIntermBinary* binaryNode = node->getAsBinaryNode(); - - if (binaryNode) { - bool errorReturn; - - switch(binaryNode->getOp()) { - case EOpIndexDirect: - case EOpIndexIndirect: - case EOpIndexDirectStruct: - return lValueErrorCheck(line, op, binaryNode->getLeft()); - case EOpVectorSwizzle: - errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft()); - if (!errorReturn) { - int offset[4] = {0,0,0,0}; - - TIntermTyped* rightNode = binaryNode->getRight(); - TIntermAggregate *aggrNode = rightNode->getAsAggregate(); - - for (TIntermSequence::iterator p = aggrNode->getSequence().begin(); - p != aggrNode->getSequence().end(); p++) { - int value = (*p)->getAsTyped()->getAsConstantUnion()->getIConst(0); - offset[value]++; - if (offset[value] > 1) { - error(line, " l-value of swizzle cannot have duplicate components", op); - - return true; - } - } - } - - return errorReturn; - default: - break; - } - error(line, " l-value required", op); - - return true; - } - - - 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 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; - default: - - // - // Type that can't be written to? - // - switch (node->getBasicType()) { - case EbtSampler2D: - case EbtSamplerCube: - message = "can't modify a sampler"; - break; - case EbtVoid: - message = "can't modify void"; - break; - default: - break; - } - } - - if (message == 0 && binaryNode == 0 && symNode == 0) { - error(line, " l-value required", op); - - return true; - } - - - // - // Everything else is okay, no error. - // - if (message == 0) - return false; - - // - // 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 true; -} - -// -// Both test, and if necessary spit out an error, to see if the node is really -// a constant. -// -// Returns true if the was an error. -// -bool TParseContext::constErrorCheck(TIntermTyped* node) -{ - if (node->getQualifier() == EvqConst) - return false; - - error(node->getLine(), "constant expression required", ""); - - return true; -} - -// -// Both test, and if necessary spit out an error, to see if the node is really -// an integer. -// -// Returns true if the was an error. -// -bool TParseContext::integerErrorCheck(TIntermTyped* node, const char* token) -{ - if (node->getBasicType() == EbtInt && node->getNominalSize() == 1) - return false; - - error(node->getLine(), "integer expression required", token); - - return true; -} - -// -// Both test, and if necessary spit out an error, to see if we are currently -// globally scoped. -// -// Returns true if the was an error. -// -bool TParseContext::globalErrorCheck(const TSourceLoc& line, bool global, const char* token) -{ - if (global) - return false; - - error(line, "only allowed at global scope", token); - - 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. -// -// Returns true if there was an error. -// -bool TParseContext::reservedErrorCheck(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 true; - } - if (isWebGLBasedSpec(shaderSpec)) { - if (identifier.compare(0, 6, "webgl_") == 0) { - error(line, reservedErrMsg, "webgl_"); - return true; - } - if (identifier.compare(0, 7, "_webgl_") == 0) { - error(line, reservedErrMsg, "_webgl_"); - return true; - } - if (shaderSpec == SH_CSS_SHADERS_SPEC && identifier.compare(0, 4, "css_") == 0) { - error(line, reservedErrMsg, "css_"); - return true; - } - } - if (identifier.find("__") != TString::npos) { - error(line, "identifiers containing two consecutive underscores (__) are reserved as possible future keywords", identifier.c_str()); - return true; - } - } - - return false; -} - -// -// 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. -// -// Returns true if there was an error in construction. -// -bool TParseContext::constructorErrorCheck(const TSourceLoc& line, TIntermNode* node, TFunction& function, TOperator op, TType* type) -{ - *type = function.getReturnType(); - - bool constructingMatrix = false; - switch(op) { - case EOpConstructMat2: - case EOpConstructMat3: - 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 constType = true; - bool full = false; - bool overFull = false; - bool matrixInMatrix = false; - bool arrayArg = false; - for (size_t i = 0; i < function.getParamCount(); ++i) { - const TParameter& 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->getQualifier() != EvqConst) - constType = false; - if (param.type->isArray()) - arrayArg = true; - } - - if (constType) - type->setQualifier(EvqConst); - - if (type->isArray() && static_cast<size_t>(type->getArraySize()) != function.getParamCount()) { - error(line, "array constructor needs one argument per array element", "constructor"); - return true; - } - - if (arrayArg && op != EOpConstructStruct) { - error(line, "constructing from a non-dereferenced array", "constructor"); - return true; - } - - if (matrixInMatrix && !type->isArray()) { - if (function.getParamCount() != 1) { - error(line, "constructing matrix from matrix can only take one argument", "constructor"); - return true; - } - } - - if (overFull) { - error(line, "too many arguments", "constructor"); - return true; - } - - if (op == EOpConstructStruct && !type->isArray() && int(type->getStruct()->fields().size()) != function.getParamCount()) { - error(line, "Number of constructor parameters does not match the number of structure fields", "constructor"); - return true; - } - - 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 true; - } - } - - TIntermTyped *typed = node ? node->getAsTyped() : 0; - if (typed == 0) { - error(line, "constructor argument does not have a type", "constructor"); - return true; - } - if (op != EOpConstructStruct && IsSampler(typed->getBasicType())) { - error(line, "cannot convert a sampler", "constructor"); - return true; - } - if (typed->getBasicType() == EbtVoid) { - error(line, "cannot convert a void", "constructor"); - return true; - } - - return false; -} - -// 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::voidErrorCheck(const TSourceLoc& line, const TString& identifier, const TPublicType& pubType) -{ - if (pubType.type == EbtVoid) { - error(line, "illegal use of type 'void'", identifier.c_str()); - return true; - } - - return false; -} - -// This function checks to see if the node (for the expression) contains a scalar boolean expression or not -// -// returns true in case of an error -// -bool TParseContext::boolErrorCheck(const TSourceLoc& line, const TIntermTyped* type) -{ - if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) { - error(line, "boolean expression expected", ""); - return true; - } - - return false; -} - -// This function checks to see if the node (for the expression) contains a scalar boolean expression or not -// -// returns true in case of an error -// -bool TParseContext::boolErrorCheck(const TSourceLoc& line, const TPublicType& pType) -{ - if (pType.type != EbtBool || pType.array || pType.matrix || (pType.size > 1)) { - error(line, "boolean expression expected", ""); - return true; - } - - return false; -} - -bool TParseContext::samplerErrorCheck(const TSourceLoc& line, const TPublicType& pType, const char* reason) -{ - if (pType.type == EbtStruct) { - if (containsSampler(*pType.userDef)) { - error(line, reason, getBasicString(pType.type), "(structure contains a sampler)"); - - return true; - } - - return false; - } else if (IsSampler(pType.type)) { - error(line, reason, getBasicString(pType.type)); - - return true; - } - - return false; -} - -bool TParseContext::structQualifierErrorCheck(const TSourceLoc& line, const TPublicType& pType) -{ - if ((pType.qualifier == EvqVaryingIn || pType.qualifier == EvqVaryingOut || pType.qualifier == EvqAttribute) && - pType.type == EbtStruct) { - error(line, "cannot be used with a structure", getQualifierString(pType.qualifier)); - - return true; - } - - if (pType.qualifier != EvqUniform && samplerErrorCheck(line, pType, "samplers must be uniform")) - return true; - - return false; -} - -bool TParseContext::parameterSamplerErrorCheck(const TSourceLoc& line, TQualifier qualifier, const TType& type) -{ - if ((qualifier == EvqOut || qualifier == EvqInOut) && - type.getBasicType() != EbtStruct && IsSampler(type.getBasicType())) { - error(line, "samplers cannot be output parameters", type.getBasicString()); - return true; - } - - return false; -} - -bool TParseContext::containsSampler(TType& type) -{ - if (IsSampler(type.getBasicType())) - return true; - - if (type.getBasicType() == EbtStruct) { - const TFieldList& fields = type.getStruct()->fields(); - for (unsigned int i = 0; i < fields.size(); ++i) { - if (containsSampler(*fields[i]->type())) - return true; - } - } - - return false; -} - -// -// Do size checking for an array type's size. -// -// Returns true if there was an error. -// -bool TParseContext::arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* expr, int& size) -{ - TIntermConstantUnion* constant = expr->getAsConstantUnion(); - if (constant == 0 || constant->getBasicType() != EbtInt) { - error(line, "array size must be a constant integer expression", ""); - return true; - } - - size = constant->getIConst(0); - - if (size <= 0) { - error(line, "array size must be a positive integer", ""); - size = 1; - return true; - } - - return false; -} - -// -// See if this qualifier can be an array. -// -// Returns true if there is an error. -// -bool TParseContext::arrayQualifierErrorCheck(const TSourceLoc& line, TPublicType type) -{ - if ((type.qualifier == EvqAttribute) || (type.qualifier == EvqConst)) { - error(line, "cannot declare arrays of this qualifier", TType(type).getCompleteString().c_str()); - return true; - } - - return false; -} - -// -// See if this type can be an array. -// -// Returns true if there is an error. -// -bool TParseContext::arrayTypeErrorCheck(const TSourceLoc& line, TPublicType type) -{ - // - // Can the type be an array? - // - if (type.array) { - error(line, "cannot declare arrays of arrays", TType(type).getCompleteString().c_str()); - return true; - } - - return false; -} - -// -// Do all the semantic checking for declaring an array, with and -// without a size, and make the right changes to the symbol table. -// -// size == 0 means no specified size. -// -// Returns true if there was an error. -// -bool TParseContext::arrayErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType type, TVariable*& variable) -{ - // - // Don't check for reserved word use until after we know it's not in the symbol table, - // because reserved arrays can be redeclared. - // - - bool builtIn = false; - bool sameScope = false; - TSymbol* symbol = symbolTable.find(identifier, &builtIn, &sameScope); - if (symbol == 0 || !sameScope) { - if (reservedErrorCheck(line, identifier)) - return true; - - variable = new TVariable(&identifier, TType(type)); - - if (type.arraySize) - variable->getType().setArraySize(type.arraySize); - - if (! symbolTable.insert(*variable)) { - delete variable; - error(line, "INTERNAL ERROR inserting new symbol", identifier.c_str()); - return true; - } - } else { - if (! symbol->isVariable()) { - error(line, "variable expected", identifier.c_str()); - return true; - } - - variable = static_cast<TVariable*>(symbol); - if (! variable->getType().isArray()) { - error(line, "redeclaring non-array as array", identifier.c_str()); - return true; - } - if (variable->getType().getArraySize() > 0) { - error(line, "redeclaration of array with size", identifier.c_str()); - return true; - } - - if (! variable->getType().sameElementType(TType(type))) { - error(line, "redeclaration of array with a different type", identifier.c_str()); - return true; - } - - if (type.arraySize) - variable->getType().setArraySize(type.arraySize); - } - - if (voidErrorCheck(line, identifier, type)) - return true; - - return false; -} - -// -// Enforce non-initializer type/qualifier rules. -// -// Returns true if there was an error. -// -bool TParseContext::nonInitConstErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType& type, bool array) -{ - if (type.qualifier == EvqConst) - { - // Make the qualifier make sense. - type.qualifier = EvqTemporary; - - if (array) - { - error(line, "arrays may not be declared constant since they cannot be initialized", identifier.c_str()); - } - else if (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 true; - } - - return false; -} - -// -// Do semantic checking for a variable declaration that has no initializer, -// and update the symbol table. -// -// Returns true if there was an error. -// -bool TParseContext::nonInitErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType& type, TVariable*& variable) -{ - if (reservedErrorCheck(line, identifier)) - recover(); - - variable = new TVariable(&identifier, TType(type)); - - if (! symbolTable.insert(*variable)) { - error(line, "redefinition", variable->getName().c_str()); - delete variable; - variable = 0; - return true; - } - - if (voidErrorCheck(line, identifier, type)) - return true; - - return false; -} - -bool TParseContext::paramErrorCheck(const TSourceLoc& line, TQualifier qualifier, TQualifier paramQualifier, TType* type) -{ - if (qualifier != EvqConst && qualifier != EvqTemporary) { - error(line, "qualifier not allowed on function parameter", getQualifierString(qualifier)); - return true; - } - if (qualifier == EvqConst && paramQualifier != EvqIn) { - error(line, "qualifier not allowed with ", getQualifierString(qualifier), getQualifierString(paramQualifier)); - return true; - } - - if (qualifier == EvqConst) - type->setQualifier(EvqConstReadOnly); - else - type->setQualifier(paramQualifier); - - return false; -} - -bool TParseContext::extensionErrorCheck(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 true; - } - // 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 true; - } - if (iter->second == EBhWarn) { - warning(line, "extension", extension.c_str(), "is being used"); - return false; - } - - return false; -} - -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 -{ - const TExtensionBehavior& extbehavior = extensionBehavior(); - TExtensionBehavior::const_iterator iter = extbehavior.find(extension); - - if (iter == extbehavior.end()) - { - return false; - } - - return (iter->second == EBhEnable || iter->second == EBhRequire); -} - -///////////////////////////////////////////////////////////////////////////////// -// -// Non-Errors. -// -///////////////////////////////////////////////////////////////////////////////// - -// -// 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, 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(), builtIn); - if (symbol == 0 || symbol->isFunction()) { - symbol = symbolTable.find(call->getMangledName(), 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. -// -bool TParseContext::executeInitializer(const TSourceLoc& line, TString& identifier, TPublicType& pType, - TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable) -{ - TType type = TType(pType); - - if (variable == 0) { - if (reservedErrorCheck(line, identifier)) - return true; - - if (voidErrorCheck(line, identifier, pType)) - return true; - - // - // add variable to symbol table - // - variable = new TVariable(&identifier, type); - if (! symbolTable.insert(*variable)) { - error(line, "redefinition", variable->getName().c_str()); - return true; - // don't delete variable, it's used by error recovery, and the pool - // pop will take care of the memory - } - } - - // - // 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; - } - if (initializer->getAsConstantUnion()) { - variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer()); - } else if (initializer->getAsSymbolNode()) { - const TSymbol* symbol = symbolTable.find(initializer->getAsSymbolNode()->getSymbol()); - const TVariable* tVar = static_cast<const TVariable*>(symbol); - - ConstantUnion* constArray = tVar->getConstPointer(); - variable->shareConstPointer(constArray); - } else { - std::stringstream extraInfoStream; - extraInfoStream << "'" << variable->getType().getCompleteString() << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, " cannot assign to", "=", extraInfo.c_str()); - variable->getType().setQualifier(EvqTemporary); - return true; - } - } - - if (qualifier != EvqConst) { - TIntermSymbol* intermSymbol = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), line); - intermNode = intermediate.addAssign(EOpInitialize, intermSymbol, initializer, line); - if (intermNode == 0) { - assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); - return true; - } - } else - intermNode = 0; - - return false; -} - -bool TParseContext::areAllChildConst(TIntermAggregate* aggrNode) -{ - ASSERT(aggrNode != NULL); - if (!aggrNode->isConstructor()) - return false; - - bool allConstant = true; - - // check if all the child nodes are constants so that they can be inserted into - // the parent node - TIntermSequence &sequence = aggrNode->getSequence() ; - for (TIntermSequence::iterator p = sequence.begin(); p != sequence.end(); ++p) { - if (!(*p)->getAsTyped()->getAsConstantUnion()) - return false; - } - - return allConstant; -} - -// 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 0 for an error or the constructed node (aggregate or typed) for no error. -// -TIntermTyped* TParseContext::addConstructor(TIntermNode* node, const TType* type, TOperator op, TFunction* fnCall, const TSourceLoc& line) -{ - if (node == 0) - return 0; - - TIntermAggregate* aggrNode = node->getAsAggregate(); - - TFieldList::const_iterator memberFields; - if (op == EOpConstructStruct) - memberFields = type->getStruct()->fields().begin(); - - TType elementType = *type; - if (type->isArray()) - elementType.clearArrayness(); - - bool singleArg; - if (aggrNode) { - if (aggrNode->getOp() != EOpNull || aggrNode->getSequence().size() == 1) - singleArg = true; - else - singleArg = false; - } else - singleArg = true; - - TIntermTyped *newNode; - if (singleArg) { - // If structure constructor or array constructor is being called - // for only one parameter inside the structure, we need to call constructStruct function once. - if (type->isArray()) - newNode = constructStruct(node, &elementType, 1, node->getLine(), false); - else if (op == EOpConstructStruct) - newNode = constructStruct(node, (*memberFields)->type(), 1, node->getLine(), false); - else - newNode = constructBuiltIn(type, op, node, node->getLine(), false); - - if (newNode && newNode->getAsAggregate()) { - TIntermTyped* constConstructor = foldConstConstructor(newNode->getAsAggregate(), *type); - if (constConstructor) - return constConstructor; - } - - return newNode; - } - - // - // Handle list of arguments. - // - TIntermSequence &sequenceVector = aggrNode->getSequence() ; // Stores the information about the parameter to the constructor - // if the structure constructor contains more than one parameter, then construct - // each parameter - - int paramCount = 0; // keeps a track of the constructor parameter number being checked - - // for each parameter to the constructor call, check to see if the right type is passed or convert them - // to the right type if possible (and allowed). - // for structure constructors, just check if the right type is passed, no conversion is allowed. - - for (TIntermSequence::iterator p = sequenceVector.begin(); - p != sequenceVector.end(); p++, paramCount++) { - if (type->isArray()) - newNode = constructStruct(*p, &elementType, paramCount+1, node->getLine(), true); - else if (op == EOpConstructStruct) - newNode = constructStruct(*p, memberFields[paramCount]->type(), paramCount+1, node->getLine(), true); - else - newNode = constructBuiltIn(type, op, *p, node->getLine(), true); - - if (newNode) { - *p = newNode; - } - } - - TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, line); - TIntermTyped* constConstructor = foldConstConstructor(constructor->getAsAggregate(), *type); - if (constConstructor) - return constConstructor; - - return constructor; -} - -TIntermTyped* TParseContext::foldConstConstructor(TIntermAggregate* aggrNode, const TType& type) -{ - bool canBeFolded = areAllChildConst(aggrNode); - aggrNode->setType(type); - if (canBeFolded) { - bool returnVal = false; - ConstantUnion* unionArray = new ConstantUnion[type.getObjectSize()]; - if (aggrNode->getSequence().size() == 1) { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, aggrNode->getOp(), symbolTable, type, true); - } - else { - returnVal = intermediate.parseConstTree(aggrNode->getLine(), aggrNode, unionArray, aggrNode->getOp(), symbolTable, type); - } - if (returnVal) - return 0; - - return intermediate.addConstantUnion(unionArray, type, aggrNode->getLine()); - } - - return 0; -} - -// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value -// for the parameter to the constructor (passed to this function). Essentially, it converts -// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a -// float, then float is converted to int. -// -// Returns 0 for an error or the constructed node. -// -TIntermTyped* TParseContext::constructBuiltIn(const TType* type, TOperator op, TIntermNode* node, const TSourceLoc& line, bool subset) -{ - TIntermTyped* newNode; - TOperator basicOp; - - // - // First, convert types as needed. - // - switch (op) { - case EOpConstructVec2: - case EOpConstructVec3: - case EOpConstructVec4: - case EOpConstructMat2: - case EOpConstructMat3: - case EOpConstructMat4: - case EOpConstructFloat: - basicOp = EOpConstructFloat; - break; - - case EOpConstructIVec2: - case EOpConstructIVec3: - case EOpConstructIVec4: - case EOpConstructInt: - basicOp = EOpConstructInt; - break; - - case EOpConstructBVec2: - case EOpConstructBVec3: - case EOpConstructBVec4: - case EOpConstructBool: - basicOp = EOpConstructBool; - break; - - default: - error(line, "unsupported construction", ""); - recover(); - - return 0; - } - newNode = intermediate.addUnaryMath(basicOp, node, node->getLine(), symbolTable); - if (newNode == 0) { - error(line, "can't convert", "constructor"); - return 0; - } - - // - // Now, if there still isn't an operation to do the construction, and we need one, add one. - // - - // Otherwise, skip out early. - if (subset || (newNode != node && newNode->getType() == *type)) - return newNode; - - // setAggregateOperator will insert a new node for the constructor, as needed. - return intermediate.setAggregateOperator(newNode, op, line); -} - -// This function tests for the type of the parameters to the structures constructors. Raises -// an error message if the expected type does not match the parameter passed to the constructor. -// -// Returns 0 for an error or the input node itself if the expected and the given parameter types match. -// -TIntermTyped* TParseContext::constructStruct(TIntermNode* node, TType* type, int paramCount, const TSourceLoc& line, bool subset) -{ - if (*type == node->getAsTyped()->getType()) { - if (subset) - return node->getAsTyped(); - else - return intermediate.setAggregateOperator(node->getAsTyped(), EOpConstructStruct, line); - } else { - std::stringstream extraInfoStream; - extraInfoStream << "cannot convert parameter " << paramCount - << " from '" << node->getAsTyped()->getType().getBasicString() - << "' to '" << type->getBasicString() << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, "", "constructor", extraInfo.c_str()); - recover(); - } - - return 0; -} - -// -// This function returns the tree representation for the vector field(s) being accessed from contant vector. -// If only one component of vector is accessed (v.x or v[0] where v is a contant vector), then a contant node is -// returned, else an aggregate node is returned (for v.xy). The input to this function could either be the symbol -// node or it could be the intermediate tree representation of accessing fields in a constant structure or column of -// a constant matrix. -// -TIntermTyped* TParseContext::addConstVectorNode(TVectorFields& fields, TIntermTyped* node, const TSourceLoc& line) -{ - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - - ConstantUnion *unionArray; - if (tempConstantNode) { - unionArray = tempConstantNode->getUnionArrayPointer(); - - if (!unionArray) { - return node; - } - } else { // The node has to be either a symbol node or an aggregate node or a tempConstant node, else, its an error - error(line, "Cannot offset into the vector", "Error"); - recover(); - - return 0; - } - - ConstantUnion* constArray = new ConstantUnion[fields.num]; - - for (int i = 0; i < fields.num; i++) { - if (fields.offsets[i] >= node->getType().getNominalSize()) { - std::stringstream extraInfoStream; - extraInfoStream << "vector field selection out of range '" << fields.offsets[i] << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - fields.offsets[i] = 0; - } - - constArray[i] = unionArray[fields.offsets[i]]; - - } - typedNode = intermediate.addConstantUnion(constArray, node->getType(), line); - return typedNode; -} - -// -// This function returns the column being accessed from a constant matrix. The values are retrieved from -// the symbol table and parse-tree is built for a vector (each column of a matrix is a vector). The input -// to the function could either be a symbol node (m[0] where m is a constant matrix)that represents a -// constant matrix or it could be the tree representation of the constant matrix (s.m1[0] where s is a constant structure) -// -TIntermTyped* TParseContext::addConstMatrixNode(int index, TIntermTyped* node, const TSourceLoc& line) -{ - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - - if (index >= node->getType().getNominalSize()) { - std::stringstream extraInfoStream; - extraInfoStream << "matrix field selection out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; - } - - if (tempConstantNode) { - ConstantUnion* unionArray = tempConstantNode->getUnionArrayPointer(); - int size = tempConstantNode->getType().getNominalSize(); - typedNode = intermediate.addConstantUnion(&unionArray[size*index], tempConstantNode->getType(), line); - } else { - error(line, "Cannot offset into the matrix", "Error"); - recover(); - - return 0; - } - - return typedNode; -} - - -// -// This function returns an element of an array accessed from a constant array. The values are retrieved from -// the symbol table and parse-tree is built for the type of the element. The input -// to the function could either be a symbol node (a[0] where a is a constant array)that represents a -// constant array or it could be the tree representation of the constant array (s.a1[0] where s is a constant structure) -// -TIntermTyped* TParseContext::addConstArrayNode(int index, TIntermTyped* node, const TSourceLoc& line) -{ - TIntermTyped* typedNode; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - TType arrayElementType = node->getType(); - arrayElementType.clearArrayness(); - - if (index >= node->getType().getArraySize()) { - std::stringstream extraInfoStream; - extraInfoStream << "array field selection out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(line, "", "[", extraInfo.c_str()); - recover(); - index = 0; - } - - if (tempConstantNode) { - size_t arrayElementSize = arrayElementType.getObjectSize(); - ConstantUnion* unionArray = tempConstantNode->getUnionArrayPointer(); - typedNode = intermediate.addConstantUnion(&unionArray[arrayElementSize * index], tempConstantNode->getType(), line); - } else { - error(line, "Cannot offset into the array", "Error"); - recover(); - - return 0; - } - - return typedNode; -} - - -// -// This function returns the value of a particular field inside a constant structure from the symbol table. -// If there is an embedded/nested struct, it appropriately calls addConstStructNested or addConstStructFromAggr -// function and returns the parse-tree with the values of the embedded/nested struct. -// -TIntermTyped* TParseContext::addConstStruct(TString& identifier, TIntermTyped* node, const TSourceLoc& line) -{ - const TFieldList& fields = node->getType().getStruct()->fields(); - - size_t instanceSize = 0; - for (size_t index = 0; index < fields.size(); ++index) { - if (fields[index]->name() == identifier) { - break; - } else { - instanceSize += fields[index]->type()->getObjectSize(); - } - } - - TIntermTyped* typedNode = 0; - TIntermConstantUnion* tempConstantNode = node->getAsConstantUnion(); - if (tempConstantNode) { - ConstantUnion* constArray = tempConstantNode->getUnionArrayPointer(); - - typedNode = intermediate.addConstantUnion(constArray+instanceSize, tempConstantNode->getType(), line); // type will be changed in the calling function - } else { - error(line, "Cannot offset into the structure", "Error"); - recover(); - - return 0; - } - - return typedNode; -} - -bool TParseContext::enterStructDeclaration(const TSourceLoc& line, const TString& identifier) -{ - ++structNestingLevel; - - // 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 (structNestingLevel > 1) { - error(line, "", "Embedded struct definitions are not allowed"); - return true; - } - - return false; -} - -void TParseContext::exitStructDeclaration() -{ - --structNestingLevel; -} - -namespace { - -const int kWebGLMaxStructNesting = 4; - -} // namespace - -bool TParseContext::structNestingErrorCheck(const TSourceLoc& line, const TField& field) -{ - if (!isWebGLBasedSpec(shaderSpec)) { - return false; - } - - if (field.type()->getBasicType() != EbtStruct) { - return false; - } - - // 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 true; - } - - return false; -} - -// -// Parse an array index expression -// -TIntermTyped* TParseContext::addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc& location, TIntermTyped *indexExpression) -{ - TIntermTyped *indexedExpression = NULL; - - if (!baseExpression->isArray() && !baseExpression->isMatrix() && !baseExpression->isVector()) - { - if (baseExpression->getAsSymbolNode()) - { - error(location, " left of '[' is not of type array, matrix, or vector ", baseExpression->getAsSymbolNode()->getSymbol().c_str()); - } - else - { - error(location, " left of '[' is not of type array, matrix, or vector ", "expression"); - } - recover(); - } - - if (indexExpression->getQualifier() == EvqConst) - { - int index = indexExpression->getAsConstantUnion()->getIConst(0); - if (index < 0) - { - std::stringstream infoStream; - infoStream << index; - std::string info = infoStream.str(); - error(location, "negative index", info.c_str()); - recover(); - index = 0; - } - if (baseExpression->getType().getQualifier() == EvqConst) - { - if (baseExpression->isArray()) - { - // constant folding for arrays - indexedExpression = addConstArrayNode(index, baseExpression, location); - } - else if (baseExpression->isVector()) - { - // constant folding for vectors - TVectorFields fields; - fields.num = 1; - fields.offsets[0] = index; // need to do it this way because v.xy sends fields integer array - indexedExpression = addConstVectorNode(fields, baseExpression, location); - } - else if (baseExpression->isMatrix()) - { - // constant folding for matrices - indexedExpression = addConstMatrixNode(index, baseExpression, location); - } - } - else - { - if (baseExpression->isArray()) - { - if (index >= baseExpression->getType().getArraySize()) - { - std::stringstream extraInfoStream; - extraInfoStream << "array index out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - index = baseExpression->getType().getArraySize() - 1; - } - else if (baseExpression->getQualifier() == EvqFragData && index > 0 && !isExtensionEnabled("GL_EXT_draw_buffers")) - { - error(location, "", "[", "array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is disabled"); - recover(); - index = 0; - } - } - else if ((baseExpression->isVector() || baseExpression->isMatrix()) && baseExpression->getType().getNominalSize() <= index) - { - std::stringstream extraInfoStream; - extraInfoStream << "field selection out of range '" << index << "'"; - std::string extraInfo = extraInfoStream.str(); - error(location, "", "[", extraInfo.c_str()); - recover(); - index = baseExpression->getType().getNominalSize() - 1; - } - - indexExpression->getAsConstantUnion()->getUnionArrayPointer()->setIConst(index); - indexedExpression = intermediate.addIndex(EOpIndexDirect, baseExpression, indexExpression, location); - } - } - else - { - indexedExpression = intermediate.addIndex(EOpIndexIndirect, baseExpression, indexExpression, location); - } - - if (indexedExpression == 0) - { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setFConst(0.0f); - indexedExpression = intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpHigh, EvqConst), location); - } - else if (baseExpression->isArray()) - { - const TType &baseType = baseExpression->getType(); - if (baseType.getStruct()) - { - TType copyOfType(baseType.getStruct()); - indexedExpression->setType(copyOfType); - } - else - { - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), EvqTemporary, baseExpression->getNominalSize(), baseExpression->isMatrix())); - } - - if (baseExpression->getType().getQualifier() == EvqConst) - { - indexedExpression->getTypePointer()->setQualifier(EvqConst); - } - } - else if (baseExpression->isMatrix()) - { - TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConst ? EvqConst : EvqTemporary; - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier, baseExpression->getNominalSize())); - } - else if (baseExpression->isVector()) - { - TQualifier qualifier = baseExpression->getType().getQualifier() == EvqConst ? EvqConst : EvqTemporary; - indexedExpression->setType(TType(baseExpression->getBasicType(), baseExpression->getPrecision(), qualifier)); - } - else - { - indexedExpression->setType(baseExpression->getType()); - } - - return indexedExpression; -} - -// -// 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; -} - - - diff --git a/Source/ThirdParty/ANGLE/src/compiler/ParseContext.h b/Source/ThirdParty/ANGLE/src/compiler/ParseContext.h deleted file mode 100644 index c2b3c3f7e..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ParseContext.h +++ /dev/null @@ -1,134 +0,0 @@ -// -// 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 _PARSER_HELPER_INCLUDED_ -#define _PARSER_HELPER_INCLUDED_ - -#include "compiler/Diagnostics.h" -#include "compiler/DirectiveHandler.h" -#include "compiler/localintermediate.h" -#include "compiler/preprocessor/Preprocessor.h" -#include "compiler/ShHandle.h" -#include "compiler/SymbolTable.h" - -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. -// -struct TParseContext { - TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, ShShaderType type, ShShaderSpec spec, int options, bool checksPrecErrors, const char* sourcePath, TInfoSink& is) : - intermediate(interm), - symbolTable(symt), - shaderType(type), - shaderSpec(spec), - compileOptions(options), - sourcePath(sourcePath), - treeRoot(0), - loopNestingLevel(0), - structNestingLevel(0), - currentFunctionType(NULL), - functionReturnsValue(false), - checksPrecisionErrors(checksPrecErrors), - diagnostics(is), - directiveHandler(ext, diagnostics), - preprocessor(&diagnostics, &directiveHandler), - scanner(NULL) { } - TIntermediate& intermediate; // to hold and build a parse tree - TSymbolTable& symbolTable; // symbol table that goes with the language currently being parsed - ShShaderType shaderType; // vertex or fragment language (future: pack or unpack) - ShShaderSpec shaderSpec; // The language specification compiler conforms to - GLES2 or WebGL. - int compileOptions; - const char* sourcePath; // Path of source file or NULL. - TIntermNode* treeRoot; // root of parse tree being created - int loopNestingLevel; // 0 if outside all loops - int structNestingLevel; // incremented while parsing a struct declaration - const TType* currentFunctionType; // the return type of the function that's currently being parsed - bool functionReturnsValue; // true if a non-void function has a return - bool checksPrecisionErrors; // true if an error will be generated when a variable is declared without precision, explicit or implicit. - bool fragmentPrecisionHigh; // true if highp precision is supported in the fragment language. - TString HashErrMsg; - TDiagnostics diagnostics; - TDirectiveHandler directiveHandler; - pp::Preprocessor preprocessor; - void* scanner; - - int numErrors() const { return diagnostics.numErrors(); } - TInfoSink& infoSink() { return diagnostics.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=""); - void trace(const char* str); - void recover(); - - bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc& line); - bool parseMatrixFields(const TString&, int matSize, TMatrixFields&, const TSourceLoc& line); - - bool reservedErrorCheck(const TSourceLoc& line, const TString& identifier); - 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); - bool precisionErrorCheck(const TSourceLoc& line, TPrecision precision, TBasicType type); - bool lValueErrorCheck(const TSourceLoc& line, const char* op, TIntermTyped*); - bool constErrorCheck(TIntermTyped* node); - bool integerErrorCheck(TIntermTyped* node, const char* token); - bool globalErrorCheck(const TSourceLoc& line, bool global, const char* token); - bool constructorErrorCheck(const TSourceLoc& line, TIntermNode*, TFunction&, TOperator, TType*); - bool arraySizeErrorCheck(const TSourceLoc& line, TIntermTyped* expr, int& size); - bool arrayQualifierErrorCheck(const TSourceLoc& line, TPublicType type); - bool arrayTypeErrorCheck(const TSourceLoc& line, TPublicType type); - bool arrayErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType type, TVariable*& variable); - bool voidErrorCheck(const TSourceLoc&, const TString&, const TPublicType&); - bool boolErrorCheck(const TSourceLoc&, const TIntermTyped*); - bool boolErrorCheck(const TSourceLoc&, const TPublicType&); - bool samplerErrorCheck(const TSourceLoc& line, const TPublicType& pType, const char* reason); - bool structQualifierErrorCheck(const TSourceLoc& line, const TPublicType& pType); - bool parameterSamplerErrorCheck(const TSourceLoc& line, TQualifier qualifier, const TType& type); - bool nonInitConstErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType& type, bool array); - bool nonInitErrorCheck(const TSourceLoc& line, TString& identifier, TPublicType& type, TVariable*& variable); - bool paramErrorCheck(const TSourceLoc& line, TQualifier qualifier, TQualifier paramQualifier, TType* type); - bool extensionErrorCheck(const TSourceLoc& line, const TString&); - - const TPragma& pragma() const { return directiveHandler.pragma(); } - const TExtensionBehavior& extensionBehavior() const { return directiveHandler.extensionBehavior(); } - bool supportsExtension(const char* extension); - bool isExtensionEnabled(const char* extension) const; - - bool containsSampler(TType& type); - bool areAllChildConst(TIntermAggregate* aggrNode); - const TFunction* findFunction(const TSourceLoc& line, TFunction* pfnCall, bool *builtIn = 0); - bool executeInitializer(const TSourceLoc& line, TString& identifier, TPublicType& pType, - TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable = 0); - - TIntermTyped* addConstructor(TIntermNode*, const TType*, TOperator, TFunction*, const TSourceLoc&); - TIntermTyped* foldConstConstructor(TIntermAggregate* aggrNode, const TType& type); - TIntermTyped* constructStruct(TIntermNode*, TType*, int, const TSourceLoc&, bool subset); - TIntermTyped* constructBuiltIn(const TType*, TOperator, TIntermNode*, const TSourceLoc&, bool subset); - TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, const TSourceLoc&); - TIntermTyped* addConstMatrixNode(int , TIntermTyped*, const TSourceLoc&); - TIntermTyped* addConstArrayNode(int index, TIntermTyped* node, const TSourceLoc& line); - TIntermTyped* addConstStruct(TString& , TIntermTyped*, const TSourceLoc&); - TIntermTyped* addIndexExpression(TIntermTyped *baseExpression, const TSourceLoc& location, TIntermTyped *indexExpression); - - // Performs an error check for embedded struct declarations. - // Returns true if an error was raised due to the declaration of - // this struct. - bool enterStructDeclaration(const TSourceLoc& line, const TString& identifier); - void exitStructDeclaration(); - - bool structNestingErrorCheck(const TSourceLoc& line, const TField& field); -}; - -int PaParseStrings(size_t count, const char* const string[], const int length[], - TParseContext* context); - -#endif // _PARSER_HELPER_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/Pragma.h b/Source/ThirdParty/ANGLE/src/compiler/Pragma.h deleted file mode 100644 index 2f744123b..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Pragma.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// 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_PRAGMA_H_ -#define COMPILER_PRAGMA_H_ - -struct TPragma { - // By default optimization is turned on and debug is turned off. - TPragma() : optimize(true), debug(false) { } - TPragma(bool o, bool d) : optimize(o), debug(d) { } - - bool optimize; - bool debug; -}; - -#endif // COMPILER_PRAGMA_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.cpp b/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.cpp deleted file mode 100644 index 92a6874eb..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// -// 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/intermediate.h" - -class TAliveTraverser : public TIntermTraverser { -public: - TAliveTraverser(TQualifier q) : TIntermTraverser(true, false, false, true), found(false), qualifier(q) - { - } - - bool wasFound() { return found; } - -protected: - bool found; - TQualifier qualifier; - - void visitSymbol(TIntermSymbol*); - bool visitSelection(Visit, TIntermSelection*); -}; - -// -// Report whether or not a variable of the given qualifier type -// is guaranteed written. Not always possible to determine if -// it is written conditionally. -// -// ?? It does not do this well yet, this is just a place holder -// that simply determines if it was reference at all, anywhere. -// -bool QualifierWritten(TIntermNode* node, TQualifier qualifier) -{ - TAliveTraverser it(qualifier); - - if (node) - node->traverse(&it); - - return it.wasFound(); -} - -void TAliveTraverser::visitSymbol(TIntermSymbol* node) -{ - // - // If it's what we're looking for, record it. - // - if (node->getQualifier() == qualifier) - found = true; -} - -bool TAliveTraverser::visitSelection(Visit preVisit, TIntermSelection* node) -{ - if (wasFound()) - return false; - - return true; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.h b/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.h deleted file mode 100644 index 872a06f72..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/QualifierAlive.h +++ /dev/null @@ -1,7 +0,0 @@ -// -// 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. -// - -bool QualifierWritten(TIntermNode* root, TQualifier); diff --git a/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.cpp b/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.cpp deleted file mode 100644 index a4b8c1e63..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// 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/intermediate.h" -#include "compiler/RemoveTree.h" - -// -// Code to recursively delete the intermediate tree. -// - -class RemoveTree : public TIntermTraverser -{ -public: - RemoveTree() : TIntermTraverser(false, false, true) - { - } - -protected: - void visitSymbol(TIntermSymbol*); - void visitConstantUnion(TIntermConstantUnion*); - bool visitBinary(Visit visit, TIntermBinary*); - bool visitUnary(Visit visit, TIntermUnary*); - bool visitSelection(Visit visit, TIntermSelection*); - bool visitAggregate(Visit visit, TIntermAggregate*); -}; - -void RemoveTree::visitSymbol(TIntermSymbol* node) -{ - delete node; -} - -bool RemoveTree::visitBinary(Visit visit, TIntermBinary* node) -{ - delete node; - - return true; -} - -bool RemoveTree::visitUnary(Visit visit, TIntermUnary* node) -{ - delete node; - - return true; -} - -bool RemoveTree::visitAggregate(Visit visit, TIntermAggregate* node) -{ - delete node; - - return true; -} - -bool RemoveTree::visitSelection(Visit visit, TIntermSelection* node) -{ - delete node; - - return true; -} - -void RemoveTree::visitConstantUnion(TIntermConstantUnion* node) -{ - delete node; -} - -// -// Entry point. -// -void RemoveAllTreeNodes(TIntermNode* root) -{ - RemoveTree it; - - root->traverse(&it); -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.h b/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.h deleted file mode 100644 index 97a821679..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/RemoveTree.h +++ /dev/null @@ -1,7 +0,0 @@ -// -// 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. -// - -void RemoveAllTreeNodes(TIntermNode*); diff --git a/Source/ThirdParty/ANGLE/src/compiler/RenameFunction.h b/Source/ThirdParty/ANGLE/src/compiler/RenameFunction.h deleted file mode 100644 index 3908bfddb..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/RenameFunction.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// 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_RENAME_FUNCTION -#define COMPILER_RENAME_FUNCTION - -#include "compiler/intermediate.h" - -// -// Renames a function, including its declaration and any calls to it. -// -class RenameFunction : public TIntermTraverser -{ -public: - RenameFunction(const TString& oldFunctionName, const TString& newFunctionName) - : TIntermTraverser(true, false, false) - , mOldFunctionName(oldFunctionName) - , mNewFunctionName(newFunctionName) {} - - virtual bool visitAggregate(Visit visit, TIntermAggregate* node) - { - TOperator op = node->getOp(); - if ((op == EOpFunction || op == EOpFunctionCall) && node->getName() == mOldFunctionName) - node->setName(mNewFunctionName); - return true; - } - -private: - const TString mOldFunctionName; - const TString mNewFunctionName; -}; - -#endif // COMPILER_RENAME_FUNCTION diff --git a/Source/ThirdParty/ANGLE/src/compiler/ShHandle.h b/Source/ThirdParty/ANGLE/src/compiler/ShHandle.h deleted file mode 100644 index 221d331ea..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ShHandle.h +++ /dev/null @@ -1,170 +0,0 @@ -// -// 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 _SHHANDLE_INCLUDED_ -#define _SHHANDLE_INCLUDED_ - -// -// Machine independent part of the compiler private objects -// sent as ShHandle to the driver. -// -// This should not be included by driver code. -// - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/BuiltInFunctionEmulator.h" -#include "compiler/ExtensionBehavior.h" -#include "compiler/HashNames.h" -#include "compiler/InfoSink.h" -#include "compiler/SymbolTable.h" -#include "compiler/VariableInfo.h" -#include "third_party/compiler/ArrayBoundsClamper.h" - -class LongNameMap; -class TCompiler; -class TDependencyGraph; -class TranslatorHLSL; - -// -// Helper function to identify specs that are based on the WebGL spec, -// like the CSS Shaders spec. -// -bool isWebGLBasedSpec(ShShaderSpec spec); - -// -// The base class used to back handles returned to the driver. -// -class TShHandleBase { -public: - TShHandleBase(); - virtual ~TShHandleBase(); - virtual TCompiler* getAsCompiler() { return 0; } - virtual TranslatorHLSL* getAsTranslatorHLSL() { return 0; } - -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(ShShaderType type, ShShaderSpec spec); - virtual ~TCompiler(); - virtual TCompiler* getAsCompiler() { return this; } - - bool Init(const ShBuiltInResources& resources); - bool compile(const char* const shaderStrings[], - size_t numStrings, - int compileOptions); - - // Get results of the last compilation. - TInfoSink& getInfoSink() { return infoSink; } - const TVariableInfoList& getAttribs() const { return attribs; } - const TVariableInfoList& getUniforms() const { return uniforms; } - const TVariableInfoList& getVaryings() const { return varyings; } - int getMappedNameMaxLength() const; - - ShHashFunction64 getHashFunction() const { return hashFunction; } - NameMap& getNameMap() { return nameMap; } - TSymbolTable& getSymbolTable() { return symbolTable; } - -protected: - ShShaderType getShaderType() const { return shaderType; } - ShShaderSpec getShaderSpec() const { return shaderSpec; } - // Initialize symbol-table with built-in symbols. - bool InitBuiltInSymbolTable(const ShBuiltInResources& resources); - // Clears the results from the previous compilation. - void clearResults(); - // Return true if function recursion is detected or call depth exceeded. - bool detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth); - // Rewrites a shader's intermediate tree according to the CSS Shaders spec. - void rewriteCSSShader(TIntermNode* root); - // Returns true if the given shader does not exceed the minimum - // functionality mandated in GLSL 1.0 spec Appendix A. - bool validateLimitations(TIntermNode* root); - // Collect info for all attribs, uniforms, varyings. - void collectVariables(TIntermNode* root); - // Map long variable names into shorter ones. - void mapLongVariableNames(TIntermNode* root); - // Translate to object code. - virtual void translate(TIntermNode* root) = 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 or varyings. - bool enforcePackingRestrictions(); - // Returns true if the shader passes the restrictions that aim to prevent timing attacks. - bool enforceTimingRestrictions(TIntermNode* root, bool outputGraph); - // Returns true if the shader does not use samplers. - bool enforceVertexShaderTimingRestrictions(TIntermNode* root); - // Returns true if the shader does not use sampler dependent values to affect control - // flow or in operations whose time can depend on the input values. - bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph); - // Return true if the maximum expression complexity below the limit. - bool limitExpressionComplexity(TIntermNode* root); - // Get built-in extensions with default behavior. - const TExtensionBehavior& getExtensionBehavior() const; - // Get the resources set by InitBuiltInSymbolTable - const ShBuiltInResources& getResources() const; - - const ArrayBoundsClamper& getArrayBoundsClamper() const; - ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const; - const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const; - -private: - ShShaderType shaderType; - ShShaderSpec shaderSpec; - - int maxUniformVectors; - int maxVaryingVectors; - int maxExpressionComplexity; - int maxCallStackDepth; - - ShBuiltInResources compileResources; - - // 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. - TInfoSink infoSink; // Output sink. - TVariableInfoList attribs; // Active attributes in the compiled shader. - TVariableInfoList uniforms; // Active uniforms in the compiled shader. - TVariableInfoList varyings; // Varyings in the compiled shader. - - // Cached copy of the ref-counted singleton. - LongNameMap* longNameMap; - - // name hashing. - ShHashFunction64 hashFunction; - NameMap nameMap; -}; - -// -// 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( - ShShaderType type, ShShaderSpec spec, ShShaderOutput output); -void DeleteCompiler(TCompiler*); - -#endif // _SHHANDLE_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/ShaderLang.cpp b/Source/ThirdParty/ANGLE/src/compiler/ShaderLang.cpp deleted file mode 100644 index 42cd5cc5c..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ShaderLang.cpp +++ /dev/null @@ -1,387 +0,0 @@ -// -// 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. -// - -// -// Implement the top-level of interface to the compiler, -// as defined in ShaderLang.h -// - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/InitializeDll.h" -#include "compiler/preprocessor/length_limits.h" -#include "compiler/ShHandle.h" -#include "compiler/TranslatorHLSL.h" -#include "compiler/VariablePacker.h" - -// -// This is the platform independent interface between an OGL driver -// and the shading language compiler. -// - -static bool checkVariableMaxLengths(const ShHandle handle, - size_t expectedValue) -{ - size_t activeUniformLimit = 0; - ShGetInfo(handle, SH_ACTIVE_UNIFORM_MAX_LENGTH, &activeUniformLimit); - size_t activeAttribLimit = 0; - ShGetInfo(handle, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, &activeAttribLimit); - size_t varyingLimit = 0; - ShGetInfo(handle, SH_VARYING_MAX_LENGTH, &varyingLimit); - return (expectedValue == activeUniformLimit && - expectedValue == activeAttribLimit && - expectedValue == varyingLimit); -} - -static bool checkMappedNameMaxLength(const ShHandle handle, size_t expectedValue) -{ - size_t mappedNameMaxLength = 0; - ShGetInfo(handle, SH_MAPPED_NAME_MAX_LENGTH, &mappedNameMaxLength); - return (expectedValue == mappedNameMaxLength); -} - -// -// Driver must call this first, once, before doing any other compiler operations. -// Subsequent calls to this function are no-op. -// -int ShInitialize() -{ - static const bool kInitialized = InitProcess(); - return kInitialized ? 1 : 0; -} - -// -// Cleanup symbol tables -// -int ShFinalize() -{ - DetachProcess(); - return 1; -} - -// -// Initialize built-in resources with minimum expected values. -// -void ShInitBuiltInResources(ShBuiltInResources* 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->ARB_texture_rectangle = 0; - resources->EXT_draw_buffers = 0; - resources->EXT_frag_depth = 0; - - // Disable highp precision in fragment shader by default. - resources->FragmentPrecisionHigh = 0; - - // Disable name hashing by default. - resources->HashFunction = NULL; - - resources->ArrayIndexClampingStrategy = SH_CLAMP_WITH_CLAMP_INTRINSIC; -} - -// -// Driver calls these to create and destroy compiler objects. -// -ShHandle ShConstructCompiler(ShShaderType type, ShShaderSpec spec, - ShShaderOutput output, - const ShBuiltInResources* resources) -{ - TShHandleBase* base = static_cast<TShHandleBase*>(ConstructCompiler(type, spec, output)); - TCompiler* compiler = base->getAsCompiler(); - if (compiler == 0) - return 0; - - // Generate built-in symbol table. - if (!compiler->Init(*resources)) { - ShDestruct(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()); -} - -// -// 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. -// -int ShCompile( - const ShHandle handle, - const char* const shaderStrings[], - size_t numStrings, - int compileOptions) -{ - if (handle == 0) - return 0; - - TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (compiler == 0) - return 0; - - bool success = compiler->compile(shaderStrings, numStrings, compileOptions); - return success ? 1 : 0; -} - -void ShGetInfo(const ShHandle handle, ShShaderInfo pname, size_t* params) -{ - if (!handle || !params) - return; - - TShHandleBase* base = static_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (!compiler) return; - - switch(pname) - { - case SH_INFO_LOG_LENGTH: - *params = compiler->getInfoSink().info.size() + 1; - break; - case SH_OBJECT_CODE_LENGTH: - *params = compiler->getInfoSink().obj.size() + 1; - break; - case SH_ACTIVE_UNIFORMS: - *params = compiler->getUniforms().size(); - break; - case SH_ACTIVE_UNIFORM_MAX_LENGTH: - *params = 1 + MAX_SYMBOL_NAME_LEN; - break; - case SH_ACTIVE_ATTRIBUTES: - *params = compiler->getAttribs().size(); - break; - case SH_ACTIVE_ATTRIBUTE_MAX_LENGTH: - *params = 1 + MAX_SYMBOL_NAME_LEN; - break; - case SH_VARYINGS: - *params = compiler->getVaryings().size(); - break; - case SH_VARYING_MAX_LENGTH: - *params = 1 + MAX_SYMBOL_NAME_LEN; - break; - case SH_MAPPED_NAME_MAX_LENGTH: - // Use longer length than MAX_SHORTENED_IDENTIFIER_SIZE to - // handle array and struct dereferences. - *params = 1 + MAX_SYMBOL_NAME_LEN; - break; - case SH_NAME_MAX_LENGTH: - *params = 1 + MAX_SYMBOL_NAME_LEN; - break; - case SH_HASHED_NAME_MAX_LENGTH: - if (compiler->getHashFunction() == NULL) { - *params = 0; - } else { - // 64 bits hashing output requires 16 bytes for hex - // representation. - const char HashedNamePrefix[] = HASHED_NAME_PREFIX; - *params = 16 + sizeof(HashedNamePrefix); - } - break; - case SH_HASHED_NAMES_COUNT: - *params = compiler->getNameMap().size(); - break; - default: UNREACHABLE(); - } -} - -// -// Return any compiler log of messages for the application. -// -void ShGetInfoLog(const ShHandle handle, char* infoLog) -{ - if (!handle || !infoLog) - return; - - TShHandleBase* base = static_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (!compiler) return; - - TInfoSink& infoSink = compiler->getInfoSink(); - strcpy(infoLog, infoSink.info.c_str()); -} - -// -// Return any object code. -// -void ShGetObjectCode(const ShHandle handle, char* objCode) -{ - if (!handle || !objCode) - return; - - TShHandleBase* base = static_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (!compiler) return; - - TInfoSink& infoSink = compiler->getInfoSink(); - strcpy(objCode, infoSink.obj.c_str()); -} - -void ShGetVariableInfo(const ShHandle handle, - ShShaderInfo varType, - int index, - size_t* length, - int* size, - ShDataType* type, - ShPrecisionType* precision, - int* staticUse, - char* name, - char* mappedName) -{ - if (!handle || !size || !type || !precision || !staticUse || !name) - return; - ASSERT((varType == SH_ACTIVE_ATTRIBUTES) || - (varType == SH_ACTIVE_UNIFORMS) || - (varType == SH_VARYINGS)); - - TShHandleBase* base = reinterpret_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (compiler == 0) - return; - - const TVariableInfoList& varList = - varType == SH_ACTIVE_ATTRIBUTES ? compiler->getAttribs() : - (varType == SH_ACTIVE_UNIFORMS ? compiler->getUniforms() : - compiler->getVaryings()); - if (index < 0 || index >= static_cast<int>(varList.size())) - return; - - const TVariableInfo& varInfo = varList[index]; - if (length) *length = varInfo.name.size(); - *size = varInfo.size; - *type = varInfo.type; - switch (varInfo.precision) { - case EbpLow: - *precision = SH_PRECISION_LOWP; - break; - case EbpMedium: - *precision = SH_PRECISION_MEDIUMP; - break; - case EbpHigh: - *precision = SH_PRECISION_HIGHP; - break; - default: - // Some types does not support precision, for example, boolean. - *precision = SH_PRECISION_UNDEFINED; - break; - } - *staticUse = varInfo.staticUse ? 1 : 0; - - // This size must match that queried by - // SH_ACTIVE_UNIFORM_MAX_LENGTH, SH_ACTIVE_ATTRIBUTE_MAX_LENGTH, SH_VARYING_MAX_LENGTH - // in ShGetInfo, below. - size_t variableLength = 1 + MAX_SYMBOL_NAME_LEN; - ASSERT(checkVariableMaxLengths(handle, variableLength)); - strncpy(name, varInfo.name.c_str(), variableLength); - name[variableLength - 1] = 0; - if (mappedName) { - // This size must match that queried by - // SH_MAPPED_NAME_MAX_LENGTH in ShGetInfo, below. - size_t maxMappedNameLength = 1 + MAX_SYMBOL_NAME_LEN; - ASSERT(checkMappedNameMaxLength(handle, maxMappedNameLength)); - strncpy(mappedName, varInfo.mappedName.c_str(), maxMappedNameLength); - mappedName[maxMappedNameLength - 1] = 0; - } -} - -void ShGetNameHashingEntry(const ShHandle handle, - int index, - char* name, - char* hashedName) -{ - if (!handle || !name || !hashedName || index < 0) - return; - - TShHandleBase* base = static_cast<TShHandleBase*>(handle); - TCompiler* compiler = base->getAsCompiler(); - if (!compiler) return; - - const NameMap& nameMap = compiler->getNameMap(); - if (index >= static_cast<int>(nameMap.size())) - return; - - NameMap::const_iterator it = nameMap.begin(); - for (int i = 0; i < index; ++i) - ++it; - - size_t len = it->first.length() + 1; - size_t max_len = 0; - ShGetInfo(handle, SH_NAME_MAX_LENGTH, &max_len); - if (len > max_len) { - ASSERT(false); - len = max_len; - } - strncpy(name, it->first.c_str(), len); - // To be on the safe side in case the source is longer than expected. - name[len - 1] = '\0'; - - len = it->second.length() + 1; - max_len = 0; - ShGetInfo(handle, SH_HASHED_NAME_MAX_LENGTH, &max_len); - if (len > max_len) { - ASSERT(false); - len = max_len; - } - strncpy(hashedName, it->second.c_str(), len); - // To be on the safe side in case the source is longer than expected. - hashedName[len - 1] = '\0'; -} - -void ShGetInfoPointer(const ShHandle handle, ShShaderInfo pname, void** params) -{ - if (!handle || !params) - return; - - TShHandleBase* base = static_cast<TShHandleBase*>(handle); - TranslatorHLSL* translator = base->getAsTranslatorHLSL(); - if (!translator) return; - - switch(pname) - { - case SH_ACTIVE_UNIFORMS_ARRAY: - *params = (void*)&translator->getUniforms(); - break; - default: UNREACHABLE(); - } -} - -int ShCheckVariablesWithinPackingLimits( - int maxVectors, ShVariableInfo* varInfoArray, size_t varInfoArraySize) -{ - if (varInfoArraySize == 0) - return 1; - ASSERT(varInfoArray); - TVariableInfoList variables; - for (size_t ii = 0; ii < varInfoArraySize; ++ii) - { - TVariableInfo var(varInfoArray[ii].type, varInfoArray[ii].size); - variables.push_back(var); - } - VariablePacker packer; - return packer.CheckVariablesWithinPackingLimits(maxVectors, variables) ? 1 : 0; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.cpp b/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.cpp deleted file mode 100644 index 715e13616..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// -// 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. -// - -// -// 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/SymbolTable.h" - -#include <stdio.h> -#include <algorithm> -#include <climits> - -TType::TType(const TPublicType &p) : - type(p.type), precision(p.precision), qualifier(p.qualifier), size(p.size), matrix(p.matrix), array(p.array), arraySize(p.arraySize), structure(0) -{ - if (p.userDef) - structure = p.userDef->getStruct(); -} - -// -// 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 EbtBool: mangledName += 'b'; break; - case EbtSampler2D: mangledName += "s2"; break; - case EbtSamplerCube: mangledName += "sC"; break; - case EbtStruct: mangledName += structure->mangledName(); break; - default: break; - } - - 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 = 0; - - if (getBasicType() == EbtStruct) - totalSize = structure->objectSize(); - else if (matrix) - totalSize = size * size; - else - totalSize = size; - - if (isArray()) { - size_t arraySize = getArraySize(); - if (arraySize > INT_MAX / totalSize) - totalSize = INT_MAX; - else - totalSize *= arraySize; - } - - return totalSize; -} - -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; -} - -TString TStructure::buildMangledName() const -{ - TString mangledName("struct-"); - mangledName += *mName; - for (size_t i = 0; i < mFields->size(); ++i) { - mangledName += '-'; - mangledName += (*mFields)[i]->type()->getMangledName(); - } - return mangledName; -} - -size_t TStructure::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; -} - -// -// Dump functions. -// - -void TVariable::dump(TInfoSink& infoSink) const -{ - infoSink.debug << getName().c_str() << ": " << type.getQualifierString() << " " << type.getPrecisionString() << " " << type.getBasicString(); - if (type.isArray()) { - infoSink.debug << "[0]"; - } - infoSink.debug << "\n"; -} - -void TFunction::dump(TInfoSink &infoSink) const -{ - infoSink.debug << getName().c_str() << ": " << returnType.getBasicString() << " " << getMangledName().c_str() << "\n"; -} - -void TSymbolTableLevel::dump(TInfoSink &infoSink) const -{ - tLevel::const_iterator it; - for (it = level.begin(); it != level.end(); ++it) - (*it).second->dump(infoSink); -} - -void TSymbolTable::dump(TInfoSink &infoSink) const -{ - for (int level = currentLevel(); level >= 0; --level) { - infoSink.debug << "LEVEL " << level << "\n"; - table[level]->dump(infoSink); - } -} - -// -// Functions have buried pointers to delete. -// -TFunction::~TFunction() -{ - for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i) - delete (*i).type; -} - -// -// 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) - if ((*it).first == (*it).second->getMangledName()) - delete (*it).second; -} - -// -// Change all function entries in the table with the non-mangled name -// to be related to the provided built-in operation. This is a low -// performance operation, and only intended for symbol tables that -// live across a large number of compiles. -// -void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) -{ - tLevel::iterator it; - for (it = level.begin(); it != level.end(); ++it) { - if ((*it).second->isFunction()) { - TFunction* function = static_cast<TFunction*>((*it).second); - if (function->getName() == name) - function->relateToOperator(op); - } - } -} - -// -// Change all function entries in the table with the non-mangled name -// to be related to the provided built-in extension. This is a low -// performance operation, and only intended for symbol tables that -// live across a large number of compiles. -// -void TSymbolTableLevel::relateToExtension(const char* name, const TString& ext) -{ - for (tLevel::iterator it = level.begin(); it != level.end(); ++it) { - TSymbol* symbol = it->second; - if (symbol->getName() == name) - symbol->relateToExtension(ext); - } -} - -TSymbolTable::~TSymbolTable() -{ - for (size_t i = 0; i < table.size(); ++i) - delete table[i]; - for (size_t i = 0; i < precisionStack.size(); ++i) - delete precisionStack[i]; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.h b/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.h deleted file mode 100644 index bebad4b92..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/SymbolTable.h +++ /dev/null @@ -1,382 +0,0 @@ -// -// 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 _SYMBOL_TABLE_INCLUDED_ -#define _SYMBOL_TABLE_INCLUDED_ - -// -// 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 <assert.h> - -#include "common/angleutils.h" -#include "compiler/InfoSink.h" -#include "compiler/intermediate.h" - -// -// Symbol base class. (Can build functions or variables out of these...) -// -class TSymbol { -public: - POOL_ALLOCATOR_NEW_DELETE(); - TSymbol(const TString* n) : uniqueId(0), name(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; } - void setUniqueId(int id) { uniqueId = id; } - int getUniqueId() const { return uniqueId; } - virtual void dump(TInfoSink &infoSink) const = 0; - void relateToExtension(const TString& ext) { extension = ext; } - const TString& getExtension() const { return extension; } - -private: - DISALLOW_COPY_AND_ASSIGN(TSymbol); - - int uniqueId; // For real comparing during code generation - 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) { } - virtual ~TVariable() { } - virtual bool isVariable() const { return true; } - TType& getType() { return type; } - const TType& getType() const { return type; } - bool isUserType() const { return userType; } - void setQualifier(TQualifier qualifier) { type.setQualifier(qualifier); } - - virtual void dump(TInfoSink &infoSink) const; - - ConstantUnion* getConstPointer() - { - if (!unionArray) - unionArray = new ConstantUnion[type.getObjectSize()]; - - return unionArray; - } - - ConstantUnion* getConstPointer() const { return unionArray; } - - void shareConstPointer( ConstantUnion *constArray) - { - if (unionArray == constArray) - return; - - delete[] unionArray; - unionArray = constArray; - } - -private: - DISALLOW_COPY_AND_ASSIGN(TVariable); - - TType type; - bool userType; - // we are assuming that Pool Allocator will free the memory allocated to unionArray - // when this object is destroyed - ConstantUnion *unionArray; -}; - -// -// The function sub-class of symbols and the parser will need to -// share this definition of a function parameter. -// -struct TParameter { - TString *name; - TType* type; -}; - -// -// The function sub-class of a symbol. -// -class TFunction : public TSymbol { -public: - TFunction(TOperator o) : - TSymbol(0), - returnType(TType(EbtVoid, EbpUndefined)), - op(o), - defined(false) { } - TFunction(const TString *name, TType& retType, TOperator tOp = EOpNull) : - TSymbol(name), - returnType(retType), - mangledName(TFunction::mangleName(*name)), - op(tOp), - defined(false) { } - virtual ~TFunction(); - virtual bool isFunction() const { 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(TParameter& p) - { - parameters.push_back(p); - mangledName = mangledName + p.type->getMangledName(); - } - - const TString& getMangledName() const { return mangledName; } - const TType& getReturnType() const { return returnType; } - - void relateToOperator(TOperator o) { op = o; } - TOperator getBuiltInOp() const { return op; } - - void setDefined() { defined = true; } - bool isDefined() { return defined; } - - size_t getParamCount() const { return parameters.size(); } - const TParameter& getParam(size_t i) const { return parameters[i]; } - - virtual void dump(TInfoSink &infoSink) const; - -private: - DISALLOW_COPY_AND_ASSIGN(TFunction); - - typedef TVector<TParameter> TParamList; - TParamList parameters; - TType returnType; - TString mangledName; - TOperator op; - bool defined; -}; - - -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() { } - ~TSymbolTableLevel(); - - bool insert(const TString &name, TSymbol &symbol) - { - // - // returning true means symbol was added to the table - // - tInsertResult result = level.insert(tLevelPair(name, &symbol)); - - return result.second; - } - - bool insert(TSymbol &symbol) - { - return insert(symbol.getMangledName(), symbol); - } - - TSymbol* find(const TString& name) const - { - tLevel::const_iterator it = level.find(name); - if (it == level.end()) - return 0; - else - return (*it).second; - } - - const_iterator begin() const - { - return level.begin(); - } - - const_iterator end() const - { - return level.end(); - } - - void relateToOperator(const char* name, TOperator op); - void relateToExtension(const char* name, const TString& ext); - void dump(TInfoSink &infoSink) const; - -protected: - tLevel level; -}; - -class TSymbolTable { -public: - TSymbolTable() : uniqueId(0) - { - // - // 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() { return table.size() == 0; } - bool atBuiltInLevel() { return table.size() == 1; } - bool atGlobalLevel() { return table.size() <= 2; } - 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 insert(TSymbol& symbol) - { - symbol.setUniqueId(++uniqueId); - return table[currentLevel()]->insert(symbol); - } - - bool insertConstInt(const char *name, int value) - { - TVariable *constant = new TVariable(NewPoolTString(name), TType(EbtInt, EbpUndefined, EvqConst, 1)); - constant->getConstPointer()->setIConst(value); - return insert(*constant); - } - - bool insertBuiltIn(TType *rvalue, const char *name, TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0) - { - TFunction *function = new TFunction(NewPoolTString(name), *rvalue); - - TParameter param1 = {NULL, ptype1}; - function->addParameter(param1); - - if(ptype2) - { - TParameter param2 = {NULL, ptype2}; - function->addParameter(param2); - } - - if(ptype3) - { - TParameter param3 = {NULL, ptype3}; - function->addParameter(param3); - } - - return insert(*function); - } - - TSymbol* find(const TString& name, bool* builtIn = 0, bool *sameScope = 0) - { - int level = currentLevel(); - TSymbol* symbol; - do { - symbol = table[level]->find(name); - --level; - } while (symbol == 0 && level >= 0); - level++; - if (builtIn) - *builtIn = level == 0; - if (sameScope) - *sameScope = level == currentLevel(); - return symbol; - } - - TSymbol* findBuiltIn(const TString &name) - { - return table[0]->find(name); - } - - TSymbolTableLevel* getOuterLevel() { - assert(table.size() >= 2); - return table[currentLevel() - 1]; - } - - void relateToOperator(const char* name, TOperator op) { - table[0]->relateToOperator(name, op); - } - void relateToExtension(const char* name, const TString& ext) { - table[0]->relateToExtension(name, ext); - } - void dump(TInfoSink &infoSink) const; - - bool setDefaultPrecision(const TPublicType& type, TPrecision prec) { - if (!supportsPrecision(type.type)) - return false; - if (type.size != 1 || type.matrix || type.array) - return false; // Not allowed to set for aggregate types - int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1; - (*precisionStack[indexOfLastElement])[type.type] = prec; // Uses map operator [], overwrites the current value - return true; - } - - // Searches down the precisionStack for a precision qualifier for the specified TBasicType - TPrecision getDefaultPrecision(TBasicType type) { - if (!supportsPrecision(type)) - return EbpUndefined; - int level = static_cast<int>(precisionStack.size()) - 1; - assert(level >= 0); // Just to be safe. Should not happen. - PrecisionStackLevel::iterator it; - TPrecision prec = EbpUndefined; // If we dont find anything we return this. Should we error check this? - while (level >= 0) { - it = precisionStack[level]->find(type); - if (it != precisionStack[level]->end()) { - prec = (*it).second; - break; - } - level--; - } - return prec; - } - -private: - int currentLevel() const { return static_cast<int>(table.size()) - 1; } - - bool supportsPrecision(TBasicType type) { - // Only supports precision for int, float, and sampler types. - return type == EbtFloat || type == EbtInt || IsSampler(type); - } - - int uniqueId; // for unique identification in code generation - std::vector<TSymbolTableLevel*> table; - typedef TMap<TBasicType, TPrecision> PrecisionStackLevel; - std::vector<PrecisionStackLevel*> precisionStack; -}; - -#endif // _SYMBOL_TABLE_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.cpp deleted file mode 100644 index 2900f8a8e..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// 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 "compiler/TranslatorESSL.h" - -#include "compiler/OutputESSL.h" - -TranslatorESSL::TranslatorESSL(ShShaderType type, ShShaderSpec spec) - : TCompiler(type, spec) { -} - -void TranslatorESSL::translate(TIntermNode* root) { - TInfoSinkBase& sink = getInfoSink().obj; - - // Write built-in extension behaviors. - writeExtensionBehavior(); - - // Write emulated built-in functions if needed. - getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition( - sink, getShaderType() == SH_FRAGMENT_SHADER); - - // Write array bounds clamping emulation if needed. - getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); - - // Write translated shader. - TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable()); - root->traverse(&outputESSL); -} - -void TranslatorESSL::writeExtensionBehavior() { - TInfoSinkBase& sink = getInfoSink().obj; - const TExtensionBehavior& extensionBehavior = getExtensionBehavior(); - for (TExtensionBehavior::const_iterator iter = extensionBehavior.begin(); - iter != extensionBehavior.end(); ++iter) { - if (iter->second != EBhUndefined) { - sink << "#extension " << iter->first << " : " - << getBehaviorString(iter->second) << "\n"; - } - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.h b/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.h deleted file mode 100644 index a1196bd00..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorESSL.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// 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_TRANSLATORESSL_H_ -#define COMPILER_TRANSLATORESSL_H_ - -#include "compiler/ShHandle.h" - -class TranslatorESSL : public TCompiler { -public: - TranslatorESSL(ShShaderType type, ShShaderSpec spec); - -protected: - virtual void translate(TIntermNode* root); - -private: - void writeExtensionBehavior(); -}; - -#endif // COMPILER_TRANSLATORESSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.cpp deleted file mode 100644 index 7ca4341dc..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// -// 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/TranslatorGLSL.h" - -#include "compiler/OutputGLSL.h" -#include "compiler/VersionGLSL.h" - -static void writeVersion(ShShaderType type, TIntermNode* root, - TInfoSinkBase& sink) { - TVersionGLSL versionGLSL(type); - 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) { - sink << "#version " << version << "\n"; - } -} - -TranslatorGLSL::TranslatorGLSL(ShShaderType type, ShShaderSpec spec) - : TCompiler(type, spec) { -} - -void TranslatorGLSL::translate(TIntermNode* root) { - TInfoSinkBase& sink = getInfoSink().obj; - - // Write GLSL version. - writeVersion(getShaderType(), root, sink); - - // Write emulated built-in functions if needed. - getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition( - sink, false); - - // Write array bounds clamping emulation if needed. - getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); - - // Write translated shader. - TOutputGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable()); - root->traverse(&outputGLSL); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.h deleted file mode 100644 index c2ce06d19..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorGLSL.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// 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_TRANSLATORGLSL_H_ -#define COMPILER_TRANSLATORGLSL_H_ - -#include "compiler/ShHandle.h" - -class TranslatorGLSL : public TCompiler { -public: - TranslatorGLSL(ShShaderType type, ShShaderSpec spec); - -protected: - virtual void translate(TIntermNode* root); -}; - -#endif // COMPILER_TRANSLATORGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.cpp deleted file mode 100644 index 37408a07c..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// 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/TranslatorHLSL.h" - -#include "compiler/InitializeParseContext.h" -#include "compiler/OutputHLSL.h" - -TranslatorHLSL::TranslatorHLSL(ShShaderType type, ShShaderSpec spec, ShShaderOutput output) - : TCompiler(type, spec), mOutputType(output) -{ -} - -void TranslatorHLSL::translate(TIntermNode *root) -{ - TParseContext& parseContext = *GetGlobalParseContext(); - sh::OutputHLSL outputHLSL(parseContext, getResources(), mOutputType); - - outputHLSL.output(); - mActiveUniforms = outputHLSL.getUniforms(); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.h deleted file mode 100644 index 9550e15e8..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/TranslatorHLSL.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// 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_TRANSLATORHLSL_H_ -#define COMPILER_TRANSLATORHLSL_H_ - -#include "compiler/ShHandle.h" -#include "compiler/Uniform.h" - -class TranslatorHLSL : public TCompiler { -public: - TranslatorHLSL(ShShaderType type, ShShaderSpec spec, ShShaderOutput output); - - virtual TranslatorHLSL *getAsTranslatorHLSL() { return this; } - const sh::ActiveUniforms &getUniforms() { return mActiveUniforms; } - -protected: - virtual void translate(TIntermNode* root); - - sh::ActiveUniforms mActiveUniforms; - ShShaderOutput mOutputType; -}; - -#endif // COMPILER_TRANSLATORHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/Types.h b/Source/ThirdParty/ANGLE/src/compiler/Types.h deleted file mode 100644 index 75560ddc6..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Types.h +++ /dev/null @@ -1,307 +0,0 @@ -// -// 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 _TYPES_INCLUDED -#define _TYPES_INCLUDED - -#include "common/angleutils.h" - -#include "compiler/BaseTypes.h" -#include "compiler/Common.h" -#include "compiler/debug.h" - -struct TPublicType; -class TType; - -class TField -{ -public: - POOL_ALLOCATOR_NEW_DELETE(); - TField(TType* type, TString* name) : mType(type), mName(name) {} - - // 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; } - -private: - DISALLOW_COPY_AND_ASSIGN(TField); - TType* mType; - TString* mName; -}; - -typedef TVector<TField*> TFieldList; -inline TFieldList* NewPoolTFieldList() -{ - void* memory = GetGlobalPoolAllocator()->allocate(sizeof(TFieldList)); - return new(memory) TFieldList; -} - -class TStructure -{ -public: - POOL_ALLOCATOR_NEW_DELETE(); - TStructure(TString* name, TFieldList* fields) - : mName(name), - mFields(fields), - mObjectSize(0), - mDeepestNesting(0) { - } - - const TString& name() const { return *mName; } - const TFieldList& fields() const { return *mFields; } - - const TString& mangledName() const { - if (mMangledName.empty()) - mMangledName = buildMangledName(); - return mMangledName; - } - size_t objectSize() const { - if (mObjectSize == 0) - mObjectSize = calculateObjectSize(); - return mObjectSize; - }; - int deepestNesting() const { - if (mDeepestNesting == 0) - mDeepestNesting = calculateDeepestNesting(); - return mDeepestNesting; - } - bool containsArrays() const; - -private: - DISALLOW_COPY_AND_ASSIGN(TStructure); - TString buildMangledName() const; - size_t calculateObjectSize() const; - int calculateDeepestNesting() const; - - TString* mName; - TFieldList* mFields; - - mutable TString mMangledName; - mutable size_t mObjectSize; - mutable int mDeepestNesting; -}; - -// -// Base class for things that have a type. -// -class TType -{ -public: - POOL_ALLOCATOR_NEW_DELETE(); - TType() {} - TType(TBasicType t, TPrecision p, TQualifier q = EvqTemporary, unsigned char s = 1, bool m = false, bool a = false) : - type(t), precision(p), qualifier(q), size(s), matrix(m), array(a), arraySize(0), structure(0) - { - } - explicit TType(const TPublicType &p); - TType(TStructure* userDef, TPrecision p = EbpUndefined) : - type(EbtStruct), precision(p), qualifier(EvqTemporary), size(1), matrix(false), array(false), arraySize(0), structure(userDef) - { - } - - TBasicType getBasicType() const { return type; } - void setBasicType(TBasicType t) { type = t; } - - TPrecision getPrecision() const { return precision; } - void setPrecision(TPrecision p) { precision = p; } - - TQualifier getQualifier() const { return qualifier; } - void setQualifier(TQualifier q) { qualifier = q; } - - // One-dimensional size of single instance type - int getNominalSize() const { return size; } - void setNominalSize(unsigned char s) { size = s; } - // Full size of single instance of type - size_t getObjectSize() const; - - int elementRegisterCount() const - { - if (structure) - { - const TFieldList &fields = getStruct()->fields(); - int registerCount = 0; - - for (size_t i = 0; i < fields.size(); i++) - { - registerCount += fields[i]->type()->totalRegisterCount(); - } - - return registerCount; - } - else if (isMatrix()) - { - return getNominalSize(); - } - else - { - return 1; - } - } - - int totalRegisterCount() const - { - if (array) - { - return arraySize * elementRegisterCount(); - } - else - { - return elementRegisterCount(); - } - } - - bool isMatrix() const { return matrix ? true : false; } - void setMatrix(bool m) { matrix = m; } - - bool isArray() const { return array ? true : false; } - int getArraySize() const { return arraySize; } - void setArraySize(int s) { array = true; arraySize = s; } - void clearArrayness() { array = false; arraySize = 0; } - - bool isVector() const { return size > 1 && !matrix; } - bool isScalar() const { return size == 1 && !matrix && !structure; } - - TStructure* getStruct() const { return structure; } - void setStruct(TStructure* s) { structure = s; } - - const TString& getMangledName() const { - if (mangled.empty()) { - mangled = buildMangledName(); - mangled += ';'; - } - return mangled; - } - - bool sameElementType(const TType& right) const { - return type == right.type && - size == right.size && - matrix == right.matrix && - structure == right.structure; - } - bool operator==(const TType& right) const { - return type == right.type && - size == right.size && - matrix == right.matrix && - 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 (size != right.size) return size < right.size; - if (matrix != right.matrix) return matrix < right.matrix; - 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 ::getBasicString(type); } - const char* getPrecisionString() const { return ::getPrecisionString(precision); } - const char* getQualifierString() const { return ::getQualifierString(qualifier); } - 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; - } - -private: - TString buildMangledName() const; - - TBasicType type; - TPrecision precision; - TQualifier qualifier; - unsigned char size; - bool matrix; - bool array; - int arraySize; - - TStructure* structure; // 0 unless this is a struct - - mutable TString mangled; -}; - -// -// 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 -{ - TBasicType type; - TQualifier qualifier; - TPrecision precision; - unsigned char size; // size of vector or matrix, not size of array - bool matrix; - bool array; - int arraySize; - TType* userDef; - TSourceLoc line; - - void setBasic(TBasicType bt, TQualifier q, const TSourceLoc& ln) - { - type = bt; - qualifier = q; - precision = EbpUndefined; - size = 1; - matrix = false; - array = false; - arraySize = 0; - userDef = 0; - line = ln; - } - - void setAggregate(unsigned char s, bool m = false) - { - size = s; - matrix = m; - } - - void setArray(bool a, int s = 0) - { - array = a; - arraySize = s; - } - - bool isStructureContainingArrays() const - { - if (!userDef) - { - return false; - } - - return userDef->isStructureContainingArrays(); - } -}; - -#endif // _TYPES_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.cpp b/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.cpp deleted file mode 100644 index 5cfdd3230..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// -// 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. -// -// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements. -// The results are assigned to s# temporaries, which are used by the main translator instead of -// the original expression. -// - -#include "compiler/UnfoldShortCircuit.h" - -#include "compiler/InfoSink.h" -#include "compiler/OutputHLSL.h" - -namespace sh -{ -UnfoldShortCircuit::UnfoldShortCircuit(TParseContext &context, OutputHLSL *outputHLSL) : mContext(context), mOutputHLSL(outputHLSL) -{ - mTemporaryIndex = 0; -} - -void UnfoldShortCircuit::traverse(TIntermNode *node) -{ - int rewindIndex = mTemporaryIndex; - node->traverse(this); - mTemporaryIndex = rewindIndex; -} - -bool UnfoldShortCircuit::visitBinary(Visit visit, TIntermBinary *node) -{ - TInfoSinkBase &out = mOutputHLSL->getBodyStream(); - - // 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) - if (!node->getRight()->hasSideEffects()) - { - return 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;". - { - int i = mTemporaryIndex; - - out << "bool s" << i << ";\n"; - - out << "{\n"; - - mTemporaryIndex = i + 1; - node->getLeft()->traverse(this); - out << "s" << i << " = "; - mTemporaryIndex = i + 1; - node->getLeft()->traverse(mOutputHLSL); - out << ";\n"; - out << "if (!s" << i << ")\n" - "{\n"; - mTemporaryIndex = i + 1; - node->getRight()->traverse(this); - out << " s" << i << " = "; - mTemporaryIndex = i + 1; - node->getRight()->traverse(mOutputHLSL); - out << ";\n" - "}\n"; - - out << "}\n"; - - mTemporaryIndex = i + 1; - } - 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;". - { - int i = mTemporaryIndex; - - out << "bool s" << i << ";\n"; - - out << "{\n"; - - mTemporaryIndex = i + 1; - node->getLeft()->traverse(this); - out << "s" << i << " = "; - mTemporaryIndex = i + 1; - node->getLeft()->traverse(mOutputHLSL); - out << ";\n"; - out << "if (s" << i << ")\n" - "{\n"; - mTemporaryIndex = i + 1; - node->getRight()->traverse(this); - out << " s" << i << " = "; - mTemporaryIndex = i + 1; - node->getRight()->traverse(mOutputHLSL); - out << ";\n" - "}\n"; - - out << "}\n"; - - mTemporaryIndex = i + 1; - } - return false; - default: - return true; - } -} - -bool UnfoldShortCircuit::visitSelection(Visit visit, TIntermSelection *node) -{ - TInfoSinkBase &out = mOutputHLSL->getBodyStream(); - - // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;" - if (node->usesTernaryOperator()) - { - int i = mTemporaryIndex; - - out << mOutputHLSL->typeString(node->getType()) << " s" << i << ";\n"; - - out << "{\n"; - - mTemporaryIndex = i + 1; - node->getCondition()->traverse(this); - out << "if ("; - mTemporaryIndex = i + 1; - node->getCondition()->traverse(mOutputHLSL); - out << ")\n" - "{\n"; - mTemporaryIndex = i + 1; - node->getTrueBlock()->traverse(this); - out << " s" << i << " = "; - mTemporaryIndex = i + 1; - node->getTrueBlock()->traverse(mOutputHLSL); - out << ";\n" - "}\n" - "else\n" - "{\n"; - mTemporaryIndex = i + 1; - node->getFalseBlock()->traverse(this); - out << " s" << i << " = "; - mTemporaryIndex = i + 1; - node->getFalseBlock()->traverse(mOutputHLSL); - out << ";\n" - "}\n"; - - out << "}\n"; - - mTemporaryIndex = i + 1; - } - - return false; -} - -bool UnfoldShortCircuit::visitLoop(Visit visit, TIntermLoop *node) -{ - int rewindIndex = mTemporaryIndex; - - if (node->getInit()) - { - node->getInit()->traverse(this); - } - - if (node->getCondition()) - { - node->getCondition()->traverse(this); - } - - if (node->getExpression()) - { - node->getExpression()->traverse(this); - } - - mTemporaryIndex = rewindIndex; - - return false; -} - -int UnfoldShortCircuit::getNextTemporaryIndex() -{ - return mTemporaryIndex++; -} -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.h b/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.h deleted file mode 100644 index ae2989fa4..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuit.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// 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. -// -// UnfoldShortCircuit is an AST traverser to output short-circuiting operators as if-else statements -// - -#ifndef COMPILER_UNFOLDSHORTCIRCUIT_H_ -#define COMPILER_UNFOLDSHORTCIRCUIT_H_ - -#include "compiler/intermediate.h" -#include "compiler/ParseContext.h" - -namespace sh -{ -class OutputHLSL; - -class UnfoldShortCircuit : public TIntermTraverser -{ - public: - UnfoldShortCircuit(TParseContext &context, OutputHLSL *outputHLSL); - - void traverse(TIntermNode *node); - bool visitBinary(Visit visit, TIntermBinary*); - bool visitSelection(Visit visit, TIntermSelection *node); - bool visitLoop(Visit visit, TIntermLoop *node); - - int getNextTemporaryIndex(); - - protected: - TParseContext &mContext; - OutputHLSL *const mOutputHLSL; - - int mTemporaryIndex; -}; -} - -#endif // COMPILER_UNFOLDSHORTCIRCUIT_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.cpp b/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.cpp deleted file mode 100644 index b6d0f2f30..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// -// 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/UnfoldShortCircuitAST.h" - -namespace -{ - -// "x || y" is equivalent to "x ? true : y". -TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y) -{ - const TType boolType(EbtBool, EbpUndefined); - ConstantUnion *u = new ConstantUnion; - u->setBConst(true); - TIntermConstantUnion *trueNode = new TIntermConstantUnion( - u, TType(EbtBool, EbpUndefined, EvqConst, 1)); - return new TIntermSelection(x, trueNode, y, boolType); -} - -// "x && y" is equivalent to "x ? y : false". -TIntermSelection *UnfoldAND(TIntermTyped *x, TIntermTyped *y) -{ - const TType boolType(EbtBool, EbpUndefined); - ConstantUnion *u = new ConstantUnion; - u->setBConst(false); - TIntermConstantUnion *falseNode = new TIntermConstantUnion( - u, TType(EbtBool, EbpUndefined, EvqConst, 1)); - return new TIntermSelection(x, y, falseNode, boolType); -} - -} // namespace anonymous - -bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node) -{ - TIntermSelection *replacement = NULL; - - 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) - { - replacements.push_back( - NodeUpdateEntry(getParentNode(), node, replacement)); - } - return true; -} - -void UnfoldShortCircuitAST::updateTree() -{ - for (size_t ii = 0; ii < replacements.size(); ++ii) - { - const NodeUpdateEntry& entry = replacements[ii]; - ASSERT(entry.parent); - bool replaced = entry.parent->replaceChildNode( - entry.original, entry.replacement); - ASSERT(replaced); - - // In AST traversing, a parent is visited before its children. - // After we replace a node, if an 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 < replacements.size(); ++jj) - { - NodeUpdateEntry& entry2 = replacements[jj]; - if (entry2.parent == entry.original) - entry2.parent = entry.replacement; - } - } -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.h b/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.h deleted file mode 100644 index cfc500664..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/UnfoldShortCircuitAST.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// 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_UNFOLD_SHORT_CIRCUIT_AST_H_ -#define COMPILER_UNFOLD_SHORT_CIRCUIT_AST_H_ - -#include "common/angleutils.h" -#include "compiler/intermediate.h" - -// 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() { } - - virtual bool visitBinary(Visit visit, TIntermBinary *); - - void updateTree(); - - private: - struct NodeUpdateEntry - { - NodeUpdateEntry(TIntermNode *_parent, - TIntermNode *_original, - TIntermNode *_replacement) - : parent(_parent), - original(_original), - replacement(_replacement) {} - - TIntermNode *parent; - TIntermNode *original; - TIntermNode *replacement; - }; - - // During traversing, save all the replacements that need to happen; - // then replace them by calling updateNodes(). - std::vector<NodeUpdateEntry> replacements; - - DISALLOW_COPY_AND_ASSIGN(UnfoldShortCircuitAST); -}; - -#endif // COMPILER_UNFOLD_SHORT_CIRCUIT_AST_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/Uniform.cpp b/Source/ThirdParty/ANGLE/src/compiler/Uniform.cpp deleted file mode 100644 index f367db2be..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Uniform.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// 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/Uniform.h" - -namespace sh -{ - -Uniform::Uniform(GLenum type, GLenum precision, const char *name, int arraySize, int registerIndex) -{ - this->type = type; - this->precision = precision; - this->name = name; - this->arraySize = arraySize; - this->registerIndex = registerIndex; -} - -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/Uniform.h b/Source/ThirdParty/ANGLE/src/compiler/Uniform.h deleted file mode 100644 index 4c53ffa7d..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/Uniform.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// 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_UNIFORM_H_ -#define COMPILER_UNIFORM_H_ - -#include <string> -#include <vector> - -#define GL_APICALL -#include <GLES2/gl2.h> - -namespace sh -{ - -struct Uniform -{ - Uniform(GLenum type, GLenum precision, const char *name, int arraySize, int registerIndex); - - GLenum type; - GLenum precision; - std::string name; - unsigned int arraySize; - - int registerIndex; -}; - -typedef std::vector<Uniform> ActiveUniforms; - -} - -#endif // COMPILER_UNIFORM_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.cpp b/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.cpp deleted file mode 100644 index 64969c489..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.cpp +++ /dev/null @@ -1,512 +0,0 @@ -// -// 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/ValidateLimitations.h" -#include "compiler/InfoSink.h" -#include "compiler/InitializeParseContext.h" -#include "compiler/ParseContext.h" - -namespace { -bool IsLoopIndex(const TIntermSymbol* symbol, const TLoopStack& stack) { - for (TLoopStack::const_iterator i = stack.begin(); i != stack.end(); ++i) { - if (i->index.id == symbol->getId()) - return true; - } - return false; -} - -void MarkLoopForUnroll(const TIntermSymbol* symbol, TLoopStack& stack) { - for (TLoopStack::iterator i = stack.begin(); i != stack.end(); ++i) { - if (i->index.id == symbol->getId()) { - ASSERT(i->loop != NULL); - i->loop->setUnrollFlag(true); - return; - } - } - UNREACHABLE(); -} - -// 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(const TLoopStack& stack) - : mValid(true), mLoopStack(stack) {} - - // Returns true if the parsed node represents a constant index expression. - bool isValid() const { return mValid; } - - virtual void visitSymbol(TIntermSymbol* symbol) { - // Only constants and loop indices are allowed in a - // constant index expression. - if (mValid) { - mValid = (symbol->getQualifier() == EvqConst) || - IsLoopIndex(symbol, mLoopStack); - } - } - -private: - bool mValid; - const TLoopStack& mLoopStack; -}; - -// Traverses a node to check if it uses a loop index. -// If an int loop index is used in its body as a sampler array index, -// mark the loop for unroll. -class ValidateLoopIndexExpr : public TIntermTraverser { -public: - ValidateLoopIndexExpr(TLoopStack& stack) - : mUsesFloatLoopIndex(false), - mUsesIntLoopIndex(false), - mLoopStack(stack) {} - - bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; } - bool usesIntLoopIndex() const { return mUsesIntLoopIndex; } - - virtual void visitSymbol(TIntermSymbol* symbol) { - if (IsLoopIndex(symbol, mLoopStack)) { - switch (symbol->getBasicType()) { - case EbtFloat: - mUsesFloatLoopIndex = true; - break; - case EbtInt: - mUsesIntLoopIndex = true; - MarkLoopForUnroll(symbol, mLoopStack); - break; - default: - UNREACHABLE(); - } - } - } - -private: - bool mUsesFloatLoopIndex; - bool mUsesIntLoopIndex; - TLoopStack& mLoopStack; -}; -} // namespace - -ValidateLimitations::ValidateLimitations(ShShaderType shaderType, - TInfoSinkBase& sink) - : mShaderType(shaderType), - mSink(sink), - 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: - validateIndexing(node); - break; - case EOpIndexIndirect: -#if defined(__APPLE__) - // Loop unrolling is a work-around for a Mac Cg compiler bug where it - // crashes when a sampler array's index is also the loop index. - // Once Apple fixes this bug, we should remove the code in this CL. - // See http://codereview.appspot.com/4331048/. - if ((node->getLeft() != NULL) && (node->getRight() != NULL) && - (node->getLeft()->getAsSymbolNode())) { - TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode(); - if (IsSampler(symbol->getBasicType()) && symbol->isArray()) { - ValidateLoopIndexExpr validate(mLoopStack); - node->getRight()->traverse(&validate); - if (validate.usesFloatLoopIndex()) { - error(node->getLine(), - "sampler array index is float loop index", - "for"); - } - } - } -#endif - 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 (!validateLoopType(node)) - return false; - - TLoopInfo info; - memset(&info, 0, sizeof(TLoopInfo)); - info.loop = node; - if (!validateForLoopHeader(node, &info)) - return false; - - TIntermNode* body = node->getBody(); - if (body != NULL) { - mLoopStack.push_back(info); - body->traverse(this); - mLoopStack.pop_back(); - } - - // The loop is fully processed - no need to visit children. - return false; -} - -void ValidateLimitations::error(TSourceLoc loc, - const char *reason, const char* token) -{ - mSink.prefix(EPrefixError); - mSink.location(loc); - mSink << "'" << token << "' : " << reason << "\n"; - ++mNumErrors; -} - -bool ValidateLimitations::withinLoopBody() const -{ - return !mLoopStack.empty(); -} - -bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const -{ - return IsLoopIndex(symbol, mLoopStack); -} - -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, - TLoopInfo* info) -{ - ASSERT(node->getType() == ELoopFor); - - // - // The for statement has the form: - // for ( init-declaration ; condition ; expression ) statement - // - if (!validateForLoopInit(node, info)) - return false; - if (!validateForLoopCond(node, info)) - return false; - if (!validateForLoopExpr(node, info)) - return false; - - return true; -} - -bool ValidateLimitations::validateForLoopInit(TIntermLoop* node, - TLoopInfo* info) -{ - TIntermNode* init = node->getInit(); - if (init == NULL) { - error(node->getLine(), "Missing init declaration", "for"); - return false; - } - - // - // init-declaration has the form: - // type-specifier identifier = constant-expression - // - TIntermAggregate* decl = init->getAsAggregate(); - if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) { - error(init->getLine(), "Invalid init declaration", "for"); - return false; - } - // 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 false; - } - TIntermBinary* declInit = declSeq[0]->getAsBinaryNode(); - if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) { - error(decl->getLine(), "Invalid init declaration", "for"); - return false; - } - TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode(); - if (symbol == NULL) { - error(declInit->getLine(), "Invalid init declaration", "for"); - return false; - } - // The loop index has type int or float. - TBasicType type = symbol->getBasicType(); - if ((type != EbtInt) && (type != EbtFloat)) { - error(symbol->getLine(), - "Invalid type for loop index", getBasicString(type)); - return false; - } - // 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 false; - } - - info->index.id = symbol->getId(); - return true; -} - -bool ValidateLimitations::validateForLoopCond(TIntermLoop* node, - TLoopInfo* info) -{ - 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() != info->index.id) { - 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, - TLoopInfo* info) -{ - 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() != info->index.id) { - 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->getName()); - ASSERT(symbol && symbol->isFunction()); - TFunction* function = static_cast<TFunction*>(symbol); - for (ParamIndex::const_iterator i = pIndex.begin(); - i != pIndex.end(); ++i) { - const TParameter& 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; - - const 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 != NULL); - return node->getAsConstantUnion() != NULL; -} - -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 expression must have integral type. - if (!index->isScalar() || (index->getBasicType() != EbtInt)) { - error(index->getLine(), - "Index expression must have integral type", - index->getCompleteString().c_str()); - valid = false; - } - // 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 == SH_VERTEX_SHADER) && - (operand->getQualifier() == EvqUniform); - if (!skip && !isConstIndexExpr(index)) { - error(index->getLine(), "Index expression must be constant", "[]"); - valid = false; - } - return valid; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.h b/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.h deleted file mode 100644 index a835cb3c2..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ValidateLimitations.h +++ /dev/null @@ -1,59 +0,0 @@ -// -// 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 "GLSLANG/ShaderLang.h" -#include "compiler/intermediate.h" - -class TInfoSinkBase; - -struct TLoopInfo { - struct TIndex { - int id; // symbol id. - } index; - TIntermLoop* loop; -}; -typedef TVector<TLoopInfo> TLoopStack; - -// 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(ShShaderType shaderType, TInfoSinkBase& sink); - - int numErrors() const { return mNumErrors; } - - virtual bool visitBinary(Visit, TIntermBinary*); - virtual bool visitUnary(Visit, TIntermUnary*); - virtual bool visitAggregate(Visit, TIntermAggregate*); - virtual bool visitLoop(Visit, TIntermLoop*); - -private: - void error(TSourceLoc loc, const char *reason, const char* token); - - bool withinLoopBody() const; - bool isLoopIndex(const TIntermSymbol* symbol) const; - bool validateLoopType(TIntermLoop* node); - bool validateForLoopHeader(TIntermLoop* node, TLoopInfo* info); - bool validateForLoopInit(TIntermLoop* node, TLoopInfo* info); - bool validateForLoopCond(TIntermLoop* node, TLoopInfo* info); - bool validateForLoopExpr(TIntermLoop* node, TLoopInfo* info); - // 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); - - ShShaderType mShaderType; - TInfoSinkBase& mSink; - int mNumErrors; - TLoopStack mLoopStack; -}; - diff --git a/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.cpp b/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.cpp deleted file mode 100644 index 34ea4b659..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.cpp +++ /dev/null @@ -1,308 +0,0 @@ -// -// 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/VariableInfo.h" - -namespace { - -TString arrayBrackets(int index) -{ - TStringStream stream; - stream << "[" << index << "]"; - return stream.str(); -} - -// Returns the data type for an attribute, uniform, or varying. -ShDataType getVariableDataType(const TType& type) -{ - switch (type.getBasicType()) { - case EbtFloat: - if (type.isMatrix()) { - switch (type.getNominalSize()) { - case 2: return SH_FLOAT_MAT2; - case 3: return SH_FLOAT_MAT3; - case 4: return SH_FLOAT_MAT4; - default: UNREACHABLE(); - } - } else if (type.isVector()) { - switch (type.getNominalSize()) { - case 2: return SH_FLOAT_VEC2; - case 3: return SH_FLOAT_VEC3; - case 4: return SH_FLOAT_VEC4; - default: UNREACHABLE(); - } - } else { - return SH_FLOAT; - } - case EbtInt: - if (type.isMatrix()) { - UNREACHABLE(); - } else if (type.isVector()) { - switch (type.getNominalSize()) { - case 2: return SH_INT_VEC2; - case 3: return SH_INT_VEC3; - case 4: return SH_INT_VEC4; - default: UNREACHABLE(); - } - } else { - return SH_INT; - } - case EbtBool: - if (type.isMatrix()) { - UNREACHABLE(); - } else if (type.isVector()) { - switch (type.getNominalSize()) { - case 2: return SH_BOOL_VEC2; - case 3: return SH_BOOL_VEC3; - case 4: return SH_BOOL_VEC4; - default: UNREACHABLE(); - } - } else { - return SH_BOOL; - } - case EbtSampler2D: return SH_SAMPLER_2D; - case EbtSamplerCube: return SH_SAMPLER_CUBE; - case EbtSamplerExternalOES: return SH_SAMPLER_EXTERNAL_OES; - case EbtSampler2DRect: return SH_SAMPLER_2D_RECT_ARB; - default: UNREACHABLE(); - } - return SH_NONE; -} - -void getBuiltInVariableInfo(const TType& type, - const TString& name, - const TString& mappedName, - TVariableInfoList& infoList); -void getUserDefinedVariableInfo(const TType& type, - const TString& name, - const TString& mappedName, - TVariableInfoList& infoList, - ShHashFunction64 hashFunction); - -// Returns info for an attribute, uniform, or varying. -void getVariableInfo(const TType& type, - const TString& name, - const TString& mappedName, - TVariableInfoList& infoList, - ShHashFunction64 hashFunction) -{ - if (type.getBasicType() == EbtStruct) { - if (type.isArray()) { - for (int i = 0; i < type.getArraySize(); ++i) { - TString lname = name + arrayBrackets(i); - TString lmappedName = mappedName + arrayBrackets(i); - getUserDefinedVariableInfo(type, lname, lmappedName, infoList, hashFunction); - } - } else { - getUserDefinedVariableInfo(type, name, mappedName, infoList, hashFunction); - } - } else { - getBuiltInVariableInfo(type, name, mappedName, infoList); - } -} - -void getBuiltInVariableInfo(const TType& type, - const TString& name, - const TString& mappedName, - TVariableInfoList& infoList) -{ - ASSERT(type.getBasicType() != EbtStruct); - - TVariableInfo varInfo; - if (type.isArray()) { - varInfo.name = (name + "[0]").c_str(); - varInfo.mappedName = (mappedName + "[0]").c_str(); - varInfo.size = type.getArraySize(); - } else { - varInfo.name = name.c_str(); - varInfo.mappedName = mappedName.c_str(); - varInfo.size = 1; - } - varInfo.precision = type.getPrecision(); - varInfo.type = getVariableDataType(type); - infoList.push_back(varInfo); -} - -void getUserDefinedVariableInfo(const TType& type, - const TString& name, - const TString& mappedName, - TVariableInfoList& infoList, - ShHashFunction64 hashFunction) -{ - ASSERT(type.getBasicType() == EbtStruct); - - const TFieldList& fields = type.getStruct()->fields(); - for (size_t i = 0; i < fields.size(); ++i) { - const TType& fieldType = *(fields[i]->type()); - const TString& fieldName = fields[i]->name(); - getVariableInfo(fieldType, - name + "." + fieldName, - mappedName + "." + TIntermTraverser::hash(fieldName, hashFunction), - infoList, - hashFunction); - } -} - -TVariableInfo* findVariable(const TType& type, - const TString& name, - TVariableInfoList& infoList) -{ - // TODO(zmo): optimize this function. - TString myName = name; - if (type.isArray()) - myName += "[0]"; - for (size_t ii = 0; ii < infoList.size(); ++ii) - { - if (infoList[ii].name.c_str() == myName) - return &(infoList[ii]); - } - return NULL; -} - -} // namespace anonymous - -TVariableInfo::TVariableInfo() - : type(SH_NONE), - size(0), - precision(EbpUndefined), - staticUse(false) -{ -} - -TVariableInfo::TVariableInfo(ShDataType type, int size) - : type(type), - size(size), - precision(EbpUndefined), - staticUse(false) -{ -} - -CollectVariables::CollectVariables(TVariableInfoList& attribs, - TVariableInfoList& uniforms, - TVariableInfoList& varyings, - ShHashFunction64 hashFunction) - : mAttribs(attribs), - mUniforms(uniforms), - mVaryings(varyings), - mPointCoordAdded(false), - mFrontFacingAdded(false), - mFragCoordAdded(false), - mHashFunction(hashFunction) -{ -} - -// 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); - TVariableInfo* var = NULL; - switch (symbol->getQualifier()) - { - case EvqVaryingOut: - case EvqInvariantVaryingOut: - case EvqVaryingIn: - case EvqInvariantVaryingIn: - var = findVariable(symbol->getType(), symbol->getSymbol(), mVaryings); - break; - case EvqUniform: - var = findVariable(symbol->getType(), symbol->getSymbol(), mUniforms); - break; - case EvqFragCoord: - if (!mFragCoordAdded) { - TVariableInfo info; - info.name = "gl_FragCoord"; - info.mappedName = "gl_FragCoord"; - info.type = SH_FLOAT_VEC4; - info.size = 1; - info.precision = EbpMedium; // Use mediump as it doesn't really matter. - info.staticUse = true; - mVaryings.push_back(info); - mFragCoordAdded = true; - } - return; - case EvqFrontFacing: - if (!mFrontFacingAdded) { - TVariableInfo info; - info.name = "gl_FrontFacing"; - info.mappedName = "gl_FrontFacing"; - info.type = SH_BOOL; - info.size = 1; - info.precision = EbpUndefined; - info.staticUse = true; - mVaryings.push_back(info); - mFrontFacingAdded = true; - } - return; - case EvqPointCoord: - if (!mPointCoordAdded) { - TVariableInfo info; - info.name = "gl_PointCoord"; - info.mappedName = "gl_PointCoord"; - info.type = SH_FLOAT_VEC2; - info.size = 1; - info.precision = EbpMedium; // Use mediump as it doesn't really matter. - info.staticUse = true; - mVaryings.push_back(info); - mPointCoordAdded = true; - } - return; - default: - break; - } - if (var) - var->staticUse = true; -} - -bool CollectVariables::visitAggregate(Visit, TIntermAggregate* node) -{ - bool visitChildren = true; - - switch (node->getOp()) - { - case EOpDeclaration: { - const TIntermSequence& sequence = node->getSequence(); - TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier(); - if (qualifier == EvqAttribute || qualifier == EvqUniform || - qualifier == EvqVaryingIn || qualifier == EvqVaryingOut || - qualifier == EvqInvariantVaryingIn || qualifier == EvqInvariantVaryingOut) - { - TVariableInfoList& infoList = qualifier == EvqAttribute ? mAttribs : - (qualifier == EvqUniform ? mUniforms : mVaryings); - for (TIntermSequence::const_iterator i = sequence.begin(); - i != sequence.end(); ++i) - { - const TIntermSymbol* variable = (*i)->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); - TString processedSymbol; - if (mHashFunction == NULL) - processedSymbol = variable->getSymbol(); - else - processedSymbol = TIntermTraverser::hash(variable->getOriginalSymbol(), mHashFunction); - getVariableInfo(variable->getType(), - variable->getOriginalSymbol(), - processedSymbol, - infoList, - mHashFunction); - visitChildren = false; - } - } - break; - } - default: break; - } - - return visitChildren; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.h b/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.h deleted file mode 100644 index 3c7f2a5f8..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/VariableInfo.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// 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_VARIABLE_INFO_H_ -#define COMPILER_VARIABLE_INFO_H_ - -#include "GLSLANG/ShaderLang.h" -#include "compiler/intermediate.h" - -// Provides information about a variable. -// It is currently being used to store info about active attribs and uniforms. -struct TVariableInfo { - TVariableInfo(ShDataType type, int size); - TVariableInfo(); - - TPersistString name; - TPersistString mappedName; - ShDataType type; - int size; - TPrecision precision; - bool staticUse; -}; -typedef std::vector<TVariableInfo> TVariableInfoList; - -// Traverses intermediate tree to collect all attributes, uniforms, varyings. -class CollectVariables : public TIntermTraverser { -public: - CollectVariables(TVariableInfoList& attribs, - TVariableInfoList& uniforms, - TVariableInfoList& varyings, - ShHashFunction64 hashFunction); - - virtual void visitSymbol(TIntermSymbol*); - virtual bool visitAggregate(Visit, TIntermAggregate*); - -private: - TVariableInfoList& mAttribs; - TVariableInfoList& mUniforms; - TVariableInfoList& mVaryings; - - bool mPointCoordAdded; - bool mFrontFacingAdded; - bool mFragCoordAdded; - - ShHashFunction64 mHashFunction; -}; - -#endif // COMPILER_VARIABLE_INFO_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/VariablePacker.h b/Source/ThirdParty/ANGLE/src/compiler/VariablePacker.h deleted file mode 100644 index 8987066cc..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/VariablePacker.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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 _VARIABLEPACKER_INCLUDED_ -#define _VARIABLEPACKER_INCLUDED_ - -#include <vector> -#include "compiler/ShHandle.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( - int maxVectors, - const TVariableInfoList& in_variables); - - // Gets how many components in a row a data type takes. - static int GetNumComponentsPerRow(ShDataType type); - - // Gets how many rows a data type takes. - static int GetNumRows(ShDataType 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 // _VARIABLEPACKER_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.cpp deleted file mode 100644 index 7a82bb4dc..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// 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/VersionGLSL.h" - -static const int GLSL_VERSION_110 = 110; -static const int GLSL_VERSION_120 = 120; - -// 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." -// -// TODO(alokp): The following two cases of invariant decalaration get lost -// during parsing - they do not get carried over to the intermediate tree. -// Handle these cases: -// 1. When a pragma is used to force all output variables to be invariant: -// - #pragma STDGL invariant(all) -// 2. When a previously decalared or built-in variable is marked invariant: -// - invariant gl_Position; -// - varying vec3 color; invariant color; -// -TVersionGLSL::TVersionGLSL(ShShaderType type) - : mShaderType(type), - mVersion(GLSL_VERSION_110) -{ -} - -void TVersionGLSL::visitSymbol(TIntermSymbol* node) -{ - if (node->getSymbol() == "gl_PointCoord") - updateVersion(GLSL_VERSION_120); -} - -void TVersionGLSL::visitConstantUnion(TIntermConstantUnion*) -{ -} - -bool TVersionGLSL::visitBinary(Visit, TIntermBinary*) -{ - return true; -} - -bool TVersionGLSL::visitUnary(Visit, TIntermUnary*) -{ - return true; -} - -bool TVersionGLSL::visitSelection(Visit, TIntermSelection*) -{ - return true; -} - -bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate* node) -{ - bool visitChildren = true; - - switch (node->getOp()) { - case EOpSequence: - // We need to visit sequence children to get to global or inner scope. - visitChildren = true; - break; - case EOpDeclaration: { - const TIntermSequence& sequence = node->getSequence(); - TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier(); - if ((qualifier == EvqInvariantVaryingIn) || - (qualifier == EvqInvariantVaryingOut)) { - updateVersion(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)) - { - updateVersion(GLSL_VERSION_120); - break; - } - } - } - // Fully processed. No need to visit children. - visitChildren = false; - break; - } - case EOpConstructMat2: - case EOpConstructMat3: - case EOpConstructMat4: { - const TIntermSequence& sequence = node->getSequence(); - if (sequence.size() == 1) { - TIntermTyped* typed = sequence.front()->getAsTyped(); - if (typed && typed->isMatrix()) { - updateVersion(GLSL_VERSION_120); - } - } - break; - } - - default: break; - } - - return visitChildren; -} - -bool TVersionGLSL::visitLoop(Visit, TIntermLoop*) -{ - return true; -} - -bool TVersionGLSL::visitBranch(Visit, TIntermBranch*) -{ - return true; -} - -void TVersionGLSL::updateVersion(int version) -{ - mVersion = std::max(version, mVersion); -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.h deleted file mode 100644 index 1c1cb1ab9..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/VersionGLSL.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// 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_VERSIONGLSL_H_ -#define COMPILER_VERSIONGLSL_H_ - -#include "GLSLANG/ShaderLang.h" -#include "compiler/intermediate.h" - -// 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 -// -class TVersionGLSL : public TIntermTraverser { -public: - TVersionGLSL(ShShaderType type); - - // Returns 120 if the following is used the shader: - // - "invariant", - // - "gl_PointCoord", - // - matrix/matrix constructors - // - array "out" parameters - // Else 110 is returned. - int getVersion() { return mVersion; } - - virtual void visitSymbol(TIntermSymbol*); - virtual void visitConstantUnion(TIntermConstantUnion*); - virtual bool visitBinary(Visit, TIntermBinary*); - virtual bool visitUnary(Visit, TIntermUnary*); - virtual bool visitSelection(Visit, TIntermSelection*); - virtual bool visitAggregate(Visit, TIntermAggregate*); - virtual bool visitLoop(Visit, TIntermLoop*); - virtual bool visitBranch(Visit, TIntermBranch*); - -protected: - void updateVersion(int version); - -private: - ShShaderType mShaderType; - int mVersion; -}; - -#endif // COMPILER_VERSIONGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/debug.cpp b/Source/ThirdParty/ANGLE/src/compiler/debug.cpp deleted file mode 100644 index 38d55acaf..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/debug.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// 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. -// - -// debug.cpp: Debugging utilities. - -#include "compiler/debug.h" - -#include <stdarg.h> -#include <stdio.h> - -#include "compiler/InitializeParseContext.h" -#include "compiler/ParseContext.h" - -#ifdef TRACE_ENABLED -static const int kTraceBufferLen = 1024; - -extern "C" { -void Trace(const char *format, ...) { - if (!format) return; - - TParseContext* parseContext = GetGlobalParseContext(); - if (parseContext) { - char buf[kTraceBufferLen]; - va_list args; - va_start(args, format); - vsnprintf(buf, kTraceBufferLen, format, args); - va_end(args); - - parseContext->trace(buf); - } -} -} // extern "C" -#endif // TRACE_ENABLED - diff --git a/Source/ThirdParty/ANGLE/src/compiler/debug.h b/Source/ThirdParty/ANGLE/src/compiler/debug.h deleted file mode 100644 index 7a371516a..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/debug.h +++ /dev/null @@ -1,53 +0,0 @@ -// -// 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. -// - -// debug.h: Debugging utilities. - -#ifndef COMPILER_DEBUG_H_ -#define COMPILER_DEBUG_H_ - -#include <assert.h> - -#ifdef _DEBUG -#define TRACE_ENABLED // define to enable debug message tracing -#endif // _DEBUG - -// Outputs text to the debug log -#ifdef TRACE_ENABLED - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus -void Trace(const char* format, ...); -#ifdef __cplusplus -} -#endif // __cplusplus - -#else // TRACE_ENABLED - -#define Trace(...) ((void)0) - -#endif // TRACE_ENABLED - -// A macro asserting a condition and outputting failures to the debug log -#define ASSERT(expression) do { \ - if(!(expression)) \ - Trace("Assert failed: %s(%d): "#expression"\n", __FUNCTION__, __LINE__); \ - assert(expression); \ -} while(0) - -#define UNIMPLEMENTED() do { \ - Trace("Unimplemented invoked: %s(%d)\n", __FUNCTION__, __LINE__); \ - assert(false); \ -} while(0) - -#define UNREACHABLE() do { \ - Trace("Unreachable reached: %s(%d)\n", __FUNCTION__, __LINE__); \ - assert(false); \ -} while(0) - -#endif // COMPILER_DEBUG_H_ - diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.cpp b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.cpp deleted file mode 100644 index ca661d676..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// -// 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. -// - -#pragma warning(disable: 4718) - -#include "compiler/depgraph/DependencyGraph.h" -#include "compiler/depgraph/DependencyGraphBuilder.h" - -TDependencyGraph::TDependencyGraph(TIntermNode* intermNode) -{ - TDependencyGraphBuilder::build(intermNode, this); -} - -TDependencyGraph::~TDependencyGraph() -{ - for (TGraphNodeVector::const_iterator iter = mAllNodes.begin(); iter != mAllNodes.end(); ++iter) - { - TGraphNode* node = *iter; - delete node; - } -} - -TGraphArgument* TDependencyGraph::createArgument(TIntermAggregate* intermFunctionCall, - int argumentNumber) -{ - TGraphArgument* argument = new TGraphArgument(intermFunctionCall, argumentNumber); - mAllNodes.push_back(argument); - return argument; -} - -TGraphFunctionCall* TDependencyGraph::createFunctionCall(TIntermAggregate* intermFunctionCall) -{ - TGraphFunctionCall* functionCall = new TGraphFunctionCall(intermFunctionCall); - mAllNodes.push_back(functionCall); - if (functionCall->getIntermFunctionCall()->isUserDefined()) - mUserDefinedFunctionCalls.push_back(functionCall); - return functionCall; -} - -TGraphSymbol* TDependencyGraph::getOrCreateSymbol(TIntermSymbol* intermSymbol) -{ - TSymbolIdMap::const_iterator iter = mSymbolIdMap.find(intermSymbol->getId()); - - TGraphSymbol* symbol = NULL; - - if (iter != mSymbolIdMap.end()) { - TSymbolIdPair pair = *iter; - symbol = pair.second; - } else { - symbol = new TGraphSymbol(intermSymbol); - mAllNodes.push_back(symbol); - - TSymbolIdPair pair(intermSymbol->getId(), symbol); - mSymbolIdMap.insert(pair); - - // We save all sampler symbols in a collection, so we can start graph traversals from them quickly. - if (IsSampler(intermSymbol->getBasicType())) - mSamplerSymbols.push_back(symbol); - } - - return symbol; -} - -TGraphSelection* TDependencyGraph::createSelection(TIntermSelection* intermSelection) -{ - TGraphSelection* selection = new TGraphSelection(intermSelection); - mAllNodes.push_back(selection); - return selection; -} - -TGraphLoop* TDependencyGraph::createLoop(TIntermLoop* intermLoop) -{ - TGraphLoop* loop = new TGraphLoop(intermLoop); - mAllNodes.push_back(loop); - return loop; -} - -TGraphLogicalOp* TDependencyGraph::createLogicalOp(TIntermBinary* intermLogicalOp) -{ - TGraphLogicalOp* logicalOp = new TGraphLogicalOp(intermLogicalOp); - mAllNodes.push_back(logicalOp); - return logicalOp; -} - -const char* TGraphLogicalOp::getOpString() const -{ - const char* opString = NULL; - switch (getIntermLogicalOp()->getOp()) { - case EOpLogicalAnd: opString = "and"; break; - case EOpLogicalOr: opString = "or"; break; - default: opString = "unknown"; break; - } - return opString; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.h b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.h deleted file mode 100644 index 5a9c35d00..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraph.h +++ /dev/null @@ -1,212 +0,0 @@ -// -// 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_DEPGRAPH_DEPENDENCY_GRAPH_H -#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_H - -#include "compiler/intermediate.h" - -#include <set> -#include <stack> - -class TGraphNode; -class TGraphParentNode; -class TGraphArgument; -class TGraphFunctionCall; -class TGraphSymbol; -class TGraphSelection; -class TGraphLoop; -class TGraphLogicalOp; -class TDependencyGraphTraverser; -class TDependencyGraphOutput; - -typedef std::set<TGraphNode*> TGraphNodeSet; -typedef std::vector<TGraphNode*> TGraphNodeVector; -typedef std::vector<TGraphSymbol*> TGraphSymbolVector; -typedef std::vector<TGraphFunctionCall*> TFunctionCallVector; - -// -// Base class for all dependency graph nodes. -// -class TGraphNode { -public: - TGraphNode(TIntermNode* node) : intermNode(node) {} - virtual ~TGraphNode() {} - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -protected: - TIntermNode* intermNode; -}; - -// -// Base class for dependency graph nodes that may have children. -// -class TGraphParentNode : public TGraphNode { -public: - TGraphParentNode(TIntermNode* node) : TGraphNode(node) {} - virtual ~TGraphParentNode() {} - void addDependentNode(TGraphNode* node) { if (node != this) mDependentNodes.insert(node); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -private: - TGraphNodeSet mDependentNodes; -}; - -// -// Handle function call arguments. -// -class TGraphArgument : public TGraphParentNode { -public: - TGraphArgument(TIntermAggregate* intermFunctionCall, int argumentNumber) - : TGraphParentNode(intermFunctionCall) - , mArgumentNumber(argumentNumber) {} - virtual ~TGraphArgument() {} - const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); } - int getArgumentNumber() const { return mArgumentNumber; } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -private: - int mArgumentNumber; -}; - -// -// Handle function calls. -// -class TGraphFunctionCall : public TGraphParentNode { -public: - TGraphFunctionCall(TIntermAggregate* intermFunctionCall) - : TGraphParentNode(intermFunctionCall) {} - virtual ~TGraphFunctionCall() {} - const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -}; - -// -// Handle symbols. -// -class TGraphSymbol : public TGraphParentNode { -public: - TGraphSymbol(TIntermSymbol* intermSymbol) : TGraphParentNode(intermSymbol) {} - virtual ~TGraphSymbol() {} - const TIntermSymbol* getIntermSymbol() const { return intermNode->getAsSymbolNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -}; - -// -// Handle if statements and ternary operators. -// -class TGraphSelection : public TGraphNode { -public: - TGraphSelection(TIntermSelection* intermSelection) : TGraphNode(intermSelection) {} - virtual ~TGraphSelection() {} - const TIntermSelection* getIntermSelection() const { return intermNode->getAsSelectionNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -}; - -// -// Handle for, do-while, and while loops. -// -class TGraphLoop : public TGraphNode { -public: - TGraphLoop(TIntermLoop* intermLoop) : TGraphNode(intermLoop) {} - virtual ~TGraphLoop() {} - const TIntermLoop* getIntermLoop() const { return intermNode->getAsLoopNode(); } - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -}; - -// -// Handle logical and, or. -// -class TGraphLogicalOp : public TGraphNode { -public: - TGraphLogicalOp(TIntermBinary* intermLogicalOp) : TGraphNode(intermLogicalOp) {} - virtual ~TGraphLogicalOp() {} - const TIntermBinary* getIntermLogicalOp() const { return intermNode->getAsBinaryNode(); } - const char* getOpString() const; - virtual void traverse(TDependencyGraphTraverser* graphTraverser); -}; - -// -// A dependency graph of symbols, function calls, conditions etc. -// -// This class provides an interface to the entry points of the dependency graph. -// -// Dependency graph nodes should be created by using one of the provided "create..." methods. -// This class (and nobody else) manages the memory of the created nodes. -// Nodes may not be removed after being added, so all created nodes will exist while the -// TDependencyGraph instance exists. -// -class TDependencyGraph { -public: - TDependencyGraph(TIntermNode* intermNode); - ~TDependencyGraph(); - TGraphNodeVector::const_iterator begin() const { return mAllNodes.begin(); } - TGraphNodeVector::const_iterator end() const { return mAllNodes.end(); } - - TGraphSymbolVector::const_iterator beginSamplerSymbols() const - { - return mSamplerSymbols.begin(); - } - - TGraphSymbolVector::const_iterator endSamplerSymbols() const - { - return mSamplerSymbols.end(); - } - - TFunctionCallVector::const_iterator beginUserDefinedFunctionCalls() const - { - return mUserDefinedFunctionCalls.begin(); - } - - TFunctionCallVector::const_iterator endUserDefinedFunctionCalls() const - { - return mUserDefinedFunctionCalls.end(); - } - - TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber); - TGraphFunctionCall* createFunctionCall(TIntermAggregate* intermFunctionCall); - TGraphSymbol* getOrCreateSymbol(TIntermSymbol* intermSymbol); - TGraphSelection* createSelection(TIntermSelection* intermSelection); - TGraphLoop* createLoop(TIntermLoop* intermLoop); - TGraphLogicalOp* createLogicalOp(TIntermBinary* intermLogicalOp); -private: - typedef TMap<int, TGraphSymbol*> TSymbolIdMap; - typedef std::pair<int, TGraphSymbol*> TSymbolIdPair; - - TGraphNodeVector mAllNodes; - TGraphSymbolVector mSamplerSymbols; - TFunctionCallVector mUserDefinedFunctionCalls; - TSymbolIdMap mSymbolIdMap; -}; - -// -// For traversing the dependency graph. Users should derive from this, -// put their traversal specific data in it, and then pass it to a -// traverse method. -// -// When using this, just fill in the methods for nodes you want visited. -// -class TDependencyGraphTraverser { -public: - TDependencyGraphTraverser() : mDepth(0) {} - - virtual void visitSymbol(TGraphSymbol* symbol) {}; - virtual void visitArgument(TGraphArgument* selection) {}; - virtual void visitFunctionCall(TGraphFunctionCall* functionCall) {}; - virtual void visitSelection(TGraphSelection* selection) {}; - virtual void visitLoop(TGraphLoop* loop) {}; - virtual void visitLogicalOp(TGraphLogicalOp* logicalOp) {}; - - int getDepth() const { return mDepth; } - void incrementDepth() { ++mDepth; } - void decrementDepth() { --mDepth; } - - void clearVisited() { mVisited.clear(); } - void markVisited(TGraphNode* node) { mVisited.insert(node); } - bool isVisited(TGraphNode* node) const { return mVisited.find(node) != mVisited.end(); } -private: - int mDepth; - TGraphNodeSet mVisited; -}; - -#endif diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.cpp b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.cpp deleted file mode 100644 index 026e6d57a..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// -// 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/depgraph/DependencyGraphBuilder.h" - -void TDependencyGraphBuilder::build(TIntermNode* node, TDependencyGraph* graph) -{ - TDependencyGraphBuilder builder(graph); - builder.build(node); -} - -bool TDependencyGraphBuilder::visitAggregate(Visit visit, TIntermAggregate* intermAggregate) -{ - switch (intermAggregate->getOp()) { - case EOpFunction: visitFunctionDefinition(intermAggregate); break; - case EOpFunctionCall: visitFunctionCall(intermAggregate); break; - default: visitAggregateChildren(intermAggregate); break; - } - - return false; -} - -void TDependencyGraphBuilder::visitFunctionDefinition(TIntermAggregate* intermAggregate) -{ - // Currently, we do not support user defined functions. - if (intermAggregate->getName() != "main(") - return; - - visitAggregateChildren(intermAggregate); -} - -// Takes an expression like "f(x)" and creates a dependency graph like -// "x -> argument 0 -> function call". -void TDependencyGraphBuilder::visitFunctionCall(TIntermAggregate* intermFunctionCall) -{ - TGraphFunctionCall* functionCall = mGraph->createFunctionCall(intermFunctionCall); - - // Run through the function call arguments. - int argumentNumber = 0; - TIntermSequence& intermArguments = intermFunctionCall->getSequence(); - for (TIntermSequence::const_iterator iter = intermArguments.begin(); - iter != intermArguments.end(); - ++iter, ++argumentNumber) - { - TNodeSetMaintainer nodeSetMaintainer(this); - - TIntermNode* intermArgument = *iter; - intermArgument->traverse(this); - - if (TParentNodeSet* argumentNodes = mNodeSets.getTopSet()) { - TGraphArgument* argument = mGraph->createArgument(intermFunctionCall, argumentNumber); - connectMultipleNodesToSingleNode(argumentNodes, argument); - argument->addDependentNode(functionCall); - } - } - - // Push the leftmost symbol of this function call into the current set of dependent symbols to - // represent the result of this function call. - // Thus, an expression like "y = f(x)" will yield a dependency graph like - // "x -> argument 0 -> function call -> y". - // This line essentially passes the function call node back up to an earlier visitAssignment - // call, which will create the connection "function call -> y". - mNodeSets.insertIntoTopSet(functionCall); -} - -void TDependencyGraphBuilder::visitAggregateChildren(TIntermAggregate* intermAggregate) -{ - TIntermSequence& sequence = intermAggregate->getSequence(); - for(TIntermSequence::const_iterator iter = sequence.begin(); iter != sequence.end(); ++iter) - { - TIntermNode* intermChild = *iter; - intermChild->traverse(this); - } -} - -void TDependencyGraphBuilder::visitSymbol(TIntermSymbol* intermSymbol) -{ - // Push this symbol into the set of dependent symbols for the current assignment or condition - // that we are traversing. - TGraphSymbol* symbol = mGraph->getOrCreateSymbol(intermSymbol); - mNodeSets.insertIntoTopSet(symbol); - - // If this symbol is the current leftmost symbol under an assignment, replace the previous - // leftmost symbol with this symbol. - if (!mLeftmostSymbols.empty() && mLeftmostSymbols.top() != &mRightSubtree) { - mLeftmostSymbols.pop(); - mLeftmostSymbols.push(symbol); - } -} - -bool TDependencyGraphBuilder::visitBinary(Visit visit, TIntermBinary* intermBinary) -{ - TOperator op = intermBinary->getOp(); - if (op == EOpInitialize || intermBinary->isAssignment()) - visitAssignment(intermBinary); - else if (op == EOpLogicalAnd || op == EOpLogicalOr) - visitLogicalOp(intermBinary); - else - visitBinaryChildren(intermBinary); - - return false; -} - -void TDependencyGraphBuilder::visitAssignment(TIntermBinary* intermAssignment) -{ - TIntermTyped* intermLeft = intermAssignment->getLeft(); - if (!intermLeft) - return; - - TGraphSymbol* leftmostSymbol = NULL; - - { - TNodeSetMaintainer nodeSetMaintainer(this); - - { - TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mLeftSubtree); - intermLeft->traverse(this); - leftmostSymbol = mLeftmostSymbols.top(); - - // After traversing the left subtree of this assignment, we should have found a real - // leftmost symbol, and the leftmost symbol should not be a placeholder. - ASSERT(leftmostSymbol != &mLeftSubtree); - ASSERT(leftmostSymbol != &mRightSubtree); - } - - if (TIntermTyped* intermRight = intermAssignment->getRight()) { - TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree); - intermRight->traverse(this); - } - - if (TParentNodeSet* assignmentNodes = mNodeSets.getTopSet()) - connectMultipleNodesToSingleNode(assignmentNodes, leftmostSymbol); - } - - // Push the leftmost symbol of this assignment into the current set of dependent symbols to - // represent the result of this assignment. - // An expression like "a = (b = c)" will yield a dependency graph like "c -> b -> a". - // This line essentially passes the leftmost symbol of the nested assignment ("b" in this - // example) back up to the earlier visitAssignment call for the outer assignment, which will - // create the connection "b -> a". - mNodeSets.insertIntoTopSet(leftmostSymbol); -} - -void TDependencyGraphBuilder::visitLogicalOp(TIntermBinary* intermLogicalOp) -{ - if (TIntermTyped* intermLeft = intermLogicalOp->getLeft()) { - TNodeSetPropagatingMaintainer nodeSetMaintainer(this); - - intermLeft->traverse(this); - if (TParentNodeSet* leftNodes = mNodeSets.getTopSet()) { - TGraphLogicalOp* logicalOp = mGraph->createLogicalOp(intermLogicalOp); - connectMultipleNodesToSingleNode(leftNodes, logicalOp); - } - } - - if (TIntermTyped* intermRight = intermLogicalOp->getRight()) { - TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree); - intermRight->traverse(this); - } -} - -void TDependencyGraphBuilder::visitBinaryChildren(TIntermBinary* intermBinary) -{ - if (TIntermTyped* intermLeft = intermBinary->getLeft()) - intermLeft->traverse(this); - - if (TIntermTyped* intermRight = intermBinary->getRight()) { - TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree); - intermRight->traverse(this); - } -} - -bool TDependencyGraphBuilder::visitSelection(Visit visit, TIntermSelection* intermSelection) -{ - if (TIntermNode* intermCondition = intermSelection->getCondition()) { - TNodeSetMaintainer nodeSetMaintainer(this); - - intermCondition->traverse(this); - if (TParentNodeSet* conditionNodes = mNodeSets.getTopSet()) { - TGraphSelection* selection = mGraph->createSelection(intermSelection); - connectMultipleNodesToSingleNode(conditionNodes, selection); - } - } - - if (TIntermNode* intermTrueBlock = intermSelection->getTrueBlock()) - intermTrueBlock->traverse(this); - - if (TIntermNode* intermFalseBlock = intermSelection->getFalseBlock()) - intermFalseBlock->traverse(this); - - return false; -} - -bool TDependencyGraphBuilder::visitLoop(Visit visit, TIntermLoop* intermLoop) -{ - if (TIntermTyped* intermCondition = intermLoop->getCondition()) { - TNodeSetMaintainer nodeSetMaintainer(this); - - intermCondition->traverse(this); - if (TParentNodeSet* conditionNodes = mNodeSets.getTopSet()) { - TGraphLoop* loop = mGraph->createLoop(intermLoop); - connectMultipleNodesToSingleNode(conditionNodes, loop); - } - } - - if (TIntermNode* intermBody = intermLoop->getBody()) - intermBody->traverse(this); - - if (TIntermTyped* intermExpression = intermLoop->getExpression()) - intermExpression->traverse(this); - - return false; -} - - -void TDependencyGraphBuilder::connectMultipleNodesToSingleNode(TParentNodeSet* nodes, - TGraphNode* node) const -{ - for (TParentNodeSet::const_iterator iter = nodes->begin(); iter != nodes->end(); ++iter) - { - TGraphParentNode* currentNode = *iter; - currentNode->addDependentNode(node); - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.h b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.h deleted file mode 100644 index c5f232cb2..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphBuilder.h +++ /dev/null @@ -1,181 +0,0 @@ -// -// 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_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H -#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H - -#include "compiler/depgraph/DependencyGraph.h" - -// -// Creates a dependency graph of symbols, function calls, conditions etc. by traversing a -// intermediate tree. -// -class TDependencyGraphBuilder : public TIntermTraverser { -public: - static void build(TIntermNode* node, TDependencyGraph* graph); - - virtual void visitSymbol(TIntermSymbol*); - virtual bool visitBinary(Visit visit, TIntermBinary*); - virtual bool visitSelection(Visit visit, TIntermSelection*); - virtual bool visitAggregate(Visit visit, TIntermAggregate*); - virtual bool visitLoop(Visit visit, TIntermLoop*); - -private: - typedef std::stack<TGraphSymbol*> TSymbolStack; - typedef std::set<TGraphParentNode*> TParentNodeSet; - - // - // For collecting the dependent nodes of assignments, conditions, etc. - // while traversing the intermediate tree. - // - // This data structure is stack of sets. Each set contains dependency graph parent nodes. - // - class TNodeSetStack { - public: - TNodeSetStack() {}; - ~TNodeSetStack() { clear(); } - - // This should only be called after a pushSet. - // Returns NULL if the top set is empty. - TParentNodeSet* getTopSet() const - { - ASSERT(!nodeSets.empty()); - TParentNodeSet* topSet = nodeSets.top(); - return !topSet->empty() ? topSet : NULL; - } - - void pushSet() { nodeSets.push(new TParentNodeSet()); } - void popSet() - { - ASSERT(!nodeSets.empty()); - delete nodeSets.top(); - nodeSets.pop(); - } - - // Pops the top set and adds its contents to the new top set. - // This should only be called after a pushSet. - // If there is no set below the top set, the top set is just deleted. - void popSetIntoNext() - { - ASSERT(!nodeSets.empty()); - TParentNodeSet* oldTopSet = nodeSets.top(); - nodeSets.pop(); - - if (!nodeSets.empty()) { - TParentNodeSet* newTopSet = nodeSets.top(); - newTopSet->insert(oldTopSet->begin(), oldTopSet->end()); - } - - delete oldTopSet; - } - - // Does nothing if there is no top set. - // This can be called when there is no top set if we are visiting - // symbols that are not under an assignment or condition. - // We don't need to track those symbols. - void insertIntoTopSet(TGraphParentNode* node) - { - if (nodeSets.empty()) - return; - - nodeSets.top()->insert(node); - } - - void clear() - { - while (!nodeSets.empty()) - popSet(); - } - - private: - typedef std::stack<TParentNodeSet*> TParentNodeSetStack; - - TParentNodeSetStack nodeSets; - }; - - // - // An instance of this class pushes a new node set when instantiated. - // When the instance goes out of scope, it and pops the node set. - // - class TNodeSetMaintainer { - public: - TNodeSetMaintainer(TDependencyGraphBuilder* factory) - : sets(factory->mNodeSets) { sets.pushSet(); } - ~TNodeSetMaintainer() { sets.popSet(); } - protected: - TNodeSetStack& sets; - }; - - // - // An instance of this class pushes a new node set when instantiated. - // When the instance goes out of scope, it and pops the top node set and adds its contents to - // the new top node set. - // - class TNodeSetPropagatingMaintainer { - public: - TNodeSetPropagatingMaintainer(TDependencyGraphBuilder* factory) - : sets(factory->mNodeSets) { sets.pushSet(); } - ~TNodeSetPropagatingMaintainer() { sets.popSetIntoNext(); } - protected: - TNodeSetStack& sets; - }; - - // - // An instance of this class keeps track of the leftmost symbol while we're exploring an - // assignment. - // It will push the placeholder symbol kLeftSubtree when instantiated under a left subtree, - // and kRightSubtree under a right subtree. - // When it goes out of scope, it will pop the leftmost symbol at the top of the scope. - // During traversal, the TDependencyGraphBuilder will replace kLeftSubtree with a real symbol. - // kRightSubtree will never be replaced by a real symbol because we are tracking the leftmost - // symbol. - // - class TLeftmostSymbolMaintainer { - public: - TLeftmostSymbolMaintainer(TDependencyGraphBuilder* factory, TGraphSymbol& subtree) - : leftmostSymbols(factory->mLeftmostSymbols) - { - needsPlaceholderSymbol = leftmostSymbols.empty() || leftmostSymbols.top() != &subtree; - if (needsPlaceholderSymbol) - leftmostSymbols.push(&subtree); - } - - ~TLeftmostSymbolMaintainer() - { - if (needsPlaceholderSymbol) - leftmostSymbols.pop(); - } - - protected: - TSymbolStack& leftmostSymbols; - bool needsPlaceholderSymbol; - }; - - TDependencyGraphBuilder(TDependencyGraph* graph) - : TIntermTraverser(true, false, false) - , mLeftSubtree(NULL) - , mRightSubtree(NULL) - , mGraph(graph) {} - void build(TIntermNode* intermNode) { intermNode->traverse(this); } - - void connectMultipleNodesToSingleNode(TParentNodeSet* nodes, TGraphNode* node) const; - - void visitAssignment(TIntermBinary*); - void visitLogicalOp(TIntermBinary*); - void visitBinaryChildren(TIntermBinary*); - void visitFunctionDefinition(TIntermAggregate*); - void visitFunctionCall(TIntermAggregate* intermFunctionCall); - void visitAggregateChildren(TIntermAggregate*); - - TGraphSymbol mLeftSubtree; - TGraphSymbol mRightSubtree; - - TDependencyGraph* mGraph; - TNodeSetStack mNodeSets; - TSymbolStack mLeftmostSymbols; -}; - -#endif // COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.cpp b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.cpp deleted file mode 100644 index 6fc489e7b..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// -// 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/depgraph/DependencyGraphOutput.h" - -void TDependencyGraphOutput::outputIndentation() -{ - for (int i = 0; i < getDepth(); ++i) - mSink << " "; -} - -void TDependencyGraphOutput::visitArgument(TGraphArgument* parameter) -{ - outputIndentation(); - mSink << "argument " << parameter->getArgumentNumber() << " of call to " - << parameter->getIntermFunctionCall()->getName() << "\n"; -} - -void TDependencyGraphOutput::visitFunctionCall(TGraphFunctionCall* functionCall) -{ - outputIndentation(); - mSink << "function call " << functionCall->getIntermFunctionCall()->getName() << "\n"; -} - -void TDependencyGraphOutput::visitSymbol(TGraphSymbol* symbol) -{ - outputIndentation(); - mSink << symbol->getIntermSymbol()->getSymbol() << " (symbol id: " - << symbol->getIntermSymbol()->getId() << ")\n"; -} - -void TDependencyGraphOutput::visitSelection(TGraphSelection* selection) -{ - outputIndentation(); - mSink << "selection\n"; -} - -void TDependencyGraphOutput::visitLoop(TGraphLoop* loop) -{ - outputIndentation(); - mSink << "loop condition\n"; -} - -void TDependencyGraphOutput::visitLogicalOp(TGraphLogicalOp* logicalOp) -{ - outputIndentation(); - mSink << "logical " << logicalOp->getOpString() << "\n"; -} - -void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph) -{ - mSink << "\n"; - - for (TGraphNodeVector::const_iterator iter = graph.begin(); iter != graph.end(); ++iter) - { - TGraphNode* symbol = *iter; - mSink << "--- Dependency graph spanning tree ---\n"; - clearVisited(); - symbol->traverse(this); - mSink << "\n"; - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.h b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.h deleted file mode 100644 index 01447da98..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphOutput.h +++ /dev/null @@ -1,30 +0,0 @@ -// -// 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_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H -#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H - -#include "compiler/depgraph/DependencyGraph.h" -#include "compiler/InfoSink.h" - -class TDependencyGraphOutput : public TDependencyGraphTraverser { -public: - TDependencyGraphOutput(TInfoSinkBase& sink) : mSink(sink) {} - virtual void visitSymbol(TGraphSymbol* symbol); - virtual void visitArgument(TGraphArgument* parameter); - virtual void visitFunctionCall(TGraphFunctionCall* functionCall); - virtual void visitSelection(TGraphSelection* selection); - virtual void visitLoop(TGraphLoop* loop); - virtual void visitLogicalOp(TGraphLogicalOp* logicalOp); - - void outputAllSpanningTrees(TDependencyGraph& graph); -private: - void outputIndentation(); - - TInfoSinkBase& mSink; -}; - -#endif // COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H diff --git a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphTraverse.cpp b/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphTraverse.cpp deleted file mode 100644 index b158575ce..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/depgraph/DependencyGraphTraverse.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// -// 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/depgraph/DependencyGraph.h" - -// These methods do a breadth-first traversal through the graph and mark visited nodes. - -void TGraphNode::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->markVisited(this); -} - -void TGraphParentNode::traverse(TDependencyGraphTraverser* graphTraverser) -{ - TGraphNode::traverse(graphTraverser); - - graphTraverser->incrementDepth(); - - // Visit the parent node's children. - for (TGraphNodeSet::const_iterator iter = mDependentNodes.begin(); - iter != mDependentNodes.end(); - ++iter) - { - TGraphNode* node = *iter; - if (!graphTraverser->isVisited(node)) - node->traverse(graphTraverser); - } - - graphTraverser->decrementDepth(); -} - -void TGraphArgument::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitArgument(this); - TGraphParentNode::traverse(graphTraverser); -} - -void TGraphFunctionCall::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitFunctionCall(this); - TGraphParentNode::traverse(graphTraverser); -} - -void TGraphSymbol::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitSymbol(this); - TGraphParentNode::traverse(graphTraverser); -} - -void TGraphSelection::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitSelection(this); - TGraphNode::traverse(graphTraverser); -} - -void TGraphLoop::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitLoop(this); - TGraphNode::traverse(graphTraverser); -} - -void TGraphLogicalOp::traverse(TDependencyGraphTraverser* graphTraverser) -{ - graphTraverser->visitLogicalOp(this); - TGraphNode::traverse(graphTraverser); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/fuzz/translator_fuzzer.cpp b/Source/ThirdParty/ANGLE/src/compiler/fuzz/translator_fuzzer.cpp new file mode 100644 index 000000000..518552255 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/fuzz/translator_fuzzer.cpp @@ -0,0 +1,162 @@ +// +// 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" + +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_OUTPUT); + validOutputs.push_back(SH_HLSL9_OUTPUT); + validOutputs.push_back(SH_HLSL11_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/Source/ThirdParty/ANGLE/src/compiler/glslang.l b/Source/ThirdParty/ANGLE/src/compiler/glslang.l deleted file mode 100644 index a4350d4c2..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/glslang.l +++ /dev/null @@ -1,365 +0,0 @@ -/* -// -// 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. -// - -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 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! - -// 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: 4505) -#pragma warning(disable: 4701) -#endif - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma clang diagnostic ignored "-Wdeprecated-register" -#endif -} - -%{ -#include "compiler/glslang.h" -#include "compiler/ParseContext.h" -#include "compiler/preprocessor/Token.h" -#include "compiler/util.h" -#include "glslang_tab.h" - -/* windows only pragma */ -#ifdef _MSC_VER -#pragma warning(disable : 4102) -#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 int_constant(yyscan_t yyscanner); -static int float_constant(yyscan_t yyscanner); -%} - -%option noyywrap nounput never-interactive -%option yylineno reentrant bison-bridge bison-locations -%option extra-type="TParseContext*" - -D [0-9] -L [a-zA-Z_] -H [a-fA-F0-9] -E [Ee][+-]?{D}+ -O [0-7] - -%% - -"invariant" { return INVARIANT; } -"highp" { return HIGH_PRECISION; } -"mediump" { return MEDIUM_PRECISION; } -"lowp" { return LOW_PRECISION; } -"precision" { return PRECISION; } - -"attribute" { return ATTRIBUTE; } -"const" { return CONST_QUAL; } -"uniform" { return UNIFORM; } -"varying" { return VARYING; } - -"break" { return BREAK; } -"continue" { return CONTINUE; } -"do" { return DO; } -"for" { return FOR; } -"while" { return WHILE; } - -"if" { return IF; } -"else" { return ELSE; } - -"in" { return IN_QUAL; } -"out" { return OUT_QUAL; } -"inout" { return INOUT_QUAL; } - -"float" { return FLOAT_TYPE; } -"int" { return INT_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; } - -"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; } - -"sampler2D" { return SAMPLER2D; } -"samplerCube" { return SAMPLERCUBE; } -"samplerExternalOES" { return SAMPLER_EXTERNAL_OES; } -"sampler2DRect" { return SAMPLER2DRECT; } - -"struct" { return STRUCT; } - -"asm" { return reserved_word(yyscanner); } - -"class" { return reserved_word(yyscanner); } -"union" { return reserved_word(yyscanner); } -"enum" { return reserved_word(yyscanner); } -"typedef" { return reserved_word(yyscanner); } -"template" { return reserved_word(yyscanner); } -"this" { return reserved_word(yyscanner); } -"packed" { return reserved_word(yyscanner); } - -"goto" { return reserved_word(yyscanner); } -"switch" { return reserved_word(yyscanner); } -"default" { return reserved_word(yyscanner); } - -"inline" { return reserved_word(yyscanner); } -"noinline" { return reserved_word(yyscanner); } -"volatile" { return reserved_word(yyscanner); } -"public" { return reserved_word(yyscanner); } -"static" { return reserved_word(yyscanner); } -"extern" { return reserved_word(yyscanner); } -"external" { return reserved_word(yyscanner); } -"interface" { return reserved_word(yyscanner); } -"flat" { return reserved_word(yyscanner); } - -"long" { return reserved_word(yyscanner); } -"short" { return reserved_word(yyscanner); } -"double" { return reserved_word(yyscanner); } -"half" { return reserved_word(yyscanner); } -"fixed" { return reserved_word(yyscanner); } -"unsigned" { return reserved_word(yyscanner); } -"superp" { return reserved_word(yyscanner); } - -"input" { return reserved_word(yyscanner); } -"output" { return reserved_word(yyscanner); } - -"hvec2" { return reserved_word(yyscanner); } -"hvec3" { return reserved_word(yyscanner); } -"hvec4" { return reserved_word(yyscanner); } -"dvec2" { return reserved_word(yyscanner); } -"dvec3" { return reserved_word(yyscanner); } -"dvec4" { return reserved_word(yyscanner); } -"fvec2" { return reserved_word(yyscanner); } -"fvec3" { return reserved_word(yyscanner); } -"fvec4" { return reserved_word(yyscanner); } - -"sampler1D" { return reserved_word(yyscanner); } -"sampler3D" { return reserved_word(yyscanner); } -"sampler1DShadow" { return reserved_word(yyscanner); } -"sampler2DShadow" { return reserved_word(yyscanner); } -"sampler3DRect" { return reserved_word(yyscanner); } -"sampler2DRectShadow" { return reserved_word(yyscanner); } - -"sizeof" { return reserved_word(yyscanner); } -"cast" { return reserved_word(yyscanner); } - -"namespace" { return reserved_word(yyscanner); } -"using" { return reserved_word(yyscanner); } - -{L}({L}|{D})* { - yylval->lex.string = NewPoolTString(yytext); - return check_type(yyscanner); -} - -0[xX]{H}+ { return int_constant(yyscanner); } -0{O}+ { return int_constant(yyscanner); } -{D}+ { return int_constant(yyscanner); } - -{D}+{E} { return float_constant(yyscanner); } -{D}+"."{D}*({E})? { return float_constant(yyscanner); } -"."{D}+({E})? { return float_constant(yyscanner); } - -"+=" { 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; } -"." { 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; } - -[ \t\v\n\f\r] { } -<<EOF>> { yyterminate(); } -. { assert(false); return 0; } - -%% - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) { - pp::Token token; - yyget_extra(yyscanner)->preprocessor.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); - 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, ""); - yyextra->recover(); - return 0; -} - -void yyerror(YYLTYPE* lloc, TParseContext* context, const char* reason) { - context->error(*lloc, reason, yyget_text(context->scanner)); - context->recover(); -} - -int int_constant(yyscan_t yyscanner) { - struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; - - if (!atoi_clamp(yytext, &(yylval->lex.i))) - yyextra->warning(*yylloc, "Integer overflow", yytext, ""); - return INTCONSTANT; -} - -int float_constant(yyscan_t yyscanner) { - struct yyguts_t* yyg = (struct yyguts_t*) yyscanner; - - if (!atof_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->scanner = scanner; - return 0; -} - -int glslang_finalize(TParseContext* context) { - yyscan_t scanner = context->scanner; - if (scanner == NULL) return 0; - - context->scanner = NULL; - yylex_destroy(scanner); - - return 0; -} - -int glslang_scan(size_t count, const char* const string[], const int length[], - TParseContext* context) { - yyrestart(NULL, context->scanner); - yyset_column(0, context->scanner); - yyset_lineno(1, context->scanner); - - // Initialize preprocessor. - if (!context->preprocessor.init(count, string, length)) - return 1; - context->preprocessor.setMaxTokenLength(SH_MAX_TOKEN_LENGTH); - - // Define extension macros. - const TExtensionBehavior& extBehavior = context->extensionBehavior(); - for (TExtensionBehavior::const_iterator iter = extBehavior.begin(); - iter != extBehavior.end(); ++iter) { - context->preprocessor.predefineMacro(iter->first.c_str(), 1); - } - if (context->fragmentPrecisionHigh) - context->preprocessor.predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1); - - return 0; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/glslang.y b/Source/ThirdParty/ANGLE/src/compiler/glslang.y deleted file mode 100644 index eed5d7871..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/glslang.y +++ /dev/null @@ -1,2003 +0,0 @@ -/* -// -// 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 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-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. -// - -// This file is auto-generated by generate_parser.sh. DO NOT EDIT! - -// 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: 4505) -#pragma warning(disable: 4701) -#endif - -#include "compiler/SymbolTable.h" -#include "compiler/ParseContext.h" -#include "GLSLANG/ShaderLang.h" - -#define YYENABLE_NLS 0 - -#define YYLEX_PARAM context->scanner -%} - -%expect 1 /* One shift reduce conflict because of if | else */ -%pure-parser -%parse-param {TParseContext* context} -%locations -%lex-param {YYLEX_PARAM} - -%code requires { -#define YYLTYPE TSourceLoc -#define YYLTYPE_IS_DECLARED 1 -#define SH_MAX_TOKEN_LENGTH 256 // WebGL spec. -} - -%union { - struct { - union { - TString *string; - float f; - int i; - bool b; - }; - TSymbol* symbol; - } lex; - struct { - TOperator op; - union { - TIntermNode* intermNode; - TIntermNodePair nodePair; - TIntermTyped* intermTypedNode; - TIntermAggregate* intermAggregate; - }; - union { - TPublicType type; - TPrecision precision; - TQualifier qualifier; - TFunction* function; - TParameter param; - TField* field; - TFieldList* fieldList; - }; - } interm; -} - -%{ -extern int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, void* yyscanner); -extern void yyerror(YYLTYPE* yylloc, TParseContext* context, 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->shaderType != SH_VERTEX_SHADER) { \ - context->error(L, " supported in vertex shaders only ", S); \ - context->recover(); \ - } \ -} - -#define FRAG_ONLY(S, L) { \ - if (context->shaderType != SH_FRAGMENT_SHADER) { \ - context->error(L, " supported in fragment shaders only ", S); \ - context->recover(); \ - } \ -} -%} - -%token <lex> INVARIANT HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION -%token <lex> ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE -%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN -%token <lex> BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 -%token <lex> MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING -%token <lex> STRUCT VOID_TYPE WHILE -%token <lex> SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT - -%token <lex> IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT BOOLCONSTANT -%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.intermNode> translation_unit function_definition -%type <interm.intermNode> statement simple_statement -%type <interm.intermAggregate> statement_list compound_statement -%type <interm.intermNode> declaration_statement selection_statement expression_statement -%type <interm.intermNode> declaration external_declaration -%type <interm.intermNode> for_init_statement compound_statement_no_new_scope -%type <interm.nodePair> selection_rest_statement for_rest_statement -%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.qualifier> parameter_qualifier - -%type <interm.precision> precision_qualifier -%type <interm.type> type_qualifier fully_specified_type type_specifier -%type <interm.type> type_specifier_no_prec type_specifier_nonarray -%type <interm.type> struct_specifier -%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 - -%start translation_unit -%% - -identifier - : IDENTIFIER - | TYPE_NAME - -variable_identifier - : IDENTIFIER { - // The symbol table search was done in the lexical phase - const TSymbol* symbol = $1.symbol; - const TVariable* variable; - if (symbol == 0) { - context->error(@1, "undeclared identifier", $1.string->c_str()); - context->recover(); - TType type(EbtFloat, EbpUndefined); - TVariable* fakeVariable = new TVariable($1.string, type); - context->symbolTable.insert(*fakeVariable); - variable = fakeVariable; - } else { - // This identifier can only be a variable type symbol - if (! symbol->isVariable()) { - context->error(@1, "variable expected", $1.string->c_str()); - context->recover(); - } - - variable = static_cast<const TVariable*>(symbol); - - if (context->symbolTable.findBuiltIn(variable->getName()) && - !variable->getExtension().empty() && - context->extensionErrorCheck(@1, variable->getExtension())) { - context->recover(); - } - } - - // don't delete $1.string, it's used by error recovery, and the pool - // pop will reclaim the memory - - if (variable->getType().getQualifier() == EvqConst ) { - ConstantUnion* constArray = variable->getConstPointer(); - TType t(variable->getType()); - $$ = context->intermediate.addConstantUnion(constArray, t, @1); - } else - $$ = context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), - @1); - } - ; - -primary_expression - : variable_identifier { - $$ = $1; - } - | INTCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst($1.i); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1); - } - | FLOATCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setFConst($1.f); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); - } - | BOOLCONSTANT { - ConstantUnion *unionArray = new ConstantUnion[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 identifier { - if ($1->isArray()) { - context->error(@3, "cannot apply dot operator to an array", "."); - context->recover(); - } - - if ($1->isVector()) { - TVectorFields fields; - if (! context->parseVectorFields(*$3.string, $1->getNominalSize(), fields, @3)) { - fields.num = 1; - fields.offsets[0] = 0; - context->recover(); - } - - if ($1->getType().getQualifier() == EvqConst) { // constant folding for vector fields - $$ = context->addConstVectorNode(fields, $1, @3); - if ($$ == 0) { - context->recover(); - $$ = $1; - } - else - $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst, (int) (*$3.string).size())); - } else { - TString vectorString = *$3.string; - TIntermTyped* index = context->intermediate.addSwizzle(fields, @3); - $$ = context->intermediate.addIndex(EOpVectorSwizzle, $1, index, @2); - $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, (int) vectorString.size())); - } - } else if ($1->isMatrix()) { - TMatrixFields fields; - if (! context->parseMatrixFields(*$3.string, $1->getNominalSize(), fields, @3)) { - fields.wholeRow = false; - fields.wholeCol = false; - fields.row = 0; - fields.col = 0; - context->recover(); - } - - if (fields.wholeRow || fields.wholeCol) { - context->error(@2, " non-scalar fields not implemented yet", "."); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst(0); - TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @3); - $$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, @2); - $$->setType(TType($1->getBasicType(), $1->getPrecision(),EvqTemporary, $1->getNominalSize())); - } else { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst(fields.col * $1->getNominalSize() + fields.row); - TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @3); - $$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, @2); - $$->setType(TType($1->getBasicType(), $1->getPrecision())); - } - } else if ($1->getBasicType() == EbtStruct) { - bool fieldFound = false; - const TFieldList& fields = $1->getType().getStruct()->fields(); - unsigned int i; - for (i = 0; i < fields.size(); ++i) { - if (fields[i]->name() == *$3.string) { - fieldFound = true; - break; - } - } - if (fieldFound) { - if ($1->getType().getQualifier() == EvqConst) { - $$ = context->addConstStruct(*$3.string, $1, @2); - if ($$ == 0) { - context->recover(); - $$ = $1; - } - else { - $$->setType(*fields[i]->type()); - // change the qualifier of the return type, not of the structure field - // as the structure definition is shared between various structures. - $$->getTypePointer()->setQualifier(EvqConst); - } - } else { - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setIConst(i); - TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, *fields[i]->type(), @3); - $$ = context->intermediate.addIndex(EOpIndexDirectStruct, $1, index, @2); - $$->setType(*fields[i]->type()); - } - } else { - context->error(@2, " no such field in structure", $3.string->c_str()); - context->recover(); - $$ = $1; - } - } else { - context->error(@2, " field selection requires structure, vector, or matrix on left hand side", $3.string->c_str()); - context->recover(); - $$ = $1; - } - // don't delete $3.string, it's from the pool - } - | postfix_expression INC_OP { - if (context->lValueErrorCheck(@2, "++", $1)) - context->recover(); - $$ = context->intermediate.addUnaryMath(EOpPostIncrement, $1, @2, context->symbolTable); - if ($$ == 0) { - context->unaryOpError(@2, "++", $1->getCompleteString()); - context->recover(); - $$ = $1; - } - } - | postfix_expression DEC_OP { - if (context->lValueErrorCheck(@2, "--", $1)) - context->recover(); - $$ = context->intermediate.addUnaryMath(EOpPostDecrement, $1, @2, context->symbolTable); - if ($$ == 0) { - context->unaryOpError(@2, "--", $1->getCompleteString()); - context->recover(); - $$ = $1; - } - } - ; - -integer_expression - : expression { - if (context->integerErrorCheck($1, "[]")) - context->recover(); - $$ = $1; - } - ; - -function_call - : function_call_or_method { - TFunction* fnCall = $1.function; - TOperator op = fnCall->getBuiltInOp(); - - if (op != EOpNull) - { - // - // Then this should be a constructor. - // Don't go through the symbol table for constructors. - // Their parameters will be verified algorithmically. - // - TType type(EbtVoid, EbpUndefined); // use this to get the type back - if (context->constructorErrorCheck(@1, $1.intermNode, *fnCall, op, &type)) { - $$ = 0; - } else { - // - // It's a constructor, of type 'type'. - // - $$ = context->addConstructor($1.intermNode, &type, op, fnCall, @1); - } - - if ($$ == 0) { - context->recover(); - $$ = context->intermediate.setAggregateOperator(0, op, @1); - } - $$->setType(type); - } else { - // - // Not a constructor. Find it in the symbol table. - // - const TFunction* fnCandidate; - bool builtIn; - fnCandidate = context->findFunction(@1, fnCall, &builtIn); - if (fnCandidate) { - // - // A declared function. - // - if (builtIn && !fnCandidate->getExtension().empty() && - context->extensionErrorCheck(@1, fnCandidate->getExtension())) { - context->recover(); - } - 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. - // - $$ = context->intermediate.addUnaryMath(op, $1.intermNode, @1, context->symbolTable); - if ($$ == 0) { - std::stringstream extraInfoStream; - extraInfoStream << "built in unary operator function. Type: " << static_cast<TIntermTyped*>($1.intermNode)->getCompleteString(); - std::string extraInfo = extraInfoStream.str(); - context->error($1.intermNode->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str()); - YYERROR; - } - } else { - $$ = context->intermediate.setAggregateOperator($1.intermAggregate, op, @1); - } - } else { - // This is a real function call - - $$ = context->intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, @1); - $$->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) - $$->getAsAggregate()->setUserDefined(); - $$->getAsAggregate()->setName(fnCandidate->getMangledName()); - - TQualifier qual; - for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) { - qual = fnCandidate->getParam(i).type->getQualifier(); - if (qual == EvqOut || qual == EvqInOut) { - if (context->lValueErrorCheck($$->getLine(), "assign", $$->getAsAggregate()->getSequence()[i]->getAsTyped())) { - context->error($1.intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); - context->recover(); - } - } - } - } - $$->setType(fnCandidate->getReturnType()); - } else { - // error message was put out by PaFindFunction() - // Put on a dummy node for error recovery - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setFConst(0.0f); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); - context->recover(); - } - } - delete fnCall; - } - ; - -function_call_or_method - : function_call_generic { - $$ = $1; - } - | postfix_expression DOT function_call_generic { - context->error(@3, "methods are not supported", ""); - context->recover(); - $$ = $3; - } - ; - -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; - $$.intermNode = 0; - } - | function_call_header { - $$.function = $1; - $$.intermNode = 0; - } - ; - -function_call_header_with_parameters - : function_call_header assignment_expression { - TParameter param = { 0, new TType($2->getType()) }; - $1->addParameter(param); - $$.function = $1; - $$.intermNode = $2; - } - | function_call_header_with_parameters COMMA assignment_expression { - TParameter param = { 0, new TType($3->getType()) }; - $1.function->addParameter(param); - $$.function = $1.function; - $$.intermNode = 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_nonarray { - // - // Constructor - // - TOperator op = EOpNull; - if ($1.userDef) { - op = EOpConstructStruct; - } else { - switch ($1.type) { - case EbtFloat: - if ($1.matrix) { - switch($1.size) { - case 2: op = EOpConstructMat2; break; - case 3: op = EOpConstructMat3; break; - case 4: op = EOpConstructMat4; break; - } - } else { - switch($1.size) { - case 1: op = EOpConstructFloat; break; - case 2: op = EOpConstructVec2; break; - case 3: op = EOpConstructVec3; break; - case 4: op = EOpConstructVec4; break; - } - } - break; - case EbtInt: - switch($1.size) { - case 1: op = EOpConstructInt; break; - case 2: op = EOpConstructIVec2; break; - case 3: op = EOpConstructIVec3; break; - case 4: op = EOpConstructIVec4; break; - } - break; - case EbtBool: - switch($1.size) { - case 1: op = EOpConstructBool; break; - case 2: op = EOpConstructBVec2; break; - case 3: op = EOpConstructBVec3; break; - case 4: op = EOpConstructBVec4; break; - } - break; - default: break; - } - if (op == EOpNull) { - context->error(@1, "cannot construct this type", getBasicString($1.type)); - context->recover(); - $1.type = EbtFloat; - op = EOpConstructFloat; - } - } - TString tempString; - TType type($1); - TFunction *function = new TFunction(&tempString, type, op); - $$ = function; - } - | IDENTIFIER { - if (context->reservedErrorCheck(@1, *$1.string)) - context->recover(); - TType type(EbtVoid, EbpUndefined); - TFunction *function = new TFunction($1.string, type); - $$ = function; - } - ; - -unary_expression - : postfix_expression { - $$ = $1; - } - | INC_OP unary_expression { - if (context->lValueErrorCheck(@1, "++", $2)) - context->recover(); - $$ = context->intermediate.addUnaryMath(EOpPreIncrement, $2, @1, context->symbolTable); - if ($$ == 0) { - context->unaryOpError(@1, "++", $2->getCompleteString()); - context->recover(); - $$ = $2; - } - } - | DEC_OP unary_expression { - if (context->lValueErrorCheck(@1, "--", $2)) - context->recover(); - $$ = context->intermediate.addUnaryMath(EOpPreDecrement, $2, @1, context->symbolTable); - if ($$ == 0) { - context->unaryOpError(@1, "--", $2->getCompleteString()); - context->recover(); - $$ = $2; - } - } - | unary_operator unary_expression { - if ($1.op != EOpNull) { - $$ = context->intermediate.addUnaryMath($1.op, $2, @1, context->symbolTable); - if ($$ == 0) { - const char* errorOp = ""; - switch($1.op) { - case EOpNegative: errorOp = "-"; break; - case EOpLogicalNot: errorOp = "!"; break; - default: break; - } - context->unaryOpError(@1, errorOp, $2->getCompleteString()); - context->recover(); - $$ = $2; - } - } else - $$ = $2; - } - ; -// Grammar Note: No traditional style type casts. - -unary_operator - : PLUS { $$.op = EOpNull; } - | DASH { $$.op = EOpNegative; } - | BANG { $$.op = EOpLogicalNot; } - ; -// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. - -multiplicative_expression - : unary_expression { $$ = $1; } - | multiplicative_expression STAR unary_expression { - $$ = context->intermediate.addBinaryMath(EOpMul, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "*", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $1; - } - } - | multiplicative_expression SLASH unary_expression { - $$ = context->intermediate.addBinaryMath(EOpDiv, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "/", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $1; - } - } - ; - -additive_expression - : multiplicative_expression { $$ = $1; } - | additive_expression PLUS multiplicative_expression { - $$ = context->intermediate.addBinaryMath(EOpAdd, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "+", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $1; - } - } - | additive_expression DASH multiplicative_expression { - $$ = context->intermediate.addBinaryMath(EOpSub, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "-", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $1; - } - } - ; - -shift_expression - : additive_expression { $$ = $1; } - ; - -relational_expression - : shift_expression { $$ = $1; } - | relational_expression LEFT_ANGLE shift_expression { - $$ = context->intermediate.addBinaryMath(EOpLessThan, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "<", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - | relational_expression RIGHT_ANGLE shift_expression { - $$ = context->intermediate.addBinaryMath(EOpGreaterThan, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, ">", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - | relational_expression LE_OP shift_expression { - $$ = context->intermediate.addBinaryMath(EOpLessThanEqual, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "<=", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - | relational_expression GE_OP shift_expression { - $$ = context->intermediate.addBinaryMath(EOpGreaterThanEqual, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, ">=", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - ; - -equality_expression - : relational_expression { $$ = $1; } - | equality_expression EQ_OP relational_expression { - $$ = context->intermediate.addBinaryMath(EOpEqual, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "==", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - | equality_expression NE_OP relational_expression { - $$ = context->intermediate.addBinaryMath(EOpNotEqual, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "!=", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - ; - -and_expression - : equality_expression { $$ = $1; } - ; - -exclusive_or_expression - : and_expression { $$ = $1; } - ; - -inclusive_or_expression - : exclusive_or_expression { $$ = $1; } - ; - -logical_and_expression - : inclusive_or_expression { $$ = $1; } - | logical_and_expression AND_OP inclusive_or_expression { - $$ = context->intermediate.addBinaryMath(EOpLogicalAnd, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "&&", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - ; - -logical_xor_expression - : logical_and_expression { $$ = $1; } - | logical_xor_expression XOR_OP logical_and_expression { - $$ = context->intermediate.addBinaryMath(EOpLogicalXor, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "^^", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - ; - -logical_or_expression - : logical_xor_expression { $$ = $1; } - | logical_or_expression OR_OP logical_xor_expression { - $$ = context->intermediate.addBinaryMath(EOpLogicalOr, $1, $3, @2, context->symbolTable); - if ($$ == 0) { - context->binaryOpError(@2, "||", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - ConstantUnion *unionArray = new ConstantUnion[1]; - unionArray->setBConst(false); - $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); - } - } - ; - -conditional_expression - : logical_or_expression { $$ = $1; } - | logical_or_expression QUESTION expression COLON assignment_expression { - if (context->boolErrorCheck(@2, $1)) - context->recover(); - - $$ = context->intermediate.addSelection($1, $3, $5, @2); - if ($3->getType() != $5->getType()) - $$ = 0; - - if ($$ == 0) { - context->binaryOpError(@2, ":", $3->getCompleteString(), $5->getCompleteString()); - context->recover(); - $$ = $5; - } - } - ; - -assignment_expression - : conditional_expression { $$ = $1; } - | unary_expression assignment_operator assignment_expression { - if (context->lValueErrorCheck(@2, "assign", $1)) - context->recover(); - $$ = context->intermediate.addAssign($2.op, $1, $3, @2); - if ($$ == 0) { - context->assignError(@2, "assign", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $1; - } - } - ; - -assignment_operator - : EQUAL { $$.op = EOpAssign; } - | MUL_ASSIGN { $$.op = EOpMulAssign; } - | DIV_ASSIGN { $$.op = EOpDivAssign; } - | ADD_ASSIGN { $$.op = EOpAddAssign; } - | SUB_ASSIGN { $$.op = EOpSubAssign; } - ; - -expression - : assignment_expression { - $$ = $1; - } - | expression COMMA assignment_expression { - $$ = context->intermediate.addComma($1, $3, @2); - if ($$ == 0) { - context->binaryOpError(@2, ",", $1->getCompleteString(), $3->getCompleteString()); - context->recover(); - $$ = $3; - } - } - ; - -constant_expression - : conditional_expression { - if (context->constErrorCheck($1)) - context->recover(); - $$ = $1; - } - ; - -declaration - : function_prototype SEMICOLON { - TFunction &function = *($1.function); - - TIntermAggregate *prototype = new TIntermAggregate; - prototype->setType(function.getReturnType()); - prototype->setName(function.getName()); - - for (size_t i = 0; i < function.getParamCount(); i++) - { - const TParameter ¶m = function.getParam(i); - if (param.name != 0) - { - TVariable variable(param.name, *param.type); - - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), @1), @1); - } - else - { - prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(0, "", *param.type, @1), @1); - } - } - - prototype->setOp(EOpPrototype); - $$ = prototype; - - context->symbolTable.pop(); - } - | init_declarator_list SEMICOLON { - if ($1.intermAggregate) - $1.intermAggregate->setOp(EOpDeclaration); - $$ = $1.intermAggregate; - } - | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON { - if (($2 == EbpHigh) && (context->shaderType == SH_FRAGMENT_SHADER) && !context->fragmentPrecisionHigh) { - context->error(@1, "precision is not supported in fragment shader", "highp"); - context->recover(); - } - if (!context->symbolTable.setDefaultPrecision( $3, $2 )) { - context->error(@1, "illegal type argument for default precision qualifier", getBasicString($3.type)); - context->recover(); - } - $$ = 0; - } - ; - -function_prototype - : function_declarator RIGHT_PAREN { - // - // Multiple declarations of the same function are allowed. - // - // If this is a definition, the definition production code will check for redefinitions - // (we don't know at this point if it's a definition or not). - // - // Redeclarations are allowed. But, return types and parameter qualifiers must match. - // - TFunction* prevDec = static_cast<TFunction*>(context->symbolTable.find($1->getMangledName())); - if (prevDec) { - if (prevDec->getReturnType() != $1->getReturnType()) { - context->error(@2, "overloaded functions must have the same return type", $1->getReturnType().getBasicString()); - context->recover(); - } - for (size_t i = 0; i < prevDec->getParamCount(); ++i) { - if (prevDec->getParam(i).type->getQualifier() != $1->getParam(i).type->getQualifier()) { - context->error(@2, "overloaded functions must have the same parameter qualifiers", $1->getParam(i).type->getQualifierString()); - context->recover(); - } - } - } - - // - // Check for previously declared variables using the same name. - // - TSymbol *prevSym = context->symbolTable.find($1->getName()); - if (prevSym) - { - if (!prevSym->isFunction()) - { - context->error(@2, "redefinition", $1->getName().c_str(), "function"); - context->recover(); - } - } - else - { - // Insert the unmangled name to detect potential future redefinition as a variable. - context->symbolTable.getOuterLevel()->insert($1->getName(), *$1); - } - - // - // 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. - // - $$.function = $1; - - // We're at the inner scope level of the function's arguments and body statement. - // Add the function prototype to the surrounding scope instead. - context->symbolTable.getOuterLevel()->insert(*$$.function); - } - ; - -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); - 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"); - context->recover(); - delete $3.param.type; - } else { - // Add the parameter - $$ = $1; - $1->addParameter($3.param); - } - } - ; - -function_header - : fully_specified_type IDENTIFIER LEFT_PAREN { - if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary) { - context->error(@2, "no qualifiers allowed for function return", getQualifierString($1.qualifier)); - context->recover(); - } - // make sure a sampler is not involved as well... - if (context->structQualifierErrorCheck(@2, $1)) - context->recover(); - - // Add the function as a prototype after parsing it (we do not support recursion) - TFunction *function; - TType type($1); - function = new TFunction($2.string, type); - $$ = function; - - context->symbolTable.push(); - } - ; - -parameter_declarator - // Type + name - : type_specifier identifier { - if ($1.type == EbtVoid) { - context->error(@2, "illegal use of type 'void'", $2.string->c_str()); - context->recover(); - } - if (context->reservedErrorCheck(@2, *$2.string)) - context->recover(); - 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 - if (context->arrayTypeErrorCheck(@3, $1)) - context->recover(); - - if (context->reservedErrorCheck(@2, *$2.string)) - context->recover(); - - int size; - if (context->arraySizeErrorCheck(@3, $4, size)) - context->recover(); - $1.setArray(true, 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_qualifier parameter_declarator { - $$ = $3; - if (context->paramErrorCheck(@3, $1.qualifier, $2, $$.param.type)) - context->recover(); - } - | parameter_qualifier parameter_declarator { - $$ = $2; - if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type)) - context->recover(); - if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type)) - context->recover(); - } - // - // Only type - // - | type_qualifier parameter_qualifier parameter_type_specifier { - $$ = $3; - if (context->paramErrorCheck(@3, $1.qualifier, $2, $$.param.type)) - context->recover(); - } - | parameter_qualifier parameter_type_specifier { - $$ = $2; - if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type)) - context->recover(); - if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type)) - context->recover(); - } - ; - -parameter_qualifier - : /* empty */ { - $$ = EvqIn; - } - | IN_QUAL { - $$ = EvqIn; - } - | OUT_QUAL { - $$ = EvqOut; - } - | INOUT_QUAL { - $$ = EvqInOut; - } - ; - -parameter_type_specifier - : type_specifier { - TParameter param = { 0, new TType($1) }; - $$.param = param; - } - ; - -init_declarator_list - : single_declaration { - $$ = $1; - } - | init_declarator_list COMMA identifier { - if ($1.type.type == EbtInvariant && !$3.symbol) - { - context->error(@3, "undeclared identifier declared as invariant", $3.string->c_str()); - context->recover(); - } - - TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$3.string, TType($1.type), @3); - $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, symbol, @3); - - if (context->structQualifierErrorCheck(@3, $$.type)) - context->recover(); - - if (context->nonInitConstErrorCheck(@3, *$3.string, $$.type, false)) - context->recover(); - - TVariable* variable = 0; - if (context->nonInitErrorCheck(@3, *$3.string, $$.type, variable)) - context->recover(); - if (symbol && variable) - symbol->setId(variable->getUniqueId()); - } - | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET { - if (context->structQualifierErrorCheck(@3, $1.type)) - context->recover(); - - if (context->nonInitConstErrorCheck(@3, *$3.string, $1.type, true)) - context->recover(); - - $$ = $1; - - if (context->arrayTypeErrorCheck(@4, $1.type) || context->arrayQualifierErrorCheck(@4, $1.type)) - context->recover(); - else { - $1.type.setArray(true); - TVariable* variable; - if (context->arrayErrorCheck(@4, *$3.string, $1.type, variable)) - context->recover(); - } - } - | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { - if (context->structQualifierErrorCheck(@3, $1.type)) - context->recover(); - - if (context->nonInitConstErrorCheck(@3, *$3.string, $1.type, true)) - context->recover(); - - $$ = $1; - - if (context->arrayTypeErrorCheck(@4, $1.type) || context->arrayQualifierErrorCheck(@4, $1.type)) - context->recover(); - else { - int size; - if (context->arraySizeErrorCheck(@4, $5, size)) - context->recover(); - $1.type.setArray(true, size); - TVariable* variable = 0; - if (context->arrayErrorCheck(@4, *$3.string, $1.type, variable)) - context->recover(); - TType type = TType($1.type); - type.setArraySize(size); - $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, context->intermediate.addSymbol(variable ? variable->getUniqueId() : 0, *$3.string, type, @3), @3); - } - } - | init_declarator_list COMMA identifier EQUAL initializer { - if (context->structQualifierErrorCheck(@3, $1.type)) - context->recover(); - - $$ = $1; - - TIntermNode* intermNode; - if (!context->executeInitializer(@3, *$3.string, $1.type, $5, intermNode)) { - // - // build the intermediate representation - // - if (intermNode) - $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, intermNode, @4); - else - $$.intermAggregate = $1.intermAggregate; - } else { - context->recover(); - $$.intermAggregate = 0; - } - } - ; - -single_declaration - : fully_specified_type { - $$.type = $1; - $$.intermAggregate = context->intermediate.makeAggregate(context->intermediate.addSymbol(0, "", TType($1), @1), @1); - } - | fully_specified_type identifier { - TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, TType($1), @2); - $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); - - if (context->structQualifierErrorCheck(@2, $$.type)) - context->recover(); - - if (context->nonInitConstErrorCheck(@2, *$2.string, $$.type, false)) - context->recover(); - - $$.type = $1; - - TVariable* variable = 0; - if (context->nonInitErrorCheck(@2, *$2.string, $$.type, variable)) - context->recover(); - if (variable && symbol) - symbol->setId(variable->getUniqueId()); - } - | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET { - context->error(@2, "unsized array declarations not supported", $2.string->c_str()); - context->recover(); - - TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, TType($1), @2); - $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); - $$.type = $1; - } - | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { - TType type = TType($1); - int size; - if (context->arraySizeErrorCheck(@2, $4, size)) - context->recover(); - type.setArraySize(size); - TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, type, @2); - $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); - - if (context->structQualifierErrorCheck(@2, $1)) - context->recover(); - - if (context->nonInitConstErrorCheck(@2, *$2.string, $1, true)) - context->recover(); - - $$.type = $1; - - if (context->arrayTypeErrorCheck(@3, $1) || context->arrayQualifierErrorCheck(@3, $1)) - context->recover(); - else { - int size; - if (context->arraySizeErrorCheck(@3, $4, size)) - context->recover(); - - $1.setArray(true, size); - TVariable* variable = 0; - if (context->arrayErrorCheck(@3, *$2.string, $1, variable)) - context->recover(); - if (variable && symbol) - symbol->setId(variable->getUniqueId()); - } - } - | fully_specified_type identifier EQUAL initializer { - if (context->structQualifierErrorCheck(@2, $1)) - context->recover(); - - $$.type = $1; - - TIntermNode* intermNode; - if (!context->executeInitializer(@2, *$2.string, $1, $4, intermNode)) { - // - // Build intermediate representation - // - if(intermNode) - $$.intermAggregate = context->intermediate.makeAggregate(intermNode, @3); - else - $$.intermAggregate = 0; - } else { - context->recover(); - $$.intermAggregate = 0; - } - } - | INVARIANT IDENTIFIER { - VERTEX_ONLY("invariant declaration", @1); - if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying")) - context->recover(); - $$.type.setBasic(EbtInvariant, EvqInvariantVaryingOut, @2); - if (!$2.symbol) - { - context->error(@2, "undeclared identifier declared as invariant", $2.string->c_str()); - context->recover(); - - $$.intermAggregate = 0; - } - else - { - TIntermSymbol *symbol = context->intermediate.addSymbol(0, *$2.string, TType($$.type), @2); - $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); - } - } - ; - -fully_specified_type - : type_specifier { - $$ = $1; - - if ($1.array) { - context->error(@1, "not supported", "first-class array"); - context->recover(); - $1.setArray(false); - } - } - | type_qualifier type_specifier { - if ($2.array) { - context->error(@2, "not supported", "first-class array"); - context->recover(); - $2.setArray(false); - } - - if ($1.qualifier == EvqAttribute && - ($2.type == EbtBool || $2.type == EbtInt)) { - context->error(@2, "cannot be bool or int", getQualifierString($1.qualifier)); - context->recover(); - } - if (($1.qualifier == EvqVaryingIn || $1.qualifier == EvqVaryingOut) && - ($2.type == EbtBool || $2.type == EbtInt)) { - context->error(@2, "cannot be bool or int", getQualifierString($1.qualifier)); - context->recover(); - } - $$ = $2; - $$.qualifier = $1.qualifier; - } - ; - -type_qualifier - : CONST_QUAL { - $$.setBasic(EbtVoid, EvqConst, @1); - } - | ATTRIBUTE { - VERTEX_ONLY("attribute", @1); - if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "attribute")) - context->recover(); - $$.setBasic(EbtVoid, EvqAttribute, @1); - } - | VARYING { - if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "varying")) - context->recover(); - if (context->shaderType == SH_VERTEX_SHADER) - $$.setBasic(EbtVoid, EvqVaryingOut, @1); - else - $$.setBasic(EbtVoid, EvqVaryingIn, @1); - } - | INVARIANT VARYING { - if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying")) - context->recover(); - if (context->shaderType == SH_VERTEX_SHADER) - $$.setBasic(EbtVoid, EvqInvariantVaryingOut, @1); - else - $$.setBasic(EbtVoid, EvqInvariantVaryingIn, @1); - } - | UNIFORM { - if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform")) - context->recover(); - $$.setBasic(EbtVoid, EvqUniform, @1); - } - ; - -type_specifier - : type_specifier_no_prec { - $$ = $1; - - if ($$.precision == EbpUndefined) { - $$.precision = context->symbolTable.getDefaultPrecision($1.type); - if (context->precisionErrorCheck(@1, $$.precision, $1.type)) { - context->recover(); - } - } - } - | precision_qualifier type_specifier_no_prec { - $$ = $2; - $$.precision = $1; - } - ; - -precision_qualifier - : HIGH_PRECISION { - $$ = EbpHigh; - } - | MEDIUM_PRECISION { - $$ = EbpMedium; - } - | LOW_PRECISION { - $$ = EbpLow; - } - ; - -type_specifier_no_prec - : type_specifier_nonarray { - $$ = $1; - } - | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET { - $$ = $1; - - if (context->arrayTypeErrorCheck(@2, $1)) - context->recover(); - else { - int size; - if (context->arraySizeErrorCheck(@2, $3, size)) - context->recover(); - $$.setArray(true, size); - } - } - ; - -type_specifier_nonarray - : VOID_TYPE { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtVoid, qual, @1); - } - | FLOAT_TYPE { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - } - | INT_TYPE { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtInt, qual, @1); - } - | BOOL_TYPE { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtBool, qual, @1); - } - | VEC2 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(2); - } - | VEC3 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(3); - } - | VEC4 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(4); - } - | BVEC2 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtBool, qual, @1); - $$.setAggregate(2); - } - | BVEC3 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtBool, qual, @1); - $$.setAggregate(3); - } - | BVEC4 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtBool, qual, @1); - $$.setAggregate(4); - } - | IVEC2 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtInt, qual, @1); - $$.setAggregate(2); - } - | IVEC3 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtInt, qual, @1); - $$.setAggregate(3); - } - | IVEC4 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtInt, qual, @1); - $$.setAggregate(4); - } - | MATRIX2 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(2, true); - } - | MATRIX3 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(3, true); - } - | MATRIX4 { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtFloat, qual, @1); - $$.setAggregate(4, true); - } - | SAMPLER2D { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtSampler2D, qual, @1); - } - | SAMPLERCUBE { - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtSamplerCube, qual, @1); - } - | SAMPLER_EXTERNAL_OES { - if (!context->supportsExtension("GL_OES_EGL_image_external")) { - context->error(@1, "unsupported type", "samplerExternalOES"); - context->recover(); - } - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtSamplerExternalOES, qual, @1); - } - | SAMPLER2DRECT { - if (!context->supportsExtension("GL_ARB_texture_rectangle")) { - context->error(@1, "unsupported type", "sampler2DRect"); - context->recover(); - } - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtSampler2DRect, qual, @1); - } - | struct_specifier { - $$ = $1; - $$.qualifier = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - } - | TYPE_NAME { - // - // This is for user defined type names. The lexical phase looked up the - // type. - // - TType& structure = static_cast<TVariable*>($1.symbol)->getType(); - TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; - $$.setBasic(EbtStruct, qual, @1); - $$.userDef = &structure; - } - ; - -struct_specifier - : STRUCT identifier LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { - if (context->reservedErrorCheck(@2, *$2.string)) - context->recover(); - - TType* structure = new TType(new TStructure($2.string, $5)); - TVariable* userTypeDef = new TVariable($2.string, *structure, true); - if (! context->symbolTable.insert(*userTypeDef)) { - context->error(@2, "redefinition", $2.string->c_str(), "struct"); - context->recover(); - } - $$.setBasic(EbtStruct, EvqTemporary, @1); - $$.userDef = structure; - context->exitStructDeclaration(); - } - | STRUCT LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { - TType* structure = new TType(new TStructure(NewPoolTString(""), $4)); - $$.setBasic(EbtStruct, EvqTemporary, @1); - $$.userDef = structure; - context->exitStructDeclaration(); - } - ; - -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()); - context->recover(); - } - } - $$->push_back(field); - } - } - ; - -struct_declaration - : type_specifier struct_declarator_list SEMICOLON { - $$ = $2; - - if (context->voidErrorCheck(@1, (*$2)[0]->name(), $1)) { - context->recover(); - } - for (unsigned int i = 0; i < $$->size(); ++i) { - // - // Careful not to replace already known aspects of type, like array-ness - // - TType* type = (*$$)[i]->type(); - type->setBasicType($1.type); - type->setNominalSize($1.size); - type->setMatrix($1.matrix); - type->setPrecision($1.precision); - - // don't allow arrays of arrays - if (type->isArray()) { - if (context->arrayTypeErrorCheck(@1, $1)) - context->recover(); - } - if ($1.array) - type->setArraySize($1.arraySize); - if ($1.userDef) - type->setStruct($1.userDef->getStruct()); - - if (context->structNestingErrorCheck(@1, *(*$$)[i])) - context->recover(); - } - } - ; - -struct_declarator_list - : struct_declarator { - $$ = NewPoolTFieldList(); - $$->push_back($1); - } - | struct_declarator_list COMMA struct_declarator { - $$->push_back($3); - } - ; - -struct_declarator - : identifier { - if (context->reservedErrorCheck(@1, *$1.string)) - context->recover(); - - TType* type = new TType(EbtVoid, EbpUndefined); - $$ = new TField(type, $1.string); - } - | identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { - if (context->reservedErrorCheck(@1, *$1.string)) - context->recover(); - - TType* type = new TType(EbtVoid, EbpUndefined); - int size = 0; - if (context->arraySizeErrorCheck(@3, $3, size)) - context->recover(); - type->setArraySize(size); - - $$ = new TField(type, $1.string); - } - ; - -initializer - : assignment_expression { $$ = $1; } - ; - -declaration_statement - : declaration { $$ = $1; } - ; - -statement - : compound_statement { $$ = $1; } - | simple_statement { $$ = $1; } - ; - -// Grammar Note: No labeled statements; 'goto' is not supported. - -simple_statement - : declaration_statement { $$ = $1; } - | expression_statement { $$ = $1; } - | selection_statement { $$ = $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->setOp(EOpSequence); - $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->setOp(EOpSequence); - $2->setLine(@$); - } - $$ = $2; - } - ; - -statement_list - : statement { - $$ = context->intermediate.makeAggregate($1, @$); - } - | statement_list statement { - $$ = context->intermediate.growAggregate($1, $2, @$); - } - ; - -expression_statement - : SEMICOLON { $$ = 0; } - | expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); } - ; - -selection_statement - : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { - if (context->boolErrorCheck(@1, $3)) - context->recover(); - $$ = context->intermediate.addSelection($3, $5, @1); - } - ; - -selection_rest_statement - : statement_with_scope ELSE statement_with_scope { - $$.node1 = $1; - $$.node2 = $3; - } - | statement_with_scope { - $$.node1 = $1; - $$.node2 = 0; - } - ; - -// Grammar Note: No 'switch'. Switch statements not supported. - -condition - // In 1996 c++ draft, conditions can include single declarations - : expression { - $$ = $1; - if (context->boolErrorCheck($1->getLine(), $1)) - context->recover(); - } - | fully_specified_type identifier EQUAL initializer { - TIntermNode* intermNode; - if (context->structQualifierErrorCheck(@2, $1)) - context->recover(); - if (context->boolErrorCheck(@2, $1)) - context->recover(); - - if (!context->executeInitializer(@2, *$2.string, $1, $4, intermNode)) - $$ = $4; - else { - context->recover(); - $$ = 0; - } - } - ; - -iteration_statement - : WHILE LEFT_PAREN { context->symbolTable.push(); ++context->loopNestingLevel; } condition RIGHT_PAREN statement_no_new_scope { - context->symbolTable.pop(); - $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1); - --context->loopNestingLevel; - } - | DO { ++context->loopNestingLevel; } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { - if (context->boolErrorCheck(@8, $6)) - context->recover(); - - $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4); - --context->loopNestingLevel; - } - | FOR LEFT_PAREN { context->symbolTable.push(); ++context->loopNestingLevel; } 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->loopNestingLevel; - } - ; - -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 { - if (context->loopNestingLevel <= 0) { - context->error(@1, "continue statement only allowed in loops", ""); - context->recover(); - } - $$ = context->intermediate.addBranch(EOpContinue, @1); - } - | BREAK SEMICOLON { - if (context->loopNestingLevel <= 0) { - context->error(@1, "break statement only allowed in loops", ""); - context->recover(); - } - $$ = context->intermediate.addBranch(EOpBreak, @1); - } - | RETURN SEMICOLON { - $$ = context->intermediate.addBranch(EOpReturn, @1); - if (context->currentFunctionType->getBasicType() != EbtVoid) { - context->error(@1, "non-void function must return a value", "return"); - context->recover(); - } - } - | RETURN expression SEMICOLON { - $$ = context->intermediate.addBranch(EOpReturn, $2, @1); - context->functionReturnsValue = true; - if (context->currentFunctionType->getBasicType() == EbtVoid) { - context->error(@1, "void function cannot return a value", "return"); - context->recover(); - } else if (*(context->currentFunctionType) != $2->getType()) { - context->error(@1, "function return is not matching type:", "return"); - context->recover(); - } - } - | DISCARD SEMICOLON { - FRAG_ONLY("discard", @1); - $$ = context->intermediate.addBranch(EOpKill, @1); - } - ; - -// Grammar Note: No 'goto'. Gotos are not supported. - -translation_unit - : external_declaration { - $$ = $1; - context->treeRoot = $$; - } - | translation_unit external_declaration { - $$ = context->intermediate.growAggregate($1, $2, @$); - context->treeRoot = $$; - } - ; - -external_declaration - : function_definition { - $$ = $1; - } - | declaration { - $$ = $1; - } - ; - -function_definition - : function_prototype { - TFunction* function = $1.function; - - const TSymbol *builtIn = context->symbolTable.findBuiltIn(function->getMangledName()); - - if (builtIn) - { - context->error(@1, "built-in functions cannot be redefined", function->getName().c_str()); - context->recover(); - } - - TFunction* prevDec = static_cast<TFunction*>(context->symbolTable.find(function->getMangledName())); - // - // 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 (prevDec->isDefined()) { - // - // Then this function already has a body. - // - context->error(@1, "function already has a body", function->getName().c_str()); - context->recover(); - } - prevDec->setDefined(); - - // - // Raise error message if main function takes any parameters or return anything other than void - // - if (function->getName() == "main") { - if (function->getParamCount() > 0) { - context->error(@1, "function cannot take any parameter(s)", function->getName().c_str()); - context->recover(); - } - if (function->getReturnType().getBasicType() != EbtVoid) { - context->error(@1, "", function->getReturnType().getBasicString(), "main function cannot return a value"); - context->recover(); - } - } - - // - // Remember the return type for later checking for RETURN statements. - // - context->currentFunctionType = &(prevDec->getReturnType()); - context->functionReturnsValue = 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 TParameter& 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 (! context->symbolTable.insert(*variable)) { - context->error(@1, "redefinition", variable->getName().c_str()); - context->recover(); - delete variable; - } - - // - // Add the parameter to the HIL - // - paramNodes = context->intermediate.growAggregate( - paramNodes, - context->intermediate.addSymbol(variable->getUniqueId(), - variable->getName(), - variable->getType(), - @1), - @1); - } else { - paramNodes = context->intermediate.growAggregate(paramNodes, context->intermediate.addSymbol(0, "", *param.type, @1), @1); - } - } - context->intermediate.setAggregateOperator(paramNodes, EOpParameters, @1); - $1.intermAggregate = paramNodes; - context->loopNestingLevel = 0; - } - compound_statement_no_new_scope { - //?? Check that all paths return a value if return type != void ? - // May be best done as post process phase on intermediate code - if (context->currentFunctionType->getBasicType() != EbtVoid && ! context->functionReturnsValue) { - context->error(@1, "function does not return a value:", "", $1.function->getName().c_str()); - context->recover(); - } - - $$ = context->intermediate.growAggregate($1.intermAggregate, $3, @$); - context->intermediate.setAggregateOperator($$, EOpFunction, @1); - $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); - $$->getAsAggregate()->setType($1.function->getReturnType()); - - // store the pragma information for debug and optimize and other vendor specific - // information. This information can be queried from the parse tree - $$->getAsAggregate()->setOptimize(context->pragma().optimize); - $$->getAsAggregate()->setDebug(context->pragma().debug); - - context->symbolTable.pop(); - } - ; - -%% - -int glslang_parse(TParseContext* context) { - return yyparse(context); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/intermOut.cpp b/Source/ThirdParty/ANGLE/src/compiler/intermOut.cpp deleted file mode 100644 index 13aa96af6..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/intermOut.cpp +++ /dev/null @@ -1,424 +0,0 @@ -// -// 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/localintermediate.h" - -// -// 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) : sink(i) { } - TInfoSinkBase& sink; - -protected: - void visitSymbol(TIntermSymbol*); - void visitConstantUnion(TIntermConstantUnion*); - bool visitBinary(Visit visit, TIntermBinary*); - bool visitUnary(Visit visit, TIntermUnary*); - bool visitSelection(Visit visit, TIntermSelection*); - bool visitAggregate(Visit visit, TIntermAggregate*); - bool visitLoop(Visit visit, TIntermLoop*); - bool visitBranch(Visit visit, TIntermBranch*); -}; - -TString TType::getCompleteString() const -{ - TStringStream stream; - - if (qualifier != EvqTemporary && qualifier != EvqGlobal) - stream << getQualifierString() << " " << getPrecisionString() << " "; - if (array) - stream << "array[" << getArraySize() << "] of "; - if (matrix) - stream << size << "X" << size << " matrix of "; - else if (size > 1) - stream << size << "-component vector of "; - - stream << getBasicString(); - return stream.str(); -} - -// -// 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 << " "; -} - -// -// 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, depth); - - sink << "'" << node->getSymbol() << "' "; - sink << "(" << node->getCompleteString() << ")\n"; -} - -bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary* node) -{ - TInfoSinkBase& out = sink; - - OutputTreeText(out, node, depth); - - switch (node->getOp()) { - 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 EOpIndexDirect: out << "direct index"; break; - case EOpIndexIndirect: out << "indirect index"; break; - case EOpIndexDirectStruct: out << "direct index for structure"; break; - case EOpVectorSwizzle: out << "vector swizzle"; break; - - case EOpAdd: out << "add"; break; - case EOpSub: out << "subtract"; break; - case EOpMul: out << "component-wise multiply"; break; - case EOpDiv: out << "divide"; 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"; - - return true; -} - -bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary* node) -{ - TInfoSinkBase& out = sink; - - OutputTreeText(out, node, depth); - - switch (node->getOp()) { - case EOpNegative: out << "Negate value"; break; - case EOpVectorLogicalNot: - case EOpLogicalNot: out << "Negate conditional"; 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 EOpConvIntToBool: out << "Convert int to bool"; break; - case EOpConvFloatToBool:out << "Convert float to bool";break; - case EOpConvBoolToFloat:out << "Convert bool to float";break; - case EOpConvIntToFloat: out << "Convert int to float"; break; - case EOpConvFloatToInt: out << "Convert float to int"; break; - case EOpConvBoolToInt: out << "Convert bool to int"; 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 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 EOpCeil: out << "Ceiling"; break; - case EOpFract: out << "Fraction"; 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 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::visitAggregate(Visit visit, TIntermAggregate* node) -{ - TInfoSinkBase& out = sink; - - if (node->getOp() == EOpNull) { - out.prefix(EPrefixError); - out << "node is still EOpNull!"; - return true; - } - - OutputTreeText(out, node, depth); - - switch (node->getOp()) { - case EOpSequence: out << "Sequence\n"; return true; - case EOpComma: out << "Comma\n"; return true; - case EOpFunction: out << "Function Definition: " << node->getName(); break; - case EOpFunctionCall: out << "Function Call: " << node->getName(); break; - case EOpParameters: out << "Function Parameters: "; 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 EOpConstructMat2: out << "Construct mat2"; break; - case EOpConstructMat3: out << "Construct mat3"; 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 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 EOpDeclaration: out << "Declaration: "; break; - - default: - out.prefix(EPrefixError); - out << "Bad aggregation op"; - } - - if (node->getOp() != EOpSequence && node->getOp() != EOpParameters) - out << " (" << node->getCompleteString() << ")"; - - out << "\n"; - - return true; -} - -bool TOutputTraverser::visitSelection(Visit visit, TIntermSelection* node) -{ - TInfoSinkBase& out = sink; - - OutputTreeText(out, node, depth); - - out << "Test condition and select"; - out << " (" << node->getCompleteString() << ")\n"; - - ++depth; - - OutputTreeText(sink, node, depth); - out << "Condition\n"; - node->getCondition()->traverse(this); - - OutputTreeText(sink, node, depth); - if (node->getTrueBlock()) { - out << "true case\n"; - node->getTrueBlock()->traverse(this); - } else - out << "true case is null\n"; - - if (node->getFalseBlock()) { - OutputTreeText(sink, node, depth); - out << "false case\n"; - node->getFalseBlock()->traverse(this); - } - - --depth; - - return false; -} - -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, depth); - 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; - default: - out.message(EPrefixInternalError, node->getLine(), "Unknown constant"); - break; - } - } -} - -bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop* node) -{ - TInfoSinkBase& out = sink; - - OutputTreeText(out, node, depth); - - out << "Loop with condition "; - if (node->getType() == ELoopDoWhile) - out << "not "; - out << "tested first\n"; - - ++depth; - - OutputTreeText(sink, node, depth); - if (node->getCondition()) { - out << "Loop Condition\n"; - node->getCondition()->traverse(this); - } else - out << "No loop condition\n"; - - OutputTreeText(sink, node, depth); - if (node->getBody()) { - out << "Loop Body\n"; - node->getBody()->traverse(this); - } else - out << "No loop body\n"; - - if (node->getExpression()) { - OutputTreeText(sink, node, depth); - out << "Loop Terminal Expression\n"; - node->getExpression()->traverse(this); - } - - --depth; - - return false; -} - -bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch* node) -{ - TInfoSinkBase& out = sink; - - OutputTreeText(out, node, depth); - - 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"; - ++depth; - node->getExpression()->traverse(this); - --depth; - } 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. It's children will still be processed. -// -void TIntermediate::outputTree(TIntermNode* root) -{ - if (root == 0) - return; - - TOutputTraverser it(infoSink.info); - - root->traverse(&it); -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/intermediate.h b/Source/ThirdParty/ANGLE/src/compiler/intermediate.h deleted file mode 100644 index 14e39fd56..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/intermediate.h +++ /dev/null @@ -1,635 +0,0 @@ -// -// 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. -// - -// -// 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 __INTERMEDIATE_H -#define __INTERMEDIATE_H - -#include "GLSLANG/ShaderLang.h" - -#include <algorithm> -#include "compiler/Common.h" -#include "compiler/Types.h" -#include "compiler/ConstantUnion.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 - EOpSequence, // denotes a list of statements, or parameters, etc. - EOpFunctionCall, - EOpFunction, // For function definition - EOpParameters, // an aggregate listing the parameters to a function - - EOpDeclaration, - EOpPrototype, - - // - // Unary operators - // - - EOpNegative, - EOpLogicalNot, - EOpVectorLogicalNot, - - EOpPostIncrement, - EOpPostDecrement, - EOpPreIncrement, - EOpPreDecrement, - - EOpConvIntToBool, - EOpConvFloatToBool, - EOpConvBoolToFloat, - EOpConvIntToFloat, - EOpConvFloatToInt, - EOpConvBoolToInt, - - // - // binary operations - // - - EOpAdd, - EOpSub, - EOpMul, - EOpDiv, - EOpEqual, - EOpNotEqual, - EOpVectorEqual, - EOpVectorNotEqual, - EOpLessThan, - EOpGreaterThan, - EOpLessThanEqual, - EOpGreaterThanEqual, - EOpComma, - - EOpVectorTimesScalar, - EOpVectorTimesMatrix, - EOpMatrixTimesVector, - EOpMatrixTimesScalar, - - EOpLogicalOr, - EOpLogicalXor, - EOpLogicalAnd, - - EOpIndexDirect, - EOpIndexIndirect, - EOpIndexDirectStruct, - - EOpVectorSwizzle, - - // - // Built-in functions potentially mapped to operators - // - - EOpRadians, - EOpDegrees, - EOpSin, - EOpCos, - EOpTan, - EOpAsin, - EOpAcos, - EOpAtan, - - EOpPow, - EOpExp, - EOpLog, - EOpExp2, - EOpLog2, - EOpSqrt, - EOpInverseSqrt, - - EOpAbs, - EOpSign, - EOpFloor, - EOpCeil, - EOpFract, - EOpMod, - EOpMin, - EOpMax, - EOpClamp, - EOpMix, - EOpStep, - EOpSmoothStep, - - 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, - - EOpAny, - EOpAll, - - // - // Branch - // - - EOpKill, // Fragment only - EOpReturn, - EOpBreak, - EOpContinue, - - // - // Constructors - // - - EOpConstructInt, - EOpConstructBool, - EOpConstructFloat, - EOpConstructVec2, - EOpConstructVec3, - EOpConstructVec4, - EOpConstructBVec2, - EOpConstructBVec3, - EOpConstructBVec4, - EOpConstructIVec2, - EOpConstructIVec3, - EOpConstructIVec4, - EOpConstructMat2, - EOpConstructMat3, - EOpConstructMat4, - EOpConstructStruct, - - // - // moves - // - - EOpAssign, - EOpInitialize, - EOpAddAssign, - EOpSubAssign, - EOpMulAssign, - EOpVectorTimesMatrixAssign, - EOpVectorTimesScalarAssign, - EOpMatrixTimesScalarAssign, - EOpMatrixTimesMatrixAssign, - EOpDivAssign -}; - -extern const char* getOperatorString(TOperator op); - -class TIntermTraverser; -class TIntermAggregate; -class TIntermBinary; -class TIntermUnary; -class TIntermConstantUnion; -class TIntermSelection; -class TIntermTyped; -class TIntermSymbol; -class TIntermLoop; -class TInfoSink; - -// -// Base class for the tree nodes -// -class TIntermNode { -public: - POOL_ALLOCATOR_NEW_DELETE(); - TIntermNode() { - // TODO: Move this to TSourceLoc constructor - // after getting rid of TPublicType. - line.first_file = line.last_file = 0; - line.first_line = line.last_line = 0; - } - virtual ~TIntermNode() { } - - const TSourceLoc& getLine() const { return line; } - void setLine(const TSourceLoc& l) { line = l; } - - virtual void traverse(TIntermTraverser*) = 0; - virtual TIntermTyped* getAsTyped() { return 0; } - virtual TIntermConstantUnion* getAsConstantUnion() { return 0; } - virtual TIntermAggregate* getAsAggregate() { return 0; } - virtual TIntermBinary* getAsBinaryNode() { return 0; } - virtual TIntermUnary* getAsUnaryNode() { return 0; } - virtual TIntermSelection* getAsSelectionNode() { return 0; } - virtual TIntermSymbol* getAsSymbolNode() { return 0; } - virtual TIntermLoop* getAsLoopNode() { 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 line; -}; - -// -// 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) : type(t) { } - virtual TIntermTyped* getAsTyped() { return this; } - - virtual bool hasSideEffects() const = 0; - - void setType(const TType& t) { type = t; } - const TType& getType() const { return type; } - TType* getTypePointer() { return &type; } - - TBasicType getBasicType() const { return type.getBasicType(); } - TQualifier getQualifier() const { return type.getQualifier(); } - TPrecision getPrecision() const { return type.getPrecision(); } - int getNominalSize() const { return type.getNominalSize(); } - - bool isMatrix() const { return type.isMatrix(); } - bool isArray() const { return type.isArray(); } - bool isVector() const { return type.isVector(); } - bool isScalar() const { return type.isScalar(); } - const char* getBasicString() const { return type.getBasicString(); } - const char* getQualifierString() const { return type.getQualifierString(); } - TString getCompleteString() const { return type.getCompleteString(); } - - int totalRegisterCount() const { return type.totalRegisterCount(); } - int elementRegisterCount() const { return type.elementRegisterCount(); } - int getArraySize() const { return type.getArraySize(); } - -protected: - TType type; -}; - -// -// Handle for, do-while, and while loops. -// -enum TLoopType { - ELoopFor, - ELoopWhile, - ELoopDoWhile -}; - -class TIntermLoop : public TIntermNode { -public: - TIntermLoop(TLoopType aType, - TIntermNode *aInit, TIntermTyped* aCond, TIntermTyped* aExpr, - TIntermNode* aBody) : - type(aType), - init(aInit), - cond(aCond), - expr(aExpr), - body(aBody), - unrollFlag(false) { } - - virtual TIntermLoop* getAsLoopNode() { return this; } - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - TLoopType getType() const { return type; } - TIntermNode* getInit() { return init; } - TIntermTyped* getCondition() { return cond; } - TIntermTyped* getExpression() { return expr; } - TIntermNode* getBody() { return body; } - - void setUnrollFlag(bool flag) { unrollFlag = flag; } - bool getUnrollFlag() { return unrollFlag; } - -protected: - TLoopType type; - TIntermNode* init; // for-loop initialization - TIntermTyped* cond; // loop exit condition - TIntermTyped* expr; // for-loop expression - TIntermNode* body; // loop body - - bool unrollFlag; // Whether the loop should be unrolled or not. -}; - -// -// Handle break, continue, return, and kill. -// -class TIntermBranch : public TIntermNode { -public: - TIntermBranch(TOperator op, TIntermTyped* e) : - flowOp(op), - expression(e) { } - - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - TOperator getFlowOp() { return flowOp; } - TIntermTyped* getExpression() { return expression; } - -protected: - TOperator flowOp; - TIntermTyped* expression; // 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 i, const TString& sym, const TType& t) : - TIntermTyped(t), id(i) { symbol = sym; originalSymbol = sym; } - - virtual bool hasSideEffects() const { return false; } - - int getId() const { return id; } - const TString& getSymbol() const { return symbol; } - - void setId(int newId) { id = newId; } - void setSymbol(const TString& sym) { symbol = sym; } - - const TString& getOriginalSymbol() const { return originalSymbol; } - - virtual void traverse(TIntermTraverser*); - virtual TIntermSymbol* getAsSymbolNode() { return this; } - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } - -protected: - int id; - TString symbol; - TString originalSymbol; -}; - -class TIntermConstantUnion : public TIntermTyped { -public: - TIntermConstantUnion(ConstantUnion *unionPointer, const TType& t) : TIntermTyped(t), unionArrayPointer(unionPointer) { } - - virtual bool hasSideEffects() const { return false; } - - ConstantUnion* getUnionArrayPointer() const { return unionArrayPointer; } - - int getIConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getIConst() : 0; } - float getFConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getFConst() : 0.0f; } - bool getBConst(int index) const { return unionArrayPointer ? unionArrayPointer[index].getBConst() : false; } - - virtual TIntermConstantUnion* getAsConstantUnion() { return this; } - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode(TIntermNode *, TIntermNode *) { return false; } - - TIntermTyped* fold(TOperator, TIntermTyped*, TInfoSink&); - -protected: - ConstantUnion *unionArrayPointer; -}; - -// -// Intermediate class for node types that hold operators. -// -class TIntermOperator : public TIntermTyped { -public: - TOperator getOp() const { return op; } - void setOp(TOperator o) { op = o; } - - bool isAssignment() const; - bool isConstructor() const; - - virtual bool hasSideEffects() const { return isAssignment(); } - -protected: - TIntermOperator(TOperator o) : TIntermTyped(TType(EbtFloat, EbpUndefined)), op(o) {} - TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o) {} - TOperator op; -}; - -// -// Nodes for all the basic binary math operators. -// -class TIntermBinary : public TIntermOperator { -public: - TIntermBinary(TOperator o) : TIntermOperator(o), addIndexClamp(false) {} - - virtual TIntermBinary* getAsBinaryNode() { return this; } - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - virtual bool hasSideEffects() const { return (isAssignment() || left->hasSideEffects() || right->hasSideEffects()); } - - void setLeft(TIntermTyped* n) { left = n; } - void setRight(TIntermTyped* n) { right = n; } - TIntermTyped* getLeft() const { return left; } - TIntermTyped* getRight() const { return right; } - bool promote(TInfoSink&); - - void setAddIndexClamp() { addIndexClamp = true; } - bool getAddIndexClamp() { return addIndexClamp; } - -protected: - TIntermTyped* left; - TIntermTyped* right; - - // If set to true, wrap any EOpIndexIndirect with a clamp to bounds. - bool addIndexClamp; -}; - -// -// Nodes for unary math operators. -// -class TIntermUnary : public TIntermOperator { -public: - TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0), useEmulatedFunction(false) {} - TIntermUnary(TOperator o) : TIntermOperator(o), operand(0), useEmulatedFunction(false) {} - - virtual void traverse(TIntermTraverser*); - virtual TIntermUnary* getAsUnaryNode() { return this; } - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - virtual bool hasSideEffects() const { return (isAssignment() || operand->hasSideEffects()); } - - void setOperand(TIntermTyped* o) { operand = o; } - TIntermTyped* getOperand() { return operand; } - bool promote(TInfoSink&); - - void setUseEmulatedFunction() { useEmulatedFunction = true; } - bool getUseEmulatedFunction() { return useEmulatedFunction; } - -protected: - TIntermTyped* operand; - - // If set to true, replace the built-in function call with an emulated one - // to work around driver bugs. - bool useEmulatedFunction; -}; - -typedef TVector<TIntermNode*> TIntermSequence; -typedef TVector<int> TQualifierList; - -// -// Nodes that operate on an arbitrary sized set of children. -// -class TIntermAggregate : public TIntermOperator { -public: - TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), useEmulatedFunction(false) { } - TIntermAggregate(TOperator o) : TIntermOperator(o), useEmulatedFunction(false) { } - ~TIntermAggregate() { } - - virtual TIntermAggregate* getAsAggregate() { return this; } - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - // Conservatively assume function calls and other aggregate operators have side-effects - virtual bool hasSideEffects() const { return true; } - - TIntermSequence& getSequence() { return sequence; } - - void setName(const TString& n) { name = n; } - const TString& getName() const { return name; } - - void setUserDefined() { userDefined = true; } - bool isUserDefined() const { return userDefined; } - - void setOptimize(bool o) { optimize = o; } - bool getOptimize() { return optimize; } - void setDebug(bool d) { debug = d; } - bool getDebug() { return debug; } - - void setUseEmulatedFunction() { useEmulatedFunction = true; } - bool getUseEmulatedFunction() { return useEmulatedFunction; } - -protected: - TIntermAggregate(const TIntermAggregate&); // disallow copy constructor - TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator - TIntermSequence sequence; - TString name; - bool userDefined; // used for user defined function names - - bool optimize; - bool debug; - - // If set to true, replace the built-in function call with an emulated one - // to work around driver bugs. - bool useEmulatedFunction; -}; - -// -// For if tests. Simplified since there is no switch statement. -// -class TIntermSelection : public TIntermTyped { -public: - TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : - TIntermTyped(TType(EbtVoid, EbpUndefined)), condition(cond), trueBlock(trueB), falseBlock(falseB) {} - TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : - TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB) {} - - virtual void traverse(TIntermTraverser*); - virtual bool replaceChildNode( - TIntermNode *original, TIntermNode *replacement); - - // Conservatively assume selections have side-effects - virtual bool hasSideEffects() const { return true; } - - bool usesTernaryOperator() const { return getBasicType() != EbtVoid; } - TIntermNode* getCondition() const { return condition; } - TIntermNode* getTrueBlock() const { return trueBlock; } - TIntermNode* getFalseBlock() const { return falseBlock; } - TIntermSelection* getAsSelectionNode() { return this; } - -protected: - TIntermTyped* condition; - TIntermNode* trueBlock; - TIntermNode* falseBlock; -}; - -enum Visit -{ - PreVisit, - InVisit, - PostVisit -}; - -// -// For traversing the tree. User should derive from this, -// put their traversal specific data in it, and then pass -// it to a Traverse method. -// -// 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 -{ -public: - POOL_ALLOCATOR_NEW_DELETE(); - TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) : - preVisit(preVisit), - inVisit(inVisit), - postVisit(postVisit), - rightToLeft(rightToLeft), - depth(0), - maxDepth(0) {} - virtual ~TIntermTraverser() {} - - virtual void visitSymbol(TIntermSymbol*) {} - virtual void visitConstantUnion(TIntermConstantUnion*) {} - virtual bool visitBinary(Visit visit, TIntermBinary*) {return true;} - virtual bool visitUnary(Visit visit, TIntermUnary*) {return true;} - virtual bool visitSelection(Visit visit, TIntermSelection*) {return true;} - virtual bool visitAggregate(Visit visit, TIntermAggregate*) {return true;} - virtual bool visitLoop(Visit visit, TIntermLoop*) {return true;} - virtual bool visitBranch(Visit visit, TIntermBranch*) {return true;} - - int getMaxDepth() const {return maxDepth;} - - void incrementDepth(TIntermNode *current) - { - depth++; - maxDepth = std::max(maxDepth, depth); - path.push_back(current); - } - - void decrementDepth() - { - depth--; - path.pop_back(); - } - - TIntermNode *getParentNode() - { - return path.size() == 0 ? NULL : path.back(); - } - - // Return the original name if hash function pointer is NULL; - // otherwise return the hashed name. - static TString hash(const TString& name, ShHashFunction64 hashFunction); - - const bool preVisit; - const bool inVisit; - const bool postVisit; - const bool rightToLeft; - -protected: - int depth; - int maxDepth; - - // All the nodes from root to the current node's parent during traversing. - TVector<TIntermNode *> path; -}; - -#endif // __INTERMEDIATE_H diff --git a/Source/ThirdParty/ANGLE/src/compiler/localintermediate.h b/Source/ThirdParty/ANGLE/src/compiler/localintermediate.h deleted file mode 100644 index ccbc40178..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/localintermediate.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// 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 _LOCAL_INTERMEDIATE_INCLUDED_ -#define _LOCAL_INTERMEDIATE_INCLUDED_ - -#include "GLSLANG/ShaderLang.h" -#include "compiler/intermediate.h" -#include "compiler/SymbolTable.h" - -struct TVectorFields { - int offsets[4]; - int num; -}; - -// -// Set of helper functions to help parse and build the tree. -// -class TInfoSink; -class TIntermediate { -public: - POOL_ALLOCATOR_NEW_DELETE(); - TIntermediate(TInfoSink& i) : infoSink(i) { } - - TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TSourceLoc&); - TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); - TIntermTyped* addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&, TSymbolTable&); - TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); - TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc&); - TIntermTyped* addUnaryMath(TOperator op, TIntermNode* child, const TSourceLoc&, TSymbolTable&); - TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); - TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&); - TIntermAggregate* setAggregateOperator(TIntermNode*, TOperator, const TSourceLoc&); - TIntermNode* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); - TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); - TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); - TIntermConstantUnion* addConstantUnion(ConstantUnion*, const TType&, const TSourceLoc&); - TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) ; - bool parseConstTree(const TSourceLoc&, TIntermNode*, ConstantUnion*, TOperator, TSymbolTable&, TType, bool singleConstantParam = false); - TIntermNode* addLoop(TLoopType, TIntermNode*, TIntermTyped*, TIntermTyped*, TIntermNode*, const TSourceLoc&); - TIntermBranch* addBranch(TOperator, const TSourceLoc&); - TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); - TIntermTyped* addSwizzle(TVectorFields&, const TSourceLoc&); - bool postProcess(TIntermNode*); - void remove(TIntermNode*); - void outputTree(TIntermNode*); - -private: - void operator=(TIntermediate&); // prevent assignments - - TInfoSink& infoSink; -}; - -#endif // _LOCAL_INTERMEDIATE_INCLUDED_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/osinclude.h b/Source/ThirdParty/ANGLE/src/compiler/osinclude.h deleted file mode 100644 index d8bb1a797..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/osinclude.h +++ /dev/null @@ -1,65 +0,0 @@ -// -// 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 __OSINCLUDE_H -#define __OSINCLUDE_H - -// -// This file contains contains os-specific datatypes and -// declares any os-specific functions. -// - -#if defined(_WIN32) || defined(_WIN64) -#define ANGLE_OS_WIN -#elif defined(__APPLE__) || defined(__linux__) || \ - defined(__FreeBSD__) || defined(__OpenBSD__) || \ - defined(__sun) || defined(ANDROID) || \ - defined(__GLIBC__) || defined(__GNU__) || \ - defined(__QNX__) -#define ANGLE_OS_POSIX -#else -#error Unsupported platform. -#endif - -#if defined(ANGLE_OS_WIN) -#define STRICT -#define VC_EXTRALEAN 1 -#include <windows.h> -#elif defined(ANGLE_OS_POSIX) -#include <pthread.h> -#include <semaphore.h> -#include <errno.h> -#endif // ANGLE_OS_WIN - - -#include "compiler/debug.h" - -// -// Thread Local Storage Operations -// -#if defined(ANGLE_OS_WIN) -typedef DWORD OS_TLSIndex; -#define OS_INVALID_TLS_INDEX (TLS_OUT_OF_INDEXES) -#elif defined(ANGLE_OS_POSIX) -typedef pthread_key_t OS_TLSIndex; -#define OS_INVALID_TLS_INDEX (static_cast<OS_TLSIndex>(-1)) -#endif // ANGLE_OS_WIN - -OS_TLSIndex OS_AllocTLSIndex(); -bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue); -bool OS_FreeTLSIndex(OS_TLSIndex nIndex); - -inline void* OS_GetTLSValue(OS_TLSIndex nIndex) -{ - ASSERT(nIndex != OS_INVALID_TLS_INDEX); -#if defined(ANGLE_OS_WIN) - return TlsGetValue(nIndex); -#elif defined(ANGLE_OS_POSIX) - return pthread_getspecific(nIndex); -#endif // ANGLE_OS_WIN -} - -#endif // __OSINCLUDE_H diff --git a/Source/ThirdParty/ANGLE/src/compiler/ossource_posix.cpp b/Source/ThirdParty/ANGLE/src/compiler/ossource_posix.cpp deleted file mode 100644 index 1e1e699ae..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ossource_posix.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// 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. -// - -// -// This file contains the posix specific functions -// -#include "compiler/osinclude.h" - -#if !defined(ANGLE_OS_POSIX) -#error Trying to build a posix specific file in a non-posix build. -#endif - -// -// Thread Local Storage Operations -// -OS_TLSIndex OS_AllocTLSIndex() -{ - pthread_key_t pPoolIndex; - - // - // Create global pool key. - // - if ((pthread_key_create(&pPoolIndex, NULL)) != 0) { - assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage"); - return false; - } - else { - return pPoolIndex; - } -} - - -bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue) -{ - if (nIndex == OS_INVALID_TLS_INDEX) { - assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); - return false; - } - - if (pthread_setspecific(nIndex, lpvValue) == 0) - return true; - else - return false; -} - - -bool OS_FreeTLSIndex(OS_TLSIndex nIndex) -{ - if (nIndex == OS_INVALID_TLS_INDEX) { - assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); - return false; - } - - // - // Delete the global pool key. - // - if (pthread_key_delete(nIndex) == 0) - return true; - else - return false; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/ossource_win.cpp b/Source/ThirdParty/ANGLE/src/compiler/ossource_win.cpp deleted file mode 100644 index 89922fef3..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/ossource_win.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// -// 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/osinclude.h" -// -// This file contains contains the window's specific functions -// - -#if !defined(ANGLE_OS_WIN) -#error Trying to build a windows specific file in a non windows build. -#endif - - -// -// Thread Local Storage Operations -// -OS_TLSIndex OS_AllocTLSIndex() -{ - DWORD dwIndex = TlsAlloc(); - if (dwIndex == TLS_OUT_OF_INDEXES) { - assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage"); - return OS_INVALID_TLS_INDEX; - } - - return dwIndex; -} - - -bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue) -{ - if (nIndex == OS_INVALID_TLS_INDEX) { - assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); - return false; - } - - if (TlsSetValue(nIndex, lpvValue)) - return true; - else - return false; -} - - -bool OS_FreeTLSIndex(OS_TLSIndex nIndex) -{ - if (nIndex == OS_INVALID_TLS_INDEX) { - assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); - return false; - } - - if (TlsFree(nIndex)) - return true; - else - return false; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/parseConst.cpp b/Source/ThirdParty/ANGLE/src/compiler/parseConst.cpp deleted file mode 100644 index 65ed115cb..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/parseConst.cpp +++ /dev/null @@ -1,245 +0,0 @@ -// -// 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/ParseContext.h" - -// -// Use this class to carry along data from node to node in -// the traversal -// -class TConstTraverser : public TIntermTraverser { -public: - TConstTraverser(ConstantUnion* cUnion, bool singleConstParam, TOperator constructType, TInfoSink& sink, TSymbolTable& symTable, TType& t) - : error(false), - index(0), - unionArray(cUnion), - type(t), - constructorType(constructType), - singleConstantParam(singleConstParam), - infoSink(sink), - symbolTable(symTable), - size(0), - isMatrix(false), - matrixSize(0) { - } - - bool error; - -protected: - void visitSymbol(TIntermSymbol*); - void visitConstantUnion(TIntermConstantUnion*); - bool visitBinary(Visit visit, TIntermBinary*); - bool visitUnary(Visit visit, TIntermUnary*); - bool visitSelection(Visit visit, TIntermSelection*); - bool visitAggregate(Visit visit, TIntermAggregate*); - bool visitLoop(Visit visit, TIntermLoop*); - bool visitBranch(Visit visit, TIntermBranch*); - - size_t index; - ConstantUnion *unionArray; - TType type; - TOperator constructorType; - bool singleConstantParam; - TInfoSink& infoSink; - TSymbolTable& symbolTable; - size_t size; // size of the constructor ( 4 for vec4) - bool isMatrix; - size_t matrixSize; // dimension of the matrix (nominal size and not the instance size) -}; - -// -// 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 TConstTraverser::visitSymbol(TIntermSymbol* node) -{ - infoSink.info.message(EPrefixInternalError, node->getLine(), "Symbol Node found in constant constructor"); - return; - -} - -bool TConstTraverser::visitBinary(Visit visit, TIntermBinary* node) -{ - TQualifier qualifier = node->getType().getQualifier(); - - if (qualifier != EvqConst) { - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(type.getCompleteString()); - infoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; - } - - infoSink.info.message(EPrefixInternalError, node->getLine(), "Binary Node found in constant constructor"); - - return false; -} - -bool TConstTraverser::visitUnary(Visit visit, TIntermUnary* node) -{ - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(type.getCompleteString()); - infoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; -} - -bool TConstTraverser::visitAggregate(Visit visit, TIntermAggregate* node) -{ - if (!node->isConstructor() && node->getOp() != EOpComma) { - TString buf; - buf.append("'constructor' : assigning non-constant to "); - buf.append(type.getCompleteString()); - infoSink.info.message(EPrefixError, node->getLine(), buf.c_str()); - error = true; - return false; - } - - if (node->getSequence().size() == 0) { - error = true; - return false; - } - - bool flag = node->getSequence().size() == 1 && node->getSequence()[0]->getAsTyped()->getAsConstantUnion(); - if (flag) - { - singleConstantParam = true; - constructorType = node->getOp(); - size = node->getType().getObjectSize(); - - if (node->getType().isMatrix()) { - isMatrix = true; - matrixSize = node->getType().getNominalSize(); - } - } - - for (TIntermSequence::iterator p = node->getSequence().begin(); - p != node->getSequence().end(); p++) { - - if (node->getOp() == EOpComma) - index = 0; - - (*p)->traverse(this); - } - if (flag) - { - singleConstantParam = false; - constructorType = EOpNull; - size = 0; - isMatrix = false; - matrixSize = 0; - } - return false; -} - -bool TConstTraverser::visitSelection(Visit visit, TIntermSelection* node) -{ - infoSink.info.message(EPrefixInternalError, node->getLine(), "Selection Node found in constant constructor"); - error = true; - return false; -} - -void TConstTraverser::visitConstantUnion(TIntermConstantUnion* node) -{ - if (!node->getUnionArrayPointer()) - { - // The constant was not initialized, this should already have been logged - assert(infoSink.info.size() != 0); - return; - } - - ConstantUnion* leftUnionArray = unionArray; - size_t instanceSize = type.getObjectSize(); - - if (index >= instanceSize) - return; - - if (!singleConstantParam) { - size_t size = node->getType().getObjectSize(); - - ConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - for (size_t i = 0; i < size; i++) { - if (index >= instanceSize) - return; - leftUnionArray[index] = rightUnionArray[i]; - - (index)++; - } - } else { - size_t totalSize = index + size; - ConstantUnion *rightUnionArray = node->getUnionArrayPointer(); - if (!isMatrix) { - size_t count = 0; - for (size_t i = index; i < totalSize; i++) { - if (i >= instanceSize) - return; - - leftUnionArray[i] = rightUnionArray[count]; - - (index)++; - - if (node->getType().getObjectSize() > 1) - count++; - } - } else { // for matrix constructors - size_t count = 0; - size_t element = index; - for (size_t i = index; i < totalSize; i++) { - if (i >= instanceSize) - return; - if (element - i == 0 || (i - element) % (matrixSize + 1) == 0 ) - leftUnionArray[i] = rightUnionArray[count]; - else - leftUnionArray[i].setFConst(0.0f); - - (index)++; - - if (node->getType().getObjectSize() > 1) - count++; - } - } - } -} - -bool TConstTraverser::visitLoop(Visit visit, TIntermLoop* node) -{ - infoSink.info.message(EPrefixInternalError, node->getLine(), "Loop Node found in constant constructor"); - error = true; - return false; -} - -bool TConstTraverser::visitBranch(Visit visit, TIntermBranch* node) -{ - infoSink.info.message(EPrefixInternalError, node->getLine(), "Branch Node found in constant constructor"); - error = true; - 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. It's children will still be processed. -// -bool TIntermediate::parseConstTree(const TSourceLoc& line, TIntermNode* root, ConstantUnion* unionArray, TOperator constructorType, TSymbolTable& symbolTable, TType t, bool singleConstantParam) -{ - if (root == 0) - return false; - - TConstTraverser it(unionArray, singleConstantParam, constructorType, infoSink, symbolTable, t); - - root->traverse(&it); - if (it.error) - return true; - else - return false; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/64bit-tokenizer-safety.patch b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/64bit-tokenizer-safety.patch new file mode 100644 index 000000000..912520c74 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.cpp index 3e22e1f1c..fcb24e4e8 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.cpp @@ -4,9 +4,9 @@ // found in the LICENSE file. // -#include "DiagnosticsBase.h" +#include "compiler/preprocessor/DiagnosticsBase.h" -#include <cassert> +#include "common/debug.h" namespace pp { @@ -16,8 +16,8 @@ Diagnostics::~Diagnostics() } void Diagnostics::report(ID id, - const SourceLocation& loc, - const std::string& text) + const SourceLocation &loc, + const std::string &text) { // TODO(alokp): Keep a count of errors and warnings. print(id, loc, text); @@ -25,14 +25,14 @@ void Diagnostics::report(ID id, Diagnostics::Severity Diagnostics::severity(ID id) { - if ((id > ERROR_BEGIN) && (id < ERROR_END)) - return ERROR; + if ((id > PP_ERROR_BEGIN) && (id < PP_ERROR_END)) + return PP_ERROR; - if ((id > WARNING_BEGIN) && (id < WARNING_END)) - return WARNING; + if ((id > PP_WARNING_BEGIN) && (id < PP_WARNING_END)) + return PP_WARNING; - assert(false); - return ERROR; + UNREACHABLE(); + return PP_ERROR; } std::string Diagnostics::message(ID id) @@ -40,86 +40,100 @@ std::string Diagnostics::message(ID id) switch (id) { // Errors begin. - case INTERNAL_ERROR: - return "internal error"; - case OUT_OF_MEMORY: - return "out of memory"; - case INVALID_CHARACTER: - return "invalid character"; - case INVALID_NUMBER: - return "invalid number"; - case INTEGER_OVERFLOW: - return "integer overflow"; - case FLOAT_OVERFLOW: - return "float overflow"; - case TOKEN_TOO_LONG: - return "token too long"; - case INVALID_EXPRESSION: - return "invalid expression"; - case DIVISION_BY_ZERO: - return "division by zero"; - case EOF_IN_COMMENT: - return "unexpected end of file found in comment"; - case UNEXPECTED_TOKEN: - return "unexpected token"; - case DIRECTIVE_INVALID_NAME: - return "invalid directive name"; - case MACRO_NAME_RESERVED: - return "macro name is reserved"; - case MACRO_REDEFINED: - return "macro redefined"; - case MACRO_PREDEFINED_REDEFINED: - return "predefined macro redefined"; - case MACRO_PREDEFINED_UNDEFINED: - return "predefined macro undefined"; - case MACRO_UNTERMINATED_INVOCATION: - return "unterminated macro invocation"; - case MACRO_TOO_FEW_ARGS: - return "Not enough arguments for macro"; - case MACRO_TOO_MANY_ARGS: - return "Too many arguments for macro"; - case CONDITIONAL_ENDIF_WITHOUT_IF: - return "unexpected #endif found without a matching #if"; - case CONDITIONAL_ELSE_WITHOUT_IF: - return "unexpected #else found without a matching #if"; - case CONDITIONAL_ELSE_AFTER_ELSE: - return "unexpected #else found after another #else"; - case CONDITIONAL_ELIF_WITHOUT_IF: - return "unexpected #elif found without a matching #if"; - case CONDITIONAL_ELIF_AFTER_ELSE: - return "unexpected #elif found after #else"; - case CONDITIONAL_UNTERMINATED: - return "unexpected end of file found in conditional block"; - case INVALID_EXTENSION_NAME: - return "invalid extension name"; - case INVALID_EXTENSION_BEHAVIOR: - return "invalid extension behavior"; - case INVALID_EXTENSION_DIRECTIVE: - return "invalid extension directive"; - case INVALID_VERSION_NUMBER: - return "invalid version number"; - case INVALID_VERSION_DIRECTIVE: - return "invalid version directive"; - case VERSION_NOT_FIRST_STATEMENT: + 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 INVALID_LINE_NUMBER: - return "invalid line number"; - case INVALID_FILE_NUMBER: - return "invalid file number"; - case INVALID_LINE_DIRECTIVE: - return "invalid line directive"; + 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 EOF_IN_DIRECTIVE: - return "unexpected end of file found in directive"; - case CONDITIONAL_UNEXPECTED_TOKEN: - return "unexpected token after conditional expression"; - case UNRECOGNIZED_PRAGMA: - return "unrecognized pragma"; + 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: - assert(false); + UNREACHABLE(); return ""; } } diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.h index 07bc41184..6c3fe9ee9 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DiagnosticsBase.h @@ -4,8 +4,8 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_DIAGNOSTICS_H_ -#define COMPILER_PREPROCESSOR_DIAGNOSTICS_H_ +#ifndef COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_ +#define COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_ #include <string> @@ -21,67 +21,75 @@ class Diagnostics public: enum Severity { - ERROR, - WARNING + PP_ERROR, + PP_WARNING }; enum ID { - ERROR_BEGIN, - INTERNAL_ERROR, - OUT_OF_MEMORY, - INVALID_CHARACTER, - INVALID_NUMBER, - INTEGER_OVERFLOW, - FLOAT_OVERFLOW, - TOKEN_TOO_LONG, - INVALID_EXPRESSION, - DIVISION_BY_ZERO, - EOF_IN_COMMENT, - UNEXPECTED_TOKEN, - DIRECTIVE_INVALID_NAME, - MACRO_NAME_RESERVED, - MACRO_REDEFINED, - MACRO_PREDEFINED_REDEFINED, - MACRO_PREDEFINED_UNDEFINED, - MACRO_UNTERMINATED_INVOCATION, - MACRO_TOO_FEW_ARGS, - MACRO_TOO_MANY_ARGS, - CONDITIONAL_ENDIF_WITHOUT_IF, - CONDITIONAL_ELSE_WITHOUT_IF, - CONDITIONAL_ELSE_AFTER_ELSE, - CONDITIONAL_ELIF_WITHOUT_IF, - CONDITIONAL_ELIF_AFTER_ELSE, - CONDITIONAL_UNTERMINATED, - INVALID_EXTENSION_NAME, - INVALID_EXTENSION_BEHAVIOR, - INVALID_EXTENSION_DIRECTIVE, - INVALID_VERSION_NUMBER, - INVALID_VERSION_DIRECTIVE, - VERSION_NOT_FIRST_STATEMENT, - INVALID_LINE_NUMBER, - INVALID_FILE_NUMBER, - INVALID_LINE_DIRECTIVE, - ERROR_END, + 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, - WARNING_BEGIN, - EOF_IN_DIRECTIVE, - CONDITIONAL_UNEXPECTED_TOKEN, - UNRECOGNIZED_PRAGMA, - WARNING_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); + 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; + const SourceLocation &loc, + const std::string &text) = 0; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_DIAGNOSTICS_H_ + +#endif // COMPILER_PREPROCESSOR_DIAGNOSTICSBASE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.cpp index ef35c6ed5..049dae907 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.cpp @@ -4,7 +4,7 @@ // found in the LICENSE file. // -#include "DirectiveHandlerBase.h" +#include "compiler/preprocessor/DirectiveHandlerBase.h" namespace pp { diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.h index 2aaeec281..cf6789576 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveHandlerBase.h @@ -4,8 +4,8 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_DIRECTIVE_HANDLER_H_ -#define COMPILER_PREPROCESSOR_DIRECTIVE_HANDLER_H_ +#ifndef COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_ +#define COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_ #include <string> @@ -23,21 +23,23 @@ class DirectiveHandler public: virtual ~DirectiveHandler(); - virtual void handleError(const SourceLocation& loc, - const std::string& msg) = 0; + 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) = 0; + 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 handleExtension(const SourceLocation &loc, + const std::string &name, + const std::string &behavior) = 0; - virtual void handleVersion(const SourceLocation& loc, + virtual void handleVersion(const SourceLocation &loc, int version) = 0; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_DIRECTIVE_HANDLER_H_ + +#endif // COMPILER_PREPROCESSOR_DIRECTIVEHANDLERBASE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.cpp index 94dfdf513..643f73bd9 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.cpp @@ -1,21 +1,22 @@ // -// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved. +// 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 "DirectiveParser.h" +#include "compiler/preprocessor/DirectiveParser.h" -#include <cassert> +#include <algorithm> #include <cstdlib> #include <sstream> -#include "DiagnosticsBase.h" -#include "DirectiveHandlerBase.h" -#include "ExpressionParser.h" -#include "MacroExpander.h" -#include "Token.h" -#include "Tokenizer.h" +#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 @@ -35,58 +36,57 @@ enum DirectiveType DIRECTIVE_VERSION, DIRECTIVE_LINE }; -} // namespace -static DirectiveType getDirective(const pp::Token* token) +DirectiveType getDirective(const pp::Token *token) { - static const std::string kDirectiveDefine("define"); - static const std::string kDirectiveUndef("undef"); - static const std::string kDirectiveIf("if"); - static const std::string kDirectiveIfdef("ifdef"); - static const std::string kDirectiveIfndef("ifndef"); - static const std::string kDirectiveElse("else"); - static const std::string kDirectiveElif("elif"); - static const std::string kDirectiveEndif("endif"); - static const std::string kDirectiveError("error"); - static const std::string kDirectivePragma("pragma"); - static const std::string kDirectiveExtension("extension"); - static const std::string kDirectiveVersion("version"); - static const std::string kDirectiveLine("line"); + 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; - else if (token->text == kDirectiveUndef) + if (token->text == kDirectiveUndef) return DIRECTIVE_UNDEF; - else if (token->text == kDirectiveIf) + if (token->text == kDirectiveIf) return DIRECTIVE_IF; - else if (token->text == kDirectiveIfdef) + if (token->text == kDirectiveIfdef) return DIRECTIVE_IFDEF; - else if (token->text == kDirectiveIfndef) + if (token->text == kDirectiveIfndef) return DIRECTIVE_IFNDEF; - else if (token->text == kDirectiveElse) + if (token->text == kDirectiveElse) return DIRECTIVE_ELSE; - else if (token->text == kDirectiveElif) + if (token->text == kDirectiveElif) return DIRECTIVE_ELIF; - else if (token->text == kDirectiveEndif) + if (token->text == kDirectiveEndif) return DIRECTIVE_ENDIF; - else if (token->text == kDirectiveError) + if (token->text == kDirectiveError) return DIRECTIVE_ERROR; - else if (token->text == kDirectivePragma) + if (token->text == kDirectivePragma) return DIRECTIVE_PRAGMA; - else if (token->text == kDirectiveExtension) + if (token->text == kDirectiveExtension) return DIRECTIVE_EXTENSION; - else if (token->text == kDirectiveVersion) + if (token->text == kDirectiveVersion) return DIRECTIVE_VERSION; - else if (token->text == kDirectiveLine) + if (token->text == kDirectiveLine) return DIRECTIVE_LINE; return DIRECTIVE_NONE; } -static bool isConditionalDirective(DirectiveType directive) +bool isConditionalDirective(DirectiveType directive) { switch (directive) { @@ -103,12 +103,12 @@ static bool isConditionalDirective(DirectiveType directive) } // Returns true if the token represents End Of Directive. -static bool isEOD(const pp::Token* token) +bool isEOD(const pp::Token *token) { return (token->type == '\n') || (token->type == pp::Token::LAST); } -static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token) +void skipUntilEOD(pp::Lexer *lexer, pp::Token *token) { while(!isEOD(token)) { @@ -116,45 +116,41 @@ static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token) } } -static bool isMacroNameReserved(const std::string& name) +bool isMacroNameReserved(const std::string &name) { - // Names prefixed with "GL_" are reserved. - if (name.substr(0, 3) == "GL_") - return true; - - // Names containing two consecutive underscores are reserved. - if (name.find("__") != std::string::npos) - return true; + // Names prefixed with "GL_" and the name "defined" are reserved. + return name == "defined" || (name.substr(0, 3) == "GL_"); +} - return false; +bool hasDoubleUnderscores(const std::string &name) +{ + return (name.find("__") != std::string::npos); } -static bool isMacroPredefined(const std::string& name, - const pp::MacroSet& macroSet) +bool isMacroPredefined(const std::string &name, + const pp::MacroSet ¯oSet) { 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) + DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics) + : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics) { } protected: - virtual void lex(Token* token) + void lex(Token *token) override { - static const std::string kDefined("defined"); + const char kDefined[] = "defined"; mLexer->lex(token); if (token->type != Token::IDENTIFIER) @@ -172,21 +168,20 @@ class DefinedParser : public Lexer if (token->type != Token::IDENTIFIER) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, - token->location, token->text); + 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"; + std::string expression = iter != mMacroSet->end() ? "1" : "0"; if (paren) { mLexer->lex(token); if (token->type != ')') { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, - token->location, token->text); + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, + token->text); skipUntilEOD(mLexer, token); return; } @@ -199,24 +194,26 @@ class DefinedParser : public Lexer } private: - Lexer* mLexer; - const MacroSet* mMacroSet; - Diagnostics* mDiagnostics; + Lexer *mLexer; + const MacroSet *mMacroSet; + Diagnostics *mDiagnostics; }; -DirectiveParser::DirectiveParser(Tokenizer* tokenizer, - MacroSet* macroSet, - Diagnostics* diagnostics, - DirectiveHandler* directiveHandler) : - mPastFirstStatement(false), - mTokenizer(tokenizer), - mMacroSet(macroSet), - mDiagnostics(diagnostics), - mDirectiveHandler(directiveHandler) +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) +void DirectiveParser::lex(Token *token) { do { @@ -227,26 +224,31 @@ void DirectiveParser::lex(Token* token) 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::CONDITIONAL_UNTERMINATED, + const ConditionalBlock &block = mConditionalStack.back(); + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED, block.location, block.type); } break; } - } while (skipping() || (token->type == '\n')); + } + while (skipping() || (token->type == '\n')); mPastFirstStatement = true; } -void DirectiveParser::parseDirective(Token* token) +void DirectiveParser::parseDirective(Token *token) { - assert(token->type == Token::PP_HASH); + ASSERT(token->type == Token::PP_HASH); mTokenizer->lex(token); if (isEOD(token)) @@ -268,7 +270,7 @@ void DirectiveParser::parseDirective(Token* token) switch(directive) { case DIRECTIVE_NONE: - mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME, + mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME, token->location, token->text); skipUntilEOD(mTokenizer, token); break; @@ -312,41 +314,51 @@ void DirectiveParser::parseDirective(Token* token) parseLine(token); break; default: - assert(false); - break; + UNREACHABLE(); + break; } skipUntilEOD(mTokenizer, token); if (token->type == Token::LAST) { - mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE, + mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE, token->location, token->text); } } -void DirectiveParser::parseDefine(Token* token) +void DirectiveParser::parseDefine(Token *token) { - assert(getDirective(token) == DIRECTIVE_DEFINE); + ASSERT(getDirective(token) == DIRECTIVE_DEFINE); mTokenizer->lex(token); if (token->type != Token::IDENTIFIER) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); return; } if (isMacroPredefined(token->text, *mMacroSet)) { - mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED, + mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED, token->location, token->text); return; } if (isMacroNameReserved(token->text)) { - mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, + 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; @@ -357,18 +369,28 @@ void DirectiveParser::parseDefine(Token* token) { // Function-like macro. Collect arguments. macro.type = Macro::kTypeFunc; - do { + 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 == ','); + } + while (token->type == ','); if (token->type != ')') { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); return; @@ -396,7 +418,7 @@ void DirectiveParser::parseDefine(Token* token) MacroSet::const_iterator iter = mMacroSet->find(macro.name); if (iter != mMacroSet->end() && !macro.equals(iter->second)) { - mDiagnostics->report(Diagnostics::MACRO_REDEFINED, + mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro.name); return; @@ -404,14 +426,14 @@ void DirectiveParser::parseDefine(Token* token) mMacroSet->insert(std::make_pair(macro.name, macro)); } -void DirectiveParser::parseUndef(Token* token) +void DirectiveParser::parseUndef(Token *token) { - assert(getDirective(token) == DIRECTIVE_UNDEF); + ASSERT(getDirective(token) == DIRECTIVE_UNDEF); mTokenizer->lex(token); if (token->type != Token::IDENTIFIER) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); return; } @@ -421,8 +443,15 @@ void DirectiveParser::parseUndef(Token* token) { if (iter->second.predefined) { - mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED, + 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 { @@ -431,39 +460,45 @@ void DirectiveParser::parseUndef(Token* token) } mTokenizer->lex(token); + if (!isEOD(token)) + { + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, + token->location, token->text); + skipUntilEOD(mTokenizer, token); + } } -void DirectiveParser::parseIf(Token* token) +void DirectiveParser::parseIf(Token *token) { - assert(getDirective(token) == DIRECTIVE_IF); + ASSERT(getDirective(token) == DIRECTIVE_IF); parseConditionalIf(token); } -void DirectiveParser::parseIfdef(Token* token) +void DirectiveParser::parseIfdef(Token *token) { - assert(getDirective(token) == DIRECTIVE_IFDEF); + ASSERT(getDirective(token) == DIRECTIVE_IFDEF); parseConditionalIf(token); } -void DirectiveParser::parseIfndef(Token* token) +void DirectiveParser::parseIfndef(Token *token) { - assert(getDirective(token) == DIRECTIVE_IFNDEF); + ASSERT(getDirective(token) == DIRECTIVE_IFNDEF); parseConditionalIf(token); } -void DirectiveParser::parseElse(Token* token) +void DirectiveParser::parseElse(Token *token) { - assert(getDirective(token) == DIRECTIVE_ELSE); + ASSERT(getDirective(token) == DIRECTIVE_ELSE); if (mConditionalStack.empty()) { - mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF, token->location, token->text); skipUntilEOD(mTokenizer, token); return; } - ConditionalBlock& block = mConditionalStack.back(); + ConditionalBlock &block = mConditionalStack.back(); if (block.skipBlock) { // No diagnostics. Just skip the whole line. @@ -472,7 +507,7 @@ void DirectiveParser::parseElse(Token* token) } if (block.foundElseGroup) { - mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_AFTER_ELSE, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE, token->location, token->text); skipUntilEOD(mTokenizer, token); return; @@ -482,29 +517,29 @@ void DirectiveParser::parseElse(Token* token) block.skipGroup = block.foundValidGroup; block.foundValidGroup = true; - // Warn if there are extra tokens after #else. + // Check if there are extra tokens after #else. mTokenizer->lex(token); if (!isEOD(token)) { - mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); } } -void DirectiveParser::parseElif(Token* token) +void DirectiveParser::parseElif(Token *token) { - assert(getDirective(token) == DIRECTIVE_ELIF); + ASSERT(getDirective(token) == DIRECTIVE_ELIF); if (mConditionalStack.empty()) { - mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF, token->location, token->text); skipUntilEOD(mTokenizer, token); return; } - ConditionalBlock& block = mConditionalStack.back(); + ConditionalBlock &block = mConditionalStack.back(); if (block.skipBlock) { // No diagnostics. Just skip the whole line. @@ -513,7 +548,7 @@ void DirectiveParser::parseElif(Token* token) } if (block.foundElseGroup) { - mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_AFTER_ELSE, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE, token->location, token->text); skipUntilEOD(mTokenizer, token); return; @@ -532,13 +567,13 @@ void DirectiveParser::parseElif(Token* token) block.foundValidGroup = expression != 0; } -void DirectiveParser::parseEndif(Token* token) +void DirectiveParser::parseEndif(Token *token) { - assert(getDirective(token) == DIRECTIVE_ENDIF); + ASSERT(getDirective(token) == DIRECTIVE_ENDIF); if (mConditionalStack.empty()) { - mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF, token->location, token->text); skipUntilEOD(mTokenizer, token); return; @@ -546,19 +581,19 @@ void DirectiveParser::parseEndif(Token* token) mConditionalStack.pop_back(); - // Warn if there are tokens after #endif. + // Check if there are tokens after #endif. mTokenizer->lex(token); if (!isEOD(token)) { - mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); } } -void DirectiveParser::parseError(Token* token) +void DirectiveParser::parseError(Token *token) { - assert(getDirective(token) == DIRECTIVE_ERROR); + ASSERT(getDirective(token) == DIRECTIVE_ERROR); std::ostringstream stream; mTokenizer->lex(token); @@ -571,9 +606,9 @@ void DirectiveParser::parseError(Token* token) } // Parses pragma of form: #pragma name[(value)]. -void DirectiveParser::parsePragma(Token* token) +void DirectiveParser::parsePragma(Token *token) { - assert(getDirective(token) == DIRECTIVE_PRAGMA); + ASSERT(getDirective(token) == DIRECTIVE_PRAGMA); enum State { @@ -588,6 +623,11 @@ void DirectiveParser::parsePragma(Token* token) 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++) @@ -618,18 +658,18 @@ void DirectiveParser::parsePragma(Token* token) (state == RIGHT_PAREN + 1)); // With value. if (!valid) { - mDiagnostics->report(Diagnostics::UNRECOGNIZED_PRAGMA, + 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); + mDirectiveHandler->handlePragma(token->location, name, value, stdgl); } } -void DirectiveParser::parseExtension(Token* token) +void DirectiveParser::parseExtension(Token *token) { - assert(getDirective(token) == DIRECTIVE_EXTENSION); + ASSERT(getDirective(token) == DIRECTIVE_EXTENSION); enum State { @@ -650,7 +690,7 @@ void DirectiveParser::parseExtension(Token* token) case EXT_NAME: if (valid && (token->type != Token::IDENTIFIER)) { - mDiagnostics->report(Diagnostics::INVALID_EXTENSION_NAME, + mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME, token->location, token->text); valid = false; } @@ -659,7 +699,7 @@ void DirectiveParser::parseExtension(Token* token) case COLON: if (valid && (token->type != ':')) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); valid = false; } @@ -667,7 +707,7 @@ void DirectiveParser::parseExtension(Token* token) case EXT_BEHAVIOR: if (valid && (token->type != Token::IDENTIFIER)) { - mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR, + mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR, token->location, token->text); valid = false; } @@ -676,7 +716,7 @@ void DirectiveParser::parseExtension(Token* token) default: if (valid) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); valid = false; } @@ -686,21 +726,35 @@ void DirectiveParser::parseExtension(Token* token) } if (valid && (state != EXT_BEHAVIOR + 1)) { - mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE, + 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) +void DirectiveParser::parseVersion(Token *token) { - assert(getDirective(token) == DIRECTIVE_VERSION); + ASSERT(getDirective(token) == DIRECTIVE_VERSION); if (mPastFirstStatement) { - mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT, + mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, token->location, token->text); skipUntilEOD(mTokenizer, token); return; @@ -708,7 +762,9 @@ void DirectiveParser::parseVersion(Token* token) enum State { - VERSION_NUMBER + VERSION_NUMBER, + VERSION_PROFILE, + VERSION_ENDLINE }; bool valid = true; @@ -716,127 +772,141 @@ void DirectiveParser::parseVersion(Token* token) int state = VERSION_NUMBER; mTokenizer->lex(token); - while ((token->type != '\n') && (token->type != Token::LAST)) + while (valid && (token->type != '\n') && (token->type != Token::LAST)) { - switch (state++) + switch (state) { case VERSION_NUMBER: - if (valid && (token->type != Token::CONST_INT)) + if (token->type != Token::CONST_INT) { - mDiagnostics->report(Diagnostics::INVALID_VERSION_NUMBER, + mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER, token->location, token->text); valid = false; } if (valid && !token->iValue(&version)) { - mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, + mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, token->location, token->text); valid = false; } - break; - default: if (valid) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + 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_NUMBER + 1)) + + if (valid && (state != VERSION_ENDLINE)) { - mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE, + 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) +void DirectiveParser::parseLine(Token *token) { - assert(getDirective(token) == DIRECTIVE_LINE); - - enum State - { - LINE_NUMBER, - FILE_NUMBER - }; + ASSERT(getDirective(token) == DIRECTIVE_LINE); bool valid = true; + bool parsedFileNumber = false; int line = 0, file = 0; - int state = LINE_NUMBER; MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); + + // Lex the first token after "#line" so we can check it for EOD. macroExpander.lex(token); - while ((token->type != '\n') && (token->type != Token::LAST)) + + if (isEOD(token)) { - switch (state++) + mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text); + valid = false; + } + else + { + ExpressionParser expressionParser(¯oExpander, 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)) { - case LINE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&line)) - { - mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - case FILE_NUMBER: - if (valid && (token->type != Token::CONST_INT)) - { - mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER, - token->location, token->text); - valid = false; - } - if (valid && !token->iValue(&file)) - { - mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, - token->location, token->text); - valid = false; - } - break; - default: if (valid) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); valid = false; } - break; + skipUntilEOD(mTokenizer, token); } - macroExpander.lex(token); } - if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) - { - mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE, - token->location, token->text); - valid = false; - } if (valid) { mTokenizer->setLineNumber(line); - if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file); + if (parsedFileNumber) + mTokenizer->setFileNumber(file); } } bool DirectiveParser::skipping() const { - if (mConditionalStack.empty()) return false; + if (mConditionalStack.empty()) + return false; const ConditionalBlock& block = mConditionalStack.back(); return block.skipBlock || block.skipGroup; } -void DirectiveParser::parseConditionalIf(Token* token) +void DirectiveParser::parseConditionalIf(Token *token) { ConditionalBlock block; block.type = token->text; @@ -868,8 +938,8 @@ void DirectiveParser::parseConditionalIf(Token* token) expression = parseExpressionIfdef(token) == 0 ? 1 : 0; break; default: - assert(false); - break; + UNREACHABLE(); + break; } block.skipGroup = expression == 0; block.foundValidGroup = expression != 0; @@ -877,23 +947,26 @@ void DirectiveParser::parseConditionalIf(Token* token) mConditionalStack.push_back(block); } -int DirectiveParser::parseExpressionIf(Token* token) +int DirectiveParser::parseExpressionIf(Token *token) { - assert((getDirective(token) == DIRECTIVE_IF) || - (getDirective(token) == DIRECTIVE_ELIF)); + ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF)); DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); ExpressionParser expressionParser(¯oExpander, mDiagnostics); int expression = 0; - macroExpander.lex(token); - expressionParser.parse(token, &expression); + ExpressionParser::ErrorSettings errorSettings; + errorSettings.integerLiteralsMustFit32BitSignedRange = false; + errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN; + + bool valid = true; + expressionParser.parse(token, &expression, false, errorSettings, &valid); - // Warn if there are tokens after #if expression. + // Check if there are tokens after #if expression. if (!isEOD(token)) { - mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); } @@ -901,15 +974,14 @@ int DirectiveParser::parseExpressionIf(Token* token) return expression; } -int DirectiveParser::parseExpressionIfdef(Token* token) +int DirectiveParser::parseExpressionIfdef(Token *token) { - assert((getDirective(token) == DIRECTIVE_IFDEF) || - (getDirective(token) == DIRECTIVE_IFNDEF)); + ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF)); mTokenizer->lex(token); if (token->type != Token::IDENTIFIER) { - mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); return 0; @@ -918,11 +990,11 @@ int DirectiveParser::parseExpressionIfdef(Token* token) MacroSet::const_iterator iter = mMacroSet->find(token->text); int expression = iter != mMacroSet->end() ? 1 : 0; - // Warn if there are tokens after #ifdef expression. + // Check if there are tokens after #ifdef expression. mTokenizer->lex(token); if (!isEOD(token)) { - mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, + mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); } diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.h index 8a7f0072b..f0e889c8a 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/DirectiveParser.h @@ -4,13 +4,12 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_DIRECTIVE_PARSER_H_ -#define COMPILER_PREPROCESSOR_DIRECTIVE_PARSER_H_ +#ifndef COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_ +#define COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_ -#include "Lexer.h" -#include "Macro.h" -#include "pp_utils.h" -#include "SourceLocation.h" +#include "compiler/preprocessor/Lexer.h" +#include "compiler/preprocessor/Macro.h" +#include "compiler/preprocessor/SourceLocation.h" namespace pp { @@ -22,35 +21,34 @@ class Tokenizer; class DirectiveParser : public Lexer { public: - DirectiveParser(Tokenizer* tokenizer, - MacroSet* macroSet, - Diagnostics* diagnostics, - DirectiveHandler* directiveHandler); + DirectiveParser(Tokenizer *tokenizer, + MacroSet *macroSet, + Diagnostics *diagnostics, + DirectiveHandler *directiveHandler); - virtual void lex(Token* token); + void lex(Token *token) override; private: - PP_DISALLOW_COPY_AND_ASSIGN(DirectiveParser); - 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); + 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); + void parseConditionalIf(Token *token); + int parseExpressionIf(Token *token); + int parseExpressionIfdef(Token *token); struct ConditionalBlock { @@ -61,22 +59,25 @@ class DirectiveParser : public Lexer bool foundValidGroup; bool foundElseGroup; - ConditionalBlock() : - skipBlock(false), - skipGroup(false), - foundValidGroup(false), - foundElseGroup(false) + 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; + Tokenizer *mTokenizer; + MacroSet *mMacroSet; + Diagnostics *mDiagnostics; + DirectiveHandler *mDirectiveHandler; + int mShaderVersion; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_DIRECTIVE_PARSER_H_ +#endif // COMPILER_PREPROCESSOR_DIRECTIVEPARSER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.cpp index 1fae00945..a8df2351e 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.cpp @@ -1,10 +1,10 @@ -/* Apple Note: For the avoidance of doubt, Apple elects to distribute this file under the terms of the BSD license. */ +/* A Bison parser, made by GNU Bison 3.0.4. */ -/* A Bison parser, made by GNU Bison 3.0.1. */ +/* Apple Note: For the avoidance of doubt, Apple elects to distribute this file under the terms of the BSD license. */ /* Bison implementation for Yacc-like parsers in C - Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc. + 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 @@ -46,7 +46,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "3.0.1" +#define YYBISON_VERSION "3.0.4" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -88,7 +88,7 @@ #pragma GCC diagnostic ignored "-Wuninitialized" #endif #elif defined(_MSC_VER) -#pragma warning(disable: 4065 4701) +#pragma warning(disable: 4065 4244 4701 4702) #endif #include "ExpressionParser.h" @@ -101,17 +101,16 @@ #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; -#if defined(_MSC_VER) -typedef __int64 YYSTYPE; -#else -#include <stdint.h> -typedef intmax_t YYSTYPE; -#endif // _MSC_VER #define YYENABLE_NLS 0 #define YYLTYPE_IS_TRIVIAL 1 #define YYSTYPE_IS_TRIVIAL 1 @@ -124,6 +123,17 @@ struct Context 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 @@ -164,15 +174,16 @@ extern int ppdebug; enum yytokentype { TOK_CONST_INT = 258, - TOK_OP_OR = 259, - TOK_OP_AND = 260, - TOK_OP_EQ = 261, - TOK_OP_NE = 262, - TOK_OP_LE = 263, - TOK_OP_GE = 264, - TOK_OP_LEFT = 265, - TOK_OP_RIGHT = 266, - TOK_UNARY = 267 + 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 @@ -431,23 +442,23 @@ union yyalloc #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ -#define YYFINAL 14 +#define YYFINAL 15 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 175 +#define YYLAST 176 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 27 +#define YYNTOKENS 28 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 3 +#define YYNNTS 5 /* YYNRULES -- Number of rules. */ -#define YYNRULES 26 +#define YYNRULES 29 /* YYNSTATES -- Number of states. */ -#define YYNSTATES 52 +#define YYNSTATES 55 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 267 +#define YYMAXUTOK 268 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -459,16 +470,16 @@ 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, 23, 2, 2, 2, 21, 8, 2, - 25, 26, 19, 17, 2, 18, 2, 20, 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, - 11, 2, 12, 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, 7, 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, 6, 2, 24, 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, @@ -482,16 +493,16 @@ static const yytype_uint8 yytranslate[] = 2, 2, 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, 9, 10, 13, 14, 15, 16, 22 + 5, 6, 10, 11, 14, 15, 16, 17, 23 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ -static const yytype_uint8 yyrline[] = +static const yytype_uint16 yyrline[] = { - 0, 97, 97, 104, 105, 108, 111, 114, 117, 120, - 123, 126, 129, 132, 135, 138, 141, 144, 147, 150, - 163, 176, 179, 182, 185, 188, 191 + 0, 108, 108, 115, 116, 127, 127, 148, 148, 169, + 172, 175, 178, 181, 184, 187, 190, 193, 196, 221, + 246, 249, 252, 272, 299, 302, 305, 308, 320, 323 }; #endif @@ -500,11 +511,11 @@ static const yytype_uint8 yyrline[] = First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { - "$end", "error", "$undefined", "TOK_CONST_INT", "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", YY_NULLPTR + "$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 @@ -513,16 +524,16 @@ static const char *const yytname[] = (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { - 0, 256, 257, 258, 259, 260, 124, 94, 38, 261, - 262, 60, 62, 263, 264, 265, 266, 43, 45, 42, - 47, 37, 267, 33, 126, 40, 41 + 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 -11 +#define YYPACT_NINF -12 #define yypact_value_is_default(Yystate) \ - (!!((Yystate) == (-11))) + (!!((Yystate) == (-12))) #define YYTABLE_NINF -1 @@ -533,12 +544,12 @@ static const yytype_uint16 yytoknum[] = STATE-NUM. */ static const yytype_int16 yypact[] = { - 46, -11, 46, 46, 46, 46, 46, 12, 68, -11, - -11, -11, -11, 27, -11, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, -11, 85, 101, 116, 130, 143, 154, - 154, -10, -10, -10, -10, 37, 37, 31, 31, -11, - -11, -11 + 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. @@ -546,24 +557,24 @@ static const yytype_int16 yypact[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 0, 3, 0, 0, 0, 0, 0, 0, 2, 25, - 24, 22, 23, 0, 1, 0, 0, 0, 0, 0, + 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, 26, 4, 5, 6, 7, 8, 10, - 9, 14, 13, 12, 11, 16, 15, 18, 17, 21, - 20, 19 + 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[] = { - -11, -11, -2 + -12, -12, -3, -12, -12 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int8 yydefgoto[] = { - -1, 7, 8 + -1, 8, 9, 35, 36 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If @@ -571,74 +582,74 @@ static const yytype_int8 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { - 9, 10, 11, 12, 13, 26, 27, 28, 29, 30, - 31, 32, 14, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 1, - 30, 31, 32, 33, 28, 29, 30, 31, 32, 0, - 0, 0, 0, 2, 3, 0, 0, 0, 0, 4, - 5, 6, 15, 16, 17, 18, 19, 20, 21, 22, + 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, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, - 26, 27, 28, 29, 30, 31, 32, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 20, 21, 22, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 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[] = { - 2, 3, 4, 5, 6, 15, 16, 17, 18, 19, - 20, 21, 0, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 16, 17, 18, 19, 20, 21, 3, - 19, 20, 21, 26, 17, 18, 19, 20, 21, -1, - -1, -1, -1, 17, 18, -1, -1, -1, -1, 23, - 24, 25, 4, 5, 6, 7, 8, 9, 10, 11, + 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, - 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 6, 7, 8, - 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 9, 10, 11, 12, 13, 14, 15, 16, - 17, 18, 19, 20, 21, 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, 17, 18, 23, 24, 25, 28, 29, 29, - 29, 29, 29, 29, 0, 4, 5, 6, 7, 8, + 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, 26, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29 + 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, 27, 28, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29 + 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, 3, 3, 3, 3, 3, 3, + 0, 2, 1, 1, 1, 0, 4, 0, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 2, 2, 2, 2, 3 + 3, 3, 3, 3, 3, 2, 2, 2, 2, 3 }; @@ -1332,7 +1343,15 @@ yyreduce: case 4: { - (yyval) = (yyvsp[-2]) || (yyvsp[0]); + 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; @@ -1340,7 +1359,15 @@ yyreduce: case 5: { - (yyval) = (yyvsp[-2]) && (yyvsp[0]); + 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; @@ -1348,7 +1375,15 @@ yyreduce: case 6: { - (yyval) = (yyvsp[-2]) | (yyvsp[0]); + if ((yyvsp[-3]) != 0) + { + context->endIgnoreErrors(); + (yyval) = static_cast<YYSTYPE>(1); + } + else + { + (yyval) = (yyvsp[-3]) || (yyvsp[0]); + } } break; @@ -1356,7 +1391,15 @@ yyreduce: case 7: { - (yyval) = (yyvsp[-2]) ^ (yyvsp[0]); + 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; @@ -1364,7 +1407,15 @@ yyreduce: case 8: { - (yyval) = (yyvsp[-2]) & (yyvsp[0]); + if ((yyvsp[-3]) == 0) + { + context->endIgnoreErrors(); + (yyval) = static_cast<YYSTYPE>(0); + } + else + { + (yyval) = (yyvsp[-3]) && (yyvsp[0]); + } } break; @@ -1372,7 +1423,7 @@ yyreduce: case 9: { - (yyval) = (yyvsp[-2]) != (yyvsp[0]); + (yyval) = (yyvsp[-2]) | (yyvsp[0]); } break; @@ -1380,7 +1431,7 @@ yyreduce: case 10: { - (yyval) = (yyvsp[-2]) == (yyvsp[0]); + (yyval) = (yyvsp[-2]) ^ (yyvsp[0]); } break; @@ -1388,7 +1439,7 @@ yyreduce: case 11: { - (yyval) = (yyvsp[-2]) >= (yyvsp[0]); + (yyval) = (yyvsp[-2]) & (yyvsp[0]); } break; @@ -1396,7 +1447,7 @@ yyreduce: case 12: { - (yyval) = (yyvsp[-2]) <= (yyvsp[0]); + (yyval) = (yyvsp[-2]) != (yyvsp[0]); } break; @@ -1404,7 +1455,7 @@ yyreduce: case 13: { - (yyval) = (yyvsp[-2]) > (yyvsp[0]); + (yyval) = (yyvsp[-2]) == (yyvsp[0]); } break; @@ -1412,7 +1463,7 @@ yyreduce: case 14: { - (yyval) = (yyvsp[-2]) < (yyvsp[0]); + (yyval) = (yyvsp[-2]) >= (yyvsp[0]); } break; @@ -1420,7 +1471,7 @@ yyreduce: case 15: { - (yyval) = (yyvsp[-2]) >> (yyvsp[0]); + (yyval) = (yyvsp[-2]) <= (yyvsp[0]); } break; @@ -1428,7 +1479,7 @@ yyreduce: case 16: { - (yyval) = (yyvsp[-2]) << (yyvsp[0]); + (yyval) = (yyvsp[-2]) > (yyvsp[0]); } break; @@ -1436,7 +1487,7 @@ yyreduce: case 17: { - (yyval) = (yyvsp[-2]) - (yyvsp[0]); + (yyval) = (yyvsp[-2]) < (yyvsp[0]); } break; @@ -1444,7 +1495,29 @@ yyreduce: case 18: { - (yyval) = (yyvsp[-2]) + (yyvsp[0]); + 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; @@ -1452,16 +1525,28 @@ yyreduce: case 19: { - if ((yyvsp[0]) == 0) { - std::ostringstream stream; - stream << (yyvsp[-2]) << " % " << (yyvsp[0]); - std::string text = stream.str(); - context->diagnostics->report(pp::Diagnostics::DIVISION_BY_ZERO, - context->token->location, - text.c_str()); - YYABORT; - } else { - (yyval) = (yyvsp[-2]) % (yyvsp[0]); + 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]); } } @@ -1470,17 +1555,7 @@ yyreduce: case 20: { - if ((yyvsp[0]) == 0) { - std::ostringstream stream; - stream << (yyvsp[-2]) << " / " << (yyvsp[0]); - std::string text = stream.str(); - context->diagnostics->report(pp::Diagnostics::DIVISION_BY_ZERO, - context->token->location, - text.c_str()); - YYABORT; - } else { - (yyval) = (yyvsp[-2]) / (yyvsp[0]); - } + (yyval) = gl::WrappingDiff<YYSTYPE>((yyvsp[-2]), (yyvsp[0])); } break; @@ -1488,7 +1563,7 @@ yyreduce: case 21: { - (yyval) = (yyvsp[-2]) * (yyvsp[0]); + (yyval) = gl::WrappingSum<YYSTYPE>((yyvsp[-2]), (yyvsp[0])); } break; @@ -1496,7 +1571,24 @@ yyreduce: case 22: { - (yyval) = ! (yyvsp[0]); + 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 + { + (yyval) = (yyvsp[-2]) % (yyvsp[0]); + } } break; @@ -1504,7 +1596,31 @@ yyreduce: case 23: { - (yyval) = ~ (yyvsp[0]); + 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; @@ -1512,7 +1628,7 @@ yyreduce: case 24: { - (yyval) = - (yyvsp[0]); + (yyval) = gl::WrappingMul((yyvsp[-2]), (yyvsp[0])); } break; @@ -1520,7 +1636,7 @@ yyreduce: case 25: { - (yyval) = + (yyvsp[0]); + (yyval) = ! (yyvsp[0]); } break; @@ -1528,6 +1644,39 @@ yyreduce: 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]); } @@ -1765,79 +1914,115 @@ yyreturn: -int yylex(YYSTYPE* lvalp, Context* context) +int yylex(YYSTYPE *lvalp, Context *context) { + pp::Token *token = context->token; + if (!context->parsePresetToken) + { + context->lexer->lex(token); + } + context->parsePresetToken = false; + int type = 0; - pp::Token* token = context->token; switch (token->type) { - case pp::Token::CONST_INT: - { + case pp::Token::CONST_INT: { unsigned int val = 0; - if (!token->uValue(&val)) + int testVal = 0; + if (!token->uValue(&val) || (!token->iValue(&testVal) && + context->errorSettings.integerLiteralsMustFit32BitSignedRange)) { - context->diagnostics->report(pp::Diagnostics::INTEGER_OVERFLOW, + 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::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 '|': type = '|'; break; - case '^': type = '^'; break; - case '&': type = '&'; break; - case '>': type = '>'; break; - case '<': type = '<'; break; - case '-': type = '-'; break; - case '+': type = '+'; break; - case '%': type = '%'; break; - case '/': type = '/'; break; - case '*': type = '*'; break; - case '!': type = '!'; break; - case '~': type = '~'; break; - case '(': type = '('; break; - case ')': type = ')'; 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; + default: + break; } - // Advance to the next token if the current one is valid. - if (type != 0) context->lexer->lex(token); - return type; } -void yyerror(Context* context, const char* reason) +void yyerror(Context *context, const char *reason) { - context->diagnostics->report(pp::Diagnostics::INVALID_EXPRESSION, + context->diagnostics->report(pp::Diagnostics::PP_INVALID_EXPRESSION, context->token->location, reason); } namespace pp { -ExpressionParser::ExpressionParser(Lexer* lexer, Diagnostics* diagnostics) : - mLexer(lexer), - mDiagnostics(diagnostics) +ExpressionParser::ExpressionParser(Lexer *lexer, Diagnostics *diagnostics) + : mLexer(lexer), + mDiagnostics(diagnostics) { } -bool ExpressionParser::parse(Token* token, int* result) +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) { @@ -1846,12 +2031,12 @@ bool ExpressionParser::parse(Token* token, int* result) break; case 2: - mDiagnostics->report(Diagnostics::OUT_OF_MEMORY, token->location, ""); + mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token->location, ""); break; default: assert(false); - mDiagnostics->report(Diagnostics::INTERNAL_ERROR, token->location, ""); + mDiagnostics->report(Diagnostics::PP_INTERNAL_ERROR, token->location, ""); break; } diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.h index 092d05941..0f2901b87 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.h @@ -4,31 +4,40 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_EXPRESSION_PARSER_H_ -#define COMPILER_PREPROCESSOR_EXPRESSION_PARSER_H_ +#ifndef COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ +#define COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ -#include "pp_utils.h" +#include "common/angleutils.h" +#include "compiler/preprocessor/DiagnosticsBase.h" namespace pp { -class Diagnostics; class Lexer; struct Token; -class ExpressionParser +class ExpressionParser : angle::NonCopyable { public: - ExpressionParser(Lexer* lexer, Diagnostics* diagnostics); + struct ErrorSettings + { + Diagnostics::ID unexpectedIdentifier; + bool integerLiteralsMustFit32BitSignedRange; + }; - bool parse(Token* token, int* result); + ExpressionParser(Lexer *lexer, Diagnostics *diagnostics); - private: - PP_DISALLOW_COPY_AND_ASSIGN(ExpressionParser); + bool parse(Token *token, + int *result, + bool parsePresetToken, + const ErrorSettings &errorSettings, + bool *valid); - Lexer* mLexer; - Diagnostics* mDiagnostics; + private: + Lexer *mLexer; + Diagnostics *mDiagnostics; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_EXPRESSION_PARSER_H_ + +#endif // COMPILER_PREPROCESSOR_EXPRESSIONPARSER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.y b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.y new file mode 100644 index 000000000..a98638a8d --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/ExpressionParser.y @@ -0,0 +1,459 @@ +/* +// +// 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 + { + $$ = $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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.cpp index b4d970a97..8bce56ff2 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.cpp @@ -4,12 +4,13 @@ // found in the LICENSE file. // -#include "Input.h" +#include "compiler/preprocessor/Input.h" #include <algorithm> -#include <cassert> #include <cstring> +#include "common/debug.h" + namespace pp { @@ -17,7 +18,7 @@ Input::Input() : mCount(0), mString(0) { } -Input::Input(size_t count, const char* const string[], const int length[]) : +Input::Input(size_t count, const char *const string[], const int length[]) : mCount(count), mString(string) { @@ -29,13 +30,75 @@ Input::Input(size_t count, const char* const string[], const int length[]) : } } -size_t Input::read(char* buf, size_t maxSize) +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; - while ((nRead < maxSize) && (mReadLoc.sIndex < mCount)) + // 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; diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.h index 14b7597cb..ecbb04962 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Input.h @@ -7,7 +7,7 @@ #ifndef COMPILER_PREPROCESSOR_INPUT_H_ #define COMPILER_PREPROCESSOR_INPUT_H_ -#include <stddef.h> +#include <cstddef> #include <vector> namespace pp @@ -18,32 +18,49 @@ class Input { public: Input(); - Input(size_t count, const char* const string[], const int length[]); + 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 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); + 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) { } + Location() + : sIndex(0), + cIndex(0) + { + } }; - const Location& readLoc() const { return mReadLoc; } + 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; + const char * const *mString; std::vector<size_t> mLength; Location mReadLoc; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_INPUT_H_ +#endif // COMPILER_PREPROCESSOR_INPUT_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.cpp index 7c663ee76..89cb3cf44 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.cpp @@ -4,7 +4,7 @@ // found in the LICENSE file. // -#include "Lexer.h" +#include "compiler/preprocessor/Lexer.h" namespace pp { diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.h index eb85cea87..775bc0a20 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Lexer.h @@ -7,19 +7,21 @@ #ifndef COMPILER_PREPROCESSOR_LEXER_H_ #define COMPILER_PREPROCESSOR_LEXER_H_ +#include "common/angleutils.h" + namespace pp { struct Token; -class Lexer +class Lexer : angle::NonCopyable { public: virtual ~Lexer(); - virtual void lex(Token* token) = 0; + virtual void lex(Token *token) = 0; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_LEXER_H_ +#endif // COMPILER_PREPROCESSOR_LEXER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.cpp index b2e3088e3..f5c94399d 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.cpp @@ -4,14 +4,15 @@ // found in the LICENSE file. // -#include "Macro.h" +#include "compiler/preprocessor/Macro.h" -#include "Token.h" +#include "common/angleutils.h" +#include "compiler/preprocessor/Token.h" namespace pp { -bool Macro::equals(const Macro& other) const +bool Macro::equals(const Macro &other) const { return (type == other.type) && (name == other.name) && @@ -19,5 +20,20 @@ bool Macro::equals(const Macro& other) const (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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.h index 7ec014911..557df163c 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Macro.h @@ -26,11 +26,12 @@ struct Macro typedef std::vector<std::string> Parameters; typedef std::vector<Token> Replacements; - Macro() : predefined(false), disabled(false), type(kTypeObj) { } - bool equals(const Macro& other) const; + 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; @@ -40,5 +41,8 @@ struct Macro 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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.cpp index 1116c516f..f38b39635 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.cpp @@ -4,29 +4,34 @@ // found in the LICENSE file. // -#include "MacroExpander.h" +#include "compiler/preprocessor/MacroExpander.h" #include <algorithm> -#include <sstream> -#include "DiagnosticsBase.h" -#include "Token.h" +#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) + TokenLexer(TokenVector *tokens) { tokens->swap(mTokens); mIter = mTokens.begin(); } - virtual void lex(Token* token) + void lex(Token *token) override { if (mIter == mTokens.end()) { @@ -40,30 +45,26 @@ class TokenLexer : public Lexer } private: - PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer); - TokenVector mTokens; TokenVector::const_iterator mIter; }; -MacroExpander::MacroExpander(Lexer* lexer, - MacroSet* macroSet, - Diagnostics* diagnostics) : - mLexer(lexer), - mMacroSet(macroSet), - mDiagnostics(diagnostics) +} // anonymous namespace + +MacroExpander::MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics) + : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics), mTotalTokensInContexts(0) { } MacroExpander::~MacroExpander() { - for (std::size_t i = 0; i < mContextStack.size(); ++i) + for (MacroContext *context : mContextStack) { - delete mContextStack[i]; + delete context; } } -void MacroExpander::lex(Token* token) +void MacroExpander::lex(Token *token) { while (true) { @@ -97,7 +98,7 @@ void MacroExpander::lex(Token* token) } } -void MacroExpander::getToken(Token* token) +void MacroExpander::getToken(Token *token) { if (mReserveToken.get()) { @@ -118,21 +119,22 @@ void MacroExpander::getToken(Token* token) } else { + ASSERT(mTotalTokensInContexts == 0); mLexer->lex(token); } } -void MacroExpander::ungetToken(const Token& token) +void MacroExpander::ungetToken(const Token &token) { if (!mContextStack.empty()) { - MacroContext* context = mContextStack.back(); + MacroContext *context = mContextStack.back(); context->unget(); - assert(context->replacements[context->index] == token); + ASSERT(context->replacements[context->index] == token); } else { - assert(!mReserveToken.get()); + ASSERT(!mReserveToken.get()); mReserveToken.reset(new Token(token)); } } @@ -148,12 +150,14 @@ bool MacroExpander::isNextTokenLeftParen() return lparen; } -bool MacroExpander::pushMacro(const Macro& macro, const Token& identifier) +bool MacroExpander::pushMacro(const Macro ¯o, const Token &identifier) { - assert(!macro.disabled); - assert(!identifier.expansionDisabled()); - assert(identifier.type == Token::IDENTIFIER); - assert(identifier.text == macro.name); + ASSERT(!macro.disabled); + ASSERT(!identifier.expansionDisabled()); + ASSERT(identifier.type == Token::IDENTIFIER); + ASSERT(identifier.text == macro.name); + + macro.expansionCount++; std::vector<Token> replacements; if (!expandMacro(macro, identifier, &replacements)) @@ -162,31 +166,41 @@ bool MacroExpander::pushMacro(const Macro& macro, const Token& identifier) // Macro is disabled for expansion until it is popped off the stack. macro.disabled = true; - MacroContext* context = new MacroContext; + MacroContext *context = new MacroContext; context->macro = ¯o; context->replacements.swap(replacements); mContextStack.push_back(context); + mTotalTokensInContexts += context->replacements.size(); return true; } void MacroExpander::popMacro() { - assert(!mContextStack.empty()); + ASSERT(!mContextStack.empty()); - MacroContext* context = mContextStack.back(); + MacroContext *context = mContextStack.back(); mContextStack.pop_back(); - assert(context->empty()); - assert(context->macro->disabled); + ASSERT(context->empty()); + ASSERT(context->macro->disabled); + ASSERT(context->macro->expansionCount > 0); 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) +bool MacroExpander::expandMacro(const Macro ¯o, + 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(), @@ -194,31 +208,27 @@ bool MacroExpander::expandMacro(const Macro& macro, if (macro.predefined) { - static const std::string kLine = "__LINE__"; - static const std::string kFile = "__FILE__"; + const char kLine[] = "__LINE__"; + const char kFile[] = "__FILE__"; - assert(replacements->size() == 1); + ASSERT(replacements->size() == 1); Token& repl = replacements->front(); if (macro.name == kLine) { - std::ostringstream stream; - stream << identifier.location.line; - repl.text = stream.str(); + repl.text = ToString(identifier.location.line); } else if (macro.name == kFile) { - std::ostringstream stream; - stream << identifier.location.file; - repl.text = stream.str(); + repl.text = ToString(identifier.location.file); } } } else { - assert(macro.type == Macro::kTypeFunc); + ASSERT(macro.type == Macro::kTypeFunc); std::vector<MacroArg> args; args.reserve(macro.parameters.size()); - if (!collectMacroArgs(macro, identifier, &args)) + if (!collectMacroArgs(macro, identifier, &args, &replacementLocation)) return false; replaceMacroParams(macro, args, replacements); @@ -234,27 +244,30 @@ bool MacroExpander::expandMacro(const Macro& macro, repl.setAtStartOfLine(identifier.atStartOfLine()); repl.setHasLeadingSpace(identifier.hasLeadingSpace()); } - repl.location = identifier.location; + repl.location = replacementLocation; } return true; } -bool MacroExpander::collectMacroArgs(const Macro& macro, - const Token& identifier, - std::vector<MacroArg>* args) +bool MacroExpander::collectMacroArgs(const Macro ¯o, + const Token &identifier, + std::vector<MacroArg> *args, + SourceLocation *closingParenthesisLocation) { Token token; getToken(&token); - assert(token.type == '('); + ASSERT(token.type == '('); args->push_back(MacroArg()); - for (int openParens = 1; openParens != 0; ) + + int openParens = 1; + while (openParens != 0) { getToken(&token); if (token.type == Token::LAST) { - mDiagnostics->report(Diagnostics::MACRO_UNTERMINATED_INVOCATION, + mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION, identifier.location, identifier.text); // Do not lose EOF token. ungetToken(token); @@ -271,12 +284,14 @@ bool MacroExpander::collectMacroArgs(const Macro& macro, 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()); + if (openParens == 1) + args->push_back(MacroArg()); isArg = openParens != 1; break; default: @@ -285,14 +300,15 @@ bool MacroExpander::collectMacroArgs(const Macro& macro, } if (isArg) { - MacroArg& arg = args->back(); + MacroArg &arg = args->back(); // Initial whitespace is not part of the argument. - if (arg.empty()) token.setHasLeadingSpace(false); + if (arg.empty()) + token.setHasLeadingSpace(false); arg.push_back(token); } } - const Macro::Parameters& params = macro.parameters; + const Macro::Parameters ¶ms = macro.parameters; // If there is only one empty argument, it is equivalent to no argument. if (params.empty() && (args->size() == 1) && args->front().empty()) { @@ -302,8 +318,8 @@ bool MacroExpander::collectMacroArgs(const Macro& macro, if (args->size() != params.size()) { Diagnostics::ID id = args->size() < macro.parameters.size() ? - Diagnostics::MACRO_TOO_FEW_ARGS : - Diagnostics::MACRO_TOO_MANY_ARGS; + Diagnostics::PP_MACRO_TOO_FEW_ARGS : + Diagnostics::PP_MACRO_TOO_MANY_ARGS; mDiagnostics->report(id, identifier.location, identifier.text); return false; } @@ -311,9 +327,9 @@ bool MacroExpander::collectMacroArgs(const Macro& macro, // Pre-expand each argument before substitution. // This step expands each argument individually before they are // inserted into the macro body. - for (std::size_t i = 0; i < args->size(); ++i) + size_t numTokens = 0; + for (auto &arg : *args) { - MacroArg& arg = args->at(i); TokenLexer lexer(&arg); MacroExpander expander(&lexer, mMacroSet, mDiagnostics); @@ -323,18 +339,32 @@ bool MacroExpander::collectMacroArgs(const Macro& macro, { 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) +void MacroExpander::replaceMacroParams(const Macro ¯o, + const std::vector<MacroArg> &args, + std::vector<Token> *replacements) { for (std::size_t i = 0; i < macro.replacements.size(); ++i) { - const Token& repl = macro.replacements[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); @@ -353,7 +383,7 @@ void MacroExpander::replaceMacroParams(const Macro& macro, } std::size_t iArg = std::distance(macro.parameters.begin(), iter); - const MacroArg& arg = args[iArg]; + const MacroArg &arg = args[iArg]; if (arg.empty()) { continue; @@ -366,5 +396,25 @@ void MacroExpander::replaceMacroParams(const Macro& macro, } } +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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.h index 21b67571f..77c767c34 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/MacroExpander.h @@ -4,72 +4,71 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_MACRO_EXPANDER_H_ -#define COMPILER_PREPROCESSOR_MACRO_EXPANDER_H_ +#ifndef COMPILER_PREPROCESSOR_MACROEXPANDER_H_ +#define COMPILER_PREPROCESSOR_MACROEXPANDER_H_ -#include <cassert> #include <memory> #include <vector> -#include "Lexer.h" -#include "Macro.h" -#include "pp_utils.h" +#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); - virtual ~MacroExpander(); + MacroExpander(Lexer *lexer, MacroSet *macroSet, Diagnostics *diagnostics); + ~MacroExpander() override; - virtual void lex(Token* token); + void lex(Token *token) override; private: - PP_DISALLOW_COPY_AND_ASSIGN(MacroExpander); - - void getToken(Token* token); - void ungetToken(const Token& token); + void getToken(Token *token); + void ungetToken(const Token &token); bool isNextTokenLeftParen(); - bool pushMacro(const Macro& macro, const Token& identifier); + bool pushMacro(const Macro ¯o, const Token &identifier); void popMacro(); - bool expandMacro(const Macro& macro, - const Token& identifier, - std::vector<Token>* replacements); + bool expandMacro(const Macro ¯o, + const Token &identifier, + std::vector<Token> *replacements); typedef std::vector<Token> MacroArg; - bool collectMacroArgs(const Macro& macro, - const Token& identifier, - std::vector<MacroArg>* args); - void replaceMacroParams(const Macro& macro, - const std::vector<MacroArg>& args, - std::vector<Token>* replacements); + bool collectMacroArgs(const Macro ¯o, + const Token &identifier, + std::vector<MacroArg> *args, + SourceLocation *closingParenthesisLocation); + void replaceMacroParams(const Macro ¯o, + const std::vector<MacroArg> &args, + std::vector<Token> *replacements); struct MacroContext { - const Macro* macro; + MacroContext(); + bool empty() const; + const Token &get(); + void unget(); + + const Macro *macro; std::size_t index; std::vector<Token> replacements; - - MacroContext() : macro(0), index(0) { } - bool empty() const { return index == replacements.size(); } - const Token& get() { return replacements[index++]; } - void unget() { assert(index > 0); --index; } }; - Lexer* mLexer; - MacroSet* mMacroSet; - Diagnostics* mDiagnostics; + Lexer *mLexer; + MacroSet *mMacroSet; + Diagnostics *mDiagnostics; - std::auto_ptr<Token> mReserveToken; - std::vector<MacroContext*> mContextStack; + std::unique_ptr<Token> mReserveToken; + std::vector<MacroContext *> mContextStack; + size_t mTotalTokensInContexts; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_MACRO_EXPANDER_H_ +#endif // COMPILER_PREPROCESSOR_MACROEXPANDER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.cpp index b615c85dc..1709d6673 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.cpp @@ -4,41 +4,38 @@ // found in the LICENSE file. // -#include "Preprocessor.h" +#include "compiler/preprocessor/Preprocessor.h" -#include <cassert> -#include <sstream> - -#include "DiagnosticsBase.h" -#include "DirectiveParser.h" -#include "Macro.h" -#include "MacroExpander.h" -#include "Token.h" -#include "Tokenizer.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; + Diagnostics *diagnostics; MacroSet macroSet; Tokenizer tokenizer; DirectiveParser directiveParser; MacroExpander macroExpander; - PreprocessorImpl(Diagnostics* diag, - DirectiveHandler* directiveHandler) : - diagnostics(diag), - tokenizer(diag), - directiveParser(&tokenizer, ¯oSet, diag, directiveHandler), - macroExpander(&directiveParser, ¯oSet, diag) + PreprocessorImpl(Diagnostics *diag, DirectiveHandler *directiveHandler) + : diagnostics(diag), + tokenizer(diag), + directiveParser(&tokenizer, ¯oSet, diag, directiveHandler), + macroExpander(&directiveParser, ¯oSet, diag) { } }; -Preprocessor::Preprocessor(Diagnostics* diagnostics, - DirectiveHandler* directiveHandler) +Preprocessor::Preprocessor(Diagnostics *diagnostics, + DirectiveHandler *directiveHandler) { mImpl = new PreprocessorImpl(diagnostics, directiveHandler); } @@ -49,44 +46,26 @@ Preprocessor::~Preprocessor() } bool Preprocessor::init(size_t count, - const char* const string[], + const char * const string[], const int length[]) { - static const int kGLSLVersion = 100; + static const int kDefaultGLSLVersion = 100; // Add standard pre-defined macros. predefineMacro("__LINE__", 0); predefineMacro("__FILE__", 0); - predefineMacro("__VERSION__", kGLSLVersion); + predefineMacro("__VERSION__", kDefaultGLSLVersion); predefineMacro("GL_ES", 1); return mImpl->tokenizer.init(count, string, length); } -void Preprocessor::predefineMacro(const char* name, int value) -{ - std::ostringstream stream; - stream << value; - - Token token; - token.type = Token::CONST_INT; - token.text = stream.str(); - - Macro macro; - macro.predefined = true; - macro.type = Macro::kTypeObj; - macro.name = name; - macro.replacements.push_back(token); - - mImpl->macroSet[name] = macro; -} - -void Preprocessor::setMaxTokenLength(size_t maxLength) +void Preprocessor::predefineMacro(const char *name, int value) { - mImpl->tokenizer.setMaxTokenLength(maxLength); + PredefineMacro(&mImpl->macroSet, name, value); } -void Preprocessor::lex(Token* token) +void Preprocessor::lex(Token *token) { bool validToken = false; while (!validToken) @@ -98,14 +77,14 @@ void Preprocessor::lex(Token* token) // Convert preprocessing tokens to compiler tokens or report // diagnostics. case Token::PP_HASH: - assert(false); - break; + UNREACHABLE(); + break; case Token::PP_NUMBER: - mImpl->diagnostics->report(Diagnostics::INVALID_NUMBER, + mImpl->diagnostics->report(Diagnostics::PP_INVALID_NUMBER, token->location, token->text); break; case Token::PP_OTHER: - mImpl->diagnostics->report(Diagnostics::INVALID_CHARACTER, + mImpl->diagnostics->report(Diagnostics::PP_INVALID_CHARACTER, token->location, token->text); break; default: @@ -115,5 +94,9 @@ void Preprocessor::lex(Token* token) } } -} // namespace pp +void Preprocessor::setMaxTokenSize(size_t maxTokenSize) +{ + mImpl->tokenizer.setMaxTokenSize(maxTokenSize); +} +} // namespace pp diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.h index 9a90d79a1..cd699786f 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Preprocessor.h @@ -7,9 +7,9 @@ #ifndef COMPILER_PREPROCESSOR_PREPROCESSOR_H_ #define COMPILER_PREPROCESSOR_PREPROCESSOR_H_ -#include <stddef.h> +#include <cstddef> -#include "pp_utils.h" +#include "common/angleutils.h" namespace pp { @@ -19,10 +19,10 @@ class DirectiveHandler; struct PreprocessorImpl; struct Token; -class Preprocessor +class Preprocessor : angle::NonCopyable { public: - Preprocessor(Diagnostics* diagnostics, DirectiveHandler* directiveHandler); + Preprocessor(Diagnostics *diagnostics, DirectiveHandler *directiveHandler); ~Preprocessor(); // count: specifies the number of elements in the string and length arrays. @@ -34,24 +34,19 @@ class Preprocessor // 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[]); + bool init(size_t count, const char * const string[], const int length[]); // Adds a pre-defined macro. - void predefineMacro(const char* name, int value); - // Sets maximum allowed token length. - // If token length exceeds this limit, - // the token text will be truncated to the given maximum length, and - // TOKEN_TOO_LONG diagnostic will be generated. - // The maximum length defaults to 256. - void setMaxTokenLength(size_t maxLength); + void predefineMacro(const char *name, int value); - void lex(Token* token); + void lex(Token *token); - private: - PP_DISALLOW_COPY_AND_ASSIGN(Preprocessor); + // Set maximum preprocessor token size + void setMaxTokenSize(size_t maxTokenSize); - PreprocessorImpl* mImpl; + private: + PreprocessorImpl *mImpl; }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_PREPROCESSOR_H_ +#endif // COMPILER_PREPROCESSOR_PREPROCESSOR_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/SourceLocation.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/SourceLocation.h index 6982613ac..af8a8d5d1 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/SourceLocation.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/SourceLocation.h @@ -4,18 +4,26 @@ // found in the LICENSE file. // -#ifndef COMPILER_PREPROCESSOR_SOURCE_LOCATION_H_ -#define COMPILER_PREPROCESSOR_SOURCE_LOCATION_H_ +#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) { } + SourceLocation() + : file(0), + line(0) + { + } + SourceLocation(int f, int l) + : file(f), + line(l) + { + } - bool equals(const SourceLocation& other) const + bool equals(const SourceLocation &other) const { return (file == other.file) && (line == other.line); } @@ -24,15 +32,16 @@ struct SourceLocation int line; }; -inline bool operator==(const SourceLocation& lhs, const SourceLocation& rhs) +inline bool operator==(const SourceLocation &lhs, const SourceLocation &rhs) { return lhs.equals(rhs); } -inline bool operator!=(const SourceLocation& lhs, const SourceLocation& rhs) +inline bool operator!=(const SourceLocation &lhs, const SourceLocation &rhs) { return !lhs.equals(rhs); } } // namespace pp -#endif // COMPILER_PREPROCESSOR_SOURCE_LOCATION_H_ + +#endif // COMPILER_PREPROCESSOR_SOURCELOCATION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.cpp index 67f50aa32..41610a402 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.cpp @@ -4,11 +4,10 @@ // found in the LICENSE file. // -#include "Token.h" +#include "compiler/preprocessor/Token.h" -#include <cassert> - -#include "numeric_lex.h" +#include "common/debug.h" +#include "compiler/preprocessor/numeric_lex.h" namespace pp { @@ -21,7 +20,7 @@ void Token::reset() text.clear(); } -bool Token::equals(const Token& other) const +bool Token::equals(const Token &other) const { return (type == other.type) && (flags == other.flags) && @@ -53,25 +52,25 @@ void Token::setExpansionDisabled(bool disable) flags &= ~EXPANSION_DISABLED; } -bool Token::iValue(int* value) const +bool Token::iValue(int *value) const { - assert(type == CONST_INT); + ASSERT(type == CONST_INT); return numeric_lex_int(text, value); } -bool Token::uValue(unsigned int* value) const +bool Token::uValue(unsigned int *value) const { - assert(type == CONST_INT); + ASSERT(type == CONST_INT); return numeric_lex_int(text, value); } -bool Token::fValue(float* value) const +bool Token::fValue(float *value) const { - assert(type == CONST_FLOAT); + ASSERT(type == CONST_FLOAT); return numeric_lex_float(text, value); } -std::ostream& operator<<(std::ostream& out, const Token& token) +std::ostream &operator<<(std::ostream &out, const Token &token) { if (token.hasLeadingSpace()) out << " "; diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.h index 8b553aecb..716503b32 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Token.h @@ -10,7 +10,7 @@ #include <ostream> #include <string> -#include "SourceLocation.h" +#include "compiler/preprocessor/SourceLocation.h" namespace pp { @@ -62,27 +62,40 @@ struct Token EXPANSION_DISABLED = 1 << 2 }; - Token() : type(0), flags(0) { } + Token() + : type(0), + flags(0) + { + } void reset(); - bool equals(const Token& other) const; + 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; } + bool atStartOfLine() const + { + return (flags & AT_START_OF_LINE) != 0; + } void setAtStartOfLine(bool start); - bool hasLeadingSpace() const { return (flags & HAS_LEADING_SPACE) != 0; } + bool hasLeadingSpace() const + { + return (flags & HAS_LEADING_SPACE) != 0; + } void setHasLeadingSpace(bool space); - bool expansionDisabled() const { return (flags & EXPANSION_DISABLED) != 0; } + 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; + bool iValue(int *value) const; + bool uValue(unsigned int *value) const; + bool fValue(float *value) const; int type; unsigned int flags; @@ -90,17 +103,18 @@ struct Token std::string text; }; -inline bool operator==(const Token& lhs, const Token& rhs) +inline bool operator==(const Token &lhs, const Token &rhs) { return lhs.equals(rhs); } -inline bool operator!=(const Token& lhs, const Token& rhs) +inline bool operator!=(const Token &lhs, const Token &rhs) { return !lhs.equals(rhs); } -extern std::ostream& operator<<(std::ostream& out, const Token& token); +std::ostream &operator<<(std::ostream &out, const Token &token); } // namepsace pp + #endif // COMPILER_PREPROCESSOR_TOKEN_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.cpp b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.cpp index 363a37b36..fee20c674 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.cpp @@ -1,6 +1,6 @@ #line 16 "./Tokenizer.l" // -// Copyright (c) 2011-2013 The ANGLE Project Authors. All rights reserved. +// 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. // @@ -9,20 +9,96 @@ -#line 13 "./Tokenizer.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 35 +#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. */ @@ -56,7 +132,6 @@ 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; @@ -64,7 +139,6 @@ typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; -#endif /* ! C99 */ /* Limits of integral types. */ #ifndef INT8_MIN @@ -95,32 +169,31 @@ typedef unsigned int flex_uint32_t; #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 */ +/* TODO: this is always defined, so inline it */ +#define yyconst const -/* C99 requires __STDC__ to be defined as 1. */ -#if defined (__STDC__) +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif -#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 @@ -128,12 +201,34 @@ typedef unsigned int flex_uint32_t; */ #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 @@ -145,12 +240,29 @@ typedef void* yyscan_t; #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. @@ -158,23 +270,41 @@ typedef void* yyscan_t; #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; @@ -185,12 +315,22 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; 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 \ @@ -205,26 +345,31 @@ typedef size_t yy_size_t; } \ 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; + int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ - yy_size_t yy_n_chars; + 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 @@ -248,6 +393,7 @@ struct yy_buffer_state 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. */ @@ -272,6 +418,10 @@ struct yy_buffer_state }; #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". @@ -282,11 +432,18 @@ struct yy_buffer_state ? 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 ); @@ -295,22 +452,30 @@ 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,yy_size_t len ,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 ){ \ @@ -321,6 +486,8 @@ void ppfree (void * ,yyscan_t yyscanner ); YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } + + #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ @@ -331,34 +498,50 @@ void ppfree (void * ,yyscan_t 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(n) 1 +#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 yy_fatal_error (yyconst char msg[] ,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 = (yy_size_t) (yy_cp - 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, @@ -368,20 +551,22 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[87] = +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, 11, 9, 14, 16, 18, 17, 15, 8, - 36, 36, 31, 21, 32, 22, 3, 5, 6, 11, - 10, 11, 1, 10, 11, 0, 10, 9, 28, 29, - 0, 10, 10, 10, 10, 0 + 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 flex_int32_t yy_ec[256] = +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, @@ -389,14 +574,14 @@ static yyconst flex_int32_t yy_ec[256] = 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, 21, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 24, 23, 23, - 9, 25, 9, 26, 23, 1, 21, 21, 21, 21, - - 22, 21, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, - 23, 23, 9, 27, 9, 9, 1, 1, 1, 1, + 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, @@ -413,89 +598,101 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst flex_int32_t yy_meta[28] = +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, 1, 1, 1 + 5, 5, 5, 5, 5, 5, 1, 1, 1 } ; -static yyconst flex_int16_t yy_base[92] = +static yyconst flex_uint16_t yy_base[103] = { 0, - 0, 0, 25, 27, 162, 163, 159, 163, 152, 132, - 163, 131, 24, 163, 116, 22, 26, 31, 30, 37, - 40, 44, 115, 46, 0, 64, 50, 15, 0, 163, - 124, 91, 88, 163, 163, 163, 163, 163, 163, 163, - 163, 163, 163, 64, 163, 0, 163, 76, 54, 58, - 79, 91, 91, 0, 56, 163, 163, 163, 32, 0, - 163, 36, 163, 163, 163, 163, 0, 163, 163, 94, - 0, 106, 0, 0, 113, 55, 72, 113, 163, 163, - 116, 101, 108, 123, 126, 163, 143, 31, 148, 153, - 155 - + 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[92] = +static yyconst flex_int16_t yy_def[103] = { 0, - 86, 1, 87, 87, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 20, 86, 86, 86, 88, 86, 86, 86, 89, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 90, 86, 86, 20, 20, - 48, 51, 91, 21, 86, 86, 86, 86, 86, 88, - 86, 86, 86, 86, 86, 86, 89, 86, 86, 44, - 44, 70, 90, 48, 51, 86, 52, 91, 86, 86, - 86, 72, 75, 86, 86, 0, 86, 86, 86, 86, - 86 - + 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_int16_t yy_nxt[191] = +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, 26, 27, 28, 30, 31, 30, - 31, 37, 40, 65, 32, 60, 32, 42, 61, 45, - 41, 66, 38, 46, 43, 44, 44, 44, 47, 48, - 80, 49, 49, 50, 54, 54, 54, 51, 52, 51, - 53, 55, 56, 51, 58, 59, 61, 62, 63, 84, - 84, 84, 50, 50, 79, 64, 70, 51, 71, 71, - 71, 51, 86, 86, 70, 72, 70, 70, 51, 33, - 74, 74, 74, 51, 51, 51, 51, 75, 51, 51, - - 51, 76, 76, 51, 69, 77, 77, 77, 70, 70, - 70, 86, 86, 51, 51, 70, 81, 81, 86, 86, - 82, 82, 82, 81, 81, 51, 68, 83, 83, 83, - 85, 85, 85, 57, 39, 51, 51, 84, 84, 84, - 85, 85, 85, 29, 29, 29, 29, 29, 67, 36, - 35, 67, 67, 73, 34, 73, 73, 73, 78, 78, - 33, 86, 5, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86 + 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[191] = +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, 3, 3, 4, - 4, 13, 16, 28, 3, 88, 4, 17, 62, 19, - 16, 28, 13, 19, 17, 18, 18, 18, 19, 20, - 59, 20, 20, 20, 21, 21, 21, 20, 20, 20, - 20, 22, 22, 21, 24, 24, 26, 26, 27, 76, - 76, 76, 50, 50, 55, 27, 44, 49, 44, 44, - 44, 50, 77, 77, 44, 44, 44, 44, 48, 33, - 48, 48, 48, 51, 51, 51, 48, 48, 48, 48, - - 51, 52, 52, 53, 32, 52, 52, 52, 70, 70, - 70, 82, 82, 53, 53, 70, 72, 72, 83, 83, - 72, 72, 72, 75, 75, 78, 31, 75, 75, 75, - 81, 81, 81, 23, 15, 78, 78, 84, 84, 84, - 85, 85, 85, 87, 87, 87, 87, 87, 89, 12, - 10, 89, 89, 90, 9, 90, 90, 90, 91, 91, - 7, 5, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86 + 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 @@ -507,7 +704,7 @@ static yyconst flex_int16_t yy_chk[191] = #define YY_RESTORE_YY_MORE_OFFSET /* // -// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. +// 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. // @@ -519,20 +716,27 @@ http://msdn.microsoft.com/en-us/library/2scxys89.aspx IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh. */ -#include "Tokenizer.h" +#if defined(_MSC_VER) +#pragma warning(disable: 4005) +#endif + +#include "compiler/preprocessor/Tokenizer.h" -#include "DiagnosticsBase.h" -#include "Token.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 -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-pragmas" -#pragma clang diagnostic ignored "-Wdeprecated-register" +// Workaround for flex using the register keyword, deprecated in C++11. +#ifdef __cplusplus +#if __cplusplus > 199711L +#define register +#endif #endif typedef std::string YYSTYPE; @@ -566,13 +770,25 @@ typedef pp::SourceLocation YYLTYPE; } while(0); #define YY_INPUT(buf, result, maxSize) \ - result = yyextra->input.read(buf, 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 { @@ -586,8 +802,8 @@ struct yyguts_t 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; + int yy_n_chars; + int yyleng_r; char *yy_c_buf_p; int yy_init; int yy_start; @@ -601,65 +817,132 @@ struct yyguts_t 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 ); + + +void ppset_in (FILE * _in_str ,yyscan_t yyscanner ); + + FILE *ppget_out (yyscan_t yyscanner ); -void ppset_out (FILE * out_str ,yyscan_t yyscanner ); -yy_size_t ppget_leng (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 ); + + +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. */ @@ -672,6 +955,12 @@ 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 @@ -680,19 +969,44 @@ static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #endif +#if 0 + +#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 fwrite( yytext, yyleng, 1, yyout ) +#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". */ @@ -701,7 +1015,7 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ - yy_size_t n; \ + size_t n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ @@ -714,7 +1028,7 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); else \ { \ errno=0; \ - while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + while ( (result = (int) fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ { \ if( errno != EINTR) \ { \ @@ -729,6 +1043,8 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #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. @@ -737,24 +1053,48 @@ static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); #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); @@ -762,6 +1102,7 @@ extern 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. */ @@ -769,29 +1110,39 @@ extern int pplex \ #define YY_USER_ACTION #endif + + /* Code executed at the end of each rule. */ #ifndef YY_BREAK -#define YY_BREAK 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 { - register yy_state_type yy_current_state; - register char *yy_cp, *yy_bp; - register int yy_act; + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - /* Line comment */ + + yylval = yylval_param; + + yylloc = yylloc_param; + if ( !yyg->yy_init ) { yyg->yy_init = 1; @@ -800,6 +1151,8 @@ YY_DECL YY_USER_INIT; #endif + + if ( ! yyg->yy_start ) yyg->yy_start = 1; /* first start state */ @@ -818,7 +1171,12 @@ YY_DECL pp_load_buffer_state(yyscanner ); } - while ( 1 ) /* loops until end-of-file is reached */ + { + + + /* Line comment */ + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = yyg->yy_c_buf_p; @@ -834,7 +1192,7 @@ YY_DECL yy_match: do { - register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + 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; @@ -843,13 +1201,13 @@ yy_match: 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 >= 87 ) + if ( yy_current_state >= 98 ) yy_c = yy_meta[(unsigned int) yy_c]; } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (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 != 86 ); + while ( yy_current_state != 97 ); yy_cp = yyg->yy_last_accepting_cpos; yy_current_state = yyg->yy_last_accepting_state; @@ -858,8 +1216,11 @@ yy_find_action: 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 */ @@ -875,7 +1236,7 @@ YY_RULE_SETUP YY_BREAK /* Block comment */ /* Line breaks are just counted - not returned. */ -/* The comment is replaced by a single space. */ +/* The comment is replaced by a single space. */ case 2: YY_RULE_SETUP { BEGIN(COMMENT); } @@ -1139,7 +1500,7 @@ case YY_STATE_EOF(COMMENT): if (YY_START == COMMENT) { - yyextra->diagnostics->report(pp::Diagnostics::EOF_IN_COMMENT, + yyextra->diagnostics->report(pp::Diagnostics::PP_EOF_IN_COMMENT, pp::SourceLocation(yyfileno, yylineno), ""); } @@ -1279,8 +1640,14 @@ ECHO; "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: @@ -1291,9 +1658,9 @@ ECHO; 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; + 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] ) @@ -1322,7 +1689,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) /* 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; + 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++); @@ -1335,21 +1702,21 @@ static int yy_get_next_buffer (yyscan_t yyscanner) else { - yy_size_t num_to_read = - YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + 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; + 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; + int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; @@ -1362,7 +1729,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) } else /* Can't grow it, we don't own it. */ - b->yy_ch_buf = 0; + b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( @@ -1370,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); } @@ -1379,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; } @@ -1404,7 +1773,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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) { + 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 ); @@ -1412,7 +1781,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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; @@ -1421,19 +1790,21 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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; + 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 ) { - register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + 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; @@ -1442,15 +1813,16 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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 >= 87 ) + if ( yy_current_state >= 98 ) yy_c = yy_meta[(unsigned int) yy_c]; } - yy_current_state = yy_nxt[yy_base[yy_current_state] + (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 @@ -1458,11 +1830,11 @@ static int yy_get_next_buffer (yyscan_t yyscanner) */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) { - register int yy_is_jam; + 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; + char *yy_cp = yyg->yy_c_buf_p; - register YY_CHAR yy_c = 1; + YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yyg->yy_last_accepting_state = yy_current_state; @@ -1471,15 +1843,97 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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 >= 87 ) + if ( yy_current_state >= 98 ) 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 == 86); + 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 + +#if 0 +#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 */ + auto 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. @@ -1499,6 +1953,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) pp_load_buffer_state(yyscanner ); } + /** Switch to a different input buffer. * @param new_buffer The new input buffer. * @param yyscanner The scanner object. @@ -1535,6 +1990,7 @@ static int yy_get_next_buffer (yyscan_t yyscanner) 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; @@ -1574,6 +2030,7 @@ static void pp_load_buffer_state (yyscan_t yyscanner) return b; } + /** Destroy the buffer. * @param b a buffer created with pp_create_buffer() * @param yyscanner The scanner object. @@ -1594,6 +2051,7 @@ static void pp_load_buffer_state (yyscan_t 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. @@ -1618,8 +2076,11 @@ static void pp_load_buffer_state (yyscan_t yyscanner) b->yy_bs_column = 0; } + + b->yy_is_interactive = 0; + errno = oerrno; } @@ -1684,6 +2145,7 @@ void pppush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t 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. @@ -1705,6 +2167,7 @@ void pppop_buffer_state (yyscan_t yyscanner) } } + /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ @@ -1719,13 +2182,14 @@ static void ppensure_buffer_stack (yyscan_t yyscanner) * 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; + 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; @@ -1736,7 +2200,7 @@ static void ppensure_buffer_stack (yyscan_t yyscanner) 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 */; + 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 @@ -1752,6 +2216,10 @@ static void ppensure_buffer_stack (yyscan_t yyscanner) } } + + + + /** 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 @@ -1766,16 +2234,16 @@ YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann 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; + 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 = 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 = 0; + b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; @@ -1787,6 +2255,9 @@ YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann 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 @@ -1798,29 +2269,33 @@ YY_BUFFER_STATE pp_scan_buffer (char * base, yy_size_t size , yyscan_t yyscann YY_BUFFER_STATE pp_scan_string (yyconst char * yystr , yyscan_t yyscanner) { - return pp_scan_bytes(yystr,strlen(yystr) ,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 bytes the byte buffer to scan - * @param len the number of bytes in the buffer pointed to by @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, yy_size_t _yybytes_len , yyscan_t yyscanner) +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, i; + yy_size_t n; + yy_size_t i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = _yybytes_len + 2; + 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 < _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; @@ -1837,13 +2312,25 @@ YY_BUFFER_STATE pp_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len return b; } + + + + + + + + + + #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif -static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +static void yynoreturn yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) { - (void) fprintf( stderr, "%s\n", msg ); + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } @@ -1864,8 +2351,11 @@ static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) } \ while ( 0 ) + + /* Accessor methods (get/set functions) to struct members. */ + /** Get the user-defined data for this scanner. * @param yyscanner The scanner object. */ @@ -1875,6 +2365,8 @@ YY_EXTRA_TYPE ppget_extra (yyscan_t yyscanner) return yyextra; } + + /** Get the current line number. * @param yyscanner The scanner object. */ @@ -1882,12 +2374,16 @@ 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. */ @@ -1895,12 +2391,16 @@ 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. */ @@ -1910,6 +2410,8 @@ FILE *ppget_in (yyscan_t yyscanner) return yyin; } + + /** Get the output stream. * @param yyscanner The scanner object. */ @@ -1919,15 +2421,18 @@ FILE *ppget_out (yyscan_t yyscanner) return yyout; } + + /** Get the length of the current token. * @param yyscanner The scanner object. */ -yy_size_t ppget_leng (yyscan_t yyscanner) +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. */ @@ -1938,6 +2443,8 @@ char *ppget_text (yyscan_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. @@ -1948,92 +2455,123 @@ void ppset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) yyextra = user_defined ; } + + /** Set the current line number. - * @param line_number + * @param _line_number line number * @param yyscanner The scanner object. */ -void ppset_lineno (int line_number , yyscan_t yyscanner) +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" , yyscanner); + YY_FATAL_ERROR( "ppset_lineno called with no buffer" ); - yylineno = line_number; + yylineno = _line_number; } + + + /** Set the current column. - * @param line_number + * @param _column_no column number * @param yyscanner The scanner object. */ -void ppset_column (int column_no , yyscan_t yyscanner) +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" , yyscanner); + YY_FATAL_ERROR( "ppset_column called with no buffer" ); - yycolumn = column_no; + yycolumn = _column_no; } + + + + /** Set the input stream. This does not discard the current * input buffer. - * @param in_str A readable stream. + * @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) +void ppset_in (FILE * _in_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - yyin = in_str ; + yyin = _in_str ; } -void ppset_out (FILE * out_str , yyscan_t yyscanner) + + +void ppset_out (FILE * _out_str , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - yyout = out_str ; + 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) + + +void ppset_debug (int _bdebug , yyscan_t yyscanner) { struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; - yy_flex_debug = bdebug ; + 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 @@ -2062,6 +2600,7 @@ int pplex_init(yyscan_t* ptr_yy_globals) 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 @@ -2098,6 +2637,7 @@ int pplex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* 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; @@ -2105,24 +2645,31 @@ static int yy_init_globals (yyscan_t yyscanner) * This function is called from pplex_destroy(), so don't allocate here. */ - yyg->yy_buffer_stack = 0; + + yyg->yy_buffer_stack = NULL; yyg->yy_buffer_stack_top = 0; yyg->yy_buffer_stack_max = 0; - yyg->yy_c_buf_p = (char *) 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 = (FILE *) 0; - yyout = (FILE *) 0; + yyin = NULL; + yyout = NULL; #endif /* For future reference: Set errno on error, since we are called by @@ -2131,6 +2678,7 @@ static int yy_init_globals (yyscan_t yyscanner) return 0; } + /* pplex_destroy is for both reentrant and non-reentrant scanners. */ int pplex_destroy (yyscan_t yyscanner) { @@ -2147,10 +2695,14 @@ int pplex_destroy (yyscan_t yyscanner) 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); @@ -2161,23 +2713,32 @@ int pplex_destroy (yyscan_t yyscanner) 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; + 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) { - register int n; + int n; for ( n = 0; s[n]; ++n ) ; @@ -2185,13 +2746,22 @@ static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) } #endif + + void *ppalloc (yy_size_t size , yyscan_t yyscanner) { - return (void *) malloc( size ); + 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 @@ -2199,25 +2769,32 @@ void *pprealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ - return (void *) realloc( (char *) ptr, size ); + 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" -#if defined(__clang__) -#pragma clang diagnostic pop -#endif + + + + + + + namespace pp { -Tokenizer::Tokenizer(Diagnostics* diagnostics) - : mHandle(0), - mMaxTokenLength(256) +Tokenizer::Tokenizer(Diagnostics *diagnostics) : mHandle(nullptr), mMaxTokenSize(256) { mContext.diagnostics = diagnostics; } @@ -2227,9 +2804,10 @@ Tokenizer::~Tokenizer() destroyScanner(); } -bool Tokenizer::init(size_t count, const char* const string[], const int length[]) +bool Tokenizer::init(size_t count, const char * const string[], const int length[]) { - if ((count > 0) && (string == 0)) return false; + if ((count > 0) && (string == 0)) + return false; mContext.input = Input(count, string, length); return initScanner(); @@ -2247,14 +2825,19 @@ void Tokenizer::setLineNumber(int line) ppset_lineno(line,mHandle); } -void Tokenizer::lex(Token* token) +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() > mMaxTokenLength) + if (token->text.size() > mMaxTokenSize) { - mContext.diagnostics->report(Diagnostics::TOKEN_TOO_LONG, + mContext.diagnostics->report(Diagnostics::PP_TOKEN_TOO_LONG, token->location, token->text); - token->text.erase(mMaxTokenLength); + token->text.erase(mMaxTokenSize); } token->flags = 0; @@ -2268,7 +2851,7 @@ void Tokenizer::lex(Token* token) bool Tokenizer::initScanner() { - if ((mHandle == NULL) && pplex_init_extra(&mContext,&mHandle)) + if ((mHandle == nullptr) && pplex_init_extra(&mContext, &mHandle)) return false; pprestart(0,mHandle); @@ -2277,12 +2860,13 @@ bool Tokenizer::initScanner() void Tokenizer::destroyScanner() { - if (mHandle == NULL) + if (mHandle == nullptr) return; pplex_destroy(mHandle); - mHandle = NULL; + mHandle = nullptr; } } // namespace pp + diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.h index 9d131f865..6dfb19c66 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. +// 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. // @@ -7,9 +7,9 @@ #ifndef COMPILER_PREPROCESSOR_TOKENIZER_H_ #define COMPILER_PREPROCESSOR_TOKENIZER_H_ -#include "Input.h" -#include "Lexer.h" -#include "pp_utils.h" +#include "common/angleutils.h" +#include "compiler/preprocessor/Input.h" +#include "compiler/preprocessor/Lexer.h" namespace pp { @@ -21,7 +21,7 @@ class Tokenizer : public Lexer public: struct Context { - Diagnostics* diagnostics; + Diagnostics *diagnostics; Input input; // The location where yytext points to. Token location should track @@ -33,27 +33,26 @@ class Tokenizer : public Lexer bool lineStart; }; - Tokenizer(Diagnostics* diagnostics); + Tokenizer(Diagnostics *diagnostics); ~Tokenizer(); - bool init(size_t count, const char* const string[], const int length[]); + bool init(size_t count, const char * const string[], const int length[]); - void setMaxTokenLength(size_t maxLength) { mMaxTokenLength = maxLength; } void setFileNumber(int file); void setLineNumber(int line); + void setMaxTokenSize(size_t maxTokenSize); - virtual void lex(Token* token); + void lex(Token *token) override; private: - PP_DISALLOW_COPY_AND_ASSIGN(Tokenizer); bool initScanner(); void destroyScanner(); - void* mHandle; // Scanner handle. + void *mHandle; // Scanner handle. Context mContext; // Scanner extra. - size_t mMaxTokenLength; + size_t mMaxTokenSize; // Maximum token size }; } // namespace pp -#endif // COMPILER_PREPROCESSOR_TOKENIZER_H_ +#endif // COMPILER_PREPROCESSOR_TOKENIZER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.l b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/Tokenizer.l new file mode 100644 index 000000000..62eb4caa6 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/generate_parser.sh b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/generate_parser.sh new file mode 100755 index 000000000..032858722 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/preprocessor/length_limits.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/length_limits.h deleted file mode 100644 index 4f1f71319..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/length_limits.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// 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. -// - -// -// length_limits.h -// - -#if !defined(__LENGTH_LIMITS_H) -#define __LENGTH_LIMITS_H 1 - -// These constants are factored out from the rest of the headers to -// make it easier to reference them from the compiler sources. - -// These lengths do not include the NULL terminator. -#define MAX_SYMBOL_NAME_LEN 256 -#define MAX_STRING_LEN 511 - -#endif // !(defined(__LENGTH_LIMITS_H) diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/numeric_lex.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/numeric_lex.h index b04125d23..b32e42253 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/numeric_lex.h +++ b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/numeric_lex.h @@ -6,14 +6,14 @@ // numeric_lex.h: Functions to extract numeric values from string. -#ifndef COMPILER_PREPROCESSOR_NUMERIC_LEX_H_ -#define COMPILER_PREPROCESSOR_NUMERIC_LEX_H_ +#ifndef COMPILER_PREPROCESSOR_NUMERICLEX_H_ +#define COMPILER_PREPROCESSOR_NUMERICLEX_H_ #include <sstream> namespace pp { -inline std::ios::fmtflags numeric_base_int(const std::string& str) +inline std::ios::fmtflags numeric_base_int(const std::string &str) { if ((str.size() >= 2) && (str[0] == '0') && @@ -21,7 +21,7 @@ inline std::ios::fmtflags numeric_base_int(const std::string& str) { return std::ios::hex; } - else if ((str.size() >= 1) && (str[0] == '0')) + if ((str.size() >= 1) && (str[0] == '0')) { return std::ios::oct; } @@ -34,7 +34,7 @@ inline std::ios::fmtflags numeric_base_int(const std::string& str) // in which case false is returned. template<typename IntType> -bool numeric_lex_int(const std::string& str, IntType* value) +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. @@ -46,8 +46,17 @@ bool numeric_lex_int(const std::string& str, IntType* value) } template<typename FloatType> -bool numeric_lex_float(const std::string& str, FloatType* value) +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. @@ -55,7 +64,9 @@ bool numeric_lex_float(const std::string& str, FloatType* value) stream >> (*value); return !stream.fail(); +#endif } } // namespace pp. -#endif // COMPILER_PREPROCESSOR_NUMERIC_LEX_H_ + +#endif // COMPILER_PREPROCESSOR_NUMERICLEX_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/pp_utils.h b/Source/ThirdParty/ANGLE/src/compiler/preprocessor/pp_utils.h deleted file mode 100644 index 17164ea8b..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/preprocessor/pp_utils.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// 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. -// - -// pp_utils.h: Common preprocessor utilities - -#ifndef COMPILER_PREPROCESSOR_PPUTILS_H_ -#define COMPILER_PREPROCESSOR_PPUTILS_H_ - -// A macro to disallow the copy constructor and operator= functions -// This must be used in the private: declarations for a class. -#define PP_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - void operator=(const TypeName&) - -#endif // COMPILER_PREPROCESSOR_PPUTILS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.cpp b/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.cpp deleted file mode 100644 index b9d2bab17..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// -// 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/InfoSink.h" -#include "compiler/ParseContext.h" -#include "compiler/depgraph/DependencyGraphOutput.h" -#include "compiler/timing/RestrictFragmentShaderTiming.h" - -RestrictFragmentShaderTiming::RestrictFragmentShaderTiming(TInfoSinkBase& sink) - : mSink(sink) - , mNumErrors(0) -{ - // Sampling ops found only in fragment shaders. - mSamplingOps.insert("texture2D(s21;vf2;f1;"); - mSamplingOps.insert("texture2DProj(s21;vf3;f1;"); - mSamplingOps.insert("texture2DProj(s21;vf4;f1;"); - mSamplingOps.insert("textureCube(sC1;vf3;f1;"); - // Sampling ops found in both vertex and fragment shaders. - mSamplingOps.insert("texture2D(s21;vf2;"); - mSamplingOps.insert("texture2DProj(s21;vf3;"); - mSamplingOps.insert("texture2DProj(s21;vf4;"); - mSamplingOps.insert("textureCube(sC1;vf3;"); - // Sampling ops provided by OES_EGL_image_external. - mSamplingOps.insert("texture2D(1;vf2;"); - mSamplingOps.insert("texture2DProj(1;vf3;"); - mSamplingOps.insert("texture2DProj(1;vf4;"); - // Sampling ops provided by ARB_texture_rectangle. - mSamplingOps.insert("texture2DRect(1;vf2;"); - mSamplingOps.insert("texture2DRectProj(1;vf3;"); - mSamplingOps.insert("texture2DRectProj(1;vf4;"); -} - -// FIXME(mvujovic): We do not know if the execution time of built-in operations like sin, pow, etc. -// can vary based on the value of the input arguments. If so, we should restrict those as well. -void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& graph) -{ - mNumErrors = 0; - - // FIXME(mvujovic): The dependency graph does not support user defined function calls right now, - // so we generate errors for them. - validateUserDefinedFunctionCallUsage(graph); - - // Starting from each sampler, traverse the dependency graph and generate an error each time we - // hit a node where sampler dependent values are not allowed. - for (TGraphSymbolVector::const_iterator iter = graph.beginSamplerSymbols(); - iter != graph.endSamplerSymbols(); - ++iter) - { - TGraphSymbol* samplerSymbol = *iter; - clearVisited(); - samplerSymbol->traverse(this); - } -} - -void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph) -{ - for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); - iter != graph.endUserDefinedFunctionCalls(); - ++iter) - { - TGraphFunctionCall* functionCall = *iter; - beginError(functionCall->getIntermFunctionCall()); - mSink << "A call to a user defined function is not permitted.\n"; - } -} - -void RestrictFragmentShaderTiming::beginError(const TIntermNode* node) -{ - ++mNumErrors; - mSink.prefix(EPrefixError); - mSink.location(node->getLine()); -} - -bool RestrictFragmentShaderTiming::isSamplingOp(const TIntermAggregate* intermFunctionCall) const -{ - return !intermFunctionCall->isUserDefined() && - mSamplingOps.find(intermFunctionCall->getName()) != mSamplingOps.end(); -} - -void RestrictFragmentShaderTiming::visitArgument(TGraphArgument* parameter) -{ - // Texture cache access time might leak sensitive information. - // Thus, we restrict sampler dependent values from affecting the coordinate or LOD bias of a - // sampling operation. - if (isSamplingOp(parameter->getIntermFunctionCall())) { - switch (parameter->getArgumentNumber()) { - case 1: - // Second argument (coord) - beginError(parameter->getIntermFunctionCall()); - mSink << "An expression dependent on a sampler is not permitted to be the" - << " coordinate argument of a sampling operation.\n"; - break; - case 2: - // Third argument (bias) - beginError(parameter->getIntermFunctionCall()); - mSink << "An expression dependent on a sampler is not permitted to be the" - << " bias argument of a sampling operation.\n"; - break; - default: - // First argument (sampler) - break; - } - } -} - -void RestrictFragmentShaderTiming::visitSelection(TGraphSelection* selection) -{ - beginError(selection->getIntermSelection()); - mSink << "An expression dependent on a sampler is not permitted in a conditional statement.\n"; -} - -void RestrictFragmentShaderTiming::visitLoop(TGraphLoop* loop) -{ - beginError(loop->getIntermLoop()); - mSink << "An expression dependent on a sampler is not permitted in a loop condition.\n"; -} - -void RestrictFragmentShaderTiming::visitLogicalOp(TGraphLogicalOp* logicalOp) -{ - beginError(logicalOp->getIntermLogicalOp()); - mSink << "An expression dependent on a sampler is not permitted on the left hand side of a logical " - << logicalOp->getOpString() - << " operator.\n"; -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.h b/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.h deleted file mode 100644 index 899165ca2..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictFragmentShaderTiming.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// 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_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_ -#define COMPILER_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_ - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/intermediate.h" -#include "compiler/depgraph/DependencyGraph.h" - -class TInfoSinkBase; - -class RestrictFragmentShaderTiming : TDependencyGraphTraverser { -public: - RestrictFragmentShaderTiming(TInfoSinkBase& sink); - void enforceRestrictions(const TDependencyGraph& graph); - int numErrors() const { return mNumErrors; } - - virtual void visitArgument(TGraphArgument* parameter); - virtual void visitSelection(TGraphSelection* selection); - virtual void visitLoop(TGraphLoop* loop); - virtual void visitLogicalOp(TGraphLogicalOp* logicalOp); - -private: - void beginError(const TIntermNode* node); - void validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph); - bool isSamplingOp(const TIntermAggregate* intermFunctionCall) const; - - TInfoSinkBase& mSink; - int mNumErrors; - - typedef std::set<TString> StringSet; - StringSet mSamplingOps; -}; - -#endif // COMPILER_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.cpp b/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.cpp deleted file mode 100644 index 355eb62d6..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// 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/timing/RestrictVertexShaderTiming.h" - -void RestrictVertexShaderTiming::visitSymbol(TIntermSymbol* node) -{ - if (IsSampler(node->getBasicType())) { - ++mNumErrors; - mSink.message(EPrefixError, - node->getLine(), - "Samplers are not permitted in vertex shaders"); - } -} diff --git a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.h b/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.h deleted file mode 100644 index 19a05fa68..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/timing/RestrictVertexShaderTiming.h +++ /dev/null @@ -1,33 +0,0 @@ -// -// 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_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_ -#define COMPILER_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_ - -#include "GLSLANG/ShaderLang.h" - -#include "compiler/intermediate.h" -#include "compiler/InfoSink.h" - -class TInfoSinkBase; - -class RestrictVertexShaderTiming : public TIntermTraverser { -public: - RestrictVertexShaderTiming(TInfoSinkBase& sink) - : TIntermTraverser(true, false, false) - , mSink(sink) - , mNumErrors(0) {} - - void enforceRestrictions(TIntermNode* root) { root->traverse(this); } - int numErrors() { return mNumErrors; } - - virtual void visitSymbol(TIntermSymbol*); -private: - TInfoSinkBase& mSink; - int mNumErrors; -}; - -#endif // COMPILER_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/64bit-lexer-safety.patch b/Source/ThirdParty/ANGLE/src/compiler/translator/64bit-lexer-safety.patch new file mode 100644 index 000000000..db3b2fab5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/64bit-lexer-safety.patch @@ -0,0 +1,40 @@ +--- a/src/compiler/glslang_lex.cpp ++++ b/src/compiler/glslang_lex.cpp +@@ -68,6 +68,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; +@@ -191,6 +192,11 @@ typedef void* yyscan_t; + 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 +@@ -204,7 +210,7 @@ typedef struct yy_buffer_state *YY_BUFFER_STATE; + */ + #define YY_LESS_LINENO(n) \ + do { \ +- int yyl;\ ++ yy_size_t yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ +@@ -378,7 +379,7 @@ static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + */ + #define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ +- yyleng = (size_t) (yy_cp - yy_bp); \ ++ yyleng = (yy_size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.cpp new file mode 100644 index 000000000..cbeaff5ae --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.cpp @@ -0,0 +1,452 @@ +// +// 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 +{ + +// 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); + UNUSED_ASSERTION_VARIABLE(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); + UNUSED_ASSERTION_VARIABLE(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); + UNUSED_ASSERTION_VARIABLE(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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.h new file mode 100644 index 000000000..4795bc387 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ASTMetadataHLSL.h @@ -0,0 +1,58 @@ +// +// 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> + +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); + +#endif // COMPILER_TRANSLATOR_ASTMETADATAHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/AddAndTrueToLoopCondition.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/AddAndTrueToLoopCondition.cpp new file mode 100644 index 000000000..0177fea96 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/AddAndTrueToLoopCondition.h b/Source/ThirdParty/ANGLE/src/compiler/translator/AddAndTrueToLoopCondition.h new file mode 100644 index 000000000..34debe0ed --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/AddDefaultReturnStatements.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/AddDefaultReturnStatements.cpp new file mode 100644 index 000000000..5767aea2b --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/AddDefaultReturnStatements.h b/Source/ThirdParty/ANGLE/src/compiler/translator/AddDefaultReturnStatements.h new file mode 100644 index 000000000..d765a7ae4 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.cpp new file mode 100644 index 000000000..f2891cfca --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.cpp @@ -0,0 +1,212 @@ +// +// 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 +{ + +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 ¶m : *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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.h new file mode 100644 index 000000000..983e203e6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ArrayReturnValueToOutParameter.h @@ -0,0 +1,16 @@ +// +// 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_ + +class TIntermNode; + +void ArrayReturnValueToOutParameter(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_ARRAYRETURNVALUETOOUTPARAMETER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BaseTypes.h b/Source/ThirdParty/ANGLE/src/compiler/translator/BaseTypes.h new file mode 100644 index 000000000..a3d9a6083 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BaseTypes.h @@ -0,0 +1,531 @@ +// +// 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" + +// +// 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 + EbtStruct, + EbtInterfaceBlock, + EbtAddress, // should be deprecated?? + + // end of list + EbtLast +}; + +const char* getBasicString(TBasicType t); + +inline bool IsSampler(TBasicType type) +{ + return type > EbtGuardSamplerBegin && type < EbtGuardSamplerEnd; +} + +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 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 || IsSampler(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, + + // end of list + EvqLast +}; + +inline bool IsQualifierUnspecified(TQualifier qualifier) +{ + return (qualifier == EvqTemporary || qualifier == EvqGlobal); +} + +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; + + static TLayoutQualifier create() + { + TLayoutQualifier layoutQualifier; + + layoutQualifier.location = -1; + layoutQualifier.locationsSpecified = 0; + layoutQualifier.matrixPacking = EmpUnspecified; + layoutQualifier.blockStorage = EbsUnspecified; + + layoutQualifier.localSize.fill(-1); + + return layoutQualifier; + } + + bool isEmpty() const + { + return location == -1 && matrixPacking == EmpUnspecified && + blockStorage == EbsUnspecified && !localSize.isAnyValueSet(); + } + + bool isCombinationValid() const + { + bool workSizeSpecified = localSize.isAnyValueSet(); + bool otherLayoutQualifiersSpecified = + (location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified); + + // 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); + } +}; + +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"; + 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"; + } +} + +#endif // COMPILER_TRANSLATOR_BASETYPES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp new file mode 100644 index 000000000..aada0fac7 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BreakVariableAliasingInInnerLoops.cpp @@ -0,0 +1,106 @@ +// +// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// BreakVariableAliasingInInnerLoops.h: To optimize simple assignments, the HLSL compiler frontend +// may record a variable as aliasing another. Sometimes the alias information gets garbled +// so we work around this issue by breaking the aliasing chain in inner loops. + +#include "BreakVariableAliasingInInnerLoops.h" + +#include "compiler/translator/IntermNode.h" + +// A HLSL compiler developer gave us more details on the root cause and the workaround needed: +// The root problem is that if the HLSL compiler is applying aliasing information even on +// incomplete simulations (in this case, a single pass). The bug is triggered by an assignment +// that comes from a series of assignments, possibly with swizzled or ternary operators with +// known conditionals, where the source is before the loop. +// So, a workaround is to add a +0 term to variables the first time they are assigned to in +// an inner loop (if they are declared in an outside scope, otherwise there is no need). +// This will break the aliasing chain. + +// For simplicity here we add a +0 to any assignment that is in at least two nested loops. Because +// the bug only shows up with swizzles, and ternary assignment, whole array or whole structure +// assignment don't need a workaround. + +namespace +{ + +class AliasingBreaker : public TIntermTraverser +{ + public: + AliasingBreaker() : TIntermTraverser(true, false, true) {} + + protected: + bool visitBinary(Visit visit, TIntermBinary *binary) + { + if (visit != PreVisit) + { + return false; + } + + if (mLoopLevel < 2 || !binary->isAssignment()) + { + return true; + } + + TIntermTyped *B = binary->getRight(); + TType type = B->getType(); + + if (!type.isScalar() && !type.isVector() && !type.isMatrix()) + { + return true; + } + + if (type.isArray() || IsSampler(type.getBasicType())) + { + return true; + } + + // We have a scalar / vector / matrix assignment with loop depth 2. + // Transform it from + // A = B + // to + // A = (B + typeof<B>(0)); + + TIntermBinary *bPlusZero = new TIntermBinary(EOpAdd, B, TIntermTyped::CreateZero(type)); + bPlusZero->setLine(B->getLine()); + + binary->replaceChildNode(B, bPlusZero); + + return true; + } + + bool visitLoop(Visit visit, TIntermLoop *loop) + { + if (visit == PreVisit) + { + mLoopLevel++; + } + else + { + ASSERT(mLoopLevel > 0); + mLoopLevel--; + } + + return true; + } + + private: + int mLoopLevel = 0; +}; + +} // anonymous namespace + +namespace sh +{ + +void BreakVariableAliasingInInnerLoops(TIntermNode *root) +{ + AliasingBreaker breaker; + root->traverse(&breaker); +} + +} // namespace sh diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BreakVariableAliasingInInnerLoops.h b/Source/ThirdParty/ANGLE/src/compiler/translator/BreakVariableAliasingInInnerLoops.h new file mode 100644 index 000000000..b1d906f91 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.cpp new file mode 100644 index 000000000..483fb4467 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.cpp @@ -0,0 +1,244 @@ +// +// 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" + +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 ¶m) +{ + return SetFunctionCalled(FunctionId(op, ¶m)); +} + +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2) +{ + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2)); +} + +bool BuiltInFunctionEmulator::SetFunctionCalled(TOperator op, + const TType ¶m1, const TType ¶m2, const TType ¶m3) +{ + return SetFunctionCalled(FunctionId(op, ¶m1, ¶m2, ¶m3)); +} + +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)); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.h b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.h new file mode 100644 index 000000000..6976edfd5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulator.h @@ -0,0 +1,85 @@ +// +// 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" + +// +// 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 ¶m); + bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2); + bool SetFunctionCalled(TOperator op, const TType ¶m1, const TType ¶m2, const TType ¶m3); + + 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; +}; + +#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATOR_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp new file mode 100644 index 000000000..0c8e2d9cf --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.cpp @@ -0,0 +1,250 @@ +// +// 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" + +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 + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h new file mode 100644 index 000000000..2bc5f2c88 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorGLSL.h @@ -0,0 +1,32 @@ +// +// 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" + +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); + +#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp new file mode 100644 index 000000000..988f1e2bf --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.cpp @@ -0,0 +1,494 @@ +// +// 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" + +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"); + +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.h new file mode 100644 index 000000000..f47449dfb --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/BuiltInFunctionEmulatorHLSL.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. +// + +#ifndef COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_ +#define COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_ + +#include "GLSLANG/ShaderLang.h" + +class BuiltInFunctionEmulator; + +void InitBuiltInFunctionEmulatorForHLSL(BuiltInFunctionEmulator *emu); + +// +// This works around isnan() bug on some Intel drivers. +// +void InitBuiltInIsnanFunctionEmulatorForHLSLWorkarounds(BuiltInFunctionEmulator *emu, + int targetGLSLVersion); + +#endif // COMPILER_TRANSLATOR_BUILTINFUNCTIONEMULATORHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.cpp new file mode 100644 index 000000000..57a43700c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.cpp @@ -0,0 +1,100 @@ +// +// 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 +{ + +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(); + UNUSED_ASSERTION_VARIABLE(MaxEnumValue); + + // 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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.h new file mode 100644 index 000000000..1d2abb77e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Cache.h @@ -0,0 +1,90 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_CACHE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.cpp new file mode 100644 index 000000000..9810fa935 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.cpp @@ -0,0 +1,343 @@ +// +// 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" + +// 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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.h b/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.h new file mode 100644 index 000000000..9bcda070c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/CallDAG.h @@ -0,0 +1,75 @@ +// +// 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" + + +// 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; +}; + +#endif // COMPILER_TRANSLATOR_CALLDAG_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/CodeGen.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/CodeGen.cpp new file mode 100644 index 000000000..f099bccf1 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/CodeGen.cpp @@ -0,0 +1,76 @@ +// +// 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 + +#ifdef ANGLE_ENABLE_GLSL +#include "compiler/translator/TranslatorGLSL.h" +#endif + +#ifdef ANGLE_ENABLE_HLSL +#include "compiler/translator/TranslatorHLSL.h" +#endif // ANGLE_ENABLE_HLSL + +// +// 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 ShConstructCompiler 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 ShConstructCompiler 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 ShConstructCompiler API. + return nullptr; +#endif // ANGLE_ENABLE_HLSL + default: + // Unknown format. Return NULL per the ShConstructCompiler API. + return nullptr; + } +} + +// +// Delete the compiler made by ConstructCompiler +// +void DeleteCompiler(TCompiler* compiler) +{ + delete compiler; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/Common.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Common.h index 46f9440ff..60223232a 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/Common.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Common.h @@ -4,15 +4,19 @@ // found in the LICENSE file. // -#ifndef _COMMON_INCLUDED_ -#define _COMMON_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_COMMON_H_ +#define COMPILER_TRANSLATOR_COMMON_H_ #include <map> #include <sstream> #include <string> #include <vector> +#include <limits> +#include <stdio.h> -#include "compiler/PoolAlloc.h" +#include "common/angleutils.h" +#include "common/debug.h" +#include "compiler/translator/PoolAlloc.h" struct TSourceLoc { int first_file; @@ -56,22 +60,36 @@ inline TString* NewPoolTString(const char* s) // // 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 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; +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) {} }; -#endif // _COMMON_INCLUDED_ +// 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; +} + +#endif // COMPILER_TRANSLATOR_COMMON_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.cpp new file mode 100644 index 000000000..eb5dd585c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.cpp @@ -0,0 +1,979 @@ +// +// 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/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 +{ + +#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); +} + +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); + + // 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 scalarizer( + shaderType, fragmentPrecisionHigh); + root->traverse(&scalarizer); + } + + 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.setBasicType(EbtInt); + integer.initializeSizeForScalarTypes(); + integer.array = false; + + TPublicType floatingPoint; + floatingPoint.setBasicType(EbtFloat); + floatingPoint.initializeSizeForScalarTypes(); + floatingPoint.array = false; + + 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.initializeSizeForScalarTypes(); + sampler.setBasicType(samplerType); + sampler.array = false; + 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::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); +} + +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); +} + +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); +} + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.h new file mode 100644 index 000000000..823832bcb --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.h @@ -0,0 +1,260 @@ +// +// 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" + +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); + +// +// 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); + // Collect info for all attribs, uniforms, varyings. + void collectVariables(TIntermNode* root); + // Add emulated functions to the built-in function emulator. + virtual void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, + ShCompileOptions compileOptions){}; + // 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 shouldCollectVariables(ShCompileOptions compileOptions) + { + return (compileOptions & SH_VARIABLES) != 0; + } + + virtual bool shouldFlattenPragmaStdglInvariantAll() = 0; + + 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; + bool variablesCollected; + + 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); + + // 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*); + +#endif // COMPILER_TRANSLATOR_COMPILER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.cpp new file mode 100644 index 000000000..b4074bf77 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.cpp @@ -0,0 +1,622 @@ +// +// 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 +{ + +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. + bool extendSignBit = false; + int lhsSafe = lhs.iConst; + 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(rhs.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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.h new file mode 100644 index 000000000..eec0f0ff9 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ConstantUnion.h @@ -0,0 +1,86 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_CONSTANTUNION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.cpp new file mode 100644 index 000000000..067e6cea3 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.cpp @@ -0,0 +1,189 @@ +// +// 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 +{ + +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()->getAsAggregate()->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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.h b/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.h new file mode 100644 index 000000000..85e55d9c6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/DeferGlobalInitializers.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. +// +// 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; + +void DeferGlobalInitializers(TIntermBlock *root); + +#endif // COMPILER_TRANSLATOR_DEFERGLOBALINITIALIZERS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/Diagnostics.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Diagnostics.cpp index 8a38c41a6..6ba4679c4 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/Diagnostics.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Diagnostics.cpp @@ -1,14 +1,15 @@ // -// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. +// 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/Diagnostics.h" +#include "compiler/translator/Diagnostics.h" -#include "compiler/debug.h" -#include "compiler/InfoSink.h" +#include "common/debug.h" #include "compiler/preprocessor/SourceLocation.h" +#include "compiler/translator/Common.h" +#include "compiler/translator/InfoSink.h" TDiagnostics::TDiagnostics(TInfoSink& infoSink) : mInfoSink(infoSink), @@ -30,11 +31,11 @@ void TDiagnostics::writeInfo(Severity severity, TPrefixType prefix = EPrefixNone; switch (severity) { - case ERROR: + case PP_ERROR: ++mNumErrors; prefix = EPrefixError; break; - case WARNING: + case PP_WARNING: ++mNumWarnings; prefix = EPrefixWarning; break; @@ -50,9 +51,26 @@ void TDiagnostics::writeInfo(Severity severity, sink << "'" << token << "' : " << reason << " " << extra << "\n"; } -void TDiagnostics::writeDebug(const std::string& str) +void TDiagnostics::error(const TSourceLoc &loc, + const char *reason, + const char *token, + const char *extraInfo) { - mInfoSink.debug << str; + 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, diff --git a/Source/ThirdParty/ANGLE/src/compiler/Diagnostics.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Diagnostics.h index cb71bb120..bb521da1a 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/Diagnostics.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Diagnostics.h @@ -1,21 +1,23 @@ // -// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. +// 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_DIAGNOSTICS_H_ -#define COMPILER_DIAGNOSTICS_H_ +#ifndef COMPILER_TRANSLATOR_DIAGNOSTICS_H_ +#define COMPILER_TRANSLATOR_DIAGNOSTICS_H_ +#include "common/angleutils.h" #include "compiler/preprocessor/DiagnosticsBase.h" class TInfoSink; +struct TSourceLoc; -class TDiagnostics : public pp::Diagnostics +class TDiagnostics : public pp::Diagnostics, angle::NonCopyable { public: TDiagnostics(TInfoSink& infoSink); - virtual ~TDiagnostics(); + ~TDiagnostics() override; TInfoSink& infoSink() { return mInfoSink; } @@ -28,12 +30,14 @@ class TDiagnostics : public pp::Diagnostics const std::string& token, const std::string& extra); - void writeDebug(const std::string& str); + 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: - virtual void print(ID id, - const pp::SourceLocation& loc, - const std::string& text); + void print(ID id, const pp::SourceLocation &loc, const std::string &text) override; private: TInfoSink& mInfoSink; @@ -41,4 +45,4 @@ class TDiagnostics : public pp::Diagnostics int mNumWarnings; }; -#endif // COMPILER_DIAGNOSTICS_H_ +#endif // COMPILER_TRANSLATOR_DIAGNOSTICS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.cpp new file mode 100644 index 000000000..f8081ecd6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.cpp @@ -0,0 +1,197 @@ +// +// 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" + +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"); + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.h b/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.h new file mode 100644 index 000000000..00eb49114 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/DirectiveHandler.h @@ -0,0 +1,53 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.cpp new file mode 100644 index 000000000..e34e1dc1c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.cpp @@ -0,0 +1,132 @@ +// +// 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 +{ + +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; + } + } + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.h b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.h new file mode 100644 index 000000000..627c4c67f --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulateGLFragColorBroadcast.h @@ -0,0 +1,29 @@ +// +// 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<sh::OutputVariable> *outputVariables); + +#endif // COMPILER_TRANSLATOR_EMULATEGLFRAGCOLORBROADCAST_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.cpp new file mode 100644 index 000000000..a8f6d642e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.cpp @@ -0,0 +1,718 @@ +// +// 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 +{ + +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::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 EOpDeclaration: + // Variable declaration. + if (visit == PreVisit) + { + mDeclaringVariables = true; + } + else if (visit == InVisit) + { + mDeclaringVariables = true; + } + else + { + mDeclaringVariables = 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 || + IsGLSL130OrNewer(outputLanguage)); + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.h b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.h new file mode 100644 index 000000000..f23e40be7 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/EmulatePrecision.h @@ -0,0 +1,66 @@ +// +// 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. + +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; + + 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; +}; + +#endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ExpandIntegerPowExpressions.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ExpandIntegerPowExpressions.cpp new file mode 100644 index 000000000..9409c010f --- /dev/null +++ b/Source/ThirdParty/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); + + TIntermAggregate *init = createTempInitDeclaration(lhs); + TIntermTyped *current = createTempSymbol(lhs->getType()); + + insertStatementInParentBlock(init); + + // Create a chain of n-1 multiples. + for (int i = 1; i < n; ++i) + { + TIntermBinary *mul = new TIntermBinary(EOpMul, current, createTempSymbol(lhs->getType())); + 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/Source/ThirdParty/ANGLE/src/compiler/translator/ExpandIntegerPowExpressions.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ExpandIntegerPowExpressions.h new file mode 100644 index 000000000..8bc8c9664 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/ExtensionBehavior.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionBehavior.h index 5c1595fb2..782c1c921 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/ExtensionBehavior.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionBehavior.h @@ -4,8 +4,8 @@ // found in the LICENSE file. // -#ifndef _EXTENSION_BEHAVIOR_INCLUDED_ -#define _EXTENSION_BEHAVIOR_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_ +#define COMPILER_TRANSLATOR_EXTENSIONBEHAVIOR_H_ #include <map> #include <string> @@ -34,4 +34,10 @@ inline const char* getBehaviorString(TBehavior b) // Mapping between extension name and behavior. typedef std::map<std::string, TBehavior> TExtensionBehavior; -#endif // _EXTENSION_TABLE_INCLUDED_ +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/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.cpp new file mode 100644 index 000000000..d7f45f7ee --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.cpp @@ -0,0 +1,100 @@ +// +// 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" + +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; + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.h new file mode 100644 index 000000000..6bb84d612 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ExtensionGLSL.h @@ -0,0 +1,39 @@ +// +// 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" + +// 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; +}; + +#endif // COMPILER_TRANSLATOR_EXTENSIONGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/FlagStd140Structs.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/FlagStd140Structs.cpp new file mode 100644 index 000000000..a751b768b --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/FlagStd140Structs.h b/Source/ThirdParty/ANGLE/src/compiler/translator/FlagStd140Structs.h new file mode 100644 index 000000000..cfcd775af --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.cpp new file mode 100644 index 000000000..4cc1c26a1 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.cpp @@ -0,0 +1,97 @@ +// +// 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" + +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()->getAsAggregate()->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(); + } + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.h new file mode 100644 index 000000000..9c49ecad3 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ForLoopUnroll.h @@ -0,0 +1,53 @@ +// +// 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" + +// 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; +}; + +#endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/HashNames.h b/Source/ThirdParty/ANGLE/src/compiler/translator/HashNames.h index d2141e2d8..09c959f9d 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/HashNames.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/HashNames.h @@ -4,16 +4,15 @@ // found in the LICENSE file. // -#ifndef COMPILER_HASH_NAMES_H_ -#define COMPILER_HASH_NAMES_H_ +#ifndef COMPILER_TRANSLATOR_HASHNAMES_H_ +#define COMPILER_TRANSLATOR_HASHNAMES_H_ #include <map> -#include "compiler/intermediate.h" -#include "GLSLANG/ShaderLang.h" +#include "compiler/translator/IntermNode.h" #define HASHED_NAME_PREFIX "webgl_" typedef std::map<TPersistString, TPersistString> NameMap; -#endif // COMPILER_HASH_NAMES_H_ +#endif // COMPILER_TRANSLATOR_HASHNAMES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/InfoSink.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/InfoSink.cpp index d20a6c017..cd59658ff 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InfoSink.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InfoSink.cpp @@ -4,7 +4,7 @@ // found in the LICENSE file. // -#include "compiler/InfoSink.h" +#include "compiler/translator/InfoSink.h" void TInfoSinkBase::prefix(TPrefixType p) { switch(p) { diff --git a/Source/ThirdParty/ANGLE/src/compiler/InfoSink.h b/Source/ThirdParty/ANGLE/src/compiler/translator/InfoSink.h index be0ddffe3..f47fafa8e 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InfoSink.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InfoSink.h @@ -4,12 +4,12 @@ // found in the LICENSE file. // -#ifndef _INFOSINK_INCLUDED_ -#define _INFOSINK_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_INFOSINK_H_ +#define COMPILER_TRANSLATOR_INFOSINK_H_ #include <math.h> #include <stdlib.h> -#include "compiler/Common.h" +#include "compiler/translator/Common.h" // Returns the fractional part of the given floating-point number. inline float fractionalPart(float f) { @@ -113,4 +113,4 @@ public: TInfoSinkBase obj; }; -#endif // _INFOSINK_INCLUDED_ +#endif // COMPILER_TRANSLATOR_INFOSINK_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.cpp new file mode 100644 index 000000000..759834f85 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.cpp @@ -0,0 +1,736 @@ +// +// 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" + +void InsertBuiltInFunctions(sh::GLenum type, ShShaderSpec spec, const ShBuiltInResources &resources, TSymbolTable &symbolTable) +{ + 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); + + // + // 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; + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.h new file mode 100644 index 000000000..c43ce3417 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Initialize.h @@ -0,0 +1,29 @@ +// +// 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" + +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); + +#endif // COMPILER_TRANSLATOR_INITIALIZE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeDll.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeDll.cpp index 6c7f27fce..713965389 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeDll.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeDll.cpp @@ -4,11 +4,14 @@ // found in the LICENSE file. // -#include "compiler/InitializeDll.h" +#include "compiler/translator/Cache.h" +#include "compiler/translator/InitializeDll.h" +#include "compiler/translator/InitializeGlobals.h" +#include "compiler/translator/InitializeParseContext.h" -#include "compiler/InitializeGlobals.h" -#include "compiler/InitializeParseContext.h" -#include "compiler/osinclude.h" +#include "common/platform.h" + +#include <assert.h> bool InitProcess() { @@ -22,6 +25,8 @@ bool InitProcess() return false; } + TCache::initialize(); + return true; } @@ -29,4 +34,5 @@ void DetachProcess() { FreeParseContextIndex(); FreePoolIndex(); + TCache::destroy(); } diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeDll.h b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeDll.h index 43070cc3f..4c400760f 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeDll.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeDll.h @@ -3,11 +3,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -#ifndef __INITIALIZEDLL_H -#define __INITIALIZEDLL_H +#ifndef COMPILER_TRANSLATOR_INITIALIZEDLL_H_ +#define COMPILER_TRANSLATOR_INITIALIZEDLL_H_ bool InitProcess(); void DetachProcess(); -#endif // __INITIALIZEDLL_H +#endif // COMPILER_TRANSLATOR_INITIALIZEDLL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeGlobals.h b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeGlobals.h index 071594142..8c65cb28d 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeGlobals.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeGlobals.h @@ -4,10 +4,10 @@ // found in the LICENSE file. // -#ifndef __INITIALIZE_GLOBALS_INCLUDED_ -#define __INITIALIZE_GLOBALS_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_ +#define COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_ bool InitializePoolIndex(); void FreePoolIndex(); -#endif // __INITIALIZE_GLOBALS_INCLUDED_ +#endif // COMPILER_TRANSLATOR_INITIALIZEGLOBALS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeParseContext.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeParseContext.cpp new file mode 100644 index 000000000..c35cedb34 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeParseContext.cpp @@ -0,0 +1,42 @@ +// +// 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> + +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)); +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/InitializeParseContext.h b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeParseContext.h index bffbab87d..70dac702e 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/InitializeParseContext.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeParseContext.h @@ -4,14 +4,14 @@ // found in the LICENSE file. // -#ifndef __INITIALIZE_PARSE_CONTEXT_INCLUDED_ -#define __INITIALIZE_PARSE_CONTEXT_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_ +#define COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_ bool InitializeParseContextIndex(); void FreeParseContextIndex(); -struct TParseContext; +class TParseContext; extern void SetGlobalParseContext(TParseContext* context); extern TParseContext* GetGlobalParseContext(); -#endif // __INITIALIZE_PARSE_CONTEXT_INCLUDED_ +#endif // COMPILER_TRANSLATOR_INITIALIZEPARSECONTEXT_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.cpp new file mode 100644 index 000000000..2deff0c5e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.cpp @@ -0,0 +1,105 @@ +// +// 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/util.h" + +namespace +{ + +class VariableInitializer : public TIntermTraverser +{ + public: + VariableInitializer(const InitVariableList &vars) + : TIntermTraverser(true, false, false), mVariables(vars), mCodeInserted(false) + { + } + + 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; + 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()); + TType type = sh::GetShaderVariableType(var); + + // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which + // doesn't have array assignment. + if (var.isArray()) + { + size_t pos = name.find_last_of('['); + if (pos != TString::npos) + { + name = name.substr(0, pos); + } + TType elementType = type; + elementType.clearArrayness(); + + for (unsigned int i = 0; i < var.arraySize; ++i) + { + TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type); + 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 + { + 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) +{ + VariableInitializer initializer(vars); + root->traverse(&initializer); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.h b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.h new file mode 100644 index 000000000..dce1083f6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/InitializeVariables.h @@ -0,0 +1,19 @@ +// +// 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> + +class TIntermNode; + +typedef std::vector<sh::ShaderVariable> InitVariableList; + +// This function cannot currently initialize structures containing arrays for an ESSL 1.00 backend. +void InitializeVariables(TIntermNode *root, const InitVariableList &vars); + +#endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.cpp new file mode 100644 index 000000000..0acfe40e7 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.cpp @@ -0,0 +1,2863 @@ +// +// 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 +{ + +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 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) +{ + if (statement != nullptr) + { + mStatements.push_back(statement); + } +} + +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 ¶meter, + 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); + UNUSED_ASSERTION_VARIABLE(inserted); + } + if (!insertion.insertionsBefore.empty()) + { + bool inserted = + insertion.parent->insertChildNodes(insertion.position, insertion.insertionsBefore); + ASSERT(inserted); + UNUSED_ASSERTION_VARIABLE(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); + UNUSED_ASSERTION_VARIABLE(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); + UNUSED_ASSERTION_VARIABLE(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)); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.h b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.h new file mode 100644 index 000000000..4c3703413 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNode.h @@ -0,0 +1,1175 @@ +// +// 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" + +class TDiagnostics; + +class TIntermTraverser; +class TIntermAggregate; +class TIntermBlock; +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 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(); } + 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 ¶meter, + FloatTypeUnaryFunc builtinFunc, + TConstantUnion *result) const; + + TIntermConstantUnion(const TIntermConstantUnion &node); // Note: not deleted, just private! +}; + +// +// Intermediate class for node types that hold operators. +// +class TIntermOperator : public TIntermTyped +{ + public: + TOperator getOp() const { return mOp; } + + 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; +}; + +// 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 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 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. + TIntermAggregate *createTempDeclaration(const TType &type); + // Create a node that initializes the current temporary symbol with initializer having the given qualifier. + TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier); + // Create a node that initializes the current temporary symbol with initializer. + TIntermAggregate *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; +}; + +#endif // COMPILER_TRANSLATOR_INTERMNODE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.cpp new file mode 100644 index 000000000..4177b05ac --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.cpp @@ -0,0 +1,104 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.h b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.h new file mode 100644 index 000000000..3e24f8560 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermNodePatternMatcher.h @@ -0,0 +1,53 @@ +// +// 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_ + +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); +}; + +#endif diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/IntermTraverse.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermTraverse.cpp new file mode 100644 index 000000000..29f81f401 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/IntermTraverse.cpp @@ -0,0 +1,792 @@ +// +// 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" + +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 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); + node->getTypePointer()->setQualifier(qualifier); + return node; +} + +TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type) +{ + return createTempSymbol(type, EvqTemporary); +} + +TIntermAggregate *TIntermTraverser::createTempDeclaration(const TType &type) +{ + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(createTempSymbol(type)); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier) +{ + ASSERT(initializer != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + TIntermBinary *tempInit = new TIntermBinary(EOpInitialize, tempSymbol, initializer); + tempDeclaration->getSequence()->push_back(tempInit); + return tempDeclaration; +} + +TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer) +{ + return createTempInitDeclaration(initializer, EvqTemporary); +} + +TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode) +{ + ASSERT(rightNode != nullptr); + TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType()); + TIntermBinary *assignment = new TIntermBinary(EOpAssign, tempSymbol, rightNode); + 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 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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.cpp new file mode 100644 index 000000000..8e6519dd6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.cpp @@ -0,0 +1,382 @@ +// +// 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" + +//////////////////////////////////////////////////////////////////////////// +// +// 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; + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.h new file mode 100644 index 000000000..a4fb7cc9e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Intermediate.h @@ -0,0 +1,74 @@ +// +// 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" + +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 +}; + +#endif // COMPILER_TRANSLATOR_INTERMEDIATE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.cpp new file mode 100644 index 000000000..d931a18a2 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.cpp @@ -0,0 +1,211 @@ +// +// 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 +{ + +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()->getAsAggregate()->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(); +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.h b/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.h new file mode 100644 index 000000000..ec73fd0fa --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/LoopInfo.h @@ -0,0 +1,80 @@ +// +// 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" + +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(); +}; + +#endif // COMPILER_TRANSLATOR_LOOPINFO_H_ + diff --git a/Source/ThirdParty/ANGLE/src/compiler/MMap.h b/Source/ThirdParty/ANGLE/src/compiler/translator/MMap.h index a30867151..fca843992 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/MMap.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/MMap.h @@ -4,8 +4,8 @@ // found in the LICENSE file. // -#ifndef _MMAP_INCLUDED_ -#define _MMAP_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_MMAP_H_ +#define COMPILER_TRANSLATOR_MMAP_H_ // // Encapsulate memory mapped files @@ -53,4 +53,4 @@ private: char* fBuff; // the actual data; }; -#endif // _MMAP_INCLUDED_ +#endif // COMPILER_TRANSLATOR_MMAP_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/NodeSearch.h b/Source/ThirdParty/ANGLE/src/compiler/translator/NodeSearch.h index 27e471dbb..b13b1baab 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/NodeSearch.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/NodeSearch.h @@ -6,8 +6,10 @@ // NodeSearch.h: Utilities for searching translator node graphs // -#ifndef TRANSLATOR_NODESEARCH_H_ -#define TRANSLATOR_NODESEARCH_H_ +#ifndef COMPILER_TRANSLATOR_NODESEARCH_H_ +#define COMPILER_TRANSLATOR_NODESEARCH_H_ + +#include "compiler/translator/IntermNode.h" namespace sh { @@ -17,7 +19,8 @@ class NodeSearchTraverser : public TIntermTraverser { public: NodeSearchTraverser() - : mFound(false) + : TIntermTraverser(true, false, false), + mFound(false) {} bool found() const { return mFound; } @@ -51,28 +54,6 @@ class FindDiscard : public NodeSearchTraverser<FindDiscard> } }; -class FindSideEffectRewriting : public NodeSearchTraverser<FindSideEffectRewriting> -{ - public: - virtual bool visitBinary(Visit visit, TIntermBinary *node) - { - switch (node->getOp()) - { - case EOpLogicalOr: - case EOpLogicalAnd: - if (node->getRight()->hasSideEffects()) - { - mFound = true; - } - break; - - default: break; - } - - return !mFound; - } -}; - } -#endif // TRANSLATOR_NODESEARCH_H_ +#endif // COMPILER_TRANSLATOR_NODESEARCH_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Operator.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Operator.cpp new file mode 100644 index 000000000..0b693e2d0 --- /dev/null +++ b/Source/ThirdParty/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; + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Operator.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Operator.h new file mode 100644 index 000000000..42c5c6322 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Operator.h @@ -0,0 +1,230 @@ +// +// 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 + + EOpDeclaration, + 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/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.cpp new file mode 100644 index 000000000..77e0a8fb3 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.cpp @@ -0,0 +1,38 @@ +// +// 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" + +TOutputESSL::TOutputESSL(TInfoSinkBase &objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap &nameMap, + TSymbolTable &symbolTable, + int shaderVersion, + bool forceHighp) + : TOutputGLSLBase(objSink, + clampingStrategy, + hashFunction, + nameMap, + symbolTable, + shaderVersion, + SH_ESSL_OUTPUT), + 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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.h new file mode 100644 index 000000000..c5a963499 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputESSL.h @@ -0,0 +1,30 @@ +// +// 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" + +class TOutputESSL : public TOutputGLSLBase +{ +public: + TOutputESSL(TInfoSinkBase& objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap& nameMap, + TSymbolTable& symbolTable, + int shaderVersion, + bool forceHighp); + +protected: + bool writeVariablePrecision(TPrecision precision) override; + +private: + bool mForceHighp; +}; + +#endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.cpp new file mode 100644 index 000000000..431425020 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.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/OutputGLSL.h" + +TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap& nameMap, + TSymbolTable& symbolTable, + int shaderVersion, + ShShaderOutput output) + : TOutputGLSLBase(objSink, + clampingStrategy, + hashFunction, + nameMap, + symbolTable, + shaderVersion, + output) +{ +} + +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" && IsGLSL130OrNewer(getShaderOutput())) + { + out << "webgl_FragColor"; + } + else if (symbol == "gl_FragData" && 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 = (IsGLSL130OrNewer(getShaderOutput())) ? + legacyToCoreRename : simpleRename; + + for (int i = 0; mapping[i] != NULL; i += 2) + { + if (name == mapping[i]) + { + return mapping[i+1]; + } + } + + return name; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.h new file mode 100644 index 000000000..9b1aca4ea --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSL.h @@ -0,0 +1,29 @@ +// +// 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" + +class TOutputGLSL : public TOutputGLSLBase +{ + public: + TOutputGLSL(TInfoSinkBase& objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap& nameMap, + TSymbolTable& symbolTable, + int shaderVersion, + ShShaderOutput output); + + protected: + bool writeVariablePrecision(TPrecision) override; + void visitSymbol(TIntermSymbol *node) override; + TString translateTextureFunction(TString &name) override; +}; + +#endif // COMPILER_TRANSLATOR_OUTPUTGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.cpp new file mode 100644 index 000000000..430155961 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.cpp @@ -0,0 +1,1264 @@ +// +// 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 +{ +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; +} + +} // namespace + +TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase &objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap &nameMap, + TSymbolTable &symbolTable, + int shaderVersion, + ShShaderOutput output) + : TIntermTraverser(true, true, true), + mObjSink(objSink), + mDeclaringVariables(false), + mClampingStrategy(clampingStrategy), + mHashFunction(hashFunction), + mNameMap(nameMap), + mSymbolTable(symbolTable), + mShaderVersion(shaderVersion), + mOutput(output) +{ +} + +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 (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) + { + const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); + if (layoutQualifier.location >= 0) + { + TInfoSinkBase &out = objSink(); + out << "layout(location = " << layoutQualifier.location << ") "; + } + } +} + +void TOutputGLSLBase::writeVariableType(const TType &type) +{ + TInfoSinkBase &out = objSink(); + if (type.isInvariant()) + { + out << "invariant "; + } + if (type.getBasicType() == EbtInterfaceBlock) + { + TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); + declareInterfaceBlockLayout(interfaceBlock); + } + TQualifier qualifier = type.getQualifier(); + if (qualifier != EvqTemporary && qualifier != EvqGlobal) + { + if (IsGLSL130OrNewer(mOutput)) + { + switch (qualifier) + { + case EvqAttribute: + out << "in "; + break; + case EvqVaryingIn: + out << "in "; + break; + case EvqVaryingOut: + out << "out "; + break; + default: + out << type.getQualifierString() << " "; + break; + } + } + else + { + out << type.getQualifierString() << " "; + } + } + // 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); + + const TString &name = arg->getSymbol(); + if (!name.empty()) + out << " " << hashName(name); + 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(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->getSymbol()); + + 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(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(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 EOpDeclaration: + // 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; + } + 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->getSymbol()); + } + 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::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()->getAsAggregate()->getSequence(); + TIntermSymbol *indexSymbol = + (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + TString name = hashVariableName(indexSymbol->getSymbol()); + 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(type.getStruct()->name()); + else + return type.getBuiltInTypeNameString(); +} + +TString TOutputGLSLBase::hashName(const TString &name) +{ + if (mHashFunction == NULL || name.empty()) + return name; + NameMap::const_iterator it = mNameMap.find(name.c_str()); + if (it != mNameMap.end()) + return it->second.c_str(); + TString hashedName = TIntermTraverser::hash(name, mHashFunction); + mNameMap[name.c_str()] = hashedName.c_str(); + return hashedName; +} + +TString TOutputGLSLBase::hashVariableName(const TString &name) +{ + if (mSymbolTable.findBuiltIn(name, mShaderVersion) != NULL) + return name; + 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()) + return name; + else + return hashName(name); +} + +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(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(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(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(field->name()); + if (field->type()->isArray()) + out << arrayBrackets(*field->type()); + out << ";\n"; + } + out << "}"; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.h b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.h new file mode 100644 index 000000000..aa861c73c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputGLSLBase.h @@ -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. +// + +#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" + +class TOutputGLSLBase : public TIntermTraverser +{ + public: + TOutputGLSLBase(TInfoSinkBase &objSink, + ShArrayIndexClampingStrategy clampingStrategy, + ShHashFunction64 hashFunction, + NameMap &nameMap, + TSymbolTable& symbolTable, + int shaderVersion, + ShShaderOutput output); + + 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 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 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 TString &name); + // Same as hashName(), but without hashing built-in variables. + TString hashVariableName(const TString &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); + + 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; + + const int mShaderVersion; + + ShShaderOutput mOutput; +}; + +#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputHLSL.cpp new file mode 100644 index 000000000..feae684d5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputHLSL.cpp @@ -0,0 +1,2897 @@ +// +// 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 +{ + +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 + +namespace sh +{ + +OutputHLSL::OutputHLSL(sh::GLenum shaderType, + int shaderVersion, + const TExtensionBehavior &extensionBehavior, + const char *sourcePath, + ShShaderOutput outputType, + int numRenderTargets, + const std::vector<Uniform> &uniforms, + ShCompileOptions compileOptions) + : 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); + UNUSED_ASSERTION_VARIABLE(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::visitAggregate(Visit visit, TIntermAggregate *node) +{ + TInfoSinkBase &out = getInfoSink(); + + switch (node->getOp()) + { + case EOpDeclaration: + 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; + } + else if (visit == InVisit) + { + out << ", "; + } + break; + 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; +} + +bool OutputHLSL::isSingleStatement(TIntermNode *node) +{ + if (node->getAsBlock()) + { + return false; + } + + TIntermAggregate *aggregate = node->getAsAggregate(); + if (aggregate) + { + if (aggregate->getOp() == EOpDeclaration) + { + // Declaring multiple comma-separated variables must be considered multiple statements + // because each individual declaration has side effects which are visible in the next. + return false; + } + else + { + for (TIntermSequence::iterator sit = aggregate->getSequence()->begin(); sit != aggregate->getSequence()->end(); sit++) + { + if (!isSingleStatement(*sit)) + { + return false; + } + } + + return true; + } + } + + 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()) + { + TIntermAggregate *init = node->getInit()->getAsAggregate(); + + 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); + } +} + + + +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/OutputHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/OutputHLSL.h new file mode 100644 index 000000000..f296dd928 --- /dev/null +++ b/Source/ThirdParty/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*) override; + void visitRaw(TIntermRaw*) 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 *) override; + bool visitIfElse(Visit visit, TIntermIfElse *) override; + bool visitSwitch(Visit visit, TIntermSwitch *) override; + bool visitCase(Visit visit, TIntermCase *) override; + bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; + bool visitAggregate(Visit visit, TIntermAggregate*) override; + bool visitBlock(Visit visit, TIntermBlock *node) override; + bool visitLoop(Visit visit, TIntermLoop*) override; + bool visitBranch(Visit visit, TIntermBranch*) override; + + bool isSingleStatement(TIntermNode *node); + 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/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.cpp new file mode 100644 index 000000000..70c0c27cf --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.cpp @@ -0,0 +1,3965 @@ +// +// 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" + +/////////////////////////////////////////////////////////////////////// +// +// Sub- vector and matrix fields +// +//////////////////////////////////////////////////////////////////////// + +// +// 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; + } + } + } +} + +// 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 (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 (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 ¶m = 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 (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; +} + +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::checkOutParameterIsNotSampler(const TSourceLoc &line, + TQualifier qualifier, + const TType &type) +{ + if ((qualifier == EvqOut || qualifier == EvqInOut) && type.getBasicType() != EbtStruct && + IsSampler(type.getBasicType())) + { + error(line, "samplers cannot be output parameters", type.getBasicString()); + } +} + +bool TParseContext::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; +} + +// 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) + { + checkOutParameterIsNotSampler(line, typeQualifier.qualifier, *type); + } + + 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; + } + + // 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); + } +} + +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; +} + +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, + TIntermNode **intermNode) +{ + ASSERT(intermNode != nullptr); + TType type = TType(pType); + + TVariable *variable = nullptr; + if (type.isUnsizedArray()) + { + type.setArraySize(initializer->getArraySize()); + } + 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()); + *intermNode = 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); + *intermNode = nullptr; + return false; + } + } + } + + TIntermSymbol *intermSymbol = intermediate.addSymbol( + variable->getUniqueId(), variable->getName(), variable->getType(), line); + *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line); + if (*intermNode == nullptr) + { + assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + return true; + } + + return false; +} + +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.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)); + } + } +} + +TIntermAggregate *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; + + 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()); + } + + return TIntermediate::MakeAggregate(symbol, identifierOrTypeLocation); +} + +TIntermAggregate *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); + + TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation); + if (variable && symbol) + symbol->setId(variable->getUniqueId()); + + return TIntermediate::MakeAggregate(symbol, identifierLocation); +} + +TIntermAggregate *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer) +{ + mDeferredSingleDeclarationErrorCheck = false; + + singleDeclarationErrorCheck(publicType, identifierLocation); + + TIntermNode *intermNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode)) + { + // + // Build intermediate representation + // + return intermNode ? TIntermediate::MakeAggregate(intermNode, initLocation) : nullptr; + } + else + { + return nullptr; + } +} + +TIntermAggregate *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); + + // initNode will correspond to the whole of "type b[n] = initializer". + TIntermNode *initNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode)) + { + return initNode ? TIntermediate::MakeAggregate(initNode, initLocation) : nullptr; + } + else + { + return nullptr; + } +} + +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); + + 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; +} + +TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier) +{ + // If the declaration starting this declarator list was empty (example: int,), some checks were + // 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()); + + return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); +} + +TIntermAggregate *TParseContext::parseArrayDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &arrayLocation, + TIntermTyped *indexExpression) +{ + // 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()); + + return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation); + } + + return nullptr; +} + +TIntermAggregate *TParseContext::parseInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer) +{ + // 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); + + TIntermNode *intermNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode)) + { + // + // build the intermediate representation + // + if (intermNode) + { + return intermediate.growAggregate(aggregateDeclaration, intermNode, initLocation); + } + else + { + return aggregateDeclaration; + } + } + else + { + return nullptr; + } +} + +TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer) +{ + // 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". + TIntermNode *initNode = nullptr; + if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode)) + { + if (initNode) + { + return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation); + } + else + { + return aggregateDeclaration; + } + } + else + { + return nullptr; + } +} + +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; + } + + 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 ¶m = 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 ¶m = (*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 is not involved as well... + checkIsNotSampler(location, type.typeSpecifierNonArray, + "samplers can't be function return values"); + if (mShaderVersion < 300) + { + // Array return values are forbidden, but there's also no valid syntax for declaring array + // return values in ESSL 1.00. + ASSERT(type.arraySize == 0 || mDiagnostics.numErrors() > 0); + + if (type.isStructureContainingArrays()) + { + // 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()) + { + type.setArraySize(static_cast<unsigned int>(fnCall->getParamCount())); + } + bool constType = true; + for (size_t i = 0; i < fnCall->getParamCount(); ++i) + { + const TConstParameter ¶m = 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 +// +TIntermAggregate *TParseContext::addInterfaceBlock( + const TTypeQualifierBuilder &typeQualifierBuilder, + const TSourceLoc &nameLine, + const TString &blockName, + TFieldList *fieldList, + const TString *instanceName, + const TSourceLoc &instanceLine, + TIntermTyped *arrayIndex, + const TSourceLoc &arrayIndexLine) +{ + checkIsNotReserved(nameLine, blockName); + + TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); + + if (typeQualifier.qualifier != EvqUniform) + { + error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier), + "interface blocks must be uniform"); + } + + if (typeQualifier.invariant) + { + error(typeQualifier.line, "invalid qualifier on interface block member", "invariant"); + } + + TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier; + checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier); + + if (blockLayoutQualifier.matrixPacking == EmpUnspecified) + { + blockLayoutQualifier.matrixPacking = mDefaultMatrixPacking; + } + + if (blockLayoutQualifier.blockStorage == EbsUnspecified) + { + blockLayoutQualifier.blockStorage = mDefaultBlockStorage; + } + + checkWorkGroupSizeIsNotSpecified(nameLine, blockLayoutQualifier); + + 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"); + } + + 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(); + } + + TIntermAggregate *aggregate = TIntermediate::MakeAggregate( + intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line), + nameLine); + aggregate->setOp(EOpDeclaration); + + exitStructDeclaration(); + return aggregate; +} + +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; +} + +namespace +{ +const int kWebGLMaxStructNesting = 4; + +} // namespace + +void TParseContext::checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field) +{ + if (!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 (IsWebGLBasedSpec(mShaderSpec)) + { + error(qualifierTypeLine, "Only std140 layout is allowed in WebGL", "shared"); + } + qualifier.blockStorage = EbsShared; + } + else if (qualifierType == "packed") + { + if (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 + { + 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->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->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"); + } + + 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() || IsSampler(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; + } + 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->isMatrix() || left->isVector()) + { + return nullptr; + } + 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; +} + +TIntermTyped *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()); + } + } + } + } +} + +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); + } + + 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; + } + // 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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.h new file mode 100644 index 000000000..6ca3a3cd5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ParseContext.h @@ -0,0 +1,450 @@ +// +// 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" + +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) + : 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(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); + } + + 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); + void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType); + void checkLocationIsNotSpecified(const TSourceLoc &location, + const TLayoutQualifier &layoutQualifier); + void checkOutParameterIsNotSampler(const TSourceLoc &line, + TQualifier qualifier, + const TType &type); + void checkIsParameterQualifierValid(const TSourceLoc &line, + const TTypeQualifierBuilder &typeQualifierBuilder, + TType *type); + bool checkCanUseExtension(const TSourceLoc &line, const TString &extension); + void singleDeclarationErrorCheck(const TPublicType &publicType, + const TSourceLoc &identifierLocation); + void checkLayoutQualifierSupported(const TSourceLoc &location, + const TString &layoutQualifierName, + int versionRequired); + bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location, + const TLayoutQualifier &layoutQualifier); + + void functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall); + void checkInvariantVariableQualifier(bool invariant, + const TQualifier qualifier, + const TSourceLoc &invariantLocation); + void checkInputOutputTypeIsValidES3(const TQualifier qualifier, + const TPublicType &type, + const TSourceLoc &qualifierLocation); + + const TPragma &pragma() const { return mDirectiveHandler.pragma(); } + const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); } + bool supportsExtension(const char *extension); + bool isExtensionEnabled(const char *extension) const; + void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior); + void handlePragmaDirective(const TSourceLoc &loc, const char *name, const char *value, bool stdgl); + + bool containsSampler(const TType &type); + const TFunction* findFunction( + const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0); + bool executeInitializer(const TSourceLoc &line, + const TString &identifier, + const TPublicType &pType, + TIntermTyped *initializer, + TIntermNode **intermNode); + + TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder, + const TPublicType &typeSpecifier); + + TIntermAggregate *parseSingleDeclaration(TPublicType &publicType, + const TSourceLoc &identifierOrTypeLocation, + const TString &identifier); + TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression); + TIntermAggregate *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" + TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + TIntermAggregate *parseInvariantDeclaration(const TTypeQualifierBuilder &typeQualifierBuilder, + const TSourceLoc &identifierLoc, + const TString *identifier, + const TSymbol *symbol); + + TIntermAggregate *parseDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier); + TIntermAggregate *parseArrayDeclarator(TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &arrayLocation, + TIntermTyped *indexExpression); + TIntermAggregate *parseInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + // Parse a declarator like "a[n] = initializer" + TIntermAggregate *parseArrayInitDeclarator(const TPublicType &publicType, + TIntermAggregate *aggregateDeclaration, + const TSourceLoc &identifierLocation, + const TString &identifier, + const TSourceLoc &indexLocation, + TIntermTyped *indexExpression, + const TSourceLoc &initLocation, + TIntermTyped *initializer); + + 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); + + TIntermAggregate *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); + 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); + + TIntermTyped *addBinaryMathInternal( + TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc); + TIntermTyped *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); + +#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/PoolAlloc.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/PoolAlloc.cpp index eb993567b..3b44afe33 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/PoolAlloc.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/PoolAlloc.cpp @@ -4,73 +4,64 @@ // found in the LICENSE file. // -#include "compiler/PoolAlloc.h" +#include "compiler/translator/PoolAlloc.h" -#ifndef _MSC_VER #include <stdint.h> -#endif #include <stdio.h> +#include <assert.h> #include "common/angleutils.h" -#include "compiler/InitializeGlobals.h" -#include "compiler/osinclude.h" +#include "common/debug.h" +#include "common/platform.h" +#include "common/tls.h" +#include "compiler/translator/InitializeGlobals.h" -OS_TLSIndex PoolIndex = OS_INVALID_TLS_INDEX; +TLSIndex PoolIndex = TLS_INVALID_INDEX; bool InitializePoolIndex() { - assert(PoolIndex == OS_INVALID_TLS_INDEX); + assert(PoolIndex == TLS_INVALID_INDEX); - PoolIndex = OS_AllocTLSIndex(); - return PoolIndex != OS_INVALID_TLS_INDEX; + PoolIndex = CreateTLSIndex(); + return PoolIndex != TLS_INVALID_INDEX; } void FreePoolIndex() { - assert(PoolIndex != OS_INVALID_TLS_INDEX); + assert(PoolIndex != TLS_INVALID_INDEX); - OS_FreeTLSIndex(PoolIndex); - PoolIndex = OS_INVALID_TLS_INDEX; + DestroyTLSIndex(PoolIndex); + PoolIndex = TLS_INVALID_INDEX; } TPoolAllocator* GetGlobalPoolAllocator() { - assert(PoolIndex != OS_INVALID_TLS_INDEX); - return static_cast<TPoolAllocator*>(OS_GetTLSValue(PoolIndex)); + assert(PoolIndex != TLS_INVALID_INDEX); + return static_cast<TPoolAllocator*>(GetTLSValue(PoolIndex)); } void SetGlobalPoolAllocator(TPoolAllocator* poolAllocator) { - assert(PoolIndex != OS_INVALID_TLS_INDEX); - OS_SetTLSValue(PoolIndex, 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) : - pageSize(growthIncrement), - alignment(allocationAlignment), - freeList(0), - inUseList(0), - numCalls(0), - totalBytes(0) +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) { // - // 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; - - // // Adjust alignment to be at least pointer aligned and // power of 2. // @@ -84,6 +75,20 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : 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 // @@ -91,10 +96,14 @@ TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : 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(); @@ -111,6 +120,16 @@ TPoolAllocator::~TPoolAllocator() 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 @@ -151,14 +170,18 @@ void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, co void TPoolAllocator::push() { +#if !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC) tAllocState state = { currentPageOffset, inUseList }; - stack.push_back(state); - + 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 } // @@ -170,11 +193,12 @@ void TPoolAllocator::push() // void TPoolAllocator::pop() { - if (stack.size() < 1) + if (mStack.size() < 1) return; - tHeader* page = stack.back().page; - currentPageOffset = stack.back().offset; +#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 @@ -190,7 +214,14 @@ void TPoolAllocator::pop() inUseList = nextInUse; } - stack.pop_back(); + mStack.pop_back(); +#else // !defined(ANGLE_TRANSLATOR_DISABLE_POOL_ALLOC) + for (auto &alloc : mStack.back()) + { + free(alloc); + } + mStack.pop_back(); +#endif } // @@ -199,12 +230,15 @@ void TPoolAllocator::pop() // void TPoolAllocator::popAll() { - while (stack.size() > 0) + 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. // @@ -281,8 +315,27 @@ void* TPoolAllocator::allocate(size_t numBytes) 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. diff --git a/Source/ThirdParty/ANGLE/src/compiler/PoolAlloc.h b/Source/ThirdParty/ANGLE/src/compiler/translator/PoolAlloc.h index edd249c4d..f15b3e05d 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/PoolAlloc.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/PoolAlloc.h @@ -4,8 +4,8 @@ // found in the LICENSE file. // -#ifndef _POOLALLOC_INCLUDED_ -#define _POOLALLOC_INCLUDED_ +#ifndef COMPILER_TRANSLATOR_POOLALLOC_H_ +#define COMPILER_TRANSLATOR_POOLALLOC_H_ #ifdef _DEBUG #define GUARD_BLOCKS // define to enable guard block sanity checking @@ -152,7 +152,17 @@ public: // by calling pop(), and to not have to solve memory leak problems. // -protected: + // 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 { @@ -195,22 +205,24 @@ protected: } size_t pageSize; // granularity of allocation from the OS - size_t alignment; // all returned allocations will be aligned at - // this granularity, which will be a power of 2 - size_t alignmentMask; 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 stack; // stack of where to allocate from, to partition pool + tAllocStack mStack; // stack of where to allocate from, to partition pool int numCalls; // just an interesting statistic size_t totalBytes; // just an interesting statistic -private: + +#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; }; @@ -247,18 +259,13 @@ public: pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } - pool_allocator() : allocator(GetGlobalPoolAllocator()) { } - pool_allocator(TPoolAllocator& a) : allocator(&a) { } - pool_allocator(const pool_allocator<T>& p) : allocator(p.allocator) { } - - template <class Other> - pool_allocator<T>& operator=(const pool_allocator<Other>& p) { - allocator = p.allocator; - return *this; - } + pool_allocator() { } template<class Other> - pool_allocator(const pool_allocator<Other>& p) : allocator(&p.getAllocator()) { } + 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. @@ -284,17 +291,13 @@ public: 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 &getAllocator() == &rhs.getAllocator(); } - bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + 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; } - void setAllocator(TPoolAllocator* a) { allocator = a; } - TPoolAllocator& getAllocator() const { return *allocator; } - -protected: - TPoolAllocator* allocator; + TPoolAllocator& getAllocator() const { return *GetGlobalPoolAllocator(); } }; -#endif // _POOLALLOC_INCLUDED_ +#endif // COMPILER_TRANSLATOR_POOLALLOC_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Pragma.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Pragma.h new file mode 100644 index 000000000..57b113497 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.cpp new file mode 100644 index 000000000..4763dd6ed --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.cpp @@ -0,0 +1,111 @@ +// +// 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 +{ + +class PruneEmptyDeclarationsTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + PruneEmptyDeclarationsTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root) +{ + PruneEmptyDeclarationsTraverser prune; + root->traverse(&prune); + prune.updateTree(); +} + +PruneEmptyDeclarationsTraverser::PruneEmptyDeclarationsTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool PruneEmptyDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + 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; + } + return true; +} + +} // namespace + +void PruneEmptyDeclarations(TIntermNode *root) +{ + PruneEmptyDeclarationsTraverser::apply(root); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.h b/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.h new file mode 100644 index 000000000..122e83090 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/PruneEmptyDeclarations.h @@ -0,0 +1,15 @@ +// +// 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_ + +class TIntermNode; + +void PruneEmptyDeclarations(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.cpp new file mode 100644 index 000000000..30d8c4eed --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.cpp @@ -0,0 +1,646 @@ +// +// 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 +{ +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]; + } + } + + return joinedQualifier; +} +} // 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; +} + +#if defined(ANGLE_ENABLE_ASSERTS) +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]); +} +#endif + +// 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 ¤tQualifier = + 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; + } + 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 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; +} + +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; + 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 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 + +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 TPrecisionQualifierWrapper::getRank() const +{ + return 5u; +} + +TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc) + : layoutQualifier(TLayoutQualifier::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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.h b/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.h new file mode 100644 index 000000000..0bcc39a16 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/QualifierTypes.h @@ -0,0 +1,170 @@ +// +// 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" + +class TDiagnostics; + +namespace sh +{ +TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier, + TLayoutQualifier rightQualifier, + const TSourceLoc &rightQualifierLocation, + TDiagnostics *diagnostics); +} // namespace sh + +enum TQualifierType +{ + QtInvariant, + QtInterpolation, + QtLayout, + QtStorage, + QtPrecision +}; + +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 ::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 ::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 ::getPrecisionString(mPrecisionQualifier); } + TPrecision getQualifier() const { return mPrecisionQualifier; } + unsigned int getRank() const; + + private: + TPrecision mPrecisionQualifier; +}; + +// 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; + 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; +}; + +#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.cpp new file mode 100644 index 000000000..6eca229d8 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.cpp @@ -0,0 +1,162 @@ +// +// 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 +{ + +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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.h new file mode 100644 index 000000000..2cd401b41 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RecordConstantPrecision.h @@ -0,0 +1,23 @@ +// +// 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_ + +class TIntermNode; + +void RecordConstantPrecision(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_RECORDCONSTANTPRECISION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.cpp new file mode 100644 index 000000000..1a7d8aaf8 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.cpp @@ -0,0 +1,71 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.h new file mode 100644 index 000000000..a9101351e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RegenerateStructNames.h @@ -0,0 +1,41 @@ +// +// 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> + +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; +}; + +#endif // COMPILER_TRANSLATOR_REGENERATESTRUCTNAMES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.cpp new file mode 100644 index 000000000..994ae313a --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.cpp @@ -0,0 +1,508 @@ +// +// 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 +{ + +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 + TIntermAggregate *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()); + TIntermAggregate *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(); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.h new file mode 100644 index 000000000..ae3a93c9b --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveDynamicIndexing.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. +// +// 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_ + +class TIntermNode; +class TSymbolTable; + +void RemoveDynamicIndexing(TIntermNode *root, + unsigned int *temporaryIndex, + const TSymbolTable &symbolTable, + int shaderVersion); + +#endif // COMPILER_TRANSLATOR_REMOVEDYNAMICINDEXING_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.cpp new file mode 100644 index 000000000..4b0fe6948 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.cpp @@ -0,0 +1,94 @@ +// +// 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 +{ + +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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.h new file mode 100644 index 000000000..40f9d672b --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemovePow.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. +// +// 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_ + +class TIntermNode; + +void RemovePow(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.cpp new file mode 100644 index 000000000..df8ce1e1f --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.cpp @@ -0,0 +1,169 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.h new file mode 100644 index 000000000..171c51bf3 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RemoveSwitchFallThrough.h @@ -0,0 +1,45 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_REMOVESWITCHFALLTHROUGH_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.cpp new file mode 100644 index 000000000..9cc551bb7 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.cpp @@ -0,0 +1,154 @@ +// +// 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 +{ + +// 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; + TIntermAggregate *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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.h new file mode 100644 index 000000000..f6ec1caf0 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteDoWhile.h @@ -0,0 +1,16 @@ +// +// 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_ + +class TIntermNode; +void RewriteDoWhile(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_REWRITEDOWHILE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteElseBlocks.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteElseBlocks.cpp new file mode 100644 index 000000000..aafb86f1c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteElseBlocks.cpp @@ -0,0 +1,124 @@ +// +// 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(); + + TIntermTyped *typedCondition = ifElse->getCondition()->getAsTyped(); + TIntermAggregate *storeCondition = createTempInitDeclaration(typedCondition); + + 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/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteElseBlocks.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteElseBlocks.h new file mode 100644 index 000000000..24a425e66 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteTexelFetchOffset.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteTexelFetchOffset.cpp new file mode 100644 index 000000000..6d0c866f8 --- /dev/null +++ b/Source/ThirdParty/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 diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteTexelFetchOffset.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteTexelFetchOffset.h new file mode 100644 index 000000000..c6db66457 --- /dev/null +++ b/Source/ThirdParty/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_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteUnaryMinusOperatorInt.cpp new file mode 100644 index 000000000..d20580507 --- /dev/null +++ b/Source/ThirdParty/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 diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteUnaryMinusOperatorInt.h b/Source/ThirdParty/ANGLE/src/compiler/translator/RewriteUnaryMinusOperatorInt.h new file mode 100644 index 000000000..802ed5748 --- /dev/null +++ b/Source/ThirdParty/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_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp new file mode 100644 index 000000000..96789bdcf --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp @@ -0,0 +1,276 @@ +// +// 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/ScalarizeVecAndMatConstructorArgs.h" + +#include <algorithm> + +#include "angle_gl.h" +#include "common/angleutils.h" + +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)); +} + +} // namespace anonymous + +bool ScalarizeVecAndMatConstructorArgs::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 ScalarizeVecAndMatConstructorArgs::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 ScalarizeVecAndMatConstructorArgs::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); + TString varName = createTempVariable(node); + if (node->isScalar()) + { + TIntermSymbol *symbolNode = + new TIntermSymbol(-1, varName, node->getType()); + sequence->push_back(symbolNode); + 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 = + new TIntermSymbol(-1, varName, node->getType()); + TIntermBinary *newNode = ConstructVectorIndexBinaryNode( + symbolNode, index); + sequence->push_back(newNode); + } + } + else + { + TIntermSymbol *symbolNode = + new TIntermSymbol(-1, varName, 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 = + new TIntermSymbol(-1, varName, node->getType()); + TIntermBinary *newNode = ConstructMatrixIndexBinaryNode( + symbolNode, colIndex, rowIndex); + sequence->push_back(newNode); + rowIndex++; + if (rowIndex >= node->getRows()) + { + rowIndex = 0; + colIndex++; + } + repeat--; + } + } + else + { + TIntermSymbol *symbolNode = + new TIntermSymbol(-1, varName, node->getType()); + sequence->push_back(symbolNode); + size -= node->getCols() * node->getRows(); + } + } + } +} + +TString ScalarizeVecAndMatConstructorArgs::createTempVariable(TIntermTyped *original) +{ + TString tempVarName = "_webgl_tmp_"; + if (original->isScalar()) + { + tempVarName += "scalar_"; + } + else if (original->isVector()) + { + tempVarName += "vec_"; + } + else + { + ASSERT(original->isMatrix()); + tempVarName += "mat_"; + } + tempVarName += Str(mTempVarCount).c_str(); + mTempVarCount++; + + ASSERT(original); + TType type = original->getType(); + type.setQualifier(EvqTemporary); + + 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. + type.setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium); + } + + TIntermSymbol *symbolNode = new TIntermSymbol(-1, tempVarName, type); + TIntermBinary *init = new TIntermBinary(EOpInitialize, symbolNode, original); + + TIntermAggregate *decl = new TIntermAggregate(EOpDeclaration); + decl->getSequence()->push_back(init); + + ASSERT(mBlockStack.size() > 0); + TIntermSequence &sequence = mBlockStack.back(); + sequence.push_back(decl); + + return tempVarName; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h new file mode 100644 index 000000000..eac6125a6 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.h @@ -0,0 +1,49 @@ +// +// 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_SCALARIZEVECANDMATCONSTRUCTORARGS_H_ +#define COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_ + +#include "compiler/translator/IntermNode.h" + +class ScalarizeVecAndMatConstructorArgs : public TIntermTraverser +{ + public: + ScalarizeVecAndMatConstructorArgs(sh::GLenum shaderType, + bool fragmentPrecisionHigh) + : TIntermTraverser(true, false, false), + mTempVarCount(0), + mShaderType(shaderType), + mFragmentPrecisionHigh(fragmentPrecisionHigh) {} + + 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 _webgl_tmp_mat_0 = m; + // vec4 v(1, _webgl_tmp_mat_0[0][0], _webgl_tmp_mat_0[0][1], _webgl_tmp_mat_0[0][2]); + // This function is to create nodes for "mat4 _webgl_tmp_mat_0 = m;" and insert it to + // the code sequence. + // Return the temporary variable name. + TString createTempVariable(TIntermTyped *original); + + std::vector<TIntermSequence> mBlockStack; + int mTempVarCount; + + sh::GLenum mShaderType; + bool mFragmentPrecisionHigh; +}; + +#endif // COMPILER_TRANSLATOR_SCALARIZEVECANDMATCONSTRUCTORARGS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/SearchSymbol.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SearchSymbol.cpp index 9368f1a4f..cccd4d3ff 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/SearchSymbol.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SearchSymbol.cpp @@ -6,14 +6,15 @@ // SearchSymbol is an AST traverser to detect the use of a given symbol name // -#include "compiler/SearchSymbol.h" +#include "compiler/translator/SearchSymbol.h" -#include "compiler/InfoSink.h" -#include "compiler/OutputHLSL.h" +#include "compiler/translator/InfoSink.h" namespace sh { -SearchSymbol::SearchSymbol(const TString &symbol) : mSymbol(symbol) +SearchSymbol::SearchSymbol(const TString &symbol) + : TIntermTraverser(true, false, false), + mSymbol(symbol) { match = false; } diff --git a/Source/ThirdParty/ANGLE/src/compiler/SearchSymbol.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SearchSymbol.h index 224bc77c6..1e5e1700d 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/SearchSymbol.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SearchSymbol.h @@ -6,11 +6,11 @@ // SearchSymbol is an AST traverser to detect the use of a given symbol name // -#ifndef COMPILER_SEARCHSYMBOL_H_ -#define COMPILER_SEARCHSYMBOL_H_ +#ifndef COMPILER_TRANSLATOR_SEARCHSYMBOL_H_ +#define COMPILER_TRANSLATOR_SEARCHSYMBOL_H_ -#include "compiler/intermediate.h" -#include "compiler/ParseContext.h" +#include "compiler/translator/IntermNode.h" +#include "compiler/translator/ParseContext.h" namespace sh { @@ -20,7 +20,7 @@ class SearchSymbol : public TIntermTraverser SearchSymbol(const TString &symbol); void traverse(TIntermNode *node); - void visitSymbol(TIntermSymbol *symbolNode); + void visitSymbol(TIntermSymbol *symbolNode) override; bool foundMatch() const; @@ -30,4 +30,4 @@ class SearchSymbol : public TIntermTraverser }; } -#endif // COMPILER_SEARCHSYMBOL_H_ +#endif // COMPILER_TRANSLATOR_SEARCHSYMBOL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateArrayInitialization.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateArrayInitialization.cpp new file mode 100644 index 000000000..8cd1fa109 --- /dev/null +++ b/Source/ThirdParty/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 +{ + +class SeparateArrayInitTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + SeparateArrayInitTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void SeparateArrayInitTraverser::apply(TIntermNode *root) +{ + SeparateArrayInitTraverser separateInit; + root->traverse(&separateInit); + separateInit.updateTree(); +} + +SeparateArrayInitTraverser::SeparateArrayInitTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + 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; + + TIntermAggregate *replacementDeclaration = new TIntermAggregate; + replacementDeclaration->setOp(EOpDeclaration); + replacementDeclaration->getSequence()->push_back(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; + } + return true; +} + +} // namespace + +void SeparateArrayInitialization(TIntermNode *root) +{ + SeparateArrayInitTraverser::apply(root); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateArrayInitialization.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateArrayInitialization.h new file mode 100644 index 000000000..d16357a3a --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateArrayInitialization.h @@ -0,0 +1,25 @@ +// +// 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_ + +class TIntermNode; + +void SeparateArrayInitialization(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateDeclarations.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateDeclarations.cpp new file mode 100644 index 000000000..cc339ea64 --- /dev/null +++ b/Source/ThirdParty/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 +{ + +class SeparateDeclarationsTraverser : private TIntermTraverser +{ + public: + static void apply(TIntermNode *root); + private: + SeparateDeclarationsTraverser(); + bool visitAggregate(Visit, TIntermAggregate *node) override; +}; + +void SeparateDeclarationsTraverser::apply(TIntermNode *root) +{ + SeparateDeclarationsTraverser separateDecl; + root->traverse(&separateDecl); + separateDecl.updateTree(); +} + +SeparateDeclarationsTraverser::SeparateDeclarationsTraverser() + : TIntermTraverser(true, false, false) +{ +} + +bool SeparateDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node) +{ + if (node->getOp() == EOpDeclaration) + { + 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) + { + TIntermAggregate *replacementDeclaration = new TIntermAggregate; + + replacementDeclaration->setOp(EOpDeclaration); + replacementDeclaration->getSequence()->push_back(sequence->at(ii)); + replacementDeclaration->setLine(sequence->at(ii)->getLine()); + replacementDeclarations.push_back(replacementDeclaration); + } + + mMultiReplacements.push_back( + NodeReplaceWithMultipleEntry(parentBlock, node, replacementDeclarations)); + } + return false; + } + return true; +} + +} // namespace + +void SeparateDeclarations(TIntermNode *root) +{ + SeparateDeclarationsTraverser::apply(root); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateDeclarations.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateDeclarations.h new file mode 100644 index 000000000..77913ab8b --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateDeclarations.h @@ -0,0 +1,23 @@ +// +// 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_ + +class TIntermNode; + +void SeparateDeclarations(TIntermNode *root); + +#endif // COMPILER_TRANSLATOR_SEPARATEDECLARATIONS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.cpp new file mode 100644 index 000000000..44bf554e4 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.cpp @@ -0,0 +1,139 @@ +// +// 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 +{ + +// 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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.h new file mode 100644 index 000000000..b178ebb3e --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SeparateExpressionsReturningArrays.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. +// +// 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_ + +class TIntermNode; + +void SeparateExpressionsReturningArrays(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_SEPARATEEXPRESSIONSRETURNINGARRAYS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ShaderLang.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ShaderLang.cpp new file mode 100644 index 000000000..37b1114a2 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ShaderLang.cpp @@ -0,0 +1,410 @@ +// +// 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" + +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<sh::Uniform> *GetVariableList(const TCompiler *compiler) +{ + return &compiler->getUniforms(); +} + +template <> +const std::vector<sh::Varying> *GetVariableList(const TCompiler *compiler) +{ + return &compiler->getVaryings(); +} + +template <> +const std::vector<sh::Attribute> *GetVariableList(const TCompiler *compiler) +{ + return &compiler->getAttributes(); +} + +template <> +const std::vector<sh::OutputVariable> *GetVariableList(const TCompiler *compiler) +{ + return &compiler->getOutputVariables(); +} + +template <> +const std::vector<sh::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)) { + ShDestruct(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<sh::Uniform> *ShGetUniforms(const ShHandle handle) +{ + return GetShaderVariables<sh::Uniform>(handle); +} + +const std::vector<sh::Varying> *ShGetVaryings(const ShHandle handle) +{ + return GetShaderVariables<sh::Varying>(handle); +} + +const std::vector<sh::Attribute> *ShGetAttributes(const ShHandle handle) +{ + return GetShaderVariables<sh::Attribute>(handle); +} + +const std::vector<sh::OutputVariable> *ShGetOutputVariables(const ShHandle handle) +{ + return GetShaderVariables<sh::OutputVariable>(handle); +} + +const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handle) +{ + return GetShaderVariables<sh::InterfaceBlock>(handle); +} + +sh::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<sh::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 +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ShaderVars.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ShaderVars.cpp new file mode 100644 index 000000000..8e217f1c0 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.cpp new file mode 100644 index 000000000..b8cc976ea --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.cpp @@ -0,0 +1,280 @@ +// +// 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 +{ + +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()); + } + 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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.h new file mode 100644 index 000000000..b8802aa11 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SimplifyLoopConditions.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. +// +// 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_ + +class TIntermNode; +class TSymbolTable; + +void SimplifyLoopConditions(TIntermNode *root, + unsigned int conditionsToSimplify, + unsigned int *temporaryIndex, + const TSymbolTable &symbolTable, + int shaderVersion); + +#endif // COMPILER_TRANSLATOR_SIMPLIFYLOOPCONDITIONS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.cpp new file mode 100644 index 000000000..e0d28b9cd --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.cpp @@ -0,0 +1,153 @@ +// +// 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 +{ + +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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.h new file mode 100644 index 000000000..4a46fe36c --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SplitSequenceOperator.h @@ -0,0 +1,24 @@ +// +// 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_ + +class TIntermNode; +class TSymbolTable; + +void SplitSequenceOperator(TIntermNode *root, + int patternsToSplitMask, + unsigned int *temporaryIndex, + const TSymbolTable &symbolTable, + int shaderVersion); + +#endif // COMPILER_TRANSLATOR_SPLITSEQUENCEOPERATOR_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/StructureHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/StructureHLSL.cpp new file mode 100644 index 000000000..93e0ba573 --- /dev/null +++ b/Source/ThirdParty/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 ¶mType = 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 ¶mType = 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 ¶meter = 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 ¶meter = 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/Source/ThirdParty/ANGLE/src/compiler/translator/StructureHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/StructureHLSL.h new file mode 100644 index 000000000..96139ec9a --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.cpp new file mode 100644 index 000000000..aa23776b9 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.cpp @@ -0,0 +1,323 @@ +// +// 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> + +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 ¶metersSource) +{ + 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::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 (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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.h b/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.h new file mode 100644 index 000000000..51a82f724 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/SymbolTable.h @@ -0,0 +1,541 @@ +// +// 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" + +// 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 ¶metersSource); + + 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 *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; +}; + +#endif // COMPILER_TRANSLATOR_SYMBOLTABLE_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TextureFunctionHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/TextureFunctionHLSL.cpp new file mode 100644 index 000000000..33d098531 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/TextureFunctionHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/TextureFunctionHLSL.h new file mode 100644 index 000000000..68bf8c089 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.cpp new file mode 100644 index 000000000..109b1700d --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.cpp @@ -0,0 +1,109 @@ +// +// 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" + +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(), shaderVer, precisionEmulation); + 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"; + } + } + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.h new file mode 100644 index 000000000..f147ca066 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorESSL.h @@ -0,0 +1,25 @@ +// +// 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" + +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(); +}; + +#endif // COMPILER_TRANSLATOR_TRANSLATORESSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.cpp new file mode 100644 index 000000000..a0ee01f55 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.cpp @@ -0,0 +1,291 @@ +// +// 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" + +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) + { + collectVariables(root); + + 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(), + getShaderVersion(), + getOutputType()); + 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()); +} + +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"; + } +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.h new file mode 100644 index 000000000..f234c18ec --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorGLSL.h @@ -0,0 +1,30 @@ +// +// 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" + +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; + + private: + void writeVersion(TIntermNode *root); + void writeExtensionBehavior(TIntermNode *root); + void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName); +}; + +#endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.cpp new file mode 100644 index 000000000..fb011da76 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.cpp @@ -0,0 +1,143 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.h new file mode 100644 index 000000000..d2add14e3 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/TranslatorHLSL.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_TRANSLATORHLSL_H_ +#define COMPILER_TRANSLATOR_TRANSLATORHLSL_H_ + +#include "compiler/translator/Compiler.h" + +class TranslatorHLSL : public TCompiler +{ + public: + TranslatorHLSL(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); +#ifdef ANGLE_ENABLE_HLSL + TranslatorHLSL *getAsTranslatorHLSL() override { return this; } +#endif // ANGLE_ENABLE_HLSL + + 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; +}; + +#endif // COMPILER_TRANSLATOR_TRANSLATORHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Types.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/Types.cpp new file mode 100644 index 000000000..598be2871 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Types.cpp @@ -0,0 +1,487 @@ +// +// 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> + +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"; + default: UNREACHABLE(); return "unknown type"; + } +} + +TType::TType(const TPublicType &p) + : type(p.getBasicType()), + precision(p.precision), + qualifier(p.qualifier), + invariant(p.invariant), + 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 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; +} + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/Types.h b/Source/ThirdParty/ANGLE/src/compiler/translator/Types.h new file mode 100644 index 000000000..ab1b6c3ff --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/Types.h @@ -0,0 +1,715 @@ +// +// 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" + +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; + + 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), + 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), + 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), + 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), + 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), 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; } + + 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 ::getBasicString(type); + } + + const char *getPrecisionString() const + { + return ::getPrecisionString(precision); + } + const char *getQualifierString() const + { + return ::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; + } + + 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; + 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; + TQualifier qualifier; + bool invariant; + TPrecision precision; + bool array; + int arraySize; + + void initialize(const TTypeSpecifierNonArray &typeSpecifier, TQualifier q) + { + typeSpecifierNonArray = typeSpecifier; + layoutQualifier = TLayoutQualifier::create(); + qualifier = q; + 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; } + void initializeSizeForScalarTypes() + { + typeSpecifierNonArray.primarySize = 1; + typeSpecifierNonArray.secondarySize = 1; + } + + 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(); + } +}; + +#endif // COMPILER_TRANSLATOR_TYPES_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.cpp new file mode 100644 index 000000000..e57681eda --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.cpp @@ -0,0 +1,54 @@ +// +// 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 +{ + +// "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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.h b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.h new file mode 100644 index 000000000..b92a4e915 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitAST.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. +// +// 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" + +// 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; +}; + +#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUITAST_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.cpp new file mode 100644 index 000000000..712f59aaf --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.cpp @@ -0,0 +1,184 @@ +// +// 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 +{ + +// 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; + + TIntermSymbol *tempSymbol = createTempSymbol(node->getType()); + TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration); + tempDeclaration->getSequence()->push_back(tempSymbol); + 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()); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.h b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.h new file mode 100644 index 000000000..0fe37b714 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UnfoldShortCircuitToIf.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. +// +// 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_ + +class TIntermNode; + +void UnfoldShortCircuitToIf(TIntermNode *root, unsigned int *temporaryIndex); + +#endif // COMPILER_TRANSLATOR_UNFOLDSHORTCIRCUIT_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UniformHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/UniformHLSL.cpp new file mode 100644 index 000000000..3a85c60fc --- /dev/null +++ b/Source/ThirdParty/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, ®isterCount); + } + else + { + ASSERT(samplerInStructSymbolsToAPINames.find(uniform) != + samplerInStructSymbolsToAPINames.end()); + samplerArrayIndex = assignSamplerInStructUniformRegister( + type, samplerInStructSymbolsToAPINames.at(uniform), ®isterCount); + } + 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 ®isterString = + 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/Source/ThirdParty/ANGLE/src/compiler/translator/UniformHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/UniformHLSL.h new file mode 100644 index 000000000..c01b90fe0 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.cpp new file mode 100644 index 000000000..2a1a812e2 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.cpp @@ -0,0 +1,153 @@ +// +// 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/util.h" + +namespace sh +{ + +namespace +{ + +class UseUniformBlockMembers : public TIntermTraverser +{ + public: + UseUniformBlockMembers(const InterfaceBlockList &blocks) + : TIntermTraverser(true, false, false), mBlocks(blocks), mCodeInserted(false) + { + } + + 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; + 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()); + TType type = GetShaderVariableType(var); + + if (var.isArray()) + { + size_t pos = name.find_last_of('['); + if (pos != TString::npos) + { + name = name.substr(0, pos); + } + TType elementType = type; + elementType.clearArrayness(); + + TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type); + for (unsigned int i = 0; i < var.arraySize; ++i) + { + TIntermBinary *element = + new TIntermBinary(EOpIndexDirect, arraySymbol, TIntermTyped::CreateIndexNode(i)); + + sequence->insert(sequence->begin(), element); + } + } + else if (var.isStruct()) + { + TIntermSymbol *structSymbol = new TIntermSymbol(0, name, type); + for (unsigned int i = 0; i < var.fields.size(); ++i) + { + TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, structSymbol, + TIntermTyped::CreateIndexNode(i)); + + sequence->insert(sequence->begin(), element); + } + } + else + { + TIntermSymbol *symbol = new TIntermSymbol(0, name, type); + + 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) + { + TType type = GetInterfaceBlockType(block); + TString name = TString(block.instanceName.c_str()); + TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, type); + 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 + { + TType type = GetInterfaceBlockType(block); + TString name = TString(block.instanceName.c_str()); + TIntermSymbol *blockSymbol = new TIntermSymbol(0, name, type); + 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) +{ + UseUniformBlockMembers useUniformBlock(blocks); + root->traverse(&useUniformBlock); +} + +} // namespace sh diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.h b/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.h new file mode 100644 index 000000000..024bfd4b1 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/UseInterfaceBlockFields.h @@ -0,0 +1,25 @@ +// +// 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 +{ + +using InterfaceBlockList = std::vector<sh::InterfaceBlock>; + +void UseInterfaceBlockFields(TIntermNode *root, const InterfaceBlockList &blocks); +} // namespace sh + +#endif // COMPILER_TRANSLATOR_USEINTERFACEBLOCKFIELDS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/UtilsHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/UtilsHLSL.cpp new file mode 100644 index 000000000..221d5d9b5 --- /dev/null +++ b/Source/ThirdParty/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 ¶mType = 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/Source/ThirdParty/ANGLE/src/compiler/translator/UtilsHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/UtilsHLSL.h new file mode 100644 index 000000000..748b3513e --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.cpp new file mode 100644 index 000000000..2461b6a43 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.cpp @@ -0,0 +1,112 @@ +// +// 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 +{ + +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(); +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.h new file mode 100644 index 000000000..c3d2a47eb --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateGlobalInitializer.h @@ -0,0 +1,16 @@ +// +// 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_ + +class TIntermTyped; +class TParseContext; + +// Returns true if the initializer is valid. +bool ValidateGlobalInitializer(TIntermTyped *initializer, const TParseContext *context, bool *warning); + +#endif // COMPILER_TRANSLATOR_VALIDATEGLOBALINITIALIZER_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.cpp new file mode 100644 index 000000000..5dcfe69c5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.cpp @@ -0,0 +1,498 @@ +// +// 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 +{ + +// 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 + // + TIntermAggregate *decl = init->getAsAggregate(); + if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) + { + 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 ¶m = 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; +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.h new file mode 100644 index 000000000..666e38ff5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateLimitations.h @@ -0,0 +1,63 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.cpp new file mode 100644 index 000000000..00b3c9b45 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.cpp @@ -0,0 +1,35 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.h new file mode 100644 index 000000000..87916afef --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateMaxParameters.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. +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_VALIDATEMAXPARAMETERS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.cpp new file mode 100644 index 000000000..9f5f8f577 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.cpp @@ -0,0 +1,108 @@ +// +// 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 +{ +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.h new file mode 100644 index 000000000..0122ca25f --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateOutputs.h @@ -0,0 +1,36 @@ +// +// 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> + +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; +}; + +#endif // COMPILER_TRANSLATOR_VALIDATEOUTPUTS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.cpp new file mode 100644 index 000000000..8fbd8325b --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.cpp @@ -0,0 +1,210 @@ +// +// 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" + +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; +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.h b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.h new file mode 100644 index 000000000..9c6dee212 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/ValidateSwitch.h @@ -0,0 +1,55 @@ +// +// 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" + +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; +}; + +#endif // COMPILER_TRANSLATOR_VALIDATESWITCH_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/VariableInfo.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/VariableInfo.cpp new file mode 100644 index 000000000..7e1e5cd82 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/VariableInfo.cpp @@ -0,0 +1,693 @@ +// +// 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::visitAggregate(Visit, TIntermAggregate *node) +{ + bool visitChildren = true; + + switch (node->getOp()) + { + case EOpDeclaration: + { + 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); + visitChildren = 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; + } + + visitChildren = false; + } + break; + } + default: break; + } + + return visitChildren; +} + +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/Source/ThirdParty/ANGLE/src/compiler/translator/VariableInfo.h b/Source/ThirdParty/ANGLE/src/compiler/translator/VariableInfo.h new file mode 100644 index 000000000..f79035d38 --- /dev/null +++ b/Source/ThirdParty/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 visitAggregate(Visit, TIntermAggregate *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/Source/ThirdParty/ANGLE/src/compiler/VariablePacker.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/VariablePacker.cpp index 895728776..a981c8ae0 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/VariablePacker.cpp +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/VariablePacker.cpp @@ -3,122 +3,81 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -#include "compiler/VariablePacker.h" #include <algorithm> -#include "compiler/ShHandle.h" -namespace { -int GetSortOrder(ShDataType type) -{ - switch (type) { - case SH_FLOAT_MAT4: - return 0; - case SH_FLOAT_MAT2: - return 1; - case SH_FLOAT_VEC4: - case SH_INT_VEC4: - case SH_BOOL_VEC4: - return 2; - case SH_FLOAT_MAT3: - return 3; - case SH_FLOAT_VEC3: - case SH_INT_VEC3: - case SH_BOOL_VEC3: - return 4; - case SH_FLOAT_VEC2: - case SH_INT_VEC2: - case SH_BOOL_VEC2: - return 5; - case SH_FLOAT: - case SH_INT: - case SH_BOOL: - case SH_SAMPLER_2D: - case SH_SAMPLER_CUBE: - case SH_SAMPLER_EXTERNAL_OES: - case SH_SAMPLER_2D_RECT_ARB: - return 6; - default: - ASSERT(false); - return 7; - } -} -} // namespace +#include "angle_gl.h" -int VariablePacker::GetNumComponentsPerRow(ShDataType type) +#include "compiler/translator/VariablePacker.h" +#include "common/utilities.h" + +int VariablePacker::GetNumComponentsPerRow(sh::GLenum type) { - switch (type) { - case SH_FLOAT_MAT4: - case SH_FLOAT_MAT2: - case SH_FLOAT_VEC4: - case SH_INT_VEC4: - case SH_BOOL_VEC4: - return 4; - case SH_FLOAT_MAT3: - case SH_FLOAT_VEC3: - case SH_INT_VEC3: - case SH_BOOL_VEC3: - return 3; - case SH_FLOAT_VEC2: - case SH_INT_VEC2: - case SH_BOOL_VEC2: - return 2; - case SH_FLOAT: - case SH_INT: - case SH_BOOL: - case SH_SAMPLER_2D: - case SH_SAMPLER_CUBE: - case SH_SAMPLER_EXTERNAL_OES: - case SH_SAMPLER_2D_RECT_ARB: - return 1; - default: - ASSERT(false); - return 5; + 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(ShDataType type) +int VariablePacker::GetNumRows(sh::GLenum type) { - switch (type) { - case SH_FLOAT_MAT4: - return 4; - case SH_FLOAT_MAT3: - return 3; - case SH_FLOAT_MAT2: - return 2; - case SH_FLOAT_VEC4: - case SH_INT_VEC4: - case SH_BOOL_VEC4: - case SH_FLOAT_VEC3: - case SH_INT_VEC3: - case SH_BOOL_VEC3: - case SH_FLOAT_VEC2: - case SH_INT_VEC2: - case SH_BOOL_VEC2: - case SH_FLOAT: - case SH_INT: - case SH_BOOL: - case SH_SAMPLER_2D: - case SH_SAMPLER_CUBE: - case SH_SAMPLER_EXTERNAL_OES: - case SH_SAMPLER_2D_RECT_ARB: - return 1; - default: - ASSERT(false); - return 100000; + 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 TVariableInfo& lhs, const TVariableInfo& rhs) const +struct TVariableInfoComparer +{ + bool operator()(const sh::ShaderVariable &lhs, const sh::ShaderVariable &rhs) const { - int lhsSortOrder = GetSortOrder(lhs.type); - int rhsSortOrder = GetSortOrder(rhs.type); + int lhsSortOrder = gl::VariableSortOrder(lhs.type); + int rhsSortOrder = gl::VariableSortOrder(rhs.type); if (lhsSortOrder != rhsSortOrder) { return lhsSortOrder < rhsSortOrder; } // Sort by largest first. - return lhs.size > rhs.size; + return lhs.arraySize > rhs.arraySize; } }; @@ -189,13 +148,29 @@ bool VariablePacker::searchColumn(int column, int numRows, int* destRow, int* de return true; } -bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVariableInfoList& in_variables) +bool VariablePacker::CheckVariablesWithinPackingLimits( + unsigned int maxVectors, + const std::vector<sh::ShaderVariable> &in_variables) { ASSERT(maxVectors > 0); maxRows_ = maxVectors; topNonFullRow_ = 0; bottomNonFullRow_ = maxRows_ - 1; - TVariableInfoList variables(in_variables); + 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. @@ -206,11 +181,11 @@ bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVa // Packs the 4 column variables. size_t ii = 0; for (; ii < variables.size(); ++ii) { - const TVariableInfo& variable = variables[ii]; + const sh::ShaderVariable &variable = variables[ii]; if (GetNumComponentsPerRow(variable.type) != 4) { break; } - topNonFullRow_ += GetNumRows(variable.type) * variable.size; + topNonFullRow_ += GetNumRows(variable.type) * variable.elementCount(); } if (topNonFullRow_ > maxRows_) { @@ -220,11 +195,11 @@ bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVa // Packs the 3 column variables. int num3ColumnRows = 0; for (; ii < variables.size(); ++ii) { - const TVariableInfo& variable = variables[ii]; + const sh::ShaderVariable &variable = variables[ii]; if (GetNumComponentsPerRow(variable.type) != 3) { break; } - num3ColumnRows += GetNumRows(variable.type) * variable.size; + num3ColumnRows += GetNumRows(variable.type) * variable.elementCount(); } if (topNonFullRow_ + num3ColumnRows > maxRows_) { @@ -239,11 +214,11 @@ bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVa int rowsAvailableInColumns01 = twoColumnRowsAvailable; int rowsAvailableInColumns23 = twoColumnRowsAvailable; for (; ii < variables.size(); ++ii) { - const TVariableInfo& variable = variables[ii]; + const sh::ShaderVariable &variable = variables[ii]; if (GetNumComponentsPerRow(variable.type) != 2) { break; } - int numRows = GetNumRows(variable.type) * variable.size; + int numRows = GetNumRows(variable.type) * variable.elementCount(); if (numRows <= rowsAvailableInColumns01) { rowsAvailableInColumns01 -= numRows; } else if (numRows <= rowsAvailableInColumns23) { @@ -263,9 +238,9 @@ bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVa // Packs the 1 column variables. for (; ii < variables.size(); ++ii) { - const TVariableInfo& variable = variables[ii]; + const sh::ShaderVariable &variable = variables[ii]; ASSERT(1 == GetNumComponentsPerRow(variable.type)); - int numRows = GetNumRows(variable.type) * variable.size; + int numRows = GetNumRows(variable.type) * variable.elementCount(); int smallestColumn = -1; int smallestSize = maxRows_ + 1; int topRow = -1; @@ -292,6 +267,3 @@ bool VariablePacker::CheckVariablesWithinPackingLimits(int maxVectors, const TVa return true; } - - - diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/VariablePacker.h b/Source/ThirdParty/ANGLE/src/compiler/translator/VariablePacker.h new file mode 100644 index 000000000..5f38a0a98 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.cpp new file mode 100644 index 000000000..5c0c51914 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.cpp @@ -0,0 +1,136 @@ +// +// 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" + +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::visitAggregate(Visit, TIntermAggregate *node) +{ + bool visitChildren = true; + + switch (node->getOp()) + { + case EOpDeclaration: + { + const TIntermSequence &sequence = *(node->getSequence()); + if (sequence.front()->getAsTyped()->getType().isInvariant()) + { + ensureVersionIsAtLeast(GLSL_VERSION_120); + } + break; + } + case EOpInvariantDeclaration: + ensureVersionIsAtLeast(GLSL_VERSION_120); + break; + case EOpParameters: + { + const TIntermSequence ¶ms = *(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); +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.h new file mode 100644 index 000000000..c41069d42 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/VersionGLSL.h @@ -0,0 +1,68 @@ +// +// 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" + +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; + + private: + void ensureVersionIsAtLeast(int version); + + int mVersion; +}; + +#endif // COMPILER_TRANSLATOR_VERSIONGLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayout.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayout.cpp new file mode 100644 index 000000000..ba6322848 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayout.h b/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayout.h new file mode 100644 index 000000000..dd5fe0737 --- /dev/null +++ b/Source/ThirdParty/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 COMPILER_EXPORT 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 COMPILER_EXPORT 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 COMPILER_EXPORT 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/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayoutHLSL.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayoutHLSL.cpp new file mode 100644 index 000000000..43119248e --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayoutHLSL.h b/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayoutHLSL.h new file mode 100644 index 000000000..c61cb1ae5 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/blocklayoutHLSL.h @@ -0,0 +1,62 @@ +// +// 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 COMPILER_EXPORT 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). +COMPILER_EXPORT unsigned int HLSLVariableRegisterCount(const Varying &variable, bool transposeMatrices); +COMPILER_EXPORT unsigned int HLSLVariableRegisterCount(const Uniform &variable, ShShaderOutput outputType); + +} + +#endif // COMMON_BLOCKLAYOUTHLSL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/generate_parser.sh b/Source/ThirdParty/ANGLE/src/compiler/translator/generate_parser.sh new file mode 100644 index 000000000..61eecce73 --- /dev/null +++ b/Source/ThirdParty/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/Source/ThirdParty/ANGLE/src/compiler/glslang.h b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.h index f22119909..0555e96d4 100644 --- a/Source/ThirdParty/ANGLE/src/compiler/glslang.h +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.h @@ -4,7 +4,10 @@ // found in the LICENSE file. // -struct TParseContext; +#ifndef COMPILER_TRANSLATOR_GLSLANG_H_ +#define COMPILER_TRANSLATOR_GLSLANG_H_ + +class TParseContext; extern int glslang_initialize(TParseContext* context); extern int glslang_finalize(TParseContext* context); @@ -14,3 +17,4 @@ extern int glslang_scan(size_t count, TParseContext* context); extern int glslang_parse(TParseContext* context); +#endif // COMPILER_TRANSLATOR_GLSLANG_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.l b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.l new file mode 100644 index 000000000..7fe289302 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.l @@ -0,0 +1,586 @@ +/* +// +// 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! + +// 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" +#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 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); } + + /* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */ +"coherent" | +"restrict" | +"readonly" | +"writeonly" | +"resource" | +"atomic_uint" | +"noperspective" | +"patch" | +"sample" | +"subroutine" | +"common" | +"partition" | +"active" | + +"filter" | +"image1D" | +"image2D" | +"image3D" | +"imageCube" | +"iimage1D" | +"iimage2D" | +"iimage3D" | +"iimageCube" | +"uimage1D" | +"uimage2D" | +"uimage3D" | +"uimageCube" | +"image1DArray" | +"image2DArray" | +"iimage1DArray" | +"iimage2DArray" | +"uimage1DArray" | +"uimage2DArray" | +"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" | +"volatile" | +"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_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 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(GetGlobalMaxTokenSize(context->getShaderSpec())); + + return 0; +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.y b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.y new file mode 100644 index 000000000..7476f62cb --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang.y @@ -0,0 +1,1491 @@ +/* +// +// 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 + +%} +%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; + 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> 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> 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> storage_qualifier interpolation_qualifier +%type <interm.qualifierWrapper> 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 { + TIntermAggregate *aggNode = $1.intermAggregate; + if (aggNode && aggNode->getOp() == EOpNull) + aggNode->setOp(EOpDeclaration); + $$ = aggNode; + } + | 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; + $$.intermAggregate = context->parseDeclarator($$.type, $1.intermAggregate, @3, *$3.string); + } + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + $$ = $1; + $$.intermAggregate = context->parseArrayDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5); + } + | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("[]", @3, "implicitly sized array"); + $$ = $1; + $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, nullptr, @6, $7); + } + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("=", @7, "first-class arrays (array initializer)"); + $$ = $1; + $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5, @7, $8); + } + | init_declarator_list COMMA identifier EQUAL initializer { + $$ = $1; + $$.intermAggregate = context->parseInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermAggregate = context->parseSingleDeclaration($$.type, @1, ""); + } + | fully_specified_type identifier { + $$.type = $1; + $$.intermAggregate = context->parseSingleDeclaration($$.type, @2, *$2.string); + } + | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + $$.type = $1; + $$.intermAggregate = 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; + $$.intermAggregate = 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; + $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7); + } + | fully_specified_type identifier EQUAL initializer { + $$.type = $1; + $$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4); + } + ; + +fully_specified_type + : type_specifier { + $$ = $1; + + if ($1.array) { + ES3_OR_NEWER("[]", @1, "first-class-array"); + if (context->getShaderVersion() != 300) { + $1.clearArrayness(); + } + } + } + | 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 { + if (!context->declaringFunction() && $1 != EvqConst && !context->symbolTable.atGlobalLevel()) + { + context->error(@1, "Local variables can only use the const storage qualifier.", getQualifierString($1)); + } + $$ = new TStorageQualifierWrapper($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"); + $$ = EvqAttribute; + } + | VARYING { + ES2_ONLY("varying", @1); + context->checkIsAtGlobalLevel(@1, "varying"); + if (context->getShaderType() == GL_VERTEX_SHADER) + $$ = EvqVaryingOut; + else + $$ = EvqVaryingIn; + } + | CONST_QUAL { + $$ = EvqConst; + } + | IN_QUAL { + if (context->declaringFunction()) + { + $$ = EvqIn; + } + else if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + ES3_OR_NEWER("in", @1, "storage qualifier"); + $$ = EvqFragmentIn; + } + else if (context->getShaderType() == GL_VERTEX_SHADER) + { + ES3_OR_NEWER("in", @1, "storage qualifier"); + $$ = EvqVertexIn; + } + else + { + $$ = EvqComputeIn; + } + } + | OUT_QUAL { + if (context->declaringFunction()) + { + $$ = EvqOut; + } + else + { + ES3_OR_NEWER("out", @1, "storage qualifier"); + NON_COMPUTE_ONLY("out", @1); + if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + $$ = EvqFragmentOut; + } + else + { + $$ = EvqVertexOut; + } + } + } + | INOUT_QUAL { + if (!context->declaringFunction()) + { + context->error(@1, "invalid inout qualifier", "'inout' can be only used with function parameters"); + } + $$ = EvqInOut; + } + | CENTROID { + ES3_OR_NEWER("centroid", @1, "storage qualifier"); + $$ = EvqCentroid; + } + | UNIFORM { + context->checkIsAtGlobalLevel(@1, "uniform"); + $$ = EvqUniform; + } + ; + +type_specifier + : type_specifier_no_prec { + $$ = $1; + + if ($$.precision == EbpUndefined) { + $$.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; + } + | 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 { + TIntermNode *intermNode; + context->checkIsScalarBool(@2, $1); + + if (!context->executeInitializer(@2, *$2.string, $1, $4, &intermNode)) + $$ = $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/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_lex.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_lex.cpp new file mode 100644 index 000000000..8ad7b1464 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_lex.cpp @@ -0,0 +1,3424 @@ +#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 +// found in the LICENSE file. +// + +// This file is auto-generated by generate_parser.sh. DO NOT EDIT! + +// 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 28 "./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 < 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 = (yy_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, 136, 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, 139, 177, 177, 177, 177, 21, 177, + 177, 143, 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, 142, 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, 137, 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, 86, 177, 177, 177, + + 177, 177, 177, 177, 153, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 138, 177, 177, 176, + 56, 57, 58, 177, 177, 14, 177, 91, 177, 177, + 177, 177, 89, 177, 177, 177, 154, 149, 92, 177, + 177, 177, 177, 177, 177, 144, 177, 177, 177, 78, + 38, 41, 43, 42, 39, 45, 44, 46, 40, 177, + 177, 177, 177, 160, 135, 177, 177, 147, 177, 177, + 177, 34, 87, 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, 93, 94, 95, 177, 177, 177, 177, 177, + 3, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 140, 177, 177, 177, 177, 177, 8, 177, + 177, 9, 177, 177, 177, 177, 20, 79, 11, 150, + 97, 98, 99, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 145, 177, 177, 177, 81, + 83, 80, 177, 177, 177, 177, 177, 177, 177, 141, + 101, 102, 103, 177, 177, 157, 177, 146, 177, 177, + 6, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 96, 151, 1, 177, 177, 177, 177, 177, 175, 177, + + 90, 5, 170, 59, 62, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 177, 82, 177, + 177, 177, 177, 100, 177, 177, 177, 177, 177, 120, + 66, 67, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 88, 177, 177, 177, 104, + 122, 70, 71, 177, 177, 84, 177, 177, 177, 177, + 177, 177, 177, 115, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 129, 177, 177, 177, 177, 60, + 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 116, 105, 177, 106, 177, 177, 177, 130, + + 177, 177, 68, 177, 177, 177, 177, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 117, 177, 177, 131, + 177, 177, 72, 107, 108, 177, 111, 177, 112, 177, + 177, 177, 177, 177, 85, 177, 177, 177, 177, 64, + 177, 63, 126, 177, 177, 109, 110, 177, 177, 177, + 177, 177, 177, 177, 177, 177, 177, 124, 127, 118, + 177, 65, 177, 177, 177, 177, 177, 177, 177, 177, + 125, 128, 177, 177, 121, 69, 177, 177, 171, 177, + 177, 177, 74, 177, 177, 123, 73, 177, 177, 177, + 177, 177, 177, 132, 177, 177, 177, 177, 177, 177, + + 133, 177, 177, 177, 75, 177, 134, 113, 114, 177, + 177, 177, 61, 177, 177, 172, 119, 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" +#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 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 < 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 +/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */ +case 79: +case 80: +case 81: +case 82: +case 83: +case 84: +case 85: +case 86: +case 87: +case 88: +case 89: +case 90: +case 91: +case 92: +case 93: +case 94: +case 95: +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: +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 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 */ +case 136: +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 + { + yy_size_t 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 - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + 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 = 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 = 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 < _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_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 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(GetGlobalMaxTokenSize(context->getShaderSpec())); + + return 0; +} + diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.cpp new file mode 100644 index 000000000..3e50fbc4b --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.cpp @@ -0,0 +1,4917 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Apple Note: For the avoidance of doubt, Apple elects to distribute this file under the terms of the BSD license. */ + +/* 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 + + + + +# 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, + STRUCT = 309, + VOID_TYPE = 310, + WHILE = 311, + SAMPLER2D = 312, + SAMPLERCUBE = 313, + SAMPLER_EXTERNAL_OES = 314, + SAMPLER2DRECT = 315, + SAMPLER2DARRAY = 316, + ISAMPLER2D = 317, + ISAMPLER3D = 318, + ISAMPLERCUBE = 319, + ISAMPLER2DARRAY = 320, + USAMPLER2D = 321, + USAMPLER3D = 322, + USAMPLERCUBE = 323, + USAMPLER2DARRAY = 324, + SAMPLER3D = 325, + SAMPLER3DRECT = 326, + SAMPLER2DSHADOW = 327, + SAMPLERCUBESHADOW = 328, + SAMPLER2DARRAYSHADOW = 329, + LAYOUT = 330, + IDENTIFIER = 331, + TYPE_NAME = 332, + FLOATCONSTANT = 333, + INTCONSTANT = 334, + UINTCONSTANT = 335, + BOOLCONSTANT = 336, + FIELD_SELECTION = 337, + LEFT_OP = 338, + RIGHT_OP = 339, + INC_OP = 340, + DEC_OP = 341, + LE_OP = 342, + GE_OP = 343, + EQ_OP = 344, + NE_OP = 345, + AND_OP = 346, + OR_OP = 347, + XOR_OP = 348, + MUL_ASSIGN = 349, + DIV_ASSIGN = 350, + ADD_ASSIGN = 351, + MOD_ASSIGN = 352, + LEFT_ASSIGN = 353, + RIGHT_ASSIGN = 354, + AND_ASSIGN = 355, + XOR_ASSIGN = 356, + OR_ASSIGN = 357, + SUB_ASSIGN = 358, + LEFT_PAREN = 359, + RIGHT_PAREN = 360, + LEFT_BRACKET = 361, + RIGHT_BRACKET = 362, + LEFT_BRACE = 363, + RIGHT_BRACE = 364, + DOT = 365, + COMMA = 366, + COLON = 367, + EQUAL = 368, + SEMICOLON = 369, + BANG = 370, + DASH = 371, + TILDE = 372, + PLUS = 373, + STAR = 374, + SLASH = 375, + PERCENT = 376, + LEFT_ANGLE = 377, + RIGHT_ANGLE = 378, + VERTICAL_BAR = 379, + CARET = 380, + AMPERSAND = 381, + QUESTION = 382 + }; +#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; + 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 109 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 2432 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 128 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 94 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 269 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 405 + +/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned + by yylex, with out-of-bounds checking. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 382 + +#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 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 237, 237, 238, 241, 251, 254, 259, 264, 269, + 274, 280, 283, 286, 289, 292, 295, 301, 308, 319, + 323, 331, 334, 340, 344, 351, 357, 366, 374, 380, + 386, 395, 398, 401, 404, 414, 415, 416, 417, 425, + 426, 429, 432, 439, 440, 443, 449, 450, 454, 461, + 462, 465, 468, 471, 477, 478, 481, 487, 488, 495, + 496, 503, 504, 511, 512, 518, 519, 525, 526, 532, + 533, 539, 540, 547, 548, 549, 550, 554, 555, 556, + 560, 564, 568, 572, 579, 582, 588, 595, 602, 605, + 611, 620, 624, 628, 632, 636, 643, 650, 653, 660, + 668, 688, 698, 706, 731, 735, 739, 743, 750, 757, + 760, 764, 768, 773, 778, 785, 789, 793, 797, 802, + 807, 814, 824, 830, 833, 839, 843, 850, 856, 863, + 867, 870, 873, 882, 888, 896, 899, 919, 938, 945, + 949, 956, 966, 969, 972, 978, 985, 988, 994, 997, + 1000, 1006, 1009, 1014, 1025, 1028, 1031, 1034, 1037, 1040, + 1044, 1048, 1052, 1056, 1060, 1064, 1068, 1072, 1076, 1080, + 1084, 1088, 1092, 1096, 1100, 1104, 1108, 1112, 1116, 1120, + 1124, 1127, 1130, 1133, 1136, 1139, 1142, 1145, 1148, 1151, + 1154, 1157, 1160, 1163, 1166, 1169, 1176, 1182, 1185, 1197, + 1197, 1200, 1200, 1206, 1209, 1224, 1227, 1234, 1238, 1244, + 1250, 1262, 1266, 1270, 1271, 1277, 1278, 1279, 1280, 1281, + 1282, 1283, 1287, 1288, 1288, 1288, 1297, 1298, 1302, 1302, + 1303, 1303, 1308, 1311, 1320, 1325, 1332, 1333, 1337, 1344, + 1348, 1355, 1355, 1362, 1365, 1372, 1376, 1389, 1389, 1394, + 1394, 1400, 1400, 1408, 1411, 1417, 1420, 1426, 1430, 1437, + 1440, 1443, 1446, 1449, 1458, 1464, 1470, 1473, 1479, 1479 +}; +#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", "STRUCT", + "VOID_TYPE", "WHILE", "SAMPLER2D", "SAMPLERCUBE", "SAMPLER_EXTERNAL_OES", + "SAMPLER2DRECT", "SAMPLER2DARRAY", "ISAMPLER2D", "ISAMPLER3D", + "ISAMPLERCUBE", "ISAMPLER2DARRAY", "USAMPLER2D", "USAMPLER3D", + "USAMPLERCUBE", "USAMPLER2DARRAY", "SAMPLER3D", "SAMPLER3DRECT", + "SAMPLER2DSHADOW", "SAMPLERCUBESHADOW", "SAMPLER2DARRAYSHADOW", "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 +}; +# endif + +#define YYPACT_NINF -348 + +#define yypact_value_is_default(Yystate) \ + (!!((Yystate) == (-348))) + +#define YYTABLE_NINF -229 + +#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[] = +{ + 2144, -348, -348, -348, -348, 144, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -53, -348, -348, -348, -348, -348, -348, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, -348, + -83, -348, -348, -89, -70, -81, 2219, -26, -348, 60, + -348, 1163, -348, -348, -348, -348, -348, -348, -348, -66, + -348, 2069, -348, -348, 2355, -348, -348, -348, -41, -2, + -348, -30, -348, 2219, -348, -348, -348, 2219, 80, 80, + -348, -17, -75, -65, -348, 2219, -348, -348, 1233, -348, + -348, -15, 2219, -348, -10, -85, -348, 389, -348, -348, + -348, -348, 1, -49, -348, 1331, 1622, -348, -348, 2219, + 80, 1814, -348, 19, -348, -348, -348, -348, -348, 1622, + 1622, 1622, -348, -348, -348, -348, -348, -348, -348, -24, + -348, -348, -348, 37, -22, 1717, 67, -348, 1622, 34, + 14, 76, -46, 72, 51, 75, 78, 112, 113, -77, + -348, 98, -348, -348, 1899, 2219, 84, -348, -2, 94, + 97, -348, 108, 109, 100, 1429, 114, 1622, 104, 115, + 111, -348, -348, 190, -348, -348, -19, -348, -89, 117, + -348, -348, -348, -348, 505, -348, -348, -348, -348, -348, + -348, 1622, 1524, 1622, 116, 110, -348, -348, 80, 118, + 27, -348, -58, -348, -348, -348, -5, -348, -348, 1622, + 2287, -348, -348, 1622, 121, -348, -348, -348, 1622, 1622, + 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, + 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, -348, -348, + 1984, -348, -348, -348, -348, -348, 119, -348, 1622, -348, + -348, 32, 1622, 122, -348, -348, -348, 621, -348, -348, + -348, -348, -348, -348, -348, -348, -348, -348, -348, 1622, + 1622, -348, -348, -348, 124, 120, 129, -348, 1622, 125, + 33, 1622, 80, -348, -87, -348, -348, 130, 128, -348, + 136, -348, -348, -348, -348, -348, 34, 34, 14, 14, + 76, 76, 76, 76, -46, -46, 72, 51, 75, 78, + 112, 113, 54, -348, 166, -30, 853, 969, -3, -348, + -1, -348, 1066, 621, -348, -348, -348, 1622, 132, -348, + 1622, -348, 135, -348, 1622, -348, -348, 1622, 139, -348, + -348, -348, -348, 1066, 119, -348, 128, 80, 2219, 141, + 140, -348, 1622, -348, -348, 145, -348, 1622, -348, 134, + 146, 236, -348, 147, 143, 737, -348, -348, 148, 17, + 1622, 737, 119, -348, 1622, -348, -348, -348, -348, 149, + 128, -348, -348, -348, -348 +}; + + /* 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, 142, 143, 144, 0, 133, 135, 158, 155, + 156, 157, 162, 163, 164, 165, 166, 167, 159, 160, + 161, 168, 169, 170, 171, 172, 173, 136, 137, 138, + 140, 134, 174, 175, 176, 177, 178, 179, 139, 124, + 123, 0, 154, 180, 182, 195, 196, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 181, 192, 193, 194, + 0, 198, 267, 268, 0, 98, 97, 0, 109, 115, + 131, 0, 132, 125, 128, 121, 130, 129, 141, 151, + 197, 0, 264, 266, 0, 2, 3, 201, 0, 0, + 88, 0, 96, 0, 105, 99, 107, 0, 108, 0, + 89, 2, 116, 0, 94, 0, 126, 122, 0, 1, + 265, 0, 0, 199, 148, 0, 146, 0, 269, 100, + 104, 106, 102, 110, 101, 0, 0, 87, 95, 0, + 0, 0, 203, 4, 8, 6, 7, 9, 30, 0, + 0, 0, 152, 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, 145, 0, 0, + 0, 249, 0, 0, 0, 0, 0, 0, 0, 0, + 223, 232, 236, 39, 71, 84, 0, 212, 0, 141, + 215, 234, 214, 213, 0, 216, 217, 218, 219, 220, + 221, 0, 0, 0, 0, 0, 211, 120, 0, 209, + 0, 207, 0, 204, 32, 33, 0, 15, 16, 0, + 0, 22, 21, 0, 154, 25, 27, 34, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 153, 202, + 0, 149, 150, 147, 260, 259, 230, 251, 0, 263, + 261, 0, 0, 0, 244, 247, 222, 0, 74, 75, + 77, 76, 79, 80, 81, 82, 83, 78, 73, 0, + 0, 237, 233, 235, 0, 0, 0, 114, 0, 117, + 0, 0, 0, 205, 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, 200, 0, 0, 0, 0, 0, 262, + 0, 243, 0, 224, 72, 85, 103, 0, 111, 118, + 0, 206, 0, 208, 0, 92, 12, 0, 0, 229, + 231, 254, 253, 256, 230, 241, 245, 0, 0, 0, + 0, 112, 0, 119, 210, 0, 70, 0, 255, 0, + 0, 240, 238, 0, 0, 0, 225, 113, 0, 0, + 257, 0, 230, 242, 0, 227, 248, 226, 93, 0, + 258, 252, 239, 246, 250 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -348, -40, -348, -348, -348, -348, -348, -348, 28, -348, + -348, -348, -348, -60, -348, -72, -71, -127, -52, 8, + 12, 16, 7, 10, 11, -348, -86, -122, -348, -133, + -78, -348, 9, 13, -348, -348, -348, 169, 177, 174, + -348, -348, -326, -348, -59, -348, -69, -348, -61, 268, + -348, -348, 96, 0, -348, -348, -348, -348, -100, -120, + 57, -23, -140, -57, -198, -322, -105, -348, -348, -110, + -347, -348, -348, -88, 5, -42, -348, -348, -348, -348, + -348, -67, -348, -348, -348, -348, -348, -348, -348, -348, + -348, 216, -348, -348 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 219, 147, 148, 149, 307, 150, 151, 152, 153, + 154, 155, 156, 193, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 194, 195, 289, 196, + 171, 105, 197, 198, 64, 65, 66, 94, 95, 96, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 115, 116, 172, 79, 80, 175, 112, 131, 132, + 220, 221, 217, 200, 201, 202, 203, 277, 370, 396, + 334, 335, 336, 397, 204, 205, 206, 382, 207, 383, + 208, 369, 209, 342, 266, 337, 363, 379, 380, 210, + 81, 82, 83, 91 +}; + + /* 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[] = +{ + 78, 88, 106, 118, 216, 98, 293, 97, 226, 62, + 107, 223, 174, 63, 360, 256, 367, 381, 304, 354, + 177, 89, 170, 85, 86, 90, 178, 355, 106, 102, + 93, 125, 98, 235, 97, 92, 98, 367, 126, 170, + 108, 245, 246, 127, 130, 402, 129, 215, 157, 128, + 257, 130, 271, 129, 223, 87, 305, 212, 122, 123, + 106, 227, 228, 395, 213, 157, 78, 113, 218, 395, + 130, 78, 129, 297, 114, 260, 247, 248, 117, 224, + 225, 78, 229, 232, 111, 99, 230, 124, 100, 233, + 62, 216, 290, 78, 63, 291, 308, 78, 237, 173, + 306, 170, 364, 176, 365, 78, 290, 211, 290, 273, + 290, 312, 78, 130, 130, 129, 129, 199, 320, 321, + 322, 323, 399, -29, 332, 170, 170, 157, 290, 78, + 241, 78, 242, 294, 296, 338, 101, 86, 302, 340, + 223, 303, 231, 290, 302, 293, 339, 351, 2, 3, + 4, 157, 157, 238, 239, 240, 85, 86, 349, 243, + 244, 249, 250, 261, 262, 290, 357, 344, 345, 316, + 317, 236, 318, 319, 78, 78, 216, 251, 313, 314, + 315, 157, 157, 157, 157, 157, 157, 157, 157, 157, + 157, 157, 157, 157, 157, 157, 157, 324, 325, 130, + 252, 129, 253, 254, 199, 258, 255, 371, 264, 366, + 373, 265, 267, 268, 269, 170, 274, 299, 272, 275, + 276, -28, 358, 352, 301, 216, -23, -228, 216, 298, + 366, 346, 387, 347, 341, 376, 348, 356, 350, 290, + -30, 157, 374, 377, 389, 372, 385, 359, 390, 386, + 216, 391, 388, 392, 403, 190, 394, 400, 311, 326, + 78, 329, 398, 404, 327, 330, 120, 331, 170, 328, + 119, 121, 216, 84, 263, 300, 375, 199, 393, 353, + 361, 401, 343, 368, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 157, 362, 378, 110, 0, 106, + 0, 0, 0, 288, 368, 0, 0, 107, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 384, 0, 0, + 0, 0, 0, 0, 0, 0, 199, 199, 0, 0, + 0, 0, 199, 199, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 199, 0, 0, 0, 0, 78, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 199, 0, 0, 0, 0, + 0, 199, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 179, 180, 181, 0, 182, 183, 184, + 185, 186, 187, 188, 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, 189, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 60, 133, 61, 134, 135, 136, + 137, 138, 0, 0, 139, 140, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 141, 0, 0, 0, 190, 191, 0, + 0, 0, 0, 192, 143, 144, 145, 146, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 179, + 180, 181, 0, 182, 183, 184, 185, 186, 187, 188, + 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, 189, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 0, 57, 58, 59, + 60, 133, 61, 134, 135, 136, 137, 138, 0, 0, + 139, 140, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, + 0, 0, 0, 190, 292, 0, 0, 0, 0, 192, + 143, 144, 145, 146, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 179, 180, 181, 0, 182, + 183, 184, 185, 186, 187, 188, 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, 189, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 0, 57, 58, 59, 60, 133, 61, 134, + 135, 136, 137, 138, 0, 0, 139, 140, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 141, 0, 0, 0, 190, + 0, 0, 0, 0, 0, 192, 143, 144, 145, 146, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 179, 180, 181, 0, 182, 183, 184, 185, 186, + 187, 188, 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, 189, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 0, 57, + 58, 59, 60, 133, 61, 134, 135, 136, 137, 138, + 0, 0, 139, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 141, 0, 0, 0, 117, 0, 0, 0, 0, + 0, 192, 143, 144, 145, 146, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 179, 180, 181, + 0, 182, 183, 184, 185, 186, 187, 188, 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, 189, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 0, 57, 58, 59, 60, 133, + 61, 134, 135, 136, 137, 138, 0, 0, 139, 140, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 141, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 192, 143, 144, + 145, 146, 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, 0, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 60, 133, 61, 134, 135, 136, + 137, 138, 0, 0, 139, 140, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 2, 3, 4, 141, 6, 7, 8, 9, 10, 11, + 0, 0, 0, 192, 143, 144, 145, 146, 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, 0, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 0, 57, 58, + 59, 60, 133, 61, 134, 135, 136, 137, 138, 0, + 0, 139, 140, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 141, 6, 7, 8, 9, 10, 11, 0, 0, 0, + 0, 143, 144, 145, 146, 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, 0, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 0, 57, 58, 59, 60, 103, + 61, 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, 104, 32, 33, + 34, 35, 36, 37, 0, 0, 0, 41, 42, 0, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 0, 57, 58, 59, 0, 133, + 61, 134, 135, 136, 137, 138, 0, 0, 139, 140, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 141, 0, 0, + 142, 8, 9, 10, 11, 0, 0, 0, 143, 144, + 145, 146, 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, 41, 42, 0, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 0, 57, 58, 59, 0, 133, 61, 134, + 135, 136, 137, 138, 0, 0, 139, 140, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 141, 0, 0, 214, 8, + 9, 10, 11, 0, 0, 0, 143, 144, 145, 146, + 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, 41, 42, 0, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 0, 133, 61, 134, 135, 136, + 137, 138, 0, 0, 139, 140, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 141, 8, 9, 10, 11, 0, 0, + 0, 0, 0, 270, 143, 144, 145, 146, 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, 41, 42, + 0, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 0, 57, 58, 59, 0, + 133, 61, 134, 135, 136, 137, 138, 0, 0, 139, + 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 141, 0, + 0, 295, 8, 9, 10, 11, 0, 0, 0, 143, + 144, 145, 146, 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, 41, 42, 0, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 0, 57, 58, 59, 0, 133, 61, + 134, 135, 136, 137, 138, 0, 0, 139, 140, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 141, 8, 9, 10, + 11, 0, 0, 0, 0, 0, 0, 143, 144, 145, + 146, 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, 41, 234, 0, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 0, 57, + 58, 59, 0, 133, 61, 134, 135, 136, 137, 138, + 0, 0, 139, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 4, 141, 6, 7, 8, 9, 10, 11, 0, 0, + 0, 0, 143, 144, 145, 146, 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, + 0, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 0, 57, 58, 59, 60, + 0, 61, 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, 222, 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, 0, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 60, 0, 61, 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, 259, 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, + 0, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 0, 57, 58, 59, 60, + 0, 61, 0, 0, 0, 0, 0, 0, 0, 109, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 333, 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, 0, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 60, 0, 61, 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, + 0, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 0, 57, 58, 59, 60, + 0, 61, 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, 0, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 0, 57, 58, 59, 60, 0, 61, 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, 41, 42, 0, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 0, 57, + 58, 59, 0, 309, 61, 8, 9, 10, 11, 310, + 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, 41, + 42, 0, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 0, 57, 58, 59, + 0, 0, 61 +}; + +static const yytype_int16 yycheck[] = +{ + 0, 41, 71, 91, 126, 66, 204, 66, 141, 0, + 71, 131, 112, 0, 336, 92, 342, 364, 76, 106, + 105, 104, 108, 76, 77, 114, 111, 114, 97, 69, + 111, 106, 93, 155, 93, 105, 97, 363, 113, 125, + 106, 87, 88, 108, 105, 392, 105, 125, 108, 114, + 127, 112, 185, 112, 174, 108, 114, 106, 98, 99, + 129, 85, 86, 385, 113, 125, 66, 108, 129, 391, + 131, 71, 131, 213, 76, 175, 122, 123, 108, 139, + 140, 81, 106, 105, 84, 111, 110, 104, 114, 111, + 81, 213, 111, 93, 81, 114, 229, 97, 158, 114, + 105, 187, 105, 113, 105, 105, 111, 106, 111, 187, + 111, 233, 112, 174, 175, 174, 175, 117, 245, 246, + 247, 248, 105, 104, 257, 211, 212, 187, 111, 129, + 116, 131, 118, 211, 212, 268, 76, 77, 111, 272, + 260, 114, 105, 111, 111, 343, 114, 114, 4, 5, + 6, 211, 212, 119, 120, 121, 76, 77, 298, 83, + 84, 89, 90, 79, 80, 111, 112, 289, 290, 241, + 242, 104, 243, 244, 174, 175, 298, 126, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 249, 250, 260, + 125, 260, 124, 91, 204, 107, 93, 347, 114, 342, + 350, 114, 104, 104, 114, 301, 112, 107, 104, 104, + 109, 104, 56, 301, 106, 347, 105, 108, 350, 113, + 363, 107, 372, 113, 112, 357, 107, 107, 113, 111, + 104, 301, 107, 104, 377, 113, 105, 335, 114, 109, + 372, 105, 107, 17, 394, 108, 113, 390, 230, 251, + 260, 254, 114, 114, 252, 255, 97, 256, 354, 253, + 93, 97, 394, 5, 178, 218, 354, 277, 383, 302, + 337, 391, 277, 342, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 354, 337, 363, 81, -1, 368, + -1, -1, -1, 113, 363, -1, -1, 368, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 367, -1, -1, + -1, -1, -1, -1, -1, -1, 336, 337, -1, -1, + -1, -1, 342, 343, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 363, -1, -1, -1, -1, 368, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 385, -1, -1, -1, -1, + -1, 391, 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, + -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, -1, -1, 85, 86, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 104, -1, -1, -1, 108, 109, -1, + -1, -1, -1, 114, 115, 116, 117, 118, 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, -1, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, -1, -1, + 85, 86, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 104, + -1, -1, -1, 108, 109, -1, -1, -1, -1, 114, + 115, 116, 117, 118, 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, -1, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, -1, -1, 85, 86, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 104, -1, -1, -1, 108, + -1, -1, -1, -1, -1, 114, 115, 116, 117, 118, + 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, -1, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + -1, -1, 85, 86, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 104, -1, -1, -1, 108, -1, -1, -1, -1, + -1, 114, 115, 116, 117, 118, 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, -1, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, -1, -1, 85, 86, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 104, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 114, 115, 116, + 117, 118, 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, -1, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, -1, -1, 85, 86, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, + 4, 5, 6, 104, 8, 9, 10, 11, 12, 13, + -1, -1, -1, 114, 115, 116, 117, 118, -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, -1, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, -1, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, -1, + -1, 85, 86, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 3, 4, 5, 6, + 104, 8, 9, 10, 11, 12, 13, -1, -1, -1, + -1, 115, 116, 117, 118, -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, -1, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, -1, 72, 73, 74, 75, 76, + 77, -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, 114, 45, 46, + 47, 48, 49, 50, -1, -1, -1, 54, 55, -1, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, + 77, 78, 79, 80, 81, 82, -1, -1, 85, 86, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 104, -1, -1, + 107, 10, 11, 12, 13, -1, -1, -1, 115, 116, + 117, 118, -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, 54, 55, -1, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, -1, 72, 73, 74, -1, 76, 77, 78, + 79, 80, 81, 82, -1, -1, 85, 86, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 104, -1, -1, 107, 10, + 11, 12, 13, -1, -1, -1, 115, 116, 117, 118, + -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, 54, 55, -1, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, -1, 76, 77, 78, 79, 80, + 81, 82, -1, -1, 85, 86, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 104, 10, 11, 12, 13, -1, -1, + -1, -1, -1, 114, 115, 116, 117, 118, -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, 54, 55, + -1, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, -1, 72, 73, 74, -1, + 76, 77, 78, 79, 80, 81, 82, -1, -1, 85, + 86, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 104, -1, + -1, 107, 10, 11, 12, 13, -1, -1, -1, 115, + 116, 117, 118, -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, 54, 55, -1, 57, + 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, -1, 72, 73, 74, -1, 76, 77, + 78, 79, 80, 81, 82, -1, -1, 85, 86, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 104, 10, 11, 12, + 13, -1, -1, -1, -1, -1, -1, 115, 116, 117, + 118, -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, 54, 55, -1, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, -1, 72, + 73, 74, -1, 76, 77, 78, 79, 80, 81, 82, + -1, -1, 85, 86, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 3, 4, 5, + 6, 104, 8, 9, 10, 11, 12, 13, -1, -1, + -1, -1, 115, 116, 117, 118, -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, + -1, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, -1, 72, 73, 74, 75, + -1, 77, -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, 109, 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, -1, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, 75, -1, 77, -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, 109, 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, + -1, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, -1, 72, 73, 74, 75, + -1, 77, -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, 109, 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, -1, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, 75, -1, 77, 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, + -1, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, -1, 72, 73, 74, 75, + -1, 77, 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, -1, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + -1, 72, 73, 74, 75, -1, 77, 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, 54, 55, -1, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, -1, 72, + 73, 74, -1, 76, 77, 10, 11, 12, 13, 82, + -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, 54, + 55, -1, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, -1, 72, 73, 74, + -1, -1, 77 +}; + + /* 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, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 72, 73, 74, + 75, 77, 160, 161, 162, 163, 164, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 181, 182, + 183, 218, 219, 220, 177, 76, 77, 108, 129, 104, + 114, 221, 105, 111, 165, 166, 167, 172, 176, 111, + 114, 76, 129, 76, 114, 159, 174, 176, 106, 0, + 219, 181, 185, 108, 76, 179, 180, 108, 201, 166, + 165, 167, 129, 129, 104, 106, 113, 108, 114, 172, + 176, 186, 187, 76, 78, 79, 80, 81, 82, 85, + 86, 104, 107, 115, 116, 117, 118, 130, 131, 132, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 158, 181, 114, 186, 184, 113, 105, 111, 14, + 15, 16, 18, 19, 20, 21, 22, 23, 24, 56, + 108, 109, 114, 141, 154, 155, 157, 160, 161, 181, + 191, 192, 193, 194, 202, 203, 204, 206, 208, 210, + 217, 106, 106, 113, 107, 158, 155, 190, 176, 129, + 188, 189, 109, 187, 141, 141, 157, 85, 86, 106, + 110, 105, 105, 111, 55, 155, 104, 141, 119, 120, + 121, 116, 118, 83, 84, 87, 88, 122, 123, 89, + 90, 126, 125, 124, 91, 93, 92, 127, 107, 109, + 186, 79, 80, 180, 114, 114, 212, 104, 104, 114, + 114, 157, 104, 158, 112, 104, 109, 195, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 113, 156, + 111, 114, 109, 192, 158, 107, 158, 190, 113, 107, + 188, 106, 111, 114, 76, 114, 105, 133, 157, 76, + 82, 136, 155, 141, 141, 141, 143, 143, 144, 144, + 145, 145, 145, 145, 146, 146, 147, 148, 149, 150, + 151, 152, 157, 109, 198, 199, 200, 213, 157, 114, + 157, 112, 211, 202, 155, 155, 107, 113, 107, 190, + 113, 114, 158, 189, 106, 114, 107, 112, 56, 201, + 193, 191, 203, 214, 105, 105, 157, 170, 172, 209, + 196, 190, 113, 190, 107, 158, 155, 104, 209, 215, + 216, 198, 205, 207, 129, 105, 109, 190, 107, 157, + 114, 105, 17, 194, 113, 193, 197, 201, 114, 105, + 157, 197, 198, 190, 114 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 128, 129, 129, 130, 131, 131, 131, 131, 131, + 131, 132, 132, 132, 132, 132, 132, 133, 134, 135, + 135, 136, 136, 137, 137, 138, 138, 139, 140, 140, + 140, 141, 141, 141, 141, 142, 142, 142, 142, 143, + 143, 143, 143, 144, 144, 144, 145, 145, 145, 146, + 146, 146, 146, 146, 147, 147, 147, 148, 148, 149, + 149, 150, 150, 151, 151, 152, 152, 153, 153, 154, + 154, 155, 155, 156, 156, 156, 156, 156, 156, 156, + 156, 156, 156, 156, 157, 157, 158, 159, 160, 160, + 160, 160, 160, 160, 160, 160, 161, 162, 162, 163, + 163, 164, 165, 165, 166, 166, 166, 166, 167, 168, + 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, + 169, 170, 170, 171, 171, 172, 172, 173, 174, 174, + 174, 174, 174, 175, 175, 175, 175, 175, 175, 175, + 175, 176, 177, 177, 177, 178, 179, 179, 180, 180, + 180, 181, 181, 181, 182, 182, 182, 182, 182, 182, + 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, + 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, + 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, + 182, 182, 182, 182, 182, 182, 182, 182, 182, 184, + 183, 185, 183, 186, 186, 187, 187, 188, 188, 189, + 189, 190, 191, 192, 192, 193, 193, 193, 193, 193, + 193, 193, 194, 195, 196, 194, 197, 197, 199, 198, + 200, 198, 201, 201, 202, 202, 203, 203, 204, 205, + 205, 207, 206, 208, 208, 209, 209, 211, 210, 212, + 210, 213, 210, 214, 214, 215, 215, 216, 216, 217, + 217, 217, 217, 217, 218, 218, 219, 219, 221, 220 +}; + + /* 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, 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, 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: + + { + TIntermAggregate *aggNode = (yyvsp[-1].interm).intermAggregate; + if (aggNode && aggNode->getOp() == EOpNull) + aggNode->setOp(EOpDeclaration); + (yyval.interm.intermNode) = aggNode; + } + + 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); + (yyval.interm).intermAggregate = context->parseDeclarator((yyval.interm).type, (yyvsp[-2].interm).intermAggregate, (yylsp[0]), *(yyvsp[0].lex).string); + } + + break; + + case 111: + + { + (yyval.interm) = (yyvsp[-5].interm); + (yyval.interm).intermAggregate = context->parseArrayDeclarator((yyval.interm).type, (yyvsp[-5].interm).intermAggregate, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode)); + } + + break; + + case 112: + + { + ES3_OR_NEWER("[]", (yylsp[-4]), "implicitly sized array"); + (yyval.interm) = (yyvsp[-6].interm); + (yyval.interm).intermAggregate = context->parseArrayInitDeclarator((yyval.interm).type, (yyvsp[-6].interm).intermAggregate, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode)); + } + + break; + + case 113: + + { + ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)"); + (yyval.interm) = (yyvsp[-7].interm); + (yyval.interm).intermAggregate = context->parseArrayInitDeclarator((yyval.interm).type, (yyvsp[-7].interm).intermAggregate, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode)); + } + + break; + + case 114: + + { + (yyval.interm) = (yyvsp[-4].interm); + (yyval.interm).intermAggregate = context->parseInitDeclarator((yyval.interm).type, (yyvsp[-4].interm).intermAggregate, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode)); + } + + break; + + case 115: + + { + (yyval.interm).type = (yyvsp[0].interm.type); + (yyval.interm).intermAggregate = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), ""); + } + + break; + + case 116: + + { + (yyval.interm).type = (yyvsp[-1].interm.type); + (yyval.interm).intermAggregate = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string); + } + + break; + + case 117: + + { + (yyval.interm).type = (yyvsp[-4].interm.type); + (yyval.interm).intermAggregate = 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).intermAggregate = 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).intermAggregate = 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).intermAggregate = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode)); + } + + break; + + case 121: + + { + (yyval.interm.type) = (yyvsp[0].interm.type); + + if ((yyvsp[0].interm.type).array) { + ES3_OR_NEWER("[]", (yylsp[0]), "first-class-array"); + if (context->getShaderVersion() != 300) { + (yyvsp[0].interm.type).clearArrayness(); + } + } + } + + 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: + + { + if (!context->declaringFunction() && (yyvsp[0].interm.qualifier) != EvqConst && !context->symbolTable.atGlobalLevel()) + { + context->error((yylsp[0]), "Local variables can only use the const storage qualifier.", getQualifierString((yyvsp[0].interm.qualifier))); + } + (yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper((yyvsp[0].interm.qualifier), (yylsp[0])); + } + + 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.qualifier) = EvqAttribute; + } + + break; + + case 134: + + { + ES2_ONLY("varying", (yylsp[0])); + context->checkIsAtGlobalLevel((yylsp[0]), "varying"); + if (context->getShaderType() == GL_VERTEX_SHADER) + (yyval.interm.qualifier) = EvqVaryingOut; + else + (yyval.interm.qualifier) = EvqVaryingIn; + } + + break; + + case 135: + + { + (yyval.interm.qualifier) = EvqConst; + } + + break; + + case 136: + + { + if (context->declaringFunction()) + { + (yyval.interm.qualifier) = EvqIn; + } + else if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier"); + (yyval.interm.qualifier) = EvqFragmentIn; + } + else if (context->getShaderType() == GL_VERTEX_SHADER) + { + ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier"); + (yyval.interm.qualifier) = EvqVertexIn; + } + else + { + (yyval.interm.qualifier) = EvqComputeIn; + } + } + + break; + + case 137: + + { + if (context->declaringFunction()) + { + (yyval.interm.qualifier) = EvqOut; + } + else + { + ES3_OR_NEWER("out", (yylsp[0]), "storage qualifier"); + NON_COMPUTE_ONLY("out", (yylsp[0])); + if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + (yyval.interm.qualifier) = EvqFragmentOut; + } + else + { + (yyval.interm.qualifier) = EvqVertexOut; + } + } + } + + break; + + case 138: + + { + if (!context->declaringFunction()) + { + context->error((yylsp[0]), "invalid inout qualifier", "'inout' can be only used with function parameters"); + } + (yyval.interm.qualifier) = EvqInOut; + } + + break; + + case 139: + + { + ES3_OR_NEWER("centroid", (yylsp[0]), "storage qualifier"); + (yyval.interm.qualifier) = EvqCentroid; + } + + break; + + case 140: + + { + context->checkIsAtGlobalLevel((yylsp[0]), "uniform"); + (yyval.interm.qualifier) = EvqUniform; + } + + break; + + case 141: + + { + (yyval.interm.type) = (yyvsp[0].interm.type); + + if ((yyval.interm.type).precision == EbpUndefined) { + (yyval.interm.type).precision = context->symbolTable.getDefaultPrecision((yyvsp[0].interm.type).getBasicType()); + } + } + + break; + + case 142: + + { + (yyval.interm.precision) = EbpHigh; + } + + break; + + case 143: + + { + (yyval.interm.precision) = EbpMedium; + } + + break; + + case 144: + + { + (yyval.interm.precision) = EbpLow; + } + + break; + + case 145: + + { + ES3_OR_NEWER("layout", (yylsp[-3]), "qualifier"); + (yyval.interm.layoutQualifier) = (yyvsp[-1].interm.layoutQualifier); + } + + break; + + case 146: + + { + (yyval.interm.layoutQualifier) = (yyvsp[0].interm.layoutQualifier); + } + + break; + + case 147: + + { + (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier), (yylsp[0])); + } + + break; + + case 148: + + { + (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[0].lex).string, (yylsp[0])); + } + + break; + + case 149: + + { + (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0])); + } + + break; + + case 150: + + { + (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0])); + } + + break; + + case 151: + + { + (yyval.interm.type).initialize((yyvsp[0].interm.typeSpecifierNonArray), (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); + } + + break; + + case 152: + + { + 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 153: + + { + (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 154: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtVoid, (yylsp[0])); + } + + break; + + case 155: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + } + + break; + + case 156: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0])); + } + + break; + + case 157: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0])); + } + + break; + + case 158: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0])); + } + + break; + + case 159: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(2); + } + + break; + + case 160: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(3); + } + + break; + + case 161: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(4); + } + + break; + + case 162: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(2); + } + + break; + + case 163: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(3); + } + + break; + + case 164: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtBool, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(4); + } + + break; + + case 165: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(2); + } + + break; + + case 166: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(3); + } + + break; + + case 167: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(4); + } + + break; + + case 168: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(2); + } + + break; + + case 169: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(3); + } + + break; + + case 170: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUInt, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setAggregate(4); + } + + break; + + case 171: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(2, 2); + } + + break; + + case 172: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(3, 3); + } + + break; + + case 173: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(4, 4); + } + + break; + + case 174: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(2, 3); + } + + break; + + case 175: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(3, 2); + } + + break; + + case 176: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(2, 4); + } + + break; + + case 177: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(4, 2); + } + + break; + + case 178: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(3, 4); + } + + break; + + case 179: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtFloat, (yylsp[0])); + (yyval.interm.typeSpecifierNonArray).setMatrix(4, 3); + } + + break; + + case 180: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2D, (yylsp[0])); + } + + break; + + case 181: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler3D, (yylsp[0])); + } + + break; + + case 182: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerCube, (yylsp[0])); + } + + break; + + case 183: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DArray, (yylsp[0])); + } + + break; + + case 184: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler2D, (yylsp[0])); + } + + break; + + case 185: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler3D, (yylsp[0])); + } + + break; + + case 186: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtISamplerCube, (yylsp[0])); + } + + break; + + case 187: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtISampler2DArray, (yylsp[0])); + } + + break; + + case 188: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler2D, (yylsp[0])); + } + + break; + + case 189: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler3D, (yylsp[0])); + } + + break; + + case 190: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUSamplerCube, (yylsp[0])); + } + + break; + + case 191: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtUSampler2DArray, (yylsp[0])); + } + + break; + + case 192: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DShadow, (yylsp[0])); + } + + break; + + case 193: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSamplerCubeShadow, (yylsp[0])); + } + + break; + + case 194: + + { + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DArrayShadow, (yylsp[0])); + } + + break; + + case 195: + + { + 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 196: + + { + if (!context->supportsExtension("GL_ARB_texture_rectangle")) { + context->error((yylsp[0]), "unsupported type", "sampler2DRect"); + } + (yyval.interm.typeSpecifierNonArray).initialize(EbtSampler2DRect, (yylsp[0])); + } + + break; + + case 197: + + { + (yyval.interm.typeSpecifierNonArray) = (yyvsp[0].interm.typeSpecifierNonArray); + } + + break; + + case 198: + + { + // + // 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 199: + + { context->enterStructDeclaration((yylsp[-1]), *(yyvsp[-1].lex).string); } + + break; + + case 200: + + { + (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-5]), (yylsp[-4]), (yyvsp[-4].lex).string, (yyvsp[-1].interm.fieldList)); + } + + break; + + case 201: + + { context->enterStructDeclaration((yylsp[0]), *(yyvsp[0].lex).string); } + + break; + + case 202: + + { + (yyval.interm.typeSpecifierNonArray) = context->addStructure((yylsp[-4]), (yyloc), NewPoolTString(""), (yyvsp[-1].interm.fieldList)); + } + + break; + + case 203: + + { + (yyval.interm.fieldList) = (yyvsp[0].interm.fieldList); + } + + break; + + case 204: + + { + (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 205: + + { + (yyval.interm.fieldList) = context->addStructDeclaratorList((yyvsp[-2].interm.type), (yyvsp[-1].interm.fieldList)); + } + + break; + + case 206: + + { + // 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 207: + + { + (yyval.interm.fieldList) = NewPoolTFieldList(); + (yyval.interm.fieldList)->push_back((yyvsp[0].interm.field)); + } + + break; + + case 208: + + { + (yyval.interm.fieldList)->push_back((yyvsp[0].interm.field)); + } + + break; + + case 209: + + { + 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 210: + + { + 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 211: + + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } + + break; + + case 212: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 213: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); } + + break; + + case 214: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 215: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 216: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 217: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 218: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermSwitch); } + + break; + + case 219: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermCase); } + + break; + + case 220: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 221: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 222: + + { (yyval.interm.intermBlock) = 0; } + + break; + + case 223: + + { context->symbolTable.push(); } + + break; + + case 224: + + { context->symbolTable.pop(); } + + break; + + case 225: + + { + if ((yyvsp[-2].interm.intermBlock) != 0) { + (yyvsp[-2].interm.intermBlock)->setLine((yyloc)); + } + (yyval.interm.intermBlock) = (yyvsp[-2].interm.intermBlock); + } + + break; + + case 226: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); } + + break; + + case 227: + + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 228: + + { context->symbolTable.push(); } + + break; + + case 229: + + { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermBlock); } + + break; + + case 230: + + { context->symbolTable.push(); } + + break; + + case 231: + + { context->symbolTable.pop(); (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } + + break; + + case 232: + + { + (yyval.interm.intermBlock) = 0; + } + + break; + + case 233: + + { + if ((yyvsp[-1].interm.intermBlock)) { + (yyvsp[-1].interm.intermBlock)->setLine((yyloc)); + } + (yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock); + } + + break; + + case 234: + + { + (yyval.interm.intermBlock) = new TIntermBlock(); + (yyval.interm.intermBlock)->setLine((yyloc)); + (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode)); + } + + break; + + case 235: + + { + (yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock); + (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode)); + } + + break; + + case 236: + + { (yyval.interm.intermNode) = 0; } + + break; + + case 237: + + { (yyval.interm.intermNode) = static_cast<TIntermNode*>((yyvsp[-1].interm.intermTypedNode)); } + + break; + + case 238: + + { + 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 239: + + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode); + } + + break; + + case 240: + + { + (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode); + (yyval.interm.nodePair).node2 = 0; + } + + break; + + case 241: + + { context->incrSwitchNestingLevel(); } + + break; + + case 242: + + { + (yyval.interm.intermSwitch) = context->addSwitch((yyvsp[-3].interm.intermTypedNode), (yyvsp[0].interm.intermBlock), (yylsp[-5])); + context->decrSwitchNestingLevel(); + } + + break; + + case 243: + + { + (yyval.interm.intermCase) = context->addCase((yyvsp[-1].interm.intermTypedNode), (yylsp[-2])); + } + + break; + + case 244: + + { + (yyval.interm.intermCase) = context->addDefault((yylsp[-1])); + } + + break; + + case 245: + + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + context->checkIsScalarBool((yyvsp[0].interm.intermTypedNode)->getLine(), (yyvsp[0].interm.intermTypedNode)); + } + + break; + + case 246: + + { + TIntermNode *intermNode; + 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), &intermNode)) + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + else { + (yyval.interm.intermTypedNode) = 0; + } + } + + break; + + case 247: + + { context->symbolTable.push(); context->incrLoopNestingLevel(); } + + break; + + case 248: + + { + 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 249: + + { context->incrLoopNestingLevel(); } + + break; + + case 250: + + { + 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 251: + + { context->symbolTable.push(); context->incrLoopNestingLevel(); } + + break; + + case 252: + + { + 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 253: + + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } + + break; + + case 254: + + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } + + break; + + case 255: + + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + + break; + + case 256: + + { + (yyval.interm.intermTypedNode) = 0; + } + + break; + + case 257: + + { + (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = 0; + } + + break; + + case 258: + + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode); + } + + break; + + case 259: + + { + (yyval.interm.intermNode) = context->addBranch(EOpContinue, (yylsp[-1])); + } + + break; + + case 260: + + { + (yyval.interm.intermNode) = context->addBranch(EOpBreak, (yylsp[-1])); + } + + break; + + case 261: + + { + (yyval.interm.intermNode) = context->addBranch(EOpReturn, (yylsp[-1])); + } + + break; + + case 262: + + { + (yyval.interm.intermNode) = context->addBranch(EOpReturn, (yyvsp[-1].interm.intermTypedNode), (yylsp[-2])); + } + + break; + + case 263: + + { + FRAG_ONLY("discard", (yylsp[-1])); + (yyval.interm.intermNode) = context->addBranch(EOpKill, (yylsp[-1])); + } + + break; + + case 264: + + { + (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 265: + + { + (yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode)); + } + + break; + + case 266: + + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } + + break; + + case 267: + + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } + + break; + + case 268: + + { + context->parseFunctionDefinitionHeader((yylsp[0]), &((yyvsp[0].interm).function), &(yyvsp[0].interm).intermAggregate); + } + + break; + + case 269: + + { + (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/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.h b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.h new file mode 100644 index 000000000..74a56da04 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/glslang_tab.h @@ -0,0 +1,253 @@ +/* A Bison parser, made by GNU Bison 3.0.4. */ + +/* Apple Note: For the avoidance of doubt, Apple elects to distribute this file under the terms of the BSD license. */ + +/* 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, + STRUCT = 309, + VOID_TYPE = 310, + WHILE = 311, + SAMPLER2D = 312, + SAMPLERCUBE = 313, + SAMPLER_EXTERNAL_OES = 314, + SAMPLER2DRECT = 315, + SAMPLER2DARRAY = 316, + ISAMPLER2D = 317, + ISAMPLER3D = 318, + ISAMPLERCUBE = 319, + ISAMPLER2DARRAY = 320, + USAMPLER2D = 321, + USAMPLER3D = 322, + USAMPLERCUBE = 323, + USAMPLER2DARRAY = 324, + SAMPLER3D = 325, + SAMPLER3DRECT = 326, + SAMPLER2DSHADOW = 327, + SAMPLERCUBESHADOW = 328, + SAMPLER2DARRAYSHADOW = 329, + LAYOUT = 330, + IDENTIFIER = 331, + TYPE_NAME = 332, + FLOATCONSTANT = 333, + INTCONSTANT = 334, + UINTCONSTANT = 335, + BOOLCONSTANT = 336, + FIELD_SELECTION = 337, + LEFT_OP = 338, + RIGHT_OP = 339, + INC_OP = 340, + DEC_OP = 341, + LE_OP = 342, + GE_OP = 343, + EQ_OP = 344, + NE_OP = 345, + AND_OP = 346, + OR_OP = 347, + XOR_OP = 348, + MUL_ASSIGN = 349, + DIV_ASSIGN = 350, + ADD_ASSIGN = 351, + MOD_ASSIGN = 352, + LEFT_ASSIGN = 353, + RIGHT_ASSIGN = 354, + AND_ASSIGN = 355, + XOR_ASSIGN = 356, + OR_ASSIGN = 357, + SUB_ASSIGN = 358, + LEFT_PAREN = 359, + RIGHT_PAREN = 360, + LEFT_BRACKET = 361, + RIGHT_BRACKET = 362, + LEFT_BRACE = 363, + RIGHT_BRACE = 364, + DOT = 365, + COMMA = 366, + COLON = 367, + EQUAL = 368, + SEMICOLON = 369, + BANG = 370, + DASH = 371, + TILDE = 372, + PLUS = 373, + STAR = 374, + SLASH = 375, + PERCENT = 376, + LEFT_ANGLE = 377, + RIGHT_ANGLE = 378, + VERTICAL_BAR = 379, + CARET = 380, + AMPERSAND = 381, + QUESTION = 382 + }; +#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; + 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/Source/ThirdParty/ANGLE/src/compiler/translator/intermOut.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/intermOut.cpp new file mode 100644 index 000000000..352fde35a --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/intermOut.cpp @@ -0,0 +1,723 @@ +// +// 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 +{ + +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 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 EOpDeclaration: out << "Declaration: "; 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::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); +} diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/length_limits.h b/Source/ThirdParty/ANGLE/src/compiler/translator/length_limits.h new file mode 100644 index 000000000..88634381f --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/length_limits.h @@ -0,0 +1,21 @@ +// +// 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. + +size_t GetGlobalMaxTokenSize(ShShaderSpec spec); + +#endif // COMPILER_TRANSLATOR_LENGTHLIMITS_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/translator/util.cpp b/Source/ThirdParty/ANGLE/src/compiler/translator/util.cpp new file mode 100644 index 000000000..584d65377 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/util.cpp @@ -0,0 +1,646 @@ +// +// 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; + 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 GetInterfaceBlockType(const sh::InterfaceBlock &block) +{ + TType type; + TFieldList *fields = new TFieldList; + TSourceLoc loc; + for (const auto &field : block.fields) + { + TType *fieldType = new TType(GetShaderVariableType(field)); + fields->push_back(new TField(fieldType, new TString(field.name.c_str()), loc)); + } + + TInterfaceBlock *interfaceBlock = new TInterfaceBlock( + new TString(block.name.c_str()), fields, new TString(block.instanceName.c_str()), + block.arraySize, TLayoutQualifier::create()); + + type.setBasicType(EbtInterfaceBlock); + type.setInterfaceBlock(interfaceBlock); + type.setArraySize(block.arraySize); + return type; +} + +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(); + } +} + +TType GetShaderVariableType(const sh::ShaderVariable &var) +{ + TType type; + if (var.isStruct()) + { + TFieldList *fields = new TFieldList; + TSourceLoc loc; + for (const auto &field : var.fields) + { + TType *fieldType = new TType(GetShaderVariableType(field)); + fields->push_back(new TField(fieldType, new TString(field.name.c_str()), loc)); + } + TStructure *structure = new TStructure(new TString(var.structName.c_str()), fields); + + type.setBasicType(EbtStruct); + type.setStruct(structure); + } + else + { + type = GetShaderVariableBasicType(var); + } + + if (var.isArray()) + { + type.setArraySize(var.elementCount()); + } + return type; +} + +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/Source/ThirdParty/ANGLE/src/compiler/translator/util.h b/Source/ThirdParty/ANGLE/src/compiler/translator/util.h new file mode 100644 index 000000000..b52689165 --- /dev/null +++ b/Source/ThirdParty/ANGLE/src/compiler/translator/util.h @@ -0,0 +1,76 @@ +// +// 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); + +class TSymbolTable; + +namespace sh +{ + +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 GetInterfaceBlockType(const sh::InterfaceBlock &block); +TType GetShaderVariableBasicType(const sh::ShaderVariable &var); +TType GetShaderVariableType(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); +} + +#endif // COMPILER_TRANSLATOR_UTIL_H_ diff --git a/Source/ThirdParty/ANGLE/src/compiler/util.cpp b/Source/ThirdParty/ANGLE/src/compiler/util.cpp deleted file mode 100644 index d6e5eeed9..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/util.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// 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/util.h" - -#include <limits> - -#include "compiler/preprocessor/numeric_lex.h" - -bool atof_clamp(const char *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, int *value) -{ - bool success = pp::numeric_lex_int(str, value); - if (!success) - *value = std::numeric_limits<int>::max(); - return success; -} - diff --git a/Source/ThirdParty/ANGLE/src/compiler/util.h b/Source/ThirdParty/ANGLE/src/compiler/util.h deleted file mode 100644 index dc69f3906..000000000 --- a/Source/ThirdParty/ANGLE/src/compiler/util.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// 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_UTIL_H -#define COMPILER_UTIL_H - -// atof_clamp is like atof 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. -extern bool atof_clamp(const char *str, float *value); - -// If overflow happens, clamp the value to INT_MIN or INT_MAX. -// Return false if overflow happens. -extern bool atoi_clamp(const char *str, int *value); - -#endif // COMPILER_UTIL_H |