Reland "Interpreter: Bounds check array access, add bool return from run"

This is a reland of f42de9e1e5

Original change's description:
> Interpreter: Bounds check array access, add bool return from run
> 
> Out of bounds access with constant indices is a compile error.
> At runtime, causes the interpreter to fail. Made several other
> conditions trigger the same failure logic, and updated all
> uses of the interpreter to validate success.
> 
> Change-Id: I3720b3c83903220b010ec574121fc64dbe102378
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228256
> Commit-Queue: Brian Osman <brianosman@google.com>
> Reviewed-by: Mike Reed <reed@google.com>

Change-Id: I8849de815f7efb730ac9c55b6edd296cb9ca7599
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/228353
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2019-07-18 17:00:34 -04:00 committed by Skia Commit-Bot
parent de71a74fc4
commit 869a3e81ea
8 changed files with 143 additions and 76 deletions

View File

@ -53,9 +53,10 @@ protected:
fPixels.data() + 3 * fCount,
};
fByteCode->runStriped(fMain, args, 4, fCount, nullptr, 0, nullptr, 0);
SkAssertResult(fByteCode->runStriped(fMain, args, 4, fCount,
nullptr, 0, nullptr, 0));
} else {
fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0);
SkAssertResult(fByteCode->run(fMain, fPixels.data(), nullptr, fCount, nullptr, 0));
}
}
}
@ -186,7 +187,7 @@ protected:
}
// Trigger one run now to check correctness
fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
for (int i = 0; i < fGroups; ++i) {
for (int j = 1; j < fValues; ++j) {
SkASSERT(fDst[i * fValues + j] >= fDst[i * fValues + j - 1]);
@ -196,7 +197,7 @@ protected:
void onDraw(int loops, SkCanvas*) override {
for (int i = 0; i < loops; i++) {
fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0);
SkAssertResult(fByteCode->run(fMain, fSrc.data(), fDst.data(), fGroups, nullptr, 0));
}
}

View File

@ -477,7 +477,7 @@ public:
void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
for (int i = 0; i < count; ++i) {
fRandomValue->setRandom(&ps[i].fRandom);
fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, &params.fDeltaTime, 2);
SkAssertResult(fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, &params.fDeltaTime, 2));
}
}

View File

@ -2584,8 +2584,8 @@ STAGE(interpreter, SkRasterPipeline_InterpreterCtx* c) {
sk_unaligned_store(aa, a);
}
c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
(const float*)c->inputs, c->ninputs, nullptr, 0);
SkAssertResult(c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
(const float*)c->inputs, c->ninputs, nullptr, 0));
r = sk_unaligned_load<F>(rr);
g = sk_unaligned_load<F>(gg);

View File

@ -59,6 +59,7 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
printf("callexternal %d, %d, %d", argumentCount, returnCount, externalValue);
break;
}
case ByteCodeInstruction::kClampIndex: printf("clampindex %d", READ8()); break;
VECTOR_DISASSEMBLE(kCompareIEQ, "compareieq")
VECTOR_DISASSEMBLE(kCompareINEQ, "compareineq")
VECTOR_MATRIX_DISASSEMBLE(kCompareFEQ, "comparefeq")
@ -356,7 +357,7 @@ static T vec_mod(T a, T b) {
#define spf(index) sp[index].fFloat
static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
static bool innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue* stack,
float* outReturn[], VValue globals[], bool stripedOutput, int N,
int baseIndex) {
// Needs to be the first N non-negative integers, at least as large as VecWidth
@ -387,8 +388,7 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
if (f->fConditionCount + 1 > (int)SK_ARRAY_COUNT(condStack) ||
f->fLoopCount + 1 > (int)SK_ARRAY_COUNT(loopStack)) {
SkDEBUGFAIL("Function with too much nested control flow to evaluate");
return;
return false;
}
auto mask = [&]() { return *maskPtr & *loopPtr; };
@ -468,6 +468,14 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
break;
}
case ByteCodeInstruction::kClampIndex: {
int length = READ8();
if (skvx::any(mask() & ((sp[0].fSigned < 0) | (sp[0].fSigned >= length)))) {
return false;
}
break;
}
VECTOR_BINARY_OP(kCompareIEQ, fSigned, ==)
VECTOR_MATRIX_BINARY_OP(kCompareFEQ, fFloat, ==)
VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
@ -841,7 +849,7 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
}
}
}
return;
return true;
} else {
// When we were called, the caller reserved stack space for their copy of our
// return value, then 'stack' was positioned after that, where our parameters
@ -1076,8 +1084,11 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
default:
SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
return false;
}
}
// Unreachable
return false;
}
} // namespace Interpreter
@ -1097,7 +1108,7 @@ void ByteCodeFunction::disassemble() const {
#endif
}
void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
bool ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int N,
const float* uniforms, int uniformCount) const {
#if defined(SK_ENABLE_SKSL_INTERPRETER)
#ifdef TRACE
@ -1106,13 +1117,17 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
Interpreter::VValue stack[128];
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
SkDEBUGFAIL("Function requires too much stack space to evaluate");
return;
return false;
}
if (uniformCount != (int)fInputSlots.size()) {
return false;
}
SkASSERT(uniformCount == (int)fInputSlots.size());
Interpreter::VValue globals[32];
SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
return false;
}
for (uint8_t slot : fInputSlots) {
globals[slot].fFloat = *uniforms++;
}
@ -1136,7 +1151,9 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
bool stripedOutput = false;
float** outArray = outReturn ? &outReturn : nullptr;
innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex);
if (!innerRun(this, f, stack, outArray, globals, stripedOutput, w, baseIndex)) {
return false;
}
// Transpose out parameters back
{
@ -1164,12 +1181,14 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
N -= w;
baseIndex += w;
}
return true;
#else
SkDEBUGFAIL("ByteCode interpreter not enabled");
return false;
#endif
}
void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
bool ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, int N,
const float* uniforms, int uniformCount,
float* outArgs[], int outCount) const {
#if defined(SK_ENABLE_SKSL_INTERPRETER)
@ -1179,8 +1198,21 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
Interpreter::VValue stack[128];
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
SkDEBUGFAIL("Function requires too much stack space to evaluate");
return;
return false;
}
if (nargs != f->fParameterCount ||
outCount != f->fReturnCount ||
uniformCount != (int)fInputSlots.size()) {
return false;
}
Interpreter::VValue globals[32];
if (fGlobalCount > (int)SK_ARRAY_COUNT(globals)) {
return false;
}
for (uint8_t slot : fInputSlots) {
globals[slot].fFloat = *uniforms++;
}
// innerRun just takes outArgs, so clear it if the count is zero
@ -1188,15 +1220,6 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
outArgs = nullptr;
}
SkASSERT(nargs == f->fParameterCount);
SkASSERT(outCount == f->fReturnCount);
SkASSERT(uniformCount == (int)fInputSlots.size());
Interpreter::VValue globals[32];
SkASSERT((int)SK_ARRAY_COUNT(globals) >= fGlobalCount);
for (uint8_t slot : fInputSlots) {
globals[slot].fFloat = *uniforms++;
}
int baseIndex = 0;
while (N) {
@ -1208,7 +1231,9 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
}
bool stripedOutput = true;
innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex);
if (!innerRun(this, f, stack, outArgs, globals, stripedOutput, w, baseIndex)) {
return false;
}
// Copy out parameters back
int slot = 0;
@ -1228,8 +1253,11 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
N -= w;
baseIndex += w;
}
return true;
#else
SkDEBUGFAIL("ByteCode interpreter not enabled");
return false;
#endif
}

View File

@ -32,6 +32,8 @@ enum class ByteCodeInstruction : uint16_t {
// Followed by three bytes indicating: the number of argument slots, the number of return slots,
// and the index of the external value to call
kCallExternal,
// For dynamic array access: Followed by byte indicating length of array
kClampIndex,
VECTOR(kCompareIEQ),
VECTOR(kCompareINEQ),
VECTOR_MATRIX(kCompareFEQ),
@ -198,15 +200,13 @@ struct SK_API ByteCode {
* The return value is stored in 'outReturn' (may be null, to discard the return value).
* 'uniforms' are mapped to 'uniform' globals, in order.
*/
void run(const ByteCodeFunction*, float* args, float* outReturn, int N,
const float* uniforms, int uniformCount) const;
bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*, float* args, float* outReturn, int N,
const float* uniforms, int uniformCount) const;
// For now, if outArgCount > 0, then we will memcpy VecWidth number of elements into
// each slot in outArgs. This may change in the future, as that may be more than is actually
// valid (depending on the N that was passed in.
void runStriped(const ByteCodeFunction*, float* args[], int nargs, int N,
const float* uniforms, int uniformCount,
float* outArgs[], int outArgCount) const;
bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*,
float* args[], int nargs, int N,
const float* uniforms, int uniformCount,
float* outArgs[], int outArgCount) const;
};
}

View File

@ -208,6 +208,7 @@ int ByteCodeGenerator::StackUsage(ByteCodeInstruction inst, int count_) {
case ByteCodeInstruction::kInverse3x3:
case ByteCodeInstruction::kInverse4x4: return 0;
case ByteCodeInstruction::kClampIndex: return 0;
case ByteCodeInstruction::kNotB: return 0;
case ByteCodeInstruction::kNegateFN: return 0;
@ -459,9 +460,16 @@ int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* st
case Expression::kIndex_Kind: {
const IndexExpression& i = (const IndexExpression&)expr;
int stride = SlotCount(i.fType);
int length = i.fBase->fType.columns();
SkASSERT(length <= 255);
int offset = -1;
if (i.fIndex->isConstant()) {
offset = i.fIndex->getConstantInt() * stride;
int64_t index = i.fIndex->getConstantInt();
if (index < 0 || index >= length) {
fErrors.error(i.fIndex->fOffset, "Array index out of bounds.");
return 0;
}
offset = index * stride;
} else {
if (i.fIndex->hasSideEffects()) {
// Having a side-effect in an indexer is technically safe for an rvalue,
@ -471,6 +479,8 @@ int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* st
return 0;
}
this->writeExpression(*i.fIndex);
this->write(ByteCodeInstruction::kClampIndex);
this->write8(length);
if (stride != 1) {
this->write(ByteCodeInstruction::kPushImmediate);
this->write32(stride);
@ -829,7 +839,7 @@ void ByteCodeGenerator::writeExternalValue(const ExternalValueReference& e) {
}
void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
Variable::Storage storage;
Variable::Storage storage = Variable::kLocal_Storage;
int location = this->getLocation(expr, &storage);
bool isGlobal = storage == Variable::kGlobal_Storage;
int count = SlotCount(expr.fType);
@ -1230,7 +1240,7 @@ public:
if (!discard) {
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
}
Variable::Storage storage;
Variable::Storage storage = Variable::kLocal_Storage;
int location = fGenerator.getLocation(*fSwizzle.fBase, &storage);
bool isGlobal = storage == Variable::kGlobal_Storage;
if (location < 0) {
@ -1275,7 +1285,7 @@ public:
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
}
}
Variable::Storage storage;
Variable::Storage storage = Variable::kLocal_Storage;
int location = fGenerator.getLocation(fExpression, &storage);
bool isGlobal = storage == Variable::kGlobal_Storage;
if (location < 0 || count > 4) {

View File

@ -29,12 +29,12 @@
#include "include/core/SkTypes.h"
#endif
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#if defined(__clang__) || defined(__GNUC__)
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define SKSL_PRINTF_LIKE(A, B)
#define SKSL_WARN_UNUSED_RESULT
#endif
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())

View File

@ -40,7 +40,7 @@ void test(skiatest::Reporter* r, const char* src, float* in, int expectedCount,
}
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
std::unique_ptr<float[]> out = std::unique_ptr<float[]>(new float[expectedCount]);
byteCode->run(main, in, out.get(), 1, nullptr, 0);
SkAssertResult(byteCode->run(main, in, out.get(), 1, nullptr, 0));
bool valid = exactCompare ? !memcmp(out.get(), expected, sizeof(float) * expectedCount)
: nearly_equal(out.get(), expected, expectedCount);
if (!valid) {
@ -95,11 +95,11 @@ void vec_test(skiatest::Reporter* r, const char* src) {
// First run in scalar mode to determine the expected output
for (int i = 0; i < 4; ++i) {
byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, out_s + i * 4, nullptr, 1, nullptr, 0));
}
// Now run in parallel and compare results
byteCode->run(main, out_v, nullptr, 4, nullptr, 0);
SkAssertResult(byteCode->run(main, out_v, nullptr, 4, nullptr, 0));
if (memcmp(out_s, out_v, sizeof(out_s)) != 0) {
printf("for program: %s\n", src);
for (int i = 0; i < 4; ++i) {
@ -131,7 +131,7 @@ void test(skiatest::Reporter* r, const char* src, float inR, float inG, float in
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
float inoutColor[4] = { inR, inG, inB, inA };
byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, inoutColor, nullptr, 1, nullptr, 0));
if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
printf("for program: %s\n", src);
@ -559,14 +559,14 @@ DEF_TEST(SkSLInterpreterCompound, r) {
{
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
int out = 0;
byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(rect_height, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
REPORTER_ASSERT(r, out == 30);
}
{
int in[2] = { 15, 25 };
RectAndColor out;
byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(make_blue_rect, (float*)in, (float*)&out, 1, (float*)gRects, 16));
REPORTER_ASSERT(r, out.fRect.width() == 15);
REPORTER_ASSERT(r, out.fRect.height() == 25);
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
@ -576,14 +576,14 @@ DEF_TEST(SkSLInterpreterCompound, r) {
{
int in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int out = 0;
byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(median, (float*)in, (float*)&out, 1, (float*)gRects, 16));
REPORTER_ASSERT(r, out == 8);
}
{
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
float out[8] = { 0 };
byteCode->run(sums, in, out, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(sums, in, out, 1, (float*)gRects, 16));
for (int i = 0; i < 8; ++i) {
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
}
@ -592,7 +592,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
{
int in = 2;
SkIRect out = SkIRect::MakeEmpty();
byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(get_rect, (float*)&in, (float*)&out, 1, (float*)gRects, 16));
REPORTER_ASSERT(r, out == gRects[2]);
}
@ -600,7 +600,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
ManyRects in;
memset(&in, 0, sizeof(in));
in.fNumRects = 2;
byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16);
SkAssertResult(byteCode->run(fill_rects, (float*)&in, nullptr, 1, (float*)gRects, 16));
ManyRects expected;
memset(&expected, 0, sizeof(expected));
expected.fNumRects = 2;
@ -613,27 +613,55 @@ DEF_TEST(SkSLInterpreterCompound, r) {
}
}
static void expect_failure(skiatest::Reporter* r, const char* src) {
SkSL::Compiler compiler;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
SkSL::Program::Settings());
REPORTER_ASSERT(r, program);
auto byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, compiler.errorCount() > 0);
REPORTER_ASSERT(r, !byteCode);
}
static void expect_run_failure(skiatest::Reporter* r, const char* src, float* in) {
SkSL::Compiler compiler;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
SkSL::Program::Settings());
REPORTER_ASSERT(r, program);
auto byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, byteCode);
bool result = byteCode->run(byteCode->getFunction("main"), in, nullptr, 1, nullptr, 0);
REPORTER_ASSERT(r, !result);
}
DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
auto check = [r](const char* src) {
SkSL::Compiler compiler;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, SkSL::String(src),
SkSL::Program::Settings());
REPORTER_ASSERT(r, program);
auto byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, compiler.errorCount() > 0);
REPORTER_ASSERT(r, !byteCode);
};
// Ensure that simple recursion is not allowed
check("float main() { return main() + 1; }");
expect_failure(r, "float main() { return main() + 1; }");
// Ensure that calls to undefined functions are not allowed (to prevent mutual recursion)
check("float foo(); float bar() { return foo(); } float foo() { return bar(); }");
expect_failure(r, "float foo(); float bar() { return foo(); } float foo() { return bar(); }");
// returns are not allowed inside conditionals (or loops, which are effectively the same thing)
check("float main(float x, float y) { if (x < y) { return x; } return y; }");
check("float main(float x) { while (x > 1) { return x; } return 0; }");
expect_failure(r, "float main(float x, float y) { if (x < y) { return x; } return y; }");
expect_failure(r, "float main(float x) { while (x > 1) { return x; } return 0; }");
}
DEF_TEST(SkSLInterpreterArrayBounds, r) {
// Out of bounds array access at compile time
expect_failure(r, "float main(float x[4]) { return x[-1]; }");
expect_failure(r, "float2 main(float2 x[2]) { return x[2]; }");
// Out of bounds array access at runtime is pinned, and we don't update any inout data
float in[3] = { -1.0f, 1.0f, 2.0f };
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
REPORTER_ASSERT(r, in[0] == -1.0f && in[1] == 1.0f && in[2] == 2.0f);
in[0] = 3.0f;
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
REPORTER_ASSERT(r, in[0] == 3.0f && in[1] == 1.0f && in[2] == 2.0f);
}
DEF_TEST(SkSLInterpreterFunctions, r) {
@ -674,13 +702,13 @@ DEF_TEST(SkSLInterpreterFunctions, r) {
float out = 0.0f;
float in = 3.0f;
byteCode->run(main, &in, &out, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, &in, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = 6.0f);
byteCode->run(dot3, &in, &out, 1, nullptr, 0);
SkAssertResult(byteCode->run(dot3, &in, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = 9.0f);
byteCode->run(dot2, &in, &out, 1, nullptr, 0);
SkAssertResult(byteCode->run(dot2, &in, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = -1.0f);
}
@ -910,7 +938,7 @@ DEF_TEST(SkSLInterpreterExternalValues, r) {
}
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
float out;
byteCode->run(main, nullptr, &out, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out == 66.0);
REPORTER_ASSERT(r, outValue == 152);
} else {
@ -942,7 +970,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVector, r) {
return;
}
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
byteCode->run(main, nullptr, nullptr, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, nullptr, nullptr, 1, nullptr, 0));
REPORTER_ASSERT(r, value[0] == 2);
REPORTER_ASSERT(r, value[1] == 4);
REPORTER_ASSERT(r, value[2] == 6);
@ -1008,7 +1036,7 @@ DEF_TEST(SkSLInterpreterExternalValuesCall, r) {
}
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
float out;
byteCode->run(main, nullptr, &out, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, nullptr, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out == 5.0);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
@ -1076,7 +1104,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
}
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
float out[4];
byteCode->run(main, nullptr, out, 1, nullptr, 0);
SkAssertResult(byteCode->run(main, nullptr, out, 1, nullptr, 0));
REPORTER_ASSERT(r, out[0] == 1.0);
REPORTER_ASSERT(r, out[1] == 2.0);
REPORTER_ASSERT(r, out[2] == 3.0);