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:
parent
3560b58de3
commit
26a9aad63b
12
BUILD.gn
12
BUILD.gn
@ -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.
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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,7 +43,11 @@ SkRasterPipeline::StartPipelineFn SkRasterPipeline::build_pipeline(void** ip) co
|
||||
if (st->ctx) {
|
||||
*--ip = st->ctx;
|
||||
}
|
||||
*--ip = (void*)SkOpts::stages_highp[st->stage];
|
||||
if (st->rawFunction) {
|
||||
*--ip = (void*)st->stage;
|
||||
} else {
|
||||
*--ip = (void*)SkOpts::stages_highp[st->stage];
|
||||
}
|
||||
}
|
||||
return SkOpts::start_pipeline_highp;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
@ -278,9 +284,9 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
|
||||
this->addDefinition(b->fLeft.get(), &b->fRight, definitions);
|
||||
} else if (Compiler::IsAssignment(b->fOperator)) {
|
||||
this->addDefinition(
|
||||
b->fLeft.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
|
||||
definitions);
|
||||
b->fLeft.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
|
||||
definitions);
|
||||
|
||||
}
|
||||
break;
|
||||
@ -289,9 +295,9 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
|
||||
const PrefixExpression* p = (PrefixExpression*) expr;
|
||||
if (p->fOperator == Token::MINUSMINUS || p->fOperator == Token::PLUSPLUS) {
|
||||
this->addDefinition(
|
||||
p->fOperand.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
|
||||
definitions);
|
||||
p->fOperand.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
|
||||
definitions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -299,9 +305,9 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
|
||||
const PostfixExpression* p = (PostfixExpression*) expr;
|
||||
if (p->fOperator == Token::MINUSMINUS || p->fOperator == Token::PLUSPLUS) {
|
||||
this->addDefinition(
|
||||
p->fOperand.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
|
||||
definitions);
|
||||
p->fOperand.get(),
|
||||
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
|
||||
definitions);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -309,9 +315,9 @@ void Compiler::addDefinitions(const BasicBlock::Node& node,
|
||||
const VariableReference* v = (VariableReference*) expr;
|
||||
if (v->fRefKind != VariableReference::kRead_RefKind) {
|
||||
this->addDefinition(
|
||||
v,
|
||||
(std::unique_ptr<Expression>*) &fContext.fDefined_Expression,
|
||||
definitions);
|
||||
v,
|
||||
(std::unique_ptr<Expression>*) &fContext->fDefined_Expression,
|
||||
definitions);
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -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();
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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(
|
||||
|
473
src/sksl/SkSLInterpreter.cpp
Normal file
473
src/sksl/SkSLInterpreter.cpp
Normal 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
|
89
src/sksl/SkSLInterpreter.h
Normal file
89
src/sksl/SkSLInterpreter.h
Normal 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
1747
src/sksl/SkSLJIT.cpp
Normal file
File diff suppressed because it is too large
Load Diff
344
src/sksl/SkSLJIT.h
Normal file
344
src/sksl/SkSLJIT.h
Normal 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
|
53
src/sksl/ir/SkSLAppendStage.h
Normal file
53
src/sksl/ir/SkSLAppendStage.h
Normal 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
|
@ -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,
|
||||
|
@ -29,7 +29,6 @@ struct FunctionReference : public Expression {
|
||||
}
|
||||
|
||||
String description() const override {
|
||||
ASSERT(false);
|
||||
return String("<function>");
|
||||
}
|
||||
|
||||
|
@ -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
12
src/sksl/sksl_cpu.inc
Normal 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
120
tests/SkSLJITTest.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user