6cd7a5a73a
The WasmContext struct introduced in this CL is used to store the mem_size and mem_start address of the wasm memory. These variables can be accessed at C++ level at graph build time (e.g., initialized during instance building). When the GrowMemory runtime is invoked, the context variables can be changed in the WasmContext at C++ level so that the generated code will load the correct values. This requires to insert a relocatable pointer only in the JSToWasmWrapper (and in the other wasm entry points), the value is then passed from function to function as an automatically added additional parameter. The WasmContext is then dropped when creating an Interpreter Entry or when invoking a JavaScript function. This removes the need of patching the generated code at runtime (i.e., when the memory grows) with respect to WASM_MEMORY_REFERENCE and WASM_MEMORY_SIZE_REFERENCE. However, we still need to patch the code at instance build time to patch the JSToWasmWrappers; in fact the address of the WasmContext is not known during compilation, but only when the instance is built. The WasmContext address is passed as the first parameter. This has the advantage of not having to move the WasmContext around if the function does not use many registers. This CL also changes the wasm calling convention so that the first parameter register is different from the return value register. The WasmContext is attached to every WasmMemoryObject, to share the same context with multiple instances sharing the same memory. Moreover, the nodes representing the WasmContext variables are cached in the SSA environment, similarly to other local variables that might change during execution. The nodes are created when initializing the SSA environment and refreshed every time a grow_memory or a function call happens, so that we are sure that they always represent the correct mem_size and mem_start variables. This CL also removes the WasmMemorySize runtime (since it's now possible to directly retrieve mem_size from the context) and simplifies the GrowMemory runtime (since every instance now has a memory_object). R=ahaas@chromium.org,clemensh@chromium.org CC=gdeepti@chromium.org Change-Id: I3f058e641284f5a1bbbfc35a64c88da6ff08e240 Reviewed-on: https://chromium-review.googlesource.com/671008 Commit-Queue: Enrico Bacis <enricobacis@google.com> Reviewed-by: Clemens Hammacher <clemensh@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#48209}
201 lines
6.0 KiB
C++
201 lines
6.0 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 "src/assembler-inl.h"
|
|
#include "src/base/platform/elapsed-timer.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 {
|
|
|
|
WASM_EXEC_TEST(Int32AsmjsDivS) {
|
|
WasmRunner<int32_t, int32_t, int32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_BINOP(kExprI32AsmjsDivS, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
const int32_t kMin = std::numeric_limits<int32_t>::min();
|
|
CHECK_EQ(0, r.Call(0, 100));
|
|
CHECK_EQ(0, r.Call(100, 0));
|
|
CHECK_EQ(0, r.Call(-1001, 0));
|
|
CHECK_EQ(kMin, r.Call(kMin, -1));
|
|
CHECK_EQ(0, r.Call(kMin, 0));
|
|
}
|
|
|
|
WASM_EXEC_TEST(Int32AsmjsRemS) {
|
|
WasmRunner<int32_t, int32_t, int32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_BINOP(kExprI32AsmjsRemS, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
const int32_t kMin = std::numeric_limits<int32_t>::min();
|
|
CHECK_EQ(33, r.Call(133, 100));
|
|
CHECK_EQ(0, r.Call(kMin, -1));
|
|
CHECK_EQ(0, r.Call(100, 0));
|
|
CHECK_EQ(0, r.Call(-1001, 0));
|
|
CHECK_EQ(0, r.Call(kMin, 0));
|
|
}
|
|
|
|
WASM_EXEC_TEST(Int32AsmjsDivU) {
|
|
WasmRunner<int32_t, int32_t, int32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_BINOP(kExprI32AsmjsDivU, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
const int32_t kMin = std::numeric_limits<int32_t>::min();
|
|
CHECK_EQ(0, r.Call(0, 100));
|
|
CHECK_EQ(0, r.Call(kMin, -1));
|
|
CHECK_EQ(0, r.Call(100, 0));
|
|
CHECK_EQ(0, r.Call(-1001, 0));
|
|
CHECK_EQ(0, r.Call(kMin, 0));
|
|
}
|
|
|
|
WASM_EXEC_TEST(Int32AsmjsRemU) {
|
|
WasmRunner<int32_t, int32_t, int32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_BINOP(kExprI32AsmjsRemU, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
|
|
const int32_t kMin = std::numeric_limits<int32_t>::min();
|
|
CHECK_EQ(17, r.Call(217, 100));
|
|
CHECK_EQ(0, r.Call(100, 0));
|
|
CHECK_EQ(0, r.Call(-1001, 0));
|
|
CHECK_EQ(0, r.Call(kMin, 0));
|
|
CHECK_EQ(kMin, r.Call(kMin, -1));
|
|
}
|
|
|
|
WASM_EXEC_TEST(I32AsmjsSConvertF32) {
|
|
WasmRunner<int32_t, float> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_UNOP(kExprI32AsmjsSConvertF32, WASM_GET_LOCAL(0)));
|
|
|
|
FOR_FLOAT32_INPUTS(i) {
|
|
int32_t expected = DoubleToInt32(*i);
|
|
CHECK_EQ(expected, r.Call(*i));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(I32AsmjsSConvertF64) {
|
|
WasmRunner<int32_t, double> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_UNOP(kExprI32AsmjsSConvertF64, WASM_GET_LOCAL(0)));
|
|
|
|
FOR_FLOAT64_INPUTS(i) {
|
|
int32_t expected = DoubleToInt32(*i);
|
|
CHECK_EQ(expected, r.Call(*i));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(I32AsmjsUConvertF32) {
|
|
WasmRunner<uint32_t, float> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_UNOP(kExprI32AsmjsUConvertF32, WASM_GET_LOCAL(0)));
|
|
|
|
FOR_FLOAT32_INPUTS(i) {
|
|
uint32_t expected = DoubleToUint32(*i);
|
|
CHECK_EQ(expected, r.Call(*i));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(I32AsmjsUConvertF64) {
|
|
WasmRunner<uint32_t, double> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
BUILD(r, WASM_UNOP(kExprI32AsmjsUConvertF64, WASM_GET_LOCAL(0)));
|
|
|
|
FOR_FLOAT64_INPUTS(i) {
|
|
uint32_t expected = DoubleToUint32(*i);
|
|
CHECK_EQ(expected, r.Call(*i));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(LoadMemI32_oob_asm) {
|
|
WasmRunner<int32_t, uint32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
int32_t* memory = r.builder().AddMemoryElems<int32_t>(8);
|
|
r.builder().RandomizeMemory(1112);
|
|
|
|
BUILD(r, WASM_UNOP(kExprI32AsmjsLoadMem, WASM_GET_LOCAL(0)));
|
|
|
|
memory[0] = 999999;
|
|
CHECK_EQ(999999, r.Call(0u));
|
|
// TODO(titzer): offset 29-31 should also be OOB.
|
|
for (uint32_t offset = 32; offset < 40; offset++) {
|
|
CHECK_EQ(0, r.Call(offset));
|
|
}
|
|
|
|
for (uint32_t offset = 0x80000000; offset < 0x80000010; offset++) {
|
|
CHECK_EQ(0, r.Call(offset));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(LoadMemF32_oob_asm) {
|
|
WasmRunner<float, uint32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
float* memory = r.builder().AddMemoryElems<float>(8);
|
|
r.builder().RandomizeMemory(1112);
|
|
|
|
BUILD(r, WASM_UNOP(kExprF32AsmjsLoadMem, WASM_GET_LOCAL(0)));
|
|
|
|
memory[0] = 9999.5f;
|
|
CHECK_EQ(9999.5f, r.Call(0u));
|
|
// TODO(titzer): offset 29-31 should also be OOB.
|
|
for (uint32_t offset = 32; offset < 40; offset++) {
|
|
CHECK(std::isnan(r.Call(offset)));
|
|
}
|
|
|
|
for (uint32_t offset = 0x80000000; offset < 0x80000010; offset++) {
|
|
CHECK(std::isnan(r.Call(offset)));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(LoadMemF64_oob_asm) {
|
|
WasmRunner<double, uint32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
double* memory = r.builder().AddMemoryElems<double>(8);
|
|
r.builder().RandomizeMemory(1112);
|
|
|
|
BUILD(r, WASM_UNOP(kExprF64AsmjsLoadMem, WASM_GET_LOCAL(0)));
|
|
|
|
memory[0] = 9799.5;
|
|
CHECK_EQ(9799.5, r.Call(0u));
|
|
memory[1] = 11799.25;
|
|
CHECK_EQ(11799.25, r.Call(8u));
|
|
// TODO(titzer): offset 57-63 should also be OOB.
|
|
for (uint32_t offset = 64; offset < 80; offset++) {
|
|
CHECK(std::isnan(r.Call(offset)));
|
|
}
|
|
|
|
for (uint32_t offset = 0x80000000; offset < 0x80000010; offset++) {
|
|
CHECK(std::isnan(r.Call(offset)));
|
|
}
|
|
}
|
|
|
|
WASM_EXEC_TEST(StoreMemI32_oob_asm) {
|
|
WasmRunner<int32_t, uint32_t, uint32_t> r(execution_mode);
|
|
r.builder().ChangeOriginToAsmjs();
|
|
int32_t* memory = r.builder().AddMemoryElems<int32_t>(8);
|
|
r.builder().RandomizeMemory(1112);
|
|
|
|
BUILD(r, WASM_BINOP(kExprI32AsmjsStoreMem, WASM_GET_LOCAL(0),
|
|
WASM_GET_LOCAL(1)));
|
|
|
|
memory[0] = 7777;
|
|
CHECK_EQ(999999, r.Call(0u, 999999));
|
|
CHECK_EQ(999999, memory[0]);
|
|
// TODO(titzer): offset 29-31 should also be OOB.
|
|
for (uint32_t offset = 32; offset < 40; offset++) {
|
|
CHECK_EQ(8888, r.Call(offset, 8888));
|
|
}
|
|
|
|
for (uint32_t offset = 0x10000000; offset < 0xF0000000; offset += 0x1000000) {
|
|
CHECK_EQ(7777, r.Call(offset, 7777));
|
|
}
|
|
}
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|