Initial checkin of new SkSL interpreter.
Not quite feature complete yet, but at a point where it's worth checking
in.
Bug: skia:
Change-Id: I21141d30e8582a79e94450d84e56bacc067249e0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/201685
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/gm/runtimecolorfilter.cpp b/gm/runtimecolorfilter.cpp
index ebc6a20..0f84dff 100644
--- a/gm/runtimecolorfilter.cpp
+++ b/gm/runtimecolorfilter.cpp
@@ -33,7 +33,7 @@
canvas->drawImage(img, 0, 0, nullptr);
float b = 0.75;
- sk_sp<SkData> data = SkData::MakeWithoutCopy(&b, sizeof(b));
+ sk_sp<SkData> data = SkData::MakeWithCopy(&b, sizeof(b));
auto cf1 = SkRuntimeColorFilterFactory(SkString(SKSL_TEST_SRC), runtimeCpuFunc).make(data);
SkPaint p;
p.setColorFilter(cf1);
@@ -49,3 +49,25 @@
p.setColorFilter(cf2);
canvas->drawImage(img, 512, 0, &p);
}
+
+DEF_SIMPLE_GM(runtimecolorfilter_interpreted, canvas, 768, 256) {
+ auto img = GetResourceAsImage("images/mandrill_256.png");
+ canvas->drawImage(img, 0, 0, nullptr);
+
+ float b = 0.75;
+ sk_sp<SkData> data = SkData::MakeWithCopy(&b, sizeof(b));
+ auto cf1 = SkRuntimeColorFilterFactory(SkString(SKSL_TEST_SRC), nullptr).make(data);
+ SkPaint p;
+ p.setColorFilter(cf1);
+ canvas->drawImage(img, 256, 0, &p);
+
+ static constexpr size_t kBufferSize = 512;
+ char buffer[kBufferSize];
+ SkBinaryWriteBuffer wb(buffer, kBufferSize);
+ wb.writeFlattenable(cf1.get());
+ SkReadBuffer rb(buffer, kBufferSize);
+ auto cf2 = rb.readColorFilter();
+ SkASSERT(cf2);
+ p.setColorFilter(cf2);
+ canvas->drawImage(img, 512, 0, &p);
+}
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 755fa95..38c706a 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -7,6 +7,7 @@
_src = get_path_info("../src", "abspath")
skia_sksl_sources = [
+ "$_src/sksl/SkSLByteCodeGenerator.cpp",
"$_src/sksl/SkSLCFGGenerator.cpp",
"$_src/sksl/SkSLCompiler.cpp",
"$_src/sksl/SkSLCPPCodeGenerator.cpp",
diff --git a/gn/tests.gni b/gn/tests.gni
index 14738e3..3ec84ec 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -239,6 +239,7 @@
"$_tests/SkSLErrorTest.cpp",
"$_tests/SkSLFPTest.cpp",
"$_tests/SkSLGLSLTest.cpp",
+ "$_tests/SkSLInterpreterTest.cpp",
"$_tests/SkSLJITTest.cpp",
"$_tests/SkSLMemoryLayoutTest.cpp",
"$_tests/SkSLMetalTest.cpp",
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index ab337b1..8959c1b 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -382,6 +382,8 @@
#if SK_SUPPORT_GPU
#include "effects/GrSkSLFP.h"
#include "GrRecordingContext.h"
+#include "SkSLByteCode.h"
+#include "SkSLInterpreter.h"
class SkRuntimeColorFilter : public SkColorFilter {
public:
@@ -402,24 +404,51 @@
#endif
void onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
- // if this assert fails, it either means no CPU function was provided when this filter was
- // created, or we have flattened and unflattened the filter, which nulls out this pointer.
- // We don't currently have a means to flatten colorfilters containing CPU functions.
- SkASSERT(fCpuFunction);
- struct Ctx : public SkRasterPipeline_CallbackCtx {
- SkRuntimeColorFilterFn cpuFn;
- const void* inputs;
- };
- auto ctx = rec.fAlloc->make<Ctx>();
- ctx->inputs = fInputs->data();
- ctx->cpuFn = fCpuFunction;
- ctx->fn = [](SkRasterPipeline_CallbackCtx* arg, int active_pixels) {
- auto ctx = (Ctx*)arg;
- for (int i = 0; i < active_pixels; i++) {
- ctx->cpuFn(ctx->rgba + i * 4, ctx->inputs);
+ if (fCpuFunction) {
+ struct CpuFuncCtx : public SkRasterPipeline_CallbackCtx {
+ SkRuntimeColorFilterFn cpuFn;
+ const void* inputs;
+ };
+ auto ctx = rec.fAlloc->make<CpuFuncCtx>();
+ ctx->inputs = fInputs->data();
+ ctx->cpuFn = fCpuFunction;
+ ctx->fn = [](SkRasterPipeline_CallbackCtx* arg, int active_pixels) {
+ auto ctx = (CpuFuncCtx*)arg;
+ for (int i = 0; i < active_pixels; i++) {
+ ctx->cpuFn(ctx->rgba + i * 4, ctx->inputs);
+ }
+ };
+ rec.fPipeline->append(SkRasterPipeline::callback, ctx);
+ } else {
+ struct InterpreterCtx : public SkRasterPipeline_CallbackCtx {
+ SkSL::ByteCodeFunction* main;
+ std::unique_ptr<SkSL::Interpreter> interpreter;
+ const void* inputs;
+ };
+ auto ctx = rec.fAlloc->make<InterpreterCtx>();
+ ctx->inputs = fInputs->data();
+ SkSL::Compiler c;
+ std::unique_ptr<SkSL::Program> prog =
+ c.convertProgram(SkSL::Program::kPipelineStage_Kind,
+ SkSL::String(fSkSL.c_str()),
+ SkSL::Program::Settings());
+ if (c.errorCount()) {
+ SkDebugf("%s\n", c.errorText().c_str());
+ SkASSERT(false);
}
- };
- rec.fPipeline->append(SkRasterPipeline::callback, ctx);
+ std::unique_ptr<SkSL::ByteCode> byteCode = c.toByteCode(*prog);
+ ctx->main = byteCode->fFunctions[0].get();
+ ctx->interpreter.reset(new SkSL::Interpreter(std::move(prog), std::move(byteCode)));
+ ctx->fn = [](SkRasterPipeline_CallbackCtx* arg, int active_pixels) {
+ auto ctx = (InterpreterCtx*)arg;
+ for (int i = 0; i < active_pixels; i++) {
+ ctx->interpreter->run(*ctx->main,
+ (SkSL::Interpreter::Value*) (ctx->rgba + i * 4),
+ (SkSL::Interpreter::Value*) ctx->inputs);
+ }
+ };
+ rec.fPipeline->append(SkRasterPipeline::callback, ctx);
+ }
}
protected:
diff --git a/src/sksl/SkSLByteCode.h b/src/sksl/SkSLByteCode.h
new file mode 100644
index 0000000..df097e4
--- /dev/null
+++ b/src/sksl/SkSLByteCode.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BYTECODE
+#define SKSL_BYTECODE
+
+#include "ir/SkSLFunctionDeclaration.h"
+
+namespace SkSL {
+
+enum class ByteCodeInstruction : uint8_t {
+ kInvalid,
+ // B = bool, F = float, I = int, S = signed, U = unsigned
+ kAddF,
+ kAddI,
+ kAndB,
+ kAndI,
+ kBranch,
+ kCompareIEQ,
+ kCompareINEQ,
+ kCompareFEQ,
+ kCompareFGT,
+ kCompareFGTEQ,
+ kCompareFLT,
+ kCompareFLTEQ,
+ kCompareFNEQ,
+ kCompareSGT,
+ kCompareSGTEQ,
+ kCompareSLT,
+ kCompareSLTEQ,
+ kCompareUGT,
+ kCompareUGTEQ,
+ kCompareULT,
+ kCompareULTEQ,
+ // Followed by a 16 bit address
+ kConditionalBranch,
+ // Pops and prints the top value from the stack
+ kDebugPrint,
+ kDivideF,
+ kDivideS,
+ kDivideU,
+ // Duplicates the top stack value
+ kDup,
+ // Followed by a byte indicating number of slots to copy below the underlying element.
+ // dupdown 2 yields: ... value3 value2 value1 => .. value2 value1 value3 value2 value2
+ kDupDown,
+ kFloatToInt,
+ kSignedToFloat,
+ kUnsignedToFloat,
+ kLoad,
+ // Followed by a byte indicating global slot to load
+ kLoadGlobal,
+ // Followed by a count byte (1-4), and then one byte per swizzle component (0-3).
+ kLoadSwizzle,
+ kNegateF,
+ kNegateS,
+ kMultiplyF,
+ kMultiplyS,
+ kMultiplyU,
+ kNot,
+ kOrB,
+ kOrI,
+ // Followed by a byte indicating parameter slot to load
+ kParameter,
+ kPop,
+ // Followed by a 32 bit value containing the value to push
+ kPushImmediate,
+ kRemainderS,
+ kRemainderU,
+ kStore,
+ kStoreGlobal,
+ // Followed by a count byte (1-4), and then one byte per swizzle component (0-3). Expects the
+ // stack to look like: ... target v1 v2 v3 v4, where the number of 'v's is equal to the number
+ // of swizzle components. After the store, the target and all v's are popped from the stack.
+ kStoreSwizzle,
+ // Followed by two count bytes (1-4), and then one byte per swizzle component (0-3). The first
+ // count byte provides the current vector size (the vector is the top n stack elements), and the
+ // second count byte provides the swizzle component count.
+ kSwizzle,
+ kSubtractF,
+ kSubtractI,
+ // Followed by a byte indicating vector count. Modifies the next instruction to operate on the
+ // indicated number of columns, e.g. kVector 2 kMultiplyf performs a float2 * float2 operation.
+ kVector,
+};
+
+struct ByteCode;
+
+struct ByteCodeFunction {
+ ByteCodeFunction(const ByteCode* owner, const FunctionDeclaration* declaration)
+ : fOwner(*owner)
+ , fDeclaration(*declaration) {}
+
+ const ByteCode& fOwner;
+ const FunctionDeclaration& fDeclaration;
+ int fParameterCount = 0;
+ int fLocalCount = 0;
+ std::vector<uint8_t> fCode;
+};
+
+struct ByteCode {
+ int fGlobalCount = 0;
+ int fInputCount = 0;
+ // one entry per input slot, contains the global slot to which the input slot maps
+ std::vector<uint8_t> fInputSlots;
+ std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions;
+};
+
+}
+
+#endif
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
new file mode 100644
index 0000000..5a1299d
--- /dev/null
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -0,0 +1,787 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLByteCodeGenerator.h"
+
+namespace SkSL {
+
+static int slot_count(const Type& type) {
+ return type.columns() * type.rows();
+}
+
+bool ByteCodeGenerator::generateCode() {
+ for (const auto& e : fProgram) {
+ switch (e.fKind) {
+ case ProgramElement::kFunction_Kind: {
+ std::unique_ptr<ByteCodeFunction> f = this->writeFunction((FunctionDefinition&) e);
+ if (!f) {
+ return false;
+ }
+ fOutput->fFunctions.push_back(std::move(f));
+ break;
+ }
+ case ProgramElement::kVar_Kind: {
+ VarDeclarations& decl = (VarDeclarations&) e;
+ for (const auto& v : decl.fVars) {
+ const Variable* declVar = ((VarDeclaration&) *v).fVar;
+ if (declVar->fModifiers.fLayout.fBuiltin >= 0) {
+ continue;
+ }
+ if (declVar->fModifiers.fFlags & Modifiers::kIn_Flag) {
+ for (int i = slot_count(declVar->fType); i > 0; --i) {
+ fOutput->fInputSlots.push_back(fOutput->fGlobalCount++);
+ }
+ } else {
+ fOutput->fGlobalCount += slot_count(declVar->fType);
+ }
+ }
+ break;
+ }
+ default:
+ ; // ignore
+ }
+ }
+ return true;
+}
+
+std::unique_ptr<ByteCodeFunction> ByteCodeGenerator::writeFunction(const FunctionDefinition& f) {
+ fFunction = &f;
+ std::unique_ptr<ByteCodeFunction> result(new ByteCodeFunction(fOutput, &f.fDeclaration));
+ fParameterCount = 0;
+ for (const auto& p : f.fDeclaration.fParameters) {
+ fParameterCount += p->fType.columns() * p->fType.rows();
+ }
+ fCode = &result->fCode;
+ this->writeStatement(*f.fBody);
+ result->fParameterCount = fParameterCount;
+ result->fLocalCount = fLocals.size();
+ fLocals.clear();
+ fFunction = nullptr;
+ return result;
+}
+
+enum class TypeCategory {
+ kBool,
+ kSigned,
+ kUnsigned,
+ kFloat,
+};
+
+static TypeCategory type_category(const Type& type) {
+ switch (type.kind()) {
+ case Type::Kind::kVector_Kind:
+ case Type::Kind::kMatrix_Kind:
+ return type_category(type.componentType());
+ default:
+ if (type.fName == "bool") {
+ return TypeCategory::kBool;
+ } else if (type.fName == "int" || type.fName == "short") {
+ return TypeCategory::kSigned;
+ } else if (type.fName == "uint" || type.fName == "ushort") {
+ return TypeCategory::kUnsigned;
+ } else {
+ SkASSERT(type.fName == "float" || type.fName == "half");
+ return TypeCategory::kFloat;
+ }
+ ABORT("unsupported type: %s\n", type.description().c_str());
+ }
+}
+
+int ByteCodeGenerator::getLocation(const Variable& var) {
+ // given that we seldom have more than a couple of variables, linear search is probably the most
+ // efficient way to handle lookups
+ switch (var.fStorage) {
+ case Variable::kLocal_Storage: {
+ for (int i = fLocals.size() - 1; i >= 0; --i) {
+ if (fLocals[i] == &var) {
+ return fParameterCount + i;
+ }
+ }
+ int result = fParameterCount + fLocals.size();
+ fLocals.push_back(&var);
+ for (int i = 0; i < slot_count(var.fType) - 1; ++i) {
+ fLocals.push_back(nullptr);
+ }
+ return result;
+ }
+ case Variable::kParameter_Storage: {
+ int offset = 0;
+ for (const auto& p : fFunction->fDeclaration.fParameters) {
+ if (p == &var) {
+ return offset;
+ }
+ offset += slot_count(p->fType);
+ }
+ SkASSERT(false);
+ return -1;
+ }
+ case Variable::kGlobal_Storage: {
+ int offset = 0;
+ for (const auto& e : fProgram) {
+ if (e.fKind == ProgramElement::kVar_Kind) {
+ VarDeclarations& decl = (VarDeclarations&) e;
+ for (const auto& v : decl.fVars) {
+ const Variable* declVar = ((VarDeclaration&) *v).fVar;
+ if (declVar->fModifiers.fLayout.fBuiltin >= 0) {
+ continue;
+ }
+ if (declVar == &var) {
+ return offset;
+ }
+ offset += slot_count(declVar->fType);
+ }
+ }
+ }
+ SkASSERT(false);
+ return -1;
+ }
+ default:
+ SkASSERT(false);
+ return 0;
+ }
+}
+
+void ByteCodeGenerator::write8(uint8_t b) {
+ fCode->push_back(b);
+}
+
+void ByteCodeGenerator::write16(uint16_t i) {
+ this->write8(i >> 8);
+ this->write8(i);
+}
+
+void ByteCodeGenerator::write32(uint32_t i) {
+ this->write8(i >> 24);
+ this->write8(i >> 16);
+ this->write8(i >> 8);
+ this->write8(i);
+}
+
+void ByteCodeGenerator::write(ByteCodeInstruction i) {
+ this->write8((uint8_t) i);
+}
+
+void ByteCodeGenerator::writeTypedInstruction(const Type& type, ByteCodeInstruction s,
+ ByteCodeInstruction u, ByteCodeInstruction f) {
+ switch (type_category(type)) {
+ case TypeCategory::kSigned:
+ this->write(s);
+ break;
+ case TypeCategory::kUnsigned:
+ this->write(u);
+ break;
+ case TypeCategory::kFloat:
+ this->write(f);
+ break;
+ default:
+ SkASSERT(false);
+ }
+}
+
+void ByteCodeGenerator::writeBinaryExpression(const BinaryExpression& b) {
+ if (b.fOperator == Token::Kind::EQ) {
+ std::unique_ptr<LValue> lvalue = this->getLValue(*b.fLeft);
+ this->writeExpression(*b.fRight);
+ this->write(ByteCodeInstruction::kDupDown);
+ this->write8(slot_count(b.fRight->fType));
+ lvalue->store();
+ return;
+ }
+ Token::Kind op;
+ std::unique_ptr<LValue> lvalue;
+ if (is_assignment(b.fOperator)) {
+ lvalue = this->getLValue(*b.fLeft);
+ lvalue->load();
+ op = remove_assignment(b.fOperator);
+ } else {
+ this->writeExpression(*b.fLeft);
+ op = b.fOperator;
+ if (b.fLeft->fType.kind() == Type::kScalar_Kind &&
+ b.fRight->fType.kind() == Type::kVector_Kind) {
+ for (int i = b.fRight->fType.columns(); i > 1; --i) {
+ this->write(ByteCodeInstruction::kDup);
+ }
+ }
+ }
+ this->writeExpression(*b.fRight);
+ if (b.fLeft->fType.kind() == Type::kVector_Kind &&
+ b.fRight->fType.kind() == Type::kScalar_Kind) {
+ for (int i = b.fLeft->fType.columns(); i > 1; --i) {
+ this->write(ByteCodeInstruction::kDup);
+ }
+ }
+ int count = slot_count(b.fType);
+ if (count > 1) {
+ this->write(ByteCodeInstruction::kVector);
+ this->write8(count);
+ }
+ switch (op) {
+ case Token::Kind::EQEQ:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareIEQ,
+ ByteCodeInstruction::kCompareIEQ,
+ ByteCodeInstruction::kCompareFEQ);
+ break;
+ case Token::Kind::GT:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareSGT,
+ ByteCodeInstruction::kCompareUGT,
+ ByteCodeInstruction::kCompareFGT);
+ break;
+ case Token::Kind::GTEQ:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareSGTEQ,
+ ByteCodeInstruction::kCompareUGTEQ,
+ ByteCodeInstruction::kCompareFGTEQ);
+ break;
+ case Token::Kind::LT:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareSLT,
+ ByteCodeInstruction::kCompareULT,
+ ByteCodeInstruction::kCompareFLT);
+ break;
+ case Token::Kind::LTEQ:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareSLTEQ,
+ ByteCodeInstruction::kCompareULTEQ,
+ ByteCodeInstruction::kCompareFLTEQ);
+ break;
+ case Token::Kind::MINUS:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kSubtractI,
+ ByteCodeInstruction::kSubtractI,
+ ByteCodeInstruction::kSubtractF);
+ break;
+ case Token::Kind::NEQ:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kCompareINEQ,
+ ByteCodeInstruction::kCompareINEQ,
+ ByteCodeInstruction::kCompareFNEQ);
+ break;
+ case Token::Kind::PERCENT:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kRemainderS,
+ ByteCodeInstruction::kRemainderU,
+ ByteCodeInstruction::kInvalid);
+ break;
+ case Token::Kind::PLUS:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kAddI,
+ ByteCodeInstruction::kAddI,
+ ByteCodeInstruction::kAddF);
+ break;
+ case Token::Kind::SLASH:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kDivideS,
+ ByteCodeInstruction::kDivideU,
+ ByteCodeInstruction::kDivideF);
+ break;
+ case Token::Kind::STAR:
+ this->writeTypedInstruction(b.fLeft->fType, ByteCodeInstruction::kMultiplyS,
+ ByteCodeInstruction::kMultiplyU,
+ ByteCodeInstruction::kMultiplyF);
+ break;
+ default:
+ SkASSERT(false);
+ }
+ if (lvalue) {
+ this->write(ByteCodeInstruction::kDupDown);
+ this->write8(slot_count(b.fType));
+ lvalue->store();
+ }
+}
+
+void ByteCodeGenerator::writeBoolLiteral(const BoolLiteral& b) {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(1);
+}
+
+void ByteCodeGenerator::writeConstructor(const Constructor& c) {
+ if (c.fArguments.size() == 1 &&
+ type_category(c.fType) == type_category(c.fArguments[0]->fType)) {
+ // cast from float to half or similar no-op
+ this->writeExpression(*c.fArguments[0]);
+ return;
+ }
+ for (const auto& arg : c.fArguments) {
+ this->writeExpression(*arg);
+ }
+ if (c.fArguments.size() == 1) {
+ TypeCategory inCategory = type_category(c.fArguments[0]->fType);
+ TypeCategory outCategory = type_category(c.fType);
+ if (inCategory != outCategory) {
+ int count = c.fType.columns();
+ if (count > 1) {
+ this->write(ByteCodeInstruction::kVector);
+ this->write8(count);
+ }
+ if (inCategory == TypeCategory::kFloat) {
+ SkASSERT(outCategory == TypeCategory::kSigned ||
+ outCategory == TypeCategory::kUnsigned);
+ this->write(ByteCodeInstruction::kFloatToInt);
+ } else if (outCategory == TypeCategory::kFloat) {
+ if (inCategory == TypeCategory::kSigned) {
+ this->write(ByteCodeInstruction::kSignedToFloat);
+ } else {
+ SkASSERT(inCategory == TypeCategory::kUnsigned);
+ this->write(ByteCodeInstruction::kUnsignedToFloat);
+ }
+ } else {
+ SkASSERT(false);
+ }
+ }
+ }
+}
+
+void ByteCodeGenerator::writeFieldAccess(const FieldAccess& f) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ union { float f; uint32_t u; } pun = { (float) f.fValue };
+ this->write32(pun.u);
+}
+
+void ByteCodeGenerator::writeFunctionCall(const FunctionCall& f) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeIndexExpression(const IndexExpression& i) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeIntLiteral(const IntLiteral& i) {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(i.fValue);
+}
+
+void ByteCodeGenerator::writeNullLiteral(const NullLiteral& n) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writePrefixExpression(const PrefixExpression& p) {
+ switch (p.fOperator) {
+ case Token::Kind::PLUSPLUS: // fall through
+ case Token::Kind::MINUSMINUS: {
+ std::unique_ptr<LValue> lvalue = this->getLValue(*p.fOperand);
+ lvalue->load();
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(1);
+ if (p.fOperator == Token::Kind::PLUSPLUS) {
+ this->writeTypedInstruction(p.fType,
+ ByteCodeInstruction::kAddI,
+ ByteCodeInstruction::kAddI,
+ ByteCodeInstruction::kAddF);
+ } else {
+ this->writeTypedInstruction(p.fType,
+ ByteCodeInstruction::kSubtractI,
+ ByteCodeInstruction::kSubtractI,
+ ByteCodeInstruction::kSubtractF);
+ }
+ this->write(ByteCodeInstruction::kDupDown);
+ this->write8(slot_count(p.fType));
+ lvalue->store();
+ break;
+ }
+ case Token::Kind::MINUS:
+ this->writeTypedInstruction(p.fType,
+ ByteCodeInstruction::kNegateS,
+ ByteCodeInstruction::kInvalid,
+ ByteCodeInstruction::kNegateF);
+ break;
+ default:
+ SkASSERT(false);
+ }
+}
+
+void ByteCodeGenerator::writePostfixExpression(const PostfixExpression& p) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeSwizzle(const Swizzle& s) {
+ switch (s.fBase->fKind) {
+ case Expression::kVariableReference_Kind: {
+ const Variable& var = ((VariableReference&) *s.fBase).fVariable;
+ int location = this->getLocation(var);
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(location);
+ this->write(ByteCodeInstruction::kLoadSwizzle);
+ this->write8(s.fComponents.size());
+ for (int c : s.fComponents) {
+ this->write8(c);
+ }
+ break;
+ }
+ default:
+ this->writeExpression(*s.fBase);
+ this->write(ByteCodeInstruction::kSwizzle);
+ this->write8(s.fBase->fType.columns());
+ this->write8(s.fComponents.size());
+ for (int c : s.fComponents) {
+ this->write8(c);
+ }
+ }
+}
+
+void ByteCodeGenerator::writeVariableReference(const VariableReference& v) {
+ if (v.fVariable.fStorage == Variable::kGlobal_Storage) {
+ this->write(ByteCodeInstruction::kLoadGlobal);
+ int location = this->getLocation(v.fVariable);
+ SkASSERT(location <= 255);
+ this->write8(location);
+ } else {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(this->getLocation(v.fVariable));
+ int count = slot_count(v.fType);
+ if (count > 1) {
+ this->write(ByteCodeInstruction::kVector);
+ this->write8(count);
+ }
+ this->write(ByteCodeInstruction::kLoad);
+ }
+}
+
+void ByteCodeGenerator::writeTernaryExpression(const TernaryExpression& t) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeExpression(const Expression& e) {
+ switch (e.fKind) {
+ case Expression::kBinary_Kind:
+ this->writeBinaryExpression((BinaryExpression&) e);
+ break;
+ case Expression::kBoolLiteral_Kind:
+ this->writeBoolLiteral((BoolLiteral&) e);
+ break;
+ case Expression::kConstructor_Kind:
+ this->writeConstructor((Constructor&) e);
+ break;
+ case Expression::kFieldAccess_Kind:
+ this->writeFieldAccess((FieldAccess&) e);
+ break;
+ case Expression::kFloatLiteral_Kind:
+ this->writeFloatLiteral((FloatLiteral&) e);
+ break;
+ case Expression::kFunctionCall_Kind:
+ this->writeFunctionCall((FunctionCall&) e);
+ break;
+ case Expression::kIndex_Kind:
+ this->writeIndexExpression((IndexExpression&) e);
+ break;
+ case Expression::kIntLiteral_Kind:
+ this->writeIntLiteral((IntLiteral&) e);
+ break;
+ case Expression::kNullLiteral_Kind:
+ this->writeNullLiteral((NullLiteral&) e);
+ break;
+ case Expression::kPrefix_Kind:
+ this->writePrefixExpression((PrefixExpression&) e);
+ break;
+ case Expression::kPostfix_Kind:
+ this->writePostfixExpression((PostfixExpression&) e);
+ break;
+ case Expression::kSwizzle_Kind:
+ this->writeSwizzle((Swizzle&) e);
+ break;
+ case Expression::kVariableReference_Kind:
+ this->writeVariableReference((VariableReference&) e);
+ break;
+ case Expression::kTernary_Kind:
+ this->writeTernaryExpression((TernaryExpression&) e);
+ break;
+ default:
+ printf("unsupported expression %s\n", e.description().c_str());
+ SkASSERT(false);
+ }
+}
+
+void ByteCodeGenerator::writeTarget(const Expression& e) {
+ switch (e.fKind) {
+ case Expression::kVariableReference_Kind:
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(this->getLocation(((VariableReference&) e).fVariable));
+ break;
+ case Expression::kIndex_Kind:
+ case Expression::kTernary_Kind:
+ default:
+ printf("unsupported target %s\n", e.description().c_str());
+ SkASSERT(false);
+ }
+}
+
+class ByteCodeSwizzleLValue : public ByteCodeGenerator::LValue {
+public:
+ ByteCodeSwizzleLValue(ByteCodeGenerator* generator, const Swizzle& swizzle)
+ : INHERITED(*generator)
+ , fSwizzle(swizzle) {
+ fGenerator.writeTarget(*swizzle.fBase);
+ }
+
+ void load() override {
+ fGenerator.write(ByteCodeInstruction::kDup);
+ fGenerator.write(ByteCodeInstruction::kLoadSwizzle);
+ fGenerator.write8(fSwizzle.fComponents.size());
+ for (int c : fSwizzle.fComponents) {
+ fGenerator.write8(c);
+ }
+ }
+
+ void store() override {
+ fGenerator.write(ByteCodeInstruction::kStoreSwizzle);
+ fGenerator.write8(fSwizzle.fComponents.size());
+ for (int c : fSwizzle.fComponents) {
+ fGenerator.write8(c);
+ }
+ }
+
+private:
+ const Swizzle& fSwizzle;
+
+ typedef LValue INHERITED;
+};
+
+class ByteCodeVariableLValue : public ByteCodeGenerator::LValue {
+public:
+ ByteCodeVariableLValue(ByteCodeGenerator* generator, const Variable& var)
+ : INHERITED(*generator)
+ , fCount(slot_count(var.fType))
+ , fIsGlobal(var.fStorage == Variable::kGlobal_Storage) {
+ fGenerator.write(ByteCodeInstruction::kPushImmediate);
+ fGenerator.write32(generator->getLocation(var));
+ }
+
+ void load() override {
+ fGenerator.write(ByteCodeInstruction::kDup);
+ if (fCount > 1) {
+ fGenerator.write(ByteCodeInstruction::kVector);
+ fGenerator.write8(fCount);
+ }
+ fGenerator.write(fIsGlobal ? ByteCodeInstruction::kLoadGlobal : ByteCodeInstruction::kLoad);
+ }
+
+ void store() override {
+ if (fCount > 1) {
+ fGenerator.write(ByteCodeInstruction::kVector);
+ fGenerator.write8(fCount);
+ }
+ fGenerator.write(fIsGlobal ? ByteCodeInstruction::kStoreGlobal
+ : ByteCodeInstruction::kStore);
+ }
+
+private:
+ typedef LValue INHERITED;
+
+ int fCount;
+
+ bool fIsGlobal;
+};
+
+std::unique_ptr<ByteCodeGenerator::LValue> ByteCodeGenerator::getLValue(const Expression& e) {
+ switch (e.fKind) {
+ case Expression::kIndex_Kind:
+ // not yet implemented
+ abort();
+ case Expression::kVariableReference_Kind:
+ return std::unique_ptr<LValue>(new ByteCodeVariableLValue(this,
+ ((VariableReference&) e).fVariable));
+ case Expression::kSwizzle_Kind:
+ return std::unique_ptr<LValue>(new ByteCodeSwizzleLValue(this, (Swizzle&) e));
+ case Expression::kTernary_Kind:
+ default:
+ printf("unsupported lvalue %s\n", e.description().c_str());
+ return nullptr;
+ }
+}
+
+void ByteCodeGenerator::writeBlock(const Block& b) {
+ for (const auto& s : b.fStatements) {
+ this->writeStatement(*s);
+ }
+}
+
+void ByteCodeGenerator::setBreakTargets() {
+ std::vector<DeferredLocation>& breaks = fBreakTargets.top();
+ for (DeferredLocation& b : breaks) {
+ b.set();
+ }
+ fBreakTargets.pop();
+}
+
+void ByteCodeGenerator::setContinueTargets() {
+ std::vector<DeferredLocation>& continues = fContinueTargets.top();
+ for (DeferredLocation& c : continues) {
+ c.set();
+ }
+ fContinueTargets.pop();
+}
+
+void ByteCodeGenerator::writeBreakStatement(const BreakStatement& b) {
+ this->write(ByteCodeInstruction::kBranch);
+ fBreakTargets.top().emplace_back(this);
+}
+
+void ByteCodeGenerator::writeContinueStatement(const ContinueStatement& c) {
+ this->write(ByteCodeInstruction::kBranch);
+ fContinueTargets.top().emplace_back(this);
+}
+
+void ByteCodeGenerator::writeDoStatement(const DoStatement& d) {
+ fContinueTargets.emplace();
+ fBreakTargets.emplace();
+ size_t start = fCode->size();
+ this->writeStatement(*d.fStatement);
+ this->setContinueTargets();
+ this->writeExpression(*d.fTest);
+ this->write(ByteCodeInstruction::kConditionalBranch);
+ this->write16(start);
+ this->setBreakTargets();
+}
+
+void ByteCodeGenerator::writeForStatement(const ForStatement& f) {
+ fContinueTargets.emplace();
+ fBreakTargets.emplace();
+ if (f.fInitializer) {
+ this->writeStatement(*f.fInitializer);
+ }
+ size_t start = fCode->size();
+ if (f.fTest) {
+ this->writeExpression(*f.fTest);
+ this->write(ByteCodeInstruction::kNot);
+ this->write(ByteCodeInstruction::kConditionalBranch);
+ DeferredLocation endLocation(this);
+ this->writeStatement(*f.fStatement);
+ this->setContinueTargets();
+ if (f.fNext) {
+ this->writeExpression(*f.fNext);
+ this->write(ByteCodeInstruction::kPop);
+ this->write8(slot_count(f.fNext->fType));
+ }
+ this->write(ByteCodeInstruction::kBranch);
+ this->write16(start);
+ endLocation.set();
+ } else {
+ this->writeStatement(*f.fStatement);
+ this->setContinueTargets();
+ if (f.fNext) {
+ this->writeExpression(*f.fNext);
+ this->write(ByteCodeInstruction::kPop);
+ this->write8(slot_count(f.fNext->fType));
+ }
+ this->write(ByteCodeInstruction::kBranch);
+ this->write16(start);
+ }
+ this->setBreakTargets();
+}
+
+void ByteCodeGenerator::writeIfStatement(const IfStatement& i) {
+ this->writeExpression(*i.fTest);
+ this->write(ByteCodeInstruction::kNot);
+ this->write(ByteCodeInstruction::kConditionalBranch);
+ DeferredLocation elseLocation(this);
+ this->writeStatement(*i.fIfTrue);
+ this->write(ByteCodeInstruction::kBranch);
+ DeferredLocation endLocation(this);
+ elseLocation.set();
+ if (i.fIfFalse) {
+ this->writeStatement(*i.fIfFalse);
+ }
+ endLocation.set();
+}
+
+void ByteCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeSwitchStatement(const SwitchStatement& r) {
+ // not yet implemented
+ abort();
+}
+
+void ByteCodeGenerator::writeVarDeclarations(const VarDeclarations& v) {
+ for (const auto& declStatement : v.fVars) {
+ const VarDeclaration& decl = (VarDeclaration&) *declStatement;
+ // we need to grab the location even if we don't use it, to ensure it
+ // has been allocated
+ int location = getLocation(*decl.fVar);
+ if (decl.fValue) {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(location);
+ this->writeExpression(*decl.fValue);
+ int count = slot_count(decl.fValue->fType);
+ if (count > 1) {
+ this->write(ByteCodeInstruction::kVector);
+ this->write8(count);
+ }
+ this->write(ByteCodeInstruction::kStore);
+ }
+ }
+}
+
+void ByteCodeGenerator::writeWhileStatement(const WhileStatement& w) {
+ fContinueTargets.emplace();
+ fBreakTargets.emplace();
+ size_t start = fCode->size();
+ this->writeExpression(*w.fTest);
+ this->write(ByteCodeInstruction::kNot);
+ this->write(ByteCodeInstruction::kConditionalBranch);
+ DeferredLocation endLocation(this);
+ this->writeStatement(*w.fStatement);
+ this->setContinueTargets();
+ this->write(ByteCodeInstruction::kBranch);
+ this->write16(start);
+ endLocation.set();
+ this->setBreakTargets();
+}
+
+void ByteCodeGenerator::writeStatement(const Statement& s) {
+ switch (s.fKind) {
+ case Statement::kBlock_Kind:
+ this->writeBlock((Block&) s);
+ break;
+ case Statement::kBreak_Kind:
+ this->writeBreakStatement((BreakStatement&) s);
+ break;
+ case Statement::kContinue_Kind:
+ this->writeContinueStatement((ContinueStatement&) s);
+ break;
+ case Statement::kDiscard_Kind:
+ // not yet implemented
+ abort();
+ case Statement::kDo_Kind:
+ this->writeDoStatement((DoStatement&) s);
+ break;
+ case Statement::kExpression_Kind: {
+ const Expression& expr = *((ExpressionStatement&) s).fExpression;
+ this->writeExpression(expr);
+ this->write(ByteCodeInstruction::kPop);
+ this->write8(slot_count(expr.fType));
+ break;
+ }
+ case Statement::kFor_Kind:
+ this->writeForStatement((ForStatement&) s);
+ break;
+ case Statement::kIf_Kind:
+ this->writeIfStatement((IfStatement&) s);
+ break;
+ case Statement::kNop_Kind:
+ break;
+ case Statement::kReturn_Kind:
+ this->writeReturnStatement((ReturnStatement&) s);
+ break;
+ case Statement::kSwitch_Kind:
+ this->writeSwitchStatement((SwitchStatement&) s);
+ break;
+ case Statement::kVarDeclarations_Kind:
+ this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration);
+ break;
+ case Statement::kWhile_Kind:
+ this->writeWhileStatement((WhileStatement&) s);
+ break;
+ default:
+ SkASSERT(false);
+ }
+}
+
+}
diff --git a/src/sksl/SkSLByteCodeGenerator.h b/src/sksl/SkSLByteCodeGenerator.h
new file mode 100644
index 0000000..e518861
--- /dev/null
+++ b/src/sksl/SkSLByteCodeGenerator.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_BYTECODEGENERATOR
+#define SKSL_BYTECODEGENERATOR
+
+#include <stack>
+#include <tuple>
+#include <unordered_map>
+
+#include "SkSLByteCode.h"
+#include "SkSLCodeGenerator.h"
+#include "SkSLMemoryLayout.h"
+#include "ir/SkSLBinaryExpression.h"
+#include "ir/SkSLBoolLiteral.h"
+#include "ir/SkSLBlock.h"
+#include "ir/SkSLBreakStatement.h"
+#include "ir/SkSLConstructor.h"
+#include "ir/SkSLContinueStatement.h"
+#include "ir/SkSLDoStatement.h"
+#include "ir/SkSLExpressionStatement.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/SkSLNullLiteral.h"
+#include "ir/SkSLPrefixExpression.h"
+#include "ir/SkSLPostfixExpression.h"
+#include "ir/SkSLProgramElement.h"
+#include "ir/SkSLReturnStatement.h"
+#include "ir/SkSLStatement.h"
+#include "ir/SkSLSwitchStatement.h"
+#include "ir/SkSLSwizzle.h"
+#include "ir/SkSLTernaryExpression.h"
+#include "ir/SkSLVarDeclarations.h"
+#include "ir/SkSLVarDeclarationsStatement.h"
+#include "ir/SkSLVariableReference.h"
+#include "ir/SkSLWhileStatement.h"
+#include "spirv.h"
+
+namespace SkSL {
+
+class ByteCodeGenerator : public CodeGenerator {
+public:
+ class LValue {
+ public:
+ LValue(ByteCodeGenerator& generator)
+ : fGenerator(generator) {}
+
+ virtual ~LValue() {}
+
+ /**
+ * Stack before call: ... lvalue
+ * Stack after call: ... lvalue load
+ */
+ virtual void load() = 0;
+
+ /**
+ * Stack before call: ... lvalue value
+ * Stack after call: ...
+ */
+ virtual void store() = 0;
+
+ protected:
+ ByteCodeGenerator& fGenerator;
+ };
+
+ ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
+ ByteCode* output)
+ : INHERITED(program, errors, nullptr)
+ , fContext(*context)
+ , fOutput(output) {}
+
+ bool generateCode() override;
+
+ void write8(uint8_t b);
+
+ void write16(uint16_t b);
+
+ void write32(uint32_t b);
+
+ void write(ByteCodeInstruction inst);
+
+ /**
+ * Based on 'type', writes the s (signed), u (unsigned), or f (float) instruction.
+ */
+ void writeTypedInstruction(const Type& type, ByteCodeInstruction s, ByteCodeInstruction u,
+ ByteCodeInstruction f);
+
+ /**
+ * Pushes the storage location of an lvalue to the stack.
+ */
+ void writeTarget(const Expression& expr);
+
+private:
+ // reserves 16 bits in the output code, to be filled in later with an address once we determine
+ // it
+ class DeferredLocation {
+ public:
+ DeferredLocation(ByteCodeGenerator* generator)
+ : fGenerator(*generator)
+ , fOffset(generator->fCode->size()) {
+ generator->write16(0);
+ }
+
+#ifdef SK_DEBUG
+ ~DeferredLocation() {
+ SkASSERT(fSet);
+ }
+#endif
+
+ void set() {
+ int target = fGenerator.fCode->size();
+ SkASSERT(target <= 65535);
+ (*fGenerator.fCode)[fOffset] = target >> 8;
+ (*fGenerator.fCode)[fOffset + 1] = target;
+#ifdef SK_DEBUG
+ fSet = true;
+#endif
+ }
+
+ private:
+ ByteCodeGenerator& fGenerator;
+ size_t fOffset;
+#ifdef SK_DEBUG
+ bool fSet = false;
+#endif
+ };
+
+ /**
+ * Returns the local slot into which var should be stored, allocating a new slot if it has not
+ * already been assigned one. Compound variables (e.g. vectors) will consume more than one local
+ * slot, with the getLocation return value indicating where the first element should be stored.
+ */
+ int getLocation(const Variable& var);
+
+ std::unique_ptr<ByteCodeFunction> writeFunction(const FunctionDefinition& f);
+
+ void writeVarDeclarations(const VarDeclarations& decl);
+
+ void writeVariableReference(const VariableReference& ref);
+
+ void writeExpression(const Expression& expr);
+
+ /**
+ * Pushes whatever values are required by the lvalue onto the stack, and returns an LValue
+ * permitting loads and stores to it.
+ */
+ std::unique_ptr<LValue> getLValue(const Expression& expr);
+
+ void writeFunctionCall(const FunctionCall& c);
+
+ void writeConstructor(const Constructor& c);
+
+ void writeFieldAccess(const FieldAccess& f);
+
+ void writeSwizzle(const Swizzle& swizzle);
+
+ void writeBinaryExpression(const BinaryExpression& b);
+
+ void writeTernaryExpression(const TernaryExpression& t);
+
+ void writeIndexExpression(const IndexExpression& expr);
+
+ void writeLogicalAnd(const BinaryExpression& b);
+
+ void writeLogicalOr(const BinaryExpression& o);
+
+ void writeNullLiteral(const NullLiteral& n);
+
+ void writePrefixExpression(const PrefixExpression& p);
+
+ void writePostfixExpression(const PostfixExpression& p);
+
+ void writeBoolLiteral(const BoolLiteral& b);
+
+ void writeIntLiteral(const IntLiteral& i);
+
+ void writeFloatLiteral(const FloatLiteral& f);
+
+ void writeStatement(const Statement& s);
+
+ void writeBlock(const Block& b);
+
+ void writeBreakStatement(const BreakStatement& b);
+
+ void writeContinueStatement(const ContinueStatement& c);
+
+ void writeIfStatement(const IfStatement& stmt);
+
+ void writeForStatement(const ForStatement& f);
+
+ void writeWhileStatement(const WhileStatement& w);
+
+ void writeDoStatement(const DoStatement& d);
+
+ void writeSwitchStatement(const SwitchStatement& s);
+
+ void writeReturnStatement(const ReturnStatement& r);
+
+ // updates the current set of breaks to branch to the current location
+ void setBreakTargets();
+
+ // updates the current set of continues to branch to the current location
+ void setContinueTargets();
+
+ const Context& fContext;
+
+ ByteCode* fOutput;
+
+ const FunctionDefinition* fFunction;
+
+ std::vector<uint8_t>* fCode;
+
+ std::vector<const Variable*> fLocals;
+
+ std::stack<std::vector<DeferredLocation>> fContinueTargets;
+
+ std::stack<std::vector<DeferredLocation>> fBreakTargets;
+
+ int fParameterCount;
+
+ friend class DeferredLocation;
+ friend class ByteCodeVariableLValue;
+
+ typedef CodeGenerator INHERITED;
+};
+
+}
+
+#endif
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index b19c94a..720fcb6 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -7,6 +7,7 @@
#include "SkSLCompiler.h"
+#include "SkSLByteCodeGenerator.h"
#include "SkSLCFGGenerator.h"
#include "SkSLCPPCodeGenerator.h"
#include "SkSLGLSLCodeGenerator.h"
@@ -1467,6 +1468,18 @@
return result;
}
+std::unique_ptr<ByteCode> Compiler::toByteCode(Program& program) {
+ if (!this->optimize(program)) {
+ return nullptr;
+ }
+ std::unique_ptr<ByteCode> result(new ByteCode());
+ ByteCodeGenerator cg(fContext.get(), &program, this, result.get());
+ if (cg.generateCode()) {
+ return result;
+ }
+ return nullptr;
+}
+
const char* Compiler::OperatorName(Token::Kind kind) {
switch (kind) {
case Token::PLUS: return "+";
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index eefcf2a..9b87925 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -13,6 +13,7 @@
#include <vector>
#include "ir/SkSLProgram.h"
#include "ir/SkSLSymbolTable.h"
+#include "SkSLByteCode.h"
#include "SkSLCFGGenerator.h"
#include "SkSLContext.h"
#include "SkSLErrorReporter.h"
@@ -111,6 +112,8 @@
bool toH(Program& program, String name, OutputStream& out);
+ std::unique_ptr<ByteCode> toByteCode(Program& program);
+
bool toPipelineStage(const Program& program, String* out,
std::vector<FormatArg>* outFormatArgs);
diff --git a/src/sksl/SkSLInterpreter.cpp b/src/sksl/SkSLInterpreter.cpp
index c39c7b2..f427cf2 100644
--- a/src/sksl/SkSLInterpreter.cpp
+++ b/src/sksl/SkSLInterpreter.cpp
@@ -27,189 +27,42 @@
namespace SkSL {
-void Interpreter::run() {
- for (const auto& e : *fProgram) {
- if (ProgramElement::kFunction_Kind == e.fKind) {
- const FunctionDefinition& f = (const FunctionDefinition&) e;
- if ("appendStages" == f.fDeclaration.fName) {
- this->run(f);
- return;
+static constexpr int UNINITIALIZED = 0xDEADBEEF;
+
+Interpreter::Value Interpreter::run(const ByteCodeFunction& f, Interpreter::Value args[],
+ Interpreter::Value inputs[]) {
+ fIP = 0;
+ fCurrentFunction = &f;
+ fStack.clear();
+ fGlobals.clear();
+#ifdef TRACE
+ this->disassemble(f);
+#endif
+ for (int i = 0; i < f.fParameterCount; ++i) {
+ this->push(args[i]);
+ }
+ for (int i = 0; i < f.fLocalCount; ++i) {
+ this->push(Value((int) UNINITIALIZED));
+ }
+ for (int i = 0; i < f.fOwner.fGlobalCount; ++i) {
+ fGlobals.push_back(Value((int) UNINITIALIZED));
+ }
+ for (int i = f.fOwner.fInputSlots.size() - 1; i >= 0; --i) {
+ fGlobals[f.fOwner.fInputSlots[i]] = inputs[i];
+ }
+ run();
+ int offset = 0;
+ for (const auto& p : f.fDeclaration.fParameters) {
+ if (p->fModifiers.fFlags & Modifiers::kOut_Flag) {
+ for (int i = p->fType.columns() * p->fType.rows() - 1; i >= 0; --i) {
+ args[offset] = fStack[offset];
+ ++offset;
}
+ } else {
+ offset += p->fType.columns() * p->fType.rows();
}
}
- SkASSERT(false);
-}
-
-static int SizeOf(const Type& type) {
- return 1;
-}
-
-void Interpreter::run(const FunctionDefinition& f) {
- fVars.emplace_back();
- StackIndex current = (StackIndex) fStack.size();
- for (int i = f.fDeclaration.fParameters.size() - 1; i >= 0; --i) {
- current -= SizeOf(f.fDeclaration.fParameters[i]->fType);
- fVars.back()[f.fDeclaration.fParameters[i]] = current;
- }
- fCurrentIndex.push_back({ f.fBody.get(), 0 });
- while (fCurrentIndex.size()) {
- this->runStatement();
- }
-}
-
-void Interpreter::push(Value value) {
- fStack.push_back(value);
-}
-
-Interpreter::Value Interpreter::pop() {
- auto iter = fStack.end() - 1;
- Value result = *iter;
- fStack.erase(iter);
- return result;
-}
-
- Interpreter::StackIndex Interpreter::stackAlloc(int count) {
- int result = fStack.size();
- for (int i = 0; i < count; ++i) {
- fStack.push_back(Value((int) 0xDEADBEEF));
- }
- return result;
-}
-
-void Interpreter::runStatement() {
- const Statement& stmt = *fCurrentIndex.back().fStatement;
- const size_t index = fCurrentIndex.back().fIndex;
- fCurrentIndex.pop_back();
- switch (stmt.fKind) {
- case Statement::kBlock_Kind: {
- const Block& b = (const Block&) stmt;
- if (!b.fStatements.size()) {
- break;
- }
- SkASSERT(index < b.fStatements.size());
- if (index < b.fStatements.size() - 1) {
- fCurrentIndex.push_back({ &b, index + 1 });
- }
- fCurrentIndex.push_back({ b.fStatements[index].get(), 0 });
- break;
- }
- case Statement::kBreak_Kind:
- SkASSERT(index == 0);
- abort();
- case Statement::kContinue_Kind:
- SkASSERT(index == 0);
- abort();
- case Statement::kDiscard_Kind:
- SkASSERT(index == 0);
- abort();
- case Statement::kDo_Kind:
- abort();
- case Statement::kExpression_Kind:
- SkASSERT(index == 0);
- this->evaluate(*((const ExpressionStatement&) stmt).fExpression);
- break;
- case Statement::kFor_Kind: {
- ForStatement& f = (ForStatement&) stmt;
- switch (index) {
- case 0:
- // initializer
- fCurrentIndex.push_back({ &f, 1 });
- if (f.fInitializer) {
- fCurrentIndex.push_back({ f.fInitializer.get(), 0 });
- }
- break;
- case 1:
- // test & body
- if (f.fTest && !evaluate(*f.fTest).fBool) {
- break;
- } else {
- fCurrentIndex.push_back({ &f, 2 });
- fCurrentIndex.push_back({ f.fStatement.get(), 0 });
- }
- break;
- case 2:
- // next
- if (f.fNext) {
- this->evaluate(*f.fNext);
- }
- fCurrentIndex.push_back({ &f, 1 });
- break;
- default:
- SkASSERT(false);
- }
- break;
- }
- case Statement::kGroup_Kind:
- abort();
- case Statement::kIf_Kind: {
- IfStatement& i = (IfStatement&) stmt;
- if (evaluate(*i.fTest).fBool) {
- fCurrentIndex.push_back({ i.fIfTrue.get(), 0 });
- } else if (i.fIfFalse) {
- fCurrentIndex.push_back({ i.fIfFalse.get(), 0 });
- }
- break;
- }
- case Statement::kNop_Kind:
- SkASSERT(index == 0);
- break;
- case Statement::kReturn_Kind:
- SkASSERT(index == 0);
- abort();
- case Statement::kSwitch_Kind:
- abort();
- case Statement::kVarDeclarations_Kind:
- SkASSERT(index == 0);
- for (const auto& decl :((const VarDeclarationsStatement&) stmt).fDeclaration->fVars) {
- const Variable* var = ((VarDeclaration&) *decl).fVar;
- StackIndex pos = this->stackAlloc(SizeOf(var->fType));
- fVars.back()[var] = pos;
- if (var->fInitialValue) {
- fStack[pos] = this->evaluate(*var->fInitialValue);
- }
- }
- break;
- case Statement::kWhile_Kind:
- abort();
- default:
- abort();
- }
-}
-
-static Interpreter::TypeKind type_kind(const Type& type) {
- if (type.fName == "int") {
- return Interpreter::kInt_TypeKind;
- } else if (type.fName == "float") {
- return Interpreter::kFloat_TypeKind;
- }
- ABORT("unsupported type: %s\n", type.description().c_str());
-}
-
-Interpreter::StackIndex Interpreter::getLValue(const Expression& expr) {
- switch (expr.fKind) {
- case Expression::kFieldAccess_Kind:
- break;
- case Expression::kIndex_Kind: {
- const IndexExpression& idx = (const IndexExpression&) expr;
- return this->evaluate(*idx.fBase).fInt + this->evaluate(*idx.fIndex).fInt;
- }
- case Expression::kSwizzle_Kind:
- break;
- case Expression::kVariableReference_Kind:
- SkASSERT(fVars.size());
- SkASSERT(fVars.back().find(&((VariableReference&) expr).fVariable) !=
- fVars.back().end());
- return fVars.back()[&((VariableReference&) expr).fVariable];
- case Expression::kTernary_Kind: {
- const TernaryExpression& t = (const TernaryExpression&) expr;
- return this->getLValue(this->evaluate(*t.fTest).fBool ? *t.fIfTrue : *t.fIfFalse);
- }
- case Expression::kTypeReference_Kind:
- break;
- default:
- break;
- }
- ABORT("unsupported lvalue");
+ return fReturnValue;
}
struct CallbackCtx : public SkRasterPipeline_CallbackCtx {
@@ -217,254 +70,421 @@
const FunctionDefinition* fFunction;
};
-static void do_callback(SkRasterPipeline_CallbackCtx* raw, int activePixels) {
- CallbackCtx& ctx = (CallbackCtx&) *raw;
- for (int i = 0; i < activePixels; ++i) {
- ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 0]));
- ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 1]));
- ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 2]));
- ctx.fInterpreter->run(*ctx.fFunction);
- ctx.read_from[i * 4 + 2] = ctx.fInterpreter->pop().fFloat;
- ctx.read_from[i * 4 + 1] = ctx.fInterpreter->pop().fFloat;
- ctx.read_from[i * 4 + 0] = ctx.fInterpreter->pop().fFloat;
+uint8_t Interpreter::read8() {
+ return fCurrentFunction->fCode[fIP++];
+}
+
+uint16_t Interpreter::read16() {
+ uint16_t result = (fCurrentFunction->fCode[fIP ] << 8) +
+ fCurrentFunction->fCode[fIP + 1];
+ fIP += 2;
+ return result;
+}
+
+uint32_t Interpreter::read32() {
+ uint32_t result = (fCurrentFunction->fCode[fIP] << 24) +
+ (fCurrentFunction->fCode[fIP + 1] << 16) +
+ (fCurrentFunction->fCode[fIP + 2] << 8) +
+ fCurrentFunction->fCode[fIP + 3];
+ fIP += 4;
+ return result;
+}
+
+void Interpreter::push(Value v) {
+ fStack.push_back(v);
+}
+
+Interpreter::Value Interpreter::pop() {
+ Value v = fStack.back();
+ fStack.pop_back();
+ return v;
+}
+
+static String value_string(uint32_t v) {
+ union { uint32_t u; float f; } pun = { v };
+ return to_string(v) + "(" + to_string(pun.f) + ")";
+}
+
+void Interpreter::disassemble(const ByteCodeFunction& f) {
+ SkASSERT(fIP == 0);
+ while (fIP < (int) f.fCode.size()) {
+ printf("%d: ", fIP);
+ switch ((ByteCodeInstruction) this->read8()) {
+ case ByteCodeInstruction::kAddF: printf("addf"); break;
+ case ByteCodeInstruction::kAddI: printf("addi"); break;
+ case ByteCodeInstruction::kAndB: printf("andb"); break;
+ case ByteCodeInstruction::kAndI: printf("andi"); break;
+ case ByteCodeInstruction::kBranch: printf("branch %d", this->read16()); break;
+ case ByteCodeInstruction::kCompareIEQ: printf("comparei eq"); break;
+ case ByteCodeInstruction::kCompareINEQ: printf("comparei neq"); break;
+ case ByteCodeInstruction::kCompareFEQ: printf("comparef eq"); break;
+ case ByteCodeInstruction::kCompareFGT: printf("comparef gt"); break;
+ case ByteCodeInstruction::kCompareFGTEQ: printf("comparef gteq"); break;
+ case ByteCodeInstruction::kCompareFLT: printf("comparef lt"); break;
+ case ByteCodeInstruction::kCompareFLTEQ: printf("comparef lteq"); break;
+ case ByteCodeInstruction::kCompareFNEQ: printf("comparef neq"); break;
+ case ByteCodeInstruction::kCompareSGT: printf("compares sgt"); break;
+ case ByteCodeInstruction::kCompareSGTEQ: printf("compares sgteq"); break;
+ case ByteCodeInstruction::kCompareSLT: printf("compares lt"); break;
+ case ByteCodeInstruction::kCompareSLTEQ: printf("compares lteq"); break;
+ case ByteCodeInstruction::kCompareUGT: printf("compareu gt"); break;
+ case ByteCodeInstruction::kCompareUGTEQ: printf("compareu gteq"); break;
+ case ByteCodeInstruction::kCompareULT: printf("compareu lt"); break;
+ case ByteCodeInstruction::kCompareULTEQ: printf("compareu lteq"); break;
+ case ByteCodeInstruction::kConditionalBranch:
+ printf("conditionalbranch %d", this->read16());
+ break;
+ case ByteCodeInstruction::kDebugPrint: printf("debugprint"); break;
+ case ByteCodeInstruction::kDivideF: printf("dividef"); break;
+ case ByteCodeInstruction::kDivideS: printf("divides"); break;
+ case ByteCodeInstruction::kDivideU: printf("divideu"); break;
+ case ByteCodeInstruction::kDup: printf("dup"); break;
+ case ByteCodeInstruction::kDupDown: printf("dupdown %d", this->read8()); break;
+ case ByteCodeInstruction::kFloatToInt: printf("floattoint"); break;
+ case ByteCodeInstruction::kLoad: printf("load"); break;
+ case ByteCodeInstruction::kLoadGlobal: printf("loadglobal"); break;
+ case ByteCodeInstruction::kLoadSwizzle: {
+ int count = this->read8();
+ printf("loadswizzle %d", count);
+ for (int i = 0; i < count; ++i) {
+ printf(", %d", this->read8());
+ }
+ break;
+ }
+ case ByteCodeInstruction::kMultiplyF: printf("multiplyf"); break;
+ case ByteCodeInstruction::kMultiplyS: printf("multiplys"); break;
+ case ByteCodeInstruction::kMultiplyU: printf("multiplyu"); break;
+ case ByteCodeInstruction::kNegateF: printf("negatef"); break;
+ case ByteCodeInstruction::kNegateS: printf("negates"); break;
+ case ByteCodeInstruction::kNot: printf("not"); break;
+ case ByteCodeInstruction::kOrB: printf("orb"); break;
+ case ByteCodeInstruction::kOrI: printf("ori"); break;
+ case ByteCodeInstruction::kParameter: printf("parameter"); break;
+ case ByteCodeInstruction::kPop: printf("pop %d", this->read8()); break;
+ case ByteCodeInstruction::kPushImmediate:
+ printf("pushimmediate %s", value_string(this->read32()).c_str());
+ break;
+ case ByteCodeInstruction::kRemainderS: printf("remainders"); break;
+ case ByteCodeInstruction::kRemainderU: printf("remainderu"); break;
+ case ByteCodeInstruction::kSignedToFloat: printf("signedtofloat"); break;
+ case ByteCodeInstruction::kStore: printf("store"); break;
+ case ByteCodeInstruction::kStoreSwizzle: {
+ int count = this->read8();
+ printf("storeswizzle %d", count);
+ for (int i = 0; i < count; ++i) {
+ printf(", %d", this->read8());
+ }
+ break;
+ }
+ case ByteCodeInstruction::kSubtractF: printf("subtractf"); break;
+ case ByteCodeInstruction::kSubtractI: printf("subtracti"); break;
+ case ByteCodeInstruction::kSwizzle: {
+ printf("swizzle %d, ", this->read8());
+ int count = this->read8();
+ printf("%d", count);
+ for (int i = 0; i < count; ++i) {
+ printf(", %d", this->read8());
+ }
+ break;
+ }
+ case ByteCodeInstruction::kUnsignedToFloat: printf("unsignedtofloat"); break;
+ case ByteCodeInstruction::kVector: printf("vector%d", this->read8()); break;
+ default: SkASSERT(false);
+ }
+ printf("\n");
}
+ fIP = 0;
}
-void Interpreter::appendStage(const AppendStage& a) {
- switch (a.fStage) {
- case SkRasterPipeline::matrix_4x5: {
- SkASSERT(a.fArguments.size() == 1);
- StackIndex transpose = evaluate(*a.fArguments[0]).fInt;
- fPipeline.append(SkRasterPipeline::matrix_4x5, &fStack[transpose]);
- break;
- }
- case SkRasterPipeline::callback: {
- SkASSERT(a.fArguments.size() == 1);
- CallbackCtx* ctx = new CallbackCtx();
- ctx->fInterpreter = this;
- ctx->fn = do_callback;
- for (const auto& e : *fProgram) {
- if (ProgramElement::kFunction_Kind == e.fKind) {
- const FunctionDefinition& f = (const FunctionDefinition&) e;
- if (&f.fDeclaration ==
- ((const FunctionReference&) *a.fArguments[0]).fFunctions[0]) {
- ctx->fFunction = &f;
- }
- }
- }
- fPipeline.append(SkRasterPipeline::callback, ctx);
- break;
- }
- default:
- fPipeline.append(a.fStage);
+void Interpreter::dumpStack() {
+ printf("STACK:");
+ for (size_t i = 0; i < fStack.size(); ++i) {
+ printf(" %d(%f)", fStack[i].fSigned, fStack[i].fFloat);
}
+ printf("\n");
}
-Interpreter::Value Interpreter::call(const FunctionCall& c) {
- abort();
-}
+#define BINARY_OP(inst, type, field, op) \
+ case ByteCodeInstruction::inst: { \
+ type b = this->pop().field; \
+ type a = this->pop().field; \
+ this->push(Value(a op b)); \
+ break; \
+ }
-Interpreter::Value Interpreter::evaluate(const Expression& expr) {
- switch (expr.fKind) {
- case Expression::kAppendStage_Kind:
- this->appendStage((const AppendStage&) expr);
- return Value((int) 0xDEADBEEF);
- case Expression::kBinary_Kind: {
- #define ARITHMETIC(op) { \
- Value left = this->evaluate(*b.fLeft); \
- Value right = this->evaluate(*b.fRight); \
- switch (type_kind(b.fLeft->fType)) { \
- case kFloat_TypeKind: \
- return Value(left.fFloat op right.fFloat); \
- case kInt_TypeKind: \
- return Value(left.fInt op right.fInt); \
- default: \
- abort(); \
- } \
- }
- #define BITWISE(op) { \
- Value left = this->evaluate(*b.fLeft); \
- Value right = this->evaluate(*b.fRight); \
- switch (type_kind(b.fLeft->fType)) { \
- case kInt_TypeKind: \
- return Value(left.fInt op right.fInt); \
- default: \
- abort(); \
- } \
- }
- #define LOGIC(op) { \
- Value left = this->evaluate(*b.fLeft); \
- Value right = this->evaluate(*b.fRight); \
- switch (type_kind(b.fLeft->fType)) { \
- case kFloat_TypeKind: \
- return Value(left.fFloat op right.fFloat); \
- case kInt_TypeKind: \
- return Value(left.fInt op right.fInt); \
- default: \
- abort(); \
- } \
- }
- #define COMPOUND_ARITHMETIC(op) { \
- StackIndex left = this->getLValue(*b.fLeft); \
- Value right = this->evaluate(*b.fRight); \
- Value result = fStack[left]; \
- switch (type_kind(b.fLeft->fType)) { \
- case kFloat_TypeKind: \
- result.fFloat op right.fFloat; \
- break; \
- case kInt_TypeKind: \
- result.fInt op right.fInt; \
- break; \
- default: \
- abort(); \
- } \
- fStack[left] = result; \
- return result; \
- }
- #define COMPOUND_BITWISE(op) { \
- StackIndex left = this->getLValue(*b.fLeft); \
- Value right = this->evaluate(*b.fRight); \
- Value result = fStack[left]; \
- switch (type_kind(b.fLeft->fType)) { \
- case kInt_TypeKind: \
- result.fInt op right.fInt; \
- break; \
- default: \
- abort(); \
- } \
- fStack[left] = result; \
- return result; \
- }
- const BinaryExpression& b = (const BinaryExpression&) expr;
- switch (b.fOperator) {
- case Token::PLUS: ARITHMETIC(+)
- case Token::MINUS: ARITHMETIC(-)
- case Token::STAR: ARITHMETIC(*)
- case Token::SLASH: ARITHMETIC(/)
- case Token::BITWISEAND: BITWISE(&)
- case Token::BITWISEOR: BITWISE(|)
- case Token::BITWISEXOR: BITWISE(^)
- case Token::LT: LOGIC(<)
- case Token::GT: LOGIC(>)
- case Token::LTEQ: LOGIC(<=)
- case Token::GTEQ: LOGIC(>=)
- case Token::LOGICALAND: {
- Value result = this->evaluate(*b.fLeft);
- if (result.fBool) {
- result = this->evaluate(*b.fRight);
- }
- return result;
- }
- case Token::LOGICALOR: {
- Value result = this->evaluate(*b.fLeft);
- if (!result.fBool) {
- result = this->evaluate(*b.fRight);
- }
- return result;
- }
- case Token::EQ: {
- StackIndex left = this->getLValue(*b.fLeft);
- Value right = this->evaluate(*b.fRight);
- fStack[left] = right;
- return right;
- }
- case Token::PLUSEQ: COMPOUND_ARITHMETIC(+=)
- case Token::MINUSEQ: COMPOUND_ARITHMETIC(-=)
- case Token::STAREQ: COMPOUND_ARITHMETIC(*=)
- case Token::SLASHEQ: COMPOUND_ARITHMETIC(/=)
- case Token::BITWISEANDEQ: COMPOUND_BITWISE(&=)
- case Token::BITWISEOREQ: COMPOUND_BITWISE(|=)
- case Token::BITWISEXOREQ: COMPOUND_BITWISE(^=)
- default:
- ABORT("unsupported operator: %s\n", expr.description().c_str());
+void Interpreter::next() {
+#ifdef TRACE
+ printf("at %d\n", fIP);
+#endif
+ ByteCodeInstruction inst = (ByteCodeInstruction) this->read8();
+ switch (inst) {
+ BINARY_OP(kAddI, int32_t, fSigned, +)
+ BINARY_OP(kAddF, float, fFloat, +)
+ case ByteCodeInstruction::kBranch:
+ fIP = this->read16();
+ break;
+ BINARY_OP(kCompareIEQ, int32_t, fSigned, ==)
+ BINARY_OP(kCompareFEQ, float, fFloat, ==)
+ BINARY_OP(kCompareINEQ, int32_t, fSigned, !=)
+ BINARY_OP(kCompareFNEQ, float, fFloat, !=)
+ BINARY_OP(kCompareSGT, int32_t, fSigned, >)
+ BINARY_OP(kCompareUGT, uint32_t, fUnsigned, >)
+ BINARY_OP(kCompareFGT, float, fFloat, >)
+ BINARY_OP(kCompareSGTEQ, int32_t, fSigned, >=)
+ BINARY_OP(kCompareUGTEQ, uint32_t, fUnsigned, >=)
+ BINARY_OP(kCompareFGTEQ, float, fFloat, >=)
+ BINARY_OP(kCompareSLT, int32_t, fSigned, <)
+ BINARY_OP(kCompareULT, uint32_t, fUnsigned, <)
+ BINARY_OP(kCompareFLT, float, fFloat, <)
+ BINARY_OP(kCompareSLTEQ, int32_t, fSigned, <=)
+ BINARY_OP(kCompareULTEQ, uint32_t, fUnsigned, <=)
+ BINARY_OP(kCompareFLTEQ, float, fFloat, <=)
+ case ByteCodeInstruction::kConditionalBranch: {
+ int target = this->read16();
+ if (this->pop().fBool) {
+ fIP = target;
}
break;
}
- case Expression::kBoolLiteral_Kind:
- return Value(((const BoolLiteral&) expr).fValue);
- case Expression::kConstructor_Kind:
+ case ByteCodeInstruction::kDebugPrint: {
+ Value v = this->pop();
+ printf("Debug: %d(int), %d(uint), %f(float)\n", v.fSigned, v.fUnsigned, v.fFloat);
break;
- case Expression::kIntLiteral_Kind:
- return Value((int) ((const IntLiteral&) expr).fValue);
- case Expression::kFieldAccess_Kind:
- break;
- case Expression::kFloatLiteral_Kind:
- return Value((float) ((const FloatLiteral&) expr).fValue);
- case Expression::kFunctionCall_Kind:
- return this->call((const FunctionCall&) expr);
- case Expression::kIndex_Kind: {
- const IndexExpression& idx = (const IndexExpression&) expr;
- StackIndex pos = this->evaluate(*idx.fBase).fInt +
- this->evaluate(*idx.fIndex).fInt;
- return fStack[pos];
}
- case Expression::kPrefix_Kind: {
- const PrefixExpression& p = (const PrefixExpression&) expr;
- switch (p.fOperator) {
- case Token::MINUS: {
- Value base = this->evaluate(*p.fOperand);
- switch (type_kind(p.fType)) {
- case kFloat_TypeKind:
- return Value(-base.fFloat);
- case kInt_TypeKind:
- return Value(-base.fInt);
- default:
- abort();
- }
- }
- case Token::LOGICALNOT: {
- Value base = this->evaluate(*p.fOperand);
- return Value(!base.fBool);
- }
- default:
- abort();
+ BINARY_OP(kDivideS, int32_t, fSigned, /)
+ BINARY_OP(kDivideU, uint32_t, fUnsigned, /)
+ BINARY_OP(kDivideF, float, fFloat, /)
+ case ByteCodeInstruction::kDup:
+ this->push(fStack.back());
+ break;
+ case ByteCodeInstruction::kDupDown: {
+ int count = this->read8();
+ for (int i = 0; i < count; ++i) {
+ fStack.insert(fStack.end() - i - count - 1, fStack[fStack.size() - i - 1]);
}
+ break;
}
- case Expression::kPostfix_Kind: {
- const PostfixExpression& p = (const PostfixExpression&) expr;
- StackIndex lvalue = this->getLValue(*p.fOperand);
- Value result = fStack[lvalue];
- switch (type_kind(p.fType)) {
- case kFloat_TypeKind:
- if (Token::PLUSPLUS == p.fOperator) {
- ++fStack[lvalue].fFloat;
- } else {
- SkASSERT(Token::MINUSMINUS == p.fOperator);
- --fStack[lvalue].fFloat;
- }
- break;
- case kInt_TypeKind:
- if (Token::PLUSPLUS == p.fOperator) {
- ++fStack[lvalue].fInt;
- } else {
- SkASSERT(Token::MINUSMINUS == p.fOperator);
- --fStack[lvalue].fInt;
- }
- break;
- default:
- abort();
+ case ByteCodeInstruction::kFloatToInt: {
+ Value& top = fStack.back();
+ top.fSigned = (int) top.fFloat;
+ break;
+ }
+ case ByteCodeInstruction::kSignedToFloat: {
+ Value& top = fStack.back();
+ top.fFloat = (float) top.fSigned;
+ break;
+ }
+ case ByteCodeInstruction::kUnsignedToFloat: {
+ Value& top = fStack.back();
+ top.fFloat = (float) top.fUnsigned;
+ break;
+ }
+ case ByteCodeInstruction::kLoad: {
+ int target = this->pop().fSigned;
+ SkASSERT(target < (int) fStack.size());
+ this->push(fStack[target]);
+ break;
+ }
+ case ByteCodeInstruction::kLoadGlobal: {
+ int target = this->read8();
+ SkASSERT(target < (int) fGlobals.size());
+ this->push(fGlobals[target]);
+ break;
+ }
+ case ByteCodeInstruction::kLoadSwizzle: {
+ Value target = this->pop();
+ int count = read8();
+ for (int i = 0; i < count; ++i) {
+ SkASSERT(target.fSigned + fCurrentFunction->fCode[fIP + i] < (int) fStack.size());
+ this->push(fStack[target.fSigned + fCurrentFunction->fCode[fIP + i]]);
}
- return result;
- }
- case Expression::kSetting_Kind:
+ fIP += count;
break;
- case Expression::kSwizzle_Kind:
- break;
- case Expression::kVariableReference_Kind:
- SkASSERT(fVars.size());
- SkASSERT(fVars.back().find(&((VariableReference&) expr).fVariable) !=
- fVars.back().end());
- return fStack[fVars.back()[&((VariableReference&) expr).fVariable]];
- case Expression::kTernary_Kind: {
- const TernaryExpression& t = (const TernaryExpression&) expr;
- return this->evaluate(this->evaluate(*t.fTest).fBool ? *t.fIfTrue : *t.fIfFalse);
}
- case Expression::kTypeReference_Kind:
+ BINARY_OP(kMultiplyS, int32_t, fSigned, *)
+ BINARY_OP(kMultiplyU, uint32_t, fUnsigned, *)
+ BINARY_OP(kMultiplyF, float, fFloat, *)
+ case ByteCodeInstruction::kNot: {
+ Value& top = fStack.back();
+ top.fBool = !top.fBool;
+ break;
+ }
+ case ByteCodeInstruction::kNegateF:
+ this->push(-this->pop().fFloat);
+ case ByteCodeInstruction::kNegateS:
+ this->push(-this->pop().fSigned);
+ case ByteCodeInstruction::kPop:
+ for (int i = read8(); i > 0; --i) {
+ this->pop();
+ }
+ break;
+ case ByteCodeInstruction::kPushImmediate:
+ this->push(Value((int) read32()));
+ break;
+ BINARY_OP(kRemainderS, int32_t, fSigned, %)
+ BINARY_OP(kRemainderU, uint32_t, fUnsigned, %)
+ case ByteCodeInstruction::kStore: {
+ Value value = this->pop();
+ int target = this->pop().fSigned;
+ SkASSERT(target < (int) fStack.size());
+ fStack[target] = value;
+ break;
+ }
+ case ByteCodeInstruction::kStoreGlobal: {
+ Value value = this->pop();
+ int target = this->pop().fSigned;
+ SkASSERT(target < (int) fGlobals.size());
+ fGlobals[target] = value;
+ break;
+ }
+ case ByteCodeInstruction::kStoreSwizzle: {
+ int count = read8();
+ int target = fStack[fStack.size() - count - 1].fSigned;
+ for (int i = count - 1; i >= 0; --i) {
+ SkASSERT(target + fCurrentFunction->fCode[fIP + i] < (int) fStack.size());
+ fStack[target + fCurrentFunction->fCode[fIP + i]] = this->pop();
+ }
+ this->pop();
+ fIP += count;
+ break;
+ }
+ BINARY_OP(kSubtractI, int32_t, fSigned, -)
+ BINARY_OP(kSubtractF, float, fFloat, -)
+ case ByteCodeInstruction::kSwizzle: {
+ Value vec[4];
+ for (int i = this->read8() - 1; i >= 0; --i) {
+ vec[i] = this->pop();
+ }
+ for (int i = this->read8() - 1; i >= 0; --i) {
+ this->push(vec[this->read8()]);
+ }
+ break;
+ }
+ case ByteCodeInstruction::kVector:
+ this->nextVector(this->read8());
break;
default:
- break;
+ printf("unsupported instruction %d\n", (int) inst);
+ SkASSERT(false);
}
- ABORT("unsupported expression: %s\n", expr.description().c_str());
+#ifdef TRACE
+ this->dumpStack();
+#endif
+}
+
+static constexpr int VECTOR_MAX = 16;
+
+#define VECTOR_BINARY_OP(inst, type, field, op) \
+ case ByteCodeInstruction::inst: { \
+ Value result[VECTOR_MAX]; \
+ for (int i = count - 1; i >= 0; --i) { \
+ result[i] = this->pop(); \
+ } \
+ for (int i = count - 1; i >= 0; --i) { \
+ result[i] = this->pop().field op result[i].field; \
+ } \
+ for (int i = 0; i < count; ++i) { \
+ this->push(result[i]); \
+ } \
+ break; \
+ }
+
+void Interpreter::nextVector(int count) {
+ ByteCodeInstruction inst = (ByteCodeInstruction) this->read8();
+ switch (inst) {
+ VECTOR_BINARY_OP(kAddI, int32_t, fSigned, +)
+ VECTOR_BINARY_OP(kAddF, float, fFloat, +)
+ case ByteCodeInstruction::kBranch:
+ fIP = this->read16();
+ break;
+ VECTOR_BINARY_OP(kCompareIEQ, int32_t, fSigned, ==)
+ VECTOR_BINARY_OP(kCompareFEQ, float, fFloat, ==)
+ VECTOR_BINARY_OP(kCompareINEQ, int32_t, fSigned, !=)
+ VECTOR_BINARY_OP(kCompareFNEQ, float, fFloat, !=)
+ VECTOR_BINARY_OP(kCompareSGT, int32_t, fSigned, >)
+ VECTOR_BINARY_OP(kCompareUGT, uint32_t, fUnsigned, >)
+ VECTOR_BINARY_OP(kCompareFGT, float, fFloat, >)
+ VECTOR_BINARY_OP(kCompareSGTEQ, int32_t, fSigned, >=)
+ VECTOR_BINARY_OP(kCompareUGTEQ, uint32_t, fUnsigned, >=)
+ VECTOR_BINARY_OP(kCompareFGTEQ, float, fFloat, >=)
+ VECTOR_BINARY_OP(kCompareSLT, int32_t, fSigned, <)
+ VECTOR_BINARY_OP(kCompareULT, uint32_t, fUnsigned, <)
+ VECTOR_BINARY_OP(kCompareFLT, float, fFloat, <)
+ VECTOR_BINARY_OP(kCompareSLTEQ, int32_t, fSigned, <=)
+ VECTOR_BINARY_OP(kCompareULTEQ, uint32_t, fUnsigned, <=)
+ VECTOR_BINARY_OP(kCompareFLTEQ, float, fFloat, <=)
+ case ByteCodeInstruction::kConditionalBranch: {
+ int target = this->read16();
+ if (this->pop().fBool) {
+ fIP = target;
+ }
+ break;
+ }
+ VECTOR_BINARY_OP(kDivideS, int32_t, fSigned, /)
+ VECTOR_BINARY_OP(kDivideU, uint32_t, fUnsigned, /)
+ VECTOR_BINARY_OP(kDivideF, float, fFloat, /)
+ case ByteCodeInstruction::kFloatToInt: {
+ for (int i = 0; i < count; ++i) {
+ Value& v = fStack[fStack.size() - i - 1];
+ v.fSigned = (int) v.fFloat;
+ }
+ break;
+ }
+ case ByteCodeInstruction::kSignedToFloat: {
+ for (int i = 0; i < count; ++i) {
+ Value& v = fStack[fStack.size() - i - 1];
+ v.fFloat = (float) v.fSigned;
+ }
+ break;
+ }
+ case ByteCodeInstruction::kUnsignedToFloat: {
+ for (int i = 0; i < count; ++i) {
+ Value& v = fStack[fStack.size() - i - 1];
+ v.fFloat = (float) v.fUnsigned;
+ }
+ break;
+ }
+ case ByteCodeInstruction::kLoad: {
+ int target = this->pop().fSigned;
+ for (int i = 0; i < count; ++i) {
+ SkASSERT(target < (int) fStack.size());
+ this->push(fStack[target++]);
+ }
+ break;
+ }
+ case ByteCodeInstruction::kLoadGlobal: {
+ int target = this->read8();
+ SkASSERT(target < (int) fGlobals.size());
+ this->push(fGlobals[target]);
+ break;
+ }
+ VECTOR_BINARY_OP(kMultiplyS, int32_t, fSigned, *)
+ VECTOR_BINARY_OP(kMultiplyU, uint32_t, fUnsigned, *)
+ VECTOR_BINARY_OP(kMultiplyF, float, fFloat, *)
+ VECTOR_BINARY_OP(kRemainderS, int32_t, fSigned, %)
+ VECTOR_BINARY_OP(kRemainderU, uint32_t, fUnsigned, %)
+ case ByteCodeInstruction::kStore: {
+ int target = fStack[fStack.size() - count - 1].fSigned + count;
+ for (int i = count - 1; i >= 0; --i) {
+ SkASSERT(target < (int) fStack.size());
+ fStack[--target] = this->pop();
+ }
+ break;
+ }
+ VECTOR_BINARY_OP(kSubtractI, int32_t, fSigned, -)
+ VECTOR_BINARY_OP(kSubtractF, float, fFloat, -)
+ case ByteCodeInstruction::kVector:
+ this->nextVector(this->read8());
+ default:
+ printf("unsupported instruction %d\n", (int) inst);
+ SkASSERT(false);
+ }
+}
+
+void Interpreter::run() {
+ while (fIP < (int) fCurrentFunction->fCode.size()) {
+ next();
+ }
}
} // namespace
diff --git a/src/sksl/SkSLInterpreter.h b/src/sksl/SkSLInterpreter.h
index 449e380..018768a 100644
--- a/src/sksl/SkSLInterpreter.h
+++ b/src/sksl/SkSLInterpreter.h
@@ -8,6 +8,7 @@
#ifndef SKSL_INTERPRETER
#define SKSL_INTERPRETER
+#include "SkSLByteCode.h"
#include "ir/SkSLAppendStage.h"
#include "ir/SkSLExpression.h"
#include "ir/SkSLFunctionCall.h"
@@ -17,31 +18,30 @@
#include <stack>
-class SkRasterPipeline;
-
namespace SkSL {
class Interpreter {
typedef int StackIndex;
- struct StatementIndex {
- const Statement* fStatement;
- size_t fIndex;
- };
-
public:
union Value {
+ Value() {}
+
Value(float f)
: fFloat(f) {}
- Value(int i)
- : fInt(i) {}
+ Value(int32_t s)
+ : fSigned(s) {}
+
+ Value(uint32_t u)
+ : fUnsigned(u) {}
Value(bool b)
: fBool(b) {}
float fFloat;
- int fInt;
+ int32_t fSigned;
+ uint32_t fUnsigned;
bool fBool;
};
@@ -51,37 +51,49 @@
kBool_TypeKind
};
- Interpreter(std::unique_ptr<Program> program, SkRasterPipeline* pipeline, std::vector<Value>* stack)
+ Interpreter(std::unique_ptr<Program> program, std::unique_ptr<ByteCode> byteCode)
: fProgram(std::move(program))
- , fPipeline(*pipeline)
- , fStack(*stack) {}
+ , fByteCode(std::move(byteCode))
+ , fReturnValue(0) {}
+
+ /**
+ * Invokes the specified function with the given arguments, returning its return value. 'out'
+ * and 'inout' parameters will result in the 'args' array being modified.
+ */
+ Value run(const ByteCodeFunction& f, Value args[], Value inputs[]);
+
+private:
+ StackIndex stackAlloc(int count);
+
+ uint8_t read8();
+
+ uint16_t read16();
+
+ uint32_t read32();
+
+ void next();
+
+ void nextVector(int count);
void run();
- void run(const FunctionDefinition& f);
-
- void push(Value value);
+ void push(Value v);
Value pop();
- StackIndex stackAlloc(int count);
+ void swizzle();
- void runStatement();
+ void disassemble(const ByteCodeFunction& f);
- StackIndex getLValue(const Expression& expr);
+ void dumpStack();
- Value call(const FunctionCall& c);
-
- void appendStage(const AppendStage& c);
-
- Value evaluate(const Expression& expr);
-
-private:
std::unique_ptr<Program> fProgram;
- SkRasterPipeline& fPipeline;
- std::vector<StatementIndex> fCurrentIndex;
- std::vector<std::unordered_map<const Variable*, StackIndex>> fVars;
- std::vector<Value> &fStack;
+ std::unique_ptr<ByteCode> fByteCode;
+ int fIP;
+ const ByteCodeFunction* fCurrentFunction;
+ std::vector<Value> fGlobals;
+ std::vector<Value> fStack;
+ Value fReturnValue;
};
} // namespace
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 27787bb..7500ea0 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -1915,28 +1915,6 @@
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::foldToBool(SpvId id, const Type& operandType, SpvOp op,
OutputStream& out) {
if (operandType.kind() == Type::kVector_Kind) {
diff --git a/src/sksl/SkSLUtil.cpp b/src/sksl/SkSLUtil.cpp
index 49d37e3..4684df4 100644
--- a/src/sksl/SkSLUtil.cpp
+++ b/src/sksl/SkSLUtil.cpp
@@ -30,4 +30,45 @@
out.write(s.str().c_str(), s.str().size());
}
+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;
+ }
+}
+
+Token::Kind remove_assignment(Token::Kind op) {
+ switch (op) {
+ case Token::PLUSEQ: return Token::PLUS;
+ case Token::MINUSEQ: return Token::MINUS;
+ case Token::STAREQ: return Token::STAR;
+ case Token::SLASHEQ: return Token::SLASH;
+ case Token::PERCENTEQ: return Token::PERCENT;
+ case Token::SHLEQ: return Token::SHL;
+ case Token::SHREQ: return Token::SHR;
+ case Token::BITWISEOREQ: return Token::BITWISEOR;
+ case Token::BITWISEXOREQ: return Token::BITWISEXOR;
+ case Token::BITWISEANDEQ: return Token::BITWISEAND;
+ case Token::LOGICALOREQ: return Token::LOGICALOR;
+ case Token::LOGICALXOREQ: return Token::LOGICALXOR;
+ case Token::LOGICALANDEQ: return Token::LOGICALAND;
+ default: return Token::INVALID;
+ }
+}
+
} // namespace
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index ca00c9b..d221f4e 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -12,6 +12,7 @@
#include <memory>
#include "stdlib.h"
#include "string.h"
+#include "SkSLLexer.h"
#include "SkSLDefines.h"
#include "SkSLString.h"
#include "SkSLStringStream.h"
@@ -396,6 +397,13 @@
void write_stringstream(const StringStream& d, OutputStream& out);
+// Returns true if op is '=' or any compound assignment operator ('+=', '-=', etc.)
+bool is_assignment(Token::Kind op);
+
+// Given a compound assignment operator, returns the non-assignment version of the operator (e.g.
+// '+=' becomes '+')
+Token::Kind remove_assignment(Token::Kind op);
+
NORETURN void sksl_abort();
} // namespace
diff --git a/tests/SkSLInterpreterTest.cpp b/tests/SkSLInterpreterTest.cpp
new file mode 100644
index 0000000..10d2f32
--- /dev/null
+++ b/tests/SkSLInterpreterTest.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkSLCompiler.h"
+#include "SkSLInterpreter.h"
+
+#include "Test.h"
+
+void test(skiatest::Reporter* r, const char* src, float inR, float inG, float inB, float inA,
+ float expectedR, float expectedG, float expectedB, float expectedA) {
+ SkSL::Compiler compiler;
+ SkSL::Program::Settings settings;
+ std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
+ SkSL::Program::kPipelineStage_Kind,
+ SkSL::String(src), settings);
+ REPORTER_ASSERT(r, program);
+ if (program) {
+ std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
+ REPORTER_ASSERT(r, !compiler.errorCount());
+ if (compiler.errorCount() > 0) {
+ printf("%s\n%s", src, compiler.errorText().c_str());
+ return;
+ }
+ SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
+ SkSL::Interpreter interpreter(std::move(program), std::move(byteCode));
+ float inoutColor[4] = { inR, inG, inB, inA };
+ interpreter.run(*main, (SkSL::Interpreter::Value*) inoutColor, nullptr);
+ if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
+ inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
+ printf("for program: %s\n", src);
+ printf(" expected (%f, %f, %f, %f), but received (%f, %f, %f, %f)\n", expectedR,
+ expectedG, expectedB, expectedA, inoutColor[0], inoutColor[1], inoutColor[2],
+ inoutColor[3]);
+ }
+ REPORTER_ASSERT(r, inoutColor[0] == expectedR);
+ REPORTER_ASSERT(r, inoutColor[1] == expectedG);
+ REPORTER_ASSERT(r, inoutColor[2] == expectedB);
+ REPORTER_ASSERT(r, inoutColor[3] == expectedA);
+ } else {
+ printf("%s\n%s", src, compiler.errorText().c_str());
+ }
+}
+
+DEF_TEST(SkSLInterpreterTEMP_TEST, r) {
+ test(r, "void main(inout half4 color) { half4 c = color; color += c; }", 0.25, 0.5, 0.75, 1,
+ 0.5, 1, 1.5, 2);
+}
+
+DEF_TEST(SkSLInterpreterAdd, r) {
+ test(r, "void main(inout half4 color) { color.r = color.r + color.g; }", 0.25, 0.75, 0, 0, 1,
+ 0.75, 0, 0);
+ test(r, "void main(inout half4 color) { color += half4(1, 2, 3, 4); }", 4, 3, 2, 1, 5, 5, 5, 5);
+ test(r, "void main(inout half4 color) { half4 c = color; color += c; }", 0.25, 0.5, 0.75, 1,
+ 0.5, 1, 1.5, 2);
+ test(r, "void main(inout half4 color) { int a = 1; int b = 3; color.r = a + b; }", 1, 2, 3, 4,
+ 4, 2, 3, 4);
+}
+
+DEF_TEST(SkSLInterpreterSubtract, r) {
+ test(r, "void main(inout half4 color) { color.r = color.r - color.g; }", 1, 0.75, 0, 0, 0.25,
+ 0.75, 0, 0);
+ test(r, "void main(inout half4 color) { color -= half4(1, 2, 3, 4); }", 5, 5, 5, 5, 4, 3, 2, 1);
+ test(r, "void main(inout half4 color) { half4 c = color; color -= c; }", 4, 3, 2, 1,
+ 0, 0, 0, 0);
+ test(r, "void main(inout half4 color) { int a = 3; int b = 1; color.r = a - b; }", 0, 0, 0, 0,
+ 2, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterMultiply, r) {
+ test(r, "void main(inout half4 color) { color.r = color.r * color.g; }", 2, 3, 0, 0, 6, 3, 0,
+ 0);
+ test(r, "void main(inout half4 color) { color *= half4(1, 2, 3, 4); }", 2, 3, 4, 5, 2, 6, 12,
+ 20);
+ test(r, "void main(inout half4 color) { half4 c = color; color *= c; }", 4, 3, 2, 1,
+ 16, 9, 4, 1);
+ test(r, "void main(inout half4 color) { int a = 3; int b = -2; color.r = a * b; }", 0, 0, 0, 0,
+ -6, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterDivide, r) {
+ test(r, "void main(inout half4 color) { color.r = color.r / color.g; }", 1, 2, 0, 0, 0.5, 2, 0,
+ 0);
+ test(r, "void main(inout half4 color) { color /= half4(1, 2, 3, 4); }", 12, 12, 12, 12, 12, 6,
+ 4, 3);
+ test(r, "void main(inout half4 color) { half4 c = color; color /= c; }", 4, 3, 2, 1,
+ 1, 1, 1, 1);
+ test(r, "void main(inout half4 color) { int a = 8; int b = -2; color.r = a / b; }", 0, 0, 0, 0,
+ -4, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterRemainder, r) {
+ test(r, "void main(inout half4 color) { int a = 8; int b = 3; a %= b; color.r = a; }", 0, 0, 0,
+ 0, 2, 0, 0, 0);
+ test(r, "void main(inout half4 color) { int a = 8; int b = 3; color.r = a % b; }", 0, 0, 0, 0,
+ 2, 0, 0, 0);
+ test(r, "void main(inout half4 color) { int2 a = int2(8, 10); a %= 6; color.rg = a; }", 0, 0, 0,
+ 0, 2, 4, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterIf, r) {
+ test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 3, 0, 0,
+ 5, 3, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 5, 0, 0,
+ 5, 5, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 6, 0, 0,
+ 5, 6, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 3, 5, 0, 0,
+ 3, 5, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 5, 5, 0, 0,
+ 5, 5, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 6, 5, 0, 0,
+ 6, 5, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 3, 0, 0,
+ 5, 3, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 5, 0, 0,
+ 5, 5, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 6, 0, 0,
+ 5, 6, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 3, 5, 0, 0,
+ 3, 5, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 5, 5, 0, 0,
+ 5, 5, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 6, 5, 0, 0,
+ 6, 5, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, 2, 0, 0,
+ 2, 2, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, -2, 0, 0,
+ 2, -2, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, 2, 0, 0,
+ 2, 2, 0, 0);
+ test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, -2, 0, 0,
+ 2, -2, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
+ "color.a = 2; }", 1, 1, 0, 0, 1, 1, 0, 1);
+ test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
+ "color.a = 2; }", 2, -2, 0, 0, 2, -2, 0, 2);
+}
+
+DEF_TEST(SkSLInterpreterWhile, r) {
+ test(r, "void main(inout half4 color) { while (color.r < 1) color.r += 0.25; }", 0, 0, 0, 0, 1,
+ 0, 0, 0);
+ test(r, "void main(inout half4 color) { while (color.r > 1) color.r += 0.25; }", 0, 0, 0, 0, 0,
+ 0, 0, 0);
+ test(r, "void main(inout half4 color) { while (true) { color.r += 0.5; "
+ "if (color.r > 1) break; } }", 0, 0, 0, 0, 1.5, 0, 0, 0);
+ test(r, "void main(inout half4 color) { while (color.r < 10) { color.r += 0.5; "
+ "if (color.r < 5) continue; break; } }", 0, 0, 0, 0, 5, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterDo, r) {
+ test(r, "void main(inout half4 color) { do color.r += 0.25; while (color.r < 1); }", 0, 0, 0, 0,
+ 1, 0, 0, 0);
+ test(r, "void main(inout half4 color) { do color.r += 0.25; while (color.r > 1); }", 0, 0, 0, 0,
+ 0.25, 0, 0, 0);
+ test(r, "void main(inout half4 color) { do { color.r += 0.5; if (color.r > 1) break; } while "
+ "(true); }", 0, 0, 0, 0, 1.5, 0, 0, 0);
+ test(r, "void main(inout half4 color) {do { color.r += 0.5; if (color.r < 5) "
+ "continue; if (color.r >= 5) break; } while (true); }", 0, 0, 0, 0, 5, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterFor, r) {
+ test(r, "void main(inout half4 color) { for (int i = 1; i <= 10; ++i) color.r += i; }", 0, 0, 0,
+ 0, 55, 0, 0, 0);
+ test(r,
+ "void main(inout half4 color) {"
+ " for (int i = 1; i <= 10; ++i)"
+ " for (int j = i; j <= 10; ++j)"
+ " color.r += j;"
+ "}",
+ 0, 0, 0, 0,
+ 385, 0, 0, 0);
+ test(r,
+ "void main(inout half4 color) {"
+ " for (int i = 1; i <= 10; ++i)"
+ " for (int j = 1; ; ++j) {"
+ " if (i == j) continue;"
+ " if (j > 10) break;"
+ " color.r += j;"
+ " }"
+ "}",
+ 0, 0, 0, 0,
+ 495, 0, 0, 0);
+}
+
+DEF_TEST(SkSLInterpreterSwizzle, r) {
+ test(r, "void main(inout half4 color) { color = color.abgr; }", 1, 2, 3, 4, 4, 3, 2, 1);
+ test(r, "void main(inout half4 color) { color.rgb = half4(5, 6, 7, 8).bbg; }", 1, 2, 3, 4, 7, 7,
+ 6, 4);
+ test(r, "void main(inout half4 color) { color.bgr = int3(5, 6, 7); }", 1, 2, 3, 4, 7, 6,
+ 5, 4);
+}
+
+DEF_TEST(SkSLInterpreterGlobal, r) {
+ test(r, "int x; void main(inout half4 color) { x = 10; color.b = x; }", 1, 2, 3, 4, 1, 2, 10,
+ 4);
+}