46a99b07fc
This adds preliminary support for handling the "br_on_exn" opcode in the interpreter. It also makes "catch" and "rethrow" use a proper exception reference instead of a dummy value. To that end this also adds {Handle<>} as a new kind of {WasmValue} which is intended to pass reference values (e.g. "anyref" or "except_ref") to the runtime system. Therefore lifetime of such a {WasmValue} is directly coupled to any surrounding {HandleScope}. For now we just store {Handle<>} directly on the simulated operand stack of the interpreter. This is of course bogus, since the surrounding scope does not outlive the interpreter activation. Decoupling the lifetime of the operand stack from a {HandleScope} will be done in a follow-up CL. As a drive-by this change also implements support for the "ref_null" and the "ref_is_null" opcodes as a proof-of-concept that the new {WasmValue} is also applicable to the "anyref" reference type. R=clemensh@chromium.org TEST=cctest/test-run-wasm-interpreter/ReferenceTypeLocals BUG=v8:8091,v8:7581 Change-Id: I2307e0689a19c4aab1d67f1ba6742cb3cc31aa3c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1550299 Commit-Queue: Michael Starzinger <mstarzinger@chromium.org> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#60598}
599 lines
19 KiB
C++
599 lines
19 KiB
C++
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <memory>
|
|
|
|
#include "src/assembler-inl.h"
|
|
#include "src/wasm/wasm-interpreter.h"
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/compiler/value-helper.h"
|
|
#include "test/cctest/wasm/wasm-run-utils.h"
|
|
#include "test/common/wasm/test-signatures.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
namespace test_run_wasm_interpreter {
|
|
|
|
TEST(Run_WasmInt8Const_i) {
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
const byte kExpectedValue = 109;
|
|
// return(kExpectedValue)
|
|
BUILD(r, WASM_I32V_2(kExpectedValue));
|
|
CHECK_EQ(kExpectedValue, r.Call());
|
|
}
|
|
|
|
TEST(Run_WasmIfElse) {
|
|
WasmRunner<int32_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_IF_ELSE_I(WASM_GET_LOCAL(0), WASM_I32V_1(9), WASM_I32V_1(10)));
|
|
CHECK_EQ(10, r.Call(0));
|
|
CHECK_EQ(9, r.Call(1));
|
|
}
|
|
|
|
TEST(Run_WasmIfReturn) {
|
|
WasmRunner<int32_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_IF(WASM_GET_LOCAL(0), WASM_RETURN1(WASM_I32V_2(77))),
|
|
WASM_I32V_2(65));
|
|
CHECK_EQ(65, r.Call(0));
|
|
CHECK_EQ(77, r.Call(1));
|
|
}
|
|
|
|
TEST(Run_WasmNopsN) {
|
|
const int kMaxNops = 10;
|
|
byte code[kMaxNops + 2];
|
|
for (int nops = 0; nops < kMaxNops; nops++) {
|
|
byte expected = static_cast<byte>(20 + nops);
|
|
memset(code, kExprNop, sizeof(code));
|
|
code[nops] = kExprI32Const;
|
|
code[nops + 1] = expected;
|
|
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
r.Build(code, code + nops + 2);
|
|
CHECK_EQ(expected, r.Call());
|
|
}
|
|
}
|
|
|
|
TEST(Run_WasmConstsN) {
|
|
const int kMaxConsts = 5;
|
|
byte code[kMaxConsts * 3];
|
|
int32_t expected = 0;
|
|
for (int count = 1; count < kMaxConsts; count++) {
|
|
for (int i = 0; i < count; i++) {
|
|
byte val = static_cast<byte>(count * 10 + i);
|
|
code[i * 3] = kExprI32Const;
|
|
code[i * 3 + 1] = val;
|
|
if (i == (count - 1)) {
|
|
code[i * 3 + 2] = kExprNop;
|
|
expected = val;
|
|
} else {
|
|
code[i * 3 + 2] = kExprDrop;
|
|
}
|
|
}
|
|
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
r.Build(code, code + (count * 3));
|
|
CHECK_EQ(expected, r.Call());
|
|
}
|
|
}
|
|
|
|
TEST(Run_WasmBlocksN) {
|
|
const int kMaxNops = 10;
|
|
const int kExtra = 5;
|
|
byte code[kMaxNops + kExtra];
|
|
for (int nops = 0; nops < kMaxNops; nops++) {
|
|
byte expected = static_cast<byte>(30 + nops);
|
|
memset(code, kExprNop, sizeof(code));
|
|
code[0] = kExprBlock;
|
|
code[1] = kLocalI32;
|
|
code[2 + nops] = kExprI32Const;
|
|
code[2 + nops + 1] = expected;
|
|
code[2 + nops + 2] = kExprEnd;
|
|
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
r.Build(code, code + nops + kExtra);
|
|
CHECK_EQ(expected, r.Call());
|
|
}
|
|
}
|
|
|
|
TEST(Run_WasmBlockBreakN) {
|
|
const int kMaxNops = 10;
|
|
const int kExtra = 6;
|
|
int run = 0;
|
|
byte code[kMaxNops + kExtra];
|
|
for (int nops = 0; nops < kMaxNops; nops++) {
|
|
// Place the break anywhere within the block.
|
|
for (int index = 0; index < nops; index++) {
|
|
memset(code, kExprNop, sizeof(code));
|
|
code[0] = kExprBlock;
|
|
code[1] = kLocalI32;
|
|
code[sizeof(code) - 1] = kExprEnd;
|
|
|
|
int expected = run++;
|
|
code[2 + index + 0] = kExprI32Const;
|
|
code[2 + index + 1] = static_cast<byte>(expected);
|
|
code[2 + index + 2] = kExprBr;
|
|
code[2 + index + 3] = 0;
|
|
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
r.Build(code, code + kMaxNops + kExtra);
|
|
CHECK_EQ(expected, r.Call());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Run_Wasm_nested_ifs_i) {
|
|
WasmRunner<int32_t, int32_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
BUILD(
|
|
r,
|
|
WASM_IF_ELSE_I(
|
|
WASM_GET_LOCAL(0),
|
|
WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I32V_1(11), WASM_I32V_1(12)),
|
|
WASM_IF_ELSE_I(WASM_GET_LOCAL(1), WASM_I32V_1(13), WASM_I32V_1(14))));
|
|
|
|
CHECK_EQ(11, r.Call(1, 1));
|
|
CHECK_EQ(12, r.Call(1, 0));
|
|
CHECK_EQ(13, r.Call(0, 1));
|
|
CHECK_EQ(14, r.Call(0, 0));
|
|
}
|
|
|
|
// Repeated from test-run-wasm.cc to avoid poluting header files.
|
|
template <typename T>
|
|
static T factorial(T v) {
|
|
T expected = 1;
|
|
for (T i = v; i > 1; i--) {
|
|
expected *= i;
|
|
}
|
|
return expected;
|
|
}
|
|
|
|
// Basic test of return call in interpreter. Good old factorial.
|
|
TEST(Run_Wasm_returnCallFactorial) {
|
|
EXPERIMENTAL_FLAG_SCOPE(return_call);
|
|
// Run in bounded amount of stack - 8kb.
|
|
FlagScope<int32_t> stack_size(&v8::internal::FLAG_stack_size, 8);
|
|
|
|
WasmRunner<uint32_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
WasmFunctionCompiler& fact_aux_fn =
|
|
r.NewFunction<int32_t, int32_t, int32_t>("fact_aux");
|
|
|
|
BUILD(r, WASM_RETURN_CALL_FUNCTION(fact_aux_fn.function_index(),
|
|
WASM_GET_LOCAL(0), WASM_I32V(1)));
|
|
|
|
BUILD(fact_aux_fn,
|
|
WASM_IF_ELSE_I(
|
|
WASM_I32_EQ(WASM_I32V(1), WASM_GET_LOCAL(0)), WASM_GET_LOCAL(1),
|
|
WASM_RETURN_CALL_FUNCTION(
|
|
fact_aux_fn.function_index(),
|
|
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
|
|
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
|
|
|
|
// Runs out of stack space without using return call.
|
|
uint32_t test_values[] = {1, 2, 5, 10, 20, 20000};
|
|
|
|
for (uint32_t v : test_values) {
|
|
uint32_t found = r.Call(v);
|
|
CHECK_EQ(factorial(v), found);
|
|
}
|
|
}
|
|
|
|
TEST(Run_Wasm_returnCallFactorial64) {
|
|
EXPERIMENTAL_FLAG_SCOPE(return_call);
|
|
|
|
int32_t test_values[] = {1, 2, 5, 10, 20};
|
|
WasmRunner<int64_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
WasmFunctionCompiler& fact_aux_fn =
|
|
r.NewFunction<int64_t, int32_t, int64_t>("fact_aux");
|
|
|
|
BUILD(r, WASM_RETURN_CALL_FUNCTION(fact_aux_fn.function_index(),
|
|
WASM_GET_LOCAL(0), WASM_I64V(1)));
|
|
|
|
BUILD(fact_aux_fn,
|
|
WASM_IF_ELSE_L(
|
|
WASM_I32_EQ(WASM_I32V(1), WASM_GET_LOCAL(0)), WASM_GET_LOCAL(1),
|
|
WASM_RETURN_CALL_FUNCTION(
|
|
fact_aux_fn.function_index(),
|
|
WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
|
|
WASM_I64_MUL(WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)),
|
|
WASM_GET_LOCAL(1)))));
|
|
|
|
for (int32_t v : test_values) {
|
|
CHECK_EQ(factorial<int64_t>(v), r.Call(v));
|
|
}
|
|
}
|
|
|
|
TEST(Run_Wasm_returnCallIndirectFactorial) {
|
|
EXPERIMENTAL_FLAG_SCOPE(return_call);
|
|
|
|
TestSignatures sigs;
|
|
|
|
WasmRunner<uint32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
WasmFunctionCompiler& fact_aux_fn = r.NewFunction(sigs.i_ii(), "fact_aux");
|
|
fact_aux_fn.SetSigIndex(0);
|
|
|
|
r.builder().AddSignature(sigs.i_ii());
|
|
|
|
// Function table.
|
|
uint16_t indirect_function_table[] = {
|
|
static_cast<uint16_t>(fact_aux_fn.function_index())};
|
|
|
|
r.builder().AddIndirectFunctionTable(indirect_function_table,
|
|
arraysize(indirect_function_table));
|
|
|
|
BUILD(r, WASM_RETURN_CALL_INDIRECT(0, WASM_I32V(0), WASM_GET_LOCAL(0),
|
|
WASM_I32V(1)));
|
|
|
|
BUILD(fact_aux_fn,
|
|
WASM_IF_ELSE_I(
|
|
WASM_I32_EQ(WASM_I32V(1), WASM_GET_LOCAL(0)), WASM_GET_LOCAL(1),
|
|
WASM_RETURN_CALL_INDIRECT(
|
|
0, WASM_I32V(0), WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V(1)),
|
|
WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)))));
|
|
|
|
uint32_t test_values[] = {1, 2, 5, 10, 20};
|
|
|
|
for (uint32_t v : test_values) {
|
|
CHECK_EQ(factorial(v), r.Call(v));
|
|
}
|
|
}
|
|
// Make tests more robust by not hard-coding offsets of various operations.
|
|
// The {Find} method finds the offsets for the given bytecodes, returning
|
|
// the offsets in an array.
|
|
std::unique_ptr<int[]> Find(byte* code, size_t code_size, int n, ...) {
|
|
va_list vl;
|
|
va_start(vl, n);
|
|
|
|
std::unique_ptr<int[]> offsets(new int[n]);
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
offsets[i] = -1;
|
|
}
|
|
|
|
int pos = 0;
|
|
WasmOpcode current = static_cast<WasmOpcode>(va_arg(vl, int));
|
|
for (size_t i = 0; i < code_size; i++) {
|
|
if (code[i] == current) {
|
|
offsets[pos++] = static_cast<int>(i);
|
|
if (pos == n) break;
|
|
current = static_cast<WasmOpcode>(va_arg(vl, int));
|
|
}
|
|
}
|
|
va_end(vl);
|
|
|
|
return offsets;
|
|
}
|
|
|
|
TEST(Breakpoint_I32Add) {
|
|
static const int kLocalsDeclSize = 1;
|
|
static const int kNumBreakpoints = 3;
|
|
byte code[] = {WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
|
|
std::unique_ptr<int[]> offsets =
|
|
Find(code, sizeof(code), kNumBreakpoints, kExprGetLocal, kExprGetLocal,
|
|
kExprI32Add);
|
|
|
|
WasmRunner<int32_t, uint32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
r.Build(code, code + arraysize(code));
|
|
|
|
WasmInterpreter* interpreter = r.interpreter();
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
for (int i = 0; i < kNumBreakpoints; i++) {
|
|
interpreter->SetBreakpoint(r.function(), kLocalsDeclSize + offsets[i],
|
|
true);
|
|
}
|
|
|
|
FOR_UINT32_INPUTS(a) {
|
|
for (uint32_t b = 11; b < 3000000000u; b += 1000000000u) {
|
|
thread->Reset();
|
|
WasmValue args[] = {WasmValue(a), WasmValue(b)};
|
|
thread->InitFrame(r.function(), args);
|
|
|
|
for (int i = 0; i < kNumBreakpoints; i++) {
|
|
thread->Run(); // run to next breakpoint
|
|
// Check the thread stopped at the right pc.
|
|
CHECK_EQ(WasmInterpreter::PAUSED, thread->state());
|
|
CHECK_EQ(static_cast<size_t>(kLocalsDeclSize + offsets[i]),
|
|
thread->GetBreakpointPc());
|
|
}
|
|
|
|
thread->Run(); // run to completion
|
|
|
|
// Check the thread finished with the right value.
|
|
CHECK_EQ(WasmInterpreter::FINISHED, thread->state());
|
|
uint32_t expected = (a) + (b);
|
|
CHECK_EQ(expected, thread->GetReturnValue().to<uint32_t>());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Step_I32Mul) {
|
|
static const int kTraceLength = 4;
|
|
byte code[] = {WASM_I32_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
|
|
|
|
WasmRunner<int32_t, uint32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
r.Build(code, code + arraysize(code));
|
|
|
|
WasmInterpreter* interpreter = r.interpreter();
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
|
|
FOR_UINT32_INPUTS(a) {
|
|
for (uint32_t b = 33; b < 3000000000u; b += 1000000000u) {
|
|
thread->Reset();
|
|
WasmValue args[] = {WasmValue(a), WasmValue(b)};
|
|
thread->InitFrame(r.function(), args);
|
|
|
|
// Run instructions one by one.
|
|
for (int i = 0; i < kTraceLength - 1; i++) {
|
|
thread->Step();
|
|
// Check the thread stopped.
|
|
CHECK_EQ(WasmInterpreter::PAUSED, thread->state());
|
|
}
|
|
|
|
// Run last instruction.
|
|
thread->Step();
|
|
|
|
// Check the thread finished with the right value.
|
|
CHECK_EQ(WasmInterpreter::FINISHED, thread->state());
|
|
uint32_t expected = (a) * (b);
|
|
CHECK_EQ(expected, thread->GetReturnValue().to<uint32_t>());
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Breakpoint_I32And_disable) {
|
|
static const int kLocalsDeclSize = 1;
|
|
static const int kNumBreakpoints = 1;
|
|
byte code[] = {WASM_I32_AND(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))};
|
|
std::unique_ptr<int[]> offsets =
|
|
Find(code, sizeof(code), kNumBreakpoints, kExprI32And);
|
|
|
|
WasmRunner<int32_t, uint32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
|
|
r.Build(code, code + arraysize(code));
|
|
|
|
WasmInterpreter* interpreter = r.interpreter();
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
|
|
FOR_UINT32_INPUTS(a) {
|
|
for (uint32_t b = 11; b < 3000000000u; b += 1000000000u) {
|
|
// Run with and without breakpoints.
|
|
for (int do_break = 0; do_break < 2; do_break++) {
|
|
interpreter->SetBreakpoint(r.function(), kLocalsDeclSize + offsets[0],
|
|
do_break);
|
|
thread->Reset();
|
|
WasmValue args[] = {WasmValue(a), WasmValue(b)};
|
|
thread->InitFrame(r.function(), args);
|
|
|
|
if (do_break) {
|
|
thread->Run(); // run to next breakpoint
|
|
// Check the thread stopped at the right pc.
|
|
CHECK_EQ(WasmInterpreter::PAUSED, thread->state());
|
|
CHECK_EQ(static_cast<size_t>(kLocalsDeclSize + offsets[0]),
|
|
thread->GetBreakpointPc());
|
|
}
|
|
|
|
thread->Run(); // run to completion
|
|
|
|
// Check the thread finished with the right value.
|
|
CHECK_EQ(WasmInterpreter::FINISHED, thread->state());
|
|
uint32_t expected = (a) & (b);
|
|
CHECK_EQ(expected, thread->GetReturnValue().to<uint32_t>());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(MemoryGrow) {
|
|
{
|
|
WasmRunner<int32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
r.builder().SetMaxMemPages(10);
|
|
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0)));
|
|
CHECK_EQ(1, r.Call(1));
|
|
}
|
|
{
|
|
WasmRunner<int32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
r.builder().SetMaxMemPages(10);
|
|
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0)));
|
|
CHECK_EQ(-1, r.Call(11));
|
|
}
|
|
}
|
|
|
|
TEST(MemoryGrowPreservesData) {
|
|
int32_t index = 16;
|
|
int32_t value = 2335;
|
|
WasmRunner<int32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
BUILD(r, WASM_STORE_MEM(MachineType::Int32(), WASM_I32V(index),
|
|
WASM_I32V(value)),
|
|
WASM_GROW_MEMORY(WASM_GET_LOCAL(0)), WASM_DROP,
|
|
WASM_LOAD_MEM(MachineType::Int32(), WASM_I32V(index)));
|
|
CHECK_EQ(value, r.Call(1));
|
|
}
|
|
|
|
TEST(MemoryGrowInvalidSize) {
|
|
// Grow memory by an invalid amount without initial memory.
|
|
WasmRunner<int32_t, uint32_t> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
BUILD(r, WASM_GROW_MEMORY(WASM_GET_LOCAL(0)));
|
|
CHECK_EQ(-1, r.Call(1048575));
|
|
}
|
|
|
|
TEST(ReferenceTypeLocals) {
|
|
{
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_REF_IS_NULL(WASM_REF_NULL));
|
|
CHECK_EQ(1, r.Call());
|
|
}
|
|
{
|
|
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
|
|
r.AllocateLocal(kWasmAnyRef);
|
|
BUILD(r, WASM_REF_IS_NULL(WASM_GET_LOCAL(0)));
|
|
CHECK_EQ(1, r.Call());
|
|
}
|
|
// TODO(mstarzinger): Test and support global anyref variables.
|
|
}
|
|
|
|
TEST(TestPossibleNondeterminism) {
|
|
{
|
|
WasmRunner<int32_t, float> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_I32_REINTERPRET_F32(WASM_GET_LOCAL(0)));
|
|
r.Call(1048575.5f);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<float>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<int64_t, double> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_I64_REINTERPRET_F64(WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<float, float> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F32_COPYSIGN(WASM_F32(42.0f), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0f);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<double, double> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F64_COPYSIGN(WASM_F64(42.0), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
int32_t index = 16;
|
|
WasmRunner<int32_t, float> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
BUILD(r, WASM_STORE_MEM(MachineType::Float32(), WASM_I32V(index),
|
|
WASM_GET_LOCAL(0)),
|
|
WASM_I32V(index));
|
|
r.Call(1345.3456f);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<float>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
int32_t index = 16;
|
|
WasmRunner<int32_t, double> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(kWasmPageSize);
|
|
BUILD(r, WASM_STORE_MEM(MachineType::Float64(), WASM_I32V(index),
|
|
WASM_GET_LOCAL(0)),
|
|
WASM_I32V(index));
|
|
r.Call(1345.3456);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<float, float> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(1048575.5f);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<float>::quiet_NaN());
|
|
CHECK(r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<double, double> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<int32_t, float> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F32_EQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<float>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<int32_t, double> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F64_EQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(!r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<float, float> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F32_MIN(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(1048575.5f);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<float>::quiet_NaN());
|
|
CHECK(r.possible_nondeterminism());
|
|
}
|
|
{
|
|
WasmRunner<double, double> r(ExecutionTier::kInterpreter);
|
|
BUILD(r, WASM_F64_MAX(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0)));
|
|
r.Call(16.0);
|
|
CHECK(!r.possible_nondeterminism());
|
|
r.Call(std::numeric_limits<double>::quiet_NaN());
|
|
CHECK(r.possible_nondeterminism());
|
|
}
|
|
}
|
|
|
|
TEST(WasmInterpreterActivations) {
|
|
WasmRunner<void> r(ExecutionTier::kInterpreter);
|
|
Isolate* isolate = r.main_isolate();
|
|
BUILD(r, WASM_UNREACHABLE);
|
|
|
|
WasmInterpreter* interpreter = r.interpreter();
|
|
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
|
CHECK_EQ(0, thread->NumActivations());
|
|
uint32_t act0 = thread->StartActivation();
|
|
CHECK_EQ(0, act0);
|
|
thread->InitFrame(r.function(), nullptr);
|
|
uint32_t act1 = thread->StartActivation();
|
|
CHECK_EQ(1, act1);
|
|
thread->InitFrame(r.function(), nullptr);
|
|
CHECK_EQ(2, thread->NumActivations());
|
|
CHECK_EQ(2, thread->GetFrameCount());
|
|
CHECK_EQ(WasmInterpreter::TRAPPED, thread->Run());
|
|
thread->RaiseException(isolate, handle(Smi::kZero, isolate));
|
|
CHECK_EQ(1, thread->GetFrameCount());
|
|
CHECK_EQ(2, thread->NumActivations());
|
|
thread->FinishActivation(act1);
|
|
isolate->clear_pending_exception();
|
|
CHECK_EQ(1, thread->GetFrameCount());
|
|
CHECK_EQ(1, thread->NumActivations());
|
|
CHECK_EQ(WasmInterpreter::TRAPPED, thread->Run());
|
|
thread->RaiseException(isolate, handle(Smi::kZero, isolate));
|
|
CHECK_EQ(0, thread->GetFrameCount());
|
|
CHECK_EQ(1, thread->NumActivations());
|
|
thread->FinishActivation(act0);
|
|
isolate->clear_pending_exception();
|
|
CHECK_EQ(0, thread->NumActivations());
|
|
}
|
|
|
|
TEST(InterpreterLoadWithoutMemory) {
|
|
WasmRunner<int32_t, int32_t> r(ExecutionTier::kInterpreter);
|
|
r.builder().AddMemory(0);
|
|
BUILD(r, WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(0)));
|
|
CHECK_TRAP32(r.Call(0));
|
|
}
|
|
|
|
} // namespace test_run_wasm_interpreter
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|