diff options
author | ethannicholas <ethannicholas@google.com> | 2016-07-01 08:22:01 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-07-01 08:22:01 -0700 |
commit | b3058bdb1049ca75d526eb9f11e1a42a49e63585 (patch) | |
tree | 9d72636cd37c2100869fcd02041072b19b86a717 /src/sksl | |
parent | e7d1b24ff0a04686aef54fcb9feaca7a03e19891 (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')
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 |