aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/sksl
diff options
context:
space:
mode:
authorGravatar ethannicholas <ethannicholas@google.com>2016-07-01 08:22:01 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-07-01 08:22:01 -0700
commitb3058bdb1049ca75d526eb9f11e1a42a49e63585 (patch)
tree9d72636cd37c2100869fcd02041072b19b86a717 /src/sksl
parente7d1b24ff0a04686aef54fcb9feaca7a03e19891 (diff)
initial checkin of SkSL compiler
BUG=skia: GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1984363002 CQ_EXTRA_TRYBOTS=client.skia.compile:Build-Ubuntu-GCC-x86_64-Release-CMake-Trybot,Build-Mac-Clang-x86_64-Release-CMake-Trybot Review-Url: https://codereview.chromium.org/1984363002
Diffstat (limited to 'src/sksl')
-rw-r--r--src/sksl/GLSL.std.450.h131
-rw-r--r--src/sksl/SkSLCodeGenerator.h30
-rw-r--r--src/sksl/SkSLCompiler.cpp243
-rw-r--r--src/sksl/SkSLCompiler.h59
-rw-r--r--src/sksl/SkSLErrorReporter.h27
-rw-r--r--src/sksl/SkSLIRGenerator.cpp1217
-rw-r--r--src/sksl/SkSLIRGenerator.h122
-rw-r--r--src/sksl/SkSLMain.cpp48
-rw-r--r--src/sksl/SkSLParser.cpp1389
-rw-r--r--src/sksl/SkSLParser.h209
-rw-r--r--src/sksl/SkSLPosition.h38
-rw-r--r--src/sksl/SkSLSPIRVCodeGenerator.cpp2628
-rw-r--r--src/sksl/SkSLSPIRVCodeGenerator.h264
-rw-r--r--src/sksl/SkSLToken.h156
-rw-r--r--src/sksl/SkSLUtil.cpp33
-rw-r--r--src/sksl/SkSLUtil.h60
-rw-r--r--src/sksl/ast/SkSLASTBinaryExpression.h42
-rw-r--r--src/sksl/ast/SkSLASTBlock.h40
-rw-r--r--src/sksl/ast/SkSLASTBoolLiteral.h34
-rw-r--r--src/sksl/ast/SkSLASTBreakStatement.h31
-rw-r--r--src/sksl/ast/SkSLASTCallSuffix.h44
-rw-r--r--src/sksl/ast/SkSLASTContinueStatement.h31
-rw-r--r--src/sksl/ast/SkSLASTDeclaration.h37
-rw-r--r--src/sksl/ast/SkSLASTDiscardStatement.h31
-rw-r--r--src/sksl/ast/SkSLASTDoStatement.h37
-rw-r--r--src/sksl/ast/SkSLASTExpression.h41
-rw-r--r--src/sksl/ast/SkSLASTExpressionStatement.h34
-rw-r--r--src/sksl/ast/SkSLASTExtension.h34
-rw-r--r--src/sksl/ast/SkSLASTFieldSuffix.h35
-rw-r--r--src/sksl/ast/SkSLASTFloatLiteral.h34
-rw-r--r--src/sksl/ast/SkSLASTForStatement.h56
-rw-r--r--src/sksl/ast/SkSLASTFunction.h57
-rw-r--r--src/sksl/ast/SkSLASTIdentifier.h34
-rw-r--r--src/sksl/ast/SkSLASTIfStatement.h47
-rw-r--r--src/sksl/ast/SkSLASTIndexSuffix.h35
-rw-r--r--src/sksl/ast/SkSLASTIntLiteral.h35
-rw-r--r--src/sksl/ast/SkSLASTInterfaceBlock.h58
-rw-r--r--src/sksl/ast/SkSLASTLayout.h68
-rw-r--r--src/sksl/ast/SkSLASTModifiers.h70
-rw-r--r--src/sksl/ast/SkSLASTNode.h28
-rw-r--r--src/sksl/ast/SkSLASTParameter.h48
-rw-r--r--src/sksl/ast/SkSLASTPositionNode.h28
-rw-r--r--src/sksl/ast/SkSLASTPrefixExpression.h37
-rw-r--r--src/sksl/ast/SkSLASTReturnStatement.h39
-rw-r--r--src/sksl/ast/SkSLASTStatement.h46
-rw-r--r--src/sksl/ast/SkSLASTSuffix.h51
-rw-r--r--src/sksl/ast/SkSLASTSuffixExpression.h37
-rw-r--r--src/sksl/ast/SkSLASTTernaryExpression.h41
-rw-r--r--src/sksl/ast/SkSLASTType.h40
-rw-r--r--src/sksl/ast/SkSLASTVarDeclaration.h71
-rw-r--r--src/sksl/ast/SkSLASTVarDeclarationStatement.h35
-rw-r--r--src/sksl/ast/SkSLASTWhileStatement.h37
-rw-r--r--src/sksl/ir/SkSLBinaryExpression.h41
-rw-r--r--src/sksl/ir/SkSLBlock.h40
-rw-r--r--src/sksl/ir/SkSLBoolLiteral.h38
-rw-r--r--src/sksl/ir/SkSLBreakStatement.h32
-rw-r--r--src/sksl/ir/SkSLConstructor.h52
-rw-r--r--src/sksl/ir/SkSLContinueStatement.h32
-rw-r--r--src/sksl/ir/SkSLDiscardStatement.h32
-rw-r--r--src/sksl/ir/SkSLDoStatement.h38
-rw-r--r--src/sksl/ir/SkSLExpression.h55
-rw-r--r--src/sksl/ir/SkSLExpressionStatement.h35
-rw-r--r--src/sksl/ir/SkSLExtension.h34
-rw-r--r--src/sksl/ir/SkSLField.h41
-rw-r--r--src/sksl/ir/SkSLFieldAccess.h37
-rw-r--r--src/sksl/ir/SkSLFloatLiteral.h38
-rw-r--r--src/sksl/ir/SkSLForStatement.h56
-rw-r--r--src/sksl/ir/SkSLFunctionCall.h46
-rw-r--r--src/sksl/ir/SkSLFunctionDeclaration.h55
-rw-r--r--src/sksl/ir/SkSLFunctionDefinition.h39
-rw-r--r--src/sksl/ir/SkSLFunctionReference.h36
-rw-r--r--src/sksl/ir/SkSLIRNode.h32
-rw-r--r--src/sksl/ir/SkSLIfStatement.h44
-rw-r--r--src/sksl/ir/SkSLIndexExpression.h64
-rw-r--r--src/sksl/ir/SkSLIntLiteral.h40
-rw-r--r--src/sksl/ir/SkSLInterfaceBlock.h49
-rw-r--r--src/sksl/ir/SkSLLayout.h83
-rw-r--r--src/sksl/ir/SkSLModifiers.h82
-rw-r--r--src/sksl/ir/SkSLPostfixExpression.h36
-rw-r--r--src/sksl/ir/SkSLPrefixExpression.h36
-rw-r--r--src/sksl/ir/SkSLProgram.h38
-rw-r--r--src/sksl/ir/SkSLProgramElement.h37
-rw-r--r--src/sksl/ir/SkSLReturnStatement.h42
-rw-r--r--src/sksl/ir/SkSLStatement.h45
-rw-r--r--src/sksl/ir/SkSLSwizzle.h88
-rw-r--r--src/sksl/ir/SkSLSymbol.h40
-rw-r--r--src/sksl/ir/SkSLSymbolTable.cpp85
-rw-r--r--src/sksl/ir/SkSLSymbolTable.h49
-rw-r--r--src/sksl/ir/SkSLTernaryExpression.h43
-rw-r--r--src/sksl/ir/SkSLType.cpp258
-rw-r--r--src/sksl/ir/SkSLType.h438
-rw-r--r--src/sksl/ir/SkSLTypeReference.h36
-rw-r--r--src/sksl/ir/SkSLUnresolvedFunction.h38
-rw-r--r--src/sksl/ir/SkSLVarDeclaration.h67
-rw-r--r--src/sksl/ir/SkSLVarDeclarationStatement.h35
-rw-r--r--src/sksl/ir/SkSLVariable.h66
-rw-r--r--src/sksl/ir/SkSLVariableReference.h38
-rw-r--r--src/sksl/ir/SkSLWhileStatement.h38
-rw-r--r--src/sksl/lex.sksl.c2473
-rw-r--r--src/sksl/sksl.flex187
-rw-r--r--src/sksl/sksl.include543
-rw-r--r--src/sksl/sksl_frag.include7
-rw-r--r--src/sksl/sksl_vert.include10
-rw-r--r--src/sksl/spirv.h870
104 files changed, 14971 insertions, 0 deletions
diff --git a/src/sksl/GLSL.std.450.h b/src/sksl/GLSL.std.450.h
new file mode 100644
index 0000000000..54cc00e9a8
--- /dev/null
+++ b/src/sksl/GLSL.std.450.h
@@ -0,0 +1,131 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+#ifndef GLSLstd450_H
+#define GLSLstd450_H
+
+static const int GLSLstd450Version = 100;
+static const int GLSLstd450Revision = 3;
+
+enum GLSLstd450 {
+ GLSLstd450Bad = 0, // Don't use
+
+ GLSLstd450Round = 1,
+ GLSLstd450RoundEven = 2,
+ GLSLstd450Trunc = 3,
+ GLSLstd450FAbs = 4,
+ GLSLstd450SAbs = 5,
+ GLSLstd450FSign = 6,
+ GLSLstd450SSign = 7,
+ GLSLstd450Floor = 8,
+ GLSLstd450Ceil = 9,
+ GLSLstd450Fract = 10,
+
+ GLSLstd450Radians = 11,
+ GLSLstd450Degrees = 12,
+ GLSLstd450Sin = 13,
+ GLSLstd450Cos = 14,
+ GLSLstd450Tan = 15,
+ GLSLstd450Asin = 16,
+ GLSLstd450Acos = 17,
+ GLSLstd450Atan = 18,
+ GLSLstd450Sinh = 19,
+ GLSLstd450Cosh = 20,
+ GLSLstd450Tanh = 21,
+ GLSLstd450Asinh = 22,
+ GLSLstd450Acosh = 23,
+ GLSLstd450Atanh = 24,
+ GLSLstd450Atan2 = 25,
+
+ GLSLstd450Pow = 26,
+ GLSLstd450Exp = 27,
+ GLSLstd450Log = 28,
+ GLSLstd450Exp2 = 29,
+ GLSLstd450Log2 = 30,
+ GLSLstd450Sqrt = 31,
+ GLSLstd450InverseSqrt = 32,
+
+ GLSLstd450Determinant = 33,
+ GLSLstd450MatrixInverse = 34,
+
+ GLSLstd450Modf = 35, // second operand needs an OpVariable to write to
+ GLSLstd450ModfStruct = 36, // no OpVariable operand
+ GLSLstd450FMin = 37,
+ GLSLstd450UMin = 38,
+ GLSLstd450SMin = 39,
+ GLSLstd450FMax = 40,
+ GLSLstd450UMax = 41,
+ GLSLstd450SMax = 42,
+ GLSLstd450FClamp = 43,
+ GLSLstd450UClamp = 44,
+ GLSLstd450SClamp = 45,
+ GLSLstd450FMix = 46,
+ GLSLstd450IMix = 47, // Reserved
+ GLSLstd450Step = 48,
+ GLSLstd450SmoothStep = 49,
+
+ GLSLstd450Fma = 50,
+ GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to
+ GLSLstd450FrexpStruct = 52, // no OpVariable operand
+ GLSLstd450Ldexp = 53,
+
+ GLSLstd450PackSnorm4x8 = 54,
+ GLSLstd450PackUnorm4x8 = 55,
+ GLSLstd450PackSnorm2x16 = 56,
+ GLSLstd450PackUnorm2x16 = 57,
+ GLSLstd450PackHalf2x16 = 58,
+ GLSLstd450PackDouble2x32 = 59,
+ GLSLstd450UnpackSnorm2x16 = 60,
+ GLSLstd450UnpackUnorm2x16 = 61,
+ GLSLstd450UnpackHalf2x16 = 62,
+ GLSLstd450UnpackSnorm4x8 = 63,
+ GLSLstd450UnpackUnorm4x8 = 64,
+ GLSLstd450UnpackDouble2x32 = 65,
+
+ GLSLstd450Length = 66,
+ GLSLstd450Distance = 67,
+ GLSLstd450Cross = 68,
+ GLSLstd450Normalize = 69,
+ GLSLstd450FaceForward = 70,
+ GLSLstd450Reflect = 71,
+ GLSLstd450Refract = 72,
+
+ GLSLstd450FindILsb = 73,
+ GLSLstd450FindSMsb = 74,
+ GLSLstd450FindUMsb = 75,
+
+ GLSLstd450InterpolateAtCentroid = 76,
+ GLSLstd450InterpolateAtSample = 77,
+ GLSLstd450InterpolateAtOffset = 78,
+
+ GLSLstd450NMin = 79,
+ GLSLstd450NMax = 80,
+ GLSLstd450NClamp = 81,
+
+ GLSLstd450Count
+};
+
+#endif // #ifndef GLSLstd450_H
diff --git a/src/sksl/SkSLCodeGenerator.h b/src/sksl/SkSLCodeGenerator.h
new file mode 100644
index 0000000000..cd50cc88fc
--- /dev/null
+++ b/src/sksl/SkSLCodeGenerator.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_CODEGENERATOR
+#define SKSL_CODEGENERATOR
+
+#include "ir/SkSLProgram.h"
+#include <vector>
+#include <iostream>
+
+namespace SkSL {
+
+/**
+ * Abstract superclass of all code generators, which take a Program as input and produce code as
+ * output.
+ */
+class CodeGenerator {
+public:
+ virtual ~CodeGenerator() {}
+
+ virtual void generateCode(Program& program, std::ostream& out) = 0;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
new file mode 100644
index 0000000000..2b4adc1026
--- /dev/null
+++ b/src/sksl/SkSLCompiler.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+
+#include <fstream>
+#include <streambuf>
+
+#include "SkSLIRGenerator.h"
+#include "SkSLParser.h"
+#include "SkSLSPIRVCodeGenerator.h"
+#include "ir/SkSLExpression.h"
+#include "ir/SkSLIntLiteral.h"
+#include "ir/SkSLSymbolTable.h"
+#include "ir/SkSLVarDeclaration.h"
+#include "SkMutex.h"
+
+#define STRINGIFY(x) #x
+
+// include the built-in shader symbols as static strings
+
+static std::string SKSL_INCLUDE =
+#include "sksl.include"
+;
+
+static std::string SKSL_VERT_INCLUDE =
+#include "sksl_vert.include"
+;
+
+static std::string SKSL_FRAG_INCLUDE =
+#include "sksl_frag.include"
+;
+
+namespace SkSL {
+
+Compiler::Compiler()
+: fErrorCount(0) {
+ auto types = std::shared_ptr<SymbolTable>(new SymbolTable(*this));
+ auto symbols = std::shared_ptr<SymbolTable>(new SymbolTable(types, *this));
+ fIRGenerator = new IRGenerator(symbols, *this);
+ fTypes = types;
+ #define ADD_TYPE(t) types->add(k ## t ## _Type->fName, k ## t ## _Type)
+ ADD_TYPE(Void);
+ ADD_TYPE(Float);
+ ADD_TYPE(Vec2);
+ ADD_TYPE(Vec3);
+ ADD_TYPE(Vec4);
+ ADD_TYPE(Double);
+ ADD_TYPE(DVec2);
+ ADD_TYPE(DVec3);
+ ADD_TYPE(DVec4);
+ ADD_TYPE(Int);
+ ADD_TYPE(IVec2);
+ ADD_TYPE(IVec3);
+ ADD_TYPE(IVec4);
+ ADD_TYPE(UInt);
+ ADD_TYPE(UVec2);
+ ADD_TYPE(UVec3);
+ ADD_TYPE(UVec4);
+ ADD_TYPE(Bool);
+ ADD_TYPE(BVec2);
+ ADD_TYPE(BVec3);
+ ADD_TYPE(BVec4);
+ ADD_TYPE(Mat2x2);
+ ADD_TYPE(Mat2x3);
+ ADD_TYPE(Mat2x4);
+ ADD_TYPE(Mat3x2);
+ ADD_TYPE(Mat3x3);
+ ADD_TYPE(Mat3x4);
+ ADD_TYPE(Mat4x2);
+ ADD_TYPE(Mat4x3);
+ ADD_TYPE(Mat4x4);
+ ADD_TYPE(GenType);
+ ADD_TYPE(GenDType);
+ ADD_TYPE(GenIType);
+ ADD_TYPE(GenUType);
+ ADD_TYPE(GenBType);
+ ADD_TYPE(Mat);
+ ADD_TYPE(Vec);
+ ADD_TYPE(GVec);
+ ADD_TYPE(GVec2);
+ ADD_TYPE(GVec3);
+ ADD_TYPE(GVec4);
+ ADD_TYPE(DVec);
+ ADD_TYPE(IVec);
+ ADD_TYPE(UVec);
+ ADD_TYPE(BVec);
+
+ ADD_TYPE(Sampler1D);
+ ADD_TYPE(Sampler2D);
+ ADD_TYPE(Sampler3D);
+ ADD_TYPE(SamplerCube);
+ ADD_TYPE(Sampler2DRect);
+ ADD_TYPE(Sampler1DArray);
+ ADD_TYPE(Sampler2DArray);
+ ADD_TYPE(SamplerCubeArray);
+ ADD_TYPE(SamplerBuffer);
+ ADD_TYPE(Sampler2DMS);
+ ADD_TYPE(Sampler2DMSArray);
+
+ ADD_TYPE(GSampler1D);
+ ADD_TYPE(GSampler2D);
+ ADD_TYPE(GSampler3D);
+ ADD_TYPE(GSamplerCube);
+ ADD_TYPE(GSampler2DRect);
+ ADD_TYPE(GSampler1DArray);
+ ADD_TYPE(GSampler2DArray);
+ ADD_TYPE(GSamplerCubeArray);
+ ADD_TYPE(GSamplerBuffer);
+ ADD_TYPE(GSampler2DMS);
+ ADD_TYPE(GSampler2DMSArray);
+
+ ADD_TYPE(Sampler1DShadow);
+ ADD_TYPE(Sampler2DShadow);
+ ADD_TYPE(SamplerCubeShadow);
+ ADD_TYPE(Sampler2DRectShadow);
+ ADD_TYPE(Sampler1DArrayShadow);
+ ADD_TYPE(Sampler2DArrayShadow);
+ ADD_TYPE(SamplerCubeArrayShadow);
+ ADD_TYPE(GSampler2DArrayShadow);
+ ADD_TYPE(GSamplerCubeArrayShadow);
+
+ std::vector<std::unique_ptr<ProgramElement>> ignored;
+ this->internalConvertProgram(SKSL_INCLUDE, &ignored);
+ ASSERT(!fErrorCount);
+}
+
+Compiler::~Compiler() {
+ delete fIRGenerator;
+}
+
+void Compiler::internalConvertProgram(std::string text,
+ std::vector<std::unique_ptr<ProgramElement>>* result) {
+ Parser parser(text, *fTypes, *this);
+ std::vector<std::unique_ptr<ASTDeclaration>> parsed = parser.file();
+ if (fErrorCount) {
+ return;
+ }
+ for (size_t i = 0; i < parsed.size(); i++) {
+ ASTDeclaration& decl = *parsed[i];
+ switch (decl.fKind) {
+ case ASTDeclaration::kVar_Kind: {
+ std::unique_ptr<VarDeclaration> s = fIRGenerator->convertVarDeclaration(
+ (ASTVarDeclaration&) decl,
+ Variable::kGlobal_Storage);
+ if (s) {
+ result->push_back(std::move(s));
+ }
+ break;
+ }
+ case ASTDeclaration::kFunction_Kind: {
+ std::unique_ptr<FunctionDefinition> f = fIRGenerator->convertFunction(
+ (ASTFunction&) decl);
+ if (f) {
+ result->push_back(std::move(f));
+ }
+ break;
+ }
+ case ASTDeclaration::kInterfaceBlock_Kind: {
+ std::unique_ptr<InterfaceBlock> i = fIRGenerator->convertInterfaceBlock(
+ (ASTInterfaceBlock&) decl);
+ if (i) {
+ result->push_back(std::move(i));
+ }
+ break;
+ }
+ case ASTDeclaration::kExtension_Kind: {
+ std::unique_ptr<Extension> e = fIRGenerator->convertExtension((ASTExtension&) decl);
+ if (e) {
+ result->push_back(std::move(e));
+ }
+ break;
+ }
+ default:
+ ABORT("unsupported declaration: %s\n", decl.description().c_str());
+ }
+ }
+}
+
+std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, std::string text) {
+ fErrorText = "";
+ fErrorCount = 0;
+ fIRGenerator->pushSymbolTable();
+ std::vector<std::unique_ptr<ProgramElement>> result;
+ switch (kind) {
+ case Program::kVertex_Kind:
+ this->internalConvertProgram(SKSL_VERT_INCLUDE, &result);
+ break;
+ case Program::kFragment_Kind:
+ this->internalConvertProgram(SKSL_FRAG_INCLUDE, &result);
+ break;
+ }
+ this->internalConvertProgram(text, &result);
+ fIRGenerator->popSymbolTable();
+ this->writeErrorCount();
+ return std::unique_ptr<Program>(new Program(kind, std::move(result)));;
+}
+
+void Compiler::error(Position position, std::string msg) {
+ fErrorCount++;
+ fErrorText += "error: " + position.description() + ": " + msg.c_str() + "\n";
+}
+
+std::string Compiler::errorText() {
+ std::string result = fErrorText;
+ return result;
+}
+
+void Compiler::writeErrorCount() {
+ if (fErrorCount) {
+ fErrorText += to_string(fErrorCount) + " error";
+ if (fErrorCount > 1) {
+ fErrorText += "s";
+ }
+ fErrorText += "\n";
+ }
+}
+
+#include <fstream>
+bool Compiler::toSPIRV(Program::Kind kind, std::string text, std::ostream& out) {
+ auto program = this->convertProgram(kind, text);
+ if (fErrorCount == 0) {
+ SkSL::SPIRVCodeGenerator cg;
+ cg.generateCode(*program.get(), out);
+ ASSERT(!out.rdstate());
+ }
+ return fErrorCount == 0;
+}
+
+bool Compiler::toSPIRV(Program::Kind kind, std::string text, std::string* out) {
+ std::stringstream buffer;
+ bool result = this->toSPIRV(kind, text, buffer);
+ if (result) {
+ *out = buffer.str();
+ }
+ return fErrorCount == 0;
+}
+
+} // namespace
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
new file mode 100644
index 0000000000..2209427eef
--- /dev/null
+++ b/src/sksl/SkSLCompiler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_COMPILER
+#define SKSL_COMPILER
+
+#include <vector>
+#include "ir/SkSLProgram.h"
+#include "ir/SkSLSymbolTable.h"
+#include "SkSLErrorReporter.h"
+
+namespace SkSL {
+
+class IRGenerator;
+
+/**
+ * Main compiler entry point. This is a traditional compiler design which first parses the .sksl
+ * file into an abstract syntax tree (a tree of ASTNodes), then performs semantic analysis to
+ * produce a Program (a tree of IRNodes), then feeds the Program into a CodeGenerator to produce
+ * compiled output.
+ */
+class Compiler : public ErrorReporter {
+public:
+ Compiler();
+
+ ~Compiler();
+
+ std::unique_ptr<Program> convertProgram(Program::Kind kind, std::string text);
+
+ bool toSPIRV(Program::Kind kind, std::string text, std::ostream& out);
+
+ bool toSPIRV(Program::Kind kind, std::string text, std::string* out);
+
+ void error(Position position, std::string msg) override;
+
+ std::string errorText();
+
+ void writeErrorCount();
+
+private:
+
+ void internalConvertProgram(std::string text,
+ std::vector<std::unique_ptr<ProgramElement>>* result);
+
+ std::shared_ptr<SymbolTable> fTypes;
+ IRGenerator* fIRGenerator;
+ std::string fSkiaVertText; // FIXME store parsed version instead
+
+ int fErrorCount;
+ std::string fErrorText;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLErrorReporter.h b/src/sksl/SkSLErrorReporter.h
new file mode 100644
index 0000000000..26b44711c3
--- /dev/null
+++ b/src/sksl/SkSLErrorReporter.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ERRORREPORTER
+#define SKSL_ERRORREPORTER
+
+#include "SkSLPosition.h"
+
+namespace SkSL {
+
+/**
+ * Interface for the compiler to report errors.
+ */
+class ErrorReporter {
+public:
+ virtual ~ErrorReporter() {}
+
+ virtual void error(Position position, std::string msg) = 0;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
new file mode 100644
index 0000000000..2cc7eacb4d
--- /dev/null
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -0,0 +1,1217 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLIRGenerator.h"
+
+#include "limits.h"
+
+#include "ast/SkSLASTBoolLiteral.h"
+#include "ast/SkSLASTFieldSuffix.h"
+#include "ast/SkSLASTFloatLiteral.h"
+#include "ast/SkSLASTIndexSuffix.h"
+#include "ast/SkSLASTIntLiteral.h"
+#include "ir/SkSLBinaryExpression.h"
+#include "ir/SkSLBoolLiteral.h"
+#include "ir/SkSLBreakStatement.h"
+#include "ir/SkSLConstructor.h"
+#include "ir/SkSLContinueStatement.h"
+#include "ir/SkSLDiscardStatement.h"
+#include "ir/SkSLDoStatement.h"
+#include "ir/SkSLExpressionStatement.h"
+#include "ir/SkSLField.h"
+#include "ir/SkSLFieldAccess.h"
+#include "ir/SkSLFloatLiteral.h"
+#include "ir/SkSLForStatement.h"
+#include "ir/SkSLFunctionCall.h"
+#include "ir/SkSLFunctionDeclaration.h"
+#include "ir/SkSLFunctionDefinition.h"
+#include "ir/SkSLFunctionReference.h"
+#include "ir/SkSLIfStatement.h"
+#include "ir/SkSLIndexExpression.h"
+#include "ir/SkSLInterfaceBlock.h"
+#include "ir/SkSLIntLiteral.h"
+#include "ir/SkSLLayout.h"
+#include "ir/SkSLPostfixExpression.h"
+#include "ir/SkSLPrefixExpression.h"
+#include "ir/SkSLReturnStatement.h"
+#include "ir/SkSLSwizzle.h"
+#include "ir/SkSLTernaryExpression.h"
+#include "ir/SkSLUnresolvedFunction.h"
+#include "ir/SkSLVariable.h"
+#include "ir/SkSLVarDeclaration.h"
+#include "ir/SkSLVarDeclarationStatement.h"
+#include "ir/SkSLVariableReference.h"
+#include "ir/SkSLWhileStatement.h"
+
+namespace SkSL {
+
+class AutoSymbolTable {
+public:
+ AutoSymbolTable(IRGenerator* ir)
+ : fIR(ir)
+ , fPrevious(fIR->fSymbolTable) {
+ fIR->pushSymbolTable();
+ }
+
+ ~AutoSymbolTable() {
+ fIR->popSymbolTable();
+ ASSERT(fPrevious == fIR->fSymbolTable);
+ }
+
+ IRGenerator* fIR;
+ std::shared_ptr<SymbolTable> fPrevious;
+};
+
+IRGenerator::IRGenerator(std::shared_ptr<SymbolTable> symbolTable,
+ ErrorReporter& errorReporter)
+: fSymbolTable(std::move(symbolTable))
+, fErrors(errorReporter) {
+}
+
+void IRGenerator::pushSymbolTable() {
+ fSymbolTable.reset(new SymbolTable(std::move(fSymbolTable), fErrors));
+}
+
+void IRGenerator::popSymbolTable() {
+ fSymbolTable = fSymbolTable->fParent;
+}
+
+std::unique_ptr<Extension> IRGenerator::convertExtension(const ASTExtension& extension) {
+ return std::unique_ptr<Extension>(new Extension(extension.fPosition, extension.fName));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertStatement(const ASTStatement& statement) {
+ switch (statement.fKind) {
+ case ASTStatement::kBlock_Kind:
+ return this->convertBlock((ASTBlock&) statement);
+ case ASTStatement::kVarDeclaration_Kind:
+ return this->convertVarDeclarationStatement((ASTVarDeclarationStatement&) statement);
+ case ASTStatement::kExpression_Kind:
+ return this->convertExpressionStatement((ASTExpressionStatement&) statement);
+ case ASTStatement::kIf_Kind:
+ return this->convertIf((ASTIfStatement&) statement);
+ case ASTStatement::kFor_Kind:
+ return this->convertFor((ASTForStatement&) statement);
+ case ASTStatement::kWhile_Kind:
+ return this->convertWhile((ASTWhileStatement&) statement);
+ case ASTStatement::kDo_Kind:
+ return this->convertDo((ASTDoStatement&) statement);
+ case ASTStatement::kReturn_Kind:
+ return this->convertReturn((ASTReturnStatement&) statement);
+ case ASTStatement::kBreak_Kind:
+ return this->convertBreak((ASTBreakStatement&) statement);
+ case ASTStatement::kContinue_Kind:
+ return this->convertContinue((ASTContinueStatement&) statement);
+ case ASTStatement::kDiscard_Kind:
+ return this->convertDiscard((ASTDiscardStatement&) statement);
+ default:
+ ABORT("unsupported statement type: %d\n", statement.fKind);
+ }
+}
+
+std::unique_ptr<Block> IRGenerator::convertBlock(const ASTBlock& block) {
+ AutoSymbolTable table(this);
+ std::vector<std::unique_ptr<Statement>> statements;
+ for (size_t i = 0; i < block.fStatements.size(); i++) {
+ std::unique_ptr<Statement> statement = this->convertStatement(*block.fStatements[i]);
+ if (!statement) {
+ return nullptr;
+ }
+ statements.push_back(std::move(statement));
+ }
+ return std::unique_ptr<Block>(new Block(block.fPosition, std::move(statements)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertVarDeclarationStatement(
+ const ASTVarDeclarationStatement& s) {
+ auto decl = this->convertVarDeclaration(*s.fDeclaration, Variable::kLocal_Storage);
+ if (!decl) {
+ return nullptr;
+ }
+ return std::unique_ptr<Statement>(new VarDeclarationStatement(std::move(decl)));
+}
+
+Modifiers IRGenerator::convertModifiers(const ASTModifiers& modifiers) {
+ return Modifiers(modifiers);
+}
+
+std::unique_ptr<VarDeclaration> IRGenerator::convertVarDeclaration(const ASTVarDeclaration& decl,
+ Variable::Storage storage) {
+ std::vector<std::shared_ptr<Variable>> variables;
+ std::vector<std::vector<std::unique_ptr<Expression>>> sizes;
+ std::vector<std::unique_ptr<Expression>> values;
+ std::shared_ptr<Type> baseType = this->convertType(*decl.fType);
+ if (!baseType) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < decl.fNames.size(); i++) {
+ Modifiers modifiers = this->convertModifiers(decl.fModifiers);
+ std::shared_ptr<Type> type = baseType;
+ ASSERT(type->kind() != Type::kArray_Kind);
+ std::vector<std::unique_ptr<Expression>> currentVarSizes;
+ for (size_t j = 0; j < decl.fSizes[i].size(); j++) {
+ if (decl.fSizes[i][j]) {
+ ASTExpression& rawSize = *decl.fSizes[i][j];
+ auto size = this->coerce(this->convertExpression(rawSize), kInt_Type);
+ if (!size) {
+ return nullptr;
+ }
+ std::string name = type->fName;
+ uint64_t count;
+ if (size->fKind == Expression::kIntLiteral_Kind) {
+ count = ((IntLiteral&) *size).fValue;
+ if (count <= 0) {
+ fErrors.error(size->fPosition, "array size must be positive");
+ }
+ name += "[" + to_string(count) + "]";
+ } else {
+ count = -1;
+ name += "[]";
+ }
+ type = std::shared_ptr<Type>(new Type(name, Type::kArray_Kind, type, (int) count));
+ currentVarSizes.push_back(std::move(size));
+ } else {
+ type = std::shared_ptr<Type>(new Type(type->fName + "[]", Type::kArray_Kind, type,
+ -1));
+ currentVarSizes.push_back(nullptr);
+ }
+ }
+ sizes.push_back(std::move(currentVarSizes));
+ auto var = std::make_shared<Variable>(decl.fPosition, modifiers, decl.fNames[i], type,
+ storage);
+ variables.push_back(var);
+ std::unique_ptr<Expression> value;
+ if (decl.fValues[i]) {
+ value = this->convertExpression(*decl.fValues[i]);
+ if (!value) {
+ return nullptr;
+ }
+ value = this->coerce(std::move(value), type);
+ }
+ fSymbolTable->add(var->fName, var);
+ values.push_back(std::move(value));
+ }
+ return std::unique_ptr<VarDeclaration>(new VarDeclaration(decl.fPosition, std::move(variables),
+ std::move(sizes), std::move(values)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertIf(const ASTIfStatement& s) {
+ std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*s.fTest), kBool_Type);
+ if (!test) {
+ return nullptr;
+ }
+ std::unique_ptr<Statement> ifTrue = this->convertStatement(*s.fIfTrue);
+ if (!ifTrue) {
+ return nullptr;
+ }
+ std::unique_ptr<Statement> ifFalse;
+ if (s.fIfFalse) {
+ ifFalse = this->convertStatement(*s.fIfFalse);
+ if (!ifFalse) {
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<Statement>(new IfStatement(s.fPosition, std::move(test),
+ std::move(ifTrue), std::move(ifFalse)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertFor(const ASTForStatement& f) {
+ AutoSymbolTable table(this);
+ std::unique_ptr<Statement> initializer = this->convertStatement(*f.fInitializer);
+ if (!initializer) {
+ return nullptr;
+ }
+ std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*f.fTest), kBool_Type);
+ if (!test) {
+ return nullptr;
+ }
+ std::unique_ptr<Expression> next = this->convertExpression(*f.fNext);
+ if (!next) {
+ return nullptr;
+ }
+ this->checkValid(*next);
+ std::unique_ptr<Statement> statement = this->convertStatement(*f.fStatement);
+ if (!statement) {
+ return nullptr;
+ }
+ return std::unique_ptr<Statement>(new ForStatement(f.fPosition, std::move(initializer),
+ std::move(test), std::move(next),
+ std::move(statement)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertWhile(const ASTWhileStatement& w) {
+ std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*w.fTest), kBool_Type);
+ if (!test) {
+ return nullptr;
+ }
+ std::unique_ptr<Statement> statement = this->convertStatement(*w.fStatement);
+ if (!statement) {
+ return nullptr;
+ }
+ return std::unique_ptr<Statement>(new WhileStatement(w.fPosition, std::move(test),
+ std::move(statement)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertDo(const ASTDoStatement& d) {
+ std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*d.fTest), kBool_Type);
+ if (!test) {
+ return nullptr;
+ }
+ std::unique_ptr<Statement> statement = this->convertStatement(*d.fStatement);
+ if (!statement) {
+ return nullptr;
+ }
+ return std::unique_ptr<Statement>(new DoStatement(d.fPosition, std::move(statement),
+ std::move(test)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertExpressionStatement(
+ const ASTExpressionStatement& s) {
+ std::unique_ptr<Expression> e = this->convertExpression(*s.fExpression);
+ if (!e) {
+ return nullptr;
+ }
+ this->checkValid(*e);
+ return std::unique_ptr<Statement>(new ExpressionStatement(std::move(e)));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertReturn(const ASTReturnStatement& r) {
+ ASSERT(fCurrentFunction);
+ if (r.fExpression) {
+ std::unique_ptr<Expression> result = this->convertExpression(*r.fExpression);
+ if (!result) {
+ return nullptr;
+ }
+ if (fCurrentFunction->fReturnType == kVoid_Type) {
+ fErrors.error(result->fPosition, "may not return a value from a void function");
+ } else {
+ result = this->coerce(std::move(result), fCurrentFunction->fReturnType);
+ if (!result) {
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<Statement>(new ReturnStatement(std::move(result)));
+ } else {
+ if (fCurrentFunction->fReturnType != kVoid_Type) {
+ fErrors.error(r.fPosition, "expected function to return '" +
+ fCurrentFunction->fReturnType->description() + "'");
+ }
+ return std::unique_ptr<Statement>(new ReturnStatement(r.fPosition));
+ }
+}
+
+std::unique_ptr<Statement> IRGenerator::convertBreak(const ASTBreakStatement& b) {
+ return std::unique_ptr<Statement>(new BreakStatement(b.fPosition));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertContinue(const ASTContinueStatement& c) {
+ return std::unique_ptr<Statement>(new ContinueStatement(c.fPosition));
+}
+
+std::unique_ptr<Statement> IRGenerator::convertDiscard(const ASTDiscardStatement& d) {
+ return std::unique_ptr<Statement>(new DiscardStatement(d.fPosition));
+}
+
+static std::shared_ptr<Type> expand_generics(std::shared_ptr<Type> type, int i) {
+ if (type->kind() == Type::kGeneric_Kind) {
+ return type->coercibleTypes()[i];
+ }
+ return type;
+}
+
+static void expand_generics(FunctionDeclaration& decl,
+ SymbolTable& symbolTable) {
+ for (int i = 0; i < 4; i++) {
+ std::shared_ptr<Type> returnType = expand_generics(decl.fReturnType, i);
+ std::vector<std::shared_ptr<Variable>> arguments;
+ for (const auto& p : decl.fParameters) {
+ arguments.push_back(std::shared_ptr<Variable>(new Variable(
+ p->fPosition,
+ Modifiers(p->fModifiers),
+ p->fName,
+ expand_generics(p->fType, i),
+ Variable::kParameter_Storage)));
+ }
+ std::shared_ptr<FunctionDeclaration> expanded(new FunctionDeclaration(
+ decl.fPosition,
+ decl.fName,
+ std::move(arguments),
+ std::move(returnType)));
+ symbolTable.add(expanded->fName, expanded);
+ }
+}
+
+std::unique_ptr<FunctionDefinition> IRGenerator::convertFunction(const ASTFunction& f) {
+ std::shared_ptr<SymbolTable> old = fSymbolTable;
+ AutoSymbolTable table(this);
+ bool isGeneric;
+ std::shared_ptr<Type> returnType = this->convertType(*f.fReturnType);
+ if (!returnType) {
+ return nullptr;
+ }
+ isGeneric = returnType->kind() == Type::kGeneric_Kind;
+ std::vector<std::shared_ptr<Variable>> parameters;
+ for (const auto& param : f.fParameters) {
+ std::shared_ptr<Type> type = this->convertType(*param->fType);
+ if (!type) {
+ return nullptr;
+ }
+ for (int j = (int) param->fSizes.size() - 1; j >= 0; j--) {
+ int size = param->fSizes[j];
+ std::string name = type->name() + "[" + to_string(size) + "]";
+ type = std::shared_ptr<Type>(new Type(std::move(name), Type::kArray_Kind,
+ std::move(type), size));
+ }
+ std::string name = param->fName;
+ Modifiers modifiers = this->convertModifiers(param->fModifiers);
+ Position pos = param->fPosition;
+ std::shared_ptr<Variable> var = std::shared_ptr<Variable>(new Variable(
+ pos,
+ modifiers,
+ std::move(name),
+ type,
+ Variable::kParameter_Storage));
+ parameters.push_back(std::move(var));
+ isGeneric |= type->kind() == Type::kGeneric_Kind;
+ }
+
+ // find existing declaration
+ std::shared_ptr<FunctionDeclaration> decl;
+ auto entry = (*old)[f.fName];
+ if (entry) {
+ std::vector<std::shared_ptr<FunctionDeclaration>> functions;
+ switch (entry->fKind) {
+ case Symbol::kUnresolvedFunction_Kind:
+ functions = std::static_pointer_cast<UnresolvedFunction>(entry)->fFunctions;
+ break;
+ case Symbol::kFunctionDeclaration_Kind:
+ functions.push_back(std::static_pointer_cast<FunctionDeclaration>(entry));
+ break;
+ default:
+ fErrors.error(f.fPosition, "symbol '" + f.fName + "' was already defined");
+ return nullptr;
+ }
+ for (const auto& other : functions) {
+ ASSERT(other->fName == f.fName);
+ if (parameters.size() == other->fParameters.size()) {
+ bool match = true;
+ for (size_t i = 0; i < parameters.size(); i++) {
+ if (parameters[i]->fType != other->fParameters[i]->fType) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ if (returnType != other->fReturnType) {
+ FunctionDeclaration newDecl = FunctionDeclaration(f.fPosition,
+ f.fName,
+ parameters,
+ returnType);
+ fErrors.error(f.fPosition, "functions '" + newDecl.description() +
+ "' and '" + other->description() +
+ "' differ only in return type");
+ return nullptr;
+ }
+ decl = other;
+ for (size_t i = 0; i < parameters.size(); i++) {
+ if (parameters[i]->fModifiers != other->fParameters[i]->fModifiers) {
+ fErrors.error(f.fPosition, "modifiers on parameter " +
+ to_string(i + 1) + " differ between " +
+ "declaration and definition");
+ return nullptr;
+ }
+ fSymbolTable->add(parameters[i]->fName, decl->fParameters[i]);
+ }
+ if (other->fDefined) {
+ fErrors.error(f.fPosition, "duplicate definition of " +
+ other->description());
+ }
+ break;
+ }
+ }
+ }
+ }
+ if (!decl) {
+ // couldn't find an existing declaration
+ decl.reset(new FunctionDeclaration(f.fPosition, f.fName, parameters, returnType));
+ for (auto var : parameters) {
+ fSymbolTable->add(var->fName, var);
+ }
+ }
+ if (isGeneric) {
+ ASSERT(!f.fBody);
+ expand_generics(*decl, *old);
+ } else {
+ old->add(decl->fName, decl);
+ if (f.fBody) {
+ ASSERT(!fCurrentFunction);
+ fCurrentFunction = decl;
+ decl->fDefined = true;
+ std::unique_ptr<Block> body = this->convertBlock(*f.fBody);
+ fCurrentFunction = nullptr;
+ if (!body) {
+ return nullptr;
+ }
+ return std::unique_ptr<FunctionDefinition>(new FunctionDefinition(f.fPosition, decl,
+ std::move(body)));
+ }
+ }
+ return nullptr;
+}
+
+std::unique_ptr<InterfaceBlock> IRGenerator::convertInterfaceBlock(const ASTInterfaceBlock& intf) {
+ std::shared_ptr<SymbolTable> old = fSymbolTable;
+ AutoSymbolTable table(this);
+ Modifiers mods = this->convertModifiers(intf.fModifiers);
+ std::vector<Type::Field> fields;
+ for (size_t i = 0; i < intf.fDeclarations.size(); i++) {
+ std::unique_ptr<VarDeclaration> decl = this->convertVarDeclaration(
+ *intf.fDeclarations[i],
+ Variable::kGlobal_Storage);
+ for (size_t j = 0; j < decl->fVars.size(); j++) {
+ fields.push_back(Type::Field(decl->fVars[j]->fModifiers, decl->fVars[j]->fName,
+ decl->fVars[j]->fType));
+ if (decl->fValues[j]) {
+ fErrors.error(decl->fPosition,
+ "initializers are not permitted on interface block fields");
+ }
+ if (decl->fVars[j]->fModifiers.fFlags & (Modifiers::kIn_Flag |
+ Modifiers::kOut_Flag |
+ Modifiers::kUniform_Flag |
+ Modifiers::kConst_Flag)) {
+ fErrors.error(decl->fPosition,
+ "interface block fields may not have storage qualifiers");
+ }
+ }
+ }
+ std::shared_ptr<Type> type = std::shared_ptr<Type>(new Type(intf.fInterfaceName, fields));
+ std::string name = intf.fValueName.length() > 0 ? intf.fValueName : intf.fInterfaceName;
+ std::shared_ptr<Variable> var = std::shared_ptr<Variable>(new Variable(intf.fPosition, mods,
+ name, type,
+ Variable::kGlobal_Storage));
+ if (intf.fValueName.length()) {
+ old->add(intf.fValueName, var);
+
+ } else {
+ for (size_t i = 0; i < fields.size(); i++) {
+ std::shared_ptr<Field> field = std::shared_ptr<Field>(new Field(intf.fPosition, var,
+ (int) i));
+ old->add(fields[i].fName, field);
+ }
+ }
+ return std::unique_ptr<InterfaceBlock>(new InterfaceBlock(intf.fPosition, var));
+}
+
+std::shared_ptr<Type> IRGenerator::convertType(const ASTType& type) {
+ std::shared_ptr<Symbol> result = (*fSymbolTable)[type.fName];
+ if (result && result->fKind == Symbol::kType_Kind) {
+ return std::static_pointer_cast<Type>(result);
+ }
+ fErrors.error(type.fPosition, "unknown type '" + type.fName + "'");
+ return nullptr;
+}
+
+std::unique_ptr<Expression> IRGenerator::convertExpression(const ASTExpression& expr) {
+ switch (expr.fKind) {
+ case ASTExpression::kIdentifier_Kind:
+ return this->convertIdentifier((ASTIdentifier&) expr);
+ case ASTExpression::kBool_Kind:
+ return std::unique_ptr<Expression>(new BoolLiteral(expr.fPosition,
+ ((ASTBoolLiteral&) expr).fValue));
+ case ASTExpression::kInt_Kind:
+ return std::unique_ptr<Expression>(new IntLiteral(expr.fPosition,
+ ((ASTIntLiteral&) expr).fValue));
+ case ASTExpression::kFloat_Kind:
+ return std::unique_ptr<Expression>(new FloatLiteral(expr.fPosition,
+ ((ASTFloatLiteral&) expr).fValue));
+ case ASTExpression::kBinary_Kind:
+ return this->convertBinaryExpression((ASTBinaryExpression&) expr);
+ case ASTExpression::kPrefix_Kind:
+ return this->convertPrefixExpression((ASTPrefixExpression&) expr);
+ case ASTExpression::kSuffix_Kind:
+ return this->convertSuffixExpression((ASTSuffixExpression&) expr);
+ case ASTExpression::kTernary_Kind:
+ return this->convertTernaryExpression((ASTTernaryExpression&) expr);
+ default:
+ ABORT("unsupported expression type: %d\n", expr.fKind);
+ }
+}
+
+std::unique_ptr<Expression> IRGenerator::convertIdentifier(const ASTIdentifier& identifier) {
+ std::shared_ptr<Symbol> result = (*fSymbolTable)[identifier.fText];
+ if (!result) {
+ fErrors.error(identifier.fPosition, "unknown identifier '" + identifier.fText + "'");
+ return nullptr;
+ }
+ switch (result->fKind) {
+ case Symbol::kFunctionDeclaration_Kind: {
+ std::vector<std::shared_ptr<FunctionDeclaration>> f = {
+ std::static_pointer_cast<FunctionDeclaration>(result)
+ };
+ return std::unique_ptr<FunctionReference>(new FunctionReference(identifier.fPosition,
+ std::move(f)));
+ }
+ case Symbol::kUnresolvedFunction_Kind: {
+ auto f = std::static_pointer_cast<UnresolvedFunction>(result);
+ return std::unique_ptr<FunctionReference>(new FunctionReference(identifier.fPosition,
+ f->fFunctions));
+ }
+ case Symbol::kVariable_Kind: {
+ std::shared_ptr<Variable> var = std::static_pointer_cast<Variable>(result);
+ this->markReadFrom(var);
+ return std::unique_ptr<VariableReference>(new VariableReference(identifier.fPosition,
+ std::move(var)));
+ }
+ case Symbol::kField_Kind: {
+ std::shared_ptr<Field> field = std::static_pointer_cast<Field>(result);
+ VariableReference* base = new VariableReference(identifier.fPosition, field->fOwner);
+ return std::unique_ptr<Expression>(new FieldAccess(std::unique_ptr<Expression>(base),
+ field->fFieldIndex));
+ }
+ case Symbol::kType_Kind: {
+ auto t = std::static_pointer_cast<Type>(result);
+ return std::unique_ptr<TypeReference>(new TypeReference(identifier.fPosition,
+ std::move(t)));
+ }
+ default:
+ ABORT("unsupported symbol type %d\n", result->fKind);
+ }
+
+}
+
+std::unique_ptr<Expression> IRGenerator::coerce(std::unique_ptr<Expression> expr,
+ std::shared_ptr<Type> type) {
+ if (!expr) {
+ return nullptr;
+ }
+ if (*expr->fType == *type) {
+ return expr;
+ }
+ this->checkValid(*expr);
+ if (*expr->fType == *kInvalid_Type) {
+ return nullptr;
+ }
+ if (!expr->fType->canCoerceTo(type)) {
+ fErrors.error(expr->fPosition, "expected '" + type->description() + "', but found '" +
+ expr->fType->description() + "'");
+ return nullptr;
+ }
+ if (type->kind() == Type::kScalar_Kind) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expr));
+ ASTIdentifier id(Position(), type->description());
+ std::unique_ptr<Expression> ctor = this->convertIdentifier(id);
+ ASSERT(ctor);
+ return this->call(Position(), std::move(ctor), std::move(args));
+ }
+ ABORT("cannot coerce %s to %s", expr->fType->description().c_str(),
+ type->description().c_str());
+}
+
+/**
+ * Determines the operand and result types of a binary expression. Returns true if the expression is
+ * legal, false otherwise. If false, the values of the out parameters are undefined.
+ */
+static bool determine_binary_type(Token::Kind op, std::shared_ptr<Type> left,
+ std::shared_ptr<Type> right,
+ std::shared_ptr<Type>* outLeftType,
+ std::shared_ptr<Type>* outRightType,
+ std::shared_ptr<Type>* outResultType,
+ bool tryFlipped) {
+ bool isLogical;
+ switch (op) {
+ case Token::EQEQ: // fall through
+ case Token::NEQ: // fall through
+ case Token::LT: // fall through
+ case Token::GT: // fall through
+ case Token::LTEQ: // fall through
+ case Token::GTEQ:
+ isLogical = true;
+ break;
+ case Token::LOGICALOR: // fall through
+ case Token::LOGICALAND: // fall through
+ case Token::LOGICALXOR: // fall through
+ case Token::LOGICALOREQ: // fall through
+ case Token::LOGICALANDEQ: // fall through
+ case Token::LOGICALXOREQ:
+ *outLeftType = kBool_Type;
+ *outRightType = kBool_Type;
+ *outResultType = kBool_Type;
+ return left->canCoerceTo(kBool_Type) && right->canCoerceTo(kBool_Type);
+ case Token::STAR: // fall through
+ case Token::STAREQ:
+ // FIXME need to handle non-square matrices
+ if (left->kind() == Type::kMatrix_Kind && right->kind() == Type::kVector_Kind) {
+ *outLeftType = left;
+ *outRightType = right;
+ *outResultType = right;
+ return left->rows() == right->columns();
+ }
+ if (left->kind() == Type::kVector_Kind && right->kind() == Type::kMatrix_Kind) {
+ *outLeftType = left;
+ *outRightType = right;
+ *outResultType = left;
+ return left->columns() == right->columns();
+ }
+ // fall through
+ default:
+ isLogical = false;
+ }
+ // FIXME: need to disallow illegal operations like vec3 > vec3. Also do not currently have
+ // full support for numbers other than float.
+ if (left == right) {
+ *outLeftType = left;
+ *outRightType = left;
+ if (isLogical) {
+ *outResultType = kBool_Type;
+ } else {
+ *outResultType = left;
+ }
+ return true;
+ }
+ // FIXME: incorrect for shift operations
+ if (left->canCoerceTo(right)) {
+ *outLeftType = right;
+ *outRightType = right;
+ if (isLogical) {
+ *outResultType = kBool_Type;
+ } else {
+ *outResultType = right;
+ }
+ return true;
+ }
+ if ((left->kind() == Type::kVector_Kind || left->kind() == Type::kMatrix_Kind) &&
+ (right->kind() == Type::kScalar_Kind)) {
+ if (determine_binary_type(op, left->componentType(), right, outLeftType, outRightType,
+ outResultType, false)) {
+ *outLeftType = (*outLeftType)->toCompound(left->columns(), left->rows());
+ if (!isLogical) {
+ *outResultType = (*outResultType)->toCompound(left->columns(), left->rows());
+ }
+ return true;
+ }
+ return false;
+ }
+ if (tryFlipped) {
+ return determine_binary_type(op, right, left, outRightType, outLeftType, outResultType,
+ false);
+ }
+ return false;
+}
+
+std::unique_ptr<Expression> IRGenerator::convertBinaryExpression(
+ const ASTBinaryExpression& expression) {
+ std::unique_ptr<Expression> left = this->convertExpression(*expression.fLeft);
+ if (!left) {
+ return nullptr;
+ }
+ std::unique_ptr<Expression> right = this->convertExpression(*expression.fRight);
+ if (!right) {
+ return nullptr;
+ }
+ std::shared_ptr<Type> leftType;
+ std::shared_ptr<Type> rightType;
+ std::shared_ptr<Type> resultType;
+ if (!determine_binary_type(expression.fOperator, left->fType, right->fType, &leftType,
+ &rightType, &resultType, true)) {
+ fErrors.error(expression.fPosition, "type mismatch: '" +
+ Token::OperatorName(expression.fOperator) +
+ "' cannot operate on '" + left->fType->fName +
+ "', '" + right->fType->fName + "'");
+ return nullptr;
+ }
+ switch (expression.fOperator) {
+ case Token::EQ: // fall through
+ case Token::PLUSEQ: // fall through
+ case Token::MINUSEQ: // fall through
+ case Token::STAREQ: // fall through
+ case Token::SLASHEQ: // fall through
+ case Token::PERCENTEQ: // fall through
+ case Token::SHLEQ: // fall through
+ case Token::SHREQ: // fall through
+ case Token::BITWISEOREQ: // fall through
+ case Token::BITWISEXOREQ: // fall through
+ case Token::BITWISEANDEQ: // fall through
+ case Token::LOGICALOREQ: // fall through
+ case Token::LOGICALXOREQ: // fall through
+ case Token::LOGICALANDEQ:
+ this->markWrittenTo(*left);
+ default:
+ break;
+ }
+ return std::unique_ptr<Expression>(new BinaryExpression(expression.fPosition,
+ this->coerce(std::move(left), leftType),
+ expression.fOperator,
+ this->coerce(std::move(right),
+ rightType),
+ resultType));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertTernaryExpression(
+ const ASTTernaryExpression& expression) {
+ std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*expression.fTest),
+ kBool_Type);
+ if (!test) {
+ return nullptr;
+ }
+ std::unique_ptr<Expression> ifTrue = this->convertExpression(*expression.fIfTrue);
+ if (!ifTrue) {
+ return nullptr;
+ }
+ std::unique_ptr<Expression> ifFalse = this->convertExpression(*expression.fIfFalse);
+ if (!ifFalse) {
+ return nullptr;
+ }
+ std::shared_ptr<Type> trueType;
+ std::shared_ptr<Type> falseType;
+ std::shared_ptr<Type> resultType;
+ if (!determine_binary_type(Token::EQEQ, ifTrue->fType, ifFalse->fType, &trueType,
+ &falseType, &resultType, true)) {
+ fErrors.error(expression.fPosition, "ternary operator result mismatch: '" +
+ ifTrue->fType->fName + "', '" +
+ ifFalse->fType->fName + "'");
+ return nullptr;
+ }
+ ASSERT(trueType == falseType);
+ ifTrue = this->coerce(std::move(ifTrue), trueType);
+ ifFalse = this->coerce(std::move(ifFalse), falseType);
+ return std::unique_ptr<Expression>(new TernaryExpression(expression.fPosition,
+ std::move(test),
+ std::move(ifTrue),
+ std::move(ifFalse)));
+}
+
+std::unique_ptr<Expression> IRGenerator::call(
+ Position position,
+ std::shared_ptr<FunctionDeclaration> function,
+ std::vector<std::unique_ptr<Expression>> arguments) {
+ if (function->fParameters.size() != arguments.size()) {
+ std::string msg = "call to '" + function->fName + "' expected " +
+ to_string(function->fParameters.size()) +
+ " argument";
+ if (function->fParameters.size() != 1) {
+ msg += "s";
+ }
+ msg += ", but found " + to_string(arguments.size());
+ fErrors.error(position, msg);
+ return nullptr;
+ }
+ for (size_t i = 0; i < arguments.size(); i++) {
+ arguments[i] = this->coerce(std::move(arguments[i]), function->fParameters[i]->fType);
+ if (arguments[i] && (function->fParameters[i]->fModifiers.fFlags & Modifiers::kOut_Flag)) {
+ this->markWrittenTo(*arguments[i]);
+ }
+ }
+ return std::unique_ptr<FunctionCall>(new FunctionCall(position, std::move(function),
+ std::move(arguments)));
+}
+
+/**
+ * Determines the cost of coercing the arguments of a function to the required types. Returns true
+ * if the cost could be computed, false if the call is not valid. Cost has no particular meaning
+ * other than "lower costs are preferred".
+ */
+bool IRGenerator::determineCallCost(std::shared_ptr<FunctionDeclaration> function,
+ const std::vector<std::unique_ptr<Expression>>& arguments,
+ int* outCost) {
+ if (function->fParameters.size() != arguments.size()) {
+ return false;
+ }
+ int total = 0;
+ for (size_t i = 0; i < arguments.size(); i++) {
+ int cost;
+ if (arguments[i]->fType->determineCoercionCost(function->fParameters[i]->fType, &cost)) {
+ total += cost;
+ } else {
+ return false;
+ }
+ }
+ *outCost = total;
+ return true;
+}
+
+std::unique_ptr<Expression> IRGenerator::call(Position position,
+ std::unique_ptr<Expression> functionValue,
+ std::vector<std::unique_ptr<Expression>> arguments) {
+ if (functionValue->fKind == Expression::kTypeReference_Kind) {
+ return this->convertConstructor(position,
+ ((TypeReference&) *functionValue).fValue,
+ std::move(arguments));
+ }
+ if (functionValue->fKind != Expression::kFunctionReference_Kind) {
+ fErrors.error(position, "'" + functionValue->description() + "' is not a function");
+ return nullptr;
+ }
+ FunctionReference* ref = (FunctionReference*) functionValue.get();
+ int bestCost = INT_MAX;
+ std::shared_ptr<FunctionDeclaration> best;
+ if (ref->fFunctions.size() > 1) {
+ for (const auto& f : ref->fFunctions) {
+ int cost;
+ if (this->determineCallCost(f, arguments, &cost) && cost < bestCost) {
+ bestCost = cost;
+ best = f;
+ }
+ }
+ if (best) {
+ return this->call(position, std::move(best), std::move(arguments));
+ }
+ std::string msg = "no match for " + ref->fFunctions[0]->fName + "(";
+ std::string separator = "";
+ for (size_t i = 0; i < arguments.size(); i++) {
+ msg += separator;
+ separator = ", ";
+ msg += arguments[i]->fType->description();
+ }
+ msg += ")";
+ fErrors.error(position, msg);
+ return nullptr;
+ }
+ return this->call(position, ref->fFunctions[0], std::move(arguments));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertConstructor(
+ Position position,
+ std::shared_ptr<Type> type,
+ std::vector<std::unique_ptr<Expression>> args) {
+ // FIXME: add support for structs and arrays
+ Type::Kind kind = type->kind();
+ if (!type->isNumber() && kind != Type::kVector_Kind && kind != Type::kMatrix_Kind) {
+ fErrors.error(position, "cannot construct '" + type->description() + "'");
+ return nullptr;
+ }
+ if (type == kFloat_Type && args.size() == 1 &&
+ args[0]->fKind == Expression::kIntLiteral_Kind) {
+ int64_t value = ((IntLiteral&) *args[0]).fValue;
+ return std::unique_ptr<Expression>(new FloatLiteral(position, (double) value));
+ }
+ if (args.size() == 1 && args[0]->fType == type) {
+ // argument is already the right type, just return it
+ return std::move(args[0]);
+ }
+ if (type->isNumber()) {
+ if (args.size() != 1) {
+ fErrors.error(position, "invalid arguments to '" + type->description() +
+ "' constructor, (expected exactly 1 argument, but found " +
+ to_string(args.size()) + ")");
+ }
+ if (args[0]->fType == kBool_Type) {
+ std::unique_ptr<IntLiteral> zero(new IntLiteral(position, 0));
+ std::unique_ptr<IntLiteral> one(new IntLiteral(position, 1));
+ return std::unique_ptr<Expression>(
+ new TernaryExpression(position, std::move(args[0]),
+ this->coerce(std::move(one), type),
+ this->coerce(std::move(zero),
+ type)));
+ } else if (!args[0]->fType->isNumber()) {
+ fErrors.error(position, "invalid argument to '" + type->description() +
+ "' constructor (expected a number or bool, but found '" +
+ args[0]->fType->description() + "')");
+ }
+ } else {
+ ASSERT(kind == Type::kVector_Kind || kind == Type::kMatrix_Kind);
+ int actual = 0;
+ for (size_t i = 0; i < args.size(); i++) {
+ if (args[i]->fType->kind() == Type::kVector_Kind ||
+ args[i]->fType->kind() == Type::kMatrix_Kind) {
+ int columns = args[i]->fType->columns();
+ int rows = args[i]->fType->rows();
+ args[i] = this->coerce(std::move(args[i]),
+ type->componentType()->toCompound(columns, rows));
+ actual += args[i]->fType->rows() * args[i]->fType->columns();
+ } else if (args[i]->fType->kind() == Type::kScalar_Kind) {
+ actual += 1;
+ if (type->kind() != Type::kScalar_Kind) {
+ args[i] = this->coerce(std::move(args[i]), type->componentType());
+ }
+ } else {
+ fErrors.error(position, "'" + args[i]->fType->description() + "' is not a valid "
+ "parameter to '" + type->description() + "' constructor");
+ return nullptr;
+ }
+ }
+ int min = type->rows() * type->columns();
+ int max = type->columns() > 1 ? INT_MAX : min;
+ if ((actual < min || actual > max) &&
+ !((kind == Type::kVector_Kind || kind == Type::kMatrix_Kind) && (actual == 1))) {
+ fErrors.error(position, "invalid arguments to '" + type->description() +
+ "' constructor (expected " + to_string(min) + " scalar" +
+ (min == 1 ? "" : "s") + ", but found " + to_string(actual) +
+ ")");
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<Expression>(new Constructor(position, std::move(type), std::move(args)));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertPrefixExpression(
+ const ASTPrefixExpression& expression) {
+ std::unique_ptr<Expression> base = this->convertExpression(*expression.fOperand);
+ if (!base) {
+ return nullptr;
+ }
+ switch (expression.fOperator) {
+ case Token::PLUS:
+ if (!base->fType->isNumber() && base->fType->kind() != Type::kVector_Kind) {
+ fErrors.error(expression.fPosition,
+ "'+' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ return base;
+ case Token::MINUS:
+ if (!base->fType->isNumber() && base->fType->kind() != Type::kVector_Kind) {
+ fErrors.error(expression.fPosition,
+ "'-' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ if (base->fKind == Expression::kIntLiteral_Kind) {
+ return std::unique_ptr<Expression>(new IntLiteral(base->fPosition,
+ -((IntLiteral&) *base).fValue));
+ }
+ if (base->fKind == Expression::kFloatLiteral_Kind) {
+ double value = -((FloatLiteral&) *base).fValue;
+ return std::unique_ptr<Expression>(new FloatLiteral(base->fPosition, value));
+ }
+ return std::unique_ptr<Expression>(new PrefixExpression(Token::MINUS, std::move(base)));
+ case Token::PLUSPLUS:
+ if (!base->fType->isNumber()) {
+ fErrors.error(expression.fPosition,
+ "'" + Token::OperatorName(expression.fOperator) +
+ "' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ this->markWrittenTo(*base);
+ break;
+ case Token::MINUSMINUS:
+ if (!base->fType->isNumber()) {
+ fErrors.error(expression.fPosition,
+ "'" + Token::OperatorName(expression.fOperator) +
+ "' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ this->markWrittenTo(*base);
+ break;
+ case Token::NOT:
+ if (base->fType != kBool_Type) {
+ fErrors.error(expression.fPosition,
+ "'" + Token::OperatorName(expression.fOperator) +
+ "' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ break;
+ default:
+ ABORT("unsupported prefix operator\n");
+ }
+ return std::unique_ptr<Expression>(new PrefixExpression(expression.fOperator,
+ std::move(base)));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertIndex(std::unique_ptr<Expression> base,
+ const ASTExpression& index) {
+ if (base->fType->kind() != Type::kArray_Kind && base->fType->kind() != Type::kMatrix_Kind) {
+ fErrors.error(base->fPosition, "expected array, but found '" + base->fType->description() +
+ "'");
+ return nullptr;
+ }
+ std::unique_ptr<Expression> converted = this->convertExpression(index);
+ if (!converted) {
+ return nullptr;
+ }
+ converted = this->coerce(std::move(converted), kInt_Type);
+ if (!converted) {
+ return nullptr;
+ }
+ return std::unique_ptr<Expression>(new IndexExpression(std::move(base), std::move(converted)));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertField(std::unique_ptr<Expression> base,
+ const std::string& field) {
+ auto fields = base->fType->fields();
+ for (size_t i = 0; i < fields.size(); i++) {
+ if (fields[i].fName == field) {
+ return std::unique_ptr<Expression>(new FieldAccess(std::move(base), (int) i));
+ }
+ }
+ fErrors.error(base->fPosition, "type '" + base->fType->description() + "' does not have a "
+ "field named '" + field + "");
+ return nullptr;
+}
+
+std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
+ const std::string& fields) {
+ if (base->fType->kind() != Type::kVector_Kind) {
+ fErrors.error(base->fPosition, "cannot swizzle type '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ std::vector<int> swizzleComponents;
+ for (char c : fields) {
+ switch (c) {
+ case 'x': // fall through
+ case 'r': // fall through
+ case 's':
+ swizzleComponents.push_back(0);
+ break;
+ case 'y': // fall through
+ case 'g': // fall through
+ case 't':
+ if (base->fType->columns() >= 2) {
+ swizzleComponents.push_back(1);
+ break;
+ }
+ // fall through
+ case 'z': // fall through
+ case 'b': // fall through
+ case 'p':
+ if (base->fType->columns() >= 3) {
+ swizzleComponents.push_back(2);
+ break;
+ }
+ // fall through
+ case 'w': // fall through
+ case 'a': // fall through
+ case 'q':
+ if (base->fType->columns() >= 4) {
+ swizzleComponents.push_back(3);
+ break;
+ }
+ // fall through
+ default:
+ fErrors.error(base->fPosition, "invalid swizzle component '" + std::string(1, c) +
+ "'");
+ return nullptr;
+ }
+ }
+ ASSERT(swizzleComponents.size() > 0);
+ if (swizzleComponents.size() > 4) {
+ fErrors.error(base->fPosition, "too many components in swizzle mask '" + fields + "'");
+ return nullptr;
+ }
+ return std::unique_ptr<Expression>(new Swizzle(std::move(base), swizzleComponents));
+}
+
+std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
+ const ASTSuffixExpression& expression) {
+ std::unique_ptr<Expression> base = this->convertExpression(*expression.fBase);
+ if (!base) {
+ return nullptr;
+ }
+ switch (expression.fSuffix->fKind) {
+ case ASTSuffix::kIndex_Kind:
+ return this->convertIndex(std::move(base),
+ *((ASTIndexSuffix&) *expression.fSuffix).fExpression);
+ case ASTSuffix::kCall_Kind: {
+ auto rawArguments = &((ASTCallSuffix&) *expression.fSuffix).fArguments;
+ std::vector<std::unique_ptr<Expression>> arguments;
+ for (size_t i = 0; i < rawArguments->size(); i++) {
+ std::unique_ptr<Expression> converted =
+ this->convertExpression(*(*rawArguments)[i]);
+ if (!converted) {
+ return nullptr;
+ }
+ arguments.push_back(std::move(converted));
+ }
+ return this->call(expression.fPosition, std::move(base), std::move(arguments));
+ }
+ case ASTSuffix::kField_Kind: {
+ switch (base->fType->kind()) {
+ case Type::kVector_Kind:
+ return this->convertSwizzle(std::move(base),
+ ((ASTFieldSuffix&) *expression.fSuffix).fField);
+ case Type::kStruct_Kind:
+ return this->convertField(std::move(base),
+ ((ASTFieldSuffix&) *expression.fSuffix).fField);
+ default:
+ fErrors.error(base->fPosition, "cannot swizzle value of type '" +
+ base->fType->description() + "'");
+ return nullptr;
+ }
+ }
+ case ASTSuffix::kPostIncrement_Kind:
+ if (!base->fType->isNumber()) {
+ fErrors.error(expression.fPosition,
+ "'++' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ this->markWrittenTo(*base);
+ return std::unique_ptr<Expression>(new PostfixExpression(std::move(base),
+ Token::PLUSPLUS));
+ case ASTSuffix::kPostDecrement_Kind:
+ if (!base->fType->isNumber()) {
+ fErrors.error(expression.fPosition,
+ "'--' cannot operate on '" + base->fType->description() + "'");
+ return nullptr;
+ }
+ this->markWrittenTo(*base);
+ return std::unique_ptr<Expression>(new PostfixExpression(std::move(base),
+ Token::MINUSMINUS));
+ default:
+ ABORT("unsupported suffix operator");
+ }
+}
+
+void IRGenerator::checkValid(const Expression& expr) {
+ switch (expr.fKind) {
+ case Expression::kFunctionReference_Kind:
+ fErrors.error(expr.fPosition, "expected '(' to begin function call");
+ break;
+ case Expression::kTypeReference_Kind:
+ fErrors.error(expr.fPosition, "expected '(' to begin constructor invocation");
+ break;
+ default:
+ ASSERT(expr.fType != kInvalid_Type);
+ break;
+ }
+}
+
+void IRGenerator::markReadFrom(std::shared_ptr<Variable> var) {
+ var->fIsReadFrom = true;
+}
+
+static bool has_duplicates(const Swizzle& swizzle) {
+ int bits = 0;
+ for (int idx : swizzle.fComponents) {
+ ASSERT(idx >= 0 && idx <= 3);
+ int bit = 1 << idx;
+ if (bits & bit) {
+ return true;
+ }
+ bits |= bit;
+ }
+ return false;
+}
+
+void IRGenerator::markWrittenTo(const Expression& expr) {
+ switch (expr.fKind) {
+ case Expression::kVariableReference_Kind: {
+ const Variable& var = *((VariableReference&) expr).fVariable;
+ if (var.fModifiers.fFlags & (Modifiers::kConst_Flag | Modifiers::kUniform_Flag)) {
+ fErrors.error(expr.fPosition,
+ "cannot modify immutable variable '" + var.fName + "'");
+ }
+ var.fIsWrittenTo = true;
+ break;
+ }
+ case Expression::kFieldAccess_Kind:
+ this->markWrittenTo(*((FieldAccess&) expr).fBase);
+ break;
+ case Expression::kSwizzle_Kind:
+ if (has_duplicates((Swizzle&) expr)) {
+ fErrors.error(expr.fPosition,
+ "cannot write to the same swizzle field more than once");
+ }
+ this->markWrittenTo(*((Swizzle&) expr).fBase);
+ break;
+ case Expression::kIndex_Kind:
+ this->markWrittenTo(*((IndexExpression&) expr).fBase);
+ break;
+ default:
+ fErrors.error(expr.fPosition, "cannot assign to '" + expr.description() + "'");
+ break;
+ }
+}
+
+}
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
new file mode 100644
index 0000000000..d23e5a1bdb
--- /dev/null
+++ b/src/sksl/SkSLIRGenerator.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_IRGENERATOR
+#define SKSL_IRGENERATOR
+
+#include "SkSLErrorReporter.h"
+#include "ast/SkSLASTBinaryExpression.h"
+#include "ast/SkSLASTBlock.h"
+#include "ast/SkSLASTBreakStatement.h"
+#include "ast/SkSLASTCallSuffix.h"
+#include "ast/SkSLASTContinueStatement.h"
+#include "ast/SkSLASTDiscardStatement.h"
+#include "ast/SkSLASTDoStatement.h"
+#include "ast/SkSLASTExpression.h"
+#include "ast/SkSLASTExpressionStatement.h"
+#include "ast/SkSLASTExtension.h"
+#include "ast/SkSLASTForStatement.h"
+#include "ast/SkSLASTFunction.h"
+#include "ast/SkSLASTIdentifier.h"
+#include "ast/SkSLASTIfStatement.h"
+#include "ast/SkSLASTInterfaceBlock.h"
+#include "ast/SkSLASTModifiers.h"
+#include "ast/SkSLASTPrefixExpression.h"
+#include "ast/SkSLASTReturnStatement.h"
+#include "ast/SkSLASTStatement.h"
+#include "ast/SkSLASTSuffixExpression.h"
+#include "ast/SkSLASTTernaryExpression.h"
+#include "ast/SkSLASTVarDeclaration.h"
+#include "ast/SkSLASTVarDeclarationStatement.h"
+#include "ast/SkSLASTWhileStatement.h"
+#include "ir/SkSLBlock.h"
+#include "ir/SkSLExpression.h"
+#include "ir/SkSLExtension.h"
+#include "ir/SkSLFunctionDefinition.h"
+#include "ir/SkSLInterfaceBlock.h"
+#include "ir/SkSLModifiers.h"
+#include "ir/SkSLSymbolTable.h"
+#include "ir/SkSLStatement.h"
+#include "ir/SkSLType.h"
+#include "ir/SkSLTypeReference.h"
+#include "ir/SkSLVarDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * Performs semantic analysis on an abstract syntax tree (AST) and produces the corresponding
+ * (unoptimized) intermediate representation (IR).
+ */
+class IRGenerator {
+public:
+ IRGenerator(std::shared_ptr<SymbolTable> root, ErrorReporter& errorReporter);
+
+ std::unique_ptr<VarDeclaration> convertVarDeclaration(const ASTVarDeclaration& decl,
+ Variable::Storage storage);
+ std::unique_ptr<FunctionDefinition> convertFunction(const ASTFunction& f);
+ std::unique_ptr<Statement> convertStatement(const ASTStatement& statement);
+ std::unique_ptr<Expression> convertExpression(const ASTExpression& expression);
+
+private:
+ void pushSymbolTable();
+ void popSymbolTable();
+
+ std::shared_ptr<Type> convertType(const ASTType& type);
+ std::unique_ptr<Expression> call(Position position,
+ std::shared_ptr<FunctionDeclaration> function,
+ std::vector<std::unique_ptr<Expression>> arguments);
+ bool determineCallCost(std::shared_ptr<FunctionDeclaration> function,
+ const std::vector<std::unique_ptr<Expression>>& arguments,
+ int* outCost);
+ std::unique_ptr<Expression> call(Position position, std::unique_ptr<Expression> function,
+ std::vector<std::unique_ptr<Expression>> arguments);
+ std::unique_ptr<Expression> coerce(std::unique_ptr<Expression> expr,
+ std::shared_ptr<Type> type);
+ std::unique_ptr<Block> convertBlock(const ASTBlock& block);
+ std::unique_ptr<Statement> convertBreak(const ASTBreakStatement& b);
+ std::unique_ptr<Expression> convertConstructor(Position position,
+ std::shared_ptr<Type> type,
+ std::vector<std::unique_ptr<Expression>> params);
+ std::unique_ptr<Statement> convertContinue(const ASTContinueStatement& c);
+ std::unique_ptr<Statement> convertDiscard(const ASTDiscardStatement& d);
+ std::unique_ptr<Statement> convertDo(const ASTDoStatement& d);
+ std::unique_ptr<Expression> convertBinaryExpression(const ASTBinaryExpression& expression);
+ std::unique_ptr<Extension> convertExtension(const ASTExtension& e);
+ std::unique_ptr<Statement> convertExpressionStatement(const ASTExpressionStatement& s);
+ std::unique_ptr<Statement> convertFor(const ASTForStatement& f);
+ std::unique_ptr<Expression> convertIdentifier(const ASTIdentifier& identifier);
+ std::unique_ptr<Statement> convertIf(const ASTIfStatement& s);
+ std::unique_ptr<Expression> convertIndex(std::unique_ptr<Expression> base,
+ const ASTExpression& index);
+ std::unique_ptr<InterfaceBlock> convertInterfaceBlock(const ASTInterfaceBlock& s);
+ Modifiers convertModifiers(const ASTModifiers& m);
+ std::unique_ptr<Expression> convertPrefixExpression(const ASTPrefixExpression& expression);
+ std::unique_ptr<Statement> convertReturn(const ASTReturnStatement& r);
+ std::unique_ptr<Expression> convertSuffixExpression(const ASTSuffixExpression& expression);
+ std::unique_ptr<Expression> convertField(std::unique_ptr<Expression> base,
+ const std::string& field);
+ std::unique_ptr<Expression> convertSwizzle(std::unique_ptr<Expression> base,
+ const std::string& fields);
+ std::unique_ptr<Expression> convertTernaryExpression(const ASTTernaryExpression& expression);
+ std::unique_ptr<Statement> convertVarDeclarationStatement(const ASTVarDeclarationStatement& s);
+ std::unique_ptr<Statement> convertWhile(const ASTWhileStatement& w);
+
+ void checkValid(const Expression& expr);
+ void markReadFrom(std::shared_ptr<Variable> var);
+ void markWrittenTo(const Expression& expr);
+
+ std::shared_ptr<FunctionDeclaration> fCurrentFunction;
+ std::shared_ptr<SymbolTable> fSymbolTable;
+ ErrorReporter& fErrors;
+
+ friend class AutoSymbolTable;
+ friend class Compiler;
+};
+
+}
+
+#endif
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
new file mode 100644
index 0000000000..24fbb6c260
--- /dev/null
+++ b/src/sksl/SkSLMain.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "stdio.h"
+#include <fstream>
+#include "SkSLCompiler.h"
+
+/**
+ * Very simple standalone executable to facilitate testing.
+ */
+int main(int argc, const char** argv) {
+ if (argc != 3) {
+ printf("usage: skslc <input> <output>\n");
+ exit(1);
+ }
+ SkSL::Program::Kind kind;
+ size_t len = strlen(argv[1]);
+ if (len > 5 && !strcmp(argv[1] + strlen(argv[1]) - 5, ".vert")) {
+ kind = SkSL::Program::kVertex_Kind;
+ } else if (len > 5 && !strcmp(argv[1] + strlen(argv[1]) - 5, ".frag")) {
+ kind = SkSL::Program::kFragment_Kind;
+ } else {
+ printf("input filename must end in '.vert' or '.frag'\n");
+ exit(1);
+ }
+
+ std::ifstream in(argv[1]);
+ std::string text((std::istreambuf_iterator<char>(in)),
+ std::istreambuf_iterator<char>());
+ if (in.rdstate()) {
+ printf("error reading '%s'\n", argv[1]);
+ exit(2);
+ }
+ std::ofstream out(argv[2], std::ofstream::binary);
+ SkSL::Compiler compiler;
+ if (!compiler.toSPIRV(kind, text, out)) {
+ printf("%s", compiler.errorText().c_str());
+ exit(3);
+ }
+ if (out.rdstate()) {
+ printf("error writing '%s'\n", argv[2]);
+ exit(4);
+ }
+}
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
new file mode 100644
index 0000000000..3526c6ee66
--- /dev/null
+++ b/src/sksl/SkSLParser.cpp
@@ -0,0 +1,1389 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "stdio.h"
+#include "SkSLParser.h"
+#include "SkSLToken.h"
+
+#define register
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunneeded-internal-declaration"
+#endif
+#include "lex.sksl.c"
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#undef register
+
+#include "ast/SkSLASTBinaryExpression.h"
+#include "ast/SkSLASTBlock.h"
+#include "ast/SkSLASTBoolLiteral.h"
+#include "ast/SkSLASTBreakStatement.h"
+#include "ast/SkSLASTCallSuffix.h"
+#include "ast/SkSLASTContinueStatement.h"
+#include "ast/SkSLASTDiscardStatement.h"
+#include "ast/SkSLASTDoStatement.h"
+#include "ast/SkSLASTExpression.h"
+#include "ast/SkSLASTExpressionStatement.h"
+#include "ast/SkSLASTExtension.h"
+#include "ast/SkSLASTFieldSuffix.h"
+#include "ast/SkSLASTFloatLiteral.h"
+#include "ast/SkSLASTForStatement.h"
+#include "ast/SkSLASTFunction.h"
+#include "ast/SkSLASTIdentifier.h"
+#include "ast/SkSLASTIfStatement.h"
+#include "ast/SkSLASTIndexSuffix.h"
+#include "ast/SkSLASTInterfaceBlock.h"
+#include "ast/SkSLASTIntLiteral.h"
+#include "ast/SkSLASTParameter.h"
+#include "ast/SkSLASTPrefixExpression.h"
+#include "ast/SkSLASTReturnStatement.h"
+#include "ast/SkSLASTStatement.h"
+#include "ast/SkSLASTSuffixExpression.h"
+#include "ast/SkSLASTTernaryExpression.h"
+#include "ast/SkSLASTType.h"
+#include "ast/SkSLASTVarDeclaration.h"
+#include "ast/SkSLASTVarDeclarationStatement.h"
+#include "ast/SkSLASTWhileStatement.h"
+#include "ir/SkSLSymbolTable.h"
+
+namespace SkSL {
+
+Parser::Parser(std::string text, SymbolTable& types, ErrorReporter& errors)
+: fPushback(Position(-1, -1), Token::INVALID_TOKEN, "")
+, fTypes(types)
+, fErrors(errors) {
+ sksllex_init(&fScanner);
+ fBuffer = sksl_scan_string(text.c_str(), fScanner);
+ skslset_lineno(1, fScanner);
+
+ if (false) {
+ // avoid unused warning
+ yyunput(0, nullptr, fScanner);
+ }
+}
+
+Parser::~Parser() {
+ sksl_delete_buffer(fBuffer, fScanner);
+}
+
+/* (precision | directive | declaration)* END_OF_FILE */
+std::vector<std::unique_ptr<ASTDeclaration>> Parser::file() {
+ std::vector<std::unique_ptr<ASTDeclaration>> result;
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::END_OF_FILE:
+ return result;
+ case Token::PRECISION:
+ this->precision();
+ break;
+ case Token::DIRECTIVE: {
+ std::unique_ptr<ASTDeclaration> decl = this->directive();
+ if (decl) {
+ result.push_back(std::move(decl));
+ }
+ break;
+ }
+ default: {
+ std::unique_ptr<ASTDeclaration> decl = this->declaration();
+ if (!decl) {
+ continue;
+ }
+ result.push_back(std::move(decl));
+ }
+ }
+ }
+}
+
+Token Parser::nextToken() {
+ if (fPushback.fKind != Token::INVALID_TOKEN) {
+ Token result = fPushback;
+ fPushback.fKind = Token::INVALID_TOKEN;
+ fPushback.fText = "";
+ return result;
+ }
+ int token = sksllex(fScanner);
+ return Token(Position(skslget_lineno(fScanner), -1), (Token::Kind) token,
+ token == Token::END_OF_FILE ? "<end of file>" :
+ std::string(skslget_text(fScanner)));
+}
+
+void Parser::pushback(Token t) {
+ ASSERT(fPushback.fKind == Token::INVALID_TOKEN);
+ fPushback = t;
+}
+
+Token Parser::peek() {
+ fPushback = this->nextToken();
+ return fPushback;
+}
+
+bool Parser::expect(Token::Kind kind, std::string expected, Token* result) {
+ Token next = this->nextToken();
+ if (next.fKind == kind) {
+ if (result) {
+ *result = next;
+ }
+ return true;
+ } else {
+ this->error(next.fPosition, "expected " + expected + ", but found '" + next.fText + "'");
+ return false;
+ }
+}
+
+void Parser::error(Position p, std::string msg) {
+ fErrors.error(p, msg);
+}
+
+bool Parser::isType(std::string name) {
+ return nullptr != fTypes[name];
+}
+
+/* PRECISION (LOWP | MEDIUMP | HIGHP) type SEMICOLON */
+void Parser::precision() {
+ if (!this->expect(Token::PRECISION, "'precision'")) {
+ return;
+ }
+ Token p = this->nextToken();
+ switch (p.fKind) {
+ case Token::LOWP: // fall through
+ case Token::MEDIUMP: // fall through
+ case Token::HIGHP:
+ // ignored for now
+ break;
+ default:
+ this->error(p.fPosition, "expected 'lowp', 'mediump', or 'highp', but found '" +
+ p.fText + "'");
+ return;
+ }
+ if (!this->type()) {
+ return;
+ }
+ this->expect(Token::SEMICOLON, "';'");
+}
+
+/* DIRECTIVE(#version) INT_LITERAL | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
+std::unique_ptr<ASTDeclaration> Parser::directive() {
+ Token start;
+ if (!this->expect(Token::DIRECTIVE, "a directive", &start)) {
+ return nullptr;
+ }
+ if (start.fText == "#version") {
+ this->expect(Token::INT_LITERAL, "a version number");
+ // ignored for now
+ return nullptr;
+ } else if (start.fText == "#extension") {
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::COLON, "':'")) {
+ return nullptr;
+ }
+ // FIXME: need to start paying attention to this token
+ if (!this->expect(Token::IDENTIFIER, "an identifier")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTDeclaration>(new ASTExtension(start.fPosition,
+ std::move(name.fText)));
+ } else {
+ this->error(start.fPosition, "unsupported directive '" + start.fText + "'");
+ return nullptr;
+ }
+}
+
+/* modifiers (structVarDeclaration | type IDENTIFIER ((LPAREN parameter
+ (COMMA parameter)* RPAREN (block | SEMICOLON)) | SEMICOLON) | interfaceBlock) */
+std::unique_ptr<ASTDeclaration> Parser::declaration() {
+ ASTModifiers modifiers = this->modifiers();
+ Token lookahead = this->peek();
+ if (lookahead.fKind == Token::IDENTIFIER && !this->isType(lookahead.fText)) {
+ // we have an identifier that's not a type, could be the start of an interface block
+ return this->interfaceBlock(modifiers);
+ }
+ if (lookahead.fKind == Token::STRUCT) {
+ return this->structVarDeclaration(modifiers);
+ }
+ std::unique_ptr<ASTType> type(this->type());
+ if (!type) {
+ return nullptr;
+ }
+ if (type->fKind == ASTType::kStruct_Kind && peek().fKind == Token::SEMICOLON) {
+ this->nextToken();
+ return nullptr;
+ }
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ if (!modifiers.fFlags && this->peek().fKind == Token::LPAREN) {
+ this->nextToken();
+ std::vector<std::unique_ptr<ASTParameter>> parameters;
+ while (this->peek().fKind != Token::RPAREN) {
+ if (parameters.size() > 0) {
+ if (!this->expect(Token::COMMA, "','")) {
+ return nullptr;
+ }
+ }
+ std::unique_ptr<ASTParameter> parameter = this->parameter();
+ if (!parameter) {
+ return nullptr;
+ }
+ parameters.push_back(std::move(parameter));
+ }
+ this->nextToken();
+ std::unique_ptr<ASTBlock> body;
+ if (this->peek().fKind == Token::SEMICOLON) {
+ this->nextToken();
+ } else {
+ body = this->block();
+ if (!body) {
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<ASTDeclaration>(new ASTFunction(name.fPosition, std::move(type),
+ std::move(name.fText),
+ std::move(parameters),
+ std::move(body)));
+ } else {
+ return this->varDeclarationEnd(modifiers, std::move(type), name.fText);
+ }
+}
+
+/* modifiers type IDENTIFIER varDeclarationEnd */
+std::unique_ptr<ASTVarDeclaration> Parser::varDeclaration() {
+ ASTModifiers modifiers = this->modifiers();
+ std::unique_ptr<ASTType> type(this->type());
+ if (!type) {
+ return nullptr;
+ }
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ return this->varDeclarationEnd(modifiers, std::move(type), std::move(name.fText));
+}
+
+/* STRUCT IDENTIFIER LBRACE varDeclaration* RBRACE */
+std::unique_ptr<ASTType> Parser::structDeclaration() {
+ if (!this->expect(Token::STRUCT, "'struct'")) {
+ return nullptr;
+ }
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::LBRACE, "'{'")) {
+ return nullptr;
+ }
+ std::vector<Type::Field> fields;
+ while (this->peek().fKind != Token::RBRACE) {
+ std::unique_ptr<ASTVarDeclaration> decl = this->varDeclaration();
+ if (!decl) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < decl->fNames.size(); i++) {
+ auto type = std::static_pointer_cast<Type>(fTypes[decl->fType->fName]);
+ for (int j = (int) decl->fSizes[i].size() - 1; j >= 0; j--) {
+ if (decl->fSizes[i][j]->fKind == ASTExpression::kInt_Kind) {
+ this->error(decl->fPosition, "array size in struct field must be a constant");
+ }
+ uint64_t columns = ((ASTIntLiteral&) *decl->fSizes[i][j]).fValue;
+ std::string name = type->name() + "[" + to_string(columns) + "]";
+ type = std::shared_ptr<Type>(new Type(name, Type::kArray_Kind, std::move(type),
+ (int) columns));
+ }
+ fields.push_back(Type::Field(decl->fModifiers, decl->fNames[i], std::move(type)));
+ if (decl->fValues[i]) {
+ this->error(decl->fPosition, "initializers are not permitted on struct fields");
+ }
+ }
+ }
+ if (!this->expect(Token::RBRACE, "'}'")) {
+ return nullptr;
+ }
+ std::shared_ptr<Type> type(new Type(name.fText, fields));
+ fTypes.add(type->fName, type);
+ return std::unique_ptr<ASTType>(new ASTType(name.fPosition, type->fName,
+ ASTType::kStruct_Kind));
+}
+
+/* structDeclaration ((IDENTIFIER varDeclarationEnd) | SEMICOLON) */
+std::unique_ptr<ASTVarDeclaration> Parser::structVarDeclaration(ASTModifiers modifiers) {
+ std::unique_ptr<ASTType> type = this->structDeclaration();
+ if (!type) {
+ return nullptr;
+ }
+ if (peek().fKind == Token::IDENTIFIER) {
+ Token name = this->nextToken();
+ std::unique_ptr<ASTVarDeclaration> result = this->varDeclarationEnd(modifiers,
+ std::move(type),
+ std::move(name.fText));
+ if (result) {
+ for (size_t i = 0; i < result->fValues.size(); i++) {
+ if (result->fValues[i]) {
+ this->error(result->fValues[i]->fPosition,
+ "struct variables cannot be initialized");
+ }
+ }
+ }
+ return result;
+ }
+ this->expect(Token::SEMICOLON, "';'");
+ return nullptr;
+}
+
+/* (LBRACKET expression? RBRACKET)* (EQ expression)? (COMMA IDENTIFER
+ (LBRACKET expression? RBRACKET)* (EQ expression)?)* SEMICOLON */
+std::unique_ptr<ASTVarDeclaration> Parser::varDeclarationEnd(ASTModifiers mods,
+ std::unique_ptr<ASTType> type,
+ std::string name) {
+ std::vector<std::string> names;
+ std::vector<std::vector<std::unique_ptr<ASTExpression>>> sizes;
+ names.push_back(name);
+ std::vector<std::unique_ptr<ASTExpression>> currentVarSizes;
+ while (this->peek().fKind == Token::LBRACKET) {
+ this->nextToken();
+ if (this->peek().fKind == Token::RBRACKET) {
+ this->nextToken();
+ currentVarSizes.push_back(nullptr);
+ } else {
+ std::unique_ptr<ASTExpression> size(this->expression());
+ if (!size) {
+ return nullptr;
+ }
+ currentVarSizes.push_back(std::move(size));
+ if (!this->expect(Token::RBRACKET, "']'")) {
+ return nullptr;
+ }
+ }
+ }
+ sizes.push_back(std::move(currentVarSizes));
+ std::vector<std::unique_ptr<ASTExpression>> values;
+ if (this->peek().fKind == Token::EQ) {
+ this->nextToken();
+ std::unique_ptr<ASTExpression> value(this->expression());
+ if (!value) {
+ return nullptr;
+ }
+ values.push_back(std::move(value));
+ } else {
+ values.push_back(nullptr);
+ }
+ while (this->peek().fKind == Token::COMMA) {
+ this->nextToken();
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ names.push_back(name.fText);
+ currentVarSizes.clear();
+ while (this->peek().fKind == Token::LBRACKET) {
+ this->nextToken();
+ if (this->peek().fKind == Token::RBRACKET) {
+ this->nextToken();
+ currentVarSizes.push_back(nullptr);
+ } else {
+ std::unique_ptr<ASTExpression> size(this->expression());
+ if (!size) {
+ return nullptr;
+ }
+ currentVarSizes.push_back(std::move(size));
+ if (!this->expect(Token::RBRACKET, "']'")) {
+ return nullptr;
+ }
+ }
+ }
+ sizes.push_back(std::move(currentVarSizes));
+ if (this->peek().fKind == Token::EQ) {
+ this->nextToken();
+ std::unique_ptr<ASTExpression> value(this->expression());
+ if (!value) {
+ return nullptr;
+ }
+ values.push_back(std::move(value));
+ } else {
+ values.push_back(nullptr);
+ }
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTVarDeclaration>(new ASTVarDeclaration(std::move(mods),
+ std::move(type),
+ std::move(names),
+ std::move(sizes),
+ std::move(values)));
+}
+
+/* modifiers type IDENTIFIER (LBRACKET INT_LITERAL RBRACKET)? */
+std::unique_ptr<ASTParameter> Parser::parameter() {
+ ASTModifiers modifiers = this->modifiersWithDefaults(ASTModifiers::kIn_Flag);
+ std::unique_ptr<ASTType> type = this->type();
+ if (!type) {
+ return nullptr;
+ }
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ std::vector<int> sizes;
+ while (this->peek().fKind == Token::LBRACKET) {
+ this->nextToken();
+ Token sizeToken;
+ if (!this->expect(Token::INT_LITERAL, "a positive integer", &sizeToken)) {
+ return nullptr;
+ }
+ sizes.push_back(SkSL::stoi(sizeToken.fText));
+ if (!this->expect(Token::RBRACKET, "']'")) {
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<ASTParameter>(new ASTParameter(name.fPosition, modifiers,
+ std::move(type), name.fText,
+ std::move(sizes)));
+}
+
+/** (EQ INT_LITERAL)? */
+int Parser::layoutInt() {
+ if (!this->expect(Token::EQ, "'='")) {
+ return -1;
+ }
+ Token resultToken;
+ if (this->expect(Token::INT_LITERAL, "a non-negative integer", &resultToken)) {
+ return SkSL::stoi(resultToken.fText);
+ }
+ return -1;
+}
+
+/* LAYOUT LPAREN IDENTIFIER EQ INT_LITERAL (COMMA IDENTIFIER EQ INT_LITERAL)*
+ RPAREN */
+ASTLayout Parser::layout() {
+ int location = -1;
+ int binding = -1;
+ int index = -1;
+ int set = -1;
+ int builtin = -1;
+ if (this->peek().fKind == Token::LAYOUT) {
+ this->nextToken();
+ if (!this->expect(Token::LPAREN, "'('")) {
+ return ASTLayout(location, binding, index, set, builtin);
+ }
+ for (;;) {
+ Token t = this->nextToken();
+ if (t.fText == "location") {
+ location = this->layoutInt();
+ } else if (t.fText == "binding") {
+ binding = this->layoutInt();
+ } else if (t.fText == "index") {
+ index = this->layoutInt();
+ } else if (t.fText == "set") {
+ set = this->layoutInt();
+ } else if (t.fText == "builtin") {
+ builtin = this->layoutInt();
+ } else {
+ this->error(t.fPosition, ("'" + t.fText +
+ "' is not a valid layout qualifier").c_str());
+ }
+ if (this->peek().fKind == Token::RPAREN) {
+ this->nextToken();
+ break;
+ }
+ if (!this->expect(Token::COMMA, "','")) {
+ break;
+ }
+ }
+ }
+ return ASTLayout(location, binding, index, set, builtin);
+}
+
+/* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP |
+ MEDIUMP | HIGHP)* */
+ASTModifiers Parser::modifiers() {
+ ASTLayout layout = this->layout();
+ int flags = 0;
+ for (;;) {
+ // TODO: handle duplicate / incompatible flags
+ switch (peek().fKind) {
+ case Token::UNIFORM:
+ this->nextToken();
+ flags |= ASTModifiers::kUniform_Flag;
+ break;
+ case Token::CONST:
+ this->nextToken();
+ flags |= ASTModifiers::kConst_Flag;
+ break;
+ case Token::IN:
+ this->nextToken();
+ flags |= ASTModifiers::kIn_Flag;
+ break;
+ case Token::OUT:
+ this->nextToken();
+ flags |= ASTModifiers::kOut_Flag;
+ break;
+ case Token::INOUT:
+ this->nextToken();
+ flags |= ASTModifiers::kIn_Flag;
+ flags |= ASTModifiers::kOut_Flag;
+ break;
+ case Token::LOWP:
+ this->nextToken();
+ flags |= ASTModifiers::kLowp_Flag;
+ break;
+ case Token::MEDIUMP:
+ this->nextToken();
+ flags |= ASTModifiers::kMediump_Flag;
+ break;
+ case Token::HIGHP:
+ this->nextToken();
+ flags |= ASTModifiers::kHighp_Flag;
+ break;
+ default:
+ return ASTModifiers(layout, flags);
+ }
+ }
+}
+
+ASTModifiers Parser::modifiersWithDefaults(int defaultFlags) {
+ ASTModifiers result = this->modifiers();
+ if (!result.fFlags) {
+ return ASTModifiers(result.fLayout, defaultFlags);
+ }
+ return result;
+}
+
+/* ifStatement | forStatement | doStatement | whileStatement | block | expression */
+std::unique_ptr<ASTStatement> Parser::statement() {
+ Token start = this->peek();
+ switch (start.fKind) {
+ case Token::IF:
+ return this->ifStatement();
+ case Token::FOR:
+ return this->forStatement();
+ case Token::DO:
+ return this->doStatement();
+ case Token::WHILE:
+ return this->whileStatement();
+ case Token::RETURN:
+ return this->returnStatement();
+ case Token::BREAK:
+ return this->breakStatement();
+ case Token::CONTINUE:
+ return this->continueStatement();
+ case Token::DISCARD:
+ return this->discardStatement();
+ case Token::LBRACE:
+ return this->block();
+ case Token::SEMICOLON:
+ this->nextToken();
+ return std::unique_ptr<ASTStatement>(new ASTBlock(start.fPosition, {}));
+ case Token::CONST: // fall through
+ case Token::HIGHP: // fall through
+ case Token::MEDIUMP: // fall through
+ case Token::LOWP: {
+ auto decl = this->varDeclaration();
+ if (!decl) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement(std::move(decl)));
+ }
+ case Token::IDENTIFIER:
+ if (this->isType(start.fText)) {
+ auto decl = this->varDeclaration();
+ if (!decl) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement(
+ std::move(decl)));
+ }
+ // fall through
+ default:
+ return this->expressionStatement();
+ }
+}
+
+/* IDENTIFIER(type) */
+std::unique_ptr<ASTType> Parser::type() {
+ Token type;
+ if (!this->expect(Token::IDENTIFIER, "a type", &type)) {
+ return nullptr;
+ }
+ if (!this->isType(type.fText)) {
+ this->error(type.fPosition, ("no type named '" + type.fText + "'").c_str());
+ return nullptr;
+ }
+ return std::unique_ptr<ASTType>(new ASTType(type.fPosition, std::move(type.fText),
+ ASTType::kIdentifier_Kind));
+}
+
+/* IDENTIFIER LBRACE varDeclaration* RBRACE */
+std::unique_ptr<ASTDeclaration> Parser::interfaceBlock(ASTModifiers mods) {
+ Token name;
+ if (!this->expect(Token::IDENTIFIER, "an identifier", &name)) {
+ return nullptr;
+ }
+ if (peek().fKind != Token::LBRACE) {
+ // we only get into interfaceBlock if we found a top-level identifier which was not a type.
+ // 99% of the time, the user was not actually intending to create an interface block, so
+ // it's better to report it as an unknown type
+ this->error(name.fPosition, "no type named '" + name.fText + "'");
+ return nullptr;
+ }
+ this->nextToken();
+ std::vector<std::unique_ptr<ASTVarDeclaration>> decls;
+ while (this->peek().fKind != Token::RBRACE) {
+ std::unique_ptr<ASTVarDeclaration> decl = this->varDeclaration();
+ if (!decl) {
+ return nullptr;
+ }
+ decls.push_back(std::move(decl));
+ }
+ this->nextToken();
+ std::string valueName;
+ if (this->peek().fKind == Token::IDENTIFIER) {
+ valueName = this->nextToken().fText;
+ }
+ this->expect(Token::SEMICOLON, "';'");
+ return std::unique_ptr<ASTDeclaration>(new ASTInterfaceBlock(name.fPosition, mods,
+ name.fText, std::move(valueName),
+ std::move(decls)));
+}
+
+/* IF LPAREN expression RPAREN statement (ELSE statement)? */
+std::unique_ptr<ASTIfStatement> Parser::ifStatement() {
+ Token start;
+ if (!this->expect(Token::IF, "'if'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::LPAREN, "'('")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTExpression> test(this->expression());
+ if (!test) {
+ return nullptr;
+ }
+ if (!this->expect(Token::RPAREN, "')'")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> ifTrue(this->statement());
+ if (!ifTrue) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> ifFalse;
+ if (this->peek().fKind == Token::ELSE) {
+ this->nextToken();
+ ifFalse = this->statement();
+ if (!ifFalse) {
+ return nullptr;
+ }
+ }
+ return std::unique_ptr<ASTIfStatement>(new ASTIfStatement(start.fPosition, std::move(test),
+ std::move(ifTrue),
+ std::move(ifFalse)));
+}
+
+/* DO statement WHILE LPAREN expression RPAREN SEMICOLON */
+std::unique_ptr<ASTDoStatement> Parser::doStatement() {
+ Token start;
+ if (!this->expect(Token::DO, "'do'", &start)) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> statement(this->statement());
+ if (!statement) {
+ return nullptr;
+ }
+ if (!this->expect(Token::WHILE, "'while'")) {
+ return nullptr;
+ }
+ if (!this->expect(Token::LPAREN, "'('")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTExpression> test(this->expression());
+ if (!test) {
+ return nullptr;
+ }
+ if (!this->expect(Token::RPAREN, "')'")) {
+ return nullptr;
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTDoStatement>(new ASTDoStatement(start.fPosition,
+ std::move(statement),
+ std::move(test)));
+}
+
+/* WHILE LPAREN expression RPAREN STATEMENT */
+std::unique_ptr<ASTWhileStatement> Parser::whileStatement() {
+ Token start;
+ if (!this->expect(Token::WHILE, "'while'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::LPAREN, "'('")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTExpression> test(this->expression());
+ if (!test) {
+ return nullptr;
+ }
+ if (!this->expect(Token::RPAREN, "')'")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> statement(this->statement());
+ if (!statement) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTWhileStatement>(new ASTWhileStatement(start.fPosition,
+ std::move(test),
+ std::move(statement)));
+}
+
+/* FOR LPAREN (declaration | expression)? SEMICOLON expression? SEMICOLON expression? RPAREN
+ STATEMENT */
+std::unique_ptr<ASTForStatement> Parser::forStatement() {
+ Token start;
+ if (!this->expect(Token::FOR, "'for'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::LPAREN, "'('")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> initializer;
+ Token nextToken = this->peek();
+ switch (nextToken.fKind) {
+ case Token::SEMICOLON:
+ break;
+ case Token::CONST:
+ initializer = std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement(
+ this->varDeclaration()));
+ break;
+ case Token::IDENTIFIER:
+ if (this->isType(nextToken.fText)) {
+ initializer = std::unique_ptr<ASTStatement>(new ASTVarDeclarationStatement(
+ this->varDeclaration()));
+ break;
+ }
+ // fall through
+ default:
+ initializer = this->expressionStatement();
+ }
+ std::unique_ptr<ASTExpression> test;
+ if (this->peek().fKind != Token::SEMICOLON) {
+ test = this->expression();
+ if (!test) {
+ return nullptr;
+ }
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTExpression> next;
+ if (this->peek().fKind != Token::SEMICOLON) {
+ next = this->expression();
+ if (!next) {
+ return nullptr;
+ }
+ }
+ if (!this->expect(Token::RPAREN, "')'")) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTStatement> statement(this->statement());
+ if (!statement) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTForStatement>(new ASTForStatement(start.fPosition,
+ std::move(initializer),
+ std::move(test), std::move(next),
+ std::move(statement)));
+}
+
+/* RETURN expression? SEMICOLON */
+std::unique_ptr<ASTReturnStatement> Parser::returnStatement() {
+ Token start;
+ if (!this->expect(Token::RETURN, "'return'", &start)) {
+ return nullptr;
+ }
+ std::unique_ptr<ASTExpression> expression;
+ if (this->peek().fKind != Token::SEMICOLON) {
+ expression = this->expression();
+ if (!expression) {
+ return nullptr;
+ }
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTReturnStatement>(new ASTReturnStatement(start.fPosition,
+ std::move(expression)));
+}
+
+/* BREAK SEMICOLON */
+std::unique_ptr<ASTBreakStatement> Parser::breakStatement() {
+ Token start;
+ if (!this->expect(Token::BREAK, "'break'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTBreakStatement>(new ASTBreakStatement(start.fPosition));
+}
+
+/* CONTINUE SEMICOLON */
+std::unique_ptr<ASTContinueStatement> Parser::continueStatement() {
+ Token start;
+ if (!this->expect(Token::CONTINUE, "'continue'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTContinueStatement>(new ASTContinueStatement(start.fPosition));
+}
+
+/* DISCARD SEMICOLON */
+std::unique_ptr<ASTDiscardStatement> Parser::discardStatement() {
+ Token start;
+ if (!this->expect(Token::DISCARD, "'continue'", &start)) {
+ return nullptr;
+ }
+ if (!this->expect(Token::SEMICOLON, "';'")) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTDiscardStatement>(new ASTDiscardStatement(start.fPosition));
+}
+
+/* LBRACE statement* RBRACE */
+std::unique_ptr<ASTBlock> Parser::block() {
+ Token start;
+ if (!this->expect(Token::LBRACE, "'{'", &start)) {
+ return nullptr;
+ }
+ std::vector<std::unique_ptr<ASTStatement>> statements;
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::RBRACE:
+ this->nextToken();
+ return std::unique_ptr<ASTBlock>(new ASTBlock(start.fPosition,
+ std::move(statements)));
+ case Token::END_OF_FILE:
+ this->error(this->peek().fPosition, "expected '}', but found end of file");
+ return nullptr;
+ default: {
+ std::unique_ptr<ASTStatement> statement = this->statement();
+ if (!statement) {
+ return nullptr;
+ }
+ statements.push_back(std::move(statement));
+ }
+ }
+ }
+}
+
+/* expression SEMICOLON */
+std::unique_ptr<ASTExpressionStatement> Parser::expressionStatement() {
+ std::unique_ptr<ASTExpression> expr = this->expression();
+ if (expr) {
+ if (this->expect(Token::SEMICOLON, "';'")) {
+ ASTExpressionStatement* result = new ASTExpressionStatement(std::move(expr));
+ return std::unique_ptr<ASTExpressionStatement>(result);
+ }
+ }
+ return nullptr;
+}
+
+/* assignmentExpression */
+std::unique_ptr<ASTExpression> Parser::expression() {
+ return this->assignmentExpression();
+}
+
+/* ternaryExpression ((EQEQ | STAREQ | SLASHEQ | PERCENTEQ | PLUSEQ | MINUSEQ | SHLEQ | SHREQ |
+ BITWISEANDEQ | BITWISEXOREQ | BITWISEOREQ | LOGICALANDEQ | LOGICALXOREQ | LOGICALOREQ)
+ assignmentExpression)*
+ */
+std::unique_ptr<ASTExpression> Parser::assignmentExpression() {
+ std::unique_ptr<ASTExpression> result = this->ternaryExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::EQ: // fall through
+ case Token::STAREQ: // fall through
+ case Token::SLASHEQ: // fall through
+ case Token::PERCENTEQ: // fall through
+ case Token::PLUSEQ: // fall through
+ case Token::MINUSEQ: // fall through
+ case Token::SHLEQ: // fall through
+ case Token::SHREQ: // fall through
+ case Token::BITWISEANDEQ: // fall through
+ case Token::BITWISEXOREQ: // fall through
+ case Token::BITWISEOREQ: // fall through
+ case Token::LOGICALANDEQ: // fall through
+ case Token::LOGICALXOREQ: // fall through
+ case Token::LOGICALOREQ: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->assignmentExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result = std::unique_ptr<ASTExpression>(new ASTBinaryExpression(std::move(result),
+ t,
+ std::move(right)));
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* logicalOrExpression ('?' expression ':' assignmentExpression)? */
+std::unique_ptr<ASTExpression> Parser::ternaryExpression() {
+ std::unique_ptr<ASTExpression> result = this->logicalOrExpression();
+ if (!result) {
+ return nullptr;
+ }
+ if (this->peek().fKind == Token::QUESTION) {
+ Token question = this->nextToken();
+ std::unique_ptr<ASTExpression> trueExpr = this->expression();
+ if (!trueExpr) {
+ return nullptr;
+ }
+ if (this->expect(Token::COLON, "':'")) {
+ std::unique_ptr<ASTExpression> falseExpr = this->assignmentExpression();
+ return std::unique_ptr<ASTExpression>(new ASTTernaryExpression(std::move(result),
+ std::move(trueExpr),
+ std::move(falseExpr)));
+ }
+ return nullptr;
+ }
+ return result;
+}
+
+/* logicalXorExpression (LOGICALOR logicalXorExpression)* */
+std::unique_ptr<ASTExpression> Parser::logicalOrExpression() {
+ std::unique_ptr<ASTExpression> result = this->logicalXorExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::LOGICALOR) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->logicalXorExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* logicalAndExpression (LOGICALXOR logicalAndExpression)* */
+std::unique_ptr<ASTExpression> Parser::logicalXorExpression() {
+ std::unique_ptr<ASTExpression> result = this->logicalAndExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::LOGICALXOR) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->logicalAndExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* bitwiseOrExpression (LOGICALAND bitwiseOrExpression)* */
+std::unique_ptr<ASTExpression> Parser::logicalAndExpression() {
+ std::unique_ptr<ASTExpression> result = this->bitwiseOrExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::LOGICALAND) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->bitwiseOrExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* bitwiseXorExpression (BITWISEOR bitwiseXorExpression)* */
+std::unique_ptr<ASTExpression> Parser::bitwiseOrExpression() {
+ std::unique_ptr<ASTExpression> result = this->bitwiseXorExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::BITWISEOR) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->bitwiseXorExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* bitwiseAndExpression (BITWISEXOR bitwiseAndExpression)* */
+std::unique_ptr<ASTExpression> Parser::bitwiseXorExpression() {
+ std::unique_ptr<ASTExpression> result = this->bitwiseAndExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::BITWISEXOR) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->bitwiseAndExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* equalityExpression (BITWISEAND equalityExpression)* */
+std::unique_ptr<ASTExpression> Parser::bitwiseAndExpression() {
+ std::unique_ptr<ASTExpression> result = this->equalityExpression();
+ if (!result) {
+ return nullptr;
+ }
+ while (this->peek().fKind == Token::BITWISEAND) {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->equalityExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ }
+ return result;
+}
+
+/* relationalExpression ((EQEQ | NEQ) relationalExpression)* */
+std::unique_ptr<ASTExpression> Parser::equalityExpression() {
+ std::unique_ptr<ASTExpression> result = this->relationalExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::EQEQ: // fall through
+ case Token::NEQ: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->relationalExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* shiftExpression ((LT | GT | LTEQ | GTEQ) shiftExpression)* */
+std::unique_ptr<ASTExpression> Parser::relationalExpression() {
+ std::unique_ptr<ASTExpression> result = this->shiftExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::LT: // fall through
+ case Token::GT: // fall through
+ case Token::LTEQ: // fall through
+ case Token::GTEQ: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->shiftExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* additiveExpression ((SHL | SHR) additiveExpression)* */
+std::unique_ptr<ASTExpression> Parser::shiftExpression() {
+ std::unique_ptr<ASTExpression> result = this->additiveExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::SHL: // fall through
+ case Token::SHR: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->additiveExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* multiplicativeExpression ((PLUS | MINUS) multiplicativeExpression)* */
+std::unique_ptr<ASTExpression> Parser::additiveExpression() {
+ std::unique_ptr<ASTExpression> result = this->multiplicativeExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::PLUS: // fall through
+ case Token::MINUS: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->multiplicativeExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* unaryExpression ((STAR | SLASH | PERCENT) unaryExpression)* */
+std::unique_ptr<ASTExpression> Parser::multiplicativeExpression() {
+ std::unique_ptr<ASTExpression> result = this->unaryExpression();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::STAR: // fall through
+ case Token::SLASH: // fall through
+ case Token::PERCENT: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> right = this->unaryExpression();
+ if (!right) {
+ return nullptr;
+ }
+ result.reset(new ASTBinaryExpression(std::move(result), t, std::move(right)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* postfixExpression | (PLUS | MINUS | NOT | PLUSPLUS | MINUSMINUS) unaryExpression */
+std::unique_ptr<ASTExpression> Parser::unaryExpression() {
+ switch (this->peek().fKind) {
+ case Token::PLUS: // fall through
+ case Token::MINUS: // fall through
+ case Token::NOT: // fall through
+ case Token::PLUSPLUS: // fall through
+ case Token::MINUSMINUS: {
+ Token t = this->nextToken();
+ std::unique_ptr<ASTExpression> expr = this->unaryExpression();
+ if (!expr) {
+ return nullptr;
+ }
+ return std::unique_ptr<ASTExpression>(new ASTPrefixExpression(t, std::move(expr)));
+ }
+ default:
+ return this->postfixExpression();
+ }
+}
+
+/* term suffix* */
+std::unique_ptr<ASTExpression> Parser::postfixExpression() {
+ std::unique_ptr<ASTExpression> result = this->term();
+ if (!result) {
+ return nullptr;
+ }
+ for (;;) {
+ switch (this->peek().fKind) {
+ case Token::LBRACKET: // fall through
+ case Token::DOT: // fall through
+ case Token::LPAREN: // fall through
+ case Token::PLUSPLUS: // fall through
+ case Token::MINUSMINUS: {
+ std::unique_ptr<ASTSuffix> s = this->suffix();
+ if (!s) {
+ return nullptr;
+ }
+ result.reset(new ASTSuffixExpression(std::move(result), std::move(s)));
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+/* LBRACKET expression RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN |
+ PLUSPLUS | MINUSMINUS */
+std::unique_ptr<ASTSuffix> Parser::suffix() {
+ Token next = this->nextToken();
+ switch (next.fKind) {
+ case Token::LBRACKET: {
+ std::unique_ptr<ASTExpression> e = this->expression();
+ if (!e) {
+ return nullptr;
+ }
+ this->expect(Token::RBRACKET, "']' to complete array access expression");
+ return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(std::move(e)));
+ }
+ case Token::DOT: {
+ Position pos = this->peek().fPosition;
+ std::string text;
+ if (this->identifier(&text)) {
+ return std::unique_ptr<ASTSuffix>(new ASTFieldSuffix(pos, std::move(text)));
+ }
+ return nullptr;
+ }
+ case Token::LPAREN: {
+ std::vector<std::unique_ptr<ASTExpression>> parameters;
+ if (this->peek().fKind != Token::RPAREN) {
+ for (;;) {
+ std::unique_ptr<ASTExpression> expr = this->expression();
+ if (!expr) {
+ return nullptr;
+ }
+ parameters.push_back(std::move(expr));
+ if (this->peek().fKind != Token::COMMA) {
+ break;
+ }
+ this->nextToken();
+ }
+ }
+ this->expect(Token::RPAREN, "')' to complete function parameters");
+ return std::unique_ptr<ASTSuffix>(new ASTCallSuffix(next.fPosition,
+ std::move(parameters)));
+ }
+ case Token::PLUSPLUS:
+ return std::unique_ptr<ASTSuffix>(new ASTSuffix(next.fPosition,
+ ASTSuffix::kPostIncrement_Kind));
+ case Token::MINUSMINUS:
+ return std::unique_ptr<ASTSuffix>(new ASTSuffix(next.fPosition,
+ ASTSuffix::kPostDecrement_Kind));
+ default: {
+ this->error(next.fPosition, "expected expression suffix, but found '" + next.fText +
+ "'\n");
+ return nullptr;
+ }
+ }
+}
+
+/* IDENTIFIER | intLiteral | floatLiteral | boolLiteral | '(' expression ')' */
+std::unique_ptr<ASTExpression> Parser::term() {
+ std::unique_ptr<ASTExpression> result;
+ Token t = this->peek();
+ switch (t.fKind) {
+ case Token::IDENTIFIER: {
+ std::string text;
+ if (this->identifier(&text)) {
+ result.reset(new ASTIdentifier(t.fPosition, std::move(text)));
+ }
+ break;
+ }
+ case Token::INT_LITERAL: {
+ int64_t i;
+ if (this->intLiteral(&i)) {
+ result.reset(new ASTIntLiteral(t.fPosition, i));
+ }
+ break;
+ }
+ case Token::FLOAT_LITERAL: {
+ double f;
+ if (this->floatLiteral(&f)) {
+ result.reset(new ASTFloatLiteral(t.fPosition, f));
+ }
+ break;
+ }
+ case Token::TRUE_LITERAL: // fall through
+ case Token::FALSE_LITERAL: {
+ bool b;
+ if (this->boolLiteral(&b)) {
+ result.reset(new ASTBoolLiteral(t.fPosition, b));
+ }
+ break;
+ }
+ case Token::LPAREN: {
+ this->nextToken();
+ result = this->expression();
+ if (result) {
+ this->expect(Token::RPAREN, "')' to complete expression");
+ }
+ break;
+ }
+ default:
+ this->nextToken();
+ this->error(t.fPosition, "expected expression, but found '" + t.fText + "'\n");
+ result = nullptr;
+ }
+ return result;
+}
+
+/* INT_LITERAL */
+bool Parser::intLiteral(int64_t* dest) {
+ Token t;
+ if (this->expect(Token::INT_LITERAL, "integer literal", &t)) {
+ *dest = SkSL::stol(t.fText);
+ return true;
+ }
+ return false;
+}
+
+/* FLOAT_LITERAL */
+bool Parser::floatLiteral(double* dest) {
+ Token t;
+ if (this->expect(Token::FLOAT_LITERAL, "float literal", &t)) {
+ *dest = SkSL::stod(t.fText);
+ return true;
+ }
+ return false;
+}
+
+/* TRUE_LITERAL | FALSE_LITERAL */
+bool Parser::boolLiteral(bool* dest) {
+ Token t = this->nextToken();
+ switch (t.fKind) {
+ case Token::TRUE_LITERAL:
+ *dest = true;
+ return true;
+ case Token::FALSE_LITERAL:
+ *dest = false;
+ return true;
+ default:
+ this->error(t.fPosition, "expected 'true' or 'false', but found '" + t.fText + "'\n");
+ return false;
+ }
+}
+
+/* IDENTIFIER */
+bool Parser::identifier(std::string* dest) {
+ Token t;
+ if (this->expect(Token::IDENTIFIER, "identifier", &t)) {
+ *dest = t.fText;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
diff --git a/src/sksl/SkSLParser.h b/src/sksl/SkSLParser.h
new file mode 100644
index 0000000000..45629a370a
--- /dev/null
+++ b/src/sksl/SkSLParser.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_PARSER
+#define SKSL_PARSER
+
+#include <string>
+#include <vector>
+#include <memory>
+#include <unordered_set>
+#include "SkSLErrorReporter.h"
+#include "SkSLToken.h"
+
+struct yy_buffer_state;
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+namespace SkSL {
+
+struct ASTBlock;
+struct ASTBreakStatement;
+struct ASTContinueStatement;
+struct ASTDeclaration;
+struct ASTDiscardStatement;
+struct ASTDoStatement;
+struct ASTExpression;
+struct ASTExpressionStatement;
+struct ASTForStatement;
+struct ASTIfStatement;
+struct ASTInterfaceBlock;
+struct ASTLayout;
+struct ASTModifiers;
+struct ASTParameter;
+struct ASTReturnStatement;
+struct ASTStatement;
+struct ASTSuffix;
+struct ASTType;
+struct ASTWhileStatement;
+struct ASTVarDeclaration;
+class SymbolTable;
+
+/**
+ * Consumes .sksl text and produces an abstract syntax tree describing the contents.
+ */
+class Parser {
+public:
+ Parser(std::string text, SymbolTable& types, ErrorReporter& errors);
+
+ ~Parser();
+
+ /**
+ * Consumes a complete .sksl file and produces a list of declarations. Errors are reported via
+ * the ErrorReporter; the return value may contain some declarations even when errors have
+ * occurred.
+ */
+ std::vector<std::unique_ptr<ASTDeclaration>> file();
+
+private:
+ /**
+ * Return the next token from the parse stream.
+ */
+ Token nextToken();
+
+ /**
+ * Push a token back onto the parse stream, so that it is the next one read. Only a single level
+ * of pushback is supported (that is, it is an error to call pushback() twice in a row without
+ * an intervening nextToken()).
+ */
+ void pushback(Token t);
+
+ /**
+ * Returns the next token without consuming it from the stream.
+ */
+ Token peek();
+
+ /**
+ * Reads the next token and generates an error if it is not the expected type. The 'expected'
+ * string is part of the error message, which reads:
+ *
+ * "expected <expected>, but found '<actual text>'"
+ *
+ * If 'result' is non-null, it is set to point to the token that was read.
+ * Returns true if the read token was as expected, false otherwise.
+ */
+ bool expect(Token::Kind kind, std::string expected, Token* result = nullptr);
+
+ void error(Position p, std::string msg);
+
+ /**
+ * Returns true if the 'name' identifier refers to a type name. For instance, isType("int") will
+ * always return true.
+ */
+ bool isType(std::string name);
+
+ // these functions parse individual grammar rules from the current parse position; you probably
+ // don't need to call any of these outside of the parser. The function declarations in the .cpp
+ // file have comments describing the grammar rules.
+
+ void precision();
+
+ std::unique_ptr<ASTDeclaration> directive();
+
+ std::unique_ptr<ASTDeclaration> declaration();
+
+ std::unique_ptr<ASTVarDeclaration> varDeclaration();
+
+ std::unique_ptr<ASTType> structDeclaration();
+
+ std::unique_ptr<ASTVarDeclaration> structVarDeclaration(ASTModifiers modifiers);
+
+ std::unique_ptr<ASTVarDeclaration> varDeclarationEnd(ASTModifiers modifiers,
+ std::unique_ptr<ASTType> type,
+ std::string name);
+
+ std::unique_ptr<ASTParameter> parameter();
+
+ int layoutInt();
+
+ ASTLayout layout();
+
+ ASTModifiers modifiers();
+
+ ASTModifiers modifiersWithDefaults(int defaultFlags);
+
+ std::unique_ptr<ASTStatement> statement();
+
+ std::unique_ptr<ASTType> type();
+
+ std::unique_ptr<ASTDeclaration> interfaceBlock(ASTModifiers mods);
+
+ std::unique_ptr<ASTIfStatement> ifStatement();
+
+ std::unique_ptr<ASTDoStatement> doStatement();
+
+ std::unique_ptr<ASTWhileStatement> whileStatement();
+
+ std::unique_ptr<ASTForStatement> forStatement();
+
+ std::unique_ptr<ASTReturnStatement> returnStatement();
+
+ std::unique_ptr<ASTBreakStatement> breakStatement();
+
+ std::unique_ptr<ASTContinueStatement> continueStatement();
+
+ std::unique_ptr<ASTDiscardStatement> discardStatement();
+
+ std::unique_ptr<ASTBlock> block();
+
+ std::unique_ptr<ASTExpressionStatement> expressionStatement();
+
+ std::unique_ptr<ASTExpression> expression();
+
+ std::unique_ptr<ASTExpression> assignmentExpression();
+
+ std::unique_ptr<ASTExpression> ternaryExpression();
+
+ std::unique_ptr<ASTExpression> logicalOrExpression();
+
+ std::unique_ptr<ASTExpression> logicalXorExpression();
+
+ std::unique_ptr<ASTExpression> logicalAndExpression();
+
+ std::unique_ptr<ASTExpression> bitwiseOrExpression();
+
+ std::unique_ptr<ASTExpression> bitwiseXorExpression();
+
+ std::unique_ptr<ASTExpression> bitwiseAndExpression();
+
+ std::unique_ptr<ASTExpression> equalityExpression();
+
+ std::unique_ptr<ASTExpression> relationalExpression();
+
+ std::unique_ptr<ASTExpression> shiftExpression();
+
+ std::unique_ptr<ASTExpression> additiveExpression();
+
+ std::unique_ptr<ASTExpression> multiplicativeExpression();
+
+ std::unique_ptr<ASTExpression> unaryExpression();
+
+ std::unique_ptr<ASTExpression> postfixExpression();
+
+ std::unique_ptr<ASTSuffix> suffix();
+
+ std::unique_ptr<ASTExpression> term();
+
+ bool intLiteral(int64_t* dest);
+
+ bool floatLiteral(double* dest);
+
+ bool boolLiteral(bool* dest);
+
+ bool identifier(std::string* dest);
+
+
+ void* fScanner;
+ YY_BUFFER_STATE fBuffer;
+ Token fPushback;
+ SymbolTable& fTypes;
+ ErrorReporter& fErrors;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLPosition.h b/src/sksl/SkSLPosition.h
new file mode 100644
index 0000000000..979f630ae7
--- /dev/null
+++ b/src/sksl/SkSLPosition.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_POSITION
+#define SKSL_POSITION
+
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * Represents a position in the source code. Both line and column are one-based. Column is currently
+ * ignored.
+ */
+struct Position {
+ Position()
+ : fLine(-1)
+ , fColumn(-1) {}
+
+ Position(int line, int column)
+ : fLine(line)
+ , fColumn(column) {}
+
+ std::string description() const {
+ return to_string(fLine);
+ }
+
+ int fLine;
+ int fColumn;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
new file mode 100644
index 0000000000..037abc03e1
--- /dev/null
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -0,0 +1,2628 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLSPIRVCodeGenerator.h"
+
+#include "string.h"
+
+#include "GLSL.std.450.h"
+
+#include "ir/SkSLExpressionStatement.h"
+#include "ir/SkSLExtension.h"
+#include "ir/SkSLIndexExpression.h"
+#include "ir/SkSLVariableReference.h"
+
+namespace SkSL {
+
+#define SPIRV_DEBUG 0
+
+static const int32_t SKSL_MAGIC = 0x0; // FIXME: we should probably register a magic number
+
+void SPIRVCodeGenerator::setupIntrinsics() {
+#define ALL_GLSL(x) std::make_tuple(kGLSL_STD_450_IntrinsicKind, GLSLstd450 ## x, GLSLstd450 ## x, \
+ GLSLstd450 ## x, GLSLstd450 ## x)
+#define BY_TYPE_GLSL(ifFloat, ifInt, ifUInt) std::make_tuple(kGLSL_STD_450_IntrinsicKind, \
+ GLSLstd450 ## ifFloat, \
+ GLSLstd450 ## ifInt, \
+ GLSLstd450 ## ifUInt, \
+ SpvOpUndef)
+#define SPECIAL(x) std::make_tuple(kSpecial_IntrinsicKind, k ## x ## _SpecialIntrinsic, \
+ k ## x ## _SpecialIntrinsic, k ## x ## _SpecialIntrinsic, \
+ k ## x ## _SpecialIntrinsic)
+ fIntrinsicMap["round"] = ALL_GLSL(Round);
+ fIntrinsicMap["roundEven"] = ALL_GLSL(RoundEven);
+ fIntrinsicMap["trunc"] = ALL_GLSL(Trunc);
+ fIntrinsicMap["abs"] = BY_TYPE_GLSL(FAbs, SAbs, SAbs);
+ fIntrinsicMap["sign"] = BY_TYPE_GLSL(FSign, SSign, SSign);
+ fIntrinsicMap["floor"] = ALL_GLSL(Floor);
+ fIntrinsicMap["ceil"] = ALL_GLSL(Ceil);
+ fIntrinsicMap["fract"] = ALL_GLSL(Fract);
+ fIntrinsicMap["radians"] = ALL_GLSL(Radians);
+ fIntrinsicMap["degrees"] = ALL_GLSL(Degrees);
+ fIntrinsicMap["sin"] = ALL_GLSL(Sin);
+ fIntrinsicMap["cos"] = ALL_GLSL(Cos);
+ fIntrinsicMap["tan"] = ALL_GLSL(Tan);
+ fIntrinsicMap["asin"] = ALL_GLSL(Asin);
+ fIntrinsicMap["acos"] = ALL_GLSL(Acos);
+ fIntrinsicMap["atan"] = SPECIAL(Atan);
+ fIntrinsicMap["sinh"] = ALL_GLSL(Sinh);
+ fIntrinsicMap["cosh"] = ALL_GLSL(Cosh);
+ fIntrinsicMap["tanh"] = ALL_GLSL(Tanh);
+ fIntrinsicMap["asinh"] = ALL_GLSL(Asinh);
+ fIntrinsicMap["acosh"] = ALL_GLSL(Acosh);
+ fIntrinsicMap["atanh"] = ALL_GLSL(Atanh);
+ fIntrinsicMap["pow"] = ALL_GLSL(Pow);
+ fIntrinsicMap["exp"] = ALL_GLSL(Exp);
+ fIntrinsicMap["log"] = ALL_GLSL(Log);
+ fIntrinsicMap["exp2"] = ALL_GLSL(Exp2);
+ fIntrinsicMap["log2"] = ALL_GLSL(Log2);
+ fIntrinsicMap["sqrt"] = ALL_GLSL(Sqrt);
+ fIntrinsicMap["inversesqrt"] = ALL_GLSL(InverseSqrt);
+ fIntrinsicMap["determinant"] = ALL_GLSL(Determinant);
+ fIntrinsicMap["matrixInverse"] = ALL_GLSL(MatrixInverse);
+ fIntrinsicMap["mod"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFMod, SpvOpSMod,
+ SpvOpUMod, SpvOpUndef);
+ fIntrinsicMap["min"] = BY_TYPE_GLSL(FMin, SMin, UMin);
+ fIntrinsicMap["max"] = BY_TYPE_GLSL(FMax, SMax, UMax);
+ fIntrinsicMap["clamp"] = BY_TYPE_GLSL(FClamp, SClamp, UClamp);
+ fIntrinsicMap["dot"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDot, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef);
+ fIntrinsicMap["mix"] = ALL_GLSL(FMix);
+ fIntrinsicMap["step"] = ALL_GLSL(Step);
+ fIntrinsicMap["smoothstep"] = ALL_GLSL(SmoothStep);
+ fIntrinsicMap["fma"] = ALL_GLSL(Fma);
+ fIntrinsicMap["frexp"] = ALL_GLSL(Frexp);
+ fIntrinsicMap["ldexp"] = ALL_GLSL(Ldexp);
+
+#define PACK(type) fIntrinsicMap["pack" #type] = ALL_GLSL(Pack ## type); \
+ fIntrinsicMap["unpack" #type] = ALL_GLSL(Unpack ## type)
+ PACK(Snorm4x8);
+ PACK(Unorm4x8);
+ PACK(Snorm2x16);
+ PACK(Unorm2x16);
+ PACK(Half2x16);
+ PACK(Double2x32);
+ fIntrinsicMap["length"] = ALL_GLSL(Length);
+ fIntrinsicMap["distance"] = ALL_GLSL(Distance);
+ fIntrinsicMap["cross"] = ALL_GLSL(Cross);
+ fIntrinsicMap["normalize"] = ALL_GLSL(Normalize);
+ fIntrinsicMap["faceForward"] = ALL_GLSL(FaceForward);
+ fIntrinsicMap["reflect"] = ALL_GLSL(Reflect);
+ fIntrinsicMap["refract"] = ALL_GLSL(Refract);
+ fIntrinsicMap["findLSB"] = ALL_GLSL(FindILsb);
+ fIntrinsicMap["findMSB"] = BY_TYPE_GLSL(FindSMsb, FindSMsb, FindUMsb);
+ fIntrinsicMap["dFdx"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdx, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef);
+ fIntrinsicMap["dFdy"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef);
+ fIntrinsicMap["dFdy"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpDPdy, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef);
+ fIntrinsicMap["texture"] = SPECIAL(Texture);
+ fIntrinsicMap["texture2D"] = SPECIAL(Texture2D);
+ fIntrinsicMap["textureProj"] = SPECIAL(TextureProj);
+
+ fIntrinsicMap["any"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef, SpvOpAny);
+ fIntrinsicMap["all"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpUndef,
+ SpvOpUndef, SpvOpUndef, SpvOpAll);
+ fIntrinsicMap["equal"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFOrdEqual,
+ SpvOpIEqual, SpvOpIEqual,
+ SpvOpLogicalEqual);
+ fIntrinsicMap["notEqual"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpFOrdNotEqual,
+ SpvOpINotEqual, SpvOpINotEqual,
+ SpvOpLogicalNotEqual);
+ fIntrinsicMap["lessThan"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpSLessThan,
+ SpvOpULessThan, SpvOpFOrdLessThan,
+ SpvOpUndef);
+ fIntrinsicMap["lessThanEqual"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpSLessThanEqual,
+ SpvOpULessThanEqual, SpvOpFOrdLessThanEqual,
+ SpvOpUndef);
+ fIntrinsicMap["greaterThan"] = std::make_tuple(kSPIRV_IntrinsicKind, SpvOpSGreaterThan,
+ SpvOpUGreaterThan, SpvOpFOrdGreaterThan,
+ SpvOpUndef);
+ fIntrinsicMap["greaterThanEqual"] = std::make_tuple(kSPIRV_IntrinsicKind,
+ SpvOpSGreaterThanEqual,
+ SpvOpUGreaterThanEqual,
+ SpvOpFOrdGreaterThanEqual,
+ SpvOpUndef);
+
+// interpolateAt* not yet supported...
+}
+
+void SPIRVCodeGenerator::writeWord(int32_t word, std::ostream& out) {
+#if SPIRV_DEBUG
+ out << "(" << word << ") ";
+#else
+ out.write((const char*) &word, sizeof(word));
+#endif
+}
+
+static bool is_float(const Type& type) {
+ if (type.kind() == Type::kVector_Kind) {
+ return is_float(*type.componentType());
+ }
+ return type == *kFloat_Type || type == *kDouble_Type;
+}
+
+static bool is_signed(const Type& type) {
+ if (type.kind() == Type::kVector_Kind) {
+ return is_signed(*type.componentType());
+ }
+ return type == *kInt_Type;
+}
+
+static bool is_unsigned(const Type& type) {
+ if (type.kind() == Type::kVector_Kind) {
+ return is_unsigned(*type.componentType());
+ }
+ return type == *kUInt_Type;
+}
+
+static bool is_bool(const Type& type) {
+ if (type.kind() == Type::kVector_Kind) {
+ return is_bool(*type.componentType());
+ }
+ return type == *kBool_Type;
+}
+
+static bool is_out(std::shared_ptr<Variable> var) {
+ return (var->fModifiers.fFlags & Modifiers::kOut_Flag) != 0;
+}
+
+#if SPIRV_DEBUG
+static std::string opcode_text(SpvOp_ opCode) {
+ switch (opCode) {
+ case SpvOpNop:
+ return "Nop";
+ case SpvOpUndef:
+ return "Undef";
+ case SpvOpSourceContinued:
+ return "SourceContinued";
+ case SpvOpSource:
+ return "Source";
+ case SpvOpSourceExtension:
+ return "SourceExtension";
+ case SpvOpName:
+ return "Name";
+ case SpvOpMemberName:
+ return "MemberName";
+ case SpvOpString:
+ return "String";
+ case SpvOpLine:
+ return "Line";
+ case SpvOpExtension:
+ return "Extension";
+ case SpvOpExtInstImport:
+ return "ExtInstImport";
+ case SpvOpExtInst:
+ return "ExtInst";
+ case SpvOpMemoryModel:
+ return "MemoryModel";
+ case SpvOpEntryPoint:
+ return "EntryPoint";
+ case SpvOpExecutionMode:
+ return "ExecutionMode";
+ case SpvOpCapability:
+ return "Capability";
+ case SpvOpTypeVoid:
+ return "TypeVoid";
+ case SpvOpTypeBool:
+ return "TypeBool";
+ case SpvOpTypeInt:
+ return "TypeInt";
+ case SpvOpTypeFloat:
+ return "TypeFloat";
+ case SpvOpTypeVector:
+ return "TypeVector";
+ case SpvOpTypeMatrix:
+ return "TypeMatrix";
+ case SpvOpTypeImage:
+ return "TypeImage";
+ case SpvOpTypeSampler:
+ return "TypeSampler";
+ case SpvOpTypeSampledImage:
+ return "TypeSampledImage";
+ case SpvOpTypeArray:
+ return "TypeArray";
+ case SpvOpTypeRuntimeArray:
+ return "TypeRuntimeArray";
+ case SpvOpTypeStruct:
+ return "TypeStruct";
+ case SpvOpTypeOpaque:
+ return "TypeOpaque";
+ case SpvOpTypePointer:
+ return "TypePointer";
+ case SpvOpTypeFunction:
+ return "TypeFunction";
+ case SpvOpTypeEvent:
+ return "TypeEvent";
+ case SpvOpTypeDeviceEvent:
+ return "TypeDeviceEvent";
+ case SpvOpTypeReserveId:
+ return "TypeReserveId";
+ case SpvOpTypeQueue:
+ return "TypeQueue";
+ case SpvOpTypePipe:
+ return "TypePipe";
+ case SpvOpTypeForwardPointer:
+ return "TypeForwardPointer";
+ case SpvOpConstantTrue:
+ return "ConstantTrue";
+ case SpvOpConstantFalse:
+ return "ConstantFalse";
+ case SpvOpConstant:
+ return "Constant";
+ case SpvOpConstantComposite:
+ return "ConstantComposite";
+ case SpvOpConstantSampler:
+ return "ConstantSampler";
+ case SpvOpConstantNull:
+ return "ConstantNull";
+ case SpvOpSpecConstantTrue:
+ return "SpecConstantTrue";
+ case SpvOpSpecConstantFalse:
+ return "SpecConstantFalse";
+ case SpvOpSpecConstant:
+ return "SpecConstant";
+ case SpvOpSpecConstantComposite:
+ return "SpecConstantComposite";
+ case SpvOpSpecConstantOp:
+ return "SpecConstantOp";
+ case SpvOpFunction:
+ return "Function";
+ case SpvOpFunctionParameter:
+ return "FunctionParameter";
+ case SpvOpFunctionEnd:
+ return "FunctionEnd";
+ case SpvOpFunctionCall:
+ return "FunctionCall";
+ case SpvOpVariable:
+ return "Variable";
+ case SpvOpImageTexelPointer:
+ return "ImageTexelPointer";
+ case SpvOpLoad:
+ return "Load";
+ case SpvOpStore:
+ return "Store";
+ case SpvOpCopyMemory:
+ return "CopyMemory";
+ case SpvOpCopyMemorySized:
+ return "CopyMemorySized";
+ case SpvOpAccessChain:
+ return "AccessChain";
+ case SpvOpInBoundsAccessChain:
+ return "InBoundsAccessChain";
+ case SpvOpPtrAccessChain:
+ return "PtrAccessChain";
+ case SpvOpArrayLength:
+ return "ArrayLength";
+ case SpvOpGenericPtrMemSemantics:
+ return "GenericPtrMemSemantics";
+ case SpvOpInBoundsPtrAccessChain:
+ return "InBoundsPtrAccessChain";
+ case SpvOpDecorate:
+ return "Decorate";
+ case SpvOpMemberDecorate:
+ return "MemberDecorate";
+ case SpvOpDecorationGroup:
+ return "DecorationGroup";
+ case SpvOpGroupDecorate:
+ return "GroupDecorate";
+ case SpvOpGroupMemberDecorate:
+ return "GroupMemberDecorate";
+ case SpvOpVectorExtractDynamic:
+ return "VectorExtractDynamic";
+ case SpvOpVectorInsertDynamic:
+ return "VectorInsertDynamic";
+ case SpvOpVectorShuffle:
+ return "VectorShuffle";
+ case SpvOpCompositeConstruct:
+ return "CompositeConstruct";
+ case SpvOpCompositeExtract:
+ return "CompositeExtract";
+ case SpvOpCompositeInsert:
+ return "CompositeInsert";
+ case SpvOpCopyObject:
+ return "CopyObject";
+ case SpvOpTranspose:
+ return "Transpose";
+ case SpvOpSampledImage:
+ return "SampledImage";
+ case SpvOpImageSampleImplicitLod:
+ return "ImageSampleImplicitLod";
+ case SpvOpImageSampleExplicitLod:
+ return "ImageSampleExplicitLod";
+ case SpvOpImageSampleDrefImplicitLod:
+ return "ImageSampleDrefImplicitLod";
+ case SpvOpImageSampleDrefExplicitLod:
+ return "ImageSampleDrefExplicitLod";
+ case SpvOpImageSampleProjImplicitLod:
+ return "ImageSampleProjImplicitLod";
+ case SpvOpImageSampleProjExplicitLod:
+ return "ImageSampleProjExplicitLod";
+ case SpvOpImageSampleProjDrefImplicitLod:
+ return "ImageSampleProjDrefImplicitLod";
+ case SpvOpImageSampleProjDrefExplicitLod:
+ return "ImageSampleProjDrefExplicitLod";
+ case SpvOpImageFetch:
+ return "ImageFetch";
+ case SpvOpImageGather:
+ return "ImageGather";
+ case SpvOpImageDrefGather:
+ return "ImageDrefGather";
+ case SpvOpImageRead:
+ return "ImageRead";
+ case SpvOpImageWrite:
+ return "ImageWrite";
+ case SpvOpImage:
+ return "Image";
+ case SpvOpImageQueryFormat:
+ return "ImageQueryFormat";
+ case SpvOpImageQueryOrder:
+ return "ImageQueryOrder";
+ case SpvOpImageQuerySizeLod:
+ return "ImageQuerySizeLod";
+ case SpvOpImageQuerySize:
+ return "ImageQuerySize";
+ case SpvOpImageQueryLod:
+ return "ImageQueryLod";
+ case SpvOpImageQueryLevels:
+ return "ImageQueryLevels";
+ case SpvOpImageQuerySamples:
+ return "ImageQuerySamples";
+ case SpvOpConvertFToU:
+ return "ConvertFToU";
+ case SpvOpConvertFToS:
+ return "ConvertFToS";
+ case SpvOpConvertSToF:
+ return "ConvertSToF";
+ case SpvOpConvertUToF:
+ return "ConvertUToF";
+ case SpvOpUConvert:
+ return "UConvert";
+ case SpvOpSConvert:
+ return "SConvert";
+ case SpvOpFConvert:
+ return "FConvert";
+ case SpvOpQuantizeToF16:
+ return "QuantizeToF16";
+ case SpvOpConvertPtrToU:
+ return "ConvertPtrToU";
+ case SpvOpSatConvertSToU:
+ return "SatConvertSToU";
+ case SpvOpSatConvertUToS:
+ return "SatConvertUToS";
+ case SpvOpConvertUToPtr:
+ return "ConvertUToPtr";
+ case SpvOpPtrCastToGeneric:
+ return "PtrCastToGeneric";
+ case SpvOpGenericCastToPtr:
+ return "GenericCastToPtr";
+ case SpvOpGenericCastToPtrExplicit:
+ return "GenericCastToPtrExplicit";
+ case SpvOpBitcast:
+ return "Bitcast";
+ case SpvOpSNegate:
+ return "SNegate";
+ case SpvOpFNegate:
+ return "FNegate";
+ case SpvOpIAdd:
+ return "IAdd";
+ case SpvOpFAdd:
+ return "FAdd";
+ case SpvOpISub:
+ return "ISub";
+ case SpvOpFSub:
+ return "FSub";
+ case SpvOpIMul:
+ return "IMul";
+ case SpvOpFMul:
+ return "FMul";
+ case SpvOpUDiv:
+ return "UDiv";
+ case SpvOpSDiv:
+ return "SDiv";
+ case SpvOpFDiv:
+ return "FDiv";
+ case SpvOpUMod:
+ return "UMod";
+ case SpvOpSRem:
+ return "SRem";
+ case SpvOpSMod:
+ return "SMod";
+ case SpvOpFRem:
+ return "FRem";
+ case SpvOpFMod:
+ return "FMod";
+ case SpvOpVectorTimesScalar:
+ return "VectorTimesScalar";
+ case SpvOpMatrixTimesScalar:
+ return "MatrixTimesScalar";
+ case SpvOpVectorTimesMatrix:
+ return "VectorTimesMatrix";
+ case SpvOpMatrixTimesVector:
+ return "MatrixTimesVector";
+ case SpvOpMatrixTimesMatrix:
+ return "MatrixTimesMatrix";
+ case SpvOpOuterProduct:
+ return "OuterProduct";
+ case SpvOpDot:
+ return "Dot";
+ case SpvOpIAddCarry:
+ return "IAddCarry";
+ case SpvOpISubBorrow:
+ return "ISubBorrow";
+ case SpvOpUMulExtended:
+ return "UMulExtended";
+ case SpvOpSMulExtended:
+ return "SMulExtended";
+ case SpvOpAny:
+ return "Any";
+ case SpvOpAll:
+ return "All";
+ case SpvOpIsNan:
+ return "IsNan";
+ case SpvOpIsInf:
+ return "IsInf";
+ case SpvOpIsFinite:
+ return "IsFinite";
+ case SpvOpIsNormal:
+ return "IsNormal";
+ case SpvOpSignBitSet:
+ return "SignBitSet";
+ case SpvOpLessOrGreater:
+ return "LessOrGreater";
+ case SpvOpOrdered:
+ return "Ordered";
+ case SpvOpUnordered:
+ return "Unordered";
+ case SpvOpLogicalEqual:
+ return "LogicalEqual";
+ case SpvOpLogicalNotEqual:
+ return "LogicalNotEqual";
+ case SpvOpLogicalOr:
+ return "LogicalOr";
+ case SpvOpLogicalAnd:
+ return "LogicalAnd";
+ case SpvOpLogicalNot:
+ return "LogicalNot";
+ case SpvOpSelect:
+ return "Select";
+ case SpvOpIEqual:
+ return "IEqual";
+ case SpvOpINotEqual:
+ return "INotEqual";
+ case SpvOpUGreaterThan:
+ return "UGreaterThan";
+ case SpvOpSGreaterThan:
+ return "SGreaterThan";
+ case SpvOpUGreaterThanEqual:
+ return "UGreaterThanEqual";
+ case SpvOpSGreaterThanEqual:
+ return "SGreaterThanEqual";
+ case SpvOpULessThan:
+ return "ULessThan";
+ case SpvOpSLessThan:
+ return "SLessThan";
+ case SpvOpULessThanEqual:
+ return "ULessThanEqual";
+ case SpvOpSLessThanEqual:
+ return "SLessThanEqual";
+ case SpvOpFOrdEqual:
+ return "FOrdEqual";
+ case SpvOpFUnordEqual:
+ return "FUnordEqual";
+ case SpvOpFOrdNotEqual:
+ return "FOrdNotEqual";
+ case SpvOpFUnordNotEqual:
+ return "FUnordNotEqual";
+ case SpvOpFOrdLessThan:
+ return "FOrdLessThan";
+ case SpvOpFUnordLessThan:
+ return "FUnordLessThan";
+ case SpvOpFOrdGreaterThan:
+ return "FOrdGreaterThan";
+ case SpvOpFUnordGreaterThan:
+ return "FUnordGreaterThan";
+ case SpvOpFOrdLessThanEqual:
+ return "FOrdLessThanEqual";
+ case SpvOpFUnordLessThanEqual:
+ return "FUnordLessThanEqual";
+ case SpvOpFOrdGreaterThanEqual:
+ return "FOrdGreaterThanEqual";
+ case SpvOpFUnordGreaterThanEqual:
+ return "FUnordGreaterThanEqual";
+ case SpvOpShiftRightLogical:
+ return "ShiftRightLogical";
+ case SpvOpShiftRightArithmetic:
+ return "ShiftRightArithmetic";
+ case SpvOpShiftLeftLogical:
+ return "ShiftLeftLogical";
+ case SpvOpBitwiseOr:
+ return "BitwiseOr";
+ case SpvOpBitwiseXor:
+ return "BitwiseXor";
+ case SpvOpBitwiseAnd:
+ return "BitwiseAnd";
+ case SpvOpNot:
+ return "Not";
+ case SpvOpBitFieldInsert:
+ return "BitFieldInsert";
+ case SpvOpBitFieldSExtract:
+ return "BitFieldSExtract";
+ case SpvOpBitFieldUExtract:
+ return "BitFieldUExtract";
+ case SpvOpBitReverse:
+ return "BitReverse";
+ case SpvOpBitCount:
+ return "BitCount";
+ case SpvOpDPdx:
+ return "DPdx";
+ case SpvOpDPdy:
+ return "DPdy";
+ case SpvOpFwidth:
+ return "Fwidth";
+ case SpvOpDPdxFine:
+ return "DPdxFine";
+ case SpvOpDPdyFine:
+ return "DPdyFine";
+ case SpvOpFwidthFine:
+ return "FwidthFine";
+ case SpvOpDPdxCoarse:
+ return "DPdxCoarse";
+ case SpvOpDPdyCoarse:
+ return "DPdyCoarse";
+ case SpvOpFwidthCoarse:
+ return "FwidthCoarse";
+ case SpvOpEmitVertex:
+ return "EmitVertex";
+ case SpvOpEndPrimitive:
+ return "EndPrimitive";
+ case SpvOpEmitStreamVertex:
+ return "EmitStreamVertex";
+ case SpvOpEndStreamPrimitive:
+ return "EndStreamPrimitive";
+ case SpvOpControlBarrier:
+ return "ControlBarrier";
+ case SpvOpMemoryBarrier:
+ return "MemoryBarrier";
+ case SpvOpAtomicLoad:
+ return "AtomicLoad";
+ case SpvOpAtomicStore:
+ return "AtomicStore";
+ case SpvOpAtomicExchange:
+ return "AtomicExchange";
+ case SpvOpAtomicCompareExchange:
+ return "AtomicCompareExchange";
+ case SpvOpAtomicCompareExchangeWeak:
+ return "AtomicCompareExchangeWeak";
+ case SpvOpAtomicIIncrement:
+ return "AtomicIIncrement";
+ case SpvOpAtomicIDecrement:
+ return "AtomicIDecrement";
+ case SpvOpAtomicIAdd:
+ return "AtomicIAdd";
+ case SpvOpAtomicISub:
+ return "AtomicISub";
+ case SpvOpAtomicSMin:
+ return "AtomicSMin";
+ case SpvOpAtomicUMin:
+ return "AtomicUMin";
+ case SpvOpAtomicSMax:
+ return "AtomicSMax";
+ case SpvOpAtomicUMax:
+ return "AtomicUMax";
+ case SpvOpAtomicAnd:
+ return "AtomicAnd";
+ case SpvOpAtomicOr:
+ return "AtomicOr";
+ case SpvOpAtomicXor:
+ return "AtomicXor";
+ case SpvOpPhi:
+ return "Phi";
+ case SpvOpLoopMerge:
+ return "LoopMerge";
+ case SpvOpSelectionMerge:
+ return "SelectionMerge";
+ case SpvOpLabel:
+ return "Label";
+ case SpvOpBranch:
+ return "Branch";
+ case SpvOpBranchConditional:
+ return "BranchConditional";
+ case SpvOpSwitch:
+ return "Switch";
+ case SpvOpKill:
+ return "Kill";
+ case SpvOpReturn:
+ return "Return";
+ case SpvOpReturnValue:
+ return "ReturnValue";
+ case SpvOpUnreachable:
+ return "Unreachable";
+ case SpvOpLifetimeStart:
+ return "LifetimeStart";
+ case SpvOpLifetimeStop:
+ return "LifetimeStop";
+ case SpvOpGroupAsyncCopy:
+ return "GroupAsyncCopy";
+ case SpvOpGroupWaitEvents:
+ return "GroupWaitEvents";
+ case SpvOpGroupAll:
+ return "GroupAll";
+ case SpvOpGroupAny:
+ return "GroupAny";
+ case SpvOpGroupBroadcast:
+ return "GroupBroadcast";
+ case SpvOpGroupIAdd:
+ return "GroupIAdd";
+ case SpvOpGroupFAdd:
+ return "GroupFAdd";
+ case SpvOpGroupFMin:
+ return "GroupFMin";
+ case SpvOpGroupUMin:
+ return "GroupUMin";
+ case SpvOpGroupSMin:
+ return "GroupSMin";
+ case SpvOpGroupFMax:
+ return "GroupFMax";
+ case SpvOpGroupUMax:
+ return "GroupUMax";
+ case SpvOpGroupSMax:
+ return "GroupSMax";
+ case SpvOpReadPipe:
+ return "ReadPipe";
+ case SpvOpWritePipe:
+ return "WritePipe";
+ case SpvOpReservedReadPipe:
+ return "ReservedReadPipe";
+ case SpvOpReservedWritePipe:
+ return "ReservedWritePipe";
+ case SpvOpReserveReadPipePackets:
+ return "ReserveReadPipePackets";
+ case SpvOpReserveWritePipePackets:
+ return "ReserveWritePipePackets";
+ case SpvOpCommitReadPipe:
+ return "CommitReadPipe";
+ case SpvOpCommitWritePipe:
+ return "CommitWritePipe";
+ case SpvOpIsValidReserveId:
+ return "IsValidReserveId";
+ case SpvOpGetNumPipePackets:
+ return "GetNumPipePackets";
+ case SpvOpGetMaxPipePackets:
+ return "GetMaxPipePackets";
+ case SpvOpGroupReserveReadPipePackets:
+ return "GroupReserveReadPipePackets";
+ case SpvOpGroupReserveWritePipePackets:
+ return "GroupReserveWritePipePackets";
+ case SpvOpGroupCommitReadPipe:
+ return "GroupCommitReadPipe";
+ case SpvOpGroupCommitWritePipe:
+ return "GroupCommitWritePipe";
+ case SpvOpEnqueueMarker:
+ return "EnqueueMarker";
+ case SpvOpEnqueueKernel:
+ return "EnqueueKernel";
+ case SpvOpGetKernelNDrangeSubGroupCount:
+ return "GetKernelNDrangeSubGroupCount";
+ case SpvOpGetKernelNDrangeMaxSubGroupSize:
+ return "GetKernelNDrangeMaxSubGroupSize";
+ case SpvOpGetKernelWorkGroupSize:
+ return "GetKernelWorkGroupSize";
+ case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
+ return "GetKernelPreferredWorkGroupSizeMultiple";
+ case SpvOpRetainEvent:
+ return "RetainEvent";
+ case SpvOpReleaseEvent:
+ return "ReleaseEvent";
+ case SpvOpCreateUserEvent:
+ return "CreateUserEvent";
+ case SpvOpIsValidEvent:
+ return "IsValidEvent";
+ case SpvOpSetUserEventStatus:
+ return "SetUserEventStatus";
+ case SpvOpCaptureEventProfilingInfo:
+ return "CaptureEventProfilingInfo";
+ case SpvOpGetDefaultQueue:
+ return "GetDefaultQueue";
+ case SpvOpBuildNDRange:
+ return "BuildNDRange";
+ case SpvOpImageSparseSampleImplicitLod:
+ return "ImageSparseSampleImplicitLod";
+ case SpvOpImageSparseSampleExplicitLod:
+ return "ImageSparseSampleExplicitLod";
+ case SpvOpImageSparseSampleDrefImplicitLod:
+ return "ImageSparseSampleDrefImplicitLod";
+ case SpvOpImageSparseSampleDrefExplicitLod:
+ return "ImageSparseSampleDrefExplicitLod";
+ case SpvOpImageSparseSampleProjImplicitLod:
+ return "ImageSparseSampleProjImplicitLod";
+ case SpvOpImageSparseSampleProjExplicitLod:
+ return "ImageSparseSampleProjExplicitLod";
+ case SpvOpImageSparseSampleProjDrefImplicitLod:
+ return "ImageSparseSampleProjDrefImplicitLod";
+ case SpvOpImageSparseSampleProjDrefExplicitLod:
+ return "ImageSparseSampleProjDrefExplicitLod";
+ case SpvOpImageSparseFetch:
+ return "ImageSparseFetch";
+ case SpvOpImageSparseGather:
+ return "ImageSparseGather";
+ case SpvOpImageSparseDrefGather:
+ return "ImageSparseDrefGather";
+ case SpvOpImageSparseTexelsResident:
+ return "ImageSparseTexelsResident";
+ case SpvOpNoLine:
+ return "NoLine";
+ case SpvOpAtomicFlagTestAndSet:
+ return "AtomicFlagTestAndSet";
+ case SpvOpAtomicFlagClear:
+ return "AtomicFlagClear";
+ case SpvOpImageSparseRead:
+ return "ImageSparseRead";
+ default:
+ ABORT("unsupported SPIR-V op");
+ }
+}
+#endif
+
+void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, std::ostream& out) {
+ ASSERT(opCode != SpvOpUndef);
+ switch (opCode) {
+ case SpvOpReturn: // fall through
+ case SpvOpReturnValue: // fall through
+ case SpvOpBranch: // fall through
+ case SpvOpBranchConditional:
+ ASSERT(fCurrentBlock);
+ fCurrentBlock = 0;
+ break;
+ case SpvOpConstant: // fall through
+ case SpvOpConstantTrue: // fall through
+ case SpvOpConstantFalse: // fall through
+ case SpvOpConstantComposite: // fall through
+ case SpvOpTypeVoid: // fall through
+ case SpvOpTypeInt: // fall through
+ case SpvOpTypeFloat: // fall through
+ case SpvOpTypeBool: // fall through
+ case SpvOpTypeVector: // fall through
+ case SpvOpTypeMatrix: // fall through
+ case SpvOpTypeArray: // fall through
+ case SpvOpTypePointer: // fall through
+ case SpvOpTypeFunction: // fall through
+ case SpvOpTypeRuntimeArray: // fall through
+ case SpvOpTypeStruct: // fall through
+ case SpvOpTypeImage: // fall through
+ case SpvOpTypeSampledImage: // fall through
+ case SpvOpVariable: // fall through
+ case SpvOpFunction: // fall through
+ case SpvOpFunctionParameter: // fall through
+ case SpvOpFunctionEnd: // fall through
+ case SpvOpExecutionMode: // fall through
+ case SpvOpMemoryModel: // fall through
+ case SpvOpCapability: // fall through
+ case SpvOpExtInstImport: // fall through
+ case SpvOpEntryPoint: // fall through
+ case SpvOpSource: // fall through
+ case SpvOpSourceExtension: // fall through
+ case SpvOpName: // fall through
+ case SpvOpMemberName: // fall through
+ case SpvOpDecorate: // fall through
+ case SpvOpMemberDecorate:
+ break;
+ default:
+ ASSERT(fCurrentBlock);
+ }
+#if SPIRV_DEBUG
+ out << std::endl << opcode_text(opCode) << " ";
+#else
+ this->writeWord((length << 16) | opCode, out);
+#endif
+}
+
+void SPIRVCodeGenerator::writeLabel(SpvId label, std::ostream& out) {
+ fCurrentBlock = label;
+ this->writeInstruction(SpvOpLabel, label, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, std::ostream& out) {
+ this->writeOpCode(opCode, 1, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, std::ostream& out) {
+ this->writeOpCode(opCode, 2, out);
+ this->writeWord(word1, out);
+}
+
+void SPIRVCodeGenerator::writeString(const char* string, std::ostream& out) {
+ size_t length = strlen(string);
+ out << string;
+ switch (length % 4) {
+ case 1:
+ out << (char) 0;
+ // fall through
+ case 2:
+ out << (char) 0;
+ // fall through
+ case 3:
+ out << (char) 0;
+ break;
+ default:
+ this->writeWord(0, out);
+ }
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, const char* string, std::ostream& out) {
+ int32_t length = (int32_t) strlen(string);
+ this->writeOpCode(opCode, 1 + (length + 4) / 4, out);
+ this->writeString(string, out);
+}
+
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, const char* string,
+ std::ostream& out) {
+ int32_t length = (int32_t) strlen(string);
+ this->writeOpCode(opCode, 2 + (length + 4) / 4, out);
+ this->writeWord(word1, out);
+ this->writeString(string, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ const char* string, std::ostream& out) {
+ int32_t length = (int32_t) strlen(string);
+ this->writeOpCode(opCode, 3 + (length + 4) / 4, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeString(string, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ std::ostream& out) {
+ this->writeOpCode(opCode, 3, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, std::ostream& out) {
+ this->writeOpCode(opCode, 4, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, int32_t word4, std::ostream& out) {
+ this->writeOpCode(opCode, 5, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+ this->writeWord(word4, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, int32_t word4, int32_t word5,
+ std::ostream& out) {
+ this->writeOpCode(opCode, 6, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+ this->writeWord(word4, out);
+ this->writeWord(word5, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, int32_t word4, int32_t word5,
+ int32_t word6, std::ostream& out) {
+ this->writeOpCode(opCode, 7, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+ this->writeWord(word4, out);
+ this->writeWord(word5, out);
+ this->writeWord(word6, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, int32_t word4, int32_t word5,
+ int32_t word6, int32_t word7, std::ostream& out) {
+ this->writeOpCode(opCode, 8, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+ this->writeWord(word4, out);
+ this->writeWord(word5, out);
+ this->writeWord(word6, out);
+ this->writeWord(word7, out);
+}
+
+void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
+ int32_t word3, int32_t word4, int32_t word5,
+ int32_t word6, int32_t word7, int32_t word8,
+ std::ostream& out) {
+ this->writeOpCode(opCode, 9, out);
+ this->writeWord(word1, out);
+ this->writeWord(word2, out);
+ this->writeWord(word3, out);
+ this->writeWord(word4, out);
+ this->writeWord(word5, out);
+ this->writeWord(word6, out);
+ this->writeWord(word7, out);
+ this->writeWord(word8, out);
+}
+
+void SPIRVCodeGenerator::writeCapabilities(std::ostream& out) {
+ for (uint64_t i = 0, bit = 1; i <= kLast_Capability; i++, bit <<= 1) {
+ if (fCapabilities & bit) {
+ this->writeInstruction(SpvOpCapability, (SpvId) i, out);
+ }
+ }
+}
+
+SpvId SPIRVCodeGenerator::nextId() {
+ return fIdCount++;
+}
+
+void SPIRVCodeGenerator::writeStruct(const Type& type, SpvId resultId) {
+ this->writeInstruction(SpvOpName, resultId, type.name().c_str(), fNameBuffer);
+ // go ahead and write all of the field types, so we don't inadvertently write them while we're
+ // in the middle of writing the struct instruction
+ std::vector<SpvId> types;
+ for (const auto& f : type.fields()) {
+ types.push_back(this->getType(*f.fType));
+ }
+ this->writeOpCode(SpvOpTypeStruct, 2 + (int32_t) types.size(), fConstantBuffer);
+ this->writeWord(resultId, fConstantBuffer);
+ for (SpvId id : types) {
+ this->writeWord(id, fConstantBuffer);
+ }
+ size_t offset = 0;
+ for (int32_t i = 0; i < (int32_t) type.fields().size(); i++) {
+ size_t size = type.fields()[i].fType->size();
+ size_t alignment = type.fields()[i].fType->alignment();
+ size_t mod = offset % alignment;
+ if (mod != 0) {
+ offset += alignment - mod;
+ }
+ this->writeInstruction(SpvOpMemberName, resultId, i, type.fields()[i].fName.c_str(),
+ fNameBuffer);
+ this->writeLayout(type.fields()[i].fModifiers.fLayout, resultId, i);
+ if (type.fields()[i].fModifiers.fLayout.fBuiltin < 0) {
+ this->writeInstruction(SpvOpMemberDecorate, resultId, (SpvId) i, SpvDecorationOffset,
+ (SpvId) offset, fDecorationBuffer);
+ }
+ if (type.fields()[i].fType->kind() == Type::kMatrix_Kind) {
+ this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationColMajor,
+ fDecorationBuffer);
+ this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationMatrixStride,
+ (SpvId) type.fields()[i].fType->stride(), fDecorationBuffer);
+ }
+ offset += size;
+ Type::Kind kind = type.fields()[i].fType->kind();
+ if ((kind == Type::kArray_Kind || kind == Type::kStruct_Kind) && offset % alignment != 0) {
+ offset += alignment - offset % alignment;
+ }
+ ASSERT(offset % alignment == 0);
+ }
+}
+
+SpvId SPIRVCodeGenerator::getType(const Type& type) {
+ auto entry = fTypeMap.find(type.name());
+ if (entry == fTypeMap.end()) {
+ SpvId result = this->nextId();
+ switch (type.kind()) {
+ case Type::kScalar_Kind:
+ if (type == *kBool_Type) {
+ this->writeInstruction(SpvOpTypeBool, result, fConstantBuffer);
+ } else if (type == *kInt_Type) {
+ this->writeInstruction(SpvOpTypeInt, result, 32, 1, fConstantBuffer);
+ } else if (type == *kUInt_Type) {
+ this->writeInstruction(SpvOpTypeInt, result, 32, 0, fConstantBuffer);
+ } else if (type == *kFloat_Type) {
+ this->writeInstruction(SpvOpTypeFloat, result, 32, fConstantBuffer);
+ } else if (type == *kDouble_Type) {
+ this->writeInstruction(SpvOpTypeFloat, result, 64, fConstantBuffer);
+ } else {
+ ASSERT(false);
+ }
+ break;
+ case Type::kVector_Kind:
+ this->writeInstruction(SpvOpTypeVector, result,
+ this->getType(*type.componentType()),
+ type.columns(), fConstantBuffer);
+ break;
+ case Type::kMatrix_Kind:
+ this->writeInstruction(SpvOpTypeMatrix, result, this->getType(*index_type(type)),
+ type.columns(), fConstantBuffer);
+ break;
+ case Type::kStruct_Kind:
+ this->writeStruct(type, result);
+ break;
+ case Type::kArray_Kind: {
+ if (type.columns() > 0) {
+ IntLiteral count(Position(), type.columns());
+ this->writeInstruction(SpvOpTypeArray, result,
+ this->getType(*type.componentType()),
+ this->writeIntLiteral(count), fConstantBuffer);
+ this->writeInstruction(SpvOpDecorate, result, SpvDecorationArrayStride,
+ (int32_t) type.stride(), fDecorationBuffer);
+ } else {
+ ABORT("runtime-sized arrays are not yet supported");
+ this->writeInstruction(SpvOpTypeRuntimeArray, result,
+ this->getType(*type.componentType()), fConstantBuffer);
+ }
+ break;
+ }
+ case Type::kSampler_Kind: {
+ SpvId image = this->nextId();
+ this->writeInstruction(SpvOpTypeImage, image, this->getType(*kFloat_Type),
+ type.dimensions(), type.isDepth(), type.isArrayed(),
+ type.isMultisampled(), type.isSampled(),
+ SpvImageFormatUnknown, fConstantBuffer);
+ this->writeInstruction(SpvOpTypeSampledImage, result, image, fConstantBuffer);
+ break;
+ }
+ default:
+ if (type == *kVoid_Type) {
+ this->writeInstruction(SpvOpTypeVoid, result, fConstantBuffer);
+ } else {
+ ABORT("invalid type: %s", type.description().c_str());
+ }
+ }
+ fTypeMap[type.name()] = result;
+ return result;
+ }
+ return entry->second;
+}
+
+SpvId SPIRVCodeGenerator::getFunctionType(std::shared_ptr<FunctionDeclaration> function) {
+ std::string key = function->fReturnType->description() + "(";
+ std::string separator = "";
+ for (size_t i = 0; i < function->fParameters.size(); i++) {
+ key += separator;
+ separator = ", ";
+ key += function->fParameters[i]->fType->description();
+ }
+ key += ")";
+ auto entry = fTypeMap.find(key);
+ if (entry == fTypeMap.end()) {
+ SpvId result = this->nextId();
+ int32_t length = 3 + (int32_t) function->fParameters.size();
+ SpvId returnType = this->getType(*function->fReturnType);
+ std::vector<SpvId> parameterTypes;
+ for (size_t i = 0; i < function->fParameters.size(); i++) {
+ // glslang seems to treat all function arguments as pointers whether they need to be or
+ // not. I was initially puzzled by this until I ran bizarre failures with certain
+ // patterns of function calls and control constructs, as exemplified by this minimal
+ // failure case:
+ //
+ // void sphere(float x) {
+ // }
+ //
+ // void map() {
+ // sphere(1.0);
+ // }
+ //
+ // void main() {
+ // for (int i = 0; i < 1; i++) {
+ // map();
+ // }
+ // }
+ //
+ // As of this writing, compiling this in the "obvious" way (with sphere taking a float)
+ // crashes. Making it take a float* and storing the argument in a temporary variable,
+ // as glslang does, fixes it. It's entirely possible I simply missed whichever part of
+ // the spec makes this make sense.
+// if (is_out(function->fParameters[i])) {
+ parameterTypes.push_back(this->getPointerType(function->fParameters[i]->fType,
+ SpvStorageClassFunction));
+// } else {
+// parameterTypes.push_back(this->getType(*function->fParameters[i]->fType));
+// }
+ }
+ this->writeOpCode(SpvOpTypeFunction, length, fConstantBuffer);
+ this->writeWord(result, fConstantBuffer);
+ this->writeWord(returnType, fConstantBuffer);
+ for (SpvId id : parameterTypes) {
+ this->writeWord(id, fConstantBuffer);
+ }
+ fTypeMap[key] = result;
+ return result;
+ }
+ return entry->second;
+}
+
+SpvId SPIRVCodeGenerator::getPointerType(std::shared_ptr<Type> type,
+ SpvStorageClass_ storageClass) {
+ std::string key = type->description() + "*" + to_string(storageClass);
+ auto entry = fTypeMap.find(key);
+ if (entry == fTypeMap.end()) {
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpTypePointer, result, storageClass,
+ this->getType(*type), fConstantBuffer);
+ fTypeMap[key] = result;
+ return result;
+ }
+ return entry->second;
+}
+
+SpvId SPIRVCodeGenerator::writeExpression(Expression& expr, std::ostream& out) {
+ switch (expr.fKind) {
+ case Expression::kBinary_Kind:
+ return this->writeBinaryExpression((BinaryExpression&) expr, out);
+ case Expression::kBoolLiteral_Kind:
+ return this->writeBoolLiteral((BoolLiteral&) expr);
+ case Expression::kConstructor_Kind:
+ return this->writeConstructor((Constructor&) expr, out);
+ case Expression::kIntLiteral_Kind:
+ return this->writeIntLiteral((IntLiteral&) expr);
+ case Expression::kFieldAccess_Kind:
+ return this->writeFieldAccess(((FieldAccess&) expr), out);
+ case Expression::kFloatLiteral_Kind:
+ return this->writeFloatLiteral(((FloatLiteral&) expr));
+ case Expression::kFunctionCall_Kind:
+ return this->writeFunctionCall((FunctionCall&) expr, out);
+ case Expression::kPrefix_Kind:
+ return this->writePrefixExpression((PrefixExpression&) expr, out);
+ case Expression::kPostfix_Kind:
+ return this->writePostfixExpression((PostfixExpression&) expr, out);
+ case Expression::kSwizzle_Kind:
+ return this->writeSwizzle((Swizzle&) expr, out);
+ case Expression::kVariableReference_Kind:
+ return this->writeVariableReference((VariableReference&) expr, out);
+ case Expression::kTernary_Kind:
+ return this->writeTernaryExpression((TernaryExpression&) expr, out);
+ case Expression::kIndex_Kind:
+ return this->writeIndexExpression((IndexExpression&) expr, out);
+ default:
+ ABORT("unsupported expression: %s", expr.description().c_str());
+ }
+ return -1;
+}
+
+SpvId SPIRVCodeGenerator::writeIntrinsicCall(FunctionCall& c, std::ostream& out) {
+ auto intrinsic = fIntrinsicMap.find(c.fFunction->fName);
+ ASSERT(intrinsic != fIntrinsicMap.end());
+ std::shared_ptr<Type> type = c.fArguments[0]->fType;
+ int32_t intrinsicId;
+ if (std::get<0>(intrinsic->second) == kSpecial_IntrinsicKind || is_float(*type)) {
+ intrinsicId = std::get<1>(intrinsic->second);
+ } else if (is_signed(*type)) {
+ intrinsicId = std::get<2>(intrinsic->second);
+ } else if (is_unsigned(*type)) {
+ intrinsicId = std::get<3>(intrinsic->second);
+ } else if (is_bool(*type)) {
+ intrinsicId = std::get<4>(intrinsic->second);
+ } else {
+ ABORT("invalid call %s, cannot operate on '%s'", c.description().c_str(),
+ type->description().c_str());
+ }
+ switch (std::get<0>(intrinsic->second)) {
+ case kGLSL_STD_450_IntrinsicKind: {
+ SpvId result = this->nextId();
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], out));
+ }
+ this->writeOpCode(SpvOpExtInst, 5 + (int32_t) arguments.size(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ this->writeWord(fGLSLExtendedInstructions, out);
+ this->writeWord(intrinsicId, out);
+ for (SpvId id : arguments) {
+ this->writeWord(id, out);
+ }
+ return result;
+ }
+ case kSPIRV_IntrinsicKind: {
+ SpvId result = this->nextId();
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], out));
+ }
+ this->writeOpCode((SpvOp_) intrinsicId, 3 + (int32_t) arguments.size(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ for (SpvId id : arguments) {
+ this->writeWord(id, out);
+ }
+ return result;
+ }
+ case kSpecial_IntrinsicKind:
+ return this->writeSpecialIntrinsic(c, (SpecialIntrinsic) intrinsicId, out);
+ default:
+ ABORT("unsupported intrinsic kind");
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(FunctionCall& c, SpecialIntrinsic kind,
+ std::ostream& out) {
+ SpvId result = this->nextId();
+ switch (kind) {
+ case kAtan_SpecialIntrinsic: {
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], out));
+ }
+ this->writeOpCode(SpvOpExtInst, 5 + (int32_t) arguments.size(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ this->writeWord(fGLSLExtendedInstructions, out);
+ this->writeWord(arguments.size() == 2 ? GLSLstd450Atan2 : GLSLstd450Atan, out);
+ for (SpvId id : arguments) {
+ this->writeWord(id, out);
+ }
+ return result;
+ }
+ case kTexture_SpecialIntrinsic: {
+ SpvId type = this->getType(*c.fType);
+ SpvId sampler = this->writeExpression(*c.fArguments[0], out);
+ SpvId uv = this->writeExpression(*c.fArguments[1], out);
+ if (c.fArguments.size() == 3) {
+ this->writeInstruction(SpvOpImageSampleImplicitLod, type, result, sampler, uv,
+ SpvImageOperandsBiasMask,
+ this->writeExpression(*c.fArguments[2], out),
+ out);
+ } else {
+ ASSERT(c.fArguments.size() == 2);
+ this->writeInstruction(SpvOpImageSampleImplicitLod, type, result, sampler, uv, out);
+ }
+ break;
+ }
+ case kTextureProj_SpecialIntrinsic: {
+ SpvId type = this->getType(*c.fType);
+ SpvId sampler = this->writeExpression(*c.fArguments[0], out);
+ SpvId uv = this->writeExpression(*c.fArguments[1], out);
+ if (c.fArguments.size() == 3) {
+ this->writeInstruction(SpvOpImageSampleProjImplicitLod, type, result, sampler, uv,
+ SpvImageOperandsBiasMask,
+ this->writeExpression(*c.fArguments[2], out),
+ out);
+ } else {
+ ASSERT(c.fArguments.size() == 2);
+ this->writeInstruction(SpvOpImageSampleProjImplicitLod, type, result, sampler, uv,
+ out);
+ }
+ break;
+ }
+ case kTexture2D_SpecialIntrinsic: {
+ SpvId img = this->writeExpression(*c.fArguments[0], out);
+ SpvId coords = this->writeExpression(*c.fArguments[1], out);
+ this->writeInstruction(SpvOpImageSampleImplicitLod,
+ this->getType(*c.fType),
+ result,
+ img,
+ coords,
+ out);
+ break;
+ }
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeFunctionCall(FunctionCall& c, std::ostream& out) {
+ const auto& entry = fFunctionMap.find(c.fFunction);
+ if (entry == fFunctionMap.end()) {
+ return this->writeIntrinsicCall(c, out);
+ }
+ // stores (variable, type, lvalue) pairs to extract and save after the function call is complete
+ std::vector<std::tuple<SpvId, SpvId, std::unique_ptr<LValue>>> lvalues;
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ // id of temporary variable that we will use to hold this argument, or 0 if it is being
+ // passed directly
+ SpvId tmpVar;
+ // if we need a temporary var to store this argument, this is the value to store in the var
+ SpvId tmpValueId;
+ if (is_out(c.fFunction->fParameters[i])) {
+ std::unique_ptr<LValue> lv = this->getLValue(*c.fArguments[i], out);
+ SpvId ptr = lv->getPointer();
+ if (ptr) {
+ arguments.push_back(ptr);
+ continue;
+ } else {
+ // lvalue cannot simply be read and written via a pointer (e.g. a swizzle). Need to
+ // copy it into a temp, call the function, read the value out of the temp, and then
+ // update the lvalue.
+ tmpValueId = lv->load(out);
+ tmpVar = this->nextId();
+ lvalues.push_back(std::make_tuple(tmpVar, this->getType(*c.fArguments[i]->fType),
+ std::move(lv)));
+ }
+ } else {
+ // see getFunctionType for an explanation of why we're always using pointer parameters
+ tmpValueId = this->writeExpression(*c.fArguments[i], out);
+ tmpVar = this->nextId();
+ }
+ this->writeInstruction(SpvOpVariable,
+ this->getPointerType(c.fArguments[i]->fType,
+ SpvStorageClassFunction),
+ tmpVar,
+ SpvStorageClassFunction,
+ out);
+ this->writeInstruction(SpvOpStore, tmpVar, tmpValueId, out);
+ arguments.push_back(tmpVar);
+ }
+ SpvId result = this->nextId();
+ this->writeOpCode(SpvOpFunctionCall, 4 + (int32_t) c.fArguments.size(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ this->writeWord(entry->second, out);
+ for (SpvId id : arguments) {
+ this->writeWord(id, out);
+ }
+ // now that the call is complete, we may need to update some lvalues with the new values of out
+ // arguments
+ for (const auto& tuple : lvalues) {
+ SpvId load = this->nextId();
+ this->writeInstruction(SpvOpLoad, std::get<1>(tuple), load, std::get<0>(tuple), out);
+ std::get<2>(tuple)->store(load, out);
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeConstantVector(Constructor& c) {
+ ASSERT(c.fType->kind() == Type::kVector_Kind && c.isConstant());
+ SpvId result = this->nextId();
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], fConstantBuffer));
+ }
+ SpvId type = this->getType(*c.fType);
+ if (c.fArguments.size() == 1) {
+ // with a single argument, a vector will have all of its entries equal to the argument
+ this->writeOpCode(SpvOpConstantComposite, 3 + c.fType->columns(), fConstantBuffer);
+ this->writeWord(type, fConstantBuffer);
+ this->writeWord(result, fConstantBuffer);
+ for (int i = 0; i < c.fType->columns(); i++) {
+ this->writeWord(arguments[0], fConstantBuffer);
+ }
+ } else {
+ this->writeOpCode(SpvOpConstantComposite, 3 + (int32_t) c.fArguments.size(),
+ fConstantBuffer);
+ this->writeWord(type, fConstantBuffer);
+ this->writeWord(result, fConstantBuffer);
+ for (SpvId id : arguments) {
+ this->writeWord(id, fConstantBuffer);
+ }
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeFloatConstructor(Constructor& c, std::ostream& out) {
+ ASSERT(c.fType == kFloat_Type);
+ ASSERT(c.fArguments.size() == 1);
+ ASSERT(c.fArguments[0]->fType->isNumber());
+ SpvId result = this->nextId();
+ SpvId parameter = this->writeExpression(*c.fArguments[0], out);
+ if (c.fArguments[0]->fType == kInt_Type) {
+ this->writeInstruction(SpvOpConvertSToF, this->getType(*c.fType), result, parameter,
+ out);
+ } else if (c.fArguments[0]->fType == kUInt_Type) {
+ this->writeInstruction(SpvOpConvertUToF, this->getType(*c.fType), result, parameter,
+ out);
+ } else if (c.fArguments[0]->fType == kFloat_Type) {
+ return parameter;
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeIntConstructor(Constructor& c, std::ostream& out) {
+ ASSERT(c.fType == kInt_Type);
+ ASSERT(c.fArguments.size() == 1);
+ ASSERT(c.fArguments[0]->fType->isNumber());
+ SpvId result = this->nextId();
+ SpvId parameter = this->writeExpression(*c.fArguments[0], out);
+ if (c.fArguments[0]->fType == kFloat_Type) {
+ this->writeInstruction(SpvOpConvertFToS, this->getType(*c.fType), result, parameter,
+ out);
+ } else if (c.fArguments[0]->fType == kUInt_Type) {
+ this->writeInstruction(SpvOpSatConvertUToS, this->getType(*c.fType), result, parameter,
+ out);
+ } else if (c.fArguments[0]->fType == kInt_Type) {
+ return parameter;
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeMatrixConstructor(Constructor& c, std::ostream& out) {
+ ASSERT(c.fType->kind() == Type::kMatrix_Kind);
+ // go ahead and write the arguments so we don't try to write new instructions in the middle of
+ // an instruction
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], out));
+ }
+ SpvId result = this->nextId();
+ int rows = c.fType->rows();
+ int columns = c.fType->columns();
+ // FIXME this won't work to create a matrix from another matrix
+ if (arguments.size() == 1) {
+ // with a single argument, a matrix will have all of its diagonal entries equal to the
+ // argument and its other values equal to zero
+ // FIXME this won't work for int matrices
+ FloatLiteral zero(Position(), 0);
+ SpvId zeroId = this->writeFloatLiteral(zero);
+ std::vector<SpvId> columnIds;
+ for (int column = 0; column < columns; column++) {
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType->rows(),
+ out);
+ this->writeWord(this->getType(*c.fType->componentType()->toCompound(rows, 1)), out);
+ SpvId columnId = this->nextId();
+ this->writeWord(columnId, out);
+ columnIds.push_back(columnId);
+ for (int row = 0; row < c.fType->columns(); row++) {
+ this->writeWord(row == column ? arguments[0] : zeroId, out);
+ }
+ }
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + columns,
+ out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ for (SpvId id : columnIds) {
+ this->writeWord(id, out);
+ }
+ } else {
+ std::vector<SpvId> columnIds;
+ int currentCount = 0;
+ for (size_t i = 0; i < arguments.size(); i++) {
+ if (c.fArguments[i]->fType->kind() == Type::kVector_Kind) {
+ ASSERT(currentCount == 0);
+ columnIds.push_back(arguments[i]);
+ currentCount = 0;
+ } else {
+ ASSERT(c.fArguments[i]->fType->kind() == Type::kScalar_Kind);
+ if (currentCount == 0) {
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType->rows(), out);
+ this->writeWord(this->getType(*c.fType->componentType()->toCompound(rows, 1)),
+ out);
+ SpvId id = this->nextId();
+ this->writeWord(id, out);
+ columnIds.push_back(id);
+ }
+ this->writeWord(arguments[i], out);
+ currentCount = (currentCount + 1) % rows;
+ }
+ }
+ ASSERT(columnIds.size() == (size_t) columns);
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + columns, out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ for (SpvId id : columnIds) {
+ this->writeWord(id, out);
+ }
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeVectorConstructor(Constructor& c, std::ostream& out) {
+ ASSERT(c.fType->kind() == Type::kVector_Kind);
+ if (c.isConstant()) {
+ return this->writeConstantVector(c);
+ }
+ // go ahead and write the arguments so we don't try to write new instructions in the middle of
+ // an instruction
+ std::vector<SpvId> arguments;
+ for (size_t i = 0; i < c.fArguments.size(); i++) {
+ arguments.push_back(this->writeExpression(*c.fArguments[i], out));
+ }
+ SpvId result = this->nextId();
+ if (arguments.size() == 1 && c.fArguments[0]->fType->kind() == Type::kScalar_Kind) {
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType->columns(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ for (int i = 0; i < c.fType->columns(); i++) {
+ this->writeWord(arguments[0], out);
+ }
+ } else {
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + (int32_t) c.fArguments.size(), out);
+ this->writeWord(this->getType(*c.fType), out);
+ this->writeWord(result, out);
+ for (SpvId id : arguments) {
+ this->writeWord(id, out);
+ }
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeConstructor(Constructor& c, std::ostream& out) {
+ if (c.fType == kFloat_Type) {
+ return this->writeFloatConstructor(c, out);
+ } else if (c.fType == kInt_Type) {
+ return this->writeIntConstructor(c, out);
+ }
+ switch (c.fType->kind()) {
+ case Type::kVector_Kind:
+ return this->writeVectorConstructor(c, out);
+ case Type::kMatrix_Kind:
+ return this->writeMatrixConstructor(c, out);
+ default:
+ ABORT("unsupported constructor: %s", c.description().c_str());
+ }
+}
+
+SpvStorageClass_ get_storage_class(const Modifiers& modifiers) {
+ if (modifiers.fFlags & Modifiers::kIn_Flag) {
+ return SpvStorageClassInput;
+ } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
+ return SpvStorageClassOutput;
+ } else if (modifiers.fFlags & Modifiers::kUniform_Flag) {
+ return SpvStorageClassUniform;
+ } else {
+ return SpvStorageClassFunction;
+ }
+}
+
+SpvStorageClass_ get_storage_class(Expression& expr) {
+ switch (expr.fKind) {
+ case Expression::kVariableReference_Kind:
+ return get_storage_class(((VariableReference&) expr).fVariable->fModifiers);
+ case Expression::kFieldAccess_Kind:
+ return get_storage_class(*((FieldAccess&) expr).fBase);
+ case Expression::kIndex_Kind:
+ return get_storage_class(*((IndexExpression&) expr).fBase);
+ default:
+ return SpvStorageClassFunction;
+ }
+}
+
+std::vector<SpvId> SPIRVCodeGenerator::getAccessChain(Expression& expr, std::ostream& out) {
+ std::vector<SpvId> chain;
+ switch (expr.fKind) {
+ case Expression::kIndex_Kind: {
+ IndexExpression& indexExpr = (IndexExpression&) expr;
+ chain = this->getAccessChain(*indexExpr.fBase, out);
+ chain.push_back(this->writeExpression(*indexExpr.fIndex, out));
+ break;
+ }
+ case Expression::kFieldAccess_Kind: {
+ FieldAccess& fieldExpr = (FieldAccess&) expr;
+ chain = this->getAccessChain(*fieldExpr.fBase, out);
+ IntLiteral index(Position(), fieldExpr.fFieldIndex);
+ chain.push_back(this->writeIntLiteral(index));
+ break;
+ }
+ default:
+ chain.push_back(this->getLValue(expr, out)->getPointer());
+ }
+ return chain;
+}
+
+class PointerLValue : public SPIRVCodeGenerator::LValue {
+public:
+ PointerLValue(SPIRVCodeGenerator& gen, SpvId pointer, SpvId type)
+ : fGen(gen)
+ , fPointer(pointer)
+ , fType(type) {}
+
+ virtual SpvId getPointer() override {
+ return fPointer;
+ }
+
+ virtual SpvId load(std::ostream& out) override {
+ SpvId result = fGen.nextId();
+ fGen.writeInstruction(SpvOpLoad, fType, result, fPointer, out);
+ return result;
+ }
+
+ virtual void store(SpvId value, std::ostream& out) override {
+ fGen.writeInstruction(SpvOpStore, fPointer, value, out);
+ }
+
+private:
+ SPIRVCodeGenerator& fGen;
+ const SpvId fPointer;
+ const SpvId fType;
+};
+
+class SwizzleLValue : public SPIRVCodeGenerator::LValue {
+public:
+ SwizzleLValue(SPIRVCodeGenerator& gen, SpvId vecPointer, const std::vector<int>& components,
+ const Type& baseType, const Type& swizzleType)
+ : fGen(gen)
+ , fVecPointer(vecPointer)
+ , fComponents(components)
+ , fBaseType(baseType)
+ , fSwizzleType(swizzleType) {}
+
+ virtual SpvId getPointer() override {
+ return 0;
+ }
+
+ virtual SpvId load(std::ostream& out) override {
+ SpvId base = fGen.nextId();
+ fGen.writeInstruction(SpvOpLoad, fGen.getType(fBaseType), base, fVecPointer, out);
+ SpvId result = fGen.nextId();
+ fGen.writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) fComponents.size(), out);
+ fGen.writeWord(fGen.getType(fSwizzleType), out);
+ fGen.writeWord(result, out);
+ fGen.writeWord(base, out);
+ fGen.writeWord(base, out);
+ for (int component : fComponents) {
+ fGen.writeWord(component, out);
+ }
+ return result;
+ }
+
+ virtual void store(SpvId value, std::ostream& out) override {
+ // use OpVectorShuffle to mix and match the vector components. We effectively create
+ // a virtual vector out of the concatenation of the left and right vectors, and then
+ // select components from this virtual vector to make the result vector. For
+ // instance, given:
+ // vec3 L = ...;
+ // vec3 R = ...;
+ // L.xz = R.xy;
+ // we end up with the virtual vector (L.x, L.y, L.z, R.x, R.y, R.z). Then we want
+ // our result vector to look like (R.x, L.y, R.y), so we need to select indices
+ // (3, 1, 4).
+ SpvId base = fGen.nextId();
+ fGen.writeInstruction(SpvOpLoad, fGen.getType(fBaseType), base, fVecPointer, out);
+ SpvId shuffle = fGen.nextId();
+ fGen.writeOpCode(SpvOpVectorShuffle, 5 + fBaseType.columns(), out);
+ fGen.writeWord(fGen.getType(fBaseType), out);
+ fGen.writeWord(shuffle, out);
+ fGen.writeWord(base, out);
+ fGen.writeWord(value, out);
+ for (int i = 0; i < fBaseType.columns(); i++) {
+ // current offset into the virtual vector, defaults to pulling the unmodified
+ // value from the left side
+ int offset = i;
+ // check to see if we are writing this component
+ for (size_t j = 0; j < fComponents.size(); j++) {
+ if (fComponents[j] == i) {
+ // we're writing to this component, so adjust the offset to pull from
+ // the correct component of the right side instead of preserving the
+ // value from the left
+ offset = (int) (j + fBaseType.columns());
+ break;
+ }
+ }
+ fGen.writeWord(offset, out);
+ }
+ fGen.writeInstruction(SpvOpStore, fVecPointer, shuffle, out);
+ }
+
+private:
+ SPIRVCodeGenerator& fGen;
+ const SpvId fVecPointer;
+ const std::vector<int>& fComponents;
+ const Type& fBaseType;
+ const Type& fSwizzleType;
+};
+
+std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(Expression& expr,
+ std::ostream& out) {
+ switch (expr.fKind) {
+ case Expression::kVariableReference_Kind: {
+ std::shared_ptr<Variable> var = ((VariableReference&) expr).fVariable;
+ auto entry = fVariableMap.find(var);
+ ASSERT(entry != fVariableMap.end());
+ return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
+ *this,
+ entry->second,
+ this->getType(*expr.fType)));
+ }
+ case Expression::kIndex_Kind: // fall through
+ case Expression::kFieldAccess_Kind: {
+ std::vector<SpvId> chain = this->getAccessChain(expr, out);
+ SpvId member = this->nextId();
+ this->writeOpCode(SpvOpAccessChain, (SpvId) (3 + chain.size()), out);
+ this->writeWord(this->getPointerType(expr.fType, get_storage_class(expr)), out);
+ this->writeWord(member, out);
+ for (SpvId idx : chain) {
+ this->writeWord(idx, out);
+ }
+ return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
+ *this,
+ member,
+ this->getType(*expr.fType)));
+ }
+
+ case Expression::kSwizzle_Kind: {
+ Swizzle& swizzle = (Swizzle&) expr;
+ size_t count = swizzle.fComponents.size();
+ SpvId base = this->getLValue(*swizzle.fBase, out)->getPointer();
+ ASSERT(base);
+ if (count == 1) {
+ IntLiteral index(Position(), swizzle.fComponents[0]);
+ SpvId member = this->nextId();
+ this->writeInstruction(SpvOpAccessChain,
+ this->getPointerType(swizzle.fType,
+ get_storage_class(*swizzle.fBase)),
+ member,
+ base,
+ this->writeIntLiteral(index),
+ out);
+ return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
+ *this,
+ member,
+ this->getType(*expr.fType)));
+ } else {
+ return std::unique_ptr<SPIRVCodeGenerator::LValue>(new SwizzleLValue(
+ *this,
+ base,
+ swizzle.fComponents,
+ *swizzle.fBase->fType,
+ *expr.fType));
+ }
+ }
+
+ default:
+ // expr isn't actually an lvalue, create a dummy variable for it. This case happens due
+ // to the need to store values in temporary variables during function calls (see
+ // comments in getFunctionType); erroneous uses of rvalues as lvalues should have been
+ // caught by IRGenerator
+ SpvId result = this->nextId();
+ SpvId type = this->getPointerType(expr.fType, SpvStorageClassFunction);
+ this->writeInstruction(SpvOpVariable, type, result, SpvStorageClassFunction, out);
+ this->writeInstruction(SpvOpStore, result, this->writeExpression(expr, out), out);
+ return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
+ *this,
+ result,
+ this->getType(*expr.fType)));
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeVariableReference(VariableReference& ref, std::ostream& out) {
+ auto entry = fVariableMap.find(ref.fVariable);
+ ASSERT(entry != fVariableMap.end());
+ SpvId var = entry->second;
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpLoad, this->getType(*ref.fVariable->fType), result, var, out);
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeIndexExpression(IndexExpression& expr, std::ostream& out) {
+ return getLValue(expr, out)->load(out);
+}
+
+SpvId SPIRVCodeGenerator::writeFieldAccess(FieldAccess& f, std::ostream& out) {
+ return getLValue(f, out)->load(out);
+}
+
+SpvId SPIRVCodeGenerator::writeSwizzle(Swizzle& swizzle, std::ostream& out) {
+ SpvId base = this->writeExpression(*swizzle.fBase, out);
+ SpvId result = this->nextId();
+ size_t count = swizzle.fComponents.size();
+ if (count == 1) {
+ this->writeInstruction(SpvOpCompositeExtract, this->getType(*swizzle.fType), result, base,
+ swizzle.fComponents[0], out);
+ } else {
+ this->writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) count, out);
+ this->writeWord(this->getType(*swizzle.fType), out);
+ this->writeWord(result, out);
+ this->writeWord(base, out);
+ this->writeWord(base, out);
+ for (int component : swizzle.fComponents) {
+ this->writeWord(component, out);
+ }
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType,
+ const Type& operandType, SpvId lhs,
+ SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt,
+ SpvOp_ ifUInt, SpvOp_ ifBool, std::ostream& out) {
+ SpvId result = this->nextId();
+ if (is_float(operandType)) {
+ this->writeInstruction(ifFloat, this->getType(resultType), result, lhs, rhs, out);
+ } else if (is_signed(operandType)) {
+ this->writeInstruction(ifInt, this->getType(resultType), result, lhs, rhs, out);
+ } else if (is_unsigned(operandType)) {
+ this->writeInstruction(ifUInt, this->getType(resultType), result, lhs, rhs, out);
+ } else if (operandType == *kBool_Type) {
+ this->writeInstruction(ifBool, this->getType(resultType), result, lhs, rhs, out);
+ } else {
+ ABORT("invalid operandType: %s", operandType.description().c_str());
+ }
+ return result;
+}
+
+bool is_assignment(Token::Kind op) {
+ switch (op) {
+ case Token::EQ: // fall through
+ case Token::PLUSEQ: // fall through
+ case Token::MINUSEQ: // fall through
+ case Token::STAREQ: // fall through
+ case Token::SLASHEQ: // fall through
+ case Token::PERCENTEQ: // fall through
+ case Token::SHLEQ: // fall through
+ case Token::SHREQ: // fall through
+ case Token::BITWISEOREQ: // fall through
+ case Token::BITWISEXOREQ: // fall through
+ case Token::BITWISEANDEQ: // fall through
+ case Token::LOGICALOREQ: // fall through
+ case Token::LOGICALXOREQ: // fall through
+ case Token::LOGICALANDEQ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeBinaryExpression(BinaryExpression& b, std::ostream& out) {
+ // handle cases where we don't necessarily evaluate both LHS and RHS
+ switch (b.fOperator) {
+ case Token::EQ: {
+ SpvId rhs = this->writeExpression(*b.fRight, out);
+ this->getLValue(*b.fLeft, out)->store(rhs, out);
+ return rhs;
+ }
+ case Token::LOGICALAND:
+ return this->writeLogicalAnd(b, out);
+ case Token::LOGICALOR:
+ return this->writeLogicalOr(b, out);
+ default:
+ break;
+ }
+
+ // "normal" operators
+ const Type& resultType = *b.fType;
+ std::unique_ptr<LValue> lvalue;
+ SpvId lhs;
+ if (is_assignment(b.fOperator)) {
+ lvalue = this->getLValue(*b.fLeft, out);
+ lhs = lvalue->load(out);
+ } else {
+ lvalue = nullptr;
+ lhs = this->writeExpression(*b.fLeft, out);
+ }
+ SpvId rhs = this->writeExpression(*b.fRight, out);
+ // component type we are operating on: float, int, uint
+ const Type* operandType;
+ // IR allows mismatched types in expressions (e.g. vec2 * float), but they need special handling
+ // in SPIR-V
+ if (b.fLeft->fType != b.fRight->fType) {
+ if (b.fLeft->fType->kind() == Type::kVector_Kind &&
+ b.fRight->fType->isNumber()) {
+ // promote number to vector
+ SpvId vec = this->nextId();
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + b.fType->columns(), out);
+ this->writeWord(this->getType(resultType), out);
+ this->writeWord(vec, out);
+ for (int i = 0; i < resultType.columns(); i++) {
+ this->writeWord(rhs, out);
+ }
+ rhs = vec;
+ operandType = b.fRight->fType.get();
+ } else if (b.fRight->fType->kind() == Type::kVector_Kind &&
+ b.fLeft->fType->isNumber()) {
+ // promote number to vector
+ SpvId vec = this->nextId();
+ this->writeOpCode(SpvOpCompositeConstruct, 3 + b.fType->columns(), out);
+ this->writeWord(this->getType(resultType), out);
+ this->writeWord(vec, out);
+ for (int i = 0; i < resultType.columns(); i++) {
+ this->writeWord(lhs, out);
+ }
+ lhs = vec;
+ ASSERT(!lvalue);
+ operandType = b.fLeft->fType.get();
+ } else if (b.fLeft->fType->kind() == Type::kMatrix_Kind) {
+ SpvOp_ op;
+ if (b.fRight->fType->kind() == Type::kMatrix_Kind) {
+ op = SpvOpMatrixTimesMatrix;
+ } else if (b.fRight->fType->kind() == Type::kVector_Kind) {
+ op = SpvOpMatrixTimesVector;
+ } else {
+ ASSERT(b.fRight->fType->kind() == Type::kScalar_Kind);
+ op = SpvOpMatrixTimesScalar;
+ }
+ SpvId result = this->nextId();
+ this->writeInstruction(op, this->getType(*b.fType), result, lhs, rhs, out);
+ if (b.fOperator == Token::STAREQ) {
+ lvalue->store(result, out);
+ } else {
+ ASSERT(b.fOperator == Token::STAR);
+ }
+ return result;
+ } else if (b.fRight->fType->kind() == Type::kMatrix_Kind) {
+ SpvId result = this->nextId();
+ if (b.fLeft->fType->kind() == Type::kVector_Kind) {
+ this->writeInstruction(SpvOpVectorTimesMatrix, this->getType(*b.fType), result,
+ lhs, rhs, out);
+ } else {
+ ASSERT(b.fLeft->fType->kind() == Type::kScalar_Kind);
+ this->writeInstruction(SpvOpMatrixTimesScalar, this->getType(*b.fType), result, rhs,
+ lhs, out);
+ }
+ if (b.fOperator == Token::STAREQ) {
+ lvalue->store(result, out);
+ } else {
+ ASSERT(b.fOperator == Token::STAR);
+ }
+ return result;
+ } else {
+ ABORT("unsupported binary expression: %s", b.description().c_str());
+ }
+ } else {
+ operandType = b.fLeft->fType.get();
+ ASSERT(*operandType == *b.fRight->fType);
+ }
+ switch (b.fOperator) {
+ case Token::EQEQ:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdEqual,
+ SpvOpIEqual, SpvOpIEqual, SpvOpLogicalEqual, out);
+ case Token::NEQ:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdNotEqual,
+ SpvOpINotEqual, SpvOpINotEqual, SpvOpLogicalNotEqual,
+ out);
+ case Token::GT:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+ SpvOpFOrdGreaterThan, SpvOpSGreaterThan,
+ SpvOpUGreaterThan, SpvOpUndef, out);
+ case Token::LT:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdLessThan,
+ SpvOpSLessThan, SpvOpULessThan, SpvOpUndef, out);
+ case Token::GTEQ:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+ SpvOpFOrdGreaterThanEqual, SpvOpSGreaterThanEqual,
+ SpvOpUGreaterThanEqual, SpvOpUndef, out);
+ case Token::LTEQ:
+ ASSERT(resultType == *kBool_Type);
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
+ SpvOpFOrdLessThanEqual, SpvOpSLessThanEqual,
+ SpvOpULessThanEqual, SpvOpUndef, out);
+ case Token::PLUS:
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd,
+ SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
+ case Token::MINUS:
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub,
+ SpvOpISub, SpvOpISub, SpvOpUndef, out);
+ case Token::STAR:
+ if (b.fLeft->fType->kind() == Type::kMatrix_Kind &&
+ b.fRight->fType->kind() == Type::kMatrix_Kind) {
+ // matrix multiply
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpMatrixTimesMatrix, this->getType(resultType), result,
+ lhs, rhs, out);
+ return result;
+ }
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul,
+ SpvOpIMul, SpvOpIMul, SpvOpUndef, out);
+ case Token::SLASH:
+ return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv,
+ SpvOpSDiv, SpvOpUDiv, SpvOpUndef, out);
+ case Token::PLUSEQ: {
+ SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFAdd,
+ SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
+ ASSERT(lvalue);
+ lvalue->store(result, out);
+ return result;
+ }
+ case Token::MINUSEQ: {
+ SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFSub,
+ SpvOpISub, SpvOpISub, SpvOpUndef, out);
+ ASSERT(lvalue);
+ lvalue->store(result, out);
+ return result;
+ }
+ case Token::STAREQ: {
+ if (b.fLeft->fType->kind() == Type::kMatrix_Kind &&
+ b.fRight->fType->kind() == Type::kMatrix_Kind) {
+ // matrix multiply
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpMatrixTimesMatrix, this->getType(resultType), result,
+ lhs, rhs, out);
+ ASSERT(lvalue);
+ lvalue->store(result, out);
+ return result;
+ }
+ SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul,
+ SpvOpIMul, SpvOpIMul, SpvOpUndef, out);
+ ASSERT(lvalue);
+ lvalue->store(result, out);
+ return result;
+ }
+ case Token::SLASHEQ: {
+ SpvId result = this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFDiv,
+ SpvOpSDiv, SpvOpUDiv, SpvOpUndef, out);
+ ASSERT(lvalue);
+ lvalue->store(result, out);
+ return result;
+ }
+ default:
+ // FIXME: missing support for some operators (bitwise, &&=, ||=, shift...)
+ ABORT("unsupported binary expression: %s", b.description().c_str());
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeLogicalAnd(BinaryExpression& a, std::ostream& out) {
+ ASSERT(a.fOperator == Token::LOGICALAND);
+ BoolLiteral falseLiteral(Position(), false);
+ SpvId falseConstant = this->writeBoolLiteral(falseLiteral);
+ SpvId lhs = this->writeExpression(*a.fLeft, out);
+ SpvId rhsLabel = this->nextId();
+ SpvId end = this->nextId();
+ SpvId lhsBlock = fCurrentBlock;
+ this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
+ this->writeInstruction(SpvOpBranchConditional, lhs, rhsLabel, end, out);
+ this->writeLabel(rhsLabel, out);
+ SpvId rhs = this->writeExpression(*a.fRight, out);
+ SpvId rhsBlock = fCurrentBlock;
+ this->writeInstruction(SpvOpBranch, end, out);
+ this->writeLabel(end, out);
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpPhi, this->getType(*kBool_Type), result, falseConstant, lhsBlock,
+ rhs, rhsBlock, out);
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeLogicalOr(BinaryExpression& o, std::ostream& out) {
+ ASSERT(o.fOperator == Token::LOGICALOR);
+ BoolLiteral trueLiteral(Position(), true);
+ SpvId trueConstant = this->writeBoolLiteral(trueLiteral);
+ SpvId lhs = this->writeExpression(*o.fLeft, out);
+ SpvId rhsLabel = this->nextId();
+ SpvId end = this->nextId();
+ SpvId lhsBlock = fCurrentBlock;
+ this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
+ this->writeInstruction(SpvOpBranchConditional, lhs, end, rhsLabel, out);
+ this->writeLabel(rhsLabel, out);
+ SpvId rhs = this->writeExpression(*o.fRight, out);
+ SpvId rhsBlock = fCurrentBlock;
+ this->writeInstruction(SpvOpBranch, end, out);
+ this->writeLabel(end, out);
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpPhi, this->getType(*kBool_Type), result, trueConstant, lhsBlock,
+ rhs, rhsBlock, out);
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeTernaryExpression(TernaryExpression& t, std::ostream& out) {
+ SpvId test = this->writeExpression(*t.fTest, out);
+ if (t.fIfTrue->isConstant() && t.fIfFalse->isConstant()) {
+ // both true and false are constants, can just use OpSelect
+ SpvId result = this->nextId();
+ SpvId trueId = this->writeExpression(*t.fIfTrue, out);
+ SpvId falseId = this->writeExpression(*t.fIfFalse, out);
+ this->writeInstruction(SpvOpSelect, this->getType(*t.fType), result, test, trueId, falseId,
+ out);
+ return result;
+ }
+ // was originally using OpPhi to choose the result, but for some reason that is crashing on
+ // Adreno. Switched to storing the result in a temp variable as glslang does.
+ SpvId var = this->nextId();
+ this->writeInstruction(SpvOpVariable, this->getPointerType(t.fType, SpvStorageClassFunction),
+ var, SpvStorageClassFunction, out);
+ SpvId trueLabel = this->nextId();
+ SpvId falseLabel = this->nextId();
+ SpvId end = this->nextId();
+ this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
+ this->writeInstruction(SpvOpBranchConditional, test, trueLabel, falseLabel, out);
+ this->writeLabel(trueLabel, out);
+ this->writeInstruction(SpvOpStore, var, this->writeExpression(*t.fIfTrue, out), out);
+ this->writeInstruction(SpvOpBranch, end, out);
+ this->writeLabel(falseLabel, out);
+ this->writeInstruction(SpvOpStore, var, this->writeExpression(*t.fIfFalse, out), out);
+ this->writeInstruction(SpvOpBranch, end, out);
+ this->writeLabel(end, out);
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpLoad, this->getType(*t.fType), result, var, out);
+ return result;
+}
+
+Expression* literal_1(const Type& type) {
+ static IntLiteral int1(Position(), 1);
+ static FloatLiteral float1(Position(), 1.0);
+ if (type == *kInt_Type) {
+ return &int1;
+ }
+ else if (type == *kFloat_Type) {
+ return &float1;
+ } else {
+ ABORT("math is unsupported on type '%s'")
+ }
+}
+
+SpvId SPIRVCodeGenerator::writePrefixExpression(PrefixExpression& p, std::ostream& out) {
+ if (p.fOperator == Token::MINUS) {
+ SpvId result = this->nextId();
+ SpvId typeId = this->getType(*p.fType);
+ SpvId expr = this->writeExpression(*p.fOperand, out);
+ if (is_float(*p.fType)) {
+ this->writeInstruction(SpvOpFNegate, typeId, result, expr, out);
+ } else if (is_signed(*p.fType)) {
+ this->writeInstruction(SpvOpSNegate, typeId, result, expr, out);
+ } else {
+ ABORT("unsupported prefix expression %s", p.description().c_str());
+ };
+ return result;
+ }
+ switch (p.fOperator) {
+ case Token::PLUS:
+ return this->writeExpression(*p.fOperand, out);
+ case Token::PLUSPLUS: {
+ std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
+ SpvId one = this->writeExpression(*literal_1(*p.fType), out);
+ SpvId result = this->writeBinaryOperation(*p.fType, *p.fType, lv->load(out), one,
+ SpvOpFAdd, SpvOpIAdd, SpvOpIAdd, SpvOpUndef,
+ out);
+ lv->store(result, out);
+ return result;
+ }
+ case Token::MINUSMINUS: {
+ std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
+ SpvId one = this->writeExpression(*literal_1(*p.fType), out);
+ SpvId result = this->writeBinaryOperation(*p.fType, *p.fType, lv->load(out), one,
+ SpvOpFSub, SpvOpISub, SpvOpISub, SpvOpUndef,
+ out);
+ lv->store(result, out);
+ return result;
+ }
+ case Token::NOT: {
+ ASSERT(p.fOperand->fType == kBool_Type);
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpLogicalNot, this->getType(*p.fOperand->fType), result,
+ this->writeExpression(*p.fOperand, out), out);
+ return result;
+ }
+ default:
+ ABORT("unsupported prefix expression: %s", p.description().c_str());
+ }
+}
+
+SpvId SPIRVCodeGenerator::writePostfixExpression(PostfixExpression& p, std::ostream& out) {
+ std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
+ SpvId result = lv->load(out);
+ SpvId one = this->writeExpression(*literal_1(*p.fType), out);
+ switch (p.fOperator) {
+ case Token::PLUSPLUS: {
+ SpvId temp = this->writeBinaryOperation(*p.fType, *p.fType, result, one, SpvOpFAdd,
+ SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
+ lv->store(temp, out);
+ return result;
+ }
+ case Token::MINUSMINUS: {
+ SpvId temp = this->writeBinaryOperation(*p.fType, *p.fType, result, one, SpvOpFSub,
+ SpvOpISub, SpvOpISub, SpvOpUndef, out);
+ lv->store(temp, out);
+ return result;
+ }
+ default:
+ ABORT("unsupported postfix expression %s", p.description().c_str());
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeBoolLiteral(BoolLiteral& b) {
+ if (b.fValue) {
+ if (fBoolTrue == 0) {
+ fBoolTrue = this->nextId();
+ this->writeInstruction(SpvOpConstantTrue, this->getType(*b.fType), fBoolTrue,
+ fConstantBuffer);
+ }
+ return fBoolTrue;
+ } else {
+ if (fBoolFalse == 0) {
+ fBoolFalse = this->nextId();
+ this->writeInstruction(SpvOpConstantFalse, this->getType(*b.fType), fBoolFalse,
+ fConstantBuffer);
+ }
+ return fBoolFalse;
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeIntLiteral(IntLiteral& i) {
+ if (i.fType == kInt_Type) {
+ auto entry = fIntConstants.find(i.fValue);
+ if (entry == fIntConstants.end()) {
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpConstant, this->getType(*i.fType), result, (SpvId) i.fValue,
+ fConstantBuffer);
+ fIntConstants[i.fValue] = result;
+ return result;
+ }
+ return entry->second;
+ } else {
+ ASSERT(i.fType == kUInt_Type);
+ auto entry = fUIntConstants.find(i.fValue);
+ if (entry == fUIntConstants.end()) {
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpConstant, this->getType(*i.fType), result, (SpvId) i.fValue,
+ fConstantBuffer);
+ fUIntConstants[i.fValue] = result;
+ return result;
+ }
+ return entry->second;
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeFloatLiteral(FloatLiteral& f) {
+ if (f.fType == kFloat_Type) {
+ float value = (float) f.fValue;
+ auto entry = fFloatConstants.find(value);
+ if (entry == fFloatConstants.end()) {
+ SpvId result = this->nextId();
+ uint32_t bits;
+ ASSERT(sizeof(bits) == sizeof(value));
+ memcpy(&bits, &value, sizeof(bits));
+ this->writeInstruction(SpvOpConstant, this->getType(*f.fType), result, bits,
+ fConstantBuffer);
+ fFloatConstants[value] = result;
+ return result;
+ }
+ return entry->second;
+ } else {
+ ASSERT(f.fType == kDouble_Type);
+ auto entry = fDoubleConstants.find(f.fValue);
+ if (entry == fDoubleConstants.end()) {
+ SpvId result = this->nextId();
+ uint64_t bits;
+ ASSERT(sizeof(bits) == sizeof(f.fValue));
+ memcpy(&bits, &f.fValue, sizeof(bits));
+ this->writeInstruction(SpvOpConstant, this->getType(*f.fType), result,
+ bits & 0xffffffff, bits >> 32, fConstantBuffer);
+ fDoubleConstants[f.fValue] = result;
+ return result;
+ }
+ return entry->second;
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeFunctionStart(std::shared_ptr<FunctionDeclaration> f,
+ std::ostream& out) {
+ SpvId result = fFunctionMap[f];
+ this->writeInstruction(SpvOpFunction, this->getType(*f->fReturnType), result,
+ SpvFunctionControlMaskNone, this->getFunctionType(f), out);
+ this->writeInstruction(SpvOpName, result, f->fName.c_str(), fNameBuffer);
+ for (size_t i = 0; i < f->fParameters.size(); i++) {
+ SpvId id = this->nextId();
+ fVariableMap[f->fParameters[i]] = id;
+ SpvId type;
+ type = this->getPointerType(f->fParameters[i]->fType, SpvStorageClassFunction);
+ this->writeInstruction(SpvOpFunctionParameter, type, id, out);
+ }
+ return result;
+}
+
+SpvId SPIRVCodeGenerator::writeFunction(FunctionDefinition& f, std::ostream& out) {
+ SpvId result = this->writeFunctionStart(f.fDeclaration, out);
+ this->writeLabel(this->nextId(), out);
+ if (f.fDeclaration->fName == "main") {
+ out << fGlobalInitializersBuffer.str();
+ }
+ std::stringstream bodyBuffer;
+ this->writeBlock(*f.fBody, bodyBuffer);
+ out << fVariableBuffer.str();
+ fVariableBuffer.str("");
+ out << bodyBuffer.str();
+ if (fCurrentBlock) {
+ this->writeInstruction(SpvOpReturn, out);
+ }
+ this->writeInstruction(SpvOpFunctionEnd, out);
+ return result;
+}
+
+void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target) {
+ if (layout.fLocation >= 0) {
+ this->writeInstruction(SpvOpDecorate, target, SpvDecorationLocation, layout.fLocation,
+ fDecorationBuffer);
+ }
+ if (layout.fBinding >= 0) {
+ this->writeInstruction(SpvOpDecorate, target, SpvDecorationBinding, layout.fBinding,
+ fDecorationBuffer);
+ }
+ if (layout.fIndex >= 0) {
+ this->writeInstruction(SpvOpDecorate, target, SpvDecorationIndex, layout.fIndex,
+ fDecorationBuffer);
+ }
+ if (layout.fSet >= 0) {
+ this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet,
+ fDecorationBuffer);
+ }
+ if (layout.fBuiltin >= 0) {
+ this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin,
+ fDecorationBuffer);
+ }
+}
+
+void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target, int member) {
+ if (layout.fLocation >= 0) {
+ this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationLocation,
+ layout.fLocation, fDecorationBuffer);
+ }
+ if (layout.fBinding >= 0) {
+ this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBinding,
+ layout.fBinding, fDecorationBuffer);
+ }
+ if (layout.fIndex >= 0) {
+ this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationIndex,
+ layout.fIndex, fDecorationBuffer);
+ }
+ if (layout.fSet >= 0) {
+ this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationDescriptorSet,
+ layout.fSet, fDecorationBuffer);
+ }
+ if (layout.fBuiltin >= 0) {
+ this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBuiltIn,
+ layout.fBuiltin, fDecorationBuffer);
+ }
+}
+
+SpvId SPIRVCodeGenerator::writeInterfaceBlock(InterfaceBlock& intf) {
+ SpvId type = this->getType(*intf.fVariable->fType);
+ SpvId result = this->nextId();
+ this->writeInstruction(SpvOpDecorate, type, SpvDecorationBlock, fDecorationBuffer);
+ SpvStorageClass_ storageClass = get_storage_class(intf.fVariable->fModifiers);
+ SpvId ptrType = this->nextId();
+ this->writeInstruction(SpvOpTypePointer, ptrType, storageClass, type, fConstantBuffer);
+ this->writeInstruction(SpvOpVariable, ptrType, result, storageClass, fConstantBuffer);
+ this->writeLayout(intf.fVariable->fModifiers.fLayout, result);
+ fVariableMap[intf.fVariable] = result;
+ return result;
+}
+
+void SPIRVCodeGenerator::writeGlobalVars(VarDeclaration& decl, std::ostream& out) {
+ for (size_t i = 0; i < decl.fVars.size(); i++) {
+ if (!decl.fVars[i]->fIsReadFrom && !decl.fVars[i]->fIsWrittenTo) {
+ continue;
+ }
+ SpvStorageClass_ storageClass;
+ if (decl.fVars[i]->fModifiers.fFlags & Modifiers::kIn_Flag) {
+ storageClass = SpvStorageClassInput;
+ } else if (decl.fVars[i]->fModifiers.fFlags & Modifiers::kOut_Flag) {
+ storageClass = SpvStorageClassOutput;
+ } else if (decl.fVars[i]->fModifiers.fFlags & Modifiers::kUniform_Flag) {
+ if (decl.fVars[i]->fType->kind() == Type::kSampler_Kind) {
+ storageClass = SpvStorageClassUniformConstant;
+ } else {
+ storageClass = SpvStorageClassUniform;
+ }
+ } else {
+ storageClass = SpvStorageClassPrivate;
+ }
+ SpvId id = this->nextId();
+ fVariableMap[decl.fVars[i]] = id;
+ SpvId type = this->getPointerType(decl.fVars[i]->fType, storageClass);
+ this->writeInstruction(SpvOpVariable, type, id, storageClass, fConstantBuffer);
+ this->writeInstruction(SpvOpName, id, decl.fVars[i]->fName.c_str(), fNameBuffer);
+ if (decl.fVars[i]->fType->kind() == Type::kMatrix_Kind) {
+ this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationColMajor,
+ fDecorationBuffer);
+ this->writeInstruction(SpvOpMemberDecorate, id, (SpvId) i, SpvDecorationMatrixStride,
+ (SpvId) decl.fVars[i]->fType->stride(), fDecorationBuffer);
+ }
+ if (decl.fValues[i]) {
+ ASSERT(!fCurrentBlock);
+ fCurrentBlock = -1;
+ SpvId value = this->writeExpression(*decl.fValues[i], fGlobalInitializersBuffer);
+ this->writeInstruction(SpvOpStore, id, value, fGlobalInitializersBuffer);
+ fCurrentBlock = 0;
+ }
+ this->writeLayout(decl.fVars[i]->fModifiers.fLayout, id);
+ }
+}
+
+void SPIRVCodeGenerator::writeVarDeclaration(VarDeclaration& decl, std::ostream& out) {
+ for (size_t i = 0; i < decl.fVars.size(); i++) {
+ SpvId id = this->nextId();
+ fVariableMap[decl.fVars[i]] = id;
+ SpvId type = this->getPointerType(decl.fVars[i]->fType, SpvStorageClassFunction);
+ this->writeInstruction(SpvOpVariable, type, id, SpvStorageClassFunction, fVariableBuffer);
+ this->writeInstruction(SpvOpName, id, decl.fVars[i]->fName.c_str(), fNameBuffer);
+ if (decl.fValues[i]) {
+ SpvId value = this->writeExpression(*decl.fValues[i], out);
+ this->writeInstruction(SpvOpStore, id, value, out);
+ }
+ }
+}
+
+void SPIRVCodeGenerator::writeStatement(Statement& s, std::ostream& out) {
+ switch (s.fKind) {
+ case Statement::kBlock_Kind:
+ this->writeBlock((Block&) s, out);
+ break;
+ case Statement::kExpression_Kind:
+ this->writeExpression(*((ExpressionStatement&) s).fExpression, out);
+ break;
+ case Statement::kReturn_Kind:
+ this->writeReturnStatement((ReturnStatement&) s, out);
+ break;
+ case Statement::kVarDeclaration_Kind:
+ this->writeVarDeclaration(*((VarDeclarationStatement&) s).fDeclaration, out);
+ break;
+ case Statement::kIf_Kind:
+ this->writeIfStatement((IfStatement&) s, out);
+ break;
+ case Statement::kFor_Kind:
+ this->writeForStatement((ForStatement&) s, out);
+ break;
+ case Statement::kBreak_Kind:
+ this->writeInstruction(SpvOpBranch, fBreakTarget.top(), out);
+ break;
+ case Statement::kContinue_Kind:
+ this->writeInstruction(SpvOpBranch, fContinueTarget.top(), out);
+ break;
+ case Statement::kDiscard_Kind:
+ this->writeInstruction(SpvOpKill, out);
+ break;
+ default:
+ ABORT("unsupported statement: %s", s.description().c_str());
+ }
+}
+
+void SPIRVCodeGenerator::writeBlock(Block& b, std::ostream& out) {
+ for (size_t i = 0; i < b.fStatements.size(); i++) {
+ this->writeStatement(*b.fStatements[i], out);
+ }
+}
+
+void SPIRVCodeGenerator::writeIfStatement(IfStatement& stmt, std::ostream& out) {
+ SpvId test = this->writeExpression(*stmt.fTest, out);
+ SpvId ifTrue = this->nextId();
+ SpvId ifFalse = this->nextId();
+ if (stmt.fIfFalse) {
+ SpvId end = this->nextId();
+ this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
+ this->writeInstruction(SpvOpBranchConditional, test, ifTrue, ifFalse, out);
+ this->writeLabel(ifTrue, out);
+ this->writeStatement(*stmt.fIfTrue, out);
+ if (fCurrentBlock) {
+ this->writeInstruction(SpvOpBranch, end, out);
+ }
+ this->writeLabel(ifFalse, out);
+ this->writeStatement(*stmt.fIfFalse, out);
+ if (fCurrentBlock) {
+ this->writeInstruction(SpvOpBranch, end, out);
+ }
+ this->writeLabel(end, out);
+ } else {
+ this->writeInstruction(SpvOpSelectionMerge, ifFalse, SpvSelectionControlMaskNone, out);
+ this->writeInstruction(SpvOpBranchConditional, test, ifTrue, ifFalse, out);
+ this->writeLabel(ifTrue, out);
+ this->writeStatement(*stmt.fIfTrue, out);
+ if (fCurrentBlock) {
+ this->writeInstruction(SpvOpBranch, ifFalse, out);
+ }
+ this->writeLabel(ifFalse, out);
+ }
+}
+
+void SPIRVCodeGenerator::writeForStatement(ForStatement& f, std::ostream& out) {
+ if (f.fInitializer) {
+ this->writeStatement(*f.fInitializer, out);
+ }
+ SpvId header = this->nextId();
+ SpvId start = this->nextId();
+ SpvId body = this->nextId();
+ SpvId next = this->nextId();
+ fContinueTarget.push(next);
+ SpvId end = this->nextId();
+ fBreakTarget.push(end);
+ this->writeInstruction(SpvOpBranch, header, out);
+ this->writeLabel(header, out);
+ this->writeInstruction(SpvOpLoopMerge, end, next, SpvLoopControlMaskNone, out);
+ this->writeInstruction(SpvOpBranch, start, out);
+ this->writeLabel(start, out);
+ SpvId test = this->writeExpression(*f.fTest, out);
+ this->writeInstruction(SpvOpBranchConditional, test, body, end, out);
+ this->writeLabel(body, out);
+ this->writeStatement(*f.fStatement, out);
+ if (fCurrentBlock) {
+ this->writeInstruction(SpvOpBranch, next, out);
+ }
+ this->writeLabel(next, out);
+ if (f.fNext) {
+ this->writeExpression(*f.fNext, out);
+ }
+ this->writeInstruction(SpvOpBranch, header, out);
+ this->writeLabel(end, out);
+ fBreakTarget.pop();
+ fContinueTarget.pop();
+}
+
+void SPIRVCodeGenerator::writeReturnStatement(ReturnStatement& r, std::ostream& out) {
+ if (r.fExpression) {
+ this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.fExpression, out),
+ out);
+ } else {
+ this->writeInstruction(SpvOpReturn, out);
+ }
+}
+
+void SPIRVCodeGenerator::writeInstructions(Program& program, std::ostream& out) {
+ fGLSLExtendedInstructions = this->nextId();
+ std::stringstream body;
+ std::vector<SpvId> interfaceVars;
+ // assign IDs to functions
+ for (size_t i = 0; i < program.fElements.size(); i++) {
+ if (program.fElements[i]->fKind == ProgramElement::kFunction_Kind) {
+ FunctionDefinition& f = (FunctionDefinition&) *program.fElements[i];
+ fFunctionMap[f.fDeclaration] = this->nextId();
+ }
+ }
+ for (size_t i = 0; i < program.fElements.size(); i++) {
+ if (program.fElements[i]->fKind == ProgramElement::kInterfaceBlock_Kind) {
+ InterfaceBlock& intf = (InterfaceBlock&) *program.fElements[i];
+ SpvId id = this->writeInterfaceBlock(intf);
+ if ((intf.fVariable->fModifiers.fFlags & Modifiers::kIn_Flag) ||
+ (intf.fVariable->fModifiers.fFlags & Modifiers::kOut_Flag)) {
+ interfaceVars.push_back(id);
+ }
+ }
+ }
+ for (size_t i = 0; i < program.fElements.size(); i++) {
+ if (program.fElements[i]->fKind == ProgramElement::kVar_Kind) {
+ this->writeGlobalVars(((VarDeclaration&) *program.fElements[i]), body);
+ }
+ }
+ for (size_t i = 0; i < program.fElements.size(); i++) {
+ if (program.fElements[i]->fKind == ProgramElement::kFunction_Kind) {
+ this->writeFunction(((FunctionDefinition&) *program.fElements[i]), body);
+ }
+ }
+ std::shared_ptr<FunctionDeclaration> main = nullptr;
+ for (auto entry : fFunctionMap) {
+ if (entry.first->fName == "main") {
+ main = entry.first;
+ }
+ }
+ ASSERT(main);
+ for (auto entry : fVariableMap) {
+ std::shared_ptr<Variable> var = entry.first;
+ if (var->fStorage == Variable::kGlobal_Storage &&
+ ((var->fModifiers.fFlags & Modifiers::kIn_Flag) ||
+ (var->fModifiers.fFlags & Modifiers::kOut_Flag))) {
+ interfaceVars.push_back(entry.second);
+ }
+ }
+ this->writeCapabilities(out);
+ this->writeInstruction(SpvOpExtInstImport, fGLSLExtendedInstructions, "GLSL.std.450", out);
+ this->writeInstruction(SpvOpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450, out);
+ this->writeOpCode(SpvOpEntryPoint, (SpvId) (3 + (strlen(main->fName.c_str()) + 4) / 4) +
+ (int32_t) interfaceVars.size(), out);
+ switch (program.fKind) {
+ case Program::kVertex_Kind:
+ this->writeWord(SpvExecutionModelVertex, out);
+ break;
+ case Program::kFragment_Kind:
+ this->writeWord(SpvExecutionModelFragment, out);
+ break;
+ }
+ this->writeWord(fFunctionMap[main], out);
+ this->writeString(main->fName.c_str(), out);
+ for (int var : interfaceVars) {
+ this->writeWord(var, out);
+ }
+ if (program.fKind == Program::kFragment_Kind) {
+ this->writeInstruction(SpvOpExecutionMode,
+ fFunctionMap[main],
+ SpvExecutionModeOriginUpperLeft,
+ out);
+ }
+ for (size_t i = 0; i < program.fElements.size(); i++) {
+ if (program.fElements[i]->fKind == ProgramElement::kExtension_Kind) {
+ this->writeInstruction(SpvOpSourceExtension,
+ ((Extension&) *program.fElements[i]).fName.c_str(),
+ out);
+ }
+ }
+
+ out << fNameBuffer.str();
+ out << fDecorationBuffer.str();
+ out << fConstantBuffer.str();
+ out << fExternalFunctionsBuffer.str();
+ out << body.str();
+}
+
+void SPIRVCodeGenerator::generateCode(Program& program, std::ostream& out) {
+ this->writeWord(SpvMagicNumber, out);
+ this->writeWord(SpvVersion, out);
+ this->writeWord(SKSL_MAGIC, out);
+ std::stringstream buffer;
+ this->writeInstructions(program, buffer);
+ this->writeWord(fIdCount, out);
+ this->writeWord(0, out); // reserved, always zero
+ out << buffer.str();
+}
+
+}
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
new file mode 100644
index 0000000000..885c6b8b70
--- /dev/null
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SPIRVCODEGENERATOR
+#define SKSL_SPIRVCODEGENERATOR
+
+#include <sstream>
+#include <stack>
+#include <tuple>
+#include <unordered_map>
+
+#include "SkSLCodeGenerator.h"
+#include "ir/SkSLBinaryExpression.h"
+#include "ir/SkSLBoolLiteral.h"
+#include "ir/SkSLConstructor.h"
+#include "ir/SkSLFloatLiteral.h"
+#include "ir/SkSLIfStatement.h"
+#include "ir/SkSLIndexExpression.h"
+#include "ir/SkSLInterfaceBlock.h"
+#include "ir/SkSLIntLiteral.h"
+#include "ir/SkSLFieldAccess.h"
+#include "ir/SkSLForStatement.h"
+#include "ir/SkSLFunctionCall.h"
+#include "ir/SkSLFunctionDeclaration.h"
+#include "ir/SkSLFunctionDefinition.h"
+#include "ir/SkSLPrefixExpression.h"
+#include "ir/SkSLPostfixExpression.h"
+#include "ir/SkSLProgramElement.h"
+#include "ir/SkSLReturnStatement.h"
+#include "ir/SkSLStatement.h"
+#include "ir/SkSLSwizzle.h"
+#include "ir/SkSLTernaryExpression.h"
+#include "ir/SkSLVarDeclaration.h"
+#include "ir/SkSLVarDeclarationStatement.h"
+#include "ir/SkSLVariableReference.h"
+#include "spirv.h"
+
+namespace SkSL {
+
+#define kLast_Capability SpvCapabilityMultiViewport
+
+/**
+ * Converts a Program into a SPIR-V binary.
+ */
+class SPIRVCodeGenerator : public CodeGenerator {
+public:
+ class LValue {
+ public:
+ virtual ~LValue() {}
+
+ // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced
+ // by a pointer (e.g. vector swizzles), returns 0.
+ virtual SpvId getPointer() = 0;
+
+ virtual SpvId load(std::ostream& out) = 0;
+
+ virtual void store(SpvId value, std::ostream& out) = 0;
+ };
+
+ SPIRVCodeGenerator()
+ : fCapabilities(1 << SpvCapabilityShader)
+ , fIdCount(1)
+ , fBoolTrue(0)
+ , fBoolFalse(0)
+ , fCurrentBlock(0) {
+ this->setupIntrinsics();
+ }
+
+ void generateCode(Program& program, std::ostream& out) override;
+
+private:
+ enum IntrinsicKind {
+ kGLSL_STD_450_IntrinsicKind,
+ kSPIRV_IntrinsicKind,
+ kSpecial_IntrinsicKind
+ };
+
+ enum SpecialIntrinsic {
+ kAtan_SpecialIntrinsic,
+ kTexture_SpecialIntrinsic,
+ kTexture2D_SpecialIntrinsic,
+ kTextureProj_SpecialIntrinsic
+ };
+
+ void setupIntrinsics();
+
+ SpvId nextId();
+
+ SpvId getType(const Type& type);
+
+ SpvId getFunctionType(std::shared_ptr<FunctionDeclaration> function);
+
+ SpvId getPointerType(std::shared_ptr<Type> type, SpvStorageClass_ storageClass);
+
+ std::vector<SpvId> getAccessChain(Expression& expr, std::ostream& out);
+
+ void writeLayout(const Layout& layout, SpvId target);
+
+ void writeLayout(const Layout& layout, SpvId target, int member);
+
+ void writeStruct(const Type& type, SpvId resultId);
+
+ void writeProgramElement(ProgramElement& pe, std::ostream& out);
+
+ SpvId writeInterfaceBlock(InterfaceBlock& intf);
+
+ SpvId writeFunctionStart(std::shared_ptr<FunctionDeclaration> f, std::ostream& out);
+
+ SpvId writeFunctionDeclaration(std::shared_ptr<FunctionDeclaration> f, std::ostream& out);
+
+ SpvId writeFunction(FunctionDefinition& f, std::ostream& out);
+
+ void writeGlobalVars(VarDeclaration& v, std::ostream& out);
+
+ void writeVarDeclaration(VarDeclaration& decl, std::ostream& out);
+
+ SpvId writeVariableReference(VariableReference& ref, std::ostream& out);
+
+ std::unique_ptr<LValue> getLValue(Expression& value, std::ostream& out);
+
+ SpvId writeExpression(Expression& expr, std::ostream& out);
+
+ SpvId writeIntrinsicCall(FunctionCall& c, std::ostream& out);
+
+ SpvId writeFunctionCall(FunctionCall& c, std::ostream& out);
+
+ SpvId writeSpecialIntrinsic(FunctionCall& c, SpecialIntrinsic kind, std::ostream& out);
+
+ SpvId writeConstantVector(Constructor& c);
+
+ SpvId writeFloatConstructor(Constructor& c, std::ostream& out);
+
+ SpvId writeIntConstructor(Constructor& c, std::ostream& out);
+
+ SpvId writeMatrixConstructor(Constructor& c, std::ostream& out);
+
+ SpvId writeVectorConstructor(Constructor& c, std::ostream& out);
+
+ SpvId writeConstructor(Constructor& c, std::ostream& out);
+
+ SpvId writeFieldAccess(FieldAccess& f, std::ostream& out);
+
+ SpvId writeSwizzle(Swizzle& swizzle, std::ostream& out);
+
+ SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
+ SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
+ SpvOp_ ifBool, std::ostream& out);
+
+ SpvId writeBinaryOperation(BinaryExpression& expr, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
+ std::ostream& out);
+
+ SpvId writeBinaryExpression(BinaryExpression& b, std::ostream& out);
+
+ SpvId writeTernaryExpression(TernaryExpression& t, std::ostream& out);
+
+ SpvId writeIndexExpression(IndexExpression& expr, std::ostream& out);
+
+ SpvId writeLogicalAnd(BinaryExpression& b, std::ostream& out);
+
+ SpvId writeLogicalOr(BinaryExpression& o, std::ostream& out);
+
+ SpvId writePrefixExpression(PrefixExpression& p, std::ostream& out);
+
+ SpvId writePostfixExpression(PostfixExpression& p, std::ostream& out);
+
+ SpvId writeBoolLiteral(BoolLiteral& b);
+
+ SpvId writeIntLiteral(IntLiteral& i);
+
+ SpvId writeFloatLiteral(FloatLiteral& f);
+
+ void writeStatement(Statement& s, std::ostream& out);
+
+ void writeBlock(Block& b, std::ostream& out);
+
+ void writeIfStatement(IfStatement& stmt, std::ostream& out);
+
+ void writeForStatement(ForStatement& f, std::ostream& out);
+
+ void writeReturnStatement(ReturnStatement& r, std::ostream& out);
+
+ void writeCapabilities(std::ostream& out);
+
+ void writeInstructions(Program& program, std::ostream& out);
+
+ void writeOpCode(SpvOp_ opCode, int length, std::ostream& out);
+
+ void writeWord(int32_t word, std::ostream& out);
+
+ void writeString(const char* string, std::ostream& out);
+
+ void writeLabel(SpvId id, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, const char* string, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, const char* string, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, const char* string,
+ std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3,
+ std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
+ std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
+ int32_t word5, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
+ int32_t word5, int32_t word6, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
+ int32_t word5, int32_t word6, int32_t word7, std::ostream& out);
+
+ void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
+ int32_t word5, int32_t word6, int32_t word7, int32_t word8,
+ std::ostream& out);
+
+ uint64_t fCapabilities;
+ SpvId fIdCount;
+ SpvId fGLSLExtendedInstructions;
+ typedef std::tuple<IntrinsicKind, int32_t, int32_t, int32_t, int32_t> Intrinsic;
+ std::unordered_map<std::string, Intrinsic> fIntrinsicMap;
+ std::unordered_map<std::shared_ptr<FunctionDeclaration>, SpvId> fFunctionMap;
+ std::unordered_map<std::shared_ptr<Variable>, SpvId> fVariableMap;
+ std::unordered_map<std::shared_ptr<Variable>, int32_t> fInterfaceBlockMap;
+ std::unordered_map<std::string, SpvId> fTypeMap;
+ std::stringstream fCapabilitiesBuffer;
+ std::stringstream fGlobalInitializersBuffer;
+ std::stringstream fConstantBuffer;
+ std::stringstream fExternalFunctionsBuffer;
+ std::stringstream fVariableBuffer;
+ std::stringstream fNameBuffer;
+ std::stringstream fDecorationBuffer;
+
+ SpvId fBoolTrue;
+ SpvId fBoolFalse;
+ std::unordered_map<int64_t, SpvId> fIntConstants;
+ std::unordered_map<uint64_t, SpvId> fUIntConstants;
+ std::unordered_map<float, SpvId> fFloatConstants;
+ std::unordered_map<double, SpvId> fDoubleConstants;
+ // label of the current block, or 0 if we are not in a block
+ SpvId fCurrentBlock;
+ std::stack<SpvId> fBreakTarget;
+ std::stack<SpvId> fContinueTarget;
+
+ friend class PointerLValue;
+ friend class SwizzleLValue;
+};
+
+}
+
+#endif
diff --git a/src/sksl/SkSLToken.h b/src/sksl/SkSLToken.h
new file mode 100644
index 0000000000..538ae50a1a
--- /dev/null
+++ b/src/sksl/SkSLToken.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_TOKEN
+#define SKSL_TOKEN
+
+#include "SkSLPosition.h"
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * Represents a lexical analysis token. Token is generally only used during the parse process, but
+ * Token::Kind is also used to represent operator kinds.
+ */
+struct Token {
+ enum Kind {
+ END_OF_FILE,
+ IDENTIFIER,
+ INT_LITERAL,
+ FLOAT_LITERAL,
+ TRUE_LITERAL,
+ FALSE_LITERAL,
+ LPAREN,
+ RPAREN,
+ LBRACE,
+ RBRACE,
+ LBRACKET,
+ RBRACKET,
+ DOT,
+ COMMA,
+ PLUSPLUS,
+ MINUSMINUS,
+ PLUS,
+ MINUS,
+ STAR,
+ SLASH,
+ PERCENT,
+ SHL,
+ SHR,
+ BITWISEOR,
+ BITWISEXOR,
+ BITWISEAND,
+ LOGICALOR,
+ LOGICALXOR,
+ LOGICALAND,
+ NOT,
+ QUESTION,
+ COLON,
+ EQ,
+ EQEQ,
+ NEQ,
+ GT,
+ LT,
+ GTEQ,
+ LTEQ,
+ PLUSEQ,
+ MINUSEQ,
+ STAREQ,
+ SLASHEQ,
+ PERCENTEQ,
+ SHLEQ,
+ SHREQ,
+ BITWISEOREQ,
+ BITWISEXOREQ,
+ BITWISEANDEQ,
+ LOGICALOREQ,
+ LOGICALXOREQ,
+ LOGICALANDEQ,
+ SEMICOLON,
+ IF,
+ ELSE,
+ FOR,
+ WHILE,
+ DO,
+ RETURN,
+ BREAK,
+ CONTINUE,
+ DISCARD,
+ IN,
+ OUT,
+ INOUT,
+ CONST,
+ LOWP,
+ MEDIUMP,
+ HIGHP,
+ UNIFORM,
+ STRUCT,
+ LAYOUT,
+ DIRECTIVE,
+ PRECISION,
+ INVALID_TOKEN
+ };
+
+ static std::string OperatorName(Kind kind) {
+ switch (kind) {
+ case Token::PLUS: return "+";
+ case Token::MINUS: return "-";
+ case Token::STAR: return "*";
+ case Token::SLASH: return "/";
+ case Token::PERCENT: return "%";
+ case Token::SHL: return "<<";
+ case Token::SHR: return ">>";
+ case Token::LOGICALAND: return "&&";
+ case Token::LOGICALOR: return "||";
+ case Token::LOGICALXOR: return "^^";
+ case Token::BITWISEAND: return "&";
+ case Token::BITWISEOR: return "|";
+ case Token::BITWISEXOR: return "^";
+ case Token::EQ: return "=";
+ case Token::EQEQ: return "==";
+ case Token::NEQ: return "!=";
+ case Token::LT: return "<";
+ case Token::GT: return ">";
+ case Token::LTEQ: return "<=";
+ case Token::GTEQ: return ">=";
+ case Token::PLUSEQ: return "+=";
+ case Token::MINUSEQ: return "-=";
+ case Token::STAREQ: return "*=";
+ case Token::SLASHEQ: return "/=";
+ case Token::PERCENTEQ: return "%=";
+ case Token::SHLEQ: return "<<=";
+ case Token::SHREQ: return ">>=";
+ case Token::LOGICALANDEQ: return "&&=";
+ case Token::LOGICALOREQ: return "||=";
+ case Token::LOGICALXOREQ: return "^^=";
+ case Token::BITWISEANDEQ: return "&=";
+ case Token::BITWISEOREQ: return "|=";
+ case Token::BITWISEXOREQ: return "^=";
+ case Token::PLUSPLUS: return "++";
+ case Token::MINUSMINUS: return "--";
+ case Token::NOT: return "!";
+ default:
+ ABORT("unsupported operator: %d\n", kind);
+ }
+ }
+
+ Token() {
+ }
+
+ Token(Position position, Kind kind, std::string text)
+ : fPosition(position)
+ , fKind(kind)
+ , fText(std::move(text)) {}
+
+ Position fPosition;
+ Kind fKind;
+ std::string fText;
+};
+
+} // namespace
+#endif
diff --git a/src/sksl/SkSLUtil.cpp b/src/sksl/SkSLUtil.cpp
new file mode 100644
index 0000000000..327bffe4f1
--- /dev/null
+++ b/src/sksl/SkSLUtil.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+int stoi(std::string s) {
+ return atoi(s.c_str());
+}
+
+double stod(std::string s) {
+ return atof(s.c_str());
+}
+
+long stol(std::string s) {
+ return atol(s.c_str());
+}
+
+void sksl_abort() {
+#ifdef SKIA
+ sk_abort_no_print();
+ exit(1);
+#else
+ abort();
+#endif
+}
+
+} // namespace
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
new file mode 100644
index 0000000000..5536d93151
--- /dev/null
+++ b/src/sksl/SkSLUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_UTIL
+#define SKSL_UTIL
+
+#include <string>
+#include <sstream>
+#include "stdlib.h"
+#include "assert.h"
+#include "SkTypes.h"
+
+namespace SkSL {
+
+// our own definitions of certain std:: functions, because they are not always present on Android
+
+template <typename T> std::string to_string(T value) {
+#ifdef SK_BUILD_FOR_ANDROID
+ std::stringstream buffer;
+ buffer << value;
+ return buffer.str();
+#else
+ return std::to_string(value);
+#endif
+}
+
+#if _MSC_VER
+#define NORETURN __declspec(noreturn)
+#else
+#define NORETURN __attribute__((__noreturn__))
+#endif
+int stoi(std::string s);
+
+double stod(std::string s);
+
+long stol(std::string s);
+
+NORETURN void sksl_abort();
+
+} // namespace
+
+#ifdef DEBUG
+#define ASSERT(x) assert(x)
+#define ASSERT_RESULT(x) ASSERT(x);
+#else
+#define ASSERT(x)
+#define ASSERT_RESULT(x) x
+#endif
+
+#ifdef SKIA
+#define ABORT(...) { SkDebugf(__VA_ARGS__); sksl_abort(); }
+#else
+#define ABORT(...) { sksl_abort(); }
+#endif
+
+#endif
diff --git a/src/sksl/ast/SkSLASTBinaryExpression.h b/src/sksl/ast/SkSLASTBinaryExpression.h
new file mode 100644
index 0000000000..88feba66a7
--- /dev/null
+++ b/src/sksl/ast/SkSLASTBinaryExpression.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTBINARYEXPRESSION
+#define SKSL_ASTBINARYEXPRESSION
+
+#include "SkSLASTExpression.h"
+#include "../SkSLToken.h"
+#include <sstream>
+
+namespace SkSL {
+
+/**
+ * Represents a binary operation, with the operator represented by the token's type.
+ */
+struct ASTBinaryExpression : public ASTExpression {
+ ASTBinaryExpression(std::unique_ptr<ASTExpression> left, Token op,
+ std::unique_ptr<ASTExpression> right)
+ : INHERITED(op.fPosition, kBinary_Kind)
+ , fLeft(std::move(left))
+ , fOperator(op.fKind)
+ , fRight(std::move(right)) {}
+
+ std::string description() const override {
+ return "(" + fLeft->description() + " " + Token::OperatorName(fOperator) + " " +
+ fRight->description() + ")";
+ }
+
+ const std::unique_ptr<ASTExpression> fLeft;
+ const Token::Kind fOperator;
+ const std::unique_ptr<ASTExpression> fRight;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTBlock.h b/src/sksl/ast/SkSLASTBlock.h
new file mode 100644
index 0000000000..09450a3db8
--- /dev/null
+++ b/src/sksl/ast/SkSLASTBlock.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTBLOCK
+#define SKSL_ASTBLOCK
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * Represents a curly-braced block of statements.
+ */
+struct ASTBlock : public ASTStatement {
+ ASTBlock(Position position, std::vector<std::unique_ptr<ASTStatement>> statements)
+ : INHERITED(position, kBlock_Kind)
+ , fStatements(std::move(statements)) {}
+
+ std::string description() const override {
+ std::string result("{");
+ for (size_t i = 0; i < fStatements.size(); i++) {
+ result += "\n";
+ result += fStatements[i]->description();
+ }
+ result += "\n}\n";
+ return result;
+ }
+
+ const std::vector<std::unique_ptr<ASTStatement>> fStatements;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTBoolLiteral.h b/src/sksl/ast/SkSLASTBoolLiteral.h
new file mode 100644
index 0000000000..ff58822952
--- /dev/null
+++ b/src/sksl/ast/SkSLASTBoolLiteral.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTBOOLLITERAL
+#define SKSL_ASTBOOLLITERAL
+
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * Represents "true" or "false".
+ */
+struct ASTBoolLiteral : public ASTExpression {
+ ASTBoolLiteral(Position position, bool value)
+ : INHERITED(position, kBool_Kind)
+ , fValue(value) {}
+
+ std::string description() const override {
+ return fValue ? "true" : "false";
+ }
+
+ const bool fValue;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTBreakStatement.h b/src/sksl/ast/SkSLASTBreakStatement.h
new file mode 100644
index 0000000000..ede548cc24
--- /dev/null
+++ b/src/sksl/ast/SkSLASTBreakStatement.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTBREAKSTATEMENT
+#define SKSL_ASTBREAKSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'break' statement.
+ */
+struct ASTBreakStatement : public ASTStatement {
+ ASTBreakStatement(Position position)
+ : INHERITED(position, kBreak_Kind) {}
+
+ std::string description() const override {
+ return "break;";
+ }
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTCallSuffix.h b/src/sksl/ast/SkSLASTCallSuffix.h
new file mode 100644
index 0000000000..5cff6f6c93
--- /dev/null
+++ b/src/sksl/ast/SkSLASTCallSuffix.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTCALLSUFFIX
+#define SKSL_ASTCALLSUFFIX
+
+#include <sstream>
+#include <vector>
+#include "SkSLASTSuffix.h"
+
+namespace SkSL {
+
+/**
+ * A parenthesized list of arguments following an expression, indicating a function call.
+ */
+struct ASTCallSuffix : public ASTSuffix {
+ ASTCallSuffix(Position position, std::vector<std::unique_ptr<ASTExpression>> arguments)
+ : INHERITED(position, ASTSuffix::kCall_Kind)
+ , fArguments(std::move(arguments)) {}
+
+ std::string description() const override {
+ std::string result("(");
+ std::string separator = "";
+ for (size_t i = 0; i < fArguments.size(); ++i) {
+ result += separator;
+ separator = ", ";
+ result += fArguments[i]->description();
+ }
+ result += ")";
+ return result;
+ }
+
+ std::vector<std::unique_ptr<ASTExpression>> fArguments;
+
+ typedef ASTSuffix INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTContinueStatement.h b/src/sksl/ast/SkSLASTContinueStatement.h
new file mode 100644
index 0000000000..d5ab7a5c74
--- /dev/null
+++ b/src/sksl/ast/SkSLASTContinueStatement.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTCONTINUESTATEMENT
+#define SKSL_ASTCONTINUESTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'continue' statement.
+ */
+struct ASTContinueStatement : public ASTStatement {
+ ASTContinueStatement(Position position)
+ : INHERITED(position, kContinue_Kind) {}
+
+ std::string description() const override {
+ return "continue;";
+ }
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTDeclaration.h b/src/sksl/ast/SkSLASTDeclaration.h
new file mode 100644
index 0000000000..8b55ecf832
--- /dev/null
+++ b/src/sksl/ast/SkSLASTDeclaration.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTDECLARATION
+#define SKSL_ASTDECLARATION
+
+#include "SkSLASTPositionNode.h"
+
+namespace SkSL {
+
+/**
+ * Abstract supertype of declarations such as variables and functions.
+ */
+struct ASTDeclaration : public ASTPositionNode {
+ enum Kind {
+ kVar_Kind,
+ kFunction_Kind,
+ kInterfaceBlock_Kind,
+ kExtension_Kind
+ };
+
+ ASTDeclaration(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ Kind fKind;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTDiscardStatement.h b/src/sksl/ast/SkSLASTDiscardStatement.h
new file mode 100644
index 0000000000..4eaeec9ea4
--- /dev/null
+++ b/src/sksl/ast/SkSLASTDiscardStatement.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTDISCARDSTATEMENT
+#define SKSL_ASTDISCARDSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'discard' statement.
+ */
+struct ASTDiscardStatement : public ASTStatement {
+ ASTDiscardStatement(Position position)
+ : INHERITED(position, kDiscard_Kind) {}
+
+ std::string description() const override {
+ return "discard;";
+ }
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTDoStatement.h b/src/sksl/ast/SkSLASTDoStatement.h
new file mode 100644
index 0000000000..a952d62eb5
--- /dev/null
+++ b/src/sksl/ast/SkSLASTDoStatement.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTDOSTATEMENT
+#define SKSL_ASTDOSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'do' loop.
+ */
+struct ASTDoStatement : public ASTStatement {
+ ASTDoStatement(Position position, std::unique_ptr<ASTStatement> statement,
+ std::unique_ptr<ASTExpression> test)
+ : INHERITED(position, kDo_Kind)
+ , fStatement(std::move(statement))
+ , fTest(std::move(test)) {}
+
+ std::string description() const override {
+ return "do " + fStatement->description() + " while (" + fTest->description() + ");";
+ }
+
+ const std::unique_ptr<ASTStatement> fStatement;
+ const std::unique_ptr<ASTExpression> fTest;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTExpression.h b/src/sksl/ast/SkSLASTExpression.h
new file mode 100644
index 0000000000..8a48271042
--- /dev/null
+++ b/src/sksl/ast/SkSLASTExpression.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTEXPRESSION
+#define SKSL_ASTEXPRESSION
+
+#include "SkSLASTPositionNode.h"
+
+namespace SkSL {
+
+/**
+ * Abstract supertype of all expressions.
+ */
+struct ASTExpression : public ASTPositionNode {
+ enum Kind {
+ kFloat_Kind,
+ kIdentifier_Kind,
+ kInt_Kind,
+ kBool_Kind,
+ kPrefix_Kind,
+ kSuffix_Kind,
+ kBinary_Kind,
+ kTernary_Kind
+ };
+
+ ASTExpression(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ const Kind fKind;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTExpressionStatement.h b/src/sksl/ast/SkSLASTExpressionStatement.h
new file mode 100644
index 0000000000..450cca29fc
--- /dev/null
+++ b/src/sksl/ast/SkSLASTExpressionStatement.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTEXPRESSIONSTATEMENT
+#define SKSL_ASTEXPRESSIONSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A lone expression being used as a statement.
+ */
+struct ASTExpressionStatement : public ASTStatement {
+ ASTExpressionStatement(std::unique_ptr<ASTExpression> expression)
+ : INHERITED(expression->fPosition, kExpression_Kind)
+ , fExpression(std::move(expression)) {}
+
+ std::string description() const override {
+ return fExpression->description() + ";";
+ }
+
+ const std::unique_ptr<ASTExpression> fExpression;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTExtension.h b/src/sksl/ast/SkSLASTExtension.h
new file mode 100644
index 0000000000..896ac46c58
--- /dev/null
+++ b/src/sksl/ast/SkSLASTExtension.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTEXTENSION
+#define SKSL_ASTEXTENSION
+
+#include "SkSLASTDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * An extension declaration.
+ */
+struct ASTExtension : public ASTDeclaration {
+ ASTExtension(Position position, std::string name)
+ : INHERITED(position, kExtension_Kind)
+ , fName(std::move(name)) {}
+
+ std::string description() const override {
+ return "#extension " + fName + " : enable";
+ }
+
+ const std::string fName;
+
+ typedef ASTDeclaration INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTFieldSuffix.h b/src/sksl/ast/SkSLASTFieldSuffix.h
new file mode 100644
index 0000000000..cf141d822f
--- /dev/null
+++ b/src/sksl/ast/SkSLASTFieldSuffix.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTFIELDSUFFIX
+#define SKSL_ASTFIELDSUFFIX
+
+#include "SkSLASTSuffix.h"
+
+namespace SkSL {
+
+/**
+ * A dotted identifier of the form ".foo". We refer to these as "fields" at parse time even if it is
+ * actually vector swizzle (which looks the same to the parser).
+ */
+struct ASTFieldSuffix : public ASTSuffix {
+ ASTFieldSuffix(Position position, std::string field)
+ : INHERITED(position, ASTSuffix::kField_Kind)
+ , fField(std::move(field)) {}
+
+ std::string description() const override {
+ return "." + fField;
+ }
+
+ std::string fField;
+
+ typedef ASTSuffix INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTFloatLiteral.h b/src/sksl/ast/SkSLASTFloatLiteral.h
new file mode 100644
index 0000000000..89d43cc003
--- /dev/null
+++ b/src/sksl/ast/SkSLASTFloatLiteral.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTFLOATLITERAL
+#define SKSL_ASTFLOATLITERAL
+
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * A literal floating point number.
+ */
+struct ASTFloatLiteral : public ASTExpression {
+ ASTFloatLiteral(Position position, double value)
+ : INHERITED(position, kFloat_Kind)
+ , fValue(value) {}
+
+ std::string description() const override {
+ return to_string(fValue);
+ }
+
+ const double fValue;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTForStatement.h b/src/sksl/ast/SkSLASTForStatement.h
new file mode 100644
index 0000000000..f4f68c8f40
--- /dev/null
+++ b/src/sksl/ast/SkSLASTForStatement.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTFORSTATEMENT
+#define SKSL_ASTFORSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'for' loop.
+ */
+struct ASTForStatement : public ASTStatement {
+ ASTForStatement(Position position, std::unique_ptr<ASTStatement> initializer,
+ std::unique_ptr<ASTExpression> test, std::unique_ptr<ASTExpression> next,
+ std::unique_ptr<ASTStatement> statement)
+ : INHERITED(position, kFor_Kind)
+ , fInitializer(std::move(initializer))
+ , fTest(std::move(test))
+ , fNext(std::move(next))
+ , fStatement(std::move(statement)) {}
+
+ std::string description() const override {
+ std::string result = "for (";
+ if (fInitializer) {
+ result.append(fInitializer->description());
+ }
+ result += " ";
+ if (fTest) {
+ result.append(fTest->description());
+ }
+ result += "; ";
+ if (fNext) {
+ result.append(fNext->description());
+ }
+ result += ") ";
+ result += fStatement->description();
+ return result;
+ }
+
+ const std::unique_ptr<ASTStatement> fInitializer;
+ const std::unique_ptr<ASTExpression> fTest;
+ const std::unique_ptr<ASTExpression> fNext;
+ const std::unique_ptr<ASTStatement> fStatement;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTFunction.h b/src/sksl/ast/SkSLASTFunction.h
new file mode 100644
index 0000000000..c5c3b9ad83
--- /dev/null
+++ b/src/sksl/ast/SkSLASTFunction.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTFUNCTION
+#define SKSL_ASTFUNCTION
+
+#include "SkSLASTBlock.h"
+#include "SkSLASTDeclaration.h"
+#include "SkSLASTParameter.h"
+#include "SkSLASTType.h"
+
+namespace SkSL {
+
+/**
+ * A function declaration or definition. The fBody field will be null for declarations.
+ */
+struct ASTFunction : public ASTDeclaration {
+ ASTFunction(Position position, std::unique_ptr<ASTType> returnType, std::string name,
+ std::vector<std::unique_ptr<ASTParameter>> parameters,
+ std::unique_ptr<ASTBlock> body)
+ : INHERITED(position, kFunction_Kind)
+ , fReturnType(std::move(returnType))
+ , fName(std::move(name))
+ , fParameters(std::move(parameters))
+ , fBody(std::move(body)) {}
+
+ std::string description() const override {
+ std::string result = fReturnType->description() + " " + fName + "(";
+ for (size_t i = 0; i < fParameters.size(); i++) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += fParameters[i]->description();
+ }
+ if (fBody) {
+ result += ") " + fBody->description();
+ } else {
+ result += ");";
+ }
+ return result;
+ }
+
+ const std::unique_ptr<ASTType> fReturnType;
+ const std::string fName;
+ const std::vector<std::unique_ptr<ASTParameter>> fParameters;
+ const std::unique_ptr<ASTBlock> fBody;
+
+ typedef ASTDeclaration INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTIdentifier.h b/src/sksl/ast/SkSLASTIdentifier.h
new file mode 100644
index 0000000000..d67f64d39b
--- /dev/null
+++ b/src/sksl/ast/SkSLASTIdentifier.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTIDENTIFIER
+#define SKSL_ASTIDENTIFIER
+
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * An identifier in an expression context.
+ */
+struct ASTIdentifier : public ASTExpression {
+ ASTIdentifier(Position position, std::string text)
+ : INHERITED(position, kIdentifier_Kind)
+ , fText(std::move(text)) {}
+
+ std::string description() const override {
+ return fText;
+ }
+
+ const std::string fText;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTIfStatement.h b/src/sksl/ast/SkSLASTIfStatement.h
new file mode 100644
index 0000000000..06f663d5fb
--- /dev/null
+++ b/src/sksl/ast/SkSLASTIfStatement.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTIFSTATEMENT
+#define SKSL_ASTIFSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * An 'if' statement.
+ */
+struct ASTIfStatement : public ASTStatement {
+ ASTIfStatement(Position position, std::unique_ptr<ASTExpression> test,
+ std::unique_ptr<ASTStatement> ifTrue, std::unique_ptr<ASTStatement> ifFalse)
+ : INHERITED(position, kIf_Kind)
+ , fTest(std::move(test))
+ , fIfTrue(std::move(ifTrue))
+ , fIfFalse(std::move(ifFalse)) {}
+
+ std::string description() const override {
+ std::string result("if (");
+ result += fTest->description();
+ result += ") ";
+ result += fIfTrue->description();
+ if (fIfFalse) {
+ result += " else ";
+ result += fIfFalse->description();
+ }
+ return result;
+ }
+
+ const std::unique_ptr<ASTExpression> fTest;
+ const std::unique_ptr<ASTStatement> fIfTrue;
+ const std::unique_ptr<ASTStatement> fIfFalse;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTIndexSuffix.h b/src/sksl/ast/SkSLASTIndexSuffix.h
new file mode 100644
index 0000000000..44d91fa4c4
--- /dev/null
+++ b/src/sksl/ast/SkSLASTIndexSuffix.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTINDEXSUFFIX
+#define SKSL_ASTINDEXSUFFIX
+
+#include "SkSLASTExpression.h"
+#include "SkSLASTSuffix.h"
+
+namespace SkSL {
+
+/**
+ * A bracketed expression, as in '[0]', indicating an array access.
+ */
+struct ASTIndexSuffix : public ASTSuffix {
+ ASTIndexSuffix(std::unique_ptr<ASTExpression> expression)
+ : INHERITED(expression->fPosition, ASTSuffix::kIndex_Kind)
+ , fExpression(std::move(expression)) {}
+
+ std::string description() const override {
+ return "[" + fExpression->description() + "]";
+ }
+
+ std::unique_ptr<ASTExpression> fExpression;
+
+ typedef ASTSuffix INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTIntLiteral.h b/src/sksl/ast/SkSLASTIntLiteral.h
new file mode 100644
index 0000000000..2598847534
--- /dev/null
+++ b/src/sksl/ast/SkSLASTIntLiteral.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTINTLITERAL
+#define SKSL_ASTINTLITERAL
+
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * A literal integer. At the AST level, integer literals are always positive; a negative number will
+ * appear as a unary minus being applied to an integer literal.
+ */
+struct ASTIntLiteral : public ASTExpression {
+ ASTIntLiteral(Position position, uint64_t value)
+ : INHERITED(position, kInt_Kind)
+ , fValue(value) {}
+
+ std::string description() const override {
+ return to_string(fValue);
+ }
+
+ const uint64_t fValue;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTInterfaceBlock.h b/src/sksl/ast/SkSLASTInterfaceBlock.h
new file mode 100644
index 0000000000..f501b125ce
--- /dev/null
+++ b/src/sksl/ast/SkSLASTInterfaceBlock.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTINTERFACEBLOCK
+#define SKSL_ASTINTERFACEBLOCK
+
+#include "SkSLASTVarDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * An interface block, as in:
+ *
+ * out gl_PerVertex {
+ * layout(builtin=0) vec4 gl_Position;
+ * layout(builtin=1) float gl_PointSize;
+ * };
+ */
+struct ASTInterfaceBlock : public ASTDeclaration {
+ // valueName is empty when it was not present in the source
+ ASTInterfaceBlock(Position position,
+ ASTModifiers modifiers,
+ std::string interfaceName,
+ std::string valueName,
+ std::vector<std::unique_ptr<ASTVarDeclaration>> declarations)
+ : INHERITED(position, kInterfaceBlock_Kind)
+ , fModifiers(modifiers)
+ , fInterfaceName(std::move(interfaceName))
+ , fValueName(std::move(valueName))
+ , fDeclarations(std::move(declarations)) {}
+
+ std::string description() const override {
+ std::string result = fModifiers.description() + fInterfaceName + " {\n";
+ for (size_t i = 0; i < fDeclarations.size(); i++) {
+ result += fDeclarations[i]->description() + "\n";
+ }
+ result += "}";
+ if (fValueName.length()) {
+ result += " " + fValueName;
+ }
+ return result + ";";
+ }
+
+ const ASTModifiers fModifiers;
+ const std::string fInterfaceName;
+ const std::string fValueName;
+ const std::vector<std::unique_ptr<ASTVarDeclaration>> fDeclarations;
+
+ typedef ASTDeclaration INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTLayout.h b/src/sksl/ast/SkSLASTLayout.h
new file mode 100644
index 0000000000..487e6e9ecb
--- /dev/null
+++ b/src/sksl/ast/SkSLASTLayout.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTLAYOUT
+#define SKSL_ASTLAYOUT
+
+#include "SkSLASTNode.h"
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * Represents a layout block appearing before a variable declaration, as in:
+ *
+ * layout (location = 0) int x;
+ */
+struct ASTLayout : public ASTNode {
+ // For all parameters, a -1 means no value
+ ASTLayout(int location, int binding, int index, int set, int builtin)
+ : fLocation(location)
+ , fBinding(binding)
+ , fIndex(index)
+ , fSet(set)
+ , fBuiltin(builtin) {}
+
+ std::string description() const {
+ std::string result;
+ std::string separator;
+ if (fLocation >= 0) {
+ result += separator + "location = " + to_string(fLocation);
+ separator = ", ";
+ }
+ if (fBinding >= 0) {
+ result += separator + "binding = " + to_string(fBinding);
+ separator = ", ";
+ }
+ if (fIndex >= 0) {
+ result += separator + "index = " + to_string(fIndex);
+ separator = ", ";
+ }
+ if (fSet >= 0) {
+ result += separator + "set = " + to_string(fSet);
+ separator = ", ";
+ }
+ if (fBuiltin >= 0) {
+ result += separator + "builtin = " + to_string(fBuiltin);
+ separator = ", ";
+ }
+ if (result.length() > 0) {
+ result = "layout (" + result + ")";
+ }
+ return result;
+ }
+
+ const int fLocation;
+ const int fBinding;
+ const int fIndex;
+ const int fSet;
+ const int fBuiltin;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTModifiers.h b/src/sksl/ast/SkSLASTModifiers.h
new file mode 100644
index 0000000000..6ef29aa72a
--- /dev/null
+++ b/src/sksl/ast/SkSLASTModifiers.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTMODIFIERS
+#define SKSL_ASTMODIFIERS
+
+#include "SkSLASTLayout.h"
+#include "SkSLASTNode.h"
+
+namespace SkSL {
+
+/**
+ * A set of modifier keywords (in, out, uniform, etc.) appearing before a declaration.
+ */
+struct ASTModifiers : public ASTNode {
+ enum Flag {
+ kNo_Flag = 0,
+ kConst_Flag = 1,
+ kIn_Flag = 2,
+ kOut_Flag = 4,
+ kLowp_Flag = 8,
+ kMediump_Flag = 16,
+ kHighp_Flag = 32,
+ kUniform_Flag = 64
+ };
+
+ ASTModifiers(ASTLayout layout, int flags)
+ : fLayout(layout)
+ , fFlags(flags) {}
+
+ std::string description() const override {
+ std::string result = fLayout.description();
+ if (fFlags & kUniform_Flag) {
+ result += "uniform ";
+ }
+ if (fFlags & kConst_Flag) {
+ result += "const ";
+ }
+ if (fFlags & kLowp_Flag) {
+ result += "lowp ";
+ }
+ if (fFlags & kMediump_Flag) {
+ result += "mediump ";
+ }
+ if (fFlags & kHighp_Flag) {
+ result += "highp ";
+ }
+
+ if ((fFlags & kIn_Flag) && (fFlags & kOut_Flag)) {
+ result += "inout ";
+ } else if (fFlags & kIn_Flag) {
+ result += "in ";
+ } else if (fFlags & kOut_Flag) {
+ result += "out ";
+ }
+
+ return result;
+ }
+
+ const ASTLayout fLayout;
+ const int fFlags;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTNode.h b/src/sksl/ast/SkSLASTNode.h
new file mode 100644
index 0000000000..26be769925
--- /dev/null
+++ b/src/sksl/ast/SkSLASTNode.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTNODE
+#define SKSL_ASTNODE
+
+#include <memory>
+#include <string>
+
+namespace SkSL {
+
+/**
+ * Represents a node in the abstract syntax tree (AST). The AST is based directly on the parse tree;
+ * it is a parsed-but-not-yet-analyzed version of the program.
+ */
+struct ASTNode {
+ virtual ~ASTNode() {}
+
+ virtual std::string description() const = 0;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTParameter.h b/src/sksl/ast/SkSLASTParameter.h
new file mode 100644
index 0000000000..8f1b4535f2
--- /dev/null
+++ b/src/sksl/ast/SkSLASTParameter.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTPARAMETER
+#define SKSL_ASTPARAMETER
+
+#include "SkSLASTModifiers.h"
+#include "SkSLASTType.h"
+
+namespace SkSL {
+
+/**
+ * A declaration of a parameter, as part of a function declaration.
+ */
+struct ASTParameter : public ASTPositionNode {
+ // 'sizes' is a list of the array sizes appearing on a parameter, in source order.
+ // e.g. int x[3][1] would have sizes [3, 1].
+ ASTParameter(Position position, ASTModifiers modifiers, std::unique_ptr<ASTType> type,
+ std::string name, std::vector<int> sizes)
+ : INHERITED(position)
+ , fModifiers(modifiers)
+ , fType(std::move(type))
+ , fName(std::move(name))
+ , fSizes(std::move(sizes)) {}
+
+ std::string description() const override {
+ std::string result = fModifiers.description() + fType->description() + " " + fName;
+ for (int size : fSizes) {
+ result += "[" + to_string(size) + "]";
+ }
+ return result;
+ }
+
+ const ASTModifiers fModifiers;
+ const std::unique_ptr<ASTType> fType;
+ const std::string fName;
+ const std::vector<int> fSizes;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTPositionNode.h b/src/sksl/ast/SkSLASTPositionNode.h
new file mode 100644
index 0000000000..226b4ae4b0
--- /dev/null
+++ b/src/sksl/ast/SkSLASTPositionNode.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTPOSITIONNODE
+#define SKSL_ASTPOSITIONNODE
+
+#include "SkSLASTNode.h"
+#include "../SkSLPosition.h"
+
+namespace SkSL {
+
+/**
+ * An AST node with an associated position in the source.
+ */
+struct ASTPositionNode : public ASTNode {
+ ASTPositionNode(Position position)
+ : fPosition(position) {}
+
+ const Position fPosition;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTPrefixExpression.h b/src/sksl/ast/SkSLASTPrefixExpression.h
new file mode 100644
index 0000000000..0d326e2aab
--- /dev/null
+++ b/src/sksl/ast/SkSLASTPrefixExpression.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTPREFIXEXPRESSION
+#define SKSL_ASTPREFIXEXPRESSION
+
+#include "SkSLASTExpression.h"
+#include "../SkSLToken.h"
+
+namespace SkSL {
+
+/**
+ * An expression modified by a unary operator appearing in front of it, such as '-x' or '!inside'.
+ */
+struct ASTPrefixExpression : public ASTExpression {
+ ASTPrefixExpression(Token op, std::unique_ptr<ASTExpression> operand)
+ : INHERITED(op.fPosition, kPrefix_Kind)
+ , fOperator(op.fKind)
+ , fOperand(std::move(operand)) {}
+
+ std::string description() const override {
+ return Token::OperatorName(fOperator) + fOperand->description();
+ }
+
+ const Token::Kind fOperator;
+ const std::unique_ptr<ASTExpression> fOperand;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTReturnStatement.h b/src/sksl/ast/SkSLASTReturnStatement.h
new file mode 100644
index 0000000000..3aac783a8c
--- /dev/null
+++ b/src/sksl/ast/SkSLASTReturnStatement.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTRETURNSTATEMENT
+#define SKSL_ASTRETURNSTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'return' statement.
+ */
+struct ASTReturnStatement : public ASTStatement {
+ // expression may be null
+ ASTReturnStatement(Position position, std::unique_ptr<ASTExpression> expression)
+ : INHERITED(position, kReturn_Kind)
+ , fExpression(std::move(expression)) {}
+
+ std::string description() const override {
+ std::string result("return");
+ if (fExpression) {
+ result += " " + fExpression->description();
+ }
+ return result + ";";
+ }
+
+ const std::unique_ptr<ASTExpression> fExpression;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTStatement.h b/src/sksl/ast/SkSLASTStatement.h
new file mode 100644
index 0000000000..9ddde063ea
--- /dev/null
+++ b/src/sksl/ast/SkSLASTStatement.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTSTATEMENT
+#define SKSL_ASTSTATEMENT
+
+#include <vector>
+#include "SkSLASTPositionNode.h"
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * Abstract supertype of all statements.
+ */
+struct ASTStatement : public ASTPositionNode {
+ enum Kind {
+ kBlock_Kind,
+ kVarDeclaration_Kind,
+ kExpression_Kind,
+ kIf_Kind,
+ kFor_Kind,
+ kWhile_Kind,
+ kDo_Kind,
+ kReturn_Kind,
+ kBreak_Kind,
+ kContinue_Kind,
+ kDiscard_Kind
+ };
+
+ ASTStatement(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ Kind fKind;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTSuffix.h b/src/sksl/ast/SkSLASTSuffix.h
new file mode 100644
index 0000000000..18f79f01ea
--- /dev/null
+++ b/src/sksl/ast/SkSLASTSuffix.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTSUFFIX
+#define SKSL_ASTSUFFIX
+
+#include "SkSLASTPositionNode.h"
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * This and its subclasses represents expression suffixes, such as '[0]' or '.rgb'. Suffixes are not
+ * expressions in and of themselves; they are attached to expressions to modify them.
+ */
+struct ASTSuffix : public ASTPositionNode {
+ enum Kind {
+ kIndex_Kind,
+ kCall_Kind,
+ kField_Kind,
+ kPostIncrement_Kind,
+ kPostDecrement_Kind
+ };
+
+ ASTSuffix(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ std::string description() const override {
+ switch (fKind) {
+ case kPostIncrement_Kind:
+ return "++";
+ case kPostDecrement_Kind:
+ return "--";
+ default:
+ ABORT("unsupported suffix operator");
+ }
+ }
+
+ Kind fKind;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTSuffixExpression.h b/src/sksl/ast/SkSLASTSuffixExpression.h
new file mode 100644
index 0000000000..c0fda294b9
--- /dev/null
+++ b/src/sksl/ast/SkSLASTSuffixExpression.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTSUFFIXEXPRESSION
+#define SKSL_ASTSUFFIXEXPRESSION
+
+#include "SkSLASTSuffix.h"
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * An expression with an associated suffix.
+ */
+struct ASTSuffixExpression : public ASTExpression {
+ ASTSuffixExpression(std::unique_ptr<ASTExpression> base, std::unique_ptr<ASTSuffix> suffix)
+ : INHERITED(base->fPosition, kSuffix_Kind)
+ , fBase(std::move(base))
+ , fSuffix(std::move(suffix)) {}
+
+ std::string description() const override {
+ return fBase->description() + fSuffix->description();
+ }
+
+ const std::unique_ptr<ASTExpression> fBase;
+ const std::unique_ptr<ASTSuffix> fSuffix;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTTernaryExpression.h b/src/sksl/ast/SkSLASTTernaryExpression.h
new file mode 100644
index 0000000000..20b827a049
--- /dev/null
+++ b/src/sksl/ast/SkSLASTTernaryExpression.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTTERNARYEXPRESSION
+#define SKSL_ASTTERNARYEXPRESSION
+
+#include "SkSLASTExpression.h"
+
+namespace SkSL {
+
+/**
+ * A ternary expression (test ? ifTrue : ifFalse).
+ */
+struct ASTTernaryExpression : public ASTExpression {
+ ASTTernaryExpression(std::unique_ptr<ASTExpression> test,
+ std::unique_ptr<ASTExpression> ifTrue,
+ std::unique_ptr<ASTExpression> ifFalse)
+ : INHERITED(test->fPosition, kTernary_Kind)
+ , fTest(std::move(test))
+ , fIfTrue(std::move(ifTrue))
+ , fIfFalse(std::move(ifFalse)) {}
+
+ std::string description() const override {
+ return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " +
+ fIfFalse->description() + ")";
+ }
+
+ const std::unique_ptr<ASTExpression> fTest;
+ const std::unique_ptr<ASTExpression> fIfTrue;
+ const std::unique_ptr<ASTExpression> fIfFalse;
+
+ typedef ASTExpression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTType.h b/src/sksl/ast/SkSLASTType.h
new file mode 100644
index 0000000000..b8fdedb214
--- /dev/null
+++ b/src/sksl/ast/SkSLASTType.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTTYPE
+#define SKSL_ASTTYPE
+
+namespace SkSL {
+
+/**
+ * A type, such as 'int' or 'struct foo'.
+ */
+struct ASTType : public ASTPositionNode {
+ enum Kind {
+ kIdentifier_Kind,
+ kStruct_Kind
+ };
+
+ ASTType(Position position, std::string name, Kind kind)
+ : INHERITED(position)
+ , fName(std::move(name))
+ , fKind(kind) {}
+
+ std::string description() const override {
+ return fName;
+ }
+
+ const std::string fName;
+
+ const Kind fKind;
+
+ typedef ASTPositionNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTVarDeclaration.h b/src/sksl/ast/SkSLASTVarDeclaration.h
new file mode 100644
index 0000000000..613867e136
--- /dev/null
+++ b/src/sksl/ast/SkSLASTVarDeclaration.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTVARDECLARATION
+#define SKSL_ASTVARDECLARATION
+
+#include "SkSLASTDeclaration.h"
+#include "SkSLASTModifiers.h"
+#include "SkSLASTStatement.h"
+#include "SkSLASTType.h"
+#include "../SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * A variable declaration, which may consist of multiple individual variables. For instance
+ * 'int x, y = 1, z[4][2]' is a single ASTVarDeclaration. This declaration would have a type of
+ * 'int', names ['x', 'y', 'z'], sizes of [[], [], [4, 2]], and values of [null, 1, null].
+ */
+struct ASTVarDeclaration : public ASTDeclaration {
+ ASTVarDeclaration(ASTModifiers modifiers,
+ std::unique_ptr<ASTType> type,
+ std::vector<std::string> names,
+ std::vector<std::vector<std::unique_ptr<ASTExpression>>> sizes,
+ std::vector<std::unique_ptr<ASTExpression>> values)
+ : INHERITED(type->fPosition, kVar_Kind)
+ , fModifiers(modifiers)
+ , fType(std::move(type))
+ , fNames(std::move(names))
+ , fSizes(std::move(sizes))
+ , fValues(std::move(values)) {
+ ASSERT(fNames.size() == fValues.size());
+ }
+
+ std::string description() const override {
+ std::string result = fModifiers.description() + fType->description() + " ";
+ std::string separator = "";
+ for (size_t i = 0; i < fNames.size(); i++) {
+ result += separator;
+ separator = ", ";
+ result += fNames[i];
+ for (size_t j = 0; j < fSizes[i].size(); j++) {
+ if (fSizes[i][j]) {
+ result += "[" + fSizes[i][j]->description() + "]";
+ } else {
+ result += "[]";
+ }
+ }
+ if (fValues[i]) {
+ result += " = " + fValues[i]->description();
+ }
+ }
+ return result;
+ }
+
+ const ASTModifiers fModifiers;
+ const std::unique_ptr<ASTType> fType;
+ const std::vector<std::string> fNames;
+ const std::vector<std::vector<std::unique_ptr<ASTExpression>>> fSizes;
+ const std::vector<std::unique_ptr<ASTExpression>> fValues;
+
+ typedef ASTDeclaration INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTVarDeclarationStatement.h b/src/sksl/ast/SkSLASTVarDeclarationStatement.h
new file mode 100644
index 0000000000..b647b6e52f
--- /dev/null
+++ b/src/sksl/ast/SkSLASTVarDeclarationStatement.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTVARDECLARATIONSTATEMENT
+#define SKSL_ASTVARDECLARATIONSTATEMENT
+
+#include "SkSLASTStatement.h"
+#include "SkSLASTVarDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * A variable declaration appearing as a statement within a function.
+ */
+struct ASTVarDeclarationStatement : public ASTStatement {
+ ASTVarDeclarationStatement(std::unique_ptr<ASTVarDeclaration> decl)
+ : INHERITED(decl->fPosition, kVarDeclaration_Kind)
+ , fDeclaration(std::move(decl)) {}
+
+ std::string description() const override {
+ return fDeclaration->description() + ";";
+ }
+
+ std::unique_ptr<ASTVarDeclaration> fDeclaration;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ast/SkSLASTWhileStatement.h b/src/sksl/ast/SkSLASTWhileStatement.h
new file mode 100644
index 0000000000..e29aa23e4a
--- /dev/null
+++ b/src/sksl/ast/SkSLASTWhileStatement.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_ASTWHILESTATEMENT
+#define SKSL_ASTWHILESTATEMENT
+
+#include "SkSLASTStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'while' statement.
+ */
+struct ASTWhileStatement : public ASTStatement {
+ ASTWhileStatement(Position position, std::unique_ptr<ASTExpression> test,
+ std::unique_ptr<ASTStatement> statement)
+ : INHERITED(position, kWhile_Kind)
+ , fTest(std::move(test))
+ , fStatement(std::move(statement)) {}
+
+ std::string description() const override {
+ return "while (" + fTest->description() + ") " + fStatement->description();
+ }
+
+ const std::unique_ptr<ASTExpression> fTest;
+ const std::unique_ptr<ASTStatement> fStatement;
+
+ typedef ASTStatement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLBinaryExpression.h b/src/sksl/ir/SkSLBinaryExpression.h
new file mode 100644
index 0000000000..bd89d6c602
--- /dev/null
+++ b/src/sksl/ir/SkSLBinaryExpression.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BINARYEXPRESSION
+#define SKSL_BINARYEXPRESSION
+
+#include "SkSLExpression.h"
+#include "../SkSLToken.h"
+
+namespace SkSL {
+
+/**
+ * A binary operation.
+ */
+struct BinaryExpression : public Expression {
+ BinaryExpression(Position position, std::unique_ptr<Expression> left, Token::Kind op,
+ std::unique_ptr<Expression> right, std::shared_ptr<Type> type)
+ : INHERITED(position, kBinary_Kind, type)
+ , fLeft(std::move(left))
+ , fOperator(op)
+ , fRight(std::move(right)) {}
+
+ virtual std::string description() const override {
+ return "(" + fLeft->description() + " " + Token::OperatorName(fOperator) + " " +
+ fRight->description() + ")";
+ }
+
+ const std::unique_ptr<Expression> fLeft;
+ const Token::Kind fOperator;
+ const std::unique_ptr<Expression> fRight;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLBlock.h b/src/sksl/ir/SkSLBlock.h
new file mode 100644
index 0000000000..56ed77a0ba
--- /dev/null
+++ b/src/sksl/ir/SkSLBlock.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BLOCK
+#define SKSL_BLOCK
+
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A block of multiple statements functioning as a single statement.
+ */
+struct Block : public Statement {
+ Block(Position position, std::vector<std::unique_ptr<Statement>> statements)
+ : INHERITED(position, kBlock_Kind)
+ , fStatements(std::move(statements)) {}
+
+ std::string description() const override {
+ std::string result = "{";
+ for (size_t i = 0; i < fStatements.size(); i++) {
+ result += "\n";
+ result += fStatements[i]->description();
+ }
+ result += "\n}\n";
+ return result;
+ }
+
+ const std::vector<std::unique_ptr<Statement>> fStatements;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLBoolLiteral.h b/src/sksl/ir/SkSLBoolLiteral.h
new file mode 100644
index 0000000000..3c40e59514
--- /dev/null
+++ b/src/sksl/ir/SkSLBoolLiteral.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BOOLLITERAL
+#define SKSL_BOOLLITERAL
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * Represents 'true' or 'false'.
+ */
+struct BoolLiteral : public Expression {
+ BoolLiteral(Position position, bool value)
+ : INHERITED(position, kBoolLiteral_Kind, kBool_Type)
+ , fValue(value) {}
+
+ std::string description() const override {
+ return fValue ? "true" : "false";
+ }
+
+ bool isConstant() const override {
+ return true;
+ }
+
+ const bool fValue;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLBreakStatement.h b/src/sksl/ir/SkSLBreakStatement.h
new file mode 100644
index 0000000000..8aa17b096b
--- /dev/null
+++ b/src/sksl/ir/SkSLBreakStatement.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BREAKSTATEMENT
+#define SKSL_BREAKSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'break' statement.
+ */
+struct BreakStatement : public Statement {
+ BreakStatement(Position position)
+ : INHERITED(position, kBreak_Kind) {}
+
+ std::string description() const override {
+ return "break;";
+ }
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLConstructor.h b/src/sksl/ir/SkSLConstructor.h
new file mode 100644
index 0000000000..c58da7e5b8
--- /dev/null
+++ b/src/sksl/ir/SkSLConstructor.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_CONSTRUCTOR
+#define SKSL_CONSTRUCTOR
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * Represents the construction of a compound type, such as "vec2(x, y)".
+ */
+struct Constructor : public Expression {
+ Constructor(Position position, std::shared_ptr<Type> type,
+ std::vector<std::unique_ptr<Expression>> arguments)
+ : INHERITED(position, kConstructor_Kind, std::move(type))
+ , fArguments(std::move(arguments)) {}
+
+ std::string description() const override {
+ std::string result = fType->description() + "(";
+ std::string separator = "";
+ for (size_t i = 0; i < fArguments.size(); i++) {
+ result += separator;
+ result += fArguments[i]->description();
+ separator = ", ";
+ }
+ result += ")";
+ return result;
+ }
+
+ bool isConstant() const override {
+ for (size_t i = 0; i < fArguments.size(); i++) {
+ if (!fArguments[i]->isConstant()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ const std::vector<std::unique_ptr<Expression>> fArguments;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLContinueStatement.h b/src/sksl/ir/SkSLContinueStatement.h
new file mode 100644
index 0000000000..1951bd990a
--- /dev/null
+++ b/src/sksl/ir/SkSLContinueStatement.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_CONTINUESTATEMENT
+#define SKSL_CONTINUESTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'continue' statement.
+ */
+struct ContinueStatement : public Statement {
+ ContinueStatement(Position position)
+ : INHERITED(position, kContinue_Kind) {}
+
+ std::string description() const override {
+ return "continue;";
+ }
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLDiscardStatement.h b/src/sksl/ir/SkSLDiscardStatement.h
new file mode 100644
index 0000000000..b39712ebd1
--- /dev/null
+++ b/src/sksl/ir/SkSLDiscardStatement.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_DISCARDSTATEMENT
+#define SKSL_DISCARDSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'discard' statement.
+ */
+struct DiscardStatement : public Statement {
+ DiscardStatement(Position position)
+ : INHERITED(position, kDiscard_Kind) {}
+
+ std::string description() const override {
+ return "discard;";
+ }
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLDoStatement.h b/src/sksl/ir/SkSLDoStatement.h
new file mode 100644
index 0000000000..6012453277
--- /dev/null
+++ b/src/sksl/ir/SkSLDoStatement.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_DOSTATEMENT
+#define SKSL_DOSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'do' statement.
+ */
+struct DoStatement : public Statement {
+ DoStatement(Position position, std::unique_ptr<Statement> statement,
+ std::unique_ptr<Expression> test)
+ : INHERITED(position, kDo_Kind)
+ , fStatement(std::move(statement))
+ , fTest(std::move(test)) {}
+
+ std::string description() const override {
+ return "do " + fStatement->description() + " while (" + fTest->description() + ");";
+ }
+
+ const std::unique_ptr<Statement> fStatement;
+ const std::unique_ptr<Expression> fTest;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
new file mode 100644
index 0000000000..1e42c7a475
--- /dev/null
+++ b/src/sksl/ir/SkSLExpression.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_EXPRESSION
+#define SKSL_EXPRESSION
+
+#include "SkSLIRNode.h"
+#include "SkSLType.h"
+
+namespace SkSL {
+
+/**
+ * Abstract supertype of all expressions.
+ */
+struct Expression : public IRNode {
+ enum Kind {
+ kBinary_Kind,
+ kBoolLiteral_Kind,
+ kConstructor_Kind,
+ kIntLiteral_Kind,
+ kFieldAccess_Kind,
+ kFloatLiteral_Kind,
+ kFunctionReference_Kind,
+ kFunctionCall_Kind,
+ kIndex_Kind,
+ kPrefix_Kind,
+ kPostfix_Kind,
+ kSwizzle_Kind,
+ kVariableReference_Kind,
+ kTernary_Kind,
+ kTypeReference_Kind,
+ };
+
+ Expression(Position position, Kind kind, std::shared_ptr<Type> type)
+ : INHERITED(position)
+ , fKind(kind)
+ , fType(std::move(type)) {}
+
+ virtual bool isConstant() const {
+ return false;
+ }
+
+ const Kind fKind;
+ const std::shared_ptr<Type> fType;
+
+ typedef IRNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLExpressionStatement.h b/src/sksl/ir/SkSLExpressionStatement.h
new file mode 100644
index 0000000000..e975ccf2ac
--- /dev/null
+++ b/src/sksl/ir/SkSLExpressionStatement.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_EXPRESSIONSTATEMENT
+#define SKSL_EXPRESSIONSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A lone expression being used as a statement.
+ */
+struct ExpressionStatement : public Statement {
+ ExpressionStatement(std::unique_ptr<Expression> expression)
+ : INHERITED(expression->fPosition, kExpression_Kind)
+ , fExpression(std::move(expression)) {}
+
+ std::string description() const override {
+ return fExpression->description() + ";";
+ }
+
+ const std::unique_ptr<Expression> fExpression;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLExtension.h b/src/sksl/ir/SkSLExtension.h
new file mode 100644
index 0000000000..d7f83fad8a
--- /dev/null
+++ b/src/sksl/ir/SkSLExtension.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_EXTENSION
+#define SKSL_EXTENSION
+
+#include "SkSLProgramElement.h"
+
+namespace SkSL {
+
+/**
+ * An extension declaration.
+ */
+struct Extension : public ProgramElement {
+ Extension(Position position, std::string name)
+ : INHERITED(position, kExtension_Kind)
+ , fName(std::move(name)) {}
+
+ std::string description() const override {
+ return "#extension " + fName + " : enable";
+ }
+
+ const std::string fName;
+
+ typedef ProgramElement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLField.h b/src/sksl/ir/SkSLField.h
new file mode 100644
index 0000000000..f2b68bc2bc
--- /dev/null
+++ b/src/sksl/ir/SkSLField.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FIELD
+#define SKSL_FIELD
+
+#include "SkSLModifiers.h"
+#include "SkSLPosition.h"
+#include "SkSLSymbol.h"
+#include "SkSLType.h"
+
+namespace SkSL {
+
+/**
+ * A symbol which should be interpreted as a field access. Fields are added to the symboltable
+ * whenever a bare reference to an identifier should refer to a struct field; in GLSL, this is the
+ * result of declaring anonymous interface blocks.
+ */
+struct Field : public Symbol {
+ Field(Position position, std::shared_ptr<Variable> owner, int fieldIndex)
+ : INHERITED(position, kField_Kind, owner->fType->fields()[fieldIndex].fName)
+ , fOwner(owner)
+ , fFieldIndex(fieldIndex) {}
+
+ virtual std::string description() const override {
+ return fOwner->description() + "." + fOwner->fType->fields()[fFieldIndex].fName;
+ }
+
+ const std::shared_ptr<Variable> fOwner;
+ const int fFieldIndex;
+
+ typedef Symbol INHERITED;
+};
+
+} // namespace SkSL
+
+#endif
diff --git a/src/sksl/ir/SkSLFieldAccess.h b/src/sksl/ir/SkSLFieldAccess.h
new file mode 100644
index 0000000000..053498e154
--- /dev/null
+++ b/src/sksl/ir/SkSLFieldAccess.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FIELDACCESS
+#define SKSL_FIELDACCESS
+
+#include "SkSLExpression.h"
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * An expression which extracts a field from a struct, as in 'foo.bar'.
+ */
+struct FieldAccess : public Expression {
+ FieldAccess(std::unique_ptr<Expression> base, int fieldIndex)
+ : INHERITED(base->fPosition, kFieldAccess_Kind, base->fType->fields()[fieldIndex].fType)
+ , fBase(std::move(base))
+ , fFieldIndex(fieldIndex) {}
+
+ virtual std::string description() const override {
+ return fBase->description() + "." + fBase->fType->fields()[fFieldIndex].fName;
+ }
+
+ const std::unique_ptr<Expression> fBase;
+ const int fFieldIndex;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLFloatLiteral.h b/src/sksl/ir/SkSLFloatLiteral.h
new file mode 100644
index 0000000000..deb5b27144
--- /dev/null
+++ b/src/sksl/ir/SkSLFloatLiteral.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FLOATLITERAL
+#define SKSL_FLOATLITERAL
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * A literal floating point number.
+ */
+struct FloatLiteral : public Expression {
+ FloatLiteral(Position position, double value)
+ : INHERITED(position, kFloatLiteral_Kind, kFloat_Type)
+ , fValue(value) {}
+
+ virtual std::string description() const override {
+ return to_string(fValue);
+ }
+
+ bool isConstant() const override {
+ return true;
+ }
+
+ const double fValue;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLForStatement.h b/src/sksl/ir/SkSLForStatement.h
new file mode 100644
index 0000000000..70bb4014c8
--- /dev/null
+++ b/src/sksl/ir/SkSLForStatement.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FORSTATEMENT
+#define SKSL_FORSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'for' statement.
+ */
+struct ForStatement : public Statement {
+ ForStatement(Position position, std::unique_ptr<Statement> initializer,
+ std::unique_ptr<Expression> test, std::unique_ptr<Expression> next,
+ std::unique_ptr<Statement> statement)
+ : INHERITED(position, kFor_Kind)
+ , fInitializer(std::move(initializer))
+ , fTest(std::move(test))
+ , fNext(std::move(next))
+ , fStatement(std::move(statement)) {}
+
+ std::string description() const override {
+ std::string result = "for (";
+ if (fInitializer) {
+ result += fInitializer->description();
+ }
+ result += " ";
+ if (fTest) {
+ result += fTest->description();
+ }
+ result += "; ";
+ if (fNext) {
+ result += fNext->description();
+ }
+ result += ") " + fStatement->description();
+ return result;
+ }
+
+ const std::unique_ptr<Statement> fInitializer;
+ const std::unique_ptr<Expression> fTest;
+ const std::unique_ptr<Expression> fNext;
+ const std::unique_ptr<Statement> fStatement;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLFunctionCall.h b/src/sksl/ir/SkSLFunctionCall.h
new file mode 100644
index 0000000000..78d2566227
--- /dev/null
+++ b/src/sksl/ir/SkSLFunctionCall.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FUNCTIONCALL
+#define SKSL_FUNCTIONCALL
+
+#include "SkSLExpression.h"
+#include "SkSLFunctionDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * A function invocation.
+ */
+struct FunctionCall : public Expression {
+ FunctionCall(Position position, std::shared_ptr<FunctionDeclaration> function,
+ std::vector<std::unique_ptr<Expression>> arguments)
+ : INHERITED(position, kFunctionCall_Kind, function->fReturnType)
+ , fFunction(std::move(function))
+ , fArguments(std::move(arguments)) {}
+
+ std::string description() const override {
+ std::string result = fFunction->fName + "(";
+ std::string separator = "";
+ for (size_t i = 0; i < fArguments.size(); i++) {
+ result += separator;
+ result += fArguments[i]->description();
+ separator = ", ";
+ }
+ result += ")";
+ return result;
+ }
+
+ const std::shared_ptr<FunctionDeclaration> fFunction;
+ const std::vector<std::unique_ptr<Expression>> fArguments;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.h b/src/sksl/ir/SkSLFunctionDeclaration.h
new file mode 100644
index 0000000000..32c23f545e
--- /dev/null
+++ b/src/sksl/ir/SkSLFunctionDeclaration.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FUNCTIONDECLARATION
+#define SKSL_FUNCTIONDECLARATION
+
+#include "SkSLModifiers.h"
+#include "SkSLSymbol.h"
+#include "SkSLType.h"
+#include "SkSLVariable.h"
+
+namespace SkSL {
+
+/**
+ * A function declaration (not a definition -- does not contain a body).
+ */
+struct FunctionDeclaration : public Symbol {
+ FunctionDeclaration(Position position, std::string name,
+ std::vector<std::shared_ptr<Variable>> parameters,
+ std::shared_ptr<Type> returnType)
+ : INHERITED(position, kFunctionDeclaration_Kind, std::move(name))
+ , fDefined(false)
+ , fParameters(parameters)
+ , fReturnType(returnType) {}
+
+ std::string description() const override {
+ std::string result = fReturnType->description() + " " + fName + "(";
+ std::string separator = "";
+ for (auto p : fParameters) {
+ result += separator;
+ separator = ", ";
+ result += p->description();
+ }
+ result += ")";
+ return result;
+ }
+
+ bool matches(FunctionDeclaration& f) {
+ return fName == f.fName && fParameters == f.fParameters;
+ }
+
+ mutable bool fDefined;
+ const std::vector<std::shared_ptr<Variable>> fParameters;
+ const std::shared_ptr<Type> fReturnType;
+
+ typedef Symbol INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLFunctionDefinition.h b/src/sksl/ir/SkSLFunctionDefinition.h
new file mode 100644
index 0000000000..fceb5474cb
--- /dev/null
+++ b/src/sksl/ir/SkSLFunctionDefinition.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FUNCTIONDEFINITION
+#define SKSL_FUNCTIONDEFINITION
+
+#include "SkSLBlock.h"
+#include "SkSLFunctionDeclaration.h"
+#include "SkSLProgramElement.h"
+
+namespace SkSL {
+
+/**
+ * A function definition (a declaration plus an associated block of code).
+ */
+struct FunctionDefinition : public ProgramElement {
+ FunctionDefinition(Position position, std::shared_ptr<FunctionDeclaration> declaration,
+ std::unique_ptr<Block> body)
+ : INHERITED(position, kFunction_Kind)
+ , fDeclaration(std::move(declaration))
+ , fBody(std::move(body)) {}
+
+ std::string description() const override {
+ return fDeclaration->description() + " " + fBody->description();
+ }
+
+ const std::shared_ptr<FunctionDeclaration> fDeclaration;
+ const std::unique_ptr<Block> fBody;
+
+ typedef ProgramElement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLFunctionReference.h b/src/sksl/ir/SkSLFunctionReference.h
new file mode 100644
index 0000000000..d5cc444000
--- /dev/null
+++ b/src/sksl/ir/SkSLFunctionReference.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_FUNCTIONREFERENCE
+#define SKSL_FUNCTIONREFERENCE
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * An identifier referring to a function name. This is an intermediate value: FunctionReferences are
+ * always eventually replaced by FunctionCalls in valid programs.
+ */
+struct FunctionReference : public Expression {
+ FunctionReference(Position position, std::vector<std::shared_ptr<FunctionDeclaration>> function)
+ : INHERITED(position, kFunctionReference_Kind, kInvalid_Type)
+ , fFunctions(function) {}
+
+ virtual std::string description() const override {
+ ASSERT(false);
+ return "<function>";
+ }
+
+ const std::vector<std::shared_ptr<FunctionDeclaration>> fFunctions;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLIRNode.h b/src/sksl/ir/SkSLIRNode.h
new file mode 100644
index 0000000000..8c433cfc6b
--- /dev/null
+++ b/src/sksl/ir/SkSLIRNode.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_IRNODE
+#define SKSL_IRNODE
+
+#include "../SkSLPosition.h"
+
+namespace SkSL {
+
+/**
+ * Represents a node in the intermediate representation (IR) tree. The IR is a fully-resolved
+ * version of the program (all types determined, everything validated), ready for code generation.
+ */
+struct IRNode {
+ IRNode(Position position)
+ : fPosition(position) {}
+
+ virtual ~IRNode() {}
+
+ virtual std::string description() const = 0;
+
+ const Position fPosition;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLIfStatement.h b/src/sksl/ir/SkSLIfStatement.h
new file mode 100644
index 0000000000..8ab5c00fd7
--- /dev/null
+++ b/src/sksl/ir/SkSLIfStatement.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_IFSTATEMENT
+#define SKSL_IFSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * An 'if' statement.
+ */
+struct IfStatement : public Statement {
+ IfStatement(Position position, std::unique_ptr<Expression> test,
+ std::unique_ptr<Statement> ifTrue, std::unique_ptr<Statement> ifFalse)
+ : INHERITED(position, kIf_Kind)
+ , fTest(std::move(test))
+ , fIfTrue(std::move(ifTrue))
+ , fIfFalse(std::move(ifFalse)) {}
+
+ std::string description() const override {
+ std::string result = "if (" + fTest->description() + ") " + fIfTrue->description();
+ if (fIfFalse) {
+ result += " else " + fIfFalse->description();
+ }
+ return result;
+ }
+
+ const std::unique_ptr<Expression> fTest;
+ const std::unique_ptr<Statement> fIfTrue;
+ const std::unique_ptr<Statement> fIfFalse;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLIndexExpression.h b/src/sksl/ir/SkSLIndexExpression.h
new file mode 100644
index 0000000000..538c656153
--- /dev/null
+++ b/src/sksl/ir/SkSLIndexExpression.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_INDEX
+#define SKSL_INDEX
+
+#include "SkSLExpression.h"
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * Given a type, returns the type that will result from extracting an array value from it.
+ */
+static std::shared_ptr<Type> index_type(const Type& type) {
+ if (type.kind() == Type::kMatrix_Kind) {
+ if (type.componentType() == kFloat_Type) {
+ switch (type.columns()) {
+ case 2: return kVec2_Type;
+ case 3: return kVec3_Type;
+ case 4: return kVec4_Type;
+ default: ASSERT(false);
+ }
+ } else {
+ ASSERT(type.componentType() == kDouble_Type);
+ switch (type.columns()) {
+ case 2: return kDVec2_Type;
+ case 3: return kDVec3_Type;
+ case 4: return kDVec4_Type;
+ default: ASSERT(false);
+ }
+ }
+ }
+ return type.componentType();
+}
+
+/**
+ * An expression which extracts a value from an array or matrix, as in 'm[2]'.
+ */
+struct IndexExpression : public Expression {
+ IndexExpression(std::unique_ptr<Expression> base, std::unique_ptr<Expression> index)
+ : INHERITED(base->fPosition, kIndex_Kind, index_type(*base->fType))
+ , fBase(std::move(base))
+ , fIndex(std::move(index)) {
+ ASSERT(fIndex->fType == kInt_Type);
+ }
+
+ std::string description() const override {
+ return fBase->description() + "[" + fIndex->description() + "]";
+ }
+
+ const std::unique_ptr<Expression> fBase;
+ const std::unique_ptr<Expression> fIndex;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLIntLiteral.h b/src/sksl/ir/SkSLIntLiteral.h
new file mode 100644
index 0000000000..80b30d7c05
--- /dev/null
+++ b/src/sksl/ir/SkSLIntLiteral.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_INTLITERAL
+#define SKSL_INTLITERAL
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * A literal integer.
+ */
+struct IntLiteral : public Expression {
+ // FIXME: we will need to revisit this if/when we add full support for both signed and unsigned
+ // 64-bit integers, but for right now an int64_t will hold every value we care about
+ IntLiteral(Position position, int64_t value)
+ : INHERITED(position, kIntLiteral_Kind, kInt_Type)
+ , fValue(value) {}
+
+ virtual std::string description() const override {
+ return to_string(fValue);
+ }
+
+ bool isConstant() const override {
+ return true;
+ }
+
+ const int64_t fValue;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLInterfaceBlock.h b/src/sksl/ir/SkSLInterfaceBlock.h
new file mode 100644
index 0000000000..baedb5864c
--- /dev/null
+++ b/src/sksl/ir/SkSLInterfaceBlock.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_INTERFACEBLOCK
+#define SKSL_INTERFACEBLOCK
+
+#include "SkSLProgramElement.h"
+#include "SkSLVarDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * An interface block, as in:
+ *
+ * out gl_PerVertex {
+ * layout(builtin=0) vec4 gl_Position;
+ * layout(builtin=1) float gl_PointSize;
+ * };
+ *
+ * At the IR level, this is represented by a single variable of struct type.
+ */
+struct InterfaceBlock : public ProgramElement {
+ InterfaceBlock(Position position, std::shared_ptr<Variable> var)
+ : INHERITED(position, kInterfaceBlock_Kind)
+ , fVariable(std::move(var)) {
+ ASSERT(fVariable->fType->kind() == Type::kStruct_Kind);
+ }
+
+ std::string description() const override {
+ std::string result = fVariable->fModifiers.description() + fVariable->fName + " {\n";
+ for (size_t i = 0; i < fVariable->fType->fields().size(); i++) {
+ result += fVariable->fType->fields()[i].description() + "\n";
+ }
+ result += "};";
+ return result;
+ }
+
+ const std::shared_ptr<Variable> fVariable;
+
+ typedef ProgramElement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
new file mode 100644
index 0000000000..bab2f0e0db
--- /dev/null
+++ b/src/sksl/ir/SkSLLayout.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_LAYOUT
+#define SKSL_LAYOUT
+
+namespace SkSL {
+
+/**
+ * Represents a layout block appearing before a variable declaration, as in:
+ *
+ * layout (location = 0) int x;
+ */
+struct Layout {
+ Layout(const ASTLayout& layout)
+ : fLocation(layout.fLocation)
+ , fBinding(layout.fBinding)
+ , fIndex(layout.fIndex)
+ , fSet(layout.fSet)
+ , fBuiltin(layout.fBuiltin) {}
+
+ Layout(int location, int binding, int index, int set, int builtin)
+ : fLocation(location)
+ , fBinding(binding)
+ , fIndex(index)
+ , fSet(set)
+ , fBuiltin(builtin) {}
+
+ std::string description() const {
+ std::string result;
+ std::string separator;
+ if (fLocation >= 0) {
+ result += separator + "location = " + to_string(fLocation);
+ separator = ", ";
+ }
+ if (fBinding >= 0) {
+ result += separator + "binding = " + to_string(fBinding);
+ separator = ", ";
+ }
+ if (fIndex >= 0) {
+ result += separator + "index = " + to_string(fIndex);
+ separator = ", ";
+ }
+ if (fSet >= 0) {
+ result += separator + "set = " + to_string(fSet);
+ separator = ", ";
+ }
+ if (fBuiltin >= 0) {
+ result += separator + "builtin = " + to_string(fBuiltin);
+ separator = ", ";
+ }
+ if (result.length() > 0) {
+ result = "layout (" + result + ")";
+ }
+ return result;
+ }
+
+ bool operator==(const Layout& other) const {
+ return fLocation == other.fLocation &&
+ fBinding == other.fBinding &&
+ fIndex == other.fIndex &&
+ fSet == other.fSet &&
+ fBuiltin == other.fBuiltin;
+ }
+
+ bool operator!=(const Layout& other) const {
+ return !(*this == other);
+ }
+
+ const int fLocation;
+ const int fBinding;
+ const int fIndex;
+ const int fSet;
+ const int fBuiltin;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLModifiers.h b/src/sksl/ir/SkSLModifiers.h
new file mode 100644
index 0000000000..d3b9c40ea1
--- /dev/null
+++ b/src/sksl/ir/SkSLModifiers.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_MODIFIERS
+#define SKSL_MODIFIERS
+
+#include "../ast/SkSLASTModifiers.h"
+#include "SkSLLayout.h"
+
+namespace SkSL {
+
+/**
+ * A set of modifier keywords (in, out, uniform, etc.) appearing before a declaration.
+ */
+struct Modifiers {
+ enum Flag {
+ kNo_Flag = ASTModifiers::kNo_Flag,
+ kConst_Flag = ASTModifiers::kConst_Flag,
+ kIn_Flag = ASTModifiers::kIn_Flag,
+ kOut_Flag = ASTModifiers::kOut_Flag,
+ kLowp_Flag = ASTModifiers::kLowp_Flag,
+ kMediump_Flag = ASTModifiers::kMediump_Flag,
+ kHighp_Flag = ASTModifiers::kHighp_Flag,
+ kUniform_Flag = ASTModifiers::kUniform_Flag
+ };
+
+ Modifiers(const ASTModifiers& modifiers)
+ : fLayout(modifiers.fLayout)
+ , fFlags(modifiers.fFlags) {}
+
+ Modifiers(Layout& layout, int flags)
+ : fLayout(layout)
+ , fFlags(flags) {}
+
+ std::string description() const {
+ std::string result = fLayout.description();
+ if (fFlags & kUniform_Flag) {
+ result += "uniform ";
+ }
+ if (fFlags & kConst_Flag) {
+ result += "const ";
+ }
+ if (fFlags & kLowp_Flag) {
+ result += "lowp ";
+ }
+ if (fFlags & kMediump_Flag) {
+ result += "mediump ";
+ }
+ if (fFlags & kHighp_Flag) {
+ result += "highp ";
+ }
+
+ if ((fFlags & kIn_Flag) && (fFlags & kOut_Flag)) {
+ result += "inout ";
+ } else if (fFlags & kIn_Flag) {
+ result += "in ";
+ } else if (fFlags & kOut_Flag) {
+ result += "out ";
+ }
+
+ return result;
+ }
+
+ bool operator==(const Modifiers& other) const {
+ return fLayout == other.fLayout && fFlags == other.fFlags;
+ }
+
+ bool operator!=(const Modifiers& other) const {
+ return !(*this == other);
+ }
+
+ const Layout fLayout;
+ const int fFlags;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLPostfixExpression.h b/src/sksl/ir/SkSLPostfixExpression.h
new file mode 100644
index 0000000000..de146ac43c
--- /dev/null
+++ b/src/sksl/ir/SkSLPostfixExpression.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_POSTFIXEXPRESSION
+#define SKSL_POSTFIXEXPRESSION
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * An expression modified by a unary operator appearing after it, such as 'i++'.
+ */
+struct PostfixExpression : public Expression {
+ PostfixExpression(std::unique_ptr<Expression> operand, Token::Kind op)
+ : INHERITED(operand->fPosition, kPostfix_Kind, operand->fType)
+ , fOperand(std::move(operand))
+ , fOperator(op) {}
+
+ virtual std::string description() const override {
+ return fOperand->description() + Token::OperatorName(fOperator);
+ }
+
+ const std::unique_ptr<Expression> fOperand;
+ const Token::Kind fOperator;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLPrefixExpression.h b/src/sksl/ir/SkSLPrefixExpression.h
new file mode 100644
index 0000000000..53c3849b38
--- /dev/null
+++ b/src/sksl/ir/SkSLPrefixExpression.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_PREFIXEXPRESSION
+#define SKSL_PREFIXEXPRESSION
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * An expression modified by a unary operator appearing before it, such as '!flag'.
+ */
+struct PrefixExpression : public Expression {
+ PrefixExpression(Token::Kind op, std::unique_ptr<Expression> operand)
+ : INHERITED(operand->fPosition, kPrefix_Kind, operand->fType)
+ , fOperand(std::move(operand))
+ , fOperator(op) {}
+
+ virtual std::string description() const override {
+ return Token::OperatorName(fOperator) + fOperand->description();
+ }
+
+ const std::unique_ptr<Expression> fOperand;
+ const Token::Kind fOperator;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLProgram.h b/src/sksl/ir/SkSLProgram.h
new file mode 100644
index 0000000000..5edcfded42
--- /dev/null
+++ b/src/sksl/ir/SkSLProgram.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_PROGRAM
+#define SKSL_PROGRAM
+
+#include <vector>
+#include <memory>
+
+#include "SkSLProgramElement.h"
+
+namespace SkSL {
+
+/**
+ * Represents a fully-digested program, ready for code generation.
+ */
+struct Program {
+ enum Kind {
+ kFragment_Kind,
+ kVertex_Kind
+ };
+
+ Program(Kind kind, std::vector<std::unique_ptr<ProgramElement>> elements)
+ : fKind(kind)
+ , fElements(std::move(elements)) {}
+
+ Kind fKind;
+
+ std::vector<std::unique_ptr<ProgramElement>> fElements;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLProgramElement.h b/src/sksl/ir/SkSLProgramElement.h
new file mode 100644
index 0000000000..44fc340667
--- /dev/null
+++ b/src/sksl/ir/SkSLProgramElement.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_PROGRAMELEMENT
+#define SKSL_PROGRAMELEMENT
+
+#include "SkSLIRNode.h"
+
+namespace SkSL {
+
+/**
+ * Represents a top-level element (e.g. function or global variable) in a program.
+ */
+struct ProgramElement : public IRNode {
+ enum Kind {
+ kVar_Kind,
+ kFunction_Kind,
+ kInterfaceBlock_Kind,
+ kExtension_Kind
+ };
+
+ ProgramElement(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ Kind fKind;
+
+ typedef IRNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLReturnStatement.h b/src/sksl/ir/SkSLReturnStatement.h
new file mode 100644
index 0000000000..ec2226cc56
--- /dev/null
+++ b/src/sksl/ir/SkSLReturnStatement.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_RETURNSTATEMENT
+#define SKSL_RETURNSTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'return' statement.
+ */
+struct ReturnStatement : public Statement {
+ ReturnStatement(Position position)
+ : INHERITED(position, kReturn_Kind) {}
+
+ ReturnStatement(std::unique_ptr<Expression> expression)
+ : INHERITED(expression->fPosition, kReturn_Kind)
+ , fExpression(std::move(expression)) {}
+
+ std::string description() const override {
+ if (fExpression) {
+ return "return " + fExpression->description() + ";";
+ } else {
+ return "return;";
+ }
+ }
+
+ const std::unique_ptr<Expression> fExpression;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLStatement.h b/src/sksl/ir/SkSLStatement.h
new file mode 100644
index 0000000000..64b7bdf276
--- /dev/null
+++ b/src/sksl/ir/SkSLStatement.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_STATEMENT
+#define SKSL_STATEMENT
+
+#include "SkSLIRNode.h"
+#include "SkSLType.h"
+
+namespace SkSL {
+
+/**
+ * Abstract supertype of all statements.
+ */
+struct Statement : public IRNode {
+ enum Kind {
+ kBlock_Kind,
+ kBreak_Kind,
+ kContinue_Kind,
+ kDiscard_Kind,
+ kDo_Kind,
+ kExpression_Kind,
+ kFor_Kind,
+ kIf_Kind,
+ kReturn_Kind,
+ kVarDeclaration_Kind,
+ kWhile_Kind
+ };
+
+ Statement(Position position, Kind kind)
+ : INHERITED(position)
+ , fKind(kind) {}
+
+ const Kind fKind;
+
+ typedef IRNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLSwizzle.h b/src/sksl/ir/SkSLSwizzle.h
new file mode 100644
index 0000000000..ce360d1847
--- /dev/null
+++ b/src/sksl/ir/SkSLSwizzle.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SWIZZLE
+#define SKSL_SWIZZLE
+
+#include "SkSLExpression.h"
+#include "SkSLUtil.h"
+
+namespace SkSL {
+
+/**
+ * Given a type and a swizzle component count, returns the type that will result from swizzling. For
+ * instance, swizzling a vec3 with two components will result in a vec2. It is possible to swizzle
+ * with more components than the source vector, as in 'vec2(1).xxxx'.
+ */
+static std::shared_ptr<Type> get_type(Expression& value,
+ size_t count) {
+ std::shared_ptr<Type> base = value.fType->componentType();
+ if (count == 1) {
+ return base;
+ }
+ if (base == kFloat_Type) {
+ switch (count) {
+ case 2: return kVec2_Type;
+ case 3: return kVec3_Type;
+ case 4: return kVec4_Type;
+ }
+ } else if (base == kDouble_Type) {
+ switch (count) {
+ case 2: return kDVec2_Type;
+ case 3: return kDVec3_Type;
+ case 4: return kDVec4_Type;
+ }
+ } else if (base == kInt_Type) {
+ switch (count) {
+ case 2: return kIVec2_Type;
+ case 3: return kIVec3_Type;
+ case 4: return kIVec4_Type;
+ }
+ } else if (base == kUInt_Type) {
+ switch (count) {
+ case 2: return kUVec2_Type;
+ case 3: return kUVec3_Type;
+ case 4: return kUVec4_Type;
+ }
+ } else if (base == kBool_Type) {
+ switch (count) {
+ case 2: return kBVec2_Type;
+ case 3: return kBVec3_Type;
+ case 4: return kBVec4_Type;
+ }
+ }
+ ABORT("cannot swizzle %s\n", value.description().c_str());
+}
+
+/**
+ * Represents a vector swizzle operation such as 'vec2(1, 2, 3).zyx'.
+ */
+struct Swizzle : public Expression {
+ Swizzle(std::unique_ptr<Expression> base, std::vector<int> components)
+ : INHERITED(base->fPosition, kSwizzle_Kind, get_type(*base, components.size()))
+ , fBase(std::move(base))
+ , fComponents(std::move(components)) {
+ ASSERT(fComponents.size() >= 1 && fComponents.size() <= 4);
+ }
+
+ std::string description() const override {
+ std::string result = fBase->description() + ".";
+ for (int x : fComponents) {
+ result += "xyzw"[x];
+ }
+ return result;
+ }
+
+ const std::unique_ptr<Expression> fBase;
+ const std::vector<int> fComponents;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLSymbol.h b/src/sksl/ir/SkSLSymbol.h
new file mode 100644
index 0000000000..d736516bc4
--- /dev/null
+++ b/src/sksl/ir/SkSLSymbol.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SYMBOL
+#define SKSL_SYMBOL
+
+#include "SkSLIRNode.h"
+
+namespace SkSL {
+
+/**
+ * Represents a symboltable entry.
+ */
+struct Symbol : public IRNode {
+ enum Kind {
+ kFunctionDeclaration_Kind,
+ kUnresolvedFunction_Kind,
+ kType_Kind,
+ kVariable_Kind,
+ kField_Kind
+ };
+
+ Symbol(Position position, Kind kind, std::string name)
+ : INHERITED(position)
+ , fKind(kind)
+ , fName(std::move(name)) {}
+
+ const Kind fKind;
+ const std::string fName;
+
+ typedef IRNode INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLSymbolTable.cpp b/src/sksl/ir/SkSLSymbolTable.cpp
new file mode 100644
index 0000000000..af83f7a456
--- /dev/null
+++ b/src/sksl/ir/SkSLSymbolTable.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+ #include "SkSLSymbolTable.h"
+
+namespace SkSL {
+
+std::vector<std::shared_ptr<FunctionDeclaration>> SymbolTable::GetFunctions(
+ const std::shared_ptr<Symbol>& s) {
+ switch (s->fKind) {
+ case Symbol::kFunctionDeclaration_Kind:
+ return { std::static_pointer_cast<FunctionDeclaration>(s) };
+ case Symbol::kUnresolvedFunction_Kind:
+ return ((UnresolvedFunction&) *s).fFunctions;
+ default:
+ return { };
+ }
+}
+
+std::shared_ptr<Symbol> SymbolTable::operator[](const std::string& name) {
+ const auto& entry = fSymbols.find(name);
+ if (entry == fSymbols.end()) {
+ if (fParent) {
+ return (*fParent)[name];
+ }
+ return nullptr;
+ }
+ if (fParent) {
+ auto functions = GetFunctions(entry->second);
+ if (functions.size() > 0) {
+ bool modified = false;
+ std::shared_ptr<Symbol> previous = (*fParent)[name];
+ if (previous) {
+ auto previousFunctions = GetFunctions(previous);
+ for (const std::shared_ptr<FunctionDeclaration>& prev : previousFunctions) {
+ bool found = false;
+ for (const std::shared_ptr<FunctionDeclaration>& current : functions) {
+ if (current->matches(*prev)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ functions.push_back(prev);
+ modified = true;
+ }
+ }
+ if (modified) {
+ ASSERT(functions.size() > 1);
+ return std::shared_ptr<Symbol>(new UnresolvedFunction(functions));
+ }
+ }
+ }
+ }
+ return entry->second;
+}
+
+void SymbolTable::add(const std::string& name, std::shared_ptr<Symbol> symbol) {
+ const auto& existing = fSymbols.find(name);
+ if (existing == fSymbols.end()) {
+ fSymbols[name] = symbol;
+ } else if (symbol->fKind == Symbol::kFunctionDeclaration_Kind) {
+ const std::shared_ptr<Symbol>& oldSymbol = existing->second;
+ if (oldSymbol->fKind == Symbol::kFunctionDeclaration_Kind) {
+ std::vector<std::shared_ptr<FunctionDeclaration>> functions;
+ functions.push_back(std::static_pointer_cast<FunctionDeclaration>(oldSymbol));
+ functions.push_back(std::static_pointer_cast<FunctionDeclaration>(symbol));
+ fSymbols[name].reset(new UnresolvedFunction(std::move(functions)));
+ } else if (oldSymbol->fKind == Symbol::kUnresolvedFunction_Kind) {
+ std::vector<std::shared_ptr<FunctionDeclaration>> functions;
+ for (const auto& f : ((UnresolvedFunction&) *oldSymbol).fFunctions) {
+ functions.push_back(f);
+ }
+ functions.push_back(std::static_pointer_cast<FunctionDeclaration>(symbol));
+ fSymbols[name].reset(new UnresolvedFunction(std::move(functions)));
+ }
+ } else {
+ fErrorReporter.error(symbol->fPosition, "symbol '" + name + "' was already defined");
+ }
+ }
+} // namespace
diff --git a/src/sksl/ir/SkSLSymbolTable.h b/src/sksl/ir/SkSLSymbolTable.h
new file mode 100644
index 0000000000..151475d642
--- /dev/null
+++ b/src/sksl/ir/SkSLSymbolTable.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_SYMBOLTABLE
+#define SKSL_SYMBOLTABLE
+
+#include <memory>
+#include <unordered_map>
+#include "SkSLErrorReporter.h"
+#include "SkSLSymbol.h"
+#include "SkSLUnresolvedFunction.h"
+
+namespace SkSL {
+
+/**
+ * Maps identifiers to symbols. Functions, in particular, are mapped to either FunctionDeclaration
+ * or UnresolvedFunction depending on whether they are overloaded or not.
+ */
+class SymbolTable {
+public:
+ SymbolTable(ErrorReporter& errorReporter)
+ : fErrorReporter(errorReporter) {}
+
+ SymbolTable(std::shared_ptr<SymbolTable> parent, ErrorReporter& errorReporter)
+ : fParent(parent)
+ , fErrorReporter(errorReporter) {}
+
+ std::shared_ptr<Symbol> operator[](const std::string& name);
+
+ void add(const std::string& name, std::shared_ptr<Symbol> symbol);
+
+ const std::shared_ptr<SymbolTable> fParent;
+
+private:
+ static std::vector<std::shared_ptr<FunctionDeclaration>> GetFunctions(
+ const std::shared_ptr<Symbol>& s);
+
+ std::unordered_map<std::string, std::shared_ptr<Symbol>> fSymbols;
+
+ ErrorReporter& fErrorReporter;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLTernaryExpression.h b/src/sksl/ir/SkSLTernaryExpression.h
new file mode 100644
index 0000000000..bfaf304e55
--- /dev/null
+++ b/src/sksl/ir/SkSLTernaryExpression.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_TERNARYEXPRESSION
+#define SKSL_TERNARYEXPRESSION
+
+#include "SkSLExpression.h"
+#include "../SkSLPosition.h"
+
+namespace SkSL {
+
+/**
+ * A ternary expression (test ? ifTrue : ifFalse).
+ */
+struct TernaryExpression : public Expression {
+ TernaryExpression(Position position, std::unique_ptr<Expression> test,
+ std::unique_ptr<Expression> ifTrue, std::unique_ptr<Expression> ifFalse)
+ : INHERITED(position, kTernary_Kind, ifTrue->fType)
+ , fTest(std::move(test))
+ , fIfTrue(std::move(ifTrue))
+ , fIfFalse(std::move(ifFalse)) {
+ ASSERT(fIfTrue->fType == fIfFalse->fType);
+ }
+
+ std::string description() const override {
+ return "(" + fTest->description() + " ? " + fIfTrue->description() + " : " +
+ fIfFalse->description() + ")";
+ }
+
+ const std::unique_ptr<Expression> fTest;
+ const std::unique_ptr<Expression> fIfTrue;
+ const std::unique_ptr<Expression> fIfFalse;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLType.cpp b/src/sksl/ir/SkSLType.cpp
new file mode 100644
index 0000000000..27cbd39e44
--- /dev/null
+++ b/src/sksl/ir/SkSLType.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLType.h"
+
+namespace SkSL {
+
+bool Type::determineCoercionCost(std::shared_ptr<Type> other, int* outCost) const {
+ if (this == other.get()) {
+ *outCost = 0;
+ return true;
+ }
+ if (this->kind() == kVector_Kind && other->kind() == kVector_Kind) {
+ if (this->columns() == other->columns()) {
+ return this->componentType()->determineCoercionCost(other->componentType(), outCost);
+ }
+ return false;
+ }
+ if (this->kind() == kMatrix_Kind) {
+ if (this->columns() == other->columns() &&
+ this->rows() == other->rows()) {
+ return this->componentType()->determineCoercionCost(other->componentType(), outCost);
+ }
+ return false;
+ }
+ for (size_t i = 0; i < fCoercibleTypes.size(); i++) {
+ if (fCoercibleTypes[i] == other) {
+ *outCost = (int) i + 1;
+ return true;
+ }
+ }
+ return false;
+}
+
+std::shared_ptr<Type> Type::toCompound(int columns, int rows) {
+ ASSERT(this->kind() == Type::kScalar_Kind);
+ if (columns == 1 && rows == 1) {
+ return std::shared_ptr<Type>(this);
+ }
+ if (*this == *kFloat_Type) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 2: return kVec2_Type;
+ case 3: return kVec3_Type;
+ case 4: return kVec4_Type;
+ default: ABORT("unsupported vector column count (%d)", columns);
+ }
+ case 2:
+ switch (columns) {
+ case 2: return kMat2x2_Type;
+ case 3: return kMat3x2_Type;
+ case 4: return kMat4x2_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 3:
+ switch (columns) {
+ case 2: return kMat2x3_Type;
+ case 3: return kMat3x3_Type;
+ case 4: return kMat4x3_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 4:
+ switch (columns) {
+ case 2: return kMat2x4_Type;
+ case 3: return kMat3x4_Type;
+ case 4: return kMat4x4_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ default: ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (*this == *kDouble_Type) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 2: return kDVec2_Type;
+ case 3: return kDVec3_Type;
+ case 4: return kDVec4_Type;
+ default: ABORT("unsupported vector column count (%d)", columns);
+ }
+ case 2:
+ switch (columns) {
+ case 2: return kDMat2x2_Type;
+ case 3: return kDMat3x2_Type;
+ case 4: return kDMat4x2_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 3:
+ switch (columns) {
+ case 2: return kDMat2x3_Type;
+ case 3: return kDMat3x3_Type;
+ case 4: return kDMat4x3_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ case 4:
+ switch (columns) {
+ case 2: return kDMat2x4_Type;
+ case 3: return kDMat3x4_Type;
+ case 4: return kDMat4x4_Type;
+ default: ABORT("unsupported matrix column count (%d)", columns);
+ }
+ default: ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (*this == *kInt_Type) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 2: return kIVec2_Type;
+ case 3: return kIVec3_Type;
+ case 4: return kIVec4_Type;
+ default: ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: ABORT("unsupported row count (%d)", rows);
+ }
+ } else if (*this == *kUInt_Type) {
+ switch (rows) {
+ case 1:
+ switch (columns) {
+ case 2: return kUVec2_Type;
+ case 3: return kUVec3_Type;
+ case 4: return kUVec4_Type;
+ default: ABORT("unsupported vector column count (%d)", columns);
+ }
+ default: ABORT("unsupported row count (%d)", rows);
+ }
+ }
+ ABORT("unsupported scalar_to_compound type %s", this->description().c_str());
+}
+
+const std::shared_ptr<Type> kVoid_Type(new Type("void"));
+
+const std::shared_ptr<Type> kDouble_Type(new Type("double", true));
+const std::shared_ptr<Type> kDVec2_Type(new Type("dvec2", kDouble_Type, 2));
+const std::shared_ptr<Type> kDVec3_Type(new Type("dvec3", kDouble_Type, 3));
+const std::shared_ptr<Type> kDVec4_Type(new Type("dvec4", kDouble_Type, 4));
+
+const std::shared_ptr<Type> kFloat_Type(new Type("float", true, { kDouble_Type }));
+const std::shared_ptr<Type> kVec2_Type(new Type("vec2", kFloat_Type, 2));
+const std::shared_ptr<Type> kVec3_Type(new Type("vec3", kFloat_Type, 3));
+const std::shared_ptr<Type> kVec4_Type(new Type("vec4", kFloat_Type, 4));
+
+const std::shared_ptr<Type> kUInt_Type(new Type("uint", true, { kFloat_Type, kDouble_Type }));
+const std::shared_ptr<Type> kUVec2_Type(new Type("uvec2", kUInt_Type, 2));
+const std::shared_ptr<Type> kUVec3_Type(new Type("uvec3", kUInt_Type, 3));
+const std::shared_ptr<Type> kUVec4_Type(new Type("uvec4", kUInt_Type, 4));
+
+const std::shared_ptr<Type> kInt_Type(new Type("int", true, { kUInt_Type, kFloat_Type,
+ kDouble_Type }));
+const std::shared_ptr<Type> kIVec2_Type(new Type("ivec2", kInt_Type, 2));
+const std::shared_ptr<Type> kIVec3_Type(new Type("ivec3", kInt_Type, 3));
+const std::shared_ptr<Type> kIVec4_Type(new Type("ivec4", kInt_Type, 4));
+
+const std::shared_ptr<Type> kBool_Type(new Type("bool", false));
+const std::shared_ptr<Type> kBVec2_Type(new Type("bvec2", kBool_Type, 2));
+const std::shared_ptr<Type> kBVec3_Type(new Type("bvec3", kBool_Type, 3));
+const std::shared_ptr<Type> kBVec4_Type(new Type("bvec4", kBool_Type, 4));
+
+const std::shared_ptr<Type> kMat2x2_Type(new Type("mat2", kFloat_Type, 2, 2));
+const std::shared_ptr<Type> kMat2x3_Type(new Type("mat2x3", kFloat_Type, 2, 3));
+const std::shared_ptr<Type> kMat2x4_Type(new Type("mat2x4", kFloat_Type, 2, 4));
+const std::shared_ptr<Type> kMat3x2_Type(new Type("mat3x2", kFloat_Type, 3, 2));
+const std::shared_ptr<Type> kMat3x3_Type(new Type("mat3", kFloat_Type, 3, 3));
+const std::shared_ptr<Type> kMat3x4_Type(new Type("mat3x4", kFloat_Type, 3, 4));
+const std::shared_ptr<Type> kMat4x2_Type(new Type("mat4x2", kFloat_Type, 4, 2));
+const std::shared_ptr<Type> kMat4x3_Type(new Type("mat4x3", kFloat_Type, 4, 3));
+const std::shared_ptr<Type> kMat4x4_Type(new Type("mat4", kFloat_Type, 4, 4));
+
+const std::shared_ptr<Type> kDMat2x2_Type(new Type("dmat2", kFloat_Type, 2, 2));
+const std::shared_ptr<Type> kDMat2x3_Type(new Type("dmat2x3", kFloat_Type, 2, 3));
+const std::shared_ptr<Type> kDMat2x4_Type(new Type("dmat2x4", kFloat_Type, 2, 4));
+const std::shared_ptr<Type> kDMat3x2_Type(new Type("dmat3x2", kFloat_Type, 3, 2));
+const std::shared_ptr<Type> kDMat3x3_Type(new Type("dmat3", kFloat_Type, 3, 3));
+const std::shared_ptr<Type> kDMat3x4_Type(new Type("dmat3x4", kFloat_Type, 3, 4));
+const std::shared_ptr<Type> kDMat4x2_Type(new Type("dmat4x2", kFloat_Type, 4, 2));
+const std::shared_ptr<Type> kDMat4x3_Type(new Type("dmat4x3", kFloat_Type, 4, 3));
+const std::shared_ptr<Type> kDMat4x4_Type(new Type("dmat4", kFloat_Type, 4, 4));
+
+const std::shared_ptr<Type> kSampler1D_Type(new Type("sampler1D", SpvDim1D, false, false, false, true));
+const std::shared_ptr<Type> kSampler2D_Type(new Type("sampler2D", SpvDim2D, false, false, false, true));
+const std::shared_ptr<Type> kSampler3D_Type(new Type("sampler3D", SpvDim3D, false, false, false, true));
+const std::shared_ptr<Type> kSamplerCube_Type(new Type("samplerCube"));
+const std::shared_ptr<Type> kSampler2DRect_Type(new Type("sampler2DRect"));
+const std::shared_ptr<Type> kSampler1DArray_Type(new Type("sampler1DArray"));
+const std::shared_ptr<Type> kSampler2DArray_Type(new Type("sampler2DArray"));
+const std::shared_ptr<Type> kSamplerCubeArray_Type(new Type("samplerCubeArray"));
+const std::shared_ptr<Type> kSamplerBuffer_Type(new Type("samplerBuffer"));
+const std::shared_ptr<Type> kSampler2DMS_Type(new Type("sampler2DMS"));
+const std::shared_ptr<Type> kSampler2DMSArray_Type(new Type("sampler2DMSArray"));
+const std::shared_ptr<Type> kSampler1DShadow_Type(new Type("sampler1DShadow"));
+const std::shared_ptr<Type> kSampler2DShadow_Type(new Type("sampler2DShadow"));
+const std::shared_ptr<Type> kSamplerCubeShadow_Type(new Type("samplerCubeShadow"));
+const std::shared_ptr<Type> kSampler2DRectShadow_Type(new Type("sampler2DRectShadow"));
+const std::shared_ptr<Type> kSampler1DArrayShadow_Type(new Type("sampler1DArrayShadow"));
+const std::shared_ptr<Type> kSampler2DArrayShadow_Type(new Type("sampler2DArrayShadow"));
+const std::shared_ptr<Type> kSamplerCubeArrayShadow_Type(new Type("samplerCubeArrayShadow"));
+
+static std::vector<std::shared_ptr<Type>> type(std::shared_ptr<Type> t) {
+ return { t, t, t, t };
+}
+
+// FIXME figure out what we're supposed to do with the gsampler et al. types
+const std::shared_ptr<Type> kGSampler1D_Type(new Type("$gsampler1D", type(kSampler1D_Type)));
+const std::shared_ptr<Type> kGSampler2D_Type(new Type("$gsampler2D", type(kSampler2D_Type)));
+const std::shared_ptr<Type> kGSampler3D_Type(new Type("$gsampler3D", type(kSampler3D_Type)));
+const std::shared_ptr<Type> kGSamplerCube_Type(new Type("$gsamplerCube", type(kSamplerCube_Type)));
+const std::shared_ptr<Type> kGSampler2DRect_Type(new Type("$gsampler2DRect",
+ type(kSampler2DRect_Type)));
+const std::shared_ptr<Type> kGSampler1DArray_Type(new Type("$gsampler1DArray",
+ type(kSampler1DArray_Type)));
+const std::shared_ptr<Type> kGSampler2DArray_Type(new Type("$gsampler2DArray",
+ type(kSampler2DArray_Type)));
+const std::shared_ptr<Type> kGSamplerCubeArray_Type(new Type("$gsamplerCubeArray",
+ type(kSamplerCubeArray_Type)));
+const std::shared_ptr<Type> kGSamplerBuffer_Type(new Type("$gsamplerBuffer",
+ type(kSamplerBuffer_Type)));
+const std::shared_ptr<Type> kGSampler2DMS_Type(new Type("$gsampler2DMS",
+ type(kSampler2DMS_Type)));
+const std::shared_ptr<Type> kGSampler2DMSArray_Type(new Type("$gsampler2DMSArray",
+ type(kSampler2DMSArray_Type)));
+const std::shared_ptr<Type> kGSampler2DArrayShadow_Type(new Type("$gsampler2DArrayShadow",
+ type(kSampler2DArrayShadow_Type)));
+const std::shared_ptr<Type> kGSamplerCubeArrayShadow_Type(new Type("$gsamplerCubeArrayShadow",
+ type(kSamplerCubeArrayShadow_Type)));
+
+const std::shared_ptr<Type> kGenType_Type(new Type("$genType", { kFloat_Type, kVec2_Type,
+ kVec3_Type, kVec4_Type }));
+const std::shared_ptr<Type> kGenDType_Type(new Type("$genDType", { kDouble_Type, kDVec2_Type,
+ kDVec3_Type, kDVec4_Type }));
+const std::shared_ptr<Type> kGenIType_Type(new Type("$genIType", { kInt_Type, kIVec2_Type,
+ kIVec3_Type, kIVec4_Type }));
+const std::shared_ptr<Type> kGenUType_Type(new Type("$genUType", { kUInt_Type, kUVec2_Type,
+ kUVec3_Type, kUVec4_Type }));
+const std::shared_ptr<Type> kGenBType_Type(new Type("$genBType", { kBool_Type, kBVec2_Type,
+ kBVec3_Type, kBVec4_Type }));
+
+const std::shared_ptr<Type> kMat_Type(new Type("$mat"));
+
+const std::shared_ptr<Type> kVec_Type(new Type("$vec", { kVec2_Type, kVec2_Type, kVec3_Type,
+ kVec4_Type }));
+
+const std::shared_ptr<Type> kGVec_Type(new Type("$gvec"));
+const std::shared_ptr<Type> kGVec2_Type(new Type("$gvec2"));
+const std::shared_ptr<Type> kGVec3_Type(new Type("$gvec3"));
+const std::shared_ptr<Type> kGVec4_Type(new Type("$gvec4", type(kVec4_Type)));
+const std::shared_ptr<Type> kDVec_Type(new Type("$dvec"));
+const std::shared_ptr<Type> kIVec_Type(new Type("$ivec"));
+const std::shared_ptr<Type> kUVec_Type(new Type("$uvec"));
+
+const std::shared_ptr<Type> kBVec_Type(new Type("$bvec", { kBVec2_Type, kBVec2_Type,
+ kBVec3_Type, kBVec4_Type }));
+
+const std::shared_ptr<Type> kInvalid_Type(new Type("<INVALID>"));
+
+} // namespace
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
new file mode 100644
index 0000000000..e17bae68db
--- /dev/null
+++ b/src/sksl/ir/SkSLType.h
@@ -0,0 +1,438 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKIASL_TYPE
+#define SKIASL_TYPE
+
+#include "SkSLModifiers.h"
+#include "SkSLSymbol.h"
+#include "../SkSLPosition.h"
+#include "../SkSLUtil.h"
+#include "../spirv.h"
+#include <vector>
+#include <memory>
+
+namespace SkSL {
+
+/**
+ * Represents a type, such as int or vec4.
+ */
+class Type : public Symbol {
+public:
+ struct Field {
+ Field(Modifiers modifiers, std::string name, std::shared_ptr<Type> type)
+ : fModifiers(modifiers)
+ , fName(std::move(name))
+ , fType(std::move(type)) {}
+
+ const std::string description() {
+ return fType->description() + " " + fName + ";";
+ }
+
+ const Modifiers fModifiers;
+ const std::string fName;
+ const std::shared_ptr<Type> fType;
+ };
+
+ enum Kind {
+ kScalar_Kind,
+ kVector_Kind,
+ kMatrix_Kind,
+ kArray_Kind,
+ kStruct_Kind,
+ kGeneric_Kind,
+ kSampler_Kind,
+ kOther_Kind
+ };
+
+ // Create an "other" (special) type with the given name. These types cannot be directly
+ // referenced from user code.
+ Type(std::string name)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kOther_Kind) {}
+
+ // Create a generic type which maps to the listed types.
+ Type(std::string name, std::vector<std::shared_ptr<Type>> types)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kGeneric_Kind)
+ , fCoercibleTypes(std::move(types)) {
+ ASSERT(fCoercibleTypes.size() == 4);
+ }
+
+ // Create a struct type with the given fields.
+ Type(std::string name, std::vector<Field> fields)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kStruct_Kind)
+ , fFields(std::move(fields)) {}
+
+ // Create a scalar type.
+ Type(std::string name, bool isNumber)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kScalar_Kind)
+ , fIsNumber(isNumber)
+ , fColumns(1)
+ , fRows(1) {}
+
+ // Create a scalar type which can be coerced to the listed types.
+ Type(std::string name, bool isNumber, std::vector<std::shared_ptr<Type>> coercibleTypes)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kScalar_Kind)
+ , fIsNumber(isNumber)
+ , fCoercibleTypes(std::move(coercibleTypes))
+ , fColumns(1)
+ , fRows(1) {}
+
+ // Create a vector type.
+ Type(std::string name, std::shared_ptr<Type> componentType, int columns)
+ : Type(name, kVector_Kind, componentType, columns) {}
+
+ // Create a vector or array type.
+ Type(std::string name, Kind kind, std::shared_ptr<Type> componentType, int columns)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kind)
+ , fComponentType(std::move(componentType))
+ , fColumns(columns)
+ , fRows(1)
+ , fDimensions(SpvDim1D) {}
+
+ // Create a matrix type.
+ Type(std::string name, std::shared_ptr<Type> componentType, int columns, int rows)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kMatrix_Kind)
+ , fComponentType(std::move(componentType))
+ , fColumns(columns)
+ , fRows(rows)
+ , fDimensions(SpvDim1D) {}
+
+ // Create a sampler type.
+ Type(std::string name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled,
+ bool isSampled)
+ : INHERITED(Position(), kType_Kind, std::move(name))
+ , fTypeKind(kSampler_Kind)
+ , fDimensions(dimensions)
+ , fIsDepth(isDepth)
+ , fIsArrayed(isArrayed)
+ , fIsMultisampled(isMultisampled)
+ , fIsSampled(isSampled) {}
+
+ std::string name() const {
+ return fName;
+ }
+
+ std::string description() const override {
+ return fName;
+ }
+
+ bool operator==(const Type& other) const {
+ return fName == other.fName;
+ }
+
+ bool operator!=(const Type& other) const {
+ return fName != other.fName;
+ }
+
+ /**
+ * Returns the category (scalar, vector, matrix, etc.) of this type.
+ */
+ Kind kind() const {
+ return fTypeKind;
+ }
+
+ /**
+ * Returns true if this is a numeric scalar type.
+ */
+ bool isNumber() const {
+ return fIsNumber;
+ }
+
+ /**
+ * Returns true if an instance of this type can be freely coerced (implicitly converted) to
+ * another type.
+ */
+ bool canCoerceTo(std::shared_ptr<Type> other) const {
+ int cost;
+ return determineCoercionCost(other, &cost);
+ }
+
+ /**
+ * Determines the "cost" of coercing (implicitly converting) this type to another type. The cost
+ * is a number with no particular meaning other than that lower costs are preferable to higher
+ * costs. Returns true if a conversion is possible, false otherwise. The value of the out
+ * parameter is undefined if false is returned.
+ */
+ bool determineCoercionCost(std::shared_ptr<Type> other, int* outCost) const;
+
+ /**
+ * For matrices and vectors, returns the type of individual cells (e.g. mat2 has a component
+ * type of kFloat_Type). For all other types, causes an assertion failure.
+ */
+ std::shared_ptr<Type> componentType() const {
+ ASSERT(fComponentType);
+ return fComponentType;
+ }
+
+ /**
+ * For matrices and vectors, returns the number of columns (e.g. both mat3 and vec3 return 3).
+ * For scalars, returns 1. For arrays, returns either the size of the array (if known) or -1.
+ * For all other types, causes an assertion failure.
+ */
+ int columns() const {
+ ASSERT(fTypeKind == kScalar_Kind || fTypeKind == kVector_Kind ||
+ fTypeKind == kMatrix_Kind || fTypeKind == kArray_Kind);
+ return fColumns;
+ }
+
+ /**
+ * For matrices, returns the number of rows (e.g. mat2x4 returns 4). For vectors and scalars,
+ * returns 1. For all other types, causes an assertion failure.
+ */
+ int rows() const {
+ ASSERT(fRows > 0);
+ return fRows;
+ }
+
+ std::vector<Field> fields() const {
+ ASSERT(fTypeKind == kStruct_Kind);
+ return fFields;
+ }
+
+ /**
+ * For generic types, returns the types that this generic type can substitute for. For other
+ * types, returns a list of other types that this type can be coerced into.
+ */
+ std::vector<std::shared_ptr<Type>> coercibleTypes() const {
+ ASSERT(fCoercibleTypes.size() > 0);
+ return fCoercibleTypes;
+ }
+
+ int dimensions() const {
+ ASSERT(fTypeKind == kSampler_Kind);
+ return fDimensions;
+ }
+
+ bool isDepth() const {
+ ASSERT(fTypeKind == kSampler_Kind);
+ return fIsDepth;
+ }
+
+ bool isArrayed() const {
+ ASSERT(fTypeKind == kSampler_Kind);
+ return fIsArrayed;
+ }
+
+ bool isMultisampled() const {
+ ASSERT(fTypeKind == kSampler_Kind);
+ return fIsMultisampled;
+ }
+
+ bool isSampled() const {
+ ASSERT(fTypeKind == kSampler_Kind);
+ return fIsSampled;
+ }
+
+ static size_t vector_alignment(size_t componentSize, int columns) {
+ return componentSize * (columns + columns % 2);
+ }
+
+ /**
+ * Returns the type's required alignment (when putting this type into a struct, the offset must
+ * be a multiple of the alignment).
+ */
+ size_t alignment() const {
+ // See OpenGL Spec 7.6.2.2 Standard Uniform Block Layout
+ switch (fTypeKind) {
+ case kScalar_Kind:
+ return this->size();
+ case kVector_Kind:
+ return vector_alignment(fComponentType->size(), fColumns);
+ case kMatrix_Kind:
+ return (vector_alignment(fComponentType->size(), fRows) + 15) & ~15;
+ case kArray_Kind:
+ // round up to next multiple of 16
+ return (fComponentType->alignment() + 15) & ~15;
+ case kStruct_Kind: {
+ size_t result = 16;
+ for (size_t i = 0; i < fFields.size(); i++) {
+ size_t alignment = fFields[i].fType->alignment();
+ if (alignment > result) {
+ result = alignment;
+ }
+ }
+ }
+ default:
+ ABORT(("cannot determine size of type " + fName).c_str());
+ }
+ }
+
+ /**
+ * For matrices and arrays, returns the number of bytes from the start of one entry (row, in
+ * the case of matrices) to the start of the next.
+ */
+ size_t stride() const {
+ switch (fTypeKind) {
+ case kMatrix_Kind: // fall through
+ case kArray_Kind:
+ return this->alignment();
+ default:
+ ABORT("type does not have a stride");
+ }
+ }
+
+ /**
+ * Returns the size of this type in bytes.
+ */
+ size_t size() const {
+ switch (fTypeKind) {
+ case kScalar_Kind:
+ // FIXME need to take precision into account, once we figure out how we want to
+ // handle it...
+ return 4;
+ case kVector_Kind:
+ return fColumns * fComponentType->size();
+ case kMatrix_Kind:
+ return vector_alignment(fComponentType->size(), fRows) * fColumns;
+ case kArray_Kind:
+ return fColumns * this->stride();
+ case kStruct_Kind: {
+ size_t total = 0;
+ for (size_t i = 0; i < fFields.size(); i++) {
+ size_t alignment = fFields[i].fType->alignment();
+ if (total % alignment != 0) {
+ total += alignment - total % alignment;
+ }
+ ASSERT(false);
+ ASSERT(total % alignment == 0);
+ total += fFields[i].fType->size();
+ }
+ return total;
+ }
+ default:
+ ABORT(("cannot determine size of type " + fName).c_str());
+ }
+ }
+
+ /**
+ * Returns the corresponding vector or matrix type with the specified number of columns and
+ * rows.
+ */
+ std::shared_ptr<Type> toCompound(int columns, int rows);
+
+private:
+ typedef Symbol INHERITED;
+
+ const Kind fTypeKind;
+ const bool fIsNumber = false;
+ const std::shared_ptr<Type> fComponentType = nullptr;
+ const std::vector<std::shared_ptr<Type>> fCoercibleTypes = { };
+ const int fColumns = -1;
+ const int fRows = -1;
+ const std::vector<Field> fFields = { };
+ const SpvDim_ fDimensions = SpvDim1D;
+ const bool fIsDepth = false;
+ const bool fIsArrayed = false;
+ const bool fIsMultisampled = false;
+ const bool fIsSampled = false;
+};
+
+extern const std::shared_ptr<Type> kVoid_Type;
+
+extern const std::shared_ptr<Type> kFloat_Type;
+extern const std::shared_ptr<Type> kVec2_Type;
+extern const std::shared_ptr<Type> kVec3_Type;
+extern const std::shared_ptr<Type> kVec4_Type;
+extern const std::shared_ptr<Type> kDouble_Type;
+extern const std::shared_ptr<Type> kDVec2_Type;
+extern const std::shared_ptr<Type> kDVec3_Type;
+extern const std::shared_ptr<Type> kDVec4_Type;
+extern const std::shared_ptr<Type> kInt_Type;
+extern const std::shared_ptr<Type> kIVec2_Type;
+extern const std::shared_ptr<Type> kIVec3_Type;
+extern const std::shared_ptr<Type> kIVec4_Type;
+extern const std::shared_ptr<Type> kUInt_Type;
+extern const std::shared_ptr<Type> kUVec2_Type;
+extern const std::shared_ptr<Type> kUVec3_Type;
+extern const std::shared_ptr<Type> kUVec4_Type;
+extern const std::shared_ptr<Type> kBool_Type;
+extern const std::shared_ptr<Type> kBVec2_Type;
+extern const std::shared_ptr<Type> kBVec3_Type;
+extern const std::shared_ptr<Type> kBVec4_Type;
+
+extern const std::shared_ptr<Type> kMat2x2_Type;
+extern const std::shared_ptr<Type> kMat2x3_Type;
+extern const std::shared_ptr<Type> kMat2x4_Type;
+extern const std::shared_ptr<Type> kMat3x2_Type;
+extern const std::shared_ptr<Type> kMat3x3_Type;
+extern const std::shared_ptr<Type> kMat3x4_Type;
+extern const std::shared_ptr<Type> kMat4x2_Type;
+extern const std::shared_ptr<Type> kMat4x3_Type;
+extern const std::shared_ptr<Type> kMat4x4_Type;
+
+extern const std::shared_ptr<Type> kDMat2x2_Type;
+extern const std::shared_ptr<Type> kDMat2x3_Type;
+extern const std::shared_ptr<Type> kDMat2x4_Type;
+extern const std::shared_ptr<Type> kDMat3x2_Type;
+extern const std::shared_ptr<Type> kDMat3x3_Type;
+extern const std::shared_ptr<Type> kDMat3x4_Type;
+extern const std::shared_ptr<Type> kDMat4x2_Type;
+extern const std::shared_ptr<Type> kDMat4x3_Type;
+extern const std::shared_ptr<Type> kDMat4x4_Type;
+
+extern const std::shared_ptr<Type> kSampler1D_Type;
+extern const std::shared_ptr<Type> kSampler2D_Type;
+extern const std::shared_ptr<Type> kSampler3D_Type;
+extern const std::shared_ptr<Type> kSamplerCube_Type;
+extern const std::shared_ptr<Type> kSampler2DRect_Type;
+extern const std::shared_ptr<Type> kSampler1DArray_Type;
+extern const std::shared_ptr<Type> kSampler2DArray_Type;
+extern const std::shared_ptr<Type> kSamplerCubeArray_Type;
+extern const std::shared_ptr<Type> kSamplerBuffer_Type;
+extern const std::shared_ptr<Type> kSampler2DMS_Type;
+extern const std::shared_ptr<Type> kSampler2DMSArray_Type;
+
+extern const std::shared_ptr<Type> kGSampler1D_Type;
+extern const std::shared_ptr<Type> kGSampler2D_Type;
+extern const std::shared_ptr<Type> kGSampler3D_Type;
+extern const std::shared_ptr<Type> kGSamplerCube_Type;
+extern const std::shared_ptr<Type> kGSampler2DRect_Type;
+extern const std::shared_ptr<Type> kGSampler1DArray_Type;
+extern const std::shared_ptr<Type> kGSampler2DArray_Type;
+extern const std::shared_ptr<Type> kGSamplerCubeArray_Type;
+extern const std::shared_ptr<Type> kGSamplerBuffer_Type;
+extern const std::shared_ptr<Type> kGSampler2DMS_Type;
+extern const std::shared_ptr<Type> kGSampler2DMSArray_Type;
+
+extern const std::shared_ptr<Type> kSampler1DShadow_Type;
+extern const std::shared_ptr<Type> kSampler2DShadow_Type;
+extern const std::shared_ptr<Type> kSamplerCubeShadow_Type;
+extern const std::shared_ptr<Type> kSampler2DRectShadow_Type;
+extern const std::shared_ptr<Type> kSampler1DArrayShadow_Type;
+extern const std::shared_ptr<Type> kSampler2DArrayShadow_Type;
+extern const std::shared_ptr<Type> kSamplerCubeArrayShadow_Type;
+extern const std::shared_ptr<Type> kGSampler2DArrayShadow_Type;
+extern const std::shared_ptr<Type> kGSamplerCubeArrayShadow_Type;
+
+extern const std::shared_ptr<Type> kGenType_Type;
+extern const std::shared_ptr<Type> kGenDType_Type;
+extern const std::shared_ptr<Type> kGenIType_Type;
+extern const std::shared_ptr<Type> kGenUType_Type;
+extern const std::shared_ptr<Type> kGenBType_Type;
+extern const std::shared_ptr<Type> kMat_Type;
+extern const std::shared_ptr<Type> kVec_Type;
+extern const std::shared_ptr<Type> kGVec_Type;
+extern const std::shared_ptr<Type> kGVec2_Type;
+extern const std::shared_ptr<Type> kGVec3_Type;
+extern const std::shared_ptr<Type> kGVec4_Type;
+extern const std::shared_ptr<Type> kDVec_Type;
+extern const std::shared_ptr<Type> kIVec_Type;
+extern const std::shared_ptr<Type> kUVec_Type;
+extern const std::shared_ptr<Type> kBVec_Type;
+
+extern const std::shared_ptr<Type> kInvalid_Type;
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLTypeReference.h b/src/sksl/ir/SkSLTypeReference.h
new file mode 100644
index 0000000000..5f4990f35d
--- /dev/null
+++ b/src/sksl/ir/SkSLTypeReference.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_TYPEREFERENCE
+#define SKSL_TYPEREFERENCE
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * Represents an identifier referring to a type. This is an intermediate value: TypeReferences are
+ * always eventually replaced by Constructors in valid programs.
+ */
+struct TypeReference : public Expression {
+ TypeReference(Position position, std::shared_ptr<Type> type)
+ : INHERITED(position, kTypeReference_Kind, kInvalid_Type)
+ , fValue(std::move(type)) {}
+
+ std::string description() const override {
+ ASSERT(false);
+ return "<type>";
+ }
+
+ const std::shared_ptr<Type> fValue;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLUnresolvedFunction.h b/src/sksl/ir/SkSLUnresolvedFunction.h
new file mode 100644
index 0000000000..a6cee0d072
--- /dev/null
+++ b/src/sksl/ir/SkSLUnresolvedFunction.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_UNRESOLVEDFUNCTION
+#define SKSL_UNRESOLVEDFUNCTION
+
+#include "SkSLFunctionDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * A symbol representing multiple functions with the same name.
+ */
+struct UnresolvedFunction : public Symbol {
+ UnresolvedFunction(std::vector<std::shared_ptr<FunctionDeclaration>> funcs)
+ : INHERITED(Position(), kUnresolvedFunction_Kind, funcs[0]->fName)
+ , fFunctions(std::move(funcs)) {
+ for (auto func : funcs) {
+ ASSERT(func->fName == fName);
+ }
+ }
+
+ virtual std::string description() const override {
+ return fName;
+ }
+
+ const std::vector<std::shared_ptr<FunctionDeclaration>> fFunctions;
+
+ typedef Symbol INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLVarDeclaration.h b/src/sksl/ir/SkSLVarDeclaration.h
new file mode 100644
index 0000000000..400f430e4c
--- /dev/null
+++ b/src/sksl/ir/SkSLVarDeclaration.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_VARDECLARATION
+#define SKSL_VARDECLARATION
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+#include "SkSLVariable.h"
+
+namespace SkSL {
+
+/**
+ * A variable declaration, which may consist of multiple individual variables. For instance
+ * 'int x, y = 1, z[4][2];' is a single VarDeclaration. This declaration would have a type of 'int',
+ * names ['x', 'y', 'z'], sizes of [[], [], [4, 2]], and values of [null, 1, null].
+ */
+struct VarDeclaration : public ProgramElement {
+ VarDeclaration(Position position, std::vector<std::shared_ptr<Variable>> vars,
+ std::vector<std::vector<std::unique_ptr<Expression>>> sizes,
+ std::vector<std::unique_ptr<Expression>> values)
+ : INHERITED(position, kVar_Kind)
+ , fVars(std::move(vars))
+ , fSizes(std::move(sizes))
+ , fValues(std::move(values)) {}
+
+ std::string description() const override {
+ std::string result = fVars[0]->fModifiers.description();
+ std::shared_ptr<Type> baseType = fVars[0]->fType;
+ while (baseType->kind() == Type::kArray_Kind) {
+ baseType = baseType->componentType();
+ }
+ result += baseType->description();
+ std::string separator = " ";
+ for (size_t i = 0; i < fVars.size(); i++) {
+ result += separator;
+ separator = ", ";
+ result += fVars[i]->fName;
+ for (size_t j = 0; j < fSizes[i].size(); j++) {
+ if (fSizes[i][j]) {
+ result += "[" + fSizes[i][j]->description() + "]";
+ } else {
+ result += "[]";
+ }
+ }
+ if (fValues[i]) {
+ result += " = " + fValues[i]->description();
+ }
+ }
+ result += ";";
+ return result;
+ }
+
+ const std::vector<std::shared_ptr<Variable>> fVars;
+ const std::vector<std::vector<std::unique_ptr<Expression>>> fSizes;
+ const std::vector<std::unique_ptr<Expression>> fValues;
+
+ typedef ProgramElement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLVarDeclarationStatement.h b/src/sksl/ir/SkSLVarDeclarationStatement.h
new file mode 100644
index 0000000000..e81c0ac3ec
--- /dev/null
+++ b/src/sksl/ir/SkSLVarDeclarationStatement.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_VARDECLARATIONSTATEMENT
+#define SKSL_VARDECLARATIONSTATEMENT
+
+#include "SkSLStatement.h"
+#include "SkSLVarDeclaration.h"
+
+namespace SkSL {
+
+/**
+ * A variable declaration appearing as a statement within a function.
+ */
+struct VarDeclarationStatement : public Statement {
+ VarDeclarationStatement(std::unique_ptr<VarDeclaration> decl)
+ : INHERITED(decl->fPosition, kVarDeclaration_Kind)
+ , fDeclaration(std::move(decl)) {}
+
+ std::string description() const override {
+ return fDeclaration->description();
+ }
+
+ const std::shared_ptr<VarDeclaration> fDeclaration;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
new file mode 100644
index 0000000000..d4ea2c4a43
--- /dev/null
+++ b/src/sksl/ir/SkSLVariable.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_VARIABLE
+#define SKSL_VARIABLE
+
+#include "SkSLModifiers.h"
+#include "SkSLPosition.h"
+#include "SkSLSymbol.h"
+#include "SkSLType.h"
+
+namespace SkSL {
+
+/**
+ * Represents a variable, whether local, global, or a function parameter. This represents the
+ * variable itself (the storage location), which is shared between all VariableReferences which
+ * read or write that storage location.
+ */
+struct Variable : public Symbol {
+ enum Storage {
+ kGlobal_Storage,
+ kLocal_Storage,
+ kParameter_Storage
+ };
+
+ Variable(Position position, Modifiers modifiers, std::string name, std::shared_ptr<Type> type,
+ Storage storage)
+ : INHERITED(position, kVariable_Kind, std::move(name))
+ , fModifiers(modifiers)
+ , fType(type)
+ , fStorage(storage)
+ , fIsReadFrom(false)
+ , fIsWrittenTo(false) {}
+
+ virtual std::string description() const override {
+ return fModifiers.description() + fType->fName + " " + fName;
+ }
+
+ const Modifiers fModifiers;
+ const std::string fValue;
+ const std::shared_ptr<Type> fType;
+ const Storage fStorage;
+
+ mutable bool fIsReadFrom;
+ mutable bool fIsWrittenTo;
+
+ typedef Symbol INHERITED;
+};
+
+} // namespace SkSL
+
+namespace std {
+ template <>
+ struct hash<SkSL::Variable> {
+ public :
+ size_t operator()(const SkSL::Variable &var) const{
+ return hash<std::string>()(var.fName) ^ hash<std::string>()(var.fType->description());
+ }
+ };
+} // namespace std
+
+#endif
diff --git a/src/sksl/ir/SkSLVariableReference.h b/src/sksl/ir/SkSLVariableReference.h
new file mode 100644
index 0000000000..8499511a1b
--- /dev/null
+++ b/src/sksl/ir/SkSLVariableReference.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_VARIABLEREFERENCE
+#define SKSL_VARIABLEREFERENCE
+
+#include "SkSLExpression.h"
+
+namespace SkSL {
+
+/**
+ * A reference to a variable, through which it can be read or written. In the statement:
+ *
+ * x = x + 1;
+ *
+ * there is only one Variable 'x', but two VariableReferences to it.
+ */
+struct VariableReference : public Expression {
+ VariableReference(Position position, std::shared_ptr<Variable> variable)
+ : INHERITED(position, kVariableReference_Kind, variable->fType)
+ , fVariable(std::move(variable)) {}
+
+ std::string description() const override {
+ return fVariable->fName;
+ }
+
+ const std::shared_ptr<Variable> fVariable;
+
+ typedef Expression INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLWhileStatement.h b/src/sksl/ir/SkSLWhileStatement.h
new file mode 100644
index 0000000000..1acb572583
--- /dev/null
+++ b/src/sksl/ir/SkSLWhileStatement.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_WHILESTATEMENT
+#define SKSL_WHILESTATEMENT
+
+#include "SkSLExpression.h"
+#include "SkSLStatement.h"
+
+namespace SkSL {
+
+/**
+ * A 'while' loop.
+ */
+struct WhileStatement : public Statement {
+ WhileStatement(Position position, std::unique_ptr<Expression> test,
+ std::unique_ptr<Statement> statement)
+ : INHERITED(position, kWhile_Kind)
+ , fTest(std::move(test))
+ , fStatement(std::move(statement)) {}
+
+ std::string description() const override {
+ return "while (" + fTest->description() + ") " + fStatement->description();
+ }
+
+ const std::unique_ptr<Expression> fTest;
+ const std::unique_ptr<Statement> fStatement;
+
+ typedef Statement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/lex.sksl.c b/src/sksl/lex.sksl.c
new file mode 100644
index 0000000000..7afbd942ff
--- /dev/null
+++ b/src/sksl/lex.sksl.c
@@ -0,0 +1,2473 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#line 3 "lex.sksl.c"
+
+#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
+#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;
+#endif /* ! C99 */
+
+/* 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 /* ! 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 skslrestart(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
+
+#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 sksllex.
+ * 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)
+
+/* 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 skslrestart()), 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 skslrestart (FILE *input_file ,yyscan_t yyscanner );
+void sksl_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE sksl_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void sksl_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void sksl_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void skslpush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void skslpop_buffer_state (yyscan_t yyscanner );
+
+static void skslensure_buffer_stack (yyscan_t yyscanner );
+static void sksl_load_buffer_state (yyscan_t yyscanner );
+static void sksl_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER sksl_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)
+
+YY_BUFFER_STATE sksl_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE sksl_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE sksl_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *skslalloc (yy_size_t ,yyscan_t yyscanner );
+void *skslrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void skslfree (void * ,yyscan_t yyscanner );
+
+#define yy_new_buffer sksl_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ skslensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sksl_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 ){\
+ skslensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ sksl_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 */
+
+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 80
+#define YY_END_OF_BUFFER 81
+/* 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[185] =
+ { 0,
+ 0, 0, 81, 79, 78, 78, 52, 79, 27, 43,
+ 48, 29, 30, 41, 39, 36, 40, 35, 42, 4,
+ 54, 75, 59, 55, 58, 53, 33, 34, 47, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 31, 46, 32, 78, 57,
+ 28, 27, 66, 51, 71, 64, 37, 62, 38, 63,
+ 1, 0, 76, 65, 2, 4, 0, 44, 61, 56,
+ 60, 45, 70, 50, 27, 27, 27, 11, 27, 27,
+ 27, 27, 7, 16, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 69, 49, 28, 74, 0, 0,
+
+ 0, 76, 1, 0, 0, 3, 67, 68, 73, 27,
+ 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
+ 17, 27, 27, 27, 27, 27, 27, 72, 0, 1,
+ 77, 0, 0, 2, 27, 27, 27, 27, 8, 27,
+ 27, 27, 27, 21, 27, 27, 27, 27, 5, 27,
+ 27, 0, 1, 12, 20, 27, 27, 6, 23, 18,
+ 27, 27, 27, 27, 27, 27, 10, 27, 27, 25,
+ 27, 27, 15, 24, 27, 27, 14, 22, 27, 19,
+ 13, 27, 26, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 1, 5, 6, 7, 8, 1, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 17, 17,
+ 17, 17, 17, 17, 17, 17, 17, 18, 19, 20,
+ 21, 22, 23, 1, 6, 6, 6, 6, 24, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 25, 1, 26, 27, 6, 1, 28, 29, 30, 31,
+
+ 32, 33, 34, 35, 36, 6, 37, 38, 39, 40,
+ 41, 42, 6, 43, 44, 45, 46, 6, 47, 6,
+ 48, 6, 49, 50, 51, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 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[52] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 4, 1, 1, 1,
+ 1, 1, 1, 3, 1, 1, 1, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 1, 1,
+ 1
+ } ;
+
+static yyconst flex_int16_t yy_base[190] =
+ { 0,
+ 0, 0, 222, 223, 50, 52, 200, 0, 0, 199,
+ 48, 223, 223, 198, 45, 223, 44, 201, 51, 44,
+ 223, 223, 43, 196, 49, 223, 223, 223, 52, 173,
+ 174, 39, 176, 46, 177, 44, 50, 180, 165, 167,
+ 177, 163, 164, 166, 170, 223, 39, 223, 79, 223,
+ 0, 0, 223, 183, 223, 223, 223, 223, 223, 223,
+ 66, 192, 0, 223, 68, 71, 82, 181, 223, 223,
+ 223, 180, 223, 179, 167, 158, 153, 0, 152, 157,
+ 151, 159, 0, 151, 143, 143, 158, 143, 155, 141,
+ 142, 138, 147, 146, 223, 160, 0, 223, 90, 169,
+
+ 163, 0, 84, 97, 161, 160, 223, 223, 223, 148,
+ 61, 145, 142, 129, 0, 137, 125, 129, 127, 132,
+ 0, 137, 120, 119, 132, 130, 124, 223, 144, 143,
+ 223, 98, 142, 141, 120, 111, 119, 126, 0, 121,
+ 110, 106, 104, 0, 103, 112, 104, 116, 0, 104,
+ 112, 126, 125, 0, 0, 101, 97, 0, 0, 0,
+ 94, 99, 93, 96, 90, 91, 0, 87, 101, 0,
+ 89, 94, 0, 0, 90, 94, 0, 0, 72, 0,
+ 0, 57, 0, 223, 90, 114, 116, 120, 124
+ } ;
+
+static yyconst flex_int16_t yy_def[190] =
+ { 0,
+ 184, 1, 184, 184, 184, 184, 184, 185, 186, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 184, 184, 184, 184, 184,
+ 187, 186, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 188, 189, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 184, 184, 187, 184, 184, 188,
+
+ 188, 189, 184, 184, 184, 184, 184, 184, 184, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 184, 184, 184,
+ 184, 184, 184, 184, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 184, 184, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
+ 186, 186, 186, 0, 184, 184, 184, 184, 184
+ } ;
+
+static yyconst flex_int16_t yy_nxt[275] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 9, 27, 28, 29, 9, 30, 31,
+ 32, 33, 34, 9, 35, 36, 9, 37, 38, 9,
+ 39, 40, 41, 42, 43, 44, 45, 9, 46, 47,
+ 48, 49, 49, 49, 49, 54, 57, 59, 65, 95,
+ 66, 62, 68, 69, 60, 58, 63, 67, 55, 71,
+ 72, 64, 73, 80, 77, 67, 83, 85, 74, 78,
+ 49, 49, 61, 84, 103, 65, 81, 66, 96, 99,
+ 86, 104, 51, 105, 67, 105, 183, 99, 106, 104,
+
+ 103, 129, 67, 129, 136, 137, 130, 132, 133, 152,
+ 133, 152, 182, 134, 153, 132, 52, 52, 97, 97,
+ 100, 100, 100, 100, 102, 181, 102, 102, 180, 179,
+ 178, 177, 176, 175, 174, 173, 172, 171, 170, 169,
+ 168, 153, 153, 167, 166, 165, 164, 163, 162, 161,
+ 160, 159, 158, 157, 156, 155, 154, 134, 134, 130,
+ 130, 151, 150, 149, 148, 147, 146, 145, 144, 143,
+ 142, 141, 140, 139, 138, 135, 106, 106, 131, 101,
+ 128, 127, 126, 125, 124, 123, 122, 121, 120, 119,
+ 118, 117, 116, 115, 114, 113, 112, 111, 110, 109,
+
+ 108, 107, 101, 98, 94, 93, 92, 91, 90, 89,
+ 88, 87, 82, 79, 76, 75, 70, 61, 56, 53,
+ 50, 184, 3, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184
+ } ;
+
+static yyconst flex_int16_t yy_chk[275] =
+ { 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, 5, 5, 6, 6, 11, 15, 17, 20, 47,
+ 20, 19, 23, 23, 17, 15, 19, 20, 11, 25,
+ 25, 19, 29, 34, 32, 20, 36, 37, 29, 32,
+ 49, 49, 61, 36, 65, 66, 34, 66, 47, 61,
+ 37, 65, 185, 67, 66, 67, 182, 61, 67, 65,
+
+ 103, 99, 66, 99, 111, 111, 99, 103, 104, 132,
+ 104, 132, 179, 104, 132, 103, 186, 186, 187, 187,
+ 188, 188, 188, 188, 189, 176, 189, 189, 175, 172,
+ 171, 169, 168, 166, 165, 164, 163, 162, 161, 157,
+ 156, 153, 152, 151, 150, 148, 147, 146, 145, 143,
+ 142, 141, 140, 138, 137, 136, 135, 134, 133, 130,
+ 129, 127, 126, 125, 124, 123, 122, 120, 119, 118,
+ 117, 116, 114, 113, 112, 110, 106, 105, 101, 100,
+ 96, 94, 93, 92, 91, 90, 89, 88, 87, 86,
+ 85, 84, 82, 81, 80, 79, 77, 76, 75, 74,
+
+ 72, 68, 62, 54, 45, 44, 43, 42, 41, 40,
+ 39, 38, 35, 33, 31, 30, 24, 18, 14, 10,
+ 7, 3, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,
+ 184, 184, 184, 184
+ } ;
+
+/* Table of booleans, true if rule could match eol. */
+static yyconst flex_int32_t yy_rule_can_match_eol[81] =
+ { 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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
+#line 1 "sksl.flex"
+/*
+
+ This file is IGNORED during the build process!
+
+ As this file is updated so infrequently and flex is not universally present on build machines,
+ the lex.sksl.c file must be manually regenerated if you make any changes to this file. Just run:
+
+ flex sksl.flex
+
+*/
+#define YY_NO_UNISTD_H 1
+#line 582 "lex.sksl.c"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* 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;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+int sksllex_init (yyscan_t* scanner);
+
+int sksllex_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 sksllex_destroy (yyscan_t yyscanner );
+
+int skslget_debug (yyscan_t yyscanner );
+
+void skslset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE skslget_extra (yyscan_t yyscanner );
+
+void skslset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *skslget_in (yyscan_t yyscanner );
+
+void skslset_in (FILE * in_str ,yyscan_t yyscanner );
+
+FILE *skslget_out (yyscan_t yyscanner );
+
+void skslset_out (FILE * out_str ,yyscan_t yyscanner );
+
+yy_size_t skslget_leng (yyscan_t yyscanner );
+
+char *skslget_text (yyscan_t yyscanner );
+
+int skslget_lineno (yyscan_t yyscanner );
+
+void skslset_lineno (int line_number ,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 skslwrap (yyscan_t yyscanner );
+#else
+extern int skslwrap (yyscan_t yyscanner );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#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 fwrite( yytext, yyleng, 1, yyout )
+#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 = '*'; \
+ yy_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 sksllex (yyscan_t yyscanner);
+
+#define YY_DECL int sksllex (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;
+
+#line 21 "sksl.flex"
+
+
+#line 806 "lex.sksl.c"
+
+ 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 ) {
+ skslensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ sksl_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ sksl_load_buffer_state(yyscanner );
+ }
+
+ 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 >= 185 )
+ 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 != 184 );
+ 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
+#line 23 "sksl.flex"
+{ return SkSL::Token::FLOAT_LITERAL; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 25 "sksl.flex"
+{ return SkSL::Token::FLOAT_LITERAL; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 27 "sksl.flex"
+{ return SkSL::Token::FLOAT_LITERAL; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 29 "sksl.flex"
+{ return SkSL::Token::INT_LITERAL; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 31 "sksl.flex"
+{ return SkSL::Token::TRUE_LITERAL; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 33 "sksl.flex"
+{ return SkSL::Token::FALSE_LITERAL; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 35 "sksl.flex"
+{ return SkSL::Token::IF; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 37 "sksl.flex"
+{ return SkSL::Token::ELSE; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 39 "sksl.flex"
+{ return SkSL::Token::FOR; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 41 "sksl.flex"
+{ return SkSL::Token::WHILE; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 43 "sksl.flex"
+{ return SkSL::Token::DO; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 45 "sksl.flex"
+{ return SkSL::Token::BREAK; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 47 "sksl.flex"
+{ return SkSL::Token::CONTINUE; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 49 "sksl.flex"
+{ return SkSL::Token::DISCARD; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 51 "sksl.flex"
+{ return SkSL::Token::RETURN; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 53 "sksl.flex"
+{ return SkSL::Token::IN; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 55 "sksl.flex"
+{ return SkSL::Token::OUT; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 57 "sksl.flex"
+{ return SkSL::Token::INOUT; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 59 "sksl.flex"
+{ return SkSL::Token::UNIFORM; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 61 "sksl.flex"
+{ return SkSL::Token::CONST; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 63 "sksl.flex"
+{ return SkSL::Token::LOWP; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 65 "sksl.flex"
+{ return SkSL::Token::MEDIUMP; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 67 "sksl.flex"
+{ return SkSL::Token::HIGHP; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 69 "sksl.flex"
+{ return SkSL::Token::STRUCT; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 71 "sksl.flex"
+{ return SkSL::Token::LAYOUT; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 73 "sksl.flex"
+{ return SkSL::Token::PRECISION; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 75 "sksl.flex"
+{ return SkSL::Token::IDENTIFIER; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 77 "sksl.flex"
+{ return SkSL::Token::DIRECTIVE; }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 79 "sksl.flex"
+{ return SkSL::Token::LPAREN; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 81 "sksl.flex"
+{ return SkSL::Token::RPAREN; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 83 "sksl.flex"
+{ return SkSL::Token::LBRACE; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 85 "sksl.flex"
+{ return SkSL::Token::RBRACE; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 87 "sksl.flex"
+{ return SkSL::Token::LBRACKET; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 89 "sksl.flex"
+{ return SkSL::Token::RBRACKET; }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 91 "sksl.flex"
+{ return SkSL::Token::DOT; }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 93 "sksl.flex"
+{ return SkSL::Token::COMMA; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 95 "sksl.flex"
+{ return SkSL::Token::PLUSPLUS; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 97 "sksl.flex"
+{ return SkSL::Token::MINUSMINUS; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 99 "sksl.flex"
+{ return SkSL::Token::PLUS; }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 101 "sksl.flex"
+{ return SkSL::Token::MINUS; }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 103 "sksl.flex"
+{ return SkSL::Token::STAR; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 105 "sksl.flex"
+{ return SkSL::Token::SLASH; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 107 "sksl.flex"
+{ return SkSL::Token::PERCENT; }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 109 "sksl.flex"
+{ return SkSL::Token::SHL; }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 111 "sksl.flex"
+{ return SkSL::Token::SHR; }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 113 "sksl.flex"
+{ return SkSL::Token::BITWISEOR; }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 115 "sksl.flex"
+{ return SkSL::Token::BITWISEXOR; }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 117 "sksl.flex"
+{ return SkSL::Token::BITWISEAND; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 119 "sksl.flex"
+{ return SkSL::Token::LOGICALOR; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 121 "sksl.flex"
+{ return SkSL::Token::LOGICALXOR; }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 123 "sksl.flex"
+{ return SkSL::Token::LOGICALAND; }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 125 "sksl.flex"
+{ return SkSL::Token::NOT; }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 127 "sksl.flex"
+{ return SkSL::Token::QUESTION; }
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 129 "sksl.flex"
+{ return SkSL::Token::COLON; }
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 131 "sksl.flex"
+{ return SkSL::Token::EQ; }
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 133 "sksl.flex"
+{ return SkSL::Token::EQEQ; }
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 135 "sksl.flex"
+{ return SkSL::Token::NEQ; }
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 137 "sksl.flex"
+{ return SkSL::Token::GT; }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 139 "sksl.flex"
+{ return SkSL::Token::LT; }
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 141 "sksl.flex"
+{ return SkSL::Token::GTEQ; }
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 143 "sksl.flex"
+{ return SkSL::Token::LTEQ; }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 145 "sksl.flex"
+{ return SkSL::Token::PLUSEQ; }
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 147 "sksl.flex"
+{ return SkSL::Token::MINUSEQ; }
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 149 "sksl.flex"
+{ return SkSL::Token::STAREQ; }
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 151 "sksl.flex"
+{ return SkSL::Token::SLASHEQ; }
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 153 "sksl.flex"
+{ return SkSL::Token::PERCENTEQ; }
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 155 "sksl.flex"
+{ return SkSL::Token::SHLEQ; }
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 157 "sksl.flex"
+{ return SkSL::Token::SHREQ; }
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 159 "sksl.flex"
+{ return SkSL::Token::BITWISEOREQ; }
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 161 "sksl.flex"
+{ return SkSL::Token::BITWISEXOREQ; }
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 163 "sksl.flex"
+{ return SkSL::Token::BITWISEANDEQ; }
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 165 "sksl.flex"
+{ return SkSL::Token::LOGICALOREQ; }
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 167 "sksl.flex"
+{ return SkSL::Token::LOGICALXOREQ; }
+ YY_BREAK
+case 74:
+YY_RULE_SETUP
+#line 169 "sksl.flex"
+{ return SkSL::Token::LOGICALANDEQ; }
+ YY_BREAK
+case 75:
+YY_RULE_SETUP
+#line 171 "sksl.flex"
+{ return SkSL::Token::SEMICOLON; }
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 173 "sksl.flex"
+/* line comment */
+ YY_BREAK
+case 77:
+/* rule 77 can match eol */
+YY_RULE_SETUP
+#line 175 "sksl.flex"
+/* block comment */
+ YY_BREAK
+case 78:
+/* rule 78 can match eol */
+YY_RULE_SETUP
+#line 177 "sksl.flex"
+/* whitespace */
+ YY_BREAK
+case 79:
+YY_RULE_SETUP
+#line 179 "sksl.flex"
+{ return SkSL::Token::INVALID_TOKEN; }
+ YY_BREAK
+case 80:
+YY_RULE_SETUP
+#line 181 "sksl.flex"
+ECHO;
+ YY_BREAK
+#line 1299 "lex.sksl.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ 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
+ * sksllex(). 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 ( skslwrap(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 sksllex */
+
+/* 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;
+
+ 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. */
+ skslrealloc((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;
+ skslrestart(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 *) skslrealloc((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 >= 185 )
+ 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 >= 185 )
+ 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 == 184);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register yy_size_t number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ if ( c == '\n' ){
+ --yylineno;
+ }
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#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. */
+ skslrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( skslwrap(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;
+
+ 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 skslrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ skslensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ sksl_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ sksl_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ sksl_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void sksl_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
+ * skslpop_buffer_state();
+ * skslpush_buffer_state(new_buffer);
+ */
+ skslensure_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;
+ sksl_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (skslwrap()) processing, but the only time this flag
+ * is looked at is after skslwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void sksl_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 sksl_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) skslalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sksl_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 *) skslalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sksl_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ sksl_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with sksl_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void sksl_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 )
+ skslfree((void *) b->yy_ch_buf ,yyscanner );
+
+ skslfree((void *) b ,yyscanner );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a skslrestart() or at EOF.
+ */
+ static void sksl_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ sksl_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then sksl_init_buffer was _probably_
+ * called from skslrestart() 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 sksl_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 )
+ sksl_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 skslpush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ skslensure_buffer_stack(yyscanner);
+
+ /* This block is copied from sksl_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 sksl_switch_to_buffer. */
+ sksl_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 skslpop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ sksl_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) {
+ sksl_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 skslensure_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**)skslalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in skslensure_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**)skslrealloc
+ (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 skslensure_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 sksl_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) skslalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in sksl_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;
+
+ sksl_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to sksllex() 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
+ * sksl_scan_bytes() instead.
+ */
+YY_BUFFER_STATE sksl_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return sksl_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to sksllex() 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 yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE sksl_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n, i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) skslalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in sksl_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 = sksl_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in sksl_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 skslget_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 skslget_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 skslget_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 *skslget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *skslget_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 skslget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *skslget_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 skslset_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 skslset_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( "skslset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void skslset_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( "skslset_column called with no buffer" , yyscanner);
+
+ 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 sksl_switch_to_buffer
+ */
+void skslset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void skslset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int skslget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void skslset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* sksllex_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 sksllex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) skslalloc ( 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 );
+}
+
+/* sksllex_init_extra has the same functionality as sksllex_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 skslalloc in
+ * the yyextra field.
+ */
+
+int sksllex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ skslset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) skslalloc ( 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));
+
+ skslset_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 sksllex_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
+ * sksllex_init()
+ */
+ return 0;
+}
+
+/* sksllex_destroy is for both reentrant and non-reentrant scanners. */
+int sksllex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ sksl_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ skslpop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ skslfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ skslfree(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
+ * sksllex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ skslfree ( 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 *skslalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *skslrealloc (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 skslfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see skslrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 181 "sksl.flex"
+
+
+
+int skslwrap(yyscan_t scanner) {
+ return 1; // terminate
+}
+
diff --git a/src/sksl/sksl.flex b/src/sksl/sksl.flex
new file mode 100644
index 0000000000..cbc938a3bb
--- /dev/null
+++ b/src/sksl/sksl.flex
@@ -0,0 +1,187 @@
+/*
+
+ This file is IGNORED during the build process!
+
+ As this file is updated so infrequently and flex is not universally present on build machines,
+ the lex.sksl.c file must be manually regenerated if you make any changes to this file. Just run:
+
+ flex sksl.flex
+
+ You will have to manually add a copyright notice to the top of lex.sksl.c.
+
+*/
+
+%option prefix="sksl"
+%option reentrant
+%option yylineno
+%option never-interactive
+%option nounistd
+
+DIGIT [0-9]
+LETTER [a-zA-Z_$]
+
+%%
+
+{DIGIT}*"."{DIGIT}+([eE][+-]?{DIGIT}+)? { return SkSL::Token::FLOAT_LITERAL; }
+
+{DIGIT}+"."{DIGIT}*([eE][+-]?{DIGIT}+)? { return SkSL::Token::FLOAT_LITERAL; }
+
+{DIGIT}+([eE][+-]?{DIGIT}+) { return SkSL::Token::FLOAT_LITERAL; }
+
+{DIGIT}+ { return SkSL::Token::INT_LITERAL; }
+
+true { return SkSL::Token::TRUE_LITERAL; }
+
+false { return SkSL::Token::FALSE_LITERAL; }
+
+if { return SkSL::Token::IF; }
+
+else { return SkSL::Token::ELSE; }
+
+for { return SkSL::Token::FOR; }
+
+while { return SkSL::Token::WHILE; }
+
+do { return SkSL::Token::DO; }
+
+break { return SkSL::Token::BREAK; }
+
+continue { return SkSL::Token::CONTINUE; }
+
+discard { return SkSL::Token::DISCARD; }
+
+return { return SkSL::Token::RETURN; }
+
+in { return SkSL::Token::IN; }
+
+out { return SkSL::Token::OUT; }
+
+inout { return SkSL::Token::INOUT; }
+
+uniform { return SkSL::Token::UNIFORM; }
+
+const { return SkSL::Token::CONST; }
+
+lowp { return SkSL::Token::LOWP; }
+
+mediump { return SkSL::Token::MEDIUMP; }
+
+highp { return SkSL::Token::HIGHP; }
+
+struct { return SkSL::Token::STRUCT; }
+
+layout { return SkSL::Token::LAYOUT; }
+
+precision { return SkSL::Token::PRECISION; }
+
+{LETTER}({DIGIT}|{LETTER})* { return SkSL::Token::IDENTIFIER; }
+
+"#"{LETTER}({DIGIT}|{LETTER})* { return SkSL::Token::DIRECTIVE; }
+
+"(" { return SkSL::Token::LPAREN; }
+
+")" { return SkSL::Token::RPAREN; }
+
+"{" { return SkSL::Token::LBRACE; }
+
+"}" { return SkSL::Token::RBRACE; }
+
+"[" { return SkSL::Token::LBRACKET; }
+
+"]" { return SkSL::Token::RBRACKET; }
+
+"." { return SkSL::Token::DOT; }
+
+"," { return SkSL::Token::COMMA; }
+
+"++" { return SkSL::Token::PLUSPLUS; }
+
+"--" { return SkSL::Token::MINUSMINUS; }
+
+"+" { return SkSL::Token::PLUS; }
+
+"-" { return SkSL::Token::MINUS; }
+
+"*" { return SkSL::Token::STAR; }
+
+"/" { return SkSL::Token::SLASH; }
+
+"%" { return SkSL::Token::PERCENT; }
+
+"<<" { return SkSL::Token::SHL; }
+
+">>" { return SkSL::Token::SHR; }
+
+"|" { return SkSL::Token::BITWISEOR; }
+
+"^" { return SkSL::Token::BITWISEXOR; }
+
+"&" { return SkSL::Token::BITWISEAND; }
+
+"||" { return SkSL::Token::LOGICALOR; }
+
+"^^" { return SkSL::Token::LOGICALXOR; }
+
+"&&" { return SkSL::Token::LOGICALAND; }
+
+"!" { return SkSL::Token::NOT; }
+
+"?" { return SkSL::Token::QUESTION; }
+
+":" { return SkSL::Token::COLON; }
+
+"=" { return SkSL::Token::EQ; }
+
+"==" { return SkSL::Token::EQEQ; }
+
+"!=" { return SkSL::Token::NEQ; }
+
+">" { return SkSL::Token::GT; }
+
+"<" { return SkSL::Token::LT; }
+
+">=" { return SkSL::Token::GTEQ; }
+
+"<=" { return SkSL::Token::LTEQ; }
+
+"+=" { return SkSL::Token::PLUSEQ; }
+
+"-=" { return SkSL::Token::MINUSEQ; }
+
+"*=" { return SkSL::Token::STAREQ; }
+
+"/=" { return SkSL::Token::SLASHEQ; }
+
+"%=" { return SkSL::Token::PERCENTEQ; }
+
+"<<=" { return SkSL::Token::SHLEQ; }
+
+">>=" { return SkSL::Token::SHREQ; }
+
+"|=" { return SkSL::Token::BITWISEOREQ; }
+
+"^=" { return SkSL::Token::BITWISEXOREQ; }
+
+"&=" { return SkSL::Token::BITWISEANDEQ; }
+
+"||=" { return SkSL::Token::LOGICALOREQ; }
+
+"^^=" { return SkSL::Token::LOGICALXOREQ; }
+
+"&&=" { return SkSL::Token::LOGICALANDEQ; }
+
+";" { return SkSL::Token::SEMICOLON; }
+
+"//".* /* line comment */
+
+"/*"([^*]|"*"[^/])*"*/" /* block comment */
+
+[ \t\r\n]+ /* whitespace */
+
+. { return SkSL::Token::INVALID_TOKEN; }
+
+%%
+
+int skslwrap(yyscan_t scanner) {
+ return 1; // terminate
+}
diff --git a/src/sksl/sksl.include b/src/sksl/sksl.include
new file mode 100644
index 0000000000..bf35f22dbd
--- /dev/null
+++ b/src/sksl/sksl.include
@@ -0,0 +1,543 @@
+STRINGIFY(
+
+// defines built-in functions supported by SkiaSL
+
+$genType radians($genType degrees);
+$genType sin($genType angle);
+$genType cos($genType angle);
+$genType tan($genType angle);
+$genType asin($genType x);
+$genType acos($genType x);
+$genType atan($genType y, $genType x);
+$genType atan($genType y_over_x);
+$genType sinh($genType x);
+$genType cosh($genType x);
+$genType tanh($genType x);
+$genType asinh($genType x);
+$genType acosh($genType x);
+$genType atanh($genType x);
+$genType pow($genType x, $genType y);
+$genType exp($genType x);
+$genType log($genType x);
+$genType exp2($genType x);
+$genType log2($genType x);
+$genType sqrt($genType x);
+$genDType sqrt($genDType x);
+$genType inversesqrt($genType x);
+$genDType inversesqrt($genDType x);
+$genType abs($genType x);
+$genIType abs($genIType x);
+$genDType abs($genDType x);
+$genType sign($genType x);
+$genIType sign($genIType x);
+$genDType sign($genDType x);
+$genType floor($genType x);
+$genDType floor($genDType x);
+$genType trunc($genType x);
+$genDType trunc($genDType x);
+$genType round($genType x);
+$genDType round($genDType x);
+$genType roundEven($genType x);
+$genDType roundEven($genDType x);
+$genType ceil($genType x);
+$genDType ceil($genDType x);
+$genType fract($genType x);
+$genDType fract($genDType x);
+$genType mod($genType x, float y);
+$genType mod($genType x, $genType y);
+$genDType mod($genDType x, double y);
+$genDType mod($genDType x, $genDType y);
+$genType modf($genType x, out $genType i);
+$genDType modf($genDType x, out $genDType i);
+$genType min($genType x, $genType y);
+$genType min($genType x, float y);
+$genDType min($genDType x, $genDType y);
+$genDType min($genDType x, double y);
+$genIType min($genIType x, $genIType y);
+$genIType min($genIType x, int y);
+$genUType min($genUType x, $genUType y);
+$genUType min($genUType x, uint y);
+$genType max($genType x, $genType y);
+$genType max($genType x, float y);
+$genDType max($genDType x, $genDType y);
+$genDType max($genDType x, double y);
+$genIType max($genIType x, $genIType y);
+$genIType max($genIType x, int y);
+$genUType max($genUType x, $genUType y);
+$genUType max($genUType x, uint y);
+$genType clamp($genType x, $genType minVal, $genType maxVal);
+$genType clamp($genType x, float minVal, float maxVal);
+$genDType clamp($genDType x, $genDType minVal, $genDType maxVal);
+$genDType clamp($genDType x, double minVal, double maxVal);
+$genIType clamp($genIType x, $genIType minVal, $genIType maxVal);
+$genIType clamp($genIType x, int minVal, int maxVal);
+$genUType clamp($genUType x, $genUType minVal, $genUType maxVal);
+$genUType clamp($genUType x, uint minVal, uint maxVal);
+$genType mix($genType x, $genType y, $genType a);
+$genType mix($genType x, $genType y, float a);
+$genDType mix($genDType x, $genDType y, $genDType a);
+$genDType mix($genDType x, $genDType y, double a);
+$genType mix($genType x, $genType y, $genBType a);
+$genDType mix($genDType x, $genDType y, $genBType a);
+$genIType mix($genIType x, $genIType y, $genBType a);
+$genUType mix($genUType x, $genUType y, $genBType a);
+$genBType mix($genBType x, $genBType y, $genBType a);
+$genType step($genType edge, $genType x);
+$genType step(float edge, $genType x);
+$genDType step($genDType edge, $genDType x);
+$genDType step(double edge, $genDType x);
+$genType smoothstep($genType edge0, $genType edge1, $genType x);
+$genType smoothstep(float edge0, float edge1, $genType x);
+$genDType smoothstep($genDType edge0, $genDType edge1, $genDType x);
+$genDType smoothstep(double edge0, double edge1, $genDType x);
+$genBType isnan($genType x);
+$genBType isnan($genDType x);
+$genBType isinf($genType x);
+$genBType isinf($genDType x);
+$genIType floatBitsToInt($genType value);
+$genUType floatBitsToUint($genType value);
+$genType intBitsToFloat($genIType value);
+$genType uintBitsToFloat($genUType value);
+$genType fma($genType a, $genType b, $genType c);
+$genDType fma($genDType a, $genDType b, $genDType c);
+$genType frexp($genType x, out $genIType exp);
+$genDType frexp($genDType x, out $genIType exp);
+$genType ldexp($genType x, in $genIType exp);
+$genDType ldexp($genDType x, in $genIType exp);
+uint packUnorm2x16(vec2 v);
+uint packSnorm2x16(vec2 v);
+uint packUnorm4x8(vec4 v);
+uint packSnorm4x8(vec4 v);
+vec2 unpackUnorm2x16(uint p);
+vec2 unpackSnorm2x16(uint p);
+vec4 unpackUnorm4x8(uint p);
+vec4 unpackSnorm4x8(uint p);
+double packDouble2x32(uvec2 v);
+uvec2 unpackDouble2x32(double v);
+uint packHalf2x16(vec2 v);
+vec2 unpackHalf2x16(uint v);
+float length($genType x);
+double length($genDType x);
+float distance($genType p0, $genType p1);
+double distance($genDType p0, $genDType p1);
+float dot($genType x, $genType y);
+double dot($genDType x, $genDType y);
+vec3 cross(vec3 x, vec3 y);
+dvec3 cross(dvec3 x, dvec3 y);
+$genType normalize($genType x);
+$genDType normalize($genDType x);
+vec4 ftransform();
+$genType faceforward($genType N, $genType I, $genType Nref);
+$genDType faceforward($genDType N, $genDType I, $genDType Nref);
+$genType reflect($genType I, $genType N);
+$genDType reflect($genDType I, $genDType N);
+$genType refract($genType I, $genType N, float eta);
+$genDType refract($genDType I, $genDType N, float eta);
+$mat matrixCompMult($mat x, $mat y);
+mat2 outerProduct(vec2 c, vec2 r);
+mat3 outerProduct(vec3 c, vec3 r);
+mat4 outerProduct(vec4 c, vec4 r);
+mat2x3 outerProduct(vec3 c, vec2 r);
+mat3x2 outerProduct(vec2 c, vec3 r);
+mat2x4 outerProduct(vec4 c, vec2 r);
+mat4x2 outerProduct(vec2 c, vec4 r);
+mat3x4 outerProduct(vec4 c, vec3 r);
+mat4x3 outerProduct(vec3 c, vec4 r);
+mat2 transpose(mat2 m);
+mat3 transpose(mat3 m);
+mat4 transpose(mat4 m);
+mat2x3 transpose(mat3x2 m);
+mat3x2 transpose(mat2x3 m);
+mat2x4 transpose(mat4x2 m);
+mat4x2 transpose(mat2x4 m);
+mat3x4 transpose(mat4x3 m);
+mat4x3 transpose(mat3x4 m);
+float determinant(mat2 m);
+float determinant(mat3 m);
+float determinant(mat4 m);
+mat2 inverse(mat2 m);
+mat3 inverse(mat3 m);
+mat4 inverse(mat4 m);
+$bvec lessThan($vec x, $vec y);
+$bvec lessThan($ivec x, $ivec y);
+$bvec lessThan($uvec x, $uvec y);
+$bvec lessThanEqual($vec x, $vec y);
+$bvec lessThanEqual($ivec x, $ivec y);
+$bvec lessThanEqual($uvec x, $uvec y);
+$bvec greaterThan($vec x, $vec y);
+$bvec greaterThan($ivec x, $ivec y);
+$bvec greaterThan($uvec x, $uvec y);
+$bvec greaterThanEqual($vec x, $vec y);
+$bvec greaterThanEqual($ivec x, $ivec y);
+$bvec greaterThanEqual($uvec x, $uvec y);
+$bvec equal($vec x, $vec y);
+$bvec equal($ivec x, $ivec y);
+$bvec equal($uvec x, $uvec y);
+$bvec equal($bvec x, $bvec y);
+$bvec notEqual($vec x, $vec y);
+$bvec notEqual($ivec x, $ivec y);
+$bvec notEqual($uvec x, $uvec y);
+$bvec notEqual($bvec x, $bvec y);
+bool any($bvec x);
+bool all($bvec x);
+$bvec not($bvec x);
+$genUType uaddCarry($genUType x, $genUType y, out $genUType carry);
+$genUType usubBorrow($genUType x, $genUType y, out $genUType borrow);
+void umulExtended($genUType x, $genUType y, out $genUType msb, out $genUType lsb);
+void imulExtended($genIType x, $genIType y, out $genIType msb, out $genIType lsb);
+$genIType bitfieldExtract($genIType value, int offset, int bits);
+$genUType bitfieldExtract($genUType value, int offset, int bits);
+$genIType bitfieldInsert($genIType base, $genIType insert, int offset, int bits);
+$genUType bitfieldInsert($genUType base, $genUType insert, int offset, int bits);
+$genIType bitfieldReverse($genIType value);
+$genUType bitfieldReverse($genUType value);
+$genIType bitCount($genIType value);
+$genIType bitCount($genUType value);
+$genIType findLSB($genIType value);
+$genIType findLSB($genUType value);
+$genIType findMSB($genIType value);
+$genIType findMSB($genUType value);
+int textureSize($gsampler1D sampler, int lod);
+ivec2 textureSize($gsampler2D sampler, int lod);
+ivec3 textureSize($gsampler3D sampler, int lod);
+ivec2 textureSize($gsamplerCube sampler, int lod);
+int textureSize(sampler1DShadow sampler, int lod);
+ivec2 textureSize(sampler2DShadow sampler, int lod);
+ivec2 textureSize(samplerCubeShadow sampler, int lod);
+ivec3 textureSize($gsamplerCubeArray sampler, int lod);
+ivec3 textureSize(samplerCubeArrayShadow sampler, int lod);
+ivec2 textureSize($gsampler2DRect sampler);
+ivec2 textureSize(sampler2DRectShadow sampler);
+ivec2 textureSize($gsampler1DArray sampler, int lod);
+ivec3 textureSize($gsampler2DArray sampler, int lod);
+ivec2 textureSize(sampler1DArrayShadow sampler, int lod);
+ivec3 textureSize(sampler2DArrayShadow sampler, int lod);
+int textureSize($gsamplerBuffer sampler);
+ivec2 textureSize($gsampler2DMS sampler);
+ivec3 textureSize($gsampler2DMSArray sampler);
+vec2 textureQueryLod($gsampler1D sampler, float P);
+vec2 textureQueryLod($gsampler2D sampler, vec2 P);
+vec2 textureQueryLod($gsampler3D sampler, vec3 P);
+vec2 textureQueryLod($gsamplerCube sampler, vec3 P);
+vec2 textureQueryLod($gsampler1DArray sampler, float P);
+vec2 textureQueryLod($gsampler2DArray sampler, vec2 P);
+vec2 textureQueryLod($gsamplerCubeArray sampler, vec3 P);
+vec2 textureQueryLod(sampler1DShadow sampler, float P);
+vec2 textureQueryLod(sampler2DShadow sampler, vec2 P);
+vec2 textureQueryLod(samplerCubeShadow sampler, vec3 P);
+vec2 textureQueryLod(sampler1DArrayShadow sampler, float P);
+vec2 textureQueryLod(sampler2DArrayShadow sampler, vec2 P);
+vec2 textureQueryLod(samplerCubeArrayShadow sampler, vec3 P);
+int textureQueryLevels($gsampler1D sampler);
+int textureQueryLevels($gsampler2D sampler);
+int textureQueryLevels($gsampler3D sampler);
+int textureQueryLevels($gsamplerCube sampler);
+int textureQueryLevels($gsampler1DArray sampler);
+int textureQueryLevels($gsampler2DArray sampler);
+int textureQueryLevels($gsamplerCubeArray sampler);
+int textureQueryLevels(sampler1DShadow sampler);
+int textureQueryLevels(sampler2DShadow sampler);
+int textureQueryLevels(samplerCubeShadow sampler);
+int textureQueryLevels(sampler1DArrayShadow sampler);
+int textureQueryLevels(sampler2DArrayShadow sampler);
+int textureQueryLevels(samplerCubeArrayShadow sampler);
+$gvec4 texture($gsampler1D sampler, float P);
+$gvec4 texture($gsampler1D sampler, float P, float bias);
+$gvec4 texture($gsampler2D sampler, vec2 P);
+$gvec4 texture($gsampler2D sampler, vec2 P, float bias);
+$gvec4 texture($gsampler3D sampler, vec3 P);
+$gvec4 texture($gsampler3D sampler, vec3 P, float bias);
+$gvec4 texture($gsamplerCube sampler, vec3 P);
+$gvec4 texture($gsamplerCube sampler, vec3 P, float bias);
+float texture(sampler1DShadow sampler, vec3 P);
+float texture(sampler1DShadow sampler, vec3 P, float bias);
+float texture(sampler2DShadow sampler, vec3 P);
+float texture(sampler2DShadow sampler, vec3 P, float bias);
+float texture(samplerCubeShadow sampler, vec4 P);
+float texture(samplerCubeShadow sampler, vec4 P, float bias);
+$gvec4 texture($gsampler1DArray sampler, vec2 P);
+$gvec4 texture($gsampler1DArray sampler, vec2 P, float bias);
+$gvec4 texture($gsampler2DArray sampler, vec3 P);
+$gvec4 texture($gsampler2DArray sampler, vec3 P, float bias);
+$gvec4 texture($gsamplerCubeArray sampler, vec4 P);
+$gvec4 texture($gsamplerCubeArray sampler, vec4 P, float bias);
+float texture(sampler1DArrayShadow sampler, vec3 P);
+float texture(sampler1DArrayShadow sampler, vec3 P, float bias);
+float texture(sampler2DArrayShadow sampler, vec4 P);
+$gvec4 texture($gsampler2DRect sampler, vec2 P);
+float texture(sampler2DRectShadow sampler, vec3 P);
+float texture($gsamplerCubeArrayShadow sampler, vec4 P, float compare);
+
+)
+
+// split into multiple chunks, as MSVC++ complains if a single string is too long
+
+STRINGIFY(
+
+$gvec4 textureProj($gsampler1D sampler, vec2 P);
+$gvec4 textureProj($gsampler1D sampler, vec2 P, float bias);
+$gvec4 textureProj($gsampler1D sampler, vec4 P);
+$gvec4 textureProj($gsampler1D sampler, vec4 P, float bias);
+$gvec4 textureProj($gsampler2D sampler, vec3 P);
+$gvec4 textureProj($gsampler2D sampler, vec3 P, float bias);
+$gvec4 textureProj($gsampler2D sampler, vec4 P);
+$gvec4 textureProj($gsampler2D sampler, vec4 P, float bias);
+$gvec4 textureProj($gsampler3D sampler, vec4 P);
+$gvec4 textureProj($gsampler3D sampler, vec4 P, float bias);
+float textureProj(sampler1DShadow sampler, vec4 P);
+float textureProj(sampler1DShadow sampler, vec4 P, float bias);
+float textureProj(sampler2DShadow sampler, vec4 P);
+float textureProj(sampler2DShadow sampler, vec4 P, float bias);
+$gvec4 textureProj($gsampler2DRect sampler, vec3 P);
+$gvec4 textureProj($gsampler2DRect sampler, vec4 P);
+float textureProj(sampler2DRectShadow sampler, vec4 P);
+$gvec4 textureLod($gsampler1D sampler, float P, float lod);
+$gvec4 textureLod($gsampler2D sampler, vec2 P, float lod);
+$gvec4 textureLod($gsampler3D sampler, vec3 P, float lod);
+$gvec4 textureLod($gsamplerCube sampler, vec3 P, float lod);
+float textureLod(sampler1DShadow sampler, vec3 P, float lod);
+float textureLod(sampler2DShadow sampler, vec3 P, float lod);
+$gvec4 textureLod($gsampler1DArray sampler, vec2 P, float lod);
+$gvec4 textureLod($gsampler2DArray sampler, vec3 P, float lod);
+float textureLod(sampler1DArrayShadow sampler, vec3 P, float lod);
+$gvec4 textureLod($gsamplerCubeArray sampler, vec4 P, float lod);
+$gvec4 textureOffset($gsampler1D sampler, float P, int offset);
+$gvec4 textureOffset($gsampler1D sampler, float P, int offset, float bias);
+$gvec4 textureOffset($gsampler2D sampler, vec2 P, ivec2 offset);
+$gvec4 textureOffset($gsampler2D sampler, vec2 P, ivec2 offset, float bias);
+$gvec4 textureOffset($gsampler3D sampler, vec3 P, ivec3 offset);
+$gvec4 textureOffset($gsampler3D sampler, vec3 P, ivec3 offset, float bias);
+$gvec4 textureOffset($gsampler2DRect sampler, vec2 P, ivec2 offset);
+float textureOffset(sampler2DRectShadow sampler, vec3 P, ivec2 offset);
+float textureOffset(sampler1DShadow sampler, vec3 P, int offset);
+float textureOffset(sampler1DShadow sampler, vec3 P, int offset, float bias);
+float textureOffset(sampler2DShadow sampler, vec3 P, ivec2 offset);
+float textureOffset(sampler2DShadow sampler, vec3 P, ivec2 offset, float bias);
+$gvec4 textureOffset($gsampler1DArray sampler, vec2 P, int offset);
+$gvec4 textureOffset($gsampler1DArray sampler, vec2 P, int offset, float bias);
+$gvec4 textureOffset($gsampler2DArray sampler, vec3 P, ivec2 offset);
+$gvec4 textureOffset($gsampler2DArray sampler, vec3 P, ivec2 offset, float bias);
+float textureOffset(sampler1DArrayShadow sampler, vec3 P, int offset);
+float textureOffset(sampler1DArrayShadow sampler, vec3 P, int offset, float bias);
+float textureOffset(sampler2DArrayShadow sampler, vec4 P, ivec2 offset);
+$gvec4 texelFetch($gsampler1D sampler, int P, int lod);
+$gvec4 texelFetch($gsampler2D sampler, ivec2 P, int lod);
+$gvec4 texelFetch($gsampler3D sampler, ivec3 P, int lod);
+$gvec4 texelFetch($gsampler2DRect sampler, ivec2 P);
+$gvec4 texelFetch($gsampler1DArray sampler, ivec2 P, int lod);
+$gvec4 texelFetch($gsampler2DArray sampler, ivec3 P, int lod);
+$gvec4 texelFetch($gsamplerBuffer sampler, int P);
+$gvec4 texelFetch($gsampler2DMS sampler, ivec2 P, int sample);
+$gvec4 texelFetch($gsampler2DMSArray sampler, ivec3 P, int sample);
+$gvec4 texelFetchOffset($gsampler1D sampler, int P, int lod, int offset);
+$gvec4 texelFetchOffset($gsampler2D sampler, ivec2 P, int lod, ivec2 offset);
+$gvec4 texelFetchOffset($gsampler3D sampler, ivec3 P, int lod, ivec3 offset);
+$gvec4 texelFetchOffset($gsampler2DRect sampler, ivec2 P, ivec2 offset);
+$gvec4 texelFetchOffset($gsampler1DArray sampler, ivec2 P, int lod, int offset);
+$gvec4 texelFetchOffset($gsampler2DArray sampler, ivec3 P, int lod, ivec2 offset);
+$gvec4 textureProjOffset($gsampler1D sampler, vec2 P, int offset);
+$gvec4 textureProjOffset($gsampler1D sampler, vec2 P, int offset, float bias);
+$gvec4 textureProjOffset($gsampler1D sampler, vec4 P, int offset);
+$gvec4 textureProjOffset($gsampler1D sampler, vec4 P, int offset, float bias);
+$gvec4 textureProjOffset($gsampler2D sampler, vec3 P, ivec2 offset);
+$gvec4 textureProjOffset($gsampler2D sampler, vec3 P, ivec2 offset, float bias);
+$gvec4 textureProjOffset($gsampler2D sampler, vec4 P, ivec2 offset);
+$gvec4 textureProjOffset($gsampler2D sampler, vec4 P, ivec2 offset, float bias);
+$gvec4 textureProjOffset($gsampler3D sampler, vec4 P, ivec3 offset);
+$gvec4 textureProjOffset($gsampler3D sampler, vec4 P, ivec3 offset, float bias);
+$gvec4 textureProjOffset($gsampler2DRect sampler, vec3 P, ivec2 offset);
+$gvec4 textureProjOffset($gsampler2DRect sampler, vec4 P, ivec2 offset);
+float textureProjOffset(sampler2DRectShadow sampler, vec4 P, ivec2 offset);
+float textureProjOffset(sampler1DShadow sampler, vec4 P, int offset);
+float textureProjOffset(sampler1DShadow sampler, vec4 P, int offset, float bias);
+float textureProjOffset(sampler2DShadow sampler, vec4 P, ivec2 offset);
+float textureProjOffset(sampler2DShadow sampler, vec4 P, ivec2 offset, float bias);
+$gvec4 textureLodOffset($gsampler1D sampler, float P, float lod, int offset);
+$gvec4 textureLodOffset($gsampler2D sampler, vec2 P, float lod, ivec2 offset);
+$gvec4 textureLodOffset($gsampler3D sampler, vec3 P, float lod, ivec3 offset);
+float textureLodOffset(sampler1DShadow sampler, vec3 P, float lod, int offset);
+float textureLodOffset(sampler2DShadow sampler, vec3 P, float lod, ivec2 offset);
+$gvec4 textureLodOffset($gsampler1DArray sampler, vec2 P, float lod, int offset);
+$gvec4 textureLodOffset($gsampler2DArray sampler, vec3 P, float lod, ivec2 offset);
+float textureLodOffset(sampler1DArrayShadow sampler, vec3 P, float lod, int offset);
+$gvec4 textureProjLod($gsampler1D sampler, vec2 P, float lod);
+$gvec4 textureProjLod($gsampler1D sampler, vec4 P, float lod);
+$gvec4 textureProjLod($gsampler2D sampler, vec3 P, float lod);
+$gvec4 textureProjLod($gsampler2D sampler, vec4 P, float lod);
+$gvec4 textureProjLod($gsampler3D sampler, vec4 P, float lod);
+float textureProjLod(sampler1DShadow sampler, vec4 P, float lod);
+float textureProjLod(sampler2DShadow sampler, vec4 P, float lod);
+$gvec4 textureProjLodOffset($gsampler1D sampler, vec2 P, float lod, int offset);
+$gvec4 textureProjLodOffset($gsampler1D sampler, vec4 P, float lod, int offset);
+$gvec4 textureProjLodOffset($gsampler2D sampler, vec3 P, float lod, ivec2 offset);
+$gvec4 textureProjLodOffset($gsampler2D sampler, vec4 P, float lod, ivec2 offset);
+$gvec4 textureProjLodOffset($gsampler3D sampler, vec4 P, float lod, ivec3 offset);
+float textureProjLodOffset(sampler1DShadow sampler, vec4 P, float lod, int offset);
+float textureProjLodOffset(sampler2DShadow sampler, vec4 P, float lod, ivec2 offset);
+$gvec4 textureGrad($gsampler1D sampler, float P, float dPdx, float dPdy);
+$gvec4 textureGrad($gsampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureGrad($gsampler3D sampler, vec3 P, vec3 dPdx, vec3 dPdy);
+$gvec4 textureGrad($gsamplerCube sampler, vec3 P, vec3 dPdx, vec3 dPdy);
+$gvec4 textureGrad($gsampler2DRect sampler, vec2 P, vec2 dPdx, vec2 dPdy);
+float textureGrad(sampler2DRectShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy);
+float textureGrad(sampler1DShadow sampler, vec3 P, float dPdx, float dPdy);
+float textureGrad(sampler2DShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy);
+float textureGrad(samplerCubeShadow sampler, vec4 P, vec3 dPdx, vec3 dPdy);
+$gvec4 textureGrad($gsampler1DArray sampler, vec2 P, float dPdx, float dPdy);
+$gvec4 textureGrad($gsampler2DArray sampler, vec3 P, vec2 dPdx, vec2 dPdy);
+float textureGrad(sampler1DArrayShadow sampler, vec3 P, float dPdx, float dPdy);
+float textureGrad(sampler2DArrayShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureGrad($gsamplerCubeArray sampler, vec4 P, vec3 dPdx, vec3 dPdy);
+$gvec4 textureGradOffset($gsampler1D sampler, float P, float dPdx, float dPdy, int offset);
+$gvec4 textureGradOffset($gsampler2D sampler, vec2 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureGradOffset($gsampler3D sampler, vec3 P, vec3 dPdx, vec3 dPdy, ivec3 offset);
+$gvec4 textureGradOffset($gsampler2DRect sampler, vec2 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+float textureGradOffset(sampler2DRectShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+float textureGradOffset(sampler1DShadow sampler, vec3 P, float dPdx, float dPdy, int offset );
+float textureGradOffset(sampler2DShadow sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureGradOffset($gsampler1DArray sampler, vec2 P, float dPdx, float dPdy, int offset);
+$gvec4 textureGradOffset($gsampler2DArray sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+float textureGradOffset(sampler1DArrayShadow sampler, vec3 P, float dPdx, float dPdy, int offset);
+float textureGradOffset(sampler2DArrayShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureProjGrad($gsampler1D sampler, vec2 P, float dPdx, float dPdy);
+$gvec4 textureProjGrad($gsampler1D sampler, vec4 P, float dPdx, float dPdy);
+$gvec4 textureProjGrad($gsampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureProjGrad($gsampler2D sampler, vec4 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureProjGrad($gsampler3D sampler, vec4 P, vec3 dPdx, vec3 dPdy);
+$gvec4 textureProjGrad($gsampler2DRect sampler, vec3 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureProjGrad($gsampler2DRect sampler, vec4 P, vec2 dPdx, vec2 dPdy);
+float textureProjGrad(sampler2DRectShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy);
+float textureProjGrad(sampler1DShadow sampler, vec4 P, float dPdx, float dPdy);
+float textureProjGrad(sampler2DShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy);
+$gvec4 textureProjGradOffset($gsampler1D sampler, vec2 P, float dPdx, float dPdy, int offset);
+$gvec4 textureProjGradOffset($gsampler1D sampler, vec4 P, float dPdx, float dPdy, int offset);
+$gvec4 textureProjGradOffset($gsampler2D sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureProjGradOffset($gsampler2D sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureProjGradOffset($gsampler2DRect sampler, vec3 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureProjGradOffset($gsampler2DRect sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+float textureProjGradOffset(sampler2DRectShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureProjGradOffset($gsampler3D sampler, vec4 P, vec3 dPdx, vec3 dPdy, ivec3 offset);
+float textureProjGradOffset(sampler1DShadow sampler, vec4 P, float dPdx, float dPdy, int offset);
+float textureProjGradOffset(sampler2DShadow sampler, vec4 P, vec2 dPdx, vec2 dPdy, ivec2 offset);
+$gvec4 textureGather($gsampler2D sampler, vec2 P);
+$gvec4 textureGather($gsampler2D sampler, vec2 P, int comp);
+$gvec4 textureGather($gsampler2DArray sampler, vec3 P);
+$gvec4 textureGather($gsampler2DArray sampler, vec3 P, int comp);
+$gvec4 textureGather($gsamplerCube sampler, vec3 P);
+$gvec4 textureGather($gsamplerCube sampler, vec3 P, int comp);
+$gvec4 textureGather($gsamplerCubeArray sampler, vec4 P);
+$gvec4 textureGather($gsamplerCubeArray sampler, vec4 P, int comp);
+$gvec4 textureGather($gsampler2DRect sampler, vec2 P);
+$gvec4 textureGather($gsampler2DRect sampler, vec2 P, int comp);
+vec4 textureGather(sampler2DShadow sampler, vec2 P, float refZ);
+vec4 textureGather(sampler2DArrayShadow sampler, vec3 P, float refZ);
+vec4 textureGather(samplerCubeShadow sampler, vec3 P, float refZ);
+vec4 textureGather(samplerCubeArrayShadow sampler, vec4 P, float refZ);
+vec4 textureGather(sampler2DRectShadow sampler, vec2 P, float refZ);
+$gvec4 textureGatherOffset($gsampler2D sampler, vec2 P, ivec2 offset);
+$gvec4 textureGatherOffset($gsampler2D sampler, vec2 P, ivec2 offset, int comp);
+$gvec4 textureGatherOffset($gsampler2DArray sampler, vec3 P, ivec2 offset);
+$gvec4 textureGatherOffset($gsampler2DArray sampler, vec3 P, ivec2 offset, int comp);
+$gvec4 textureGatherOffset($gsampler2DRect sampler, vec2 P, ivec2 offset);
+$gvec4 textureGatherOffset($gsampler2DRect sampler, vec2 P, ivec2 offset, int comp);
+vec4 textureGatherOffset(sampler2DShadow sampler, vec2 P, float refZ, ivec2 offset);
+vec4 textureGatherOffset(sampler2DArrayShadow sampler, vec3 P, float refZ, ivec2 offset);
+vec4 textureGatherOffset(sampler2DRectShadow sampler, vec2 P, float refZ, ivec2 offset);
+/*
+$gvec4 textureGatherOffsets($gsampler2D sampler, vec2 P, ivec2 offsets[4]);
+$gvec4 textureGatherOffsets($gsampler2D sampler, vec2 P, ivec2 offsets[4], int comp);
+$gvec4 textureGatherOffsets($gsampler2DArray sampler, vec3 P, ivec2 offsets[4]);
+$gvec4 textureGatherOffsets($gsampler2DArray sampler, vec3 P, ivec2 offsets[4], int comp);
+$gvec4 textureGatherOffsets($gsampler2DRect sampler, vec2 P, ivec2 offsets[4]);
+$gvec4 textureGatherOffsets($gsampler2DRect sampler, vec2 P, ivec2 offsets[4], int comp);
+vec4 textureGatherOffsets(sampler2DShadow sampler, vec2 P, float refZ, ivec2 offsets[4]);
+vec4 textureGatherOffsets(sampler2DArrayShadow sampler, vec3 P, float refZ, ivec2 offsets[4]);
+vec4 textureGatherOffsets(sampler2DRectShadow sampler, vec2 P, float refZ, ivec2 offsets[4]);
+*/
+vec4 texture1D(sampler1D sampler, float coord);
+vec4 texture1D(sampler1D sampler, float coord, float bias);
+vec4 texture1DProj(sampler1D sampler, vec2 coord);
+vec4 texture1DProj(sampler1D sampler, vec2 coord, float bias);
+vec4 texture1DProj(sampler1D sampler, vec4 coord);
+vec4 texture1DProj(sampler1D sampler, vec4 coord, float bias);
+vec4 texture1DLod(sampler1D sampler, float coord, float lod);
+vec4 texture1DProjLod(sampler1D sampler, vec2 coord, float lod);
+vec4 texture1DProjLod(sampler1D sampler, vec4 coord, float lod);
+vec4 texture2D(sampler2D sampler, vec2 coord);
+vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
+vec4 texture2DProj(sampler2D sampler, vec3 coord);
+vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
+vec4 texture2DProj(sampler2D sampler, vec4 coord);
+vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias);
+vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod);
+vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod);
+vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod);
+vec4 texture3D(sampler3D sampler, vec3 coord);
+vec4 texture3D(sampler3D sampler, vec3 coord, float bias);
+vec4 texture3DProj(sampler3D sampler, vec4 coord);
+vec4 texture3DProj(sampler3D sampler, vec4 coord, float bias);
+vec4 texture3DLod(sampler3D sampler, vec3 coord, float lod);
+vec4 texture3DProjLod(sampler3D sampler, vec4 coord, float lod);
+vec4 textureCube(samplerCube sampler, vec3 coord);
+vec4 textureCube(samplerCube sampler, vec3 coord, float bias);
+vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod);
+vec4 shadow1D(sampler1DShadow sampler, vec3 coord);
+vec4 shadow1D(sampler1DShadow sampler, vec3 coord, float bias);
+vec4 shadow2D(sampler2DShadow sampler, vec3 coord);
+vec4 shadow2D(sampler2DShadow sampler, vec3 coord, float bias);
+vec4 shadow1DProj(sampler1DShadow sampler, vec4 coord);
+vec4 shadow1DProj(sampler1DShadow sampler, vec4 coord, float bias);
+vec4 shadow2DProj(sampler2DShadow sampler, vec4 coord);
+vec4 shadow2DProj(sampler2DShadow sampler, vec4 coord, float bias);
+vec4 shadow1DLod(sampler1DShadow sampler, vec3 coord, float lod);
+vec4 shadow2DLod(sampler2DShadow sampler, vec3 coord, float lod);
+vec4 shadow1DProjLod(sampler1DShadow sampler, vec4 coord, float lod);
+vec4 shadow2DProjLod(sampler2DShadow sampler, vec4 coord, float lod);
+/*
+uint atomicCounterIncrement(atomic_uint c);
+uint atomicCounter(atomic_uint c);
+uint atomicAdd(inout uint mem, uint data);
+int atomicAdd(inout int mem, int data);
+uint atomicMin(inout uint mem, uint data);
+int atomicMin(inout int mem, int data);
+uint atomicMax(inout uint mem, uint data);
+int atomicMax(inout int mem, int data);
+uint atomicAnd(inout uint mem, uint data);
+int atomicAnd(inout int mem, int data);
+uint atomicOr(inout uint mem, uint data);
+int atomicOr(inout int mem, int data);
+uint atomicXor(inout uint mem, uint data);
+int atomicXor(inout int mem, int data);
+uint atomicExchange(inout uint mem, uint data);
+int atomicExchange(inout int mem, int data);
+uint atomicCompSwap(inout uint mem, uint compare, uint data);
+int atomicCompSwap(inout int mem, int compare, int data);
+*/
+// section 8.12 Image Functions will go here if and when we add support for them
+
+$genType dFdx($genType p);
+$genType dFdy($genType p);
+$genType fwidth($genType p);
+$genType fwidthCoarse($genType p);
+$genType fwidthFine($genType p);
+float interpolateAtSample(float interpolant, int sample);
+vec2 interpolateAtSample(vec2 interpolant, int sample);
+vec3 interpolateAtSample(vec3 interpolant, int sample);
+vec4 interpolateAtSample(vec4 interpolant, int sample);
+float interpolateAtOffset(float interpolant, vec2 offset);
+vec2 interpolateAtOffset(vec2 interpolant, vec2 offset);
+vec3 interpolateAtOffset(vec3 interpolant, vec2 offset);
+vec4 interpolateAtOffset(vec4 interpolant, vec2 offset);
+void EmitStreamVertex(int stream);
+void EndStreamPrimitive(int stream);
+void EmitVertex();
+void EndPrimitive();
+void barrier();
+void memoryBarrier();
+void memoryBarrierAtomicCounter();
+void memoryBarrierBuffer();
+void memoryBarrierShared();
+void memoryBarrierImage();
+void groupMemoryBarrier();
+
+) \ No newline at end of file
diff --git a/src/sksl/sksl_frag.include b/src/sksl/sksl_frag.include
new file mode 100644
index 0000000000..b4047fe091
--- /dev/null
+++ b/src/sksl/sksl_frag.include
@@ -0,0 +1,7 @@
+STRINGIFY(
+
+// defines built-in interfaces supported by SkiaSL fragment shaders
+
+layout(builtin=15) in vec4 gl_FragCoord;
+
+) \ No newline at end of file
diff --git a/src/sksl/sksl_vert.include b/src/sksl/sksl_vert.include
new file mode 100644
index 0000000000..e0b01efa49
--- /dev/null
+++ b/src/sksl/sksl_vert.include
@@ -0,0 +1,10 @@
+STRINGIFY(
+
+// defines built-in interfaces supported by SkiaSL vertex shaders
+
+out gl_PerVertex {
+ layout(builtin=0) vec4 gl_Position;
+ layout(builtin=1) float gl_PointSize;
+};
+
+) \ No newline at end of file
diff --git a/src/sksl/spirv.h b/src/sksl/spirv.h
new file mode 100644
index 0000000000..e4f5b5beeb
--- /dev/null
+++ b/src/sksl/spirv.h
@@ -0,0 +1,870 @@
+/*
+** Copyright (c) 2014-2016 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and/or associated documentation files (the "Materials"),
+** to deal in the Materials without restriction, including without limitation
+** the rights to use, copy, modify, merge, publish, distribute, sublicense,
+** and/or sell copies of the Materials, and to permit persons to whom the
+** Materials are furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Materials.
+**
+** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+**
+** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+** IN THE MATERIALS.
+*/
+
+/*
+** This header is automatically generated by the same tool that creates
+** the Binary Section of the SPIR-V specification.
+*/
+
+/*
+** Enumeration tokens for SPIR-V, in various styles:
+** C, C++, C++11, JSON, Lua, Python
+**
+** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+**
+** Some tokens act like mask values, which can be OR'd together,
+** while others are mutually exclusive. The mask-like ones have
+** "Mask" in their name, and a parallel enum that has the shift
+** amount (1 << x) for each corresponding enumerant.
+*/
+
+#ifndef spirv_H
+#define spirv_H
+
+typedef unsigned int SpvId;
+
+#define SPV_VERSION 0x10000
+#define SPV_REVISION 4
+
+static const unsigned int SpvMagicNumber = 0x07230203;
+static const unsigned int SpvVersion = 0x00010000;
+static const unsigned int SpvRevision = 4;
+static const unsigned int SpvOpCodeMask = 0xffff;
+static const unsigned int SpvWordCountShift = 16;
+
+typedef enum SpvSourceLanguage_ {
+ SpvSourceLanguageUnknown = 0,
+ SpvSourceLanguageESSL = 1,
+ SpvSourceLanguageGLSL = 2,
+ SpvSourceLanguageOpenCL_C = 3,
+ SpvSourceLanguageOpenCL_CPP = 4,
+} SpvSourceLanguage;
+
+typedef enum SpvExecutionModel_ {
+ SpvExecutionModelVertex = 0,
+ SpvExecutionModelTessellationControl = 1,
+ SpvExecutionModelTessellationEvaluation = 2,
+ SpvExecutionModelGeometry = 3,
+ SpvExecutionModelFragment = 4,
+ SpvExecutionModelGLCompute = 5,
+ SpvExecutionModelKernel = 6,
+} SpvExecutionModel;
+
+typedef enum SpvAddressingModel_ {
+ SpvAddressingModelLogical = 0,
+ SpvAddressingModelPhysical32 = 1,
+ SpvAddressingModelPhysical64 = 2,
+} SpvAddressingModel;
+
+typedef enum SpvMemoryModel_ {
+ SpvMemoryModelSimple = 0,
+ SpvMemoryModelGLSL450 = 1,
+ SpvMemoryModelOpenCL = 2,
+} SpvMemoryModel;
+
+typedef enum SpvExecutionMode_ {
+ SpvExecutionModeInvocations = 0,
+ SpvExecutionModeSpacingEqual = 1,
+ SpvExecutionModeSpacingFractionalEven = 2,
+ SpvExecutionModeSpacingFractionalOdd = 3,
+ SpvExecutionModeVertexOrderCw = 4,
+ SpvExecutionModeVertexOrderCcw = 5,
+ SpvExecutionModePixelCenterInteger = 6,
+ SpvExecutionModeOriginUpperLeft = 7,
+ SpvExecutionModeOriginLowerLeft = 8,
+ SpvExecutionModeEarlyFragmentTests = 9,
+ SpvExecutionModePointMode = 10,
+ SpvExecutionModeXfb = 11,
+ SpvExecutionModeDepthReplacing = 12,
+ SpvExecutionModeDepthGreater = 14,
+ SpvExecutionModeDepthLess = 15,
+ SpvExecutionModeDepthUnchanged = 16,
+ SpvExecutionModeLocalSize = 17,
+ SpvExecutionModeLocalSizeHint = 18,
+ SpvExecutionModeInputPoints = 19,
+ SpvExecutionModeInputLines = 20,
+ SpvExecutionModeInputLinesAdjacency = 21,
+ SpvExecutionModeTriangles = 22,
+ SpvExecutionModeInputTrianglesAdjacency = 23,
+ SpvExecutionModeQuads = 24,
+ SpvExecutionModeIsolines = 25,
+ SpvExecutionModeOutputVertices = 26,
+ SpvExecutionModeOutputPoints = 27,
+ SpvExecutionModeOutputLineStrip = 28,
+ SpvExecutionModeOutputTriangleStrip = 29,
+ SpvExecutionModeVecTypeHint = 30,
+ SpvExecutionModeContractionOff = 31,
+} SpvExecutionMode;
+
+typedef enum SpvStorageClass_ {
+ SpvStorageClassUniformConstant = 0,
+ SpvStorageClassInput = 1,
+ SpvStorageClassUniform = 2,
+ SpvStorageClassOutput = 3,
+ SpvStorageClassWorkgroup = 4,
+ SpvStorageClassCrossWorkgroup = 5,
+ SpvStorageClassPrivate = 6,
+ SpvStorageClassFunction = 7,
+ SpvStorageClassGeneric = 8,
+ SpvStorageClassPushConstant = 9,
+ SpvStorageClassAtomicCounter = 10,
+ SpvStorageClassImage = 11,
+} SpvStorageClass;
+
+typedef enum SpvDim_ {
+ SpvDim1D = 0,
+ SpvDim2D = 1,
+ SpvDim3D = 2,
+ SpvDimCube = 3,
+ SpvDimRect = 4,
+ SpvDimBuffer = 5,
+ SpvDimSubpassData = 6,
+} SpvDim;
+
+typedef enum SpvSamplerAddressingMode_ {
+ SpvSamplerAddressingModeNone = 0,
+ SpvSamplerAddressingModeClampToEdge = 1,
+ SpvSamplerAddressingModeClamp = 2,
+ SpvSamplerAddressingModeRepeat = 3,
+ SpvSamplerAddressingModeRepeatMirrored = 4,
+} SpvSamplerAddressingMode;
+
+typedef enum SpvSamplerFilterMode_ {
+ SpvSamplerFilterModeNearest = 0,
+ SpvSamplerFilterModeLinear = 1,
+} SpvSamplerFilterMode;
+
+typedef enum SpvImageFormat_ {
+ SpvImageFormatUnknown = 0,
+ SpvImageFormatRgba32f = 1,
+ SpvImageFormatRgba16f = 2,
+ SpvImageFormatR32f = 3,
+ SpvImageFormatRgba8 = 4,
+ SpvImageFormatRgba8Snorm = 5,
+ SpvImageFormatRg32f = 6,
+ SpvImageFormatRg16f = 7,
+ SpvImageFormatR11fG11fB10f = 8,
+ SpvImageFormatR16f = 9,
+ SpvImageFormatRgba16 = 10,
+ SpvImageFormatRgb10A2 = 11,
+ SpvImageFormatRg16 = 12,
+ SpvImageFormatRg8 = 13,
+ SpvImageFormatR16 = 14,
+ SpvImageFormatR8 = 15,
+ SpvImageFormatRgba16Snorm = 16,
+ SpvImageFormatRg16Snorm = 17,
+ SpvImageFormatRg8Snorm = 18,
+ SpvImageFormatR16Snorm = 19,
+ SpvImageFormatR8Snorm = 20,
+ SpvImageFormatRgba32i = 21,
+ SpvImageFormatRgba16i = 22,
+ SpvImageFormatRgba8i = 23,
+ SpvImageFormatR32i = 24,
+ SpvImageFormatRg32i = 25,
+ SpvImageFormatRg16i = 26,
+ SpvImageFormatRg8i = 27,
+ SpvImageFormatR16i = 28,
+ SpvImageFormatR8i = 29,
+ SpvImageFormatRgba32ui = 30,
+ SpvImageFormatRgba16ui = 31,
+ SpvImageFormatRgba8ui = 32,
+ SpvImageFormatR32ui = 33,
+ SpvImageFormatRgb10a2ui = 34,
+ SpvImageFormatRg32ui = 35,
+ SpvImageFormatRg16ui = 36,
+ SpvImageFormatRg8ui = 37,
+ SpvImageFormatR16ui = 38,
+ SpvImageFormatR8ui = 39,
+} SpvImageFormat;
+
+typedef enum SpvImageChannelOrder_ {
+ SpvImageChannelOrderR = 0,
+ SpvImageChannelOrderA = 1,
+ SpvImageChannelOrderRG = 2,
+ SpvImageChannelOrderRA = 3,
+ SpvImageChannelOrderRGB = 4,
+ SpvImageChannelOrderRGBA = 5,
+ SpvImageChannelOrderBGRA = 6,
+ SpvImageChannelOrderARGB = 7,
+ SpvImageChannelOrderIntensity = 8,
+ SpvImageChannelOrderLuminance = 9,
+ SpvImageChannelOrderRx = 10,
+ SpvImageChannelOrderRGx = 11,
+ SpvImageChannelOrderRGBx = 12,
+ SpvImageChannelOrderDepth = 13,
+ SpvImageChannelOrderDepthStencil = 14,
+ SpvImageChannelOrdersRGB = 15,
+ SpvImageChannelOrdersRGBx = 16,
+ SpvImageChannelOrdersRGBA = 17,
+ SpvImageChannelOrdersBGRA = 18,
+} SpvImageChannelOrder;
+
+typedef enum SpvImageChannelDataType_ {
+ SpvImageChannelDataTypeSnormInt8 = 0,
+ SpvImageChannelDataTypeSnormInt16 = 1,
+ SpvImageChannelDataTypeUnormInt8 = 2,
+ SpvImageChannelDataTypeUnormInt16 = 3,
+ SpvImageChannelDataTypeUnormShort565 = 4,
+ SpvImageChannelDataTypeUnormShort555 = 5,
+ SpvImageChannelDataTypeUnormInt101010 = 6,
+ SpvImageChannelDataTypeSignedInt8 = 7,
+ SpvImageChannelDataTypeSignedInt16 = 8,
+ SpvImageChannelDataTypeSignedInt32 = 9,
+ SpvImageChannelDataTypeUnsignedInt8 = 10,
+ SpvImageChannelDataTypeUnsignedInt16 = 11,
+ SpvImageChannelDataTypeUnsignedInt32 = 12,
+ SpvImageChannelDataTypeHalfFloat = 13,
+ SpvImageChannelDataTypeFloat = 14,
+ SpvImageChannelDataTypeUnormInt24 = 15,
+ SpvImageChannelDataTypeUnormInt101010_2 = 16,
+} SpvImageChannelDataType;
+
+typedef enum SpvImageOperandsShift_ {
+ SpvImageOperandsBiasShift = 0,
+ SpvImageOperandsLodShift = 1,
+ SpvImageOperandsGradShift = 2,
+ SpvImageOperandsConstOffsetShift = 3,
+ SpvImageOperandsOffsetShift = 4,
+ SpvImageOperandsConstOffsetsShift = 5,
+ SpvImageOperandsSampleShift = 6,
+ SpvImageOperandsMinLodShift = 7,
+} SpvImageOperandsShift;
+
+typedef enum SpvImageOperandsMask_ {
+ SpvImageOperandsMaskNone = 0,
+ SpvImageOperandsBiasMask = 0x00000001,
+ SpvImageOperandsLodMask = 0x00000002,
+ SpvImageOperandsGradMask = 0x00000004,
+ SpvImageOperandsConstOffsetMask = 0x00000008,
+ SpvImageOperandsOffsetMask = 0x00000010,
+ SpvImageOperandsConstOffsetsMask = 0x00000020,
+ SpvImageOperandsSampleMask = 0x00000040,
+ SpvImageOperandsMinLodMask = 0x00000080,
+} SpvImageOperandsMask;
+
+typedef enum SpvFPFastMathModeShift_ {
+ SpvFPFastMathModeNotNaNShift = 0,
+ SpvFPFastMathModeNotInfShift = 1,
+ SpvFPFastMathModeNSZShift = 2,
+ SpvFPFastMathModeAllowRecipShift = 3,
+ SpvFPFastMathModeFastShift = 4,
+} SpvFPFastMathModeShift;
+
+typedef enum SpvFPFastMathModeMask_ {
+ SpvFPFastMathModeMaskNone = 0,
+ SpvFPFastMathModeNotNaNMask = 0x00000001,
+ SpvFPFastMathModeNotInfMask = 0x00000002,
+ SpvFPFastMathModeNSZMask = 0x00000004,
+ SpvFPFastMathModeAllowRecipMask = 0x00000008,
+ SpvFPFastMathModeFastMask = 0x00000010,
+} SpvFPFastMathModeMask;
+
+typedef enum SpvFPRoundingMode_ {
+ SpvFPRoundingModeRTE = 0,
+ SpvFPRoundingModeRTZ = 1,
+ SpvFPRoundingModeRTP = 2,
+ SpvFPRoundingModeRTN = 3,
+} SpvFPRoundingMode;
+
+typedef enum SpvLinkageType_ {
+ SpvLinkageTypeExport = 0,
+ SpvLinkageTypeImport = 1,
+} SpvLinkageType;
+
+typedef enum SpvAccessQualifier_ {
+ SpvAccessQualifierReadOnly = 0,
+ SpvAccessQualifierWriteOnly = 1,
+ SpvAccessQualifierReadWrite = 2,
+} SpvAccessQualifier;
+
+typedef enum SpvFunctionParameterAttribute_ {
+ SpvFunctionParameterAttributeZext = 0,
+ SpvFunctionParameterAttributeSext = 1,
+ SpvFunctionParameterAttributeByVal = 2,
+ SpvFunctionParameterAttributeSret = 3,
+ SpvFunctionParameterAttributeNoAlias = 4,
+ SpvFunctionParameterAttributeNoCapture = 5,
+ SpvFunctionParameterAttributeNoWrite = 6,
+ SpvFunctionParameterAttributeNoReadWrite = 7,
+} SpvFunctionParameterAttribute;
+
+typedef enum SpvDecoration_ {
+ SpvDecorationRelaxedPrecision = 0,
+ SpvDecorationSpecId = 1,
+ SpvDecorationBlock = 2,
+ SpvDecorationBufferBlock = 3,
+ SpvDecorationRowMajor = 4,
+ SpvDecorationColMajor = 5,
+ SpvDecorationArrayStride = 6,
+ SpvDecorationMatrixStride = 7,
+ SpvDecorationGLSLShared = 8,
+ SpvDecorationGLSLPacked = 9,
+ SpvDecorationCPacked = 10,
+ SpvDecorationBuiltIn = 11,
+ SpvDecorationNoPerspective = 13,
+ SpvDecorationFlat = 14,
+ SpvDecorationPatch = 15,
+ SpvDecorationCentroid = 16,
+ SpvDecorationSample = 17,
+ SpvDecorationInvariant = 18,
+ SpvDecorationRestrict = 19,
+ SpvDecorationAliased = 20,
+ SpvDecorationVolatile = 21,
+ SpvDecorationConstant = 22,
+ SpvDecorationCoherent = 23,
+ SpvDecorationNonWritable = 24,
+ SpvDecorationNonReadable = 25,
+ SpvDecorationUniform = 26,
+ SpvDecorationSaturatedConversion = 28,
+ SpvDecorationStream = 29,
+ SpvDecorationLocation = 30,
+ SpvDecorationComponent = 31,
+ SpvDecorationIndex = 32,
+ SpvDecorationBinding = 33,
+ SpvDecorationDescriptorSet = 34,
+ SpvDecorationOffset = 35,
+ SpvDecorationXfbBuffer = 36,
+ SpvDecorationXfbStride = 37,
+ SpvDecorationFuncParamAttr = 38,
+ SpvDecorationFPRoundingMode = 39,
+ SpvDecorationFPFastMathMode = 40,
+ SpvDecorationLinkageAttributes = 41,
+ SpvDecorationNoContraction = 42,
+ SpvDecorationInputAttachmentIndex = 43,
+ SpvDecorationAlignment = 44,
+} SpvDecoration;
+
+typedef enum SpvBuiltIn_ {
+ SpvBuiltInPosition = 0,
+ SpvBuiltInPointSize = 1,
+ SpvBuiltInClipDistance = 3,
+ SpvBuiltInCullDistance = 4,
+ SpvBuiltInVertexId = 5,
+ SpvBuiltInInstanceId = 6,
+ SpvBuiltInPrimitiveId = 7,
+ SpvBuiltInInvocationId = 8,
+ SpvBuiltInLayer = 9,
+ SpvBuiltInViewportIndex = 10,
+ SpvBuiltInTessLevelOuter = 11,
+ SpvBuiltInTessLevelInner = 12,
+ SpvBuiltInTessCoord = 13,
+ SpvBuiltInPatchVertices = 14,
+ SpvBuiltInFragCoord = 15,
+ SpvBuiltInPointCoord = 16,
+ SpvBuiltInFrontFacing = 17,
+ SpvBuiltInSampleId = 18,
+ SpvBuiltInSamplePosition = 19,
+ SpvBuiltInSampleMask = 20,
+ SpvBuiltInFragDepth = 22,
+ SpvBuiltInHelperInvocation = 23,
+ SpvBuiltInNumWorkgroups = 24,
+ SpvBuiltInWorkgroupSize = 25,
+ SpvBuiltInWorkgroupId = 26,
+ SpvBuiltInLocalInvocationId = 27,
+ SpvBuiltInGlobalInvocationId = 28,
+ SpvBuiltInLocalInvocationIndex = 29,
+ SpvBuiltInWorkDim = 30,
+ SpvBuiltInGlobalSize = 31,
+ SpvBuiltInEnqueuedWorkgroupSize = 32,
+ SpvBuiltInGlobalOffset = 33,
+ SpvBuiltInGlobalLinearId = 34,
+ SpvBuiltInSubgroupSize = 36,
+ SpvBuiltInSubgroupMaxSize = 37,
+ SpvBuiltInNumSubgroups = 38,
+ SpvBuiltInNumEnqueuedSubgroups = 39,
+ SpvBuiltInSubgroupId = 40,
+ SpvBuiltInSubgroupLocalInvocationId = 41,
+ SpvBuiltInVertexIndex = 42,
+ SpvBuiltInInstanceIndex = 43,
+} SpvBuiltIn;
+
+typedef enum SpvSelectionControlShift_ {
+ SpvSelectionControlFlattenShift = 0,
+ SpvSelectionControlDontFlattenShift = 1,
+} SpvSelectionControlShift;
+
+typedef enum SpvSelectionControlMask_ {
+ SpvSelectionControlMaskNone = 0,
+ SpvSelectionControlFlattenMask = 0x00000001,
+ SpvSelectionControlDontFlattenMask = 0x00000002,
+} SpvSelectionControlMask;
+
+typedef enum SpvLoopControlShift_ {
+ SpvLoopControlUnrollShift = 0,
+ SpvLoopControlDontUnrollShift = 1,
+} SpvLoopControlShift;
+
+typedef enum SpvLoopControlMask_ {
+ SpvLoopControlMaskNone = 0,
+ SpvLoopControlUnrollMask = 0x00000001,
+ SpvLoopControlDontUnrollMask = 0x00000002,
+} SpvLoopControlMask;
+
+typedef enum SpvFunctionControlShift_ {
+ SpvFunctionControlInlineShift = 0,
+ SpvFunctionControlDontInlineShift = 1,
+ SpvFunctionControlPureShift = 2,
+ SpvFunctionControlConstShift = 3,
+} SpvFunctionControlShift;
+
+typedef enum SpvFunctionControlMask_ {
+ SpvFunctionControlMaskNone = 0,
+ SpvFunctionControlInlineMask = 0x00000001,
+ SpvFunctionControlDontInlineMask = 0x00000002,
+ SpvFunctionControlPureMask = 0x00000004,
+ SpvFunctionControlConstMask = 0x00000008,
+} SpvFunctionControlMask;
+
+typedef enum SpvMemorySemanticsShift_ {
+ SpvMemorySemanticsAcquireShift = 1,
+ SpvMemorySemanticsReleaseShift = 2,
+ SpvMemorySemanticsAcquireReleaseShift = 3,
+ SpvMemorySemanticsSequentiallyConsistentShift = 4,
+ SpvMemorySemanticsUniformMemoryShift = 6,
+ SpvMemorySemanticsSubgroupMemoryShift = 7,
+ SpvMemorySemanticsWorkgroupMemoryShift = 8,
+ SpvMemorySemanticsCrossWorkgroupMemoryShift = 9,
+ SpvMemorySemanticsAtomicCounterMemoryShift = 10,
+ SpvMemorySemanticsImageMemoryShift = 11,
+} SpvMemorySemanticsShift;
+
+typedef enum SpvMemorySemanticsMask_ {
+ SpvMemorySemanticsMaskNone = 0,
+ SpvMemorySemanticsAcquireMask = 0x00000002,
+ SpvMemorySemanticsReleaseMask = 0x00000004,
+ SpvMemorySemanticsAcquireReleaseMask = 0x00000008,
+ SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010,
+ SpvMemorySemanticsUniformMemoryMask = 0x00000040,
+ SpvMemorySemanticsSubgroupMemoryMask = 0x00000080,
+ SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100,
+ SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
+ SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400,
+ SpvMemorySemanticsImageMemoryMask = 0x00000800,
+} SpvMemorySemanticsMask;
+
+typedef enum SpvMemoryAccessShift_ {
+ SpvMemoryAccessVolatileShift = 0,
+ SpvMemoryAccessAlignedShift = 1,
+ SpvMemoryAccessNontemporalShift = 2,
+} SpvMemoryAccessShift;
+
+typedef enum SpvMemoryAccessMask_ {
+ SpvMemoryAccessMaskNone = 0,
+ SpvMemoryAccessVolatileMask = 0x00000001,
+ SpvMemoryAccessAlignedMask = 0x00000002,
+ SpvMemoryAccessNontemporalMask = 0x00000004,
+} SpvMemoryAccessMask;
+
+typedef enum SpvScope_ {
+ SpvScopeCrossDevice = 0,
+ SpvScopeDevice = 1,
+ SpvScopeWorkgroup = 2,
+ SpvScopeSubgroup = 3,
+ SpvScopeInvocation = 4,
+} SpvScope;
+
+typedef enum SpvGroupOperation_ {
+ SpvGroupOperationReduce = 0,
+ SpvGroupOperationInclusiveScan = 1,
+ SpvGroupOperationExclusiveScan = 2,
+} SpvGroupOperation;
+
+typedef enum SpvKernelEnqueueFlags_ {
+ SpvKernelEnqueueFlagsNoWait = 0,
+ SpvKernelEnqueueFlagsWaitKernel = 1,
+ SpvKernelEnqueueFlagsWaitWorkGroup = 2,
+} SpvKernelEnqueueFlags;
+
+typedef enum SpvKernelProfilingInfoShift_ {
+ SpvKernelProfilingInfoCmdExecTimeShift = 0,
+} SpvKernelProfilingInfoShift;
+
+typedef enum SpvKernelProfilingInfoMask_ {
+ SpvKernelProfilingInfoMaskNone = 0,
+ SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001,
+} SpvKernelProfilingInfoMask;
+
+typedef enum SpvCapability_ {
+ SpvCapabilityMatrix = 0,
+ SpvCapabilityShader = 1,
+ SpvCapabilityGeometry = 2,
+ SpvCapabilityTessellation = 3,
+ SpvCapabilityAddresses = 4,
+ SpvCapabilityLinkage = 5,
+ SpvCapabilityKernel = 6,
+ SpvCapabilityVector16 = 7,
+ SpvCapabilityFloat16Buffer = 8,
+ SpvCapabilityFloat16 = 9,
+ SpvCapabilityFloat64 = 10,
+ SpvCapabilityInt64 = 11,
+ SpvCapabilityInt64Atomics = 12,
+ SpvCapabilityImageBasic = 13,
+ SpvCapabilityImageReadWrite = 14,
+ SpvCapabilityImageMipmap = 15,
+ SpvCapabilityPipes = 17,
+ SpvCapabilityGroups = 18,
+ SpvCapabilityDeviceEnqueue = 19,
+ SpvCapabilityLiteralSampler = 20,
+ SpvCapabilityAtomicStorage = 21,
+ SpvCapabilityInt16 = 22,
+ SpvCapabilityTessellationPointSize = 23,
+ SpvCapabilityGeometryPointSize = 24,
+ SpvCapabilityImageGatherExtended = 25,
+ SpvCapabilityStorageImageMultisample = 27,
+ SpvCapabilityUniformBufferArrayDynamicIndexing = 28,
+ SpvCapabilitySampledImageArrayDynamicIndexing = 29,
+ SpvCapabilityStorageBufferArrayDynamicIndexing = 30,
+ SpvCapabilityStorageImageArrayDynamicIndexing = 31,
+ SpvCapabilityClipDistance = 32,
+ SpvCapabilityCullDistance = 33,
+ SpvCapabilityImageCubeArray = 34,
+ SpvCapabilitySampleRateShading = 35,
+ SpvCapabilityImageRect = 36,
+ SpvCapabilitySampledRect = 37,
+ SpvCapabilityGenericPointer = 38,
+ SpvCapabilityInt8 = 39,
+ SpvCapabilityInputAttachment = 40,
+ SpvCapabilitySparseResidency = 41,
+ SpvCapabilityMinLod = 42,
+ SpvCapabilitySampled1D = 43,
+ SpvCapabilityImage1D = 44,
+ SpvCapabilitySampledCubeArray = 45,
+ SpvCapabilitySampledBuffer = 46,
+ SpvCapabilityImageBuffer = 47,
+ SpvCapabilityImageMSArray = 48,
+ SpvCapabilityStorageImageExtendedFormats = 49,
+ SpvCapabilityImageQuery = 50,
+ SpvCapabilityDerivativeControl = 51,
+ SpvCapabilityInterpolationFunction = 52,
+ SpvCapabilityTransformFeedback = 53,
+ SpvCapabilityGeometryStreams = 54,
+ SpvCapabilityStorageImageReadWithoutFormat = 55,
+ SpvCapabilityStorageImageWriteWithoutFormat = 56,
+ SpvCapabilityMultiViewport = 57,
+} SpvCapability;
+
+typedef enum SpvOp_ {
+ SpvOpNop = 0,
+ SpvOpUndef = 1,
+ SpvOpSourceContinued = 2,
+ SpvOpSource = 3,
+ SpvOpSourceExtension = 4,
+ SpvOpName = 5,
+ SpvOpMemberName = 6,
+ SpvOpString = 7,
+ SpvOpLine = 8,
+ SpvOpExtension = 10,
+ SpvOpExtInstImport = 11,
+ SpvOpExtInst = 12,
+ SpvOpMemoryModel = 14,
+ SpvOpEntryPoint = 15,
+ SpvOpExecutionMode = 16,
+ SpvOpCapability = 17,
+ SpvOpTypeVoid = 19,
+ SpvOpTypeBool = 20,
+ SpvOpTypeInt = 21,
+ SpvOpTypeFloat = 22,
+ SpvOpTypeVector = 23,
+ SpvOpTypeMatrix = 24,
+ SpvOpTypeImage = 25,
+ SpvOpTypeSampler = 26,
+ SpvOpTypeSampledImage = 27,
+ SpvOpTypeArray = 28,
+ SpvOpTypeRuntimeArray = 29,
+ SpvOpTypeStruct = 30,
+ SpvOpTypeOpaque = 31,
+ SpvOpTypePointer = 32,
+ SpvOpTypeFunction = 33,
+ SpvOpTypeEvent = 34,
+ SpvOpTypeDeviceEvent = 35,
+ SpvOpTypeReserveId = 36,
+ SpvOpTypeQueue = 37,
+ SpvOpTypePipe = 38,
+ SpvOpTypeForwardPointer = 39,
+ SpvOpConstantTrue = 41,
+ SpvOpConstantFalse = 42,
+ SpvOpConstant = 43,
+ SpvOpConstantComposite = 44,
+ SpvOpConstantSampler = 45,
+ SpvOpConstantNull = 46,
+ SpvOpSpecConstantTrue = 48,
+ SpvOpSpecConstantFalse = 49,
+ SpvOpSpecConstant = 50,
+ SpvOpSpecConstantComposite = 51,
+ SpvOpSpecConstantOp = 52,
+ SpvOpFunction = 54,
+ SpvOpFunctionParameter = 55,
+ SpvOpFunctionEnd = 56,
+ SpvOpFunctionCall = 57,
+ SpvOpVariable = 59,
+ SpvOpImageTexelPointer = 60,
+ SpvOpLoad = 61,
+ SpvOpStore = 62,
+ SpvOpCopyMemory = 63,
+ SpvOpCopyMemorySized = 64,
+ SpvOpAccessChain = 65,
+ SpvOpInBoundsAccessChain = 66,
+ SpvOpPtrAccessChain = 67,
+ SpvOpArrayLength = 68,
+ SpvOpGenericPtrMemSemantics = 69,
+ SpvOpInBoundsPtrAccessChain = 70,
+ SpvOpDecorate = 71,
+ SpvOpMemberDecorate = 72,
+ SpvOpDecorationGroup = 73,
+ SpvOpGroupDecorate = 74,
+ SpvOpGroupMemberDecorate = 75,
+ SpvOpVectorExtractDynamic = 77,
+ SpvOpVectorInsertDynamic = 78,
+ SpvOpVectorShuffle = 79,
+ SpvOpCompositeConstruct = 80,
+ SpvOpCompositeExtract = 81,
+ SpvOpCompositeInsert = 82,
+ SpvOpCopyObject = 83,
+ SpvOpTranspose = 84,
+ SpvOpSampledImage = 86,
+ SpvOpImageSampleImplicitLod = 87,
+ SpvOpImageSampleExplicitLod = 88,
+ SpvOpImageSampleDrefImplicitLod = 89,
+ SpvOpImageSampleDrefExplicitLod = 90,
+ SpvOpImageSampleProjImplicitLod = 91,
+ SpvOpImageSampleProjExplicitLod = 92,
+ SpvOpImageSampleProjDrefImplicitLod = 93,
+ SpvOpImageSampleProjDrefExplicitLod = 94,
+ SpvOpImageFetch = 95,
+ SpvOpImageGather = 96,
+ SpvOpImageDrefGather = 97,
+ SpvOpImageRead = 98,
+ SpvOpImageWrite = 99,
+ SpvOpImage = 100,
+ SpvOpImageQueryFormat = 101,
+ SpvOpImageQueryOrder = 102,
+ SpvOpImageQuerySizeLod = 103,
+ SpvOpImageQuerySize = 104,
+ SpvOpImageQueryLod = 105,
+ SpvOpImageQueryLevels = 106,
+ SpvOpImageQuerySamples = 107,
+ SpvOpConvertFToU = 109,
+ SpvOpConvertFToS = 110,
+ SpvOpConvertSToF = 111,
+ SpvOpConvertUToF = 112,
+ SpvOpUConvert = 113,
+ SpvOpSConvert = 114,
+ SpvOpFConvert = 115,
+ SpvOpQuantizeToF16 = 116,
+ SpvOpConvertPtrToU = 117,
+ SpvOpSatConvertSToU = 118,
+ SpvOpSatConvertUToS = 119,
+ SpvOpConvertUToPtr = 120,
+ SpvOpPtrCastToGeneric = 121,
+ SpvOpGenericCastToPtr = 122,
+ SpvOpGenericCastToPtrExplicit = 123,
+ SpvOpBitcast = 124,
+ SpvOpSNegate = 126,
+ SpvOpFNegate = 127,
+ SpvOpIAdd = 128,
+ SpvOpFAdd = 129,
+ SpvOpISub = 130,
+ SpvOpFSub = 131,
+ SpvOpIMul = 132,
+ SpvOpFMul = 133,
+ SpvOpUDiv = 134,
+ SpvOpSDiv = 135,
+ SpvOpFDiv = 136,
+ SpvOpUMod = 137,
+ SpvOpSRem = 138,
+ SpvOpSMod = 139,
+ SpvOpFRem = 140,
+ SpvOpFMod = 141,
+ SpvOpVectorTimesScalar = 142,
+ SpvOpMatrixTimesScalar = 143,
+ SpvOpVectorTimesMatrix = 144,
+ SpvOpMatrixTimesVector = 145,
+ SpvOpMatrixTimesMatrix = 146,
+ SpvOpOuterProduct = 147,
+ SpvOpDot = 148,
+ SpvOpIAddCarry = 149,
+ SpvOpISubBorrow = 150,
+ SpvOpUMulExtended = 151,
+ SpvOpSMulExtended = 152,
+ SpvOpAny = 154,
+ SpvOpAll = 155,
+ SpvOpIsNan = 156,
+ SpvOpIsInf = 157,
+ SpvOpIsFinite = 158,
+ SpvOpIsNormal = 159,
+ SpvOpSignBitSet = 160,
+ SpvOpLessOrGreater = 161,
+ SpvOpOrdered = 162,
+ SpvOpUnordered = 163,
+ SpvOpLogicalEqual = 164,
+ SpvOpLogicalNotEqual = 165,
+ SpvOpLogicalOr = 166,
+ SpvOpLogicalAnd = 167,
+ SpvOpLogicalNot = 168,
+ SpvOpSelect = 169,
+ SpvOpIEqual = 170,
+ SpvOpINotEqual = 171,
+ SpvOpUGreaterThan = 172,
+ SpvOpSGreaterThan = 173,
+ SpvOpUGreaterThanEqual = 174,
+ SpvOpSGreaterThanEqual = 175,
+ SpvOpULessThan = 176,
+ SpvOpSLessThan = 177,
+ SpvOpULessThanEqual = 178,
+ SpvOpSLessThanEqual = 179,
+ SpvOpFOrdEqual = 180,
+ SpvOpFUnordEqual = 181,
+ SpvOpFOrdNotEqual = 182,
+ SpvOpFUnordNotEqual = 183,
+ SpvOpFOrdLessThan = 184,
+ SpvOpFUnordLessThan = 185,
+ SpvOpFOrdGreaterThan = 186,
+ SpvOpFUnordGreaterThan = 187,
+ SpvOpFOrdLessThanEqual = 188,
+ SpvOpFUnordLessThanEqual = 189,
+ SpvOpFOrdGreaterThanEqual = 190,
+ SpvOpFUnordGreaterThanEqual = 191,
+ SpvOpShiftRightLogical = 194,
+ SpvOpShiftRightArithmetic = 195,
+ SpvOpShiftLeftLogical = 196,
+ SpvOpBitwiseOr = 197,
+ SpvOpBitwiseXor = 198,
+ SpvOpBitwiseAnd = 199,
+ SpvOpNot = 200,
+ SpvOpBitFieldInsert = 201,
+ SpvOpBitFieldSExtract = 202,
+ SpvOpBitFieldUExtract = 203,
+ SpvOpBitReverse = 204,
+ SpvOpBitCount = 205,
+ SpvOpDPdx = 207,
+ SpvOpDPdy = 208,
+ SpvOpFwidth = 209,
+ SpvOpDPdxFine = 210,
+ SpvOpDPdyFine = 211,
+ SpvOpFwidthFine = 212,
+ SpvOpDPdxCoarse = 213,
+ SpvOpDPdyCoarse = 214,
+ SpvOpFwidthCoarse = 215,
+ SpvOpEmitVertex = 218,
+ SpvOpEndPrimitive = 219,
+ SpvOpEmitStreamVertex = 220,
+ SpvOpEndStreamPrimitive = 221,
+ SpvOpControlBarrier = 224,
+ SpvOpMemoryBarrier = 225,
+ SpvOpAtomicLoad = 227,
+ SpvOpAtomicStore = 228,
+ SpvOpAtomicExchange = 229,
+ SpvOpAtomicCompareExchange = 230,
+ SpvOpAtomicCompareExchangeWeak = 231,
+ SpvOpAtomicIIncrement = 232,
+ SpvOpAtomicIDecrement = 233,
+ SpvOpAtomicIAdd = 234,
+ SpvOpAtomicISub = 235,
+ SpvOpAtomicSMin = 236,
+ SpvOpAtomicUMin = 237,
+ SpvOpAtomicSMax = 238,
+ SpvOpAtomicUMax = 239,
+ SpvOpAtomicAnd = 240,
+ SpvOpAtomicOr = 241,
+ SpvOpAtomicXor = 242,
+ SpvOpPhi = 245,
+ SpvOpLoopMerge = 246,
+ SpvOpSelectionMerge = 247,
+ SpvOpLabel = 248,
+ SpvOpBranch = 249,
+ SpvOpBranchConditional = 250,
+ SpvOpSwitch = 251,
+ SpvOpKill = 252,
+ SpvOpReturn = 253,
+ SpvOpReturnValue = 254,
+ SpvOpUnreachable = 255,
+ SpvOpLifetimeStart = 256,
+ SpvOpLifetimeStop = 257,
+ SpvOpGroupAsyncCopy = 259,
+ SpvOpGroupWaitEvents = 260,
+ SpvOpGroupAll = 261,
+ SpvOpGroupAny = 262,
+ SpvOpGroupBroadcast = 263,
+ SpvOpGroupIAdd = 264,
+ SpvOpGroupFAdd = 265,
+ SpvOpGroupFMin = 266,
+ SpvOpGroupUMin = 267,
+ SpvOpGroupSMin = 268,
+ SpvOpGroupFMax = 269,
+ SpvOpGroupUMax = 270,
+ SpvOpGroupSMax = 271,
+ SpvOpReadPipe = 274,
+ SpvOpWritePipe = 275,
+ SpvOpReservedReadPipe = 276,
+ SpvOpReservedWritePipe = 277,
+ SpvOpReserveReadPipePackets = 278,
+ SpvOpReserveWritePipePackets = 279,
+ SpvOpCommitReadPipe = 280,
+ SpvOpCommitWritePipe = 281,
+ SpvOpIsValidReserveId = 282,
+ SpvOpGetNumPipePackets = 283,
+ SpvOpGetMaxPipePackets = 284,
+ SpvOpGroupReserveReadPipePackets = 285,
+ SpvOpGroupReserveWritePipePackets = 286,
+ SpvOpGroupCommitReadPipe = 287,
+ SpvOpGroupCommitWritePipe = 288,
+ SpvOpEnqueueMarker = 291,
+ SpvOpEnqueueKernel = 292,
+ SpvOpGetKernelNDrangeSubGroupCount = 293,
+ SpvOpGetKernelNDrangeMaxSubGroupSize = 294,
+ SpvOpGetKernelWorkGroupSize = 295,
+ SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ SpvOpRetainEvent = 297,
+ SpvOpReleaseEvent = 298,
+ SpvOpCreateUserEvent = 299,
+ SpvOpIsValidEvent = 300,
+ SpvOpSetUserEventStatus = 301,
+ SpvOpCaptureEventProfilingInfo = 302,
+ SpvOpGetDefaultQueue = 303,
+ SpvOpBuildNDRange = 304,
+ SpvOpImageSparseSampleImplicitLod = 305,
+ SpvOpImageSparseSampleExplicitLod = 306,
+ SpvOpImageSparseSampleDrefImplicitLod = 307,
+ SpvOpImageSparseSampleDrefExplicitLod = 308,
+ SpvOpImageSparseSampleProjImplicitLod = 309,
+ SpvOpImageSparseSampleProjExplicitLod = 310,
+ SpvOpImageSparseSampleProjDrefImplicitLod = 311,
+ SpvOpImageSparseSampleProjDrefExplicitLod = 312,
+ SpvOpImageSparseFetch = 313,
+ SpvOpImageSparseGather = 314,
+ SpvOpImageSparseDrefGather = 315,
+ SpvOpImageSparseTexelsResident = 316,
+ SpvOpNoLine = 317,
+ SpvOpAtomicFlagTestAndSet = 318,
+ SpvOpAtomicFlagClear = 319,
+ SpvOpImageSparseRead = 320,
+} SpvOp;
+
+#endif // #ifndef spirv_H