blob: e601387ce2d1a6b78fa68bb688b954cd18a0e437 [file] [log] [blame]
/*
* 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
#ifdef SK_LLVM_AVAILABLE
#include "SkSLJIT.h"
#include "SkCpu.h"
#include "SkRasterPipeline.h"
#include "ir/SkSLAppendStage.h"
#include "ir/SkSLExpressionStatement.h"
#include "ir/SkSLFunctionCall.h"
#include "ir/SkSLFunctionReference.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLProgram.h"
#include "ir/SkSLUnresolvedFunction.h"
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
static constexpr int MAX_VECTOR_COUNT = 16;
extern "C" void sksl_pipeline_append(SkRasterPipeline* p, int stage, void* ctx) {
p->append((SkRasterPipeline::StockStage) stage, ctx);
}
#define PTR_SIZE sizeof(void*)
extern "C" void sksl_pipeline_append_callback(SkRasterPipeline* p, void* fn) {
p->append(fn, nullptr);
}
extern "C" void sksl_debug_print(float f) {
printf("Debug: %f\n", f);
}
extern "C" float sksl_clamp1(float f, float min, float max) {
return SkTPin(f, min, max);
}
using float2 = __attribute__((vector_size(8))) float;
using float3 = __attribute__((vector_size(16))) float;
using float4 = __attribute__((vector_size(16))) float;
extern "C" float2 sksl_clamp2(float2 f, float min, float max) {
return float2 { SkTPin(f[0], min, max), SkTPin(f[1], min, max) };
}
extern "C" float3 sksl_clamp3(float3 f, float min, float max) {
return float3 { SkTPin(f[0], min, max), SkTPin(f[1], min, max), SkTPin(f[2], min, max) };
}
extern "C" float4 sksl_clamp4(float4 f, float min, float max) {
return float4 { SkTPin(f[0], min, max), SkTPin(f[1], min, max), SkTPin(f[2], min, max),
SkTPin(f[3], min, max) };
}
namespace SkSL {
static constexpr int STAGE_PARAM_COUNT = 12;
static bool ends_with_branch(const Statement& stmt) {
switch (stmt.fKind) {
case Statement::kBlock_Kind: {
const Block& b = (const Block&) stmt;
if (b.fStatements.size()) {
return ends_with_branch(*b.fStatements.back());
}
return false;
}
case Statement::kBreak_Kind: // fall through
case Statement::kContinue_Kind: // fall through
case Statement::kReturn_Kind: // fall through
return true;
default:
return false;
}
}
JIT::JIT(Compiler* compiler)
: fCompiler(*compiler) {
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMLinkInMCJIT();
SkASSERT(!SkCpu::Supports(SkCpu::SKX)); // not yet supported
if (SkCpu::Supports(SkCpu::HSW)) {
fVectorCount = 8;
fCPU = "haswell";
} else if (SkCpu::Supports(SkCpu::AVX)) {
fVectorCount = 8;
fCPU = "ivybridge";
} else {
fVectorCount = 4;
fCPU = nullptr;
}
fContext = LLVMContextCreate();
fVoidType = LLVMVoidTypeInContext(fContext);
fInt1Type = LLVMInt1TypeInContext(fContext);
fInt1VectorType = LLVMVectorType(fInt1Type, fVectorCount);
fInt1Vector2Type = LLVMVectorType(fInt1Type, 2);
fInt1Vector3Type = LLVMVectorType(fInt1Type, 3);
fInt1Vector4Type = LLVMVectorType(fInt1Type, 4);
fInt8Type = LLVMInt8TypeInContext(fContext);
fInt8PtrType = LLVMPointerType(fInt8Type, 0);
fInt32Type = LLVMInt32TypeInContext(fContext);
fInt64Type = LLVMInt64TypeInContext(fContext);
fSizeTType = LLVMInt64TypeInContext(fContext);
fInt32VectorType = LLVMVectorType(fInt32Type, fVectorCount);
fInt32Vector2Type = LLVMVectorType(fInt32Type, 2);
fInt32Vector3Type = LLVMVectorType(fInt32Type, 3);
fInt32Vector4Type = LLVMVectorType(fInt32Type, 4);
fFloat32Type = LLVMFloatTypeInContext(fContext);
fFloat32VectorType = LLVMVectorType(fFloat32Type, fVectorCount);
fFloat32Vector2Type = LLVMVectorType(fFloat32Type, 2);
fFloat32Vector3Type = LLVMVectorType(fFloat32Type, 3);
fFloat32Vector4Type = LLVMVectorType(fFloat32Type, 4);
}
JIT::~JIT() {
LLVMOrcDisposeInstance(fJITStack);
LLVMContextDispose(fContext);
}
void JIT::addBuiltinFunction(const char* ourName, const char* realName, LLVMTypeRef returnType,
std::vector<LLVMTypeRef> parameters) {
bool found = false;
for (const auto& pair : *fProgram->fSymbols) {
if (Symbol::kFunctionDeclaration_Kind == pair.second->fKind) {
const FunctionDeclaration& f = (const FunctionDeclaration&) *pair.second;
if (pair.first != ourName || returnType != this->getType(f.fReturnType) ||
parameters.size() != f.fParameters.size()) {
continue;
}
for (size_t i = 0; i < parameters.size(); ++i) {
if (parameters[i] != this->getType(f.fParameters[i]->fType)) {
goto next;
}
}
fFunctions[&f] = LLVMAddFunction(fModule, realName, LLVMFunctionType(returnType,
parameters.data(),
parameters.size(),
false));
found = true;
}
if (Symbol::kUnresolvedFunction_Kind == pair.second->fKind) {
// FIXME consolidate this with the code above
for (const auto& f : ((const UnresolvedFunction&) *pair.second).fFunctions) {
if (pair.first != ourName || returnType != this->getType(f->fReturnType) ||
parameters.size() != f->fParameters.size()) {
continue;
}
for (size_t i = 0; i < parameters.size(); ++i) {
if (parameters[i] != this->getType(f->fParameters[i]->fType)) {
goto next;
}
}
fFunctions[f] = LLVMAddFunction(fModule, realName, LLVMFunctionType(
returnType,
parameters.data(),
parameters.size(),
false));
found = true;
}
}
next:;
}
SkASSERT(found);
}
void JIT::loadBuiltinFunctions() {
this->addBuiltinFunction("abs", "fabs", fFloat32Type, { fFloat32Type });
this->addBuiltinFunction("sin", "sinf", fFloat32Type, { fFloat32Type });
this->addBuiltinFunction("cos", "cosf", fFloat32Type, { fFloat32Type });
this->addBuiltinFunction("tan", "tanf", fFloat32Type, { fFloat32Type });
this->addBuiltinFunction("sqrt", "sqrtf", fFloat32Type, { fFloat32Type });
this->addBuiltinFunction("clamp", "sksl_clamp1", fFloat32Type, { fFloat32Type,
fFloat32Type,
fFloat32Type });
this->addBuiltinFunction("clamp", "sksl_clamp2", fFloat32Vector2Type, { fFloat32Vector2Type,
fFloat32Type,
fFloat32Type });
this->addBuiltinFunction("clamp", "sksl_clamp3", fFloat32Vector3Type, { fFloat32Vector3Type,
fFloat32Type,
fFloat32Type });
this->addBuiltinFunction("clamp", "sksl_clamp4", fFloat32Vector4Type, { fFloat32Vector4Type,
fFloat32Type,
fFloat32Type });
this->addBuiltinFunction("print", "sksl_debug_print", fVoidType, { fFloat32Type });
}
uint64_t JIT::resolveSymbol(const char* name, JIT* jit) {
LLVMOrcTargetAddress result;
if (!LLVMOrcGetSymbolAddress(jit->fJITStack, &result, name)) {
if (!strcmp(name, "_sksl_pipeline_append")) {
result = (uint64_t) &sksl_pipeline_append;
} else if (!strcmp(name, "_sksl_pipeline_append_callback")) {
result = (uint64_t) &sksl_pipeline_append_callback;
} else if (!strcmp(name, "_sksl_clamp1")) {
result = (uint64_t) &sksl_clamp1;
} else if (!strcmp(name, "_sksl_clamp2")) {
result = (uint64_t) &sksl_clamp2;
} else if (!strcmp(name, "_sksl_clamp3")) {
result = (uint64_t) &sksl_clamp3;
} else if (!strcmp(name, "_sksl_clamp4")) {
result = (uint64_t) &sksl_clamp4;
} else if (!strcmp(name, "_sksl_debug_print")) {
result = (uint64_t) &sksl_debug_print;
} else {
result = llvm::RTDyldMemoryManager::getSymbolAddressInProcess(name);
}
}
SkASSERT(result);
return result;
}
LLVMValueRef JIT::compileFunctionCall(LLVMBuilderRef builder, const FunctionCall& fc) {
LLVMValueRef func = fFunctions[&fc.fFunction];
SkASSERT(func);
std::vector<LLVMValueRef> parameters;
for (const auto& a : fc.fArguments) {
parameters.push_back(this->compileExpression(builder, *a));
}
return LLVMBuildCall(builder, func, parameters.data(), parameters.size(), "");
}
LLVMTypeRef JIT::getType(const Type& type) {
switch (type.kind()) {
case Type::kOther_Kind:
if (type.name() == "void") {
return fVoidType;
}
SkASSERT(type.name() == "SkRasterPipeline");
return fInt8PtrType;
case Type::kScalar_Kind:
if (type.isSigned() || type.isUnsigned()) {
return fInt32Type;
}
if (type.isUnsigned()) {
return fInt32Type;
}
if (type.isFloat()) {
return fFloat32Type;
}
SkASSERT(type.name() == "bool");
return fInt1Type;
case Type::kArray_Kind:
return LLVMPointerType(this->getType(type.componentType()), 0);
case Type::kVector_Kind:
if (type.name() == "float2" || type.name() == "half2") {
return fFloat32Vector2Type;
}
if (type.name() == "float3" || type.name() == "half3") {
return fFloat32Vector3Type;
}
if (type.name() == "float4" || type.name() == "half4") {
return fFloat32Vector4Type;
}
if (type.name() == "int2" || type.name() == "short2" || type.name == "byte2") {
return fInt32Vector2Type;
}
if (type.name() == "int3" || type.name() == "short3" || type.name == "byte3") {
return fInt32Vector3Type;
}
if (type.name() == "int4" || type.name() == "short4" || type.name == "byte3") {
return fInt32Vector4Type;
}
// fall through
default:
ABORT("unsupported type");
}
}
void JIT::setBlock(LLVMBuilderRef builder, LLVMBasicBlockRef block) {
fCurrentBlock = block;
LLVMPositionBuilderAtEnd(builder, block);
}
std::unique_ptr<JIT::LValue> JIT::getLValue(LLVMBuilderRef builder, const Expression& expr) {
switch (expr.fKind) {
case Expression::kVariableReference_Kind: {
class PointerLValue : public LValue {
public:
PointerLValue(LLVMValueRef ptr)
: fPointer(ptr) {}
LLVMValueRef load(LLVMBuilderRef builder) override {
return LLVMBuildLoad(builder, fPointer, "lvalue load");
}
void store(LLVMBuilderRef builder, LLVMValueRef value) override {
LLVMBuildStore(builder, value, fPointer);
}
private:
LLVMValueRef fPointer;
};
const Variable* var = &((VariableReference&) expr).fVariable;
if (var->fStorage == Variable::kParameter_Storage &&
!(var->fModifiers.fFlags & Modifiers::kOut_Flag) &&
fPromotedParameters.find(var) == fPromotedParameters.end()) {
// promote parameter to variable
fPromotedParameters.insert(var);
LLVMPositionBuilderAtEnd(builder, fAllocaBlock);
LLVMValueRef alloca = LLVMBuildAlloca(builder, this->getType(var->fType),
String(var->fName).c_str());
LLVMBuildStore(builder, fVariables[var], alloca);
LLVMPositionBuilderAtEnd(builder, fCurrentBlock);
fVariables[var] = alloca;
}
LLVMValueRef ptr = fVariables[var];
return std::unique_ptr<LValue>(new PointerLValue(ptr));
}
case Expression::kTernary_Kind: {
class TernaryLValue : public LValue {
public:
TernaryLValue(JIT* jit, LLVMValueRef test, std::unique_ptr<LValue> ifTrue,
std::unique_ptr<LValue> ifFalse)
: fJIT(*jit)
, fTest(test)
, fIfTrue(std::move(ifTrue))
, fIfFalse(std::move(ifFalse)) {}
LLVMValueRef load(LLVMBuilderRef builder) override {
LLVMBasicBlockRef trueBlock = LLVMAppendBasicBlockInContext(
fJIT.fContext,
fJIT.fCurrentFunction,
"true ? ...");
LLVMBasicBlockRef falseBlock = LLVMAppendBasicBlockInContext(
fJIT.fContext,
fJIT.fCurrentFunction,
"false ? ...");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fJIT.fContext,
fJIT.fCurrentFunction,
"ternary merge");
LLVMBuildCondBr(builder, fTest, trueBlock, falseBlock);
fJIT.setBlock(builder, trueBlock);
LLVMValueRef ifTrue = fIfTrue->load(builder);
LLVMBuildBr(builder, merge);
fJIT.setBlock(builder, falseBlock);
LLVMValueRef ifFalse = fIfTrue->load(builder);
LLVMBuildBr(builder, merge);
fJIT.setBlock(builder, merge);
LLVMTypeRef type = LLVMPointerType(LLVMTypeOf(ifTrue), 0);
LLVMValueRef phi = LLVMBuildPhi(builder, type, "?");
LLVMValueRef incomingValues[2] = { ifTrue, ifFalse };
LLVMBasicBlockRef incomingBlocks[2] = { trueBlock, falseBlock };
LLVMAddIncoming(phi, incomingValues, incomingBlocks, 2);
return phi;
}
void store(LLVMBuilderRef builder, LLVMValueRef value) override {
LLVMBasicBlockRef trueBlock = LLVMAppendBasicBlockInContext(
fJIT.fContext,
fJIT.fCurrentFunction,
"true ? ...");
LLVMBasicBlockRef falseBlock = LLVMAppendBasicBlockInContext(
fJIT.fContext,
fJIT.fCurrentFunction,
"false ? ...");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fJIT.fContext,
fJIT.fCurrentFunction,
"ternary merge");
LLVMBuildCondBr(builder, fTest, trueBlock, falseBlock);
fJIT.setBlock(builder, trueBlock);
fIfTrue->store(builder, value);
LLVMBuildBr(builder, merge);
fJIT.setBlock(builder, falseBlock);
fIfTrue->store(builder, value);
LLVMBuildBr(builder, merge);
fJIT.setBlock(builder, merge);
}
private:
JIT& fJIT;
LLVMValueRef fTest;
std::unique_ptr<LValue> fIfTrue;
std::unique_ptr<LValue> fIfFalse;
};
const TernaryExpression& t = (const TernaryExpression&) expr;
LLVMValueRef test = this->compileExpression(builder, *t.fTest);
return std::unique_ptr<LValue>(new TernaryLValue(this,
test,
this->getLValue(builder,
*t.fIfTrue),
this->getLValue(builder,
*t.fIfFalse)));
}
case Expression::kSwizzle_Kind: {
class SwizzleLValue : public LValue {
public:
SwizzleLValue(JIT* jit, LLVMTypeRef type, std::unique_ptr<LValue> base,
std::vector<int> components)
: fJIT(*jit)
, fType(type)
, fBase(std::move(base))
, fComponents(components) {}
LLVMValueRef load(LLVMBuilderRef builder) override {
LLVMValueRef base = fBase->load(builder);
if (fComponents.size() > 1) {
LLVMValueRef result = LLVMGetUndef(fType);
for (size_t i = 0; i < fComponents.size(); ++i) {
LLVMValueRef element = LLVMBuildExtractElement(
builder,
base,
LLVMConstInt(fJIT.fInt32Type,
fComponents[i],
false),
"swizzle extract");
result = LLVMBuildInsertElement(builder, result, element,
LLVMConstInt(fJIT.fInt32Type, i, false),
"swizzle insert");
}
return result;
}
SkASSERT(fComponents.size() == 1);
return LLVMBuildExtractElement(builder, base,
LLVMConstInt(fJIT.fInt32Type,
fComponents[0],
false),
"swizzle extract");
}
void store(LLVMBuilderRef builder, LLVMValueRef value) override {
LLVMValueRef result = fBase->load(builder);
if (fComponents.size() > 1) {
for (size_t i = 0; i < fComponents.size(); ++i) {
LLVMValueRef element = LLVMBuildExtractElement(builder, value,
LLVMConstInt(
fJIT.fInt32Type,
i,
false),
"swizzle extract");
result = LLVMBuildInsertElement(builder, result, element,
LLVMConstInt(fJIT.fInt32Type,
fComponents[i],
false),
"swizzle insert");
}
} else {
result = LLVMBuildInsertElement(builder, result, value,
LLVMConstInt(fJIT.fInt32Type,
fComponents[0],
false),
"swizzle insert");
}
fBase->store(builder, result);
}
private:
JIT& fJIT;
LLVMTypeRef fType;
std::unique_ptr<LValue> fBase;
std::vector<int> fComponents;
};
const Swizzle& s = (const Swizzle&) expr;
return std::unique_ptr<LValue>(new SwizzleLValue(this, this->getType(s.fType),
this->getLValue(builder, *s.fBase),
s.fComponents));
}
default:
ABORT("unsupported lvalue");
}
}
JIT::TypeKind JIT::typeKind(const Type& type) {
if (type.kind() == Type::kVector_Kind) {
return this->typeKind(type.componentType());
}
if (type.fName == "int" || type.fName == "short" || type.fName == "byte") {
return JIT::kInt_TypeKind;
} else if (type.fName == "uint" || type.fName == "ushort" || type.fName == "ubyte") {
return JIT::kUInt_TypeKind;
} else if (type.fName == "float" || type.fName == "double" || type.fName == "half") {
return JIT::kFloat_TypeKind;
}
ABORT("unsupported type: %s\n", type.description().c_str());
}
void JIT::vectorize(LLVMBuilderRef builder, LLVMValueRef* value, int columns) {
LLVMValueRef result = LLVMGetUndef(LLVMVectorType(LLVMTypeOf(*value), columns));
for (int i = 0; i < columns; ++i) {
result = LLVMBuildInsertElement(builder,
result,
*value,
LLVMConstInt(fInt32Type, i, false),
"vectorize");
}
*value = result;
}
void JIT::vectorize(LLVMBuilderRef builder, const BinaryExpression& b, LLVMValueRef* left,
LLVMValueRef* right) {
if (b.fLeft->fType.kind() == Type::kScalar_Kind &&
b.fRight->fType.kind() == Type::kVector_Kind) {
this->vectorize(builder, left, b.fRight->fType.columns());
} else if (b.fLeft->fType.kind() == Type::kVector_Kind &&
b.fRight->fType.kind() == Type::kScalar_Kind) {
this->vectorize(builder, right, b.fLeft->fType.columns());
}
}
LLVMValueRef JIT::compileBinary(LLVMBuilderRef builder, const BinaryExpression& b) {
#define BINARY(SFunc, UFunc, FFunc) { \
LLVMValueRef left = this->compileExpression(builder, *b.fLeft); \
LLVMValueRef right = this->compileExpression(builder, *b.fRight); \
this->vectorize(builder, b, &left, &right); \
switch (this->typeKind(b.fLeft->fType)) { \
case kInt_TypeKind: \
return SFunc(builder, left, right, "binary"); \
case kUInt_TypeKind: \
return UFunc(builder, left, right, "binary"); \
case kFloat_TypeKind: \
return FFunc(builder, left, right, "binary"); \
default: \
ABORT("unsupported typeKind"); \
} \
}
#define COMPOUND(SFunc, UFunc, FFunc) { \
std::unique_ptr<LValue> lvalue = this->getLValue(builder, *b.fLeft); \
LLVMValueRef left = lvalue->load(builder); \
LLVMValueRef right = this->compileExpression(builder, *b.fRight); \
this->vectorize(builder, b, &left, &right); \
LLVMValueRef result; \
switch (this->typeKind(b.fLeft->fType)) { \
case kInt_TypeKind: \
result = SFunc(builder, left, right, "binary"); \
break; \
case kUInt_TypeKind: \
result = UFunc(builder, left, right, "binary"); \
break; \
case kFloat_TypeKind: \
result = FFunc(builder, left, right, "binary"); \
break; \
default: \
ABORT("unsupported typeKind"); \
} \
lvalue->store(builder, result); \
return result; \
}
#define COMPARE(SFunc, SOp, UFunc, UOp, FFunc, FOp) { \
LLVMValueRef left = this->compileExpression(builder, *b.fLeft); \
LLVMValueRef right = this->compileExpression(builder, *b.fRight); \
this->vectorize(builder, b, &left, &right); \
switch (this->typeKind(b.fLeft->fType)) { \
case kInt_TypeKind: \
return SFunc(builder, SOp, left, right, "binary"); \
case kUInt_TypeKind: \
return UFunc(builder, UOp, left, right, "binary"); \
case kFloat_TypeKind: \
return FFunc(builder, FOp, left, right, "binary"); \
default: \
ABORT("unsupported typeKind"); \
} \
}
switch (b.fOperator) {
case Token::EQ: {
std::unique_ptr<LValue> lvalue = this->getLValue(builder, *b.fLeft);
LLVMValueRef result = this->compileExpression(builder, *b.fRight);
lvalue->store(builder, result);
return result;
}
case Token::PLUS:
BINARY(LLVMBuildAdd, LLVMBuildAdd, LLVMBuildFAdd);
case Token::MINUS:
BINARY(LLVMBuildSub, LLVMBuildSub, LLVMBuildFSub);
case Token::STAR:
BINARY(LLVMBuildMul, LLVMBuildMul, LLVMBuildFMul);
case Token::SLASH:
BINARY(LLVMBuildSDiv, LLVMBuildUDiv, LLVMBuildFDiv);
case Token::PERCENT:
BINARY(LLVMBuildSRem, LLVMBuildURem, LLVMBuildSRem);
case Token::BITWISEAND:
BINARY(LLVMBuildAnd, LLVMBuildAnd, LLVMBuildAnd);
case Token::BITWISEOR:
BINARY(LLVMBuildOr, LLVMBuildOr, LLVMBuildOr);
case Token::SHL:
BINARY(LLVMBuildShl, LLVMBuildShl, LLVMBuildShl);
case Token::SHR:
BINARY(LLVMBuildAShr, LLVMBuildLShr, LLVMBuildAShr);
case Token::PLUSEQ:
COMPOUND(LLVMBuildAdd, LLVMBuildAdd, LLVMBuildFAdd);
case Token::MINUSEQ:
COMPOUND(LLVMBuildSub, LLVMBuildSub, LLVMBuildFSub);
case Token::STAREQ:
COMPOUND(LLVMBuildMul, LLVMBuildMul, LLVMBuildFMul);
case Token::SLASHEQ:
COMPOUND(LLVMBuildSDiv, LLVMBuildUDiv, LLVMBuildFDiv);
case Token::BITWISEANDEQ:
COMPOUND(LLVMBuildAnd, LLVMBuildAnd, LLVMBuildAnd);
case Token::BITWISEOREQ:
COMPOUND(LLVMBuildOr, LLVMBuildOr, LLVMBuildOr);
case Token::EQEQ:
switch (b.fLeft->fType.kind()) {
case Type::kScalar_Kind:
COMPARE(LLVMBuildICmp, LLVMIntEQ,
LLVMBuildICmp, LLVMIntEQ,
LLVMBuildFCmp, LLVMRealOEQ);
case Type::kVector_Kind: {
LLVMValueRef left = this->compileExpression(builder, *b.fLeft);
LLVMValueRef right = this->compileExpression(builder, *b.fRight);
this->vectorize(builder, b, &left, &right);
LLVMValueRef value;
switch (this->typeKind(b.fLeft->fType)) {
case kInt_TypeKind:
value = LLVMBuildICmp(builder, LLVMIntEQ, left, right, "binary");
break;
case kUInt_TypeKind:
value = LLVMBuildICmp(builder, LLVMIntEQ, left, right, "binary");
break;
case kFloat_TypeKind:
value = LLVMBuildFCmp(builder, LLVMRealOEQ, left, right, "binary");
break;
default:
ABORT("unsupported typeKind");
}
LLVMValueRef args[1] = { value };
LLVMValueRef func;
switch (b.fLeft->fType.columns()) {
case 2: func = fFoldAnd2Func; break;
case 3: func = fFoldAnd3Func; break;
case 4: func = fFoldAnd4Func; break;
default:
SkASSERT(false);
func = fFoldAnd2Func;
}
return LLVMBuildCall(builder, func, args, 1, "all");
}
default:
SkASSERT(false);
}
case Token::NEQ:
switch (b.fLeft->fType.kind()) {
case Type::kScalar_Kind:
COMPARE(LLVMBuildICmp, LLVMIntNE,
LLVMBuildICmp, LLVMIntNE,
LLVMBuildFCmp, LLVMRealONE);
case Type::kVector_Kind: {
LLVMValueRef left = this->compileExpression(builder, *b.fLeft);
LLVMValueRef right = this->compileExpression(builder, *b.fRight);
this->vectorize(builder, b, &left, &right);
LLVMValueRef value;
switch (this->typeKind(b.fLeft->fType)) {
case kInt_TypeKind:
value = LLVMBuildICmp(builder, LLVMIntNE, left, right, "binary");
break;
case kUInt_TypeKind:
value = LLVMBuildICmp(builder, LLVMIntNE, left, right, "binary");
break;
case kFloat_TypeKind:
value = LLVMBuildFCmp(builder, LLVMRealONE, left, right, "binary");
break;
default:
ABORT("unsupported typeKind");
}
LLVMValueRef args[1] = { value };
LLVMValueRef func;
switch (b.fLeft->fType.columns()) {
case 2: func = fFoldOr2Func; break;
case 3: func = fFoldOr3Func; break;
case 4: func = fFoldOr4Func; break;
default:
SkASSERT(false);
func = fFoldOr2Func;
}
return LLVMBuildCall(builder, func, args, 1, "all");
}
default:
SkASSERT(false);
}
case Token::LT:
COMPARE(LLVMBuildICmp, LLVMIntSLT,
LLVMBuildICmp, LLVMIntULT,
LLVMBuildFCmp, LLVMRealOLT);
case Token::LTEQ:
COMPARE(LLVMBuildICmp, LLVMIntSLE,
LLVMBuildICmp, LLVMIntULE,
LLVMBuildFCmp, LLVMRealOLE);
case Token::GT:
COMPARE(LLVMBuildICmp, LLVMIntSGT,
LLVMBuildICmp, LLVMIntUGT,
LLVMBuildFCmp, LLVMRealOGT);
case Token::GTEQ:
COMPARE(LLVMBuildICmp, LLVMIntSGE,
LLVMBuildICmp, LLVMIntUGE,
LLVMBuildFCmp, LLVMRealOGE);
case Token::LOGICALAND: {
LLVMValueRef left = this->compileExpression(builder, *b.fLeft);
LLVMBasicBlockRef ifFalse = fCurrentBlock;
LLVMBasicBlockRef ifTrue = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"true && ...");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"&& merge");
LLVMBuildCondBr(builder, left, ifTrue, merge);
this->setBlock(builder, ifTrue);
LLVMValueRef right = this->compileExpression(builder, *b.fRight);
LLVMBuildBr(builder, merge);
this->setBlock(builder, merge);
LLVMValueRef phi = LLVMBuildPhi(builder, fInt1Type, "&&");
LLVMValueRef incomingValues[2] = { right, LLVMConstInt(fInt1Type, 0, false) };
LLVMBasicBlockRef incomingBlocks[2] = { ifTrue, ifFalse };
LLVMAddIncoming(phi, incomingValues, incomingBlocks, 2);
return phi;
}
case Token::LOGICALOR: {
LLVMValueRef left = this->compileExpression(builder, *b.fLeft);
LLVMBasicBlockRef ifTrue = fCurrentBlock;
LLVMBasicBlockRef ifFalse = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"false || ...");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"|| merge");
LLVMBuildCondBr(builder, left, merge, ifFalse);
this->setBlock(builder, ifFalse);
LLVMValueRef right = this->compileExpression(builder, *b.fRight);
LLVMBuildBr(builder, merge);
this->setBlock(builder, merge);
LLVMValueRef phi = LLVMBuildPhi(builder, fInt1Type, "||");
LLVMValueRef incomingValues[2] = { right, LLVMConstInt(fInt1Type, 1, false) };
LLVMBasicBlockRef incomingBlocks[2] = { ifFalse, ifTrue };
LLVMAddIncoming(phi, incomingValues, incomingBlocks, 2);
return phi;
}
default:
printf("%s\n", b.description().c_str());
ABORT("unsupported binary operator");
}
}
LLVMValueRef JIT::compileIndex(LLVMBuilderRef builder, const IndexExpression& idx) {
LLVMValueRef base = this->compileExpression(builder, *idx.fBase);
LLVMValueRef index = this->compileExpression(builder, *idx.fIndex);
LLVMValueRef ptr = LLVMBuildGEP(builder, base, &index, 1, "index ptr");
return LLVMBuildLoad(builder, ptr, "index load");
}
LLVMValueRef JIT::compilePostfix(LLVMBuilderRef builder, const PostfixExpression& p) {
std::unique_ptr<LValue> lvalue = this->getLValue(builder, *p.fOperand);
LLVMValueRef result = lvalue->load(builder);
LLVMValueRef mod;
LLVMValueRef one = LLVMConstInt(this->getType(p.fType), 1, false);
switch (p.fOperator) {
case Token::PLUSPLUS:
switch (this->typeKind(p.fType)) {
case kInt_TypeKind: // fall through
case kUInt_TypeKind:
mod = LLVMBuildAdd(builder, result, one, "++");
break;
case kFloat_TypeKind:
mod = LLVMBuildFAdd(builder, result, one, "++");
break;
default:
ABORT("unsupported typeKind");
}
break;
case Token::MINUSMINUS:
switch (this->typeKind(p.fType)) {
case kInt_TypeKind: // fall through
case kUInt_TypeKind:
mod = LLVMBuildSub(builder, result, one, "--");
break;
case kFloat_TypeKind:
mod = LLVMBuildFSub(builder, result, one, "--");
break;
default:
ABORT("unsupported typeKind");
}
break;
default:
ABORT("unsupported postfix op");
}
lvalue->store(builder, mod);
return result;
}
LLVMValueRef JIT::compilePrefix(LLVMBuilderRef builder, const PrefixExpression& p) {
LLVMValueRef one = LLVMConstInt(this->getType(p.fType), 1, false);
if (Token::LOGICALNOT == p.fOperator) {
LLVMValueRef base = this->compileExpression(builder, *p.fOperand);
return LLVMBuildXor(builder, base, one, "!");
}
if (Token::MINUS == p.fOperator) {
LLVMValueRef base = this->compileExpression(builder, *p.fOperand);
return LLVMBuildSub(builder, LLVMConstInt(this->getType(p.fType), 0, false), base, "-");
}
std::unique_ptr<LValue> lvalue = this->getLValue(builder, *p.fOperand);
LLVMValueRef raw = lvalue->load(builder);
LLVMValueRef result;
switch (p.fOperator) {
case Token::PLUSPLUS:
switch (this->typeKind(p.fType)) {
case kInt_TypeKind: // fall through
case kUInt_TypeKind:
result = LLVMBuildAdd(builder, raw, one, "++");
break;
case kFloat_TypeKind:
result = LLVMBuildFAdd(builder, raw, one, "++");
break;
default:
ABORT("unsupported typeKind");
}
break;
case Token::MINUSMINUS:
switch (this->typeKind(p.fType)) {
case kInt_TypeKind: // fall through
case kUInt_TypeKind:
result = LLVMBuildSub(builder, raw, one, "--");
break;
case kFloat_TypeKind:
result = LLVMBuildFSub(builder, raw, one, "--");
break;
default:
ABORT("unsupported typeKind");
}
break;
default:
ABORT("unsupported prefix op");
}
lvalue->store(builder, result);
return result;
}
LLVMValueRef JIT::compileVariableReference(LLVMBuilderRef builder, const VariableReference& v) {
const Variable& var = v.fVariable;
if (Variable::kParameter_Storage == var.fStorage &&
!(var.fModifiers.fFlags & Modifiers::kOut_Flag) &&
fPromotedParameters.find(&var) == fPromotedParameters.end()) {
return fVariables[&var];
}
return LLVMBuildLoad(builder, fVariables[&var], String(var.fName).c_str());
}
void JIT::appendStage(LLVMBuilderRef builder, const AppendStage& a) {
SkASSERT(a.fArguments.size() >= 1);
SkASSERT(a.fArguments[0]->fType == *fCompiler.context().fSkRasterPipeline_Type);
LLVMValueRef pipeline = this->compileExpression(builder, *a.fArguments[0]);
LLVMValueRef stage = LLVMConstInt(fInt32Type, a.fStage, 0);
switch (a.fStage) {
case SkRasterPipeline::callback: {
SkASSERT(a.fArguments.size() == 2);
SkASSERT(a.fArguments[1]->fKind == Expression::kFunctionReference_Kind);
const FunctionDeclaration& functionDecl =
*((FunctionReference&) *a.fArguments[1]).fFunctions[0];
bool found = false;
for (const auto& pe : *fProgram) {
if (ProgramElement::kFunction_Kind == pe.fKind) {
const FunctionDefinition& def = (const FunctionDefinition&) pe;
if (&def.fDeclaration == &functionDecl) {
LLVMValueRef fn = this->compileStageFunction(def);
LLVMValueRef args[2] = {
pipeline,
LLVMBuildBitCast(builder, fn, fInt8PtrType, "callback cast")
};
LLVMBuildCall(builder, fAppendCallbackFunc, args, 2, "");
found = true;
break;
}
}
}
SkASSERT(found);
break;
}
default: {
LLVMValueRef ctx;
if (a.fArguments.size() == 2) {
ctx = this->compileExpression(builder, *a.fArguments[1]);
ctx = LLVMBuildBitCast(builder, ctx, fInt8PtrType, "context cast");
} else {
SkASSERT(a.fArguments.size() == 1);
ctx = LLVMConstNull(fInt8PtrType);
}
LLVMValueRef args[3] = {
pipeline,
stage,
ctx
};
LLVMBuildCall(builder, fAppendFunc, args, 3, "");
break;
}
}
}
LLVMValueRef JIT::compileConstructor(LLVMBuilderRef builder, const Constructor& c) {
switch (c.fType.kind()) {
case Type::kScalar_Kind: {
SkASSERT(c.fArguments.size() == 1);
TypeKind from = this->typeKind(c.fArguments[0]->fType);
TypeKind to = this->typeKind(c.fType);
LLVMValueRef base = this->compileExpression(builder, *c.fArguments[0]);
switch (to) {
case kFloat_TypeKind:
switch (from) {
case kInt_TypeKind:
return LLVMBuildSIToFP(builder, base, this->getType(c.fType), "cast");
case kUInt_TypeKind:
return LLVMBuildUIToFP(builder, base, this->getType(c.fType), "cast");
case kFloat_TypeKind:
return base;
case kBool_TypeKind:
SkASSERT(false);
}
case kInt_TypeKind:
switch (from) {
case kInt_TypeKind:
return base;
case kUInt_TypeKind:
return base;
case kFloat_TypeKind:
return LLVMBuildFPToSI(builder, base, this->getType(c.fType), "cast");
case kBool_TypeKind:
SkASSERT(false);
}
case kUInt_TypeKind:
switch (from) {
case kInt_TypeKind:
return base;
case kUInt_TypeKind:
return base;
case kFloat_TypeKind:
return LLVMBuildFPToUI(builder, base, this->getType(c.fType), "cast");
case kBool_TypeKind:
SkASSERT(false);
}
case kBool_TypeKind:
SkASSERT(false);
}
}
case Type::kVector_Kind: {
LLVMValueRef vec = LLVMGetUndef(this->getType(c.fType));
if (c.fArguments.size() == 1 && c.fArguments[0]->fType.kind() == Type::kScalar_Kind) {
LLVMValueRef value = this->compileExpression(builder, *c.fArguments[0]);
for (int i = 0; i < c.fType.columns(); ++i) {
vec = LLVMBuildInsertElement(builder, vec, value,
LLVMConstInt(fInt32Type, i, false),
"vec build 1");
}
} else {
int index = 0;
for (const auto& arg : c.fArguments) {
LLVMValueRef value = this->compileExpression(builder, *arg);
if (arg->fType.kind() == Type::kVector_Kind) {
for (int i = 0; i < arg->fType.columns(); ++i) {
LLVMValueRef column = LLVMBuildExtractElement(builder,
vec,
LLVMConstInt(fInt32Type,
i,
false),
"construct extract");
vec = LLVMBuildInsertElement(builder, vec, column,
LLVMConstInt(fInt32Type, index++, false),
"vec build 2");
}
} else {
vec = LLVMBuildInsertElement(builder, vec, value,
LLVMConstInt(fInt32Type, index++, false),
"vec build 3");
}
}
}
return vec;
}
default:
break;
}
ABORT("unsupported constructor");
}
LLVMValueRef JIT::compileSwizzle(LLVMBuilderRef builder, const Swizzle& s) {
LLVMValueRef base = this->compileExpression(builder, *s.fBase);
if (s.fComponents.size() > 1) {
LLVMValueRef result = LLVMGetUndef(this->getType(s.fType));
for (size_t i = 0; i < s.fComponents.size(); ++i) {
LLVMValueRef element = LLVMBuildExtractElement(
builder,
base,
LLVMConstInt(fInt32Type,
s.fComponents[i],
false),
"swizzle extract");
result = LLVMBuildInsertElement(builder, result, element,
LLVMConstInt(fInt32Type, i, false),
"swizzle insert");
}
return result;
}
SkASSERT(s.fComponents.size() == 1);
return LLVMBuildExtractElement(builder, base,
LLVMConstInt(fInt32Type,
s.fComponents[0],
false),
"swizzle extract");
}
LLVMValueRef JIT::compileTernary(LLVMBuilderRef builder, const TernaryExpression& t) {
LLVMValueRef test = this->compileExpression(builder, *t.fTest);
LLVMBasicBlockRef trueBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"if true");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"if merge");
LLVMBasicBlockRef falseBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"if false");
LLVMBuildCondBr(builder, test, trueBlock, falseBlock);
this->setBlock(builder, trueBlock);
LLVMValueRef ifTrue = this->compileExpression(builder, *t.fIfTrue);
trueBlock = fCurrentBlock;
LLVMBuildBr(builder, merge);
this->setBlock(builder, falseBlock);
LLVMValueRef ifFalse = this->compileExpression(builder, *t.fIfFalse);
falseBlock = fCurrentBlock;
LLVMBuildBr(builder, merge);
this->setBlock(builder, merge);
LLVMValueRef phi = LLVMBuildPhi(builder, this->getType(t.fType), "?");
LLVMValueRef incomingValues[2] = { ifTrue, ifFalse };
LLVMBasicBlockRef incomingBlocks[2] = { trueBlock, falseBlock };
LLVMAddIncoming(phi, incomingValues, incomingBlocks, 2);
return phi;
}
LLVMValueRef JIT::compileExpression(LLVMBuilderRef builder, const Expression& expr) {
switch (expr.fKind) {
case Expression::kAppendStage_Kind: {
this->appendStage(builder, (const AppendStage&) expr);
return LLVMValueRef();
}
case Expression::kBinary_Kind:
return this->compileBinary(builder, (BinaryExpression&) expr);
case Expression::kBoolLiteral_Kind:
return LLVMConstInt(fInt1Type, ((BoolLiteral&) expr).fValue, false);
case Expression::kConstructor_Kind:
return this->compileConstructor(builder, (Constructor&) expr);
case Expression::kIntLiteral_Kind:
return LLVMConstInt(this->getType(expr.fType), ((IntLiteral&) expr).fValue, true);
case Expression::kFieldAccess_Kind:
abort();
case Expression::kFloatLiteral_Kind:
return LLVMConstReal(this->getType(expr.fType), ((FloatLiteral&) expr).fValue);
case Expression::kFunctionCall_Kind:
return this->compileFunctionCall(builder, (FunctionCall&) expr);
case Expression::kIndex_Kind:
return this->compileIndex(builder, (IndexExpression&) expr);
case Expression::kPrefix_Kind:
return this->compilePrefix(builder, (PrefixExpression&) expr);
case Expression::kPostfix_Kind:
return this->compilePostfix(builder, (PostfixExpression&) expr);
case Expression::kSetting_Kind:
abort();
case Expression::kSwizzle_Kind:
return this->compileSwizzle(builder, (Swizzle&) expr);
case Expression::kVariableReference_Kind:
return this->compileVariableReference(builder, (VariableReference&) expr);
case Expression::kTernary_Kind:
return this->compileTernary(builder, (TernaryExpression&) expr);
case Expression::kTypeReference_Kind:
abort();
default:
abort();
}
ABORT("unsupported expression: %s\n", expr.description().c_str());
}
void JIT::compileBlock(LLVMBuilderRef builder, const Block& block) {
for (const auto& stmt : block.fStatements) {
this->compileStatement(builder, *stmt);
}
}
void JIT::compileVarDeclarations(LLVMBuilderRef builder, const VarDeclarationsStatement& decls) {
for (const auto& declStatement : decls.fDeclaration->fVars) {
const VarDeclaration& decl = (VarDeclaration&) *declStatement;
LLVMPositionBuilderAtEnd(builder, fAllocaBlock);
LLVMValueRef alloca = LLVMBuildAlloca(builder, this->getType(decl.fVar->fType),
String(decl.fVar->fName).c_str());
fVariables[decl.fVar] = alloca;
LLVMPositionBuilderAtEnd(builder, fCurrentBlock);
if (decl.fValue) {
LLVMValueRef result = this->compileExpression(builder, *decl.fValue);
LLVMBuildStore(builder, result, alloca);
}
}
}
void JIT::compileIf(LLVMBuilderRef builder, const IfStatement& i) {
LLVMValueRef test = this->compileExpression(builder, *i.fTest);
LLVMBasicBlockRef ifTrue = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "if true");
LLVMBasicBlockRef merge = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"if merge");
LLVMBasicBlockRef ifFalse;
if (i.fIfFalse) {
ifFalse = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "if false");
} else {
ifFalse = merge;
}
LLVMBuildCondBr(builder, test, ifTrue, ifFalse);
this->setBlock(builder, ifTrue);
this->compileStatement(builder, *i.fIfTrue);
if (!ends_with_branch(*i.fIfTrue)) {
LLVMBuildBr(builder, merge);
}
if (i.fIfFalse) {
this->setBlock(builder, ifFalse);
this->compileStatement(builder, *i.fIfFalse);
if (!ends_with_branch(*i.fIfFalse)) {
LLVMBuildBr(builder, merge);
}
}
this->setBlock(builder, merge);
}
void JIT::compileFor(LLVMBuilderRef builder, const ForStatement& f) {
if (f.fInitializer) {
this->compileStatement(builder, *f.fInitializer);
}
LLVMBasicBlockRef start;
LLVMBasicBlockRef body = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "for body");
LLVMBasicBlockRef next = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "for next");
LLVMBasicBlockRef end = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "for end");
if (f.fTest) {
start = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "for test");
LLVMBuildBr(builder, start);
this->setBlock(builder, start);
LLVMValueRef test = this->compileExpression(builder, *f.fTest);
LLVMBuildCondBr(builder, test, body, end);
} else {
start = body;
LLVMBuildBr(builder, body);
}
this->setBlock(builder, body);
fBreakTarget.push_back(end);
fContinueTarget.push_back(next);
this->compileStatement(builder, *f.fStatement);
fBreakTarget.pop_back();
fContinueTarget.pop_back();
if (!ends_with_branch(*f.fStatement)) {
LLVMBuildBr(builder, next);
}
this->setBlock(builder, next);
if (f.fNext) {
this->compileExpression(builder, *f.fNext);
}
LLVMBuildBr(builder, start);
this->setBlock(builder, end);
}
void JIT::compileDo(LLVMBuilderRef builder, const DoStatement& d) {
LLVMBasicBlockRef testBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"do test");
LLVMBasicBlockRef body = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"do body");
LLVMBasicBlockRef end = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"do end");
LLVMBuildBr(builder, body);
this->setBlock(builder, testBlock);
LLVMValueRef test = this->compileExpression(builder, *d.fTest);
LLVMBuildCondBr(builder, test, body, end);
this->setBlock(builder, body);
fBreakTarget.push_back(end);
fContinueTarget.push_back(body);
this->compileStatement(builder, *d.fStatement);
fBreakTarget.pop_back();
fContinueTarget.pop_back();
if (!ends_with_branch(*d.fStatement)) {
LLVMBuildBr(builder, testBlock);
}
this->setBlock(builder, end);
}
void JIT::compileWhile(LLVMBuilderRef builder, const WhileStatement& w) {
LLVMBasicBlockRef testBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"while test");
LLVMBasicBlockRef body = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"while body");
LLVMBasicBlockRef end = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction,
"while end");
LLVMBuildBr(builder, testBlock);
this->setBlock(builder, testBlock);
LLVMValueRef test = this->compileExpression(builder, *w.fTest);
LLVMBuildCondBr(builder, test, body, end);
this->setBlock(builder, body);
fBreakTarget.push_back(end);
fContinueTarget.push_back(testBlock);
this->compileStatement(builder, *w.fStatement);
fBreakTarget.pop_back();
fContinueTarget.pop_back();
if (!ends_with_branch(*w.fStatement)) {
LLVMBuildBr(builder, testBlock);
}
this->setBlock(builder, end);
}
void JIT::compileBreak(LLVMBuilderRef builder, const BreakStatement& b) {
LLVMBuildBr(builder, fBreakTarget.back());
}
void JIT::compileContinue(LLVMBuilderRef builder, const ContinueStatement& b) {
LLVMBuildBr(builder, fContinueTarget.back());
}
void JIT::compileReturn(LLVMBuilderRef builder, const ReturnStatement& r) {
if (r.fExpression) {
LLVMBuildRet(builder, this->compileExpression(builder, *r.fExpression));
} else {
LLVMBuildRetVoid(builder);
}
}
void JIT::compileStatement(LLVMBuilderRef builder, const Statement& stmt) {
switch (stmt.fKind) {
case Statement::kBlock_Kind:
this->compileBlock(builder, (Block&) stmt);
break;
case Statement::kBreak_Kind:
this->compileBreak(builder, (BreakStatement&) stmt);
break;
case Statement::kContinue_Kind:
this->compileContinue(builder, (ContinueStatement&) stmt);
break;
case Statement::kDiscard_Kind:
abort();
case Statement::kDo_Kind:
this->compileDo(builder, (DoStatement&) stmt);
break;
case Statement::kExpression_Kind:
this->compileExpression(builder, *((ExpressionStatement&) stmt).fExpression);
break;
case Statement::kFor_Kind:
this->compileFor(builder, (ForStatement&) stmt);
break;
case Statement::kGroup_Kind:
abort();
case Statement::kIf_Kind:
this->compileIf(builder, (IfStatement&) stmt);
break;
case Statement::kNop_Kind:
break;
case Statement::kReturn_Kind:
this->compileReturn(builder, (ReturnStatement&) stmt);
break;
case Statement::kSwitch_Kind:
abort();
case Statement::kVarDeclarations_Kind:
this->compileVarDeclarations(builder, (VarDeclarationsStatement&) stmt);
break;
case Statement::kWhile_Kind:
this->compileWhile(builder, (WhileStatement&) stmt);
break;
default:
abort();
}
}
void JIT::compileStageFunctionLoop(const FunctionDefinition& f, LLVMValueRef newFunc) {
// loop over fVectorCount pixels, running the body of the stage function for each of them
LLVMValueRef oldFunction = fCurrentFunction;
fCurrentFunction = newFunc;
std::unique_ptr<LLVMValueRef[]> params(new LLVMValueRef[STAGE_PARAM_COUNT]);
LLVMGetParams(fCurrentFunction, params.get());
LLVMValueRef programParam = params.get()[1];
LLVMBuilderRef builder = LLVMCreateBuilderInContext(fContext);
LLVMBasicBlockRef oldAllocaBlock = fAllocaBlock;
LLVMBasicBlockRef oldCurrentBlock = fCurrentBlock;
fAllocaBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "alloca");
this->setBlock(builder, fAllocaBlock);
// temporaries to store the color channel vectors
LLVMValueRef rVec = LLVMBuildAlloca(builder, fFloat32VectorType, "rVec");
LLVMBuildStore(builder, params.get()[4], rVec);
LLVMValueRef gVec = LLVMBuildAlloca(builder, fFloat32VectorType, "gVec");
LLVMBuildStore(builder, params.get()[5], gVec);
LLVMValueRef bVec = LLVMBuildAlloca(builder, fFloat32VectorType, "bVec");
LLVMBuildStore(builder, params.get()[6], bVec);
LLVMValueRef aVec = LLVMBuildAlloca(builder, fFloat32VectorType, "aVec");
LLVMBuildStore(builder, params.get()[7], aVec);
LLVMValueRef color = LLVMBuildAlloca(builder, fFloat32Vector4Type, "color");
fVariables[f.fDeclaration.fParameters[1]] = LLVMBuildTrunc(builder, params.get()[3], fInt32Type,
"y->Int32");
fVariables[f.fDeclaration.fParameters[2]] = color;
LLVMValueRef ivar = LLVMBuildAlloca(builder, fInt32Type, "i");
LLVMBuildStore(builder, LLVMConstInt(fInt32Type, 0, false), ivar);
LLVMBasicBlockRef start = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "start");
this->setBlock(builder, start);
LLVMValueRef iload = LLVMBuildLoad(builder, ivar, "load i");
fVariables[f.fDeclaration.fParameters[0]] = LLVMBuildAdd(builder,
LLVMBuildTrunc(builder,
params.get()[2],
fInt32Type,
"x->Int32"),
iload,
"x");
LLVMValueRef vectorSize = LLVMConstInt(fInt32Type, fVectorCount, false);
LLVMValueRef test = LLVMBuildICmp(builder, LLVMIntSLT, iload, vectorSize, "i < vectorSize");
LLVMBasicBlockRef loopBody = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "body");
LLVMBasicBlockRef loopEnd = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "end");
LLVMBuildCondBr(builder, test, loopBody, loopEnd);
this->setBlock(builder, loopBody);
LLVMValueRef vec = LLVMGetUndef(fFloat32Vector4Type);
// extract the r, g, b, and a values from the color channel vectors and store them into "color"
for (int i = 0; i < 4; ++i) {
vec = LLVMBuildInsertElement(builder, vec,
LLVMBuildExtractElement(builder,
params.get()[4 + i],
iload, "initial"),
LLVMConstInt(fInt32Type, i, false),
"vec build");
}
LLVMBuildStore(builder, vec, color);
// write actual loop body
this->compileStatement(builder, *f.fBody);
// extract the r, g, b, and a values from "color" and stick them back into the color channel
// vectors
LLVMValueRef colorLoad = LLVMBuildLoad(builder, color, "color load");
LLVMBuildStore(builder,
LLVMBuildInsertElement(builder, LLVMBuildLoad(builder, rVec, "rVec"),
LLVMBuildExtractElement(builder, colorLoad,
LLVMConstInt(fInt32Type, 0,
false),
"rExtract"),
iload, "rInsert"),
rVec);
LLVMBuildStore(builder,
LLVMBuildInsertElement(builder, LLVMBuildLoad(builder, gVec, "gVec"),
LLVMBuildExtractElement(builder, colorLoad,
LLVMConstInt(fInt32Type, 1,
false),
"gExtract"),
iload, "gInsert"),
gVec);
LLVMBuildStore(builder,
LLVMBuildInsertElement(builder, LLVMBuildLoad(builder, bVec, "bVec"),
LLVMBuildExtractElement(builder, colorLoad,
LLVMConstInt(fInt32Type, 2,
false),
"bExtract"),
iload, "bInsert"),
bVec);
LLVMBuildStore(builder,
LLVMBuildInsertElement(builder, LLVMBuildLoad(builder, aVec, "aVec"),
LLVMBuildExtractElement(builder, colorLoad,
LLVMConstInt(fInt32Type, 3,
false),
"aExtract"),
iload, "aInsert"),
aVec);
LLVMValueRef inc = LLVMBuildAdd(builder, iload, LLVMConstInt(fInt32Type, 1, false), "inc i");
LLVMBuildStore(builder, inc, ivar);
LLVMBuildBr(builder, start);
this->setBlock(builder, loopEnd);
// increment program pointer, call the next stage
LLVMValueRef rawNextPtr = LLVMBuildLoad(builder, programParam, "next load");
LLVMTypeRef stageFuncType = LLVMTypeOf(newFunc);
LLVMValueRef nextPtr = LLVMBuildBitCast(builder, rawNextPtr, stageFuncType, "cast next->func");
LLVMValueRef nextInc = LLVMBuildIntToPtr(builder,
LLVMBuildAdd(builder,
LLVMBuildPtrToInt(builder,
programParam,
fInt64Type,
"cast 1"),
LLVMConstInt(fInt64Type, PTR_SIZE, false),
"add"),
LLVMPointerType(fInt8PtrType, 0), "cast 2");
LLVMValueRef args[STAGE_PARAM_COUNT] = {
params.get()[0],
nextInc,
params.get()[2],
params.get()[3],
LLVMBuildLoad(builder, rVec, "rVec"),
LLVMBuildLoad(builder, gVec, "gVec"),
LLVMBuildLoad(builder, bVec, "bVec"),
LLVMBuildLoad(builder, aVec, "aVec"),
params.get()[8],
params.get()[9],
params.get()[10],
params.get()[11]
};
LLVMBuildCall(builder, nextPtr, args, STAGE_PARAM_COUNT, "");
LLVMBuildRetVoid(builder);
// finish
LLVMPositionBuilderAtEnd(builder, fAllocaBlock);
LLVMBuildBr(builder, start);
LLVMDisposeBuilder(builder);
if (LLVMVerifyFunction(fCurrentFunction, LLVMPrintMessageAction)) {
ABORT("verify failed\n");
}
fAllocaBlock = oldAllocaBlock;
fCurrentBlock = oldCurrentBlock;
fCurrentFunction = oldFunction;
}
// FIXME maybe pluggable code generators? Need to do something to separate all
// of the normal codegen from the vector codegen and break this up into multiple
// classes.
bool JIT::getVectorLValue(LLVMBuilderRef builder, const Expression& e,
LLVMValueRef out[CHANNELS]) {
switch (e.fKind) {
case Expression::kVariableReference_Kind:
if (fColorParam == &((VariableReference&) e).fVariable) {
memcpy(out, fChannels, sizeof(fChannels));
return true;
}
return false;
case Expression::kSwizzle_Kind: {
const Swizzle& s = (const Swizzle&) e;
LLVMValueRef base[CHANNELS];
if (!this->getVectorLValue(builder, *s.fBase, base)) {
return false;
}
for (size_t i = 0; i < s.fComponents.size(); ++i) {
out[i] = base[s.fComponents[i]];
}
return true;
}
default:
return false;
}
}
bool JIT::getVectorBinaryOperands(LLVMBuilderRef builder, const Expression& left,
LLVMValueRef outLeft[CHANNELS], const Expression& right,
LLVMValueRef outRight[CHANNELS]) {
if (!this->compileVectorExpression(builder, left, outLeft)) {
return false;
}
int leftColumns = left.fType.columns();
int rightColumns = right.fType.columns();
if (leftColumns == 1 && rightColumns > 1) {
for (int i = 1; i < rightColumns; ++i) {
outLeft[i] = outLeft[0];
}
}
if (!this->compileVectorExpression(builder, right, outRight)) {
return false;
}
if (rightColumns == 1 && leftColumns > 1) {
for (int i = 1; i < leftColumns; ++i) {
outRight[i] = outRight[0];
}
}
return true;
}
bool JIT::compileVectorBinary(LLVMBuilderRef builder, const BinaryExpression& b,
LLVMValueRef out[CHANNELS]) {
LLVMValueRef left[CHANNELS];
LLVMValueRef right[CHANNELS];
#define VECTOR_BINARY(signedOp, unsignedOp, floatOp) { \
if (!this->getVectorBinaryOperands(builder, *b.fLeft, left, *b.fRight, right)) { \
return false; \
} \
for (int i = 0; i < b.fLeft->fType.columns(); ++i) { \
switch (this->typeKind(b.fLeft->fType)) { \
case kInt_TypeKind: \
out[i] = signedOp(builder, left[i], right[i], "binary"); \
break; \
case kUInt_TypeKind: \
out[i] = unsignedOp(builder, left[i], right[i], "binary"); \
break; \
case kFloat_TypeKind: \
out[i] = floatOp(builder, left[i], right[i], "binary"); \
break; \
case kBool_TypeKind: \
SkASSERT(false); \
break; \
} \
} \
return true; \
}
switch (b.fOperator) {
case Token::EQ: {
if (!this->getVectorLValue(builder, *b.fLeft, left)) {
return false;
}
if (!this->compileVectorExpression(builder, *b.fRight, right)) {
return false;
}
int columns = b.fRight->fType.columns();
for (int i = 0; i < columns; ++i) {
LLVMBuildStore(builder, right[i], left[i]);
}
return true;
}
case Token::PLUS:
VECTOR_BINARY(LLVMBuildAdd, LLVMBuildAdd, LLVMBuildFAdd);
case Token::MINUS:
VECTOR_BINARY(LLVMBuildSub, LLVMBuildSub, LLVMBuildFSub);
case Token::STAR:
VECTOR_BINARY(LLVMBuildMul, LLVMBuildMul, LLVMBuildFMul);
case Token::SLASH:
VECTOR_BINARY(LLVMBuildSDiv, LLVMBuildUDiv, LLVMBuildFDiv);
case Token::PERCENT:
VECTOR_BINARY(LLVMBuildSRem, LLVMBuildURem, LLVMBuildSRem);
case Token::BITWISEAND:
VECTOR_BINARY(LLVMBuildAnd, LLVMBuildAnd, LLVMBuildAnd);
case Token::BITWISEOR:
VECTOR_BINARY(LLVMBuildOr, LLVMBuildOr, LLVMBuildOr);
default:
printf("unsupported operator: %s\n", b.description().c_str());
return false;
}
}
bool JIT::compileVectorConstructor(LLVMBuilderRef builder, const Constructor& c,
LLVMValueRef out[CHANNELS]) {
switch (c.fType.kind()) {
case Type::kScalar_Kind: {
SkASSERT(c.fArguments.size() == 1);
TypeKind from = this->typeKind(c.fArguments[0]->fType);
TypeKind to = this->typeKind(c.fType);
LLVMValueRef base[CHANNELS];
if (!this->compileVectorExpression(builder, *c.fArguments[0], base)) {
return false;
}
#define CONSTRUCT(fn) \
out[0] = LLVMGetUndef(LLVMVectorType(this->getType(c.fType), fVectorCount)); \
for (int i = 0; i < fVectorCount; ++i) { \
LLVMValueRef index = LLVMConstInt(fInt32Type, i, false); \
LLVMValueRef baseVal = LLVMBuildExtractElement(builder, base[0], index, \
"construct extract"); \
out[0] = LLVMBuildInsertElement(builder, out[0], \
fn(builder, baseVal, this->getType(c.fType), \
"cast"), \
index, "construct insert"); \
} \
return true;
if (kFloat_TypeKind == to) {
if (kInt_TypeKind == from) {
CONSTRUCT(LLVMBuildSIToFP);
}
if (kUInt_TypeKind == from) {
CONSTRUCT(LLVMBuildUIToFP);
}
}
if (kInt_TypeKind == to) {
if (kFloat_TypeKind == from) {
CONSTRUCT(LLVMBuildFPToSI);
}
if (kUInt_TypeKind == from) {
return true;
}
}
if (kUInt_TypeKind == to) {
if (kFloat_TypeKind == from) {
CONSTRUCT(LLVMBuildFPToUI);
}
if (kInt_TypeKind == from) {
return base;
}
}
printf("%s\n", c.description().c_str());
ABORT("unsupported constructor");
}
case Type::kVector_Kind: {
if (c.fArguments.size() == 1) {
LLVMValueRef base[CHANNELS];
if (!this->compileVectorExpression(builder, *c.fArguments[0], base)) {
return false;
}
for (int i = 0; i < c.fType.columns(); ++i) {
out[i] = base[0];
}
} else {
SkASSERT(c.fArguments.size() == (size_t) c.fType.columns());
for (int i = 0; i < c.fType.columns(); ++i) {
LLVMValueRef base[CHANNELS];
if (!this->compileVectorExpression(builder, *c.fArguments[i], base)) {
return false;
}
out[i] = base[0];
}
}
return true;
}
default:
break;
}
ABORT("unsupported constructor");
}
bool JIT::compileVectorFloatLiteral(LLVMBuilderRef builder,
const FloatLiteral& f,
LLVMValueRef out[CHANNELS]) {
LLVMValueRef value = LLVMConstReal(this->getType(f.fType), f.fValue);
LLVMValueRef values[MAX_VECTOR_COUNT];
for (int i = 0; i < fVectorCount; ++i) {
values[i] = value;
}
out[0] = LLVMConstVector(values, fVectorCount);
return true;
}
bool JIT::compileVectorSwizzle(LLVMBuilderRef builder, const Swizzle& s,
LLVMValueRef out[CHANNELS]) {
LLVMValueRef all[CHANNELS];
if (!this->compileVectorExpression(builder, *s.fBase, all)) {
return false;
}
for (size_t i = 0; i < s.fComponents.size(); ++i) {
out[i] = all[s.fComponents[i]];
}
return true;
}
bool JIT::compileVectorVariableReference(LLVMBuilderRef builder, const VariableReference& v,
LLVMValueRef out[CHANNELS]) {
if (&v.fVariable == fColorParam) {
for (int i = 0; i < CHANNELS; ++i) {
out[i] = LLVMBuildLoad(builder, fChannels[i], "variable reference");
}
return true;
}
return false;
}
bool JIT::compileVectorExpression(LLVMBuilderRef builder, const Expression& expr,
LLVMValueRef out[CHANNELS]) {
switch (expr.fKind) {
case Expression::kBinary_Kind:
return this->compileVectorBinary(builder, (const BinaryExpression&) expr, out);
case Expression::kConstructor_Kind:
return this->compileVectorConstructor(builder, (const Constructor&) expr, out);
case Expression::kFloatLiteral_Kind:
return this->compileVectorFloatLiteral(builder, (const FloatLiteral&) expr, out);
case Expression::kSwizzle_Kind:
return this->compileVectorSwizzle(builder, (const Swizzle&) expr, out);
case Expression::kVariableReference_Kind:
return this->compileVectorVariableReference(builder, (const VariableReference&) expr,
out);
default:
return false;
}
}
bool JIT::compileVectorStatement(LLVMBuilderRef builder, const Statement& stmt) {
switch (stmt.fKind) {
case Statement::kBlock_Kind:
for (const auto& s : ((const Block&) stmt).fStatements) {
if (!this->compileVectorStatement(builder, *s)) {
return false;
}
}
return true;
case Statement::kExpression_Kind:
LLVMValueRef result;
return this->compileVectorExpression(builder,
*((const ExpressionStatement&) stmt).fExpression,
&result);
default:
return false;
}
}
bool JIT::compileStageFunctionVector(const FunctionDefinition& f, LLVMValueRef newFunc) {
LLVMValueRef oldFunction = fCurrentFunction;
fCurrentFunction = newFunc;
std::unique_ptr<LLVMValueRef[]> params(new LLVMValueRef[STAGE_PARAM_COUNT]);
LLVMGetParams(fCurrentFunction, params.get());
LLVMValueRef programParam = params.get()[1];
LLVMBuilderRef builder = LLVMCreateBuilderInContext(fContext);
LLVMBasicBlockRef oldAllocaBlock = fAllocaBlock;
LLVMBasicBlockRef oldCurrentBlock = fCurrentBlock;
fAllocaBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "alloca");
this->setBlock(builder, fAllocaBlock);
fChannels[0] = LLVMBuildAlloca(builder, fFloat32VectorType, "rVec");
LLVMBuildStore(builder, params.get()[4], fChannels[0]);
fChannels[1] = LLVMBuildAlloca(builder, fFloat32VectorType, "gVec");
LLVMBuildStore(builder, params.get()[5], fChannels[1]);
fChannels[2] = LLVMBuildAlloca(builder, fFloat32VectorType, "bVec");
LLVMBuildStore(builder, params.get()[6], fChannels[2]);
fChannels[3] = LLVMBuildAlloca(builder, fFloat32VectorType, "aVec");
LLVMBuildStore(builder, params.get()[7], fChannels[3]);
LLVMBasicBlockRef start = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "start");
this->setBlock(builder, start);
bool success = this->compileVectorStatement(builder, *f.fBody);
if (success) {
// increment program pointer, call next
LLVMValueRef rawNextPtr = LLVMBuildLoad(builder, programParam, "next load");
LLVMTypeRef stageFuncType = LLVMTypeOf(newFunc);
LLVMValueRef nextPtr = LLVMBuildBitCast(builder, rawNextPtr, stageFuncType,
"cast next->func");
LLVMValueRef nextInc = LLVMBuildIntToPtr(builder,
LLVMBuildAdd(builder,
LLVMBuildPtrToInt(builder,
programParam,
fInt64Type,
"cast 1"),
LLVMConstInt(fInt64Type, PTR_SIZE,
false),
"add"),
LLVMPointerType(fInt8PtrType, 0), "cast 2");
LLVMValueRef args[STAGE_PARAM_COUNT] = {
params.get()[0],
nextInc,
params.get()[2],
params.get()[3],
LLVMBuildLoad(builder, fChannels[0], "rVec"),
LLVMBuildLoad(builder, fChannels[1], "gVec"),
LLVMBuildLoad(builder, fChannels[2], "bVec"),
LLVMBuildLoad(builder, fChannels[3], "aVec"),
params.get()[8],
params.get()[9],
params.get()[10],
params.get()[11]
};
LLVMBuildCall(builder, nextPtr, args, STAGE_PARAM_COUNT, "");
LLVMBuildRetVoid(builder);
// finish
LLVMPositionBuilderAtEnd(builder, fAllocaBlock);
LLVMBuildBr(builder, start);
LLVMDisposeBuilder(builder);
if (LLVMVerifyFunction(fCurrentFunction, LLVMPrintMessageAction)) {
ABORT("verify failed\n");
}
} else {
LLVMDeleteBasicBlock(fAllocaBlock);
LLVMDeleteBasicBlock(start);
}
fAllocaBlock = oldAllocaBlock;
fCurrentBlock = oldCurrentBlock;
fCurrentFunction = oldFunction;
return success;
}
LLVMValueRef JIT::compileStageFunction(const FunctionDefinition& f) {
LLVMTypeRef returnType = fVoidType;
LLVMTypeRef parameterTypes[12] = { fSizeTType, LLVMPointerType(fInt8PtrType, 0), fSizeTType,
fSizeTType, fFloat32VectorType, fFloat32VectorType,
fFloat32VectorType, fFloat32VectorType, fFloat32VectorType,
fFloat32VectorType, fFloat32VectorType, fFloat32VectorType };
LLVMTypeRef stageFuncType = LLVMFunctionType(returnType, parameterTypes, 12, false);
LLVMValueRef result = LLVMAddFunction(fModule,
(String(f.fDeclaration.fName) + "$stage").c_str(),
stageFuncType);
fColorParam = f.fDeclaration.fParameters[2];
if (!this->compileStageFunctionVector(f, result)) {
// vectorization failed, fall back to looping over the pixels
this->compileStageFunctionLoop(f, result);
}
return result;
}
bool JIT::hasStageSignature(const FunctionDeclaration& f) {
return f.fReturnType == *fProgram->fContext->fVoid_Type &&
f.fParameters.size() == 3 &&
f.fParameters[0]->fType == *fProgram->fContext->fInt_Type &&
f.fParameters[0]->fModifiers.fFlags == 0 &&
f.fParameters[1]->fType == *fProgram->fContext->fInt_Type &&
f.fParameters[1]->fModifiers.fFlags == 0 &&
f.fParameters[2]->fType == *fProgram->fContext->fHalf4_Type &&
f.fParameters[2]->fModifiers.fFlags == (Modifiers::kIn_Flag | Modifiers::kOut_Flag);
}
LLVMValueRef JIT::compileFunction(const FunctionDefinition& f) {
if (this->hasStageSignature(f.fDeclaration)) {
this->compileStageFunction(f);
// we compile foo$stage *in addition* to compiling foo, as we can't be sure that the intent
// was to produce an SkJumper stage just because the signature matched or that the function
// is not otherwise called. May need a better way to handle this.
}
LLVMTypeRef returnType = this->getType(f.fDeclaration.fReturnType);
std::vector<LLVMTypeRef> parameterTypes;
for (const auto& p : f.fDeclaration.fParameters) {
LLVMTypeRef type = this->getType(p->fType);
if (p->fModifiers.fFlags & Modifiers::kOut_Flag) {
type = LLVMPointerType(type, 0);
}
parameterTypes.push_back(type);
}
fCurrentFunction = LLVMAddFunction(fModule,
String(f.fDeclaration.fName).c_str(),
LLVMFunctionType(returnType, parameterTypes.data(),
parameterTypes.size(), false));
fFunctions[&f.fDeclaration] = fCurrentFunction;
std::unique_ptr<LLVMValueRef[]> params(new LLVMValueRef[parameterTypes.size()]);
LLVMGetParams(fCurrentFunction, params.get());
for (size_t i = 0; i < f.fDeclaration.fParameters.size(); ++i) {
fVariables[f.fDeclaration.fParameters[i]] = params.get()[i];
}
LLVMBuilderRef builder = LLVMCreateBuilderInContext(fContext);
fAllocaBlock = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "alloca");
LLVMBasicBlockRef start = LLVMAppendBasicBlockInContext(fContext, fCurrentFunction, "start");
fCurrentBlock = start;
LLVMPositionBuilderAtEnd(builder, fCurrentBlock);
this->compileStatement(builder, *f.fBody);
if (!ends_with_branch(*f.fBody)) {
if (f.fDeclaration.fReturnType == *fProgram->fContext->fVoid_Type) {
LLVMBuildRetVoid(builder);
} else {
LLVMBuildUnreachable(builder);
}
}
LLVMPositionBuilderAtEnd(builder, fAllocaBlock);
LLVMBuildBr(builder, start);
LLVMDisposeBuilder(builder);
if (LLVMVerifyFunction(fCurrentFunction, LLVMPrintMessageAction)) {
ABORT("verify failed\n");
}
return fCurrentFunction;
}
void JIT::createModule() {
fPromotedParameters.clear();
fModule = LLVMModuleCreateWithNameInContext("skslmodule", fContext);
this->loadBuiltinFunctions();
LLVMTypeRef fold2Params[1] = { fInt1Vector2Type };
fFoldAnd2Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.and.i1.v2i1",
LLVMFunctionType(fInt1Type, fold2Params, 1, false));
fFoldOr2Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.or.i1.v2i1",
LLVMFunctionType(fInt1Type, fold2Params, 1, false));
LLVMTypeRef fold3Params[1] = { fInt1Vector3Type };
fFoldAnd3Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.and.i1.v3i1",
LLVMFunctionType(fInt1Type, fold3Params, 1, false));
fFoldOr3Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.or.i1.v3i1",
LLVMFunctionType(fInt1Type, fold3Params, 1, false));
LLVMTypeRef fold4Params[1] = { fInt1Vector4Type };
fFoldAnd4Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.and.i1.v4i1",
LLVMFunctionType(fInt1Type, fold4Params, 1, false));
fFoldOr4Func = LLVMAddFunction(fModule, "llvm.experimental.vector.reduce.or.i1.v4i1",
LLVMFunctionType(fInt1Type, fold4Params, 1, false));
// LLVM doesn't do void*, have to declare it as int8*
LLVMTypeRef appendParams[3] = { fInt8PtrType, fInt32Type, fInt8PtrType };
fAppendFunc = LLVMAddFunction(fModule, "sksl_pipeline_append", LLVMFunctionType(fVoidType,
appendParams,
3,
false));
LLVMTypeRef appendCallbackParams[2] = { fInt8PtrType, fInt8PtrType };
fAppendCallbackFunc = LLVMAddFunction(fModule, "sksl_pipeline_append_callback",
LLVMFunctionType(fVoidType, appendCallbackParams, 2,
false));
LLVMTypeRef debugParams[3] = { fFloat32Type };
fDebugFunc = LLVMAddFunction(fModule, "sksl_debug_print", LLVMFunctionType(fVoidType,
debugParams,
1,
false));
for (const auto& e : *fProgram) {
if (e.fKind == ProgramElement::kFunction_Kind) {
this->compileFunction((FunctionDefinition&) e);
}
}
}
std::unique_ptr<JIT::Module> JIT::compile(std::unique_ptr<Program> program) {
fCompiler.optimize(*program);
fProgram = std::move(program);
this->createModule();
this->optimize();
return std::unique_ptr<Module>(new Module(std::move(fProgram), fSharedModule, fJITStack));
}
void JIT::optimize() {
LLVMPassManagerBuilderRef pmb = LLVMPassManagerBuilderCreate();
LLVMPassManagerBuilderSetOptLevel(pmb, 3);
LLVMPassManagerRef functionPM = LLVMCreateFunctionPassManagerForModule(fModule);
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, functionPM);
LLVMPassManagerRef modulePM = LLVMCreatePassManager();
LLVMPassManagerBuilderPopulateModulePassManager(pmb, modulePM);
LLVMInitializeFunctionPassManager(functionPM);
LLVMValueRef func = LLVMGetFirstFunction(fModule);
for (;;) {
if (!func) {
break;
}
LLVMRunFunctionPassManager(functionPM, func);
func = LLVMGetNextFunction(func);
}
LLVMRunPassManager(modulePM, fModule);
LLVMDisposePassManager(functionPM);
LLVMDisposePassManager(modulePM);
LLVMPassManagerBuilderDispose(pmb);
std::string error_string;
if (LLVMLoadLibraryPermanently(nullptr)) {
ABORT("LLVMLoadLibraryPermanently failed");
}
char* defaultTriple = LLVMGetDefaultTargetTriple();
char* error;
LLVMTargetRef target;
if (LLVMGetTargetFromTriple(defaultTriple, &target, &error)) {
ABORT("LLVMGetTargetFromTriple failed");
}
if (!LLVMTargetHasJIT(target)) {
ABORT("!LLVMTargetHasJIT");
}
LLVMTargetMachineRef targetMachine = LLVMCreateTargetMachine(target,
defaultTriple,
fCPU,
nullptr,
LLVMCodeGenLevelDefault,
LLVMRelocDefault,
LLVMCodeModelJITDefault);
LLVMDisposeMessage(defaultTriple);
LLVMTargetDataRef dataLayout = LLVMCreateTargetDataLayout(targetMachine);
LLVMSetModuleDataLayout(fModule, dataLayout);
LLVMDisposeTargetData(dataLayout);
fJITStack = LLVMOrcCreateInstance(targetMachine);
fSharedModule = LLVMOrcMakeSharedModule(fModule);
LLVMOrcModuleHandle orcModule;
LLVMOrcAddEagerlyCompiledIR(fJITStack, &orcModule, fSharedModule,
(LLVMOrcSymbolResolverFn) resolveSymbol, this);
LLVMDisposeTargetMachine(targetMachine);
}
void* JIT::Module::getSymbol(const char* name) {
LLVMOrcTargetAddress result;
if (LLVMOrcGetSymbolAddress(fJITStack, &result, name)) {
ABORT("GetSymbolAddress error");
}
if (!result) {
ABORT("symbol not found");
}
return (void*) result;
}
void* JIT::Module::getJumperStage(const char* name) {
return this->getSymbol((String(name) + "$stage").c_str());
}
} // namespace
#endif // SK_LLVM_AVAILABLE
#endif // SKSL_STANDALONE