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:
parent
de71a74fc4
commit
869a3e81ea
@ -53,9 +53,10 @@ protected:
|
|||||||
fPixels.data() + 3 * fCount,
|
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 {
|
} 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
|
// 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 i = 0; i < fGroups; ++i) {
|
||||||
for (int j = 1; j < fValues; ++j) {
|
for (int j = 1; j < fValues; ++j) {
|
||||||
SkASSERT(fDst[i * fValues + j] >= fDst[i * fValues + j - 1]);
|
SkASSERT(fDst[i * fValues + j] >= fDst[i * fValues + j - 1]);
|
||||||
@ -196,7 +197,7 @@ protected:
|
|||||||
|
|
||||||
void onDraw(int loops, SkCanvas*) override {
|
void onDraw(int loops, SkCanvas*) override {
|
||||||
for (int i = 0; i < loops; i++) {
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,7 +477,7 @@ public:
|
|||||||
void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
|
void onApply(const SkParticleUpdateParams& params, SkParticleState ps[], int count) override {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
fRandomValue->setRandom(&ps[i].fRandom);
|
fRandomValue->setRandom(&ps[i].fRandom);
|
||||||
fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, ¶ms.fDeltaTime, 2);
|
SkAssertResult(fByteCode->run(fMain, &ps[i].fAge, nullptr, 1, ¶ms.fDeltaTime, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2584,8 +2584,8 @@ STAGE(interpreter, SkRasterPipeline_InterpreterCtx* c) {
|
|||||||
sk_unaligned_store(aa, a);
|
sk_unaligned_store(aa, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
|
SkAssertResult(c->byteCode->runStriped(c->fn, in_args, in_count, tail ? tail : N,
|
||||||
(const float*)c->inputs, c->ninputs, nullptr, 0);
|
(const float*)c->inputs, c->ninputs, nullptr, 0));
|
||||||
|
|
||||||
r = sk_unaligned_load<F>(rr);
|
r = sk_unaligned_load<F>(rr);
|
||||||
g = sk_unaligned_load<F>(gg);
|
g = sk_unaligned_load<F>(gg);
|
||||||
|
@ -59,6 +59,7 @@ static const uint8_t* disassemble_instruction(const uint8_t* ip) {
|
|||||||
printf("callexternal %d, %d, %d", argumentCount, returnCount, externalValue);
|
printf("callexternal %d, %d, %d", argumentCount, returnCount, externalValue);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ByteCodeInstruction::kClampIndex: printf("clampindex %d", READ8()); break;
|
||||||
VECTOR_DISASSEMBLE(kCompareIEQ, "compareieq")
|
VECTOR_DISASSEMBLE(kCompareIEQ, "compareieq")
|
||||||
VECTOR_DISASSEMBLE(kCompareINEQ, "compareineq")
|
VECTOR_DISASSEMBLE(kCompareINEQ, "compareineq")
|
||||||
VECTOR_MATRIX_DISASSEMBLE(kCompareFEQ, "comparefeq")
|
VECTOR_MATRIX_DISASSEMBLE(kCompareFEQ, "comparefeq")
|
||||||
@ -356,7 +357,7 @@ static T vec_mod(T a, T b) {
|
|||||||
|
|
||||||
#define spf(index) sp[index].fFloat
|
#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,
|
float* outReturn[], VValue globals[], bool stripedOutput, int N,
|
||||||
int baseIndex) {
|
int baseIndex) {
|
||||||
// Needs to be the first N non-negative integers, at least as large as VecWidth
|
// 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) ||
|
if (f->fConditionCount + 1 > (int)SK_ARRAY_COUNT(condStack) ||
|
||||||
f->fLoopCount + 1 > (int)SK_ARRAY_COUNT(loopStack)) {
|
f->fLoopCount + 1 > (int)SK_ARRAY_COUNT(loopStack)) {
|
||||||
SkDEBUGFAIL("Function with too much nested control flow to evaluate");
|
return false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mask = [&]() { return *maskPtr & *loopPtr; };
|
auto mask = [&]() { return *maskPtr & *loopPtr; };
|
||||||
@ -468,6 +468,14 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
|||||||
break;
|
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_BINARY_OP(kCompareIEQ, fSigned, ==)
|
||||||
VECTOR_MATRIX_BINARY_OP(kCompareFEQ, fFloat, ==)
|
VECTOR_MATRIX_BINARY_OP(kCompareFEQ, fFloat, ==)
|
||||||
VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
|
VECTOR_BINARY_OP(kCompareINEQ, fSigned, !=)
|
||||||
@ -841,7 +849,7 @@ static void innerRun(const ByteCode* byteCode, const ByteCodeFunction* f, VValue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// When we were called, the caller reserved stack space for their copy of our
|
// 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
|
// 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:
|
default:
|
||||||
SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
|
SkDEBUGFAILF("unsupported instruction %d\n", (int) inst);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Unreachable
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Interpreter
|
} // namespace Interpreter
|
||||||
@ -1097,7 +1108,7 @@ void ByteCodeFunction::disassemble() const {
|
|||||||
#endif
|
#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 {
|
const float* uniforms, int uniformCount) const {
|
||||||
#if defined(SK_ENABLE_SKSL_INTERPRETER)
|
#if defined(SK_ENABLE_SKSL_INTERPRETER)
|
||||||
#ifdef TRACE
|
#ifdef TRACE
|
||||||
@ -1106,13 +1117,17 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
|||||||
Interpreter::VValue stack[128];
|
Interpreter::VValue stack[128];
|
||||||
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
||||||
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
||||||
SkDEBUGFAIL("Function requires too much stack space to evaluate");
|
return false;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
if (uniformCount != (int)fInputSlots.size()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkASSERT(uniformCount == (int)fInputSlots.size());
|
|
||||||
Interpreter::VValue globals[32];
|
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) {
|
for (uint8_t slot : fInputSlots) {
|
||||||
globals[slot].fFloat = *uniforms++;
|
globals[slot].fFloat = *uniforms++;
|
||||||
}
|
}
|
||||||
@ -1136,7 +1151,9 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
|||||||
|
|
||||||
bool stripedOutput = false;
|
bool stripedOutput = false;
|
||||||
float** outArray = outReturn ? &outReturn : nullptr;
|
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
|
// Transpose out parameters back
|
||||||
{
|
{
|
||||||
@ -1164,12 +1181,14 @@ void ByteCode::run(const ByteCodeFunction* f, float* args, float* outReturn, int
|
|||||||
N -= w;
|
N -= w;
|
||||||
baseIndex += w;
|
baseIndex += w;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
#else
|
#else
|
||||||
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
||||||
|
return false;
|
||||||
#endif
|
#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,
|
const float* uniforms, int uniformCount,
|
||||||
float* outArgs[], int outCount) const {
|
float* outArgs[], int outCount) const {
|
||||||
#if defined(SK_ENABLE_SKSL_INTERPRETER)
|
#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];
|
Interpreter::VValue stack[128];
|
||||||
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
int stackNeeded = f->fParameterCount + f->fLocalCount + f->fStackCount;
|
||||||
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
if (stackNeeded > (int)SK_ARRAY_COUNT(stack)) {
|
||||||
SkDEBUGFAIL("Function requires too much stack space to evaluate");
|
return false;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
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
|
// 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;
|
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;
|
int baseIndex = 0;
|
||||||
|
|
||||||
while (N) {
|
while (N) {
|
||||||
@ -1208,7 +1231,9 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool stripedOutput = true;
|
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
|
// Copy out parameters back
|
||||||
int slot = 0;
|
int slot = 0;
|
||||||
@ -1228,8 +1253,11 @@ void ByteCode::runStriped(const ByteCodeFunction* f, float* args[], int nargs, i
|
|||||||
N -= w;
|
N -= w;
|
||||||
baseIndex += w;
|
baseIndex += w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
#else
|
#else
|
||||||
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
SkDEBUGFAIL("ByteCode interpreter not enabled");
|
||||||
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@ enum class ByteCodeInstruction : uint16_t {
|
|||||||
// Followed by three bytes indicating: the number of argument slots, the number of return slots,
|
// Followed by three bytes indicating: the number of argument slots, the number of return slots,
|
||||||
// and the index of the external value to call
|
// and the index of the external value to call
|
||||||
kCallExternal,
|
kCallExternal,
|
||||||
|
// For dynamic array access: Followed by byte indicating length of array
|
||||||
|
kClampIndex,
|
||||||
VECTOR(kCompareIEQ),
|
VECTOR(kCompareIEQ),
|
||||||
VECTOR(kCompareINEQ),
|
VECTOR(kCompareINEQ),
|
||||||
VECTOR_MATRIX(kCompareFEQ),
|
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).
|
* The return value is stored in 'outReturn' (may be null, to discard the return value).
|
||||||
* 'uniforms' are mapped to 'uniform' globals, in order.
|
* 'uniforms' are mapped to 'uniform' globals, in order.
|
||||||
*/
|
*/
|
||||||
void run(const ByteCodeFunction*, float* args, float* outReturn, int N,
|
bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*, float* args, float* outReturn, int N,
|
||||||
const float* uniforms, int uniformCount) const;
|
const float* uniforms, int uniformCount) const;
|
||||||
|
|
||||||
// For now, if outArgCount > 0, then we will memcpy VecWidth number of elements into
|
bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*,
|
||||||
// each slot in outArgs. This may change in the future, as that may be more than is actually
|
float* args[], int nargs, int N,
|
||||||
// valid (depending on the N that was passed in.
|
const float* uniforms, int uniformCount,
|
||||||
void runStriped(const ByteCodeFunction*, float* args[], int nargs, int N,
|
float* outArgs[], int outArgCount) const;
|
||||||
const float* uniforms, int uniformCount,
|
|
||||||
float* outArgs[], int outArgCount) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -208,6 +208,7 @@ int ByteCodeGenerator::StackUsage(ByteCodeInstruction inst, int count_) {
|
|||||||
case ByteCodeInstruction::kInverse3x3:
|
case ByteCodeInstruction::kInverse3x3:
|
||||||
case ByteCodeInstruction::kInverse4x4: return 0;
|
case ByteCodeInstruction::kInverse4x4: return 0;
|
||||||
|
|
||||||
|
case ByteCodeInstruction::kClampIndex: return 0;
|
||||||
case ByteCodeInstruction::kNotB: return 0;
|
case ByteCodeInstruction::kNotB: return 0;
|
||||||
case ByteCodeInstruction::kNegateFN: return 0;
|
case ByteCodeInstruction::kNegateFN: return 0;
|
||||||
|
|
||||||
@ -459,9 +460,16 @@ int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* st
|
|||||||
case Expression::kIndex_Kind: {
|
case Expression::kIndex_Kind: {
|
||||||
const IndexExpression& i = (const IndexExpression&)expr;
|
const IndexExpression& i = (const IndexExpression&)expr;
|
||||||
int stride = SlotCount(i.fType);
|
int stride = SlotCount(i.fType);
|
||||||
|
int length = i.fBase->fType.columns();
|
||||||
|
SkASSERT(length <= 255);
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
if (i.fIndex->isConstant()) {
|
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 {
|
} else {
|
||||||
if (i.fIndex->hasSideEffects()) {
|
if (i.fIndex->hasSideEffects()) {
|
||||||
// Having a side-effect in an indexer is technically safe for an rvalue,
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
this->writeExpression(*i.fIndex);
|
this->writeExpression(*i.fIndex);
|
||||||
|
this->write(ByteCodeInstruction::kClampIndex);
|
||||||
|
this->write8(length);
|
||||||
if (stride != 1) {
|
if (stride != 1) {
|
||||||
this->write(ByteCodeInstruction::kPushImmediate);
|
this->write(ByteCodeInstruction::kPushImmediate);
|
||||||
this->write32(stride);
|
this->write32(stride);
|
||||||
@ -829,7 +839,7 @@ void ByteCodeGenerator::writeExternalValue(const ExternalValueReference& e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
|
void ByteCodeGenerator::writeVariableExpression(const Expression& expr) {
|
||||||
Variable::Storage storage;
|
Variable::Storage storage = Variable::kLocal_Storage;
|
||||||
int location = this->getLocation(expr, &storage);
|
int location = this->getLocation(expr, &storage);
|
||||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||||
int count = SlotCount(expr.fType);
|
int count = SlotCount(expr.fType);
|
||||||
@ -1230,7 +1240,7 @@ public:
|
|||||||
if (!discard) {
|
if (!discard) {
|
||||||
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
||||||
}
|
}
|
||||||
Variable::Storage storage;
|
Variable::Storage storage = Variable::kLocal_Storage;
|
||||||
int location = fGenerator.getLocation(*fSwizzle.fBase, &storage);
|
int location = fGenerator.getLocation(*fSwizzle.fBase, &storage);
|
||||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||||
if (location < 0) {
|
if (location < 0) {
|
||||||
@ -1275,7 +1285,7 @@ public:
|
|||||||
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
fGenerator.write(vector_instruction(ByteCodeInstruction::kDup, count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Variable::Storage storage;
|
Variable::Storage storage = Variable::kLocal_Storage;
|
||||||
int location = fGenerator.getLocation(fExpression, &storage);
|
int location = fGenerator.getLocation(fExpression, &storage);
|
||||||
bool isGlobal = storage == Variable::kGlobal_Storage;
|
bool isGlobal = storage == Variable::kGlobal_Storage;
|
||||||
if (location < 0 || count > 4) {
|
if (location < 0 || count > 4) {
|
||||||
|
@ -29,12 +29,12 @@
|
|||||||
#include "include/core/SkTypes.h"
|
#include "include/core/SkTypes.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
|
||||||
|
|
||||||
#if defined(__clang__) || defined(__GNUC__)
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
#define SKSL_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
|
||||||
|
#define SKSL_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||||
#else
|
#else
|
||||||
#define SKSL_PRINTF_LIKE(A, B)
|
#define SKSL_PRINTF_LIKE(A, B)
|
||||||
|
#define SKSL_WARN_UNUSED_RESULT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
|
#define ABORT(...) (printf(__VA_ARGS__), sksl_abort())
|
||||||
|
@ -40,7 +40,7 @@ void test(skiatest::Reporter* r, const char* src, float* in, int expectedCount,
|
|||||||
}
|
}
|
||||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||||
std::unique_ptr<float[]> out = std::unique_ptr<float[]>(new float[expectedCount]);
|
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)
|
bool valid = exactCompare ? !memcmp(out.get(), expected, sizeof(float) * expectedCount)
|
||||||
: nearly_equal(out.get(), expected, expectedCount);
|
: nearly_equal(out.get(), expected, expectedCount);
|
||||||
if (!valid) {
|
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
|
// First run in scalar mode to determine the expected output
|
||||||
for (int i = 0; i < 4; ++i) {
|
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
|
// 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) {
|
if (memcmp(out_s, out_v, sizeof(out_s)) != 0) {
|
||||||
printf("for program: %s\n", src);
|
printf("for program: %s\n", src);
|
||||||
for (int i = 0; i < 4; ++i) {
|
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");
|
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
|
||||||
float inoutColor[4] = { inR, inG, inB, inA };
|
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 ||
|
if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
|
||||||
inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
|
inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
|
||||||
printf("for program: %s\n", src);
|
printf("for program: %s\n", src);
|
||||||
@ -559,14 +559,14 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
|||||||
{
|
{
|
||||||
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
|
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
|
||||||
int out = 0;
|
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);
|
REPORTER_ASSERT(r, out == 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int in[2] = { 15, 25 };
|
int in[2] = { 15, 25 };
|
||||||
RectAndColor out;
|
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.width() == 15);
|
||||||
REPORTER_ASSERT(r, out.fRect.height() == 25);
|
REPORTER_ASSERT(r, out.fRect.height() == 25);
|
||||||
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
|
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 in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||||
int out = 0;
|
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);
|
REPORTER_ASSERT(r, out == 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
float out[8] = { 0 };
|
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) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
|
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
|
||||||
}
|
}
|
||||||
@ -592,7 +592,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
|||||||
{
|
{
|
||||||
int in = 2;
|
int in = 2;
|
||||||
SkIRect out = SkIRect::MakeEmpty();
|
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]);
|
REPORTER_ASSERT(r, out == gRects[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +600,7 @@ DEF_TEST(SkSLInterpreterCompound, r) {
|
|||||||
ManyRects in;
|
ManyRects in;
|
||||||
memset(&in, 0, sizeof(in));
|
memset(&in, 0, sizeof(in));
|
||||||
in.fNumRects = 2;
|
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;
|
ManyRects expected;
|
||||||
memset(&expected, 0, sizeof(expected));
|
memset(&expected, 0, sizeof(expected));
|
||||||
expected.fNumRects = 2;
|
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) {
|
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
|
// 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)
|
// 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)
|
// 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; }");
|
expect_failure(r, "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) { 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) {
|
DEF_TEST(SkSLInterpreterFunctions, r) {
|
||||||
@ -674,13 +702,13 @@ DEF_TEST(SkSLInterpreterFunctions, r) {
|
|||||||
|
|
||||||
float out = 0.0f;
|
float out = 0.0f;
|
||||||
float in = 3.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);
|
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);
|
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);
|
REPORTER_ASSERT(r, out = -1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -910,7 +938,7 @@ DEF_TEST(SkSLInterpreterExternalValues, r) {
|
|||||||
}
|
}
|
||||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||||
float out;
|
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, out == 66.0);
|
||||||
REPORTER_ASSERT(r, outValue == 152);
|
REPORTER_ASSERT(r, outValue == 152);
|
||||||
} else {
|
} else {
|
||||||
@ -942,7 +970,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVector, r) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
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[0] == 2);
|
||||||
REPORTER_ASSERT(r, value[1] == 4);
|
REPORTER_ASSERT(r, value[1] == 4);
|
||||||
REPORTER_ASSERT(r, value[2] == 6);
|
REPORTER_ASSERT(r, value[2] == 6);
|
||||||
@ -1008,7 +1036,7 @@ DEF_TEST(SkSLInterpreterExternalValuesCall, r) {
|
|||||||
}
|
}
|
||||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||||
float out;
|
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);
|
REPORTER_ASSERT(r, out == 5.0);
|
||||||
} else {
|
} else {
|
||||||
printf("%s\n%s", src, compiler.errorText().c_str());
|
printf("%s\n%s", src, compiler.errorText().c_str());
|
||||||
@ -1076,7 +1104,7 @@ DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
|
|||||||
}
|
}
|
||||||
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||||
float out[4];
|
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[0] == 1.0);
|
||||||
REPORTER_ASSERT(r, out[1] == 2.0);
|
REPORTER_ASSERT(r, out[1] == 2.0);
|
||||||
REPORTER_ASSERT(r, out[2] == 3.0);
|
REPORTER_ASSERT(r, out[2] == 3.0);
|
||||||
|
Loading…
Reference in New Issue
Block a user