SkSL interpreter intrinsics
Bug: skia: Change-Id: I418fb05444f9c1ee076ace41a24072c4a5e7ef6c Reviewed-on: https://skia-review.googlesource.com/c/skia/+/214691 Commit-Queue: Ethan Nicholas <ethannicholas@google.com> Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
parent
201491e734
commit
82162eead5
@ -50,6 +50,10 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
VECTOR(kCompareULTEQ),
|
||||
// Followed by a 16 bit address
|
||||
kConditionalBranch,
|
||||
VECTOR(kConvertFtoI),
|
||||
VECTOR(kConvertStoF),
|
||||
VECTOR(kConvertUtoF),
|
||||
VECTOR(kCos),
|
||||
// Pops and prints the top value from the stack
|
||||
kDebugPrint,
|
||||
VECTOR(kDivideF),
|
||||
@ -57,9 +61,6 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
VECTOR(kDivideU),
|
||||
// Duplicates the top stack value
|
||||
VECTOR(kDup),
|
||||
VECTOR(kFloatToInt),
|
||||
VECTOR(kSignedToFloat),
|
||||
VECTOR(kUnsignedToFloat),
|
||||
// All kLoad* are followed by a byte indicating the local/global slot to load
|
||||
VECTOR(kLoad),
|
||||
VECTOR(kLoadGlobal),
|
||||
@ -83,6 +84,8 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
VECTOR(kRemainderU),
|
||||
// Followed by a byte indicating the number of slots being returned
|
||||
kReturn,
|
||||
VECTOR(kSin),
|
||||
VECTOR(kSqrt),
|
||||
// All kStore* are followed by a byte indicating the local/global slot to store
|
||||
VECTOR(kStore),
|
||||
VECTOR(kStoreGlobal),
|
||||
@ -97,6 +100,7 @@ enum class ByteCodeInstruction : uint16_t {
|
||||
kSwizzle,
|
||||
VECTOR(kSubtractF),
|
||||
VECTOR(kSubtractI),
|
||||
VECTOR(kTan),
|
||||
VECTOR(kXorB),
|
||||
VECTOR(kXorI),
|
||||
// Followed by a byte indicating external value to write
|
||||
|
@ -10,6 +10,17 @@
|
||||
|
||||
namespace SkSL {
|
||||
|
||||
ByteCodeGenerator::ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
|
||||
ByteCode* output)
|
||||
: INHERITED(program, errors, nullptr)
|
||||
, fContext(*context)
|
||||
, fOutput(output) {
|
||||
fIntrinsics["cos"] = ByteCodeInstruction::kCos;
|
||||
fIntrinsics["sin"] = ByteCodeInstruction::kSin;
|
||||
fIntrinsics["sqrt"] = ByteCodeInstruction::kSqrt;
|
||||
fIntrinsics["tan"] = ByteCodeInstruction::kTan;
|
||||
}
|
||||
|
||||
static int slot_count(const Type& type) {
|
||||
return type.columns() * type.rows();
|
||||
}
|
||||
@ -331,15 +342,15 @@ void ByteCodeGenerator::writeConstructor(const Constructor& c) {
|
||||
if (inCategory == TypeCategory::kFloat) {
|
||||
SkASSERT(outCategory == TypeCategory::kSigned ||
|
||||
outCategory == TypeCategory::kUnsigned);
|
||||
this->write(vector_instruction(ByteCodeInstruction::kFloatToInt,
|
||||
this->write(vector_instruction(ByteCodeInstruction::kConvertFtoI,
|
||||
c.fType.columns()));
|
||||
} else if (outCategory == TypeCategory::kFloat) {
|
||||
if (inCategory == TypeCategory::kSigned) {
|
||||
this->write(vector_instruction(ByteCodeInstruction::kSignedToFloat,
|
||||
this->write(vector_instruction(ByteCodeInstruction::kConvertStoF,
|
||||
c.fType.columns()));
|
||||
} else {
|
||||
SkASSERT(inCategory == TypeCategory::kUnsigned);
|
||||
this->write(vector_instruction(ByteCodeInstruction::kUnsignedToFloat,
|
||||
this->write(vector_instruction(ByteCodeInstruction::kConvertUtoF,
|
||||
c.fType.columns()));
|
||||
}
|
||||
} else {
|
||||
@ -384,7 +395,31 @@ void ByteCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
|
||||
this->write32(Interpreter::Value((float) f.fValue).fUnsigned);
|
||||
}
|
||||
|
||||
void ByteCodeGenerator::writeIntrinsicCall(const FunctionCall& c) {
|
||||
auto found = fIntrinsics.find(c.fFunction.fName);
|
||||
if (found == fIntrinsics.end()) {
|
||||
fErrors.error(c.fOffset, "unsupported intrinsic function");
|
||||
return;
|
||||
}
|
||||
switch (found->second) {
|
||||
case ByteCodeInstruction::kCos: // fall through
|
||||
case ByteCodeInstruction::kSin: // fall through
|
||||
case ByteCodeInstruction::kSqrt: // fall through
|
||||
case ByteCodeInstruction::kTan:
|
||||
SkASSERT(c.fArguments.size() == 1);
|
||||
this->write((ByteCodeInstruction) ((int) found->second +
|
||||
slot_count(c.fArguments[0]->fType) - 1));
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ByteCodeGenerator::writeFunctionCall(const FunctionCall& f) {
|
||||
if (f.fFunction.fBuiltin) {
|
||||
this->writeIntrinsicCall(f);
|
||||
return;
|
||||
}
|
||||
for (const auto& arg : f.fArguments) {
|
||||
this->writeExpression(*arg);
|
||||
}
|
||||
|
@ -78,10 +78,7 @@ public:
|
||||
};
|
||||
|
||||
ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
|
||||
ByteCode* output)
|
||||
: INHERITED(program, errors, nullptr)
|
||||
, fContext(*context)
|
||||
, fOutput(output) {}
|
||||
ByteCode* output);
|
||||
|
||||
bool generateCode() override;
|
||||
|
||||
@ -188,6 +185,8 @@ private:
|
||||
*/
|
||||
std::unique_ptr<LValue> getLValue(const Expression& expr);
|
||||
|
||||
void writeIntrinsicCall(const FunctionCall& c);
|
||||
|
||||
void writeFunctionCall(const FunctionCall& c);
|
||||
|
||||
void writeConstructor(const Constructor& c);
|
||||
@ -266,6 +265,8 @@ private:
|
||||
|
||||
int fParameterCount;
|
||||
|
||||
std::unordered_map<String, ByteCodeInstruction> fIntrinsics;
|
||||
|
||||
friend class DeferredLocation;
|
||||
friend class ByteCodeVariableLValue;
|
||||
friend class ByteCodeSwizzleLValue;
|
||||
|
@ -128,12 +128,15 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
||||
case ByteCodeInstruction::kConditionalBranch:
|
||||
printf("conditionalbranch %d", READ16());
|
||||
break;
|
||||
VECTOR_DISASSEMBLE(kConvertFtoI, "convertftoi")
|
||||
VECTOR_DISASSEMBLE(kConvertStoF, "convertstof")
|
||||
VECTOR_DISASSEMBLE(kConvertUtoF, "convertutof")
|
||||
VECTOR_DISASSEMBLE(kCos, "cos")
|
||||
case ByteCodeInstruction::kDebugPrint: printf("debugprint"); break;
|
||||
VECTOR_DISASSEMBLE(kDivideF, "dividef")
|
||||
VECTOR_DISASSEMBLE(kDivideS, "divideS")
|
||||
VECTOR_DISASSEMBLE(kDivideU, "divideu")
|
||||
VECTOR_DISASSEMBLE(kDup, "dup")
|
||||
VECTOR_DISASSEMBLE(kFloatToInt, "floattoint")
|
||||
case ByteCodeInstruction::kLoad: printf("load %d", READ8()); break;
|
||||
case ByteCodeInstruction::kLoad2: printf("load2 %d", READ8()); break;
|
||||
case ByteCodeInstruction::kLoad3: printf("load3 %d", READ8()); break;
|
||||
@ -182,7 +185,8 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
||||
VECTOR_DISASSEMBLE(kRemainderS, "remainders")
|
||||
VECTOR_DISASSEMBLE(kRemainderU, "remainderu")
|
||||
case ByteCodeInstruction::kReturn: printf("return %d", READ8()); break;
|
||||
VECTOR_DISASSEMBLE(kSignedToFloat, "signedtofloat")
|
||||
VECTOR_DISASSEMBLE(kSin, "sin")
|
||||
VECTOR_DISASSEMBLE(kSqrt, "sqrt")
|
||||
case ByteCodeInstruction::kStore: printf("store %d", READ8()); break;
|
||||
case ByteCodeInstruction::kStore2: printf("store2 %d", READ8()); break;
|
||||
case ByteCodeInstruction::kStore3: printf("store3 %d", READ8()); break;
|
||||
@ -220,7 +224,7 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
VECTOR_DISASSEMBLE(kUnsignedToFloat, "unsignedtofloat")
|
||||
VECTOR_DISASSEMBLE(kTan, "tan")
|
||||
case ByteCodeInstruction::kWriteExternal: printf("writeexternal %d", READ8()); break;
|
||||
case ByteCodeInstruction::kWriteExternal2: printf("writeexternal2 %d", READ8()); break;
|
||||
case ByteCodeInstruction::kWriteExternal3: printf("writeexternal3 %d", READ8()); break;
|
||||
@ -275,6 +279,13 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
||||
break; \
|
||||
}
|
||||
|
||||
#define VECTOR_UNARY_FN(base, fn, field) \
|
||||
case ByteCodeInstruction::base ## 4: sp[-3].field = fn(sp[-3].field); \
|
||||
case ByteCodeInstruction::base ## 3: sp[-2].field = fn(sp[-2].field); \
|
||||
case ByteCodeInstruction::base ## 2: sp[-1].field = fn(sp[-1].field); \
|
||||
case ByteCodeInstruction::base: sp[ 0].field = fn(sp[ 0].field); \
|
||||
break;
|
||||
|
||||
struct StackFrame {
|
||||
const uint8_t* fCode;
|
||||
const uint8_t* fIP;
|
||||
@ -299,9 +310,11 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
switch (inst) {
|
||||
VECTOR_BINARY_OP(kAddI, fSigned, +)
|
||||
VECTOR_BINARY_OP(kAddF, fFloat, +)
|
||||
|
||||
case ByteCodeInstruction::kBranch:
|
||||
ip = code + READ16();
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kCall: {
|
||||
// Precursor code has pushed all parameters to the stack. Update our bottom of
|
||||
// stack to point at the first parameter, and our sp to point past those parameters
|
||||
@ -314,6 +327,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
sp = stack + fun->fParameterCount + fun->fLocalCount - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kCallExternal: {
|
||||
int argumentCount = READ8();
|
||||
int returnCount = READ8();
|
||||
@ -328,6 +342,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
sp += returnCount - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_OP(kCompareIEQ, fSigned, ==)
|
||||
VECTOR_BINARY_OP(kCompareFEQ, fFloat, ==)
|
||||
VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
|
||||
@ -344,6 +359,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
VECTOR_BINARY_OP(kCompareSLTEQ, fSigned, <=)
|
||||
VECTOR_BINARY_OP(kCompareULTEQ, fUnsigned, <=)
|
||||
VECTOR_BINARY_OP(kCompareFLTEQ, fFloat, <=)
|
||||
|
||||
case ByteCodeInstruction::kConditionalBranch: {
|
||||
int target = READ16();
|
||||
if (POP().fBool) {
|
||||
@ -351,11 +367,33 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kConvertFtoI4: sp[-3].fSigned = (int)sp[-3].fFloat;
|
||||
case ByteCodeInstruction::kConvertFtoI3: sp[-2].fSigned = (int)sp[-2].fFloat;
|
||||
case ByteCodeInstruction::kConvertFtoI2: sp[-1].fSigned = (int)sp[-1].fFloat;
|
||||
case ByteCodeInstruction::kConvertFtoI: sp[ 0].fSigned = (int)sp[ 0].fFloat;
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kConvertStoF4: sp[-3].fFloat = sp[-3].fSigned;
|
||||
case ByteCodeInstruction::kConvertStoF3: sp[-2].fFloat = sp[-2].fSigned;
|
||||
case ByteCodeInstruction::kConvertStoF2: sp[-1].fFloat = sp[-1].fSigned;
|
||||
case ByteCodeInstruction::kConvertStoF : sp[ 0].fFloat = sp[ 0].fSigned;
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kConvertUtoF4: sp[-3].fFloat = sp[-3].fUnsigned;
|
||||
case ByteCodeInstruction::kConvertUtoF3: sp[-2].fFloat = sp[-2].fUnsigned;
|
||||
case ByteCodeInstruction::kConvertUtoF2: sp[-1].fFloat = sp[-1].fUnsigned;
|
||||
case ByteCodeInstruction::kConvertUtoF : sp[ 0].fFloat = sp[ 0].fUnsigned;
|
||||
break;
|
||||
|
||||
VECTOR_UNARY_FN(kCos, cos, fFloat)
|
||||
|
||||
case ByteCodeInstruction::kDebugPrint: {
|
||||
Value v = POP();
|
||||
printf("Debug: %d(int), %d(uint), %f(float)\n", v.fSigned, v.fUnsigned, v.fFloat);
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_OP(kDivideS, fSigned, /)
|
||||
VECTOR_BINARY_OP(kDivideU, fUnsigned, /)
|
||||
VECTOR_BINARY_OP(kDivideF, fFloat, /)
|
||||
@ -366,24 +404,6 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
case ByteCodeInstruction::kDup : PUSH(sp[(int)ByteCodeInstruction::kDup - (int)inst]);
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kFloatToInt4: sp[-3].fSigned = (int)sp[-3].fFloat;
|
||||
case ByteCodeInstruction::kFloatToInt3: sp[-2].fSigned = (int)sp[-2].fFloat;
|
||||
case ByteCodeInstruction::kFloatToInt2: sp[-1].fSigned = (int)sp[-1].fFloat;
|
||||
case ByteCodeInstruction::kFloatToInt: sp[ 0].fSigned = (int)sp[ 0].fFloat;
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kSignedToFloat4: sp[-3].fFloat = sp[-3].fSigned;
|
||||
case ByteCodeInstruction::kSignedToFloat3: sp[-2].fFloat = sp[-2].fSigned;
|
||||
case ByteCodeInstruction::kSignedToFloat2: sp[-1].fFloat = sp[-1].fSigned;
|
||||
case ByteCodeInstruction::kSignedToFloat : sp[ 0].fFloat = sp[ 0].fSigned;
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kUnsignedToFloat4: sp[-3].fFloat = sp[-3].fUnsigned;
|
||||
case ByteCodeInstruction::kUnsignedToFloat3: sp[-2].fFloat = sp[-2].fUnsigned;
|
||||
case ByteCodeInstruction::kUnsignedToFloat2: sp[-1].fFloat = sp[-1].fUnsigned;
|
||||
case ByteCodeInstruction::kUnsignedToFloat : sp[ 0].fFloat = sp[ 0].fUnsigned;
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kLoad4: sp[4] = stack[*ip + 3];
|
||||
case ByteCodeInstruction::kLoad3: sp[3] = stack[*ip + 2];
|
||||
case ByteCodeInstruction::kLoad2: sp[2] = stack[*ip + 1];
|
||||
@ -409,6 +429,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
ip += count;
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kLoadSwizzleGlobal: {
|
||||
int src = READ8();
|
||||
SkASSERT(src < (int) fGlobals.size());
|
||||
@ -419,8 +440,10 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
ip += count;
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_OP(kMultiplyI, fSigned, *)
|
||||
VECTOR_BINARY_OP(kMultiplyF, fFloat, *)
|
||||
|
||||
case ByteCodeInstruction::kNot:
|
||||
sp[0].fBool = !sp[0].fBool;
|
||||
break;
|
||||
@ -446,6 +469,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
case ByteCodeInstruction::kPushImmediate:
|
||||
PUSH(READ32());
|
||||
break;
|
||||
|
||||
case ByteCodeInstruction::kReadExternal: // fall through
|
||||
case ByteCodeInstruction::kReadExternal2: // fall through
|
||||
case ByteCodeInstruction::kReadExternal3: // fall through
|
||||
@ -455,9 +479,11 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
sp += (int) inst - (int) ByteCodeInstruction::kReadExternal + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_FN(kRemainderF, fFloat, fmodf)
|
||||
VECTOR_BINARY_OP(kRemainderS, fSigned, %)
|
||||
VECTOR_BINARY_OP(kRemainderU, fUnsigned, %)
|
||||
|
||||
case ByteCodeInstruction::kReturn: {
|
||||
int count = READ8();
|
||||
if (frames.empty()) {
|
||||
@ -482,6 +508,9 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
}
|
||||
}
|
||||
|
||||
VECTOR_UNARY_FN(kSin, sin, fFloat)
|
||||
VECTOR_UNARY_FN(kSqrt, sqrt, fFloat)
|
||||
|
||||
case ByteCodeInstruction::kStore4: stack[*ip + 3] = POP();
|
||||
case ByteCodeInstruction::kStore3: stack[*ip + 2] = POP();
|
||||
case ByteCodeInstruction::kStore2: stack[*ip + 1] = POP();
|
||||
@ -505,6 +534,7 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
ip += count;
|
||||
break;
|
||||
}
|
||||
|
||||
case ByteCodeInstruction::kStoreSwizzleGlobal: {
|
||||
int target = READ8();
|
||||
int count = READ8();
|
||||
@ -514,8 +544,10 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
ip += count;
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_BINARY_OP(kSubtractI, fSigned, -)
|
||||
VECTOR_BINARY_OP(kSubtractF, fFloat, -)
|
||||
|
||||
case ByteCodeInstruction::kSwizzle: {
|
||||
Value tmp[4];
|
||||
for (int i = READ8() - 1; i >= 0; --i) {
|
||||
@ -526,6 +558,9 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
VECTOR_UNARY_FN(kTan, tan, fFloat)
|
||||
|
||||
case ByteCodeInstruction::kWriteExternal: // fall through
|
||||
case ByteCodeInstruction::kWriteExternal2: // fall through
|
||||
case ByteCodeInstruction::kWriteExternal3: // fall through
|
||||
@ -536,13 +571,14 @@ void Interpreter::innerRun(const ByteCodeFunction& f, Value* stack, Value* outRe
|
||||
sp -= count;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
|
||||
}
|
||||
#ifdef TRACE
|
||||
int stackSize = (int) (sp - stack + 1);
|
||||
printf("STACK(%d):", stackSize);
|
||||
for (int i = 0; i < stackSize(); ++i) {
|
||||
for (int i = 0; i < stackSize; ++i) {
|
||||
printf(" %d(%f)", stack[i].fSigned, stack[i].fFloat);
|
||||
}
|
||||
printf("\n");
|
||||
|
@ -701,3 +701,17 @@ DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
|
||||
printf("%s\n%s", src, compiler.errorText().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_TEST(SkSLInterpreterIntrinsics, r) {
|
||||
SkSL::Interpreter::Value value, expected;
|
||||
|
||||
value = 0.0f; expected = 0.0f;
|
||||
test(r, "float main(float x) { return sin(x); }", &value, 1, &expected);
|
||||
test(r, "float main(float x) { return tan(x); }", &value, 1, &expected);
|
||||
|
||||
value = 0.0f; expected = 1.0f;
|
||||
test(r, "float main(float x) { return cos(x); }", &value, 1, &expected);
|
||||
|
||||
value = 25.0f; expected = 5.0f;
|
||||
test(r, "float main(float x) { return sqrt(x); }", &value, 1, &expected);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user