initial SkSLJIT checkin

Docs-Preview: https://skia.org/?cl=112204
Bug: skia:
Change-Id: I10042a0200db00bd8ff8078467c409b1cf191f50
Reviewed-on: https://skia-review.googlesource.com/112204
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
This commit is contained in:
Ethan Nicholas 2018-03-27 14:10:52 -04:00 committed by Skia Commit-Bot
parent 3560b58de3
commit 26a9aad63b
22 changed files with 3021 additions and 41 deletions

View File

@ -50,6 +50,9 @@ declare_args() {
skia_skqp_enable_driver_correctness_workarounds = false
skia_skqp_global_error_tolerance = 0
skia_llvm_path = ""
skia_llvm_lib = "LLVM"
}
declare_args() {
skia_use_dng_sdk = !is_fuchsia && skia_use_libjpeg_turbo && skia_use_zlib
@ -171,6 +174,9 @@ config("skia_private") {
"src/xml",
"third_party/gif",
]
if (skia_llvm_path != "") {
include_dirs += [ "$skia_llvm_path/include" ]
}
defines = [ "SK_GAMMA_APPLY_TO_A8" ]
if (is_android) {
@ -200,6 +206,12 @@ config("skia_private") {
if (!is_official_build) {
defines += [ "GR_TEST_UTILS=1" ]
}
if (skia_llvm_path != "") {
defines += [ "SK_LLVM_AVAILABLE" ]
include_dirs += [ "$skia_llvm_path/include" ]
libs += [ skia_llvm_lib ]
lib_dirs += [ "$skia_llvm_path/lib/" ]
}
}
# Any code that's linked into Skia-the-library should use this config via += skia_library_configs.

View File

@ -12,7 +12,9 @@ skia_sksl_sources = [
"$_src/sksl/SkSLCPPCodeGenerator.cpp",
"$_src/sksl/SkSLGLSLCodeGenerator.cpp",
"$_src/sksl/SkSLHCodeGenerator.cpp",
"$_src/sksl/SkSLInterpreter.cpp",
"$_src/sksl/SkSLIRGenerator.cpp",
"$_src/sksl/SkSLJIT.cpp",
"$_src/sksl/SkSLLexer.cpp",
"$_src/sksl/SkSLLayoutLexer.cpp",
"$_src/sksl/SkSLMetalCodeGenerator.cpp",

View File

@ -229,6 +229,7 @@ tests_sources = [
"$_tests/SkSLErrorTest.cpp",
"$_tests/SkSLFPTest.cpp",
"$_tests/SkSLGLSLTest.cpp",
"$_tests/SkSLJITTest.cpp",
"$_tests/SkSLMemoryLayoutTest.cpp",
"$_tests/SkSLSPIRVTest.cpp",
"$_tests/SortTest.cpp",

View File

@ -25,7 +25,12 @@ void SkRasterPipeline::append(StockStage stage, void* ctx) {
this->unchecked_append(stage, ctx);
}
void SkRasterPipeline::unchecked_append(StockStage stage, void* ctx) {
fStages = fAlloc->make<StageList>( StageList{fStages, stage, ctx} );
fStages = fAlloc->make<StageList>( StageList{fStages, (uint64_t) stage, ctx, false} );
fNumStages += 1;
fSlotsNeeded += ctx ? 2 : 1;
}
void SkRasterPipeline::append(void* fn, void* ctx) {
fStages = fAlloc->make<StageList>( StageList{fStages, (uint64_t) fn, ctx, true} );
fNumStages += 1;
fSlotsNeeded += ctx ? 2 : 1;
}

View File

@ -122,6 +122,9 @@ public:
};
void append(StockStage, void* = nullptr);
void append(StockStage stage, const void* ctx) { this->append(stage, const_cast<void*>(ctx)); }
// For raw functions (i.e. from a JIT). Don't use this unless you know exactly what fn needs to
// be. :)
void append(void* fn, void* ctx);
// Append all stages to this pipeline.
void extend(const SkRasterPipeline&);
@ -154,8 +157,9 @@ public:
private:
struct StageList {
StageList* prev;
StockStage stage;
uint64_t stage;
void* ctx;
bool rawFunction;
};
using StartPipelineFn = void(*)(size_t,size_t,size_t,size_t, void** program);

View File

@ -22,7 +22,8 @@ SkRasterPipeline::StartPipelineFn SkRasterPipeline::build_pipeline(void** ip) co
st->stage == SkRasterPipeline::clamp_1) {
continue; // No-ops in lowp.
}
if (auto fn = SkOpts::stages_lowp[st->stage]) {
SkOpts::StageFn fn;
if (!st->rawFunction && (fn = SkOpts::stages_lowp[st->stage])) {
if (st->ctx) {
*--ip = st->ctx;
}
@ -42,8 +43,12 @@ SkRasterPipeline::StartPipelineFn SkRasterPipeline::build_pipeline(void** ip) co
if (st->ctx) {
*--ip = st->ctx;
}
if (st->rawFunction) {
*--ip = (void*)st->stage;
} else {
*--ip = (void*)SkOpts::stages_highp[st->stage];
}
}
return SkOpts::start_pipeline_highp;
}

View File

@ -387,6 +387,7 @@ void CFGGenerator::addExpression(CFG& cfg, std::unique_ptr<Expression>* e, bool
cfg.fBlocks[cfg.fCurrent].fNodes.push_back({ BasicBlock::Node::kExpression_Kind,
constantPropagate, e, nullptr });
break;
case Expression::kAppendStage_Kind: // fall through
case Expression::kBoolLiteral_Kind: // fall through
case Expression::kFloatLiteral_Kind: // fall through
case Expression::kIntLiteral_Kind: // fall through

View File

@ -54,17 +54,22 @@ static const char* SKSL_FP_INCLUDE =
#include "sksl_fp.inc"
;
static const char* SKSL_CPU_INCLUDE =
#include "sksl_cpu.inc"
;
namespace SkSL {
Compiler::Compiler(Flags flags)
: fFlags(flags)
, fContext(new Context())
, fErrorCount(0) {
auto types = std::shared_ptr<SymbolTable>(new SymbolTable(this));
auto symbols = std::shared_ptr<SymbolTable>(new SymbolTable(types, this));
fIRGenerator = new IRGenerator(&fContext, symbols, *this);
fIRGenerator = new IRGenerator(fContext.get(), symbols, *this);
fTypes = types;
#define ADD_TYPE(t) types->addWithoutOwnership(fContext.f ## t ## _Type->fName, \
fContext.f ## t ## _Type.get())
#define ADD_TYPE(t) types->addWithoutOwnership(fContext->f ## t ## _Type->fName, \
fContext->f ## t ## _Type.get())
ADD_TYPE(Void);
ADD_TYPE(Float);
ADD_TYPE(Float2);
@ -188,15 +193,16 @@ Compiler::Compiler(Flags flags)
ADD_TYPE(GSampler2DArrayShadow);
ADD_TYPE(GSamplerCubeArrayShadow);
ADD_TYPE(FragmentProcessor);
ADD_TYPE(SkRasterPipeline);
StringFragment skCapsName("sk_Caps");
Variable* skCaps = new Variable(-1, Modifiers(), skCapsName,
*fContext.fSkCaps_Type, Variable::kGlobal_Storage);
*fContext->fSkCaps_Type, Variable::kGlobal_Storage);
fIRGenerator->fSymbolTable->add(skCapsName, std::unique_ptr<Symbol>(skCaps));
StringFragment skArgsName("sk_Args");
Variable* skArgs = new Variable(-1, Modifiers(), skArgsName,
*fContext.fSkArgs_Type, Variable::kGlobal_Storage);
*fContext->fSkArgs_Type, Variable::kGlobal_Storage);
fIRGenerator->fSymbolTable->add(skArgsName, std::unique_ptr<Symbol>(skArgs));
std::vector<std::unique_ptr<ProgramElement>> ignored;
@ -232,19 +238,19 @@ void Compiler::addDefinition(const Expression* lvalue, std::unique_ptr<Expressio
// but since we pass foo as a whole it is flagged as an error) unless we perform a much
// more complicated whole-program analysis. This is probably good enough.
this->addDefinition(((Swizzle*) lvalue)->fBase.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
break;
case Expression::kIndex_Kind:
// see comments in Swizzle
this->addDefinition(((IndexExpression*) lvalue)->fBase.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
break;
case Expression::kFieldAccess_Kind:
// see comments in Swizzle
this->addDefinition(((FieldAccess*) lvalue)->fBase.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
break;
case Expression::kTernary_Kind:
@ -252,10 +258,10 @@ void Compiler::addDefinition(const Expression* lvalue, std::unique_ptr<Expressio
// This allows for false positives (meaning we fail to detect that a variable might not
// have been assigned), but is preferable to false negatives.
this->addDefinition(((TernaryExpression*) lvalue)->fIfTrue.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
this->addDefinition(((TernaryExpression*) lvalue)->fIfFalse.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
break;
default:
@ -279,7 +285,7 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
} else if (Compiler::IsAssignment(b->fOperator)) {
this->addDefinition(
b->fLeft.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
}
@ -290,7 +296,7 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
if (p->fOperator == Token::MINUSMINUS || p->fOperator == Token::PLUSPLUS) {
this->addDefinition(
p->fOperand.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
}
break;
@ -300,7 +306,7 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
if (p->fOperator == Token::MINUSMINUS || p->fOperator == Token::PLUSPLUS) {
this->addDefinition(
p->fOperand.get(),
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
}
break;
@ -310,7 +316,7 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
if (v->fRefKind != VariableReference::kRead_RefKind) {
this->addDefinition(
v,
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
definitions);
}
}
@ -343,6 +349,9 @@ void Compiler::scanCFG(CFG* cfg, BlockId blockId, std::set<BlockId>* workList) {
// propagate definitions to exits
for (BlockId exitId : block.fExits) {
if (exitId == blockId) {
continue;
}
BasicBlock& exit = cfg->fBlocks[exitId];
for (const auto& pair : after) {
std::unique_ptr<Expression>* e1 = pair.second;
@ -359,7 +368,7 @@ void Compiler::scanCFG(CFG* cfg, BlockId blockId, std::set<BlockId>* workList) {
workList->insert(exitId);
if (e1 && e2) {
exit.fBefore[pair.first] =
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression;
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression;
} else {
exit.fBefore[pair.first] = nullptr;
}
@ -990,7 +999,7 @@ void Compiler::simplifyStatement(DefinitionMap& definitions,
continue;
}
ASSERT(c->fValue->fKind == s.fValue->fKind);
found = c->fValue->compareConstant(fContext, *s.fValue);
found = c->fValue->compareConstant(*fContext, *s.fValue);
if (found) {
std::unique_ptr<Statement> newBlock = block_for_case(&s, c.get());
if (newBlock) {
@ -1153,7 +1162,7 @@ void Compiler::scanCFG(FunctionDefinition& f) {
}
// check for missing return
if (f.fDeclaration.fReturnType != *fContext.fVoid_Type) {
if (f.fDeclaration.fReturnType != *fContext->fVoid_Type) {
if (cfg.fBlocks[cfg.fExit].fEntrances.size()) {
this->error(f.fOffset, String("function can exit without returning a value"));
}
@ -1183,6 +1192,10 @@ std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, String tex
fIRGenerator->convertProgram(kind, SKSL_FP_INCLUDE, strlen(SKSL_FP_INCLUDE), *fTypes,
&elements);
break;
case Program::kCPU_Kind:
fIRGenerator->convertProgram(kind, SKSL_CPU_INCLUDE, strlen(SKSL_CPU_INCLUDE),
*fTypes, &elements);
break;
}
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
for (auto& element : elements) {
@ -1203,7 +1216,7 @@ std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, String tex
auto result = std::unique_ptr<Program>(new Program(kind,
std::move(textPtr),
settings,
&fContext,
fContext,
std::move(elements),
fIRGenerator->fSymbolTable,
fIRGenerator->fInputs));
@ -1220,7 +1233,7 @@ bool Compiler::toSPIRV(const Program& program, OutputStream& out) {
#ifdef SK_ENABLE_SPIRV_VALIDATION
StringStream buffer;
fSource = program.fSource.get();
SPIRVCodeGenerator cg(&fContext, &program, this, &buffer);
SPIRVCodeGenerator cg(fContext.get(), &program, this, &buffer);
bool result = cg.generateCode();
fSource = nullptr;
if (result) {
@ -1238,7 +1251,7 @@ bool Compiler::toSPIRV(const Program& program, OutputStream& out) {
}
#else
fSource = program.fSource.get();
SPIRVCodeGenerator cg(&fContext, &program, this, &out);
SPIRVCodeGenerator cg(fContext.get(), &program, this, &out);
bool result = cg.generateCode();
fSource = nullptr;
#endif
@ -1257,7 +1270,7 @@ bool Compiler::toSPIRV(const Program& program, String* out) {
bool Compiler::toGLSL(const Program& program, OutputStream& out) {
fSource = program.fSource.get();
GLSLCodeGenerator cg(&fContext, &program, this, &out);
GLSLCodeGenerator cg(fContext.get(), &program, this, &out);
bool result = cg.generateCode();
fSource = nullptr;
this->writeErrorCount();
@ -1274,7 +1287,7 @@ bool Compiler::toGLSL(const Program& program, String* out) {
}
bool Compiler::toMetal(const Program& program, OutputStream& out) {
MetalCodeGenerator cg(&fContext, &program, this, &out);
MetalCodeGenerator cg(fContext.get(), &program, this, &out);
bool result = cg.generateCode();
this->writeErrorCount();
return result;
@ -1282,7 +1295,7 @@ bool Compiler::toMetal(const Program& program, OutputStream& out) {
bool Compiler::toCPP(const Program& program, String name, OutputStream& out) {
fSource = program.fSource.get();
CPPCodeGenerator cg(&fContext, &program, this, name, &out);
CPPCodeGenerator cg(fContext.get(), &program, this, name, &out);
bool result = cg.generateCode();
fSource = nullptr;
this->writeErrorCount();
@ -1291,7 +1304,7 @@ bool Compiler::toCPP(const Program& program, String name, OutputStream& out) {
bool Compiler::toH(const Program& program, String name, OutputStream& out) {
fSource = program.fSource.get();
HCodeGenerator cg(&fContext, &program, this, name, &out);
HCodeGenerator cg(fContext.get(), &program, this, name, &out);
bool result = cg.generateCode();
fSource = nullptr;
this->writeErrorCount();

View File

@ -88,6 +88,10 @@ public:
return fErrorCount;
}
Context& context() {
return *fContext;
}
static const char* OperatorName(Token::Kind token);
static bool IsAssignment(Token::Kind token);
@ -134,7 +138,7 @@ private:
int fFlags;
const String* fSource;
Context fContext;
std::shared_ptr<Context> fContext;
int fErrorCount;
String fErrorText;
};

View File

@ -185,6 +185,7 @@ public:
, fSkCaps_Type(new Type("$sk_Caps"))
, fSkArgs_Type(new Type("$sk_Args"))
, fFragmentProcessor_Type(new Type("fragmentProcessor"))
, fSkRasterPipeline_Type(new Type("SkRasterPipeline"))
, fDefined_Expression(new Defined(*fInvalid_Type)) {}
static std::vector<const Type*> static_type(const Type& t) {
@ -333,6 +334,7 @@ public:
const std::unique_ptr<Type> fSkCaps_Type;
const std::unique_ptr<Type> fSkArgs_Type;
const std::unique_ptr<Type> fFragmentProcessor_Type;
const std::unique_ptr<Type> fSkRasterPipeline_Type;
// dummy expression used to mark that a variable has a value during dataflow analysis (when it
// could have several different values, or the analyzer is otherwise unable to assign it a

View File

@ -17,6 +17,7 @@
#include "ast/SkSLASTFloatLiteral.h"
#include "ast/SkSLASTIndexSuffix.h"
#include "ast/SkSLASTIntLiteral.h"
#include "ir/SkSLAppendStage.h"
#include "ir/SkSLBinaryExpression.h"
#include "ir/SkSLBoolLiteral.h"
#include "ir/SkSLBreakStatement.h"
@ -1971,6 +1972,91 @@ std::unique_ptr<Expression> IRGenerator::convertTypeField(int offset, const Type
return result;
}
std::unique_ptr<Expression> IRGenerator::convertAppend(int offset,
const std::vector<std::unique_ptr<ASTExpression>>& args) {
#ifndef SKSL_STANDALONE
if (args.size() < 2) {
fErrors.error(offset, "'append' requires at least two arguments");
return nullptr;
}
std::unique_ptr<Expression> pipeline = this->convertExpression(*args[0]);
if (!pipeline) {
return nullptr;
}
if (pipeline->fType != *fContext.fSkRasterPipeline_Type) {
fErrors.error(offset, "first argument of 'append' must have type 'SkRasterPipeline'");
return nullptr;
}
if (ASTExpression::kIdentifier_Kind != args[1]->fKind) {
fErrors.error(offset, "'" + args[1]->description() + "' is not a valid stage");
return nullptr;
}
StringFragment name = ((const ASTIdentifier&) *args[1]).fText;
SkRasterPipeline::StockStage stage = SkRasterPipeline::premul;
std::vector<std::unique_ptr<Expression>> stageArgs;
stageArgs.push_back(std::move(pipeline));
for (size_t i = 2; i < args.size(); ++i) {
std::unique_ptr<Expression> arg = this->convertExpression(*args[i]);
if (!arg) {
return nullptr;
}
stageArgs.push_back(std::move(arg));
}
size_t expectedArgs = 0;
// FIXME use a map
if ("premul" == name) {
stage = SkRasterPipeline::premul;
}
else if ("unpremul" == name) {
stage = SkRasterPipeline::unpremul;
}
else if ("clamp_0" == name) {
stage = SkRasterPipeline::clamp_0;
}
else if ("clamp_1" == name) {
stage = SkRasterPipeline::clamp_1;
}
else if ("matrix_4x5" == name) {
expectedArgs = 1;
stage = SkRasterPipeline::matrix_4x5;
if (1 == stageArgs.size() && stageArgs[0]->fType.fName != "float[20]") {
fErrors.error(offset, "pipeline stage '" + name + "' expected a float[20] argument");
return nullptr;
}
}
else {
bool found = false;
for (const auto& e : *fProgramElements) {
if (ProgramElement::kFunction_Kind == e->fKind) {
const FunctionDefinition& f = (const FunctionDefinition&) *e;
if (f.fDeclaration.fName == name) {
stage = SkRasterPipeline::callback;
std::vector<const FunctionDeclaration*> functions = { &f.fDeclaration };
stageArgs.emplace_back(new FunctionReference(fContext, offset, functions));
found = true;
break;
}
}
}
if (!found) {
fErrors.error(offset, "'" + name + "' is not a valid pipeline stage");
return nullptr;
}
}
if (args.size() != expectedArgs + 2) {
fErrors.error(offset, "pipeline stage '" + name + "' expected an additional argument " +
"count of " + to_string((int) expectedArgs) + ", but found " +
to_string((int) args.size() - 1));
return nullptr;
}
return std::unique_ptr<Expression>(new AppendStage(fContext, offset, stage,
std::move(stageArgs)));
#else
ASSERT(false);
return nullptr;
#endif
}
std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
const ASTSuffixExpression& expression) {
std::unique_ptr<Expression> base = this->convertExpression(*expression.fBase);
@ -1996,6 +2082,10 @@ std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
}
case ASTSuffix::kCall_Kind: {
auto rawArguments = &((ASTCallSuffix&) *expression.fSuffix).fArguments;
if (Expression::kFunctionReference_Kind == base->fKind &&
"append" == ((const FunctionReference&) *base).fFunctions[0]->fName) {
return convertAppend(expression.fOffset, *rawArguments);
}
std::vector<std::unique_ptr<Expression>> arguments;
for (size_t i = 0; i < rawArguments->size(); i++) {
std::unique_ptr<Expression> converted =

View File

@ -114,6 +114,8 @@ private:
std::vector<std::unique_ptr<Expression>> arguments);
int coercionCost(const Expression& expr, const Type& type);
std::unique_ptr<Expression> coerce(std::unique_ptr<Expression> expr, const Type& type);
std::unique_ptr<Expression> convertAppend(int offset,
const std::vector<std::unique_ptr<ASTExpression>>& args);
std::unique_ptr<Block> convertBlock(const ASTBlock& block);
std::unique_ptr<Statement> convertBreak(const ASTBreakStatement& b);
std::unique_ptr<Expression> convertNumberConstructor(

View File

@ -0,0 +1,473 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_STANDALONE
#include "SkSLInterpreter.h"
#include "ir/SkSLBinaryExpression.h"
#include "ir/SkSLExpressionStatement.h"
#include "ir/SkSLForStatement.h"
#include "ir/SkSLFunctionCall.h"
#include "ir/SkSLFunctionReference.h"
#include "ir/SkSLIfStatement.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLPostfixExpression.h"
#include "ir/SkSLPrefixExpression.h"
#include "ir/SkSLProgram.h"
#include "ir/SkSLStatement.h"
#include "ir/SkSLTernaryExpression.h"
#include "ir/SkSLVarDeclarations.h"
#include "ir/SkSLVarDeclarationsStatement.h"
#include "ir/SkSLVariableReference.h"
#include "SkRasterPipeline.h"
#include "../jumper/SkJumper.h"
namespace SkSL {
void Interpreter::run() {
for (const auto& e : fProgram->fElements) {
if (ProgramElement::kFunction_Kind == e->fKind) {
const FunctionDefinition& f = (const FunctionDefinition&) *e;
if ("appendStages" == f.fDeclaration.fName) {
this->run(f);
return;
}
}
}
ASSERT(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;
}
ASSERT(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:
ASSERT(index == 0);
abort();
case Statement::kContinue_Kind:
ASSERT(index == 0);
abort();
case Statement::kDiscard_Kind:
ASSERT(index == 0);
abort();
case Statement::kDo_Kind:
abort();
case Statement::kExpression_Kind:
ASSERT(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:
ASSERT(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:
ASSERT(index == 0);
break;
case Statement::kReturn_Kind:
ASSERT(index == 0);
abort();
case Statement::kSwitch_Kind:
abort();
case Statement::kVarDeclarations_Kind:
ASSERT(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:
ASSERT(fVars.size());
ASSERT(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");
}
struct CallbackCtx : public SkJumper_CallbackCtx {
Interpreter* fInterpreter;
const FunctionDefinition* fFunction;
};
static void do_callback(SkJumper_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;
}
}
void Interpreter::appendStage(const AppendStage& a) {
switch (a.fStage) {
case SkRasterPipeline::matrix_4x5: {
ASSERT(a.fArguments.size() == 1);
StackIndex transpose = evaluate(*a.fArguments[0]).fInt;
fPipeline.append(SkRasterPipeline::matrix_4x5, &fStack[transpose]);
break;
}
case SkRasterPipeline::callback: {
ASSERT(a.fArguments.size() == 1);
CallbackCtx* ctx = new CallbackCtx();
ctx->fInterpreter = this;
ctx->fn = do_callback;
for (const auto& e : fProgram->fElements) {
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);
}
}
Interpreter::Value Interpreter::call(const FunctionCall& c) {
abort();
}
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());
}
break;
}
case Expression::kBoolLiteral_Kind:
return Value(((const BoolLiteral&) expr).fValue);
case Expression::kConstructor_Kind:
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();
}
}
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 {
ASSERT(Token::MINUSMINUS == p.fOperator);
--fStack[lvalue].fFloat;
}
break;
case kInt_TypeKind:
if (Token::PLUSPLUS == p.fOperator) {
++fStack[lvalue].fInt;
} else {
ASSERT(Token::MINUSMINUS == p.fOperator);
--fStack[lvalue].fInt;
}
break;
default:
abort();
}
return result;
}
case Expression::kSetting_Kind:
break;
case Expression::kSwizzle_Kind:
break;
case Expression::kVariableReference_Kind:
ASSERT(fVars.size());
ASSERT(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:
break;
default:
break;
}
ABORT("unsupported expression: %s\n", expr.description().c_str());
}
} // namespace
#endif

View File

@ -0,0 +1,89 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_INTERPRETER
#define SKSL_INTERPRETER
#include "ir/SkSLAppendStage.h"
#include "ir/SkSLExpression.h"
#include "ir/SkSLFunctionCall.h"
#include "ir/SkSLFunctionDefinition.h"
#include "ir/SkSLProgram.h"
#include "ir/SkSLStatement.h"
#include <stack>
class SkRasterPipeline;
namespace SkSL {
class Interpreter {
typedef int StackIndex;
struct StatementIndex {
const Statement* fStatement;
size_t fIndex;
};
public:
union Value {
Value(float f)
: fFloat(f) {}
Value(int i)
: fInt(i) {}
Value(bool b)
: fBool(b) {}
float fFloat;
int fInt;
bool fBool;
};
enum TypeKind {
kFloat_TypeKind,
kInt_TypeKind,
kBool_TypeKind
};
Interpreter(std::unique_ptr<Program> program, SkRasterPipeline* pipeline, std::vector<Value>* stack)
: fProgram(std::move(program))
, fPipeline(*pipeline)
, fStack(*stack) {}
void run();
void run(const FunctionDefinition& f);
void push(Value value);
Value pop();
StackIndex stackAlloc(int count);
void runStatement();
StackIndex getLValue(const Expression& expr);
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::map<const Variable*, StackIndex>> fVars;
std::vector<Value> &fStack;
};
} // namespace
#endif

1747
src/sksl/SkSLJIT.cpp Normal file

File diff suppressed because it is too large Load Diff

344
src/sksl/SkSLJIT.h Normal file
View File

@ -0,0 +1,344 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_JIT
#define SKSL_JIT
#ifdef SK_LLVM_AVAILABLE
#include "ir/SkSLAppendStage.h"
#include "ir/SkSLBinaryExpression.h"
#include "ir/SkSLBreakStatement.h"
#include "ir/SkSLContinueStatement.h"
#include "ir/SkSLExpression.h"
#include "ir/SkSLDoStatement.h"
#include "ir/SkSLForStatement.h"
#include "ir/SkSLFunctionCall.h"
#include "ir/SkSLFunctionDefinition.h"
#include "ir/SkSLIfStatement.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLPrefixExpression.h"
#include "ir/SkSLPostfixExpression.h"
#include "ir/SkSLProgram.h"
#include "ir/SkSLReturnStatement.h"
#include "ir/SkSLStatement.h"
#include "ir/SkSLSwizzle.h"
#include "ir/SkSLTernaryExpression.h"
#include "ir/SkSLVarDeclarationsStatement.h"
#include "ir/SkSLVariableReference.h"
#include "ir/SkSLWhileStatement.h"
#include "llvm-c/Analysis.h"
#include "llvm-c/Core.h"
#include "llvm-c/OrcBindings.h"
#include "llvm-c/Support.h"
#include "llvm-c/Target.h"
#include "llvm-c/Transforms/PassManagerBuilder.h"
#include "llvm-c/Types.h"
#include <stack>
class SkRasterPipeline;
namespace SkSL {
/**
* A just-in-time compiler for SkSL code which uses an LLVM backend. Only available when the
* skia_llvm_path gn arg is set.
*
* Example of using SkSLJIT to set up an SkJumper pipeline stage:
*
* #ifdef SK_LLVM_AVAILABLE
* SkSL::Compiler compiler;
* SkSL::Program::Settings settings;
* std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kCPU_Kind,
* "void swap(int x, int y, inout float4 color) {"
* " color.rb = color.br;"
* "}",
* settings);
* if (!program) {
* printf("%s\n", compiler.errorText().c_str());
* abort();
* }
* SkSL::JIT& jit = *scratch->make<SkSL::JIT>(&compiler);
* std::unique_ptr<SkSL::JIT::Module> module = jit.compile(std::move(program));
* void* func = module->getJumperStage("swap");
* p->append(func, nullptr);
* #endif
*/
class JIT {
typedef int StackIndex;
public:
class Module {
public:
/**
* Returns the address of a symbol in the module.
*/
void* getSymbol(const char* name);
/**
* Returns the address of a function as an SkJumper pipeline stage. The function must have
* the signature void <name>(int x, int y, inout float4 color). The returned function will
* have the correct signature to function as an SkJumper stage (meaning it will actually
* have a different signature at runtime, accepting vector parameters and operating on
* multiple pixels simultaneously as is normal for SkJumper stages).
*/
void* getJumperStage(const char* name);
~Module() {
LLVMOrcDisposeSharedModuleRef(fSharedModule);
}
private:
Module(std::unique_ptr<Program> program,
LLVMSharedModuleRef sharedModule,
LLVMOrcJITStackRef jitStack)
: fProgram(std::move(program))
, fSharedModule(sharedModule)
, fJITStack(jitStack) {}
std::unique_ptr<Program> fProgram;
LLVMSharedModuleRef fSharedModule;
LLVMOrcJITStackRef fJITStack;
friend class JIT;
};
JIT(Compiler* compiler);
~JIT();
/**
* Just-in-time compiles an SkSL program and returns the resulting Module. The JIT must not be
* destroyed before all of its Modules are destroyed.
*/
std::unique_ptr<Module> compile(std::unique_ptr<Program> program);
private:
static constexpr int CHANNELS = 4;
enum TypeKind {
kFloat_TypeKind,
kInt_TypeKind,
kUInt_TypeKind,
kBool_TypeKind
};
class LValue {
public:
virtual ~LValue() {}
virtual LLVMValueRef load(LLVMBuilderRef builder) = 0;
virtual void store(LLVMBuilderRef builder, LLVMValueRef value) = 0;
};
void addBuiltinFunction(const char* ourName, const char* realName, LLVMTypeRef returnType,
std::vector<LLVMTypeRef> parameters);
void loadBuiltinFunctions();
void setBlock(LLVMBuilderRef builder, LLVMBasicBlockRef block);
LLVMTypeRef getType(const Type& type);
TypeKind typeKind(const Type& type);
std::unique_ptr<LValue> getLValue(LLVMBuilderRef builder, const Expression& expr);
void vectorize(LLVMBuilderRef builder, LLVMValueRef* value, int columns);
void vectorize(LLVMBuilderRef builder, const BinaryExpression& b, LLVMValueRef* left,
LLVMValueRef* right);
LLVMValueRef compileBinary(LLVMBuilderRef builder, const BinaryExpression& b);
LLVMValueRef compileConstructor(LLVMBuilderRef builder, const Constructor& c);
LLVMValueRef compileFunctionCall(LLVMBuilderRef builder, const FunctionCall& fc);
LLVMValueRef compileIndex(LLVMBuilderRef builder, const IndexExpression& v);
LLVMValueRef compilePostfix(LLVMBuilderRef builder, const PostfixExpression& p);
LLVMValueRef compilePrefix(LLVMBuilderRef builder, const PrefixExpression& p);
LLVMValueRef compileSwizzle(LLVMBuilderRef builder, const Swizzle& s);
LLVMValueRef compileVariableReference(LLVMBuilderRef builder, const VariableReference& v);
LLVMValueRef compileTernary(LLVMBuilderRef builder, const TernaryExpression& t);
LLVMValueRef compileExpression(LLVMBuilderRef builder, const Expression& expr);
void appendStage(LLVMBuilderRef builder, const AppendStage& a);
void compileBlock(LLVMBuilderRef builder, const Block& block);
void compileBreak(LLVMBuilderRef builder, const BreakStatement& b);
void compileContinue(LLVMBuilderRef builder, const ContinueStatement& c);
void compileDo(LLVMBuilderRef builder, const DoStatement& d);
void compileFor(LLVMBuilderRef builder, const ForStatement& f);
void compileIf(LLVMBuilderRef builder, const IfStatement& i);
void compileReturn(LLVMBuilderRef builder, const ReturnStatement& r);
void compileVarDeclarations(LLVMBuilderRef builder, const VarDeclarationsStatement& decls);
void compileWhile(LLVMBuilderRef builder, const WhileStatement& w);
void compileStatement(LLVMBuilderRef builder, const Statement& stmt);
// The "Vector" variants of functions attempt to compile a given expression or statement as part
// of a vectorized SkJumper stage function - that is, with r, g, b, and a each being vectors of
// fVectorCount floats. So a statement like "color.r = 0;" looks like it modifies a single
// channel of a single pixel, but the compiled code will actually modify the red channel of
// fVectorCount pixels at once.
//
// As not everything can be vectorized, these calls return a bool to indicate whether they were
// successful. If anything anywhere in the function cannot be vectorized, the JIT will fall back
// to looping over the pixels instead.
//
// Since we process multiple pixels at once, and each pixel consists of multiple color channels,
// expressions may effectively result in a vector-of-vectors. We produce zero to four outputs
// when compiling expression, each of which is a vector, so that e.g. float2(1, 0) actually
// produces two vectors, one containing all 1s, the other all 0s. The out parameter always
// allows for 4 channels, but the functions produce 0 to 4 channels depending on the type they
// are operating on. Thus evaluating "color.rgb" actually fills in out[0] through out[2],
// leaving out[3] uninitialized.
// As the number of outputs can be inferred from the type of the expression, it is not
// explicitly signalled anywhere.
bool compileVectorBinary(LLVMBuilderRef builder, const BinaryExpression& b,
LLVMValueRef out[CHANNELS]);
bool compileVectorConstructor(LLVMBuilderRef builder, const Constructor& c,
LLVMValueRef out[CHANNELS]);
bool compileVectorFloatLiteral(LLVMBuilderRef builder, const FloatLiteral& f,
LLVMValueRef out[CHANNELS]);
bool compileVectorSwizzle(LLVMBuilderRef builder, const Swizzle& s,
LLVMValueRef out[CHANNELS]);
bool compileVectorVariableReference(LLVMBuilderRef builder, const VariableReference& v,
LLVMValueRef out[CHANNELS]);
bool compileVectorExpression(LLVMBuilderRef builder, const Expression& expr,
LLVMValueRef out[CHANNELS]);
bool getVectorLValue(LLVMBuilderRef builder, const Expression& e, LLVMValueRef out[CHANNELS]);
/**
* Evaluates the left and right operands of a binary operation, promoting one of them to a
* vector if necessary to make the types match.
*/
bool getVectorBinaryOperands(LLVMBuilderRef builder, const Expression& left,
LLVMValueRef outLeft[CHANNELS], const Expression& right,
LLVMValueRef outRight[CHANNELS]);
bool compileVectorStatement(LLVMBuilderRef builder, const Statement& stmt);
/**
* Returns true if this function has the signature void(int, int, inout float4) and thus can be
* used as an SkJumper stage.
*/
bool hasStageSignature(const FunctionDeclaration& f);
/**
* Attempts to compile a vectorized stage function, returning true on success. A stage function
* of e.g. "color.r = 0;" will produce code which sets the entire red vector to zeros in a
* single instruction, thus calculating several pixels at once.
*/
bool compileStageFunctionVector(const FunctionDefinition& f, LLVMValueRef newFunc);
/**
* Fallback function which loops over the pixels, for when vectorization fails. A stage function
* of e.g. "color.r = 0;" will produce a loop which iterates over the entries in the red vector,
* setting each one to zero individually.
*/
void compileStageFunctionLoop(const FunctionDefinition& f, LLVMValueRef newFunc);
/**
* Called when compiling a function which has the signature of an SkJumper stage. Produces a
* version of the function which can be plugged into SkJumper (thus having a signature which
* accepts four vectors, one for each color channel, containing the color data of multiple
* pixels at once). To go from SkSL code which operates on a single pixel at a time to CPU code
* which operates on multiple pixels at once, the code is either vectorized using
* compileStageFunctionVector or wrapped in a loop using compileStageFunctionLoop.
*/
LLVMValueRef compileStageFunction(const FunctionDefinition& f);
/**
* Compiles an SkSL function to an LLVM function. If the function has the signature of an
* SkJumper stage, it will *also* be compiled by compileStageFunction, resulting in both a stage
* and non-stage version of the function.
*/
LLVMValueRef compileFunction(const FunctionDefinition& f);
void createModule();
void optimize();
bool isColorRef(const Expression& expr);
static uint64_t resolveSymbol(const char* name, JIT* jit);
const char* fCPU;
int fVectorCount;
Compiler& fCompiler;
std::unique_ptr<Program> fProgram;
LLVMContextRef fContext;
LLVMModuleRef fModule;
LLVMSharedModuleRef fSharedModule;
LLVMOrcJITStackRef fJITStack;
LLVMValueRef fCurrentFunction;
LLVMBasicBlockRef fAllocaBlock;
LLVMBasicBlockRef fCurrentBlock;
LLVMTypeRef fVoidType;
LLVMTypeRef fInt1Type;
LLVMTypeRef fInt8Type;
LLVMTypeRef fInt8PtrType;
LLVMTypeRef fInt32Type;
LLVMTypeRef fInt32VectorType;
LLVMTypeRef fInt32Vector2Type;
LLVMTypeRef fInt32Vector3Type;
LLVMTypeRef fInt32Vector4Type;
LLVMTypeRef fInt64Type;
LLVMTypeRef fSizeTType;
LLVMTypeRef fFloat32Type;
LLVMTypeRef fFloat32VectorType;
LLVMTypeRef fFloat32Vector2Type;
LLVMTypeRef fFloat32Vector3Type;
LLVMTypeRef fFloat32Vector4Type;
// Our SkSL stage functions have a single float4 for color, but the actual SkJumper stage
// function has four separate vectors, one for each channel. These four values are references to
// the red, green, blue, and alpha vectors respectively.
LLVMValueRef fChannels[CHANNELS];
// when processing a stage function, this points to the SkSL color parameter (an inout float4)
const Variable* fColorParam;
std::map<const FunctionDeclaration*, LLVMValueRef> fFunctions;
std::map<const Variable*, LLVMValueRef> fVariables;
// LLVM function parameters are read-only, so when modifying function parameters we need to
// first promote them to variables. This keeps track of which parameters have been promoted.
std::set<const Variable*> fPromotedParameters;
std::vector<LLVMBasicBlockRef> fBreakTarget;
std::vector<LLVMBasicBlockRef> fContinueTarget;
LLVMValueRef fAppendFunc;
LLVMValueRef fAppendCallbackFunc;
LLVMValueRef fDebugFunc;
};
} // namespace
#endif // SK_LLVM_AVAILABLE
#endif // SKSL_JIT

View File

@ -0,0 +1,53 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SKSL_APPENDSTAGE
#define SKSL_APPENDSTAGE
#ifndef SKSL_STANDALONE
#include "SkRasterPipeline.h"
#include "SkSLContext.h"
#include "SkSLExpression.h"
namespace SkSL {
struct AppendStage : public Expression {
AppendStage(const Context& context, int offset, SkRasterPipeline::StockStage stage,
std::vector<std::unique_ptr<Expression>> arguments)
: INHERITED(offset, kAppendStage_Kind, *context.fVoid_Type)
, fStage(stage)
, fArguments(std::move(arguments)) {}
String description() const {
String result = "append(";
const char* separator = "";
for (const auto& a : fArguments) {
result += separator;
result += a->description();
separator = ", ";
}
result += ")";
return result;
}
bool hasSideEffects() const {
return true;
}
SkRasterPipeline::StockStage fStage;
std::vector<std::unique_ptr<Expression>> fArguments;
typedef Expression INHERITED;
};
} // namespace
#endif // SKSL_STANDALONE
#endif // SKSL_APPENDSTAGE

View File

@ -25,6 +25,7 @@ typedef std::unordered_map<const Variable*, std::unique_ptr<Expression>*> Defini
*/
struct Expression : public IRNode {
enum Kind {
kAppendStage_Kind,
kBinary_Kind,
kBoolLiteral_Kind,
kConstructor_Kind,

View File

@ -29,7 +29,6 @@ struct FunctionReference : public Expression {
}
String description() const override {
ASSERT(false);
return String("<function>");
}

View File

@ -103,13 +103,14 @@ struct Program {
kFragment_Kind,
kVertex_Kind,
kGeometry_Kind,
kFragmentProcessor_Kind
kFragmentProcessor_Kind,
kCPU_Kind
};
Program(Kind kind,
std::unique_ptr<String> source,
Settings settings,
Context* context,
std::shared_ptr<Context> context,
std::vector<std::unique_ptr<ProgramElement>> elements,
std::shared_ptr<SymbolTable> symbols,
Inputs inputs)
@ -124,7 +125,7 @@ struct Program {
Kind fKind;
std::unique_ptr<String> fSource;
Settings fSettings;
Context* fContext;
std::shared_ptr<Context> fContext;
// it's important to keep fElements defined after (and thus destroyed before) fSymbols,
// because destroying elements can modify reference counts in symbols
std::shared_ptr<SymbolTable> fSymbols;

12
src/sksl/sksl_cpu.inc Normal file
View File

@ -0,0 +1,12 @@
STRINGIFY(
// special-cased within the compiler - append takes various arguments depending on what kind of
// stage is being appended
sk_has_side_effects void append();
float abs(float x);
float sin(float x);
float cos(float x);
float tan(float x);
float sqrt(float x);
sk_has_side_effects void print(float x);
)

120
tests/SkSLJITTest.cpp Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright 2018 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkSLJIT.h"
#include "Test.h"
#ifdef SK_LLVM_AVAILABLE
template<typename type>
void test(skiatest::Reporter* r, const char* src, type x, type y, type result) {
SkSL::Compiler compiler;
SkSL::Program::Settings settings;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kCPU_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
if (program) {
SkSL::JIT jit(&compiler);
std::unique_ptr<SkSL::JIT::Module> module = jit.compile(std::move(program));
type (*test)(type, type) = (type(*)(type, type)) module->getSymbol("test");
REPORTER_ASSERT(r, test(x, y) == result);
} else {
printf("%s", compiler.errorText().c_str());
}
}
DEF_TEST(SkSLJITAdd, r) {
test<int>(r, "int test(int x, int y) { return x + y; }", 12, 5, 17);
test<float>(r, "float test(float x, float y) { return x + y; }", -1, 76, 75);
test<int>(r, "int test(int x, int y) { x += y; return x; }", 12, 5, 17);
test<float>(r, "float test(float x, float y) { x += y; return x; }", -1, 76, 75);
test<int>(r, "int test(int x, int y) { return (int2(x) + int2(y)).x; }", 0, -100, -100);
test<float>(r, "float test(float x, float y) { return (float2(x) + float2(y)).x; }", 36, 6, 42);
}
DEF_TEST(SkSLJITSub, r) {
test<int>(r, "int test(int x, int y) { return x - y; }", 12, 5, 7);
test<float>(r, "float test(float x, float y) { return x - y; }", -1, 76, -77);
test<int>(r, "int test(int x, int y) { x -= y; return x; }", 12, 5, 7);
test<float>(r, "float test(float x, float y) { x -= y; return x; }", -1, 76, -77);
test<int>(r, "int test(int x, int y) { return (int2(x) - int2(y)).x; }", 0, -100, 100);
test<float>(r, "float test(float x, float y) { return (float2(x) - float2(y)).x; }", 36, 6, 30);
}
DEF_TEST(SkSLJITMul, r) {
test<int>(r, "int test(int x, int y) { return x * y; }", 12, 5, 60);
test<float>(r, "float test(float x, float y) { return x * y; }", -1, 76, -76);
test<int>(r, "int test(int x, int y) { x *= y; return x; }", 12, 5, 60);
test<float>(r, "float test(float x, float y) { x *= y; return x; }", -1, 76, -76);
test<int>(r, "int test(int x, int y) { return (int2(x) * int2(y)).x; }", 0, -100, 0);
test<float>(r, "float test(float x, float y) { return (float2(x) * float2(y)).x; }", 36, 6,
216);
}
DEF_TEST(SkSLJITDiv, r) {
test<int>(r, "int test(int x, int y) { return x / y; }", 12, 5, 2);
test<float>(r, "float test(float x, float y) { return x / y; }", -1, 76, -1.0 / 76.0);
test<int>(r, "int test(int x, int y) { x /= y; return x; }", 12, 5, 2);
test<float>(r, "float test(float x, float y) { x /= y; return x; }", -1, 76, -1.0 / 76.0);
test<int>(r, "int test(int x, int y) { return (int2(x) / int2(y)).x; }", 0, -100, 0);
test<float>(r, "float test(float x, float y) { return (float2(x) / float2(y)).x; }", 36, 6,
6);
}
DEF_TEST(SkSLJITOr, r) {
test<int>(r, "int test(int x, int y) { return x | y; }", 45, 15, 47);
test<int>(r, "int test(int x, int y) { x |= y; return x; }", 45, 15, 47);
}
DEF_TEST(SkSLJITAnd, r) {
test<int>(r, "int test(int x, int y) { return x & y; }", 45, 15, 13);
test<int>(r, "int test(int x, int y) { x &= y; return x; }", 45, 15, 13);
}
DEF_TEST(SkSLJITIf, r) {
test<int>(r, "int test(int x, int y) { if (x > y) return x; else return y; }", 17, 8, 17);
test<int>(r, "int test(int x, int y) { if (x > y) return x; else return y; }", 8, 17, 17);
test<int>(r, "int test(int x, int y) { if (x > y) if (x > 0) return x; else return -x; "
"else if (y > 0) return y; else return -y; }", -8, -17, 8);
}
DEF_TEST(SkSLJITTernary, r) {
test<int>(r, "int test(int x, int y) { return x > y ? x : y; }", 17, 8, 17);
test<int>(r, "int test(int x, int y) { return x > y ? x : y; }", 8, 17, 17);
test<int>(r, "int test(int x, int y) { return x > y ? (x > 0 ? x : -x) :"
"(y > 0 ? y : -y); }", -8, -17, 8);
}
DEF_TEST(SkSLJITFor, r) {
test<int>(r, "int test(int x, int y) {"
" int result = 0;"
" for (int i = 0; i < x; ++i)"
" result += y;"
" return result;"
"}", 124, 17, 2108);
}
DEF_TEST(SkSLJITDo, r) {
test<int>(r, "int test(int x, int y) {"
" int result = -10;"
" do { result = 0; } while (false);"
" do { result += x; } while (result < y);"
" return result;"
"}", 96, 200, 288);
}
DEF_TEST(SkSLJITWhile, r) {
test<int>(r, "int test(int x, int y) {"
" int result = 0;"
" while (false) { result = -10; }"
" while (result < y) { result += x; }"
" return result;"
"}", 96, 200, 288);
}
#endif