v8/test/cctest/wasm/test-run-wasm-64.cc

1651 lines
56 KiB
C++
Raw Normal View History

// Copyright 2015 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/base/bits.h"
#include "src/wasm/wasm-macro-gen.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"
// If the target architecture is 64-bit, enable all tests.
#if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64
#define WASM_64 1
#else
#define WASM_64 0
#endif
#define CHECK_TRAP32(x) \
CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF)
#define CHECK_TRAP64(x) \
CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF)
#define CHECK_TRAP(x) CHECK_TRAP32(x)
#define asi64(x) static_cast<int64_t>(x)
#define asu64(x) static_cast<uint64_t>(x)
#define B2(a, b) kExprBlock, a, b, kExprEnd
// Can't bridge macro land with nested macros.
#if V8_TARGET_ARCH_MIPS
#define MIPS true
#else
#define MIPS false
#endif
#define FOREACH_I64_OPERATOR(V) \
V(DepthFirst, true) \
V(I64Phi, true) \
V(I64Const, true) \
V(I64Return, true) \
V(I64Param, true) \
V(I64LoadStore, true) \
V(I64Add, true) \
V(I64Sub, true) \
V(I64Mul, !MIPS) \
V(I64DivS, true) \
V(I64DivU, true) \
V(I64RemS, true) \
V(I64RemU, true) \
V(I64And, true) \
V(I64Ior, true) \
V(I64Xor, true) \
V(I64Shl, true) \
V(I64ShrU, true) \
V(I64ShrS, true) \
V(I64Eq, true) \
V(I64Ne, true) \
V(I64LtS, true) \
V(I64LeS, true) \
V(I64LtU, true) \
V(I64LeU, true) \
V(I64GtS, true) \
V(I64GeS, true) \
V(I64GtU, true) \
V(I64GeU, true) \
V(I64Ctz, true) \
V(I64Clz, true) \
V(I64Popcnt, true) \
V(I32ConvertI64, true) \
V(I64SConvertF32, true) \
V(I64SConvertF64, true) \
V(I64UConvertF32, true) \
V(I64UConvertF64, true) \
V(I64SConvertI32, true) \
V(I64UConvertI32, true) \
V(F32SConvertI64, true) \
V(F32UConvertI64, true) \
V(F64SConvertI64, true) \
V(F64UConvertI64, true) \
V(F64ReinterpretI64, true) \
V(I64ReinterpretF64, true) \
V(I64Ror, true) \
V(I64Rol, true)
#define DECLARE_CONST(name, cond) static const bool kSupported_##name = cond;
FOREACH_I64_OPERATOR(DECLARE_CONST)
#undef DECLARE_CONST
#define REQUIRE(name) \
if (!WASM_64 && !kSupported_##name) return
WASM_EXEC_TEST(I64Const) {
REQUIRE(I64Const);
WasmRunner<int64_t> r(execution_mode);
const int64_t kExpectedValue = 0x1122334455667788LL;
// return(kExpectedValue)
BUILD(r, WASM_I64V_9(kExpectedValue));
CHECK_EQ(kExpectedValue, r.Call());
}
WASM_EXEC_TEST(I64Const_many) {
REQUIRE(I64Const);
int cntr = 0;
FOR_INT32_INPUTS(i) {
WasmRunner<int64_t> r(execution_mode);
const int64_t kExpectedValue = (static_cast<int64_t>(*i) << 32) | cntr;
// return(kExpectedValue)
BUILD(r, WASM_I64V(kExpectedValue));
CHECK_EQ(kExpectedValue, r.Call());
cntr++;
}
}
WASM_EXEC_TEST(Return_I64) {
REQUIRE(I64Return);
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_RETURN1(WASM_GET_LOCAL(0)));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); }
}
WASM_EXEC_TEST(I64Add) {
REQUIRE(I64Add);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i + *j, r.Call(*i, *j)); }
}
}
// The i64 add and subtract regression tests need a 64-bit value with a non-zero
// upper half. This upper half was clobbering eax, leading to the function
// returning 1 rather than 0.
const int64_t kHasBit33On = 0x100000000;
WASM_EXEC_TEST(Regress5800_Add) {
REQUIRE(I64Add);
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_BLOCK(WASM_BR_IF(0, WASM_I64_EQZ(WASM_I64_ADD(
WASM_I64V(0), WASM_I64V(kHasBit33On)))),
WASM_RETURN1(WASM_I32V(0))),
WASM_I32V(0));
CHECK_EQ(0, r.Call());
}
WASM_EXEC_TEST(I64Sub) {
REQUIRE(I64Sub);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i - *j, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(Regress5800_Sub) {
REQUIRE(I64Sub);
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_BLOCK(WASM_BR_IF(0, WASM_I64_EQZ(WASM_I64_SUB(
WASM_I64V(0), WASM_I64V(kHasBit33On)))),
WASM_RETURN1(WASM_I32V(0))),
WASM_I32V(0));
CHECK_EQ(0, r.Call());
}
WASM_EXEC_TEST(I64AddUseOnlyLowWord) {
REQUIRE(I64Add);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
CHECK_EQ(static_cast<int32_t>(*i + *j), r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64SubUseOnlyLowWord) {
REQUIRE(I64Sub);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
CHECK_EQ(static_cast<int32_t>(*i - *j), r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64MulUseOnlyLowWord) {
REQUIRE(I64Mul);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_MUL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
CHECK_EQ(static_cast<int32_t>(*i * *j), r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64ShlUseOnlyLowWord) {
REQUIRE(I64Shl);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
int32_t expected = static_cast<int32_t>((*i) << (*j & 0x3f));
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64ShrUseOnlyLowWord) {
REQUIRE(I64ShrU);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
int32_t expected = static_cast<int32_t>((*i) >> (*j & 0x3f));
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64SarUseOnlyLowWord) {
REQUIRE(I64ShrS);
REQUIRE(I32ConvertI64);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(
WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
int32_t expected = static_cast<int32_t>((*i) >> (*j & 0x3f));
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivS) {
REQUIRE(I64DivS);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
if (*j == 0) {
CHECK_TRAP64(r.Call(*i, *j));
} else if (*j == -1 && *i == std::numeric_limits<int64_t>::min()) {
CHECK_TRAP64(r.Call(*i, *j));
} else {
CHECK_EQ(*i / *j, r.Call(*i, *j));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivS_Trap) {
REQUIRE(I64DivS);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(0, r.Call(asi64(0), asi64(100)));
CHECK_TRAP64(r.Call(asi64(100), asi64(0)));
CHECK_TRAP64(r.Call(asi64(-1001), asi64(0)));
CHECK_TRAP64(r.Call(std::numeric_limits<int64_t>::min(), asi64(-1)));
CHECK_TRAP64(r.Call(std::numeric_limits<int64_t>::min(), asi64(0)));
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivS_Byzero_Const) {
REQUIRE(I64DivS);
for (int8_t denom = -2; denom < 8; denom++) {
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVS(WASM_GET_LOCAL(0), WASM_I64V_1(denom)));
for (int64_t val = -7; val < 8; val++) {
if (denom == 0) {
CHECK_TRAP64(r.Call(val));
} else {
CHECK_EQ(val / denom, r.Call(val));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivU) {
REQUIRE(I64DivU);
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
if (*j == 0) {
CHECK_TRAP64(r.Call(*i, *j));
} else {
CHECK_EQ(*i / *j, r.Call(*i, *j));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivU_Trap) {
REQUIRE(I64DivU);
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(0, r.Call(asu64(0), asu64(100)));
CHECK_TRAP64(r.Call(asu64(100), asu64(0)));
CHECK_TRAP64(r.Call(asu64(1001), asu64(0)));
CHECK_TRAP64(r.Call(std::numeric_limits<uint64_t>::max(), asu64(0)));
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64DivU_Byzero_Const) {
REQUIRE(I64DivU);
for (uint64_t denom = 0xfffffffffffffffe; denom < 8; denom++) {
WasmRunner<uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_DIVU(WASM_GET_LOCAL(0), WASM_I64V_1(denom)));
for (uint64_t val = 0xfffffffffffffff0; val < 8; val++) {
if (denom == 0) {
CHECK_TRAP64(r.Call(val));
} else {
CHECK_EQ(val / denom, r.Call(val));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64RemS) {
REQUIRE(I64RemS);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_REMS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
if (*j == 0) {
CHECK_TRAP64(r.Call(*i, *j));
} else {
CHECK_EQ(*i % *j, r.Call(*i, *j));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64RemS_Trap) {
REQUIRE(I64RemS);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_REMS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(33, r.Call(asi64(133), asi64(100)));
CHECK_EQ(0, r.Call(std::numeric_limits<int64_t>::min(), asi64(-1)));
CHECK_TRAP64(r.Call(asi64(100), asi64(0)));
CHECK_TRAP64(r.Call(asi64(-1001), asi64(0)));
CHECK_TRAP64(r.Call(std::numeric_limits<int64_t>::min(), asi64(0)));
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64RemU) {
REQUIRE(I64RemU);
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_REMU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
if (*j == 0) {
CHECK_TRAP64(r.Call(*i, *j));
} else {
CHECK_EQ(*i % *j, r.Call(*i, *j));
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64RemU_Trap) {
REQUIRE(I64RemU);
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_REMU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(17, r.Call(asu64(217), asu64(100)));
CHECK_TRAP64(r.Call(asu64(100), asu64(0)));
CHECK_TRAP64(r.Call(asu64(1001), asu64(0)));
CHECK_TRAP64(r.Call(std::numeric_limits<uint64_t>::max(), asu64(0)));
}
WASM_EXEC_TEST(I64And) {
REQUIRE(I64And);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_AND(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ((*i) & (*j), r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64Ior) {
REQUIRE(I64Ior);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_IOR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ((*i) | (*j), r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64Xor) {
REQUIRE(I64Xor);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_XOR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ((*i) ^ (*j), r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64Shl) {
REQUIRE(I64Shl);
{
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
uint64_t expected = (*i) << (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(0)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i << 0, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(32)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i << 32, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(20)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i << 20, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHL(WASM_GET_LOCAL(0), WASM_I64V_1(40)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i << 40, r.Call(*i)); }
}
}
WASM_EXEC_TEST(I64ShrU) {
REQUIRE(I64ShrU);
{
WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
uint64_t expected = (*i) >> (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(0)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i >> 0, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(32)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i >> 32, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(20)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i >> 20, r.Call(*i)); }
}
{
WasmRunner<uint64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SHR(WASM_GET_LOCAL(0), WASM_I64V_1(40)));
FOR_UINT64_INPUTS(i) { CHECK_EQ(*i >> 40, r.Call(*i)); }
}
}
WASM_EXEC_TEST(I64ShrS) {
REQUIRE(I64ShrS);
{
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
int64_t expected = (*i) >> (*j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
{
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(0)));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i >> 0, r.Call(*i)); }
}
{
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(32)));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i >> 32, r.Call(*i)); }
}
{
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(20)));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i >> 20, r.Call(*i)); }
}
{
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_SAR(WASM_GET_LOCAL(0), WASM_I64V_1(40)));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i >> 40, r.Call(*i)); }
}
}
WASM_EXEC_TEST(I64Eq) {
REQUIRE(I64Eq);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_EQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i == *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64Ne) {
REQUIRE(I64Ne);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_NE(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i != *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64LtS) {
REQUIRE(I64LtS);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_LTS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i < *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64LeS) {
REQUIRE(I64LeS);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_LES(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i <= *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64LtU) {
REQUIRE(I64LtU);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_LTU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { CHECK_EQ(*i < *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64LeU) {
REQUIRE(I64LeU);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_LEU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { CHECK_EQ(*i <= *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64GtS) {
REQUIRE(I64GtS);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_GTS(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i > *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64GeS) {
REQUIRE(I64GeS);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_GES(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i >= *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64GtU) {
REQUIRE(I64GtU);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_GTU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { CHECK_EQ(*i > *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I64GeU) {
REQUIRE(I64GeU);
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_GEU(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) { CHECK_EQ(*i >= *j ? 1 : 0, r.Call(*i, *j)); }
}
}
WASM_EXEC_TEST(I32ConvertI64) {
REQUIRE(I32ConvertI64);
FOR_INT64_INPUTS(i) {
WasmRunner<int32_t> r(execution_mode);
BUILD(r, WASM_I32_CONVERT_I64(WASM_I64V(*i)));
CHECK_EQ(static_cast<int32_t>(*i), r.Call());
}
}
WASM_EXEC_TEST(I64SConvertI32) {
REQUIRE(I64SConvertI32);
WasmRunner<int64_t, int32_t> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)));
FOR_INT32_INPUTS(i) { CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i)); }
}
WASM_EXEC_TEST(I64UConvertI32) {
REQUIRE(I64UConvertI32);
WasmRunner<int64_t, uint32_t> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_I32(WASM_GET_LOCAL(0)));
FOR_UINT32_INPUTS(i) { CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i)); }
}
WASM_EXEC_TEST(I64Popcnt) {
struct {
int64_t expected;
uint64_t input;
} values[] = {{64, 0xffffffffffffffff},
{0, 0x0000000000000000},
{2, 0x0000080000008000},
{26, 0x1123456782345678},
{38, 0xffedcba09edcba09}};
WasmRunner<int64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_POPCNT(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(values[i].expected, r.Call(values[i].input));
}
}
WASM_EXEC_TEST(F32SConvertI64) {
REQUIRE(F32SConvertI64);
WasmRunner<float, int64_t> r(execution_mode);
BUILD(r, WASM_F32_SCONVERT_I64(WASM_GET_LOCAL(0)));
X87: Change the test case for X87 Run_WasmF32SConvertI64/Run_WasmF64SConvertI64. The CL #34701 (https://codereview.chromium.org/1779123002/) added the Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 test cases and X87 failed at them. The reason is same as the CL #33630 (Issue 1649323002: X87: Change the test case for X87 RunRoundInt32ToFloat32), please refer: https://codereview.chromium.org/1649323002. Here is the key comments from CL #33630: Some new test cases use CheckFloatEq(...) and CheckDoubleEq(...) function for result check. When GCC compiling the CheckFloatEq() and CheckDoubleEq() function, those inlined functions has different behavior comparing with GCC ia32 build and x87 build. The major difference is sse float register still has single precision rounding semantic. While X87 register has no such rounding precsion semantic when directly use register value. The V8 turbofan JITTed has exactly same result in both X87 and IA32 port. For CHECK_EQ(a, b) function, if a and b are double or float, it will has similar behaviors like CheckFloatEq(...) and CheckDoubleEq(...) function when compiled by GCC and causes the test case fail. So we add the following sentence to do type case to keep the same precision for Run_WasmF32SConvertI64/Run_WasmF64SConvertI64. Such as: volatile double expect = static_cast<float>(*i). ahaas put those codes in CHECK_FLOAT_EQ and CHECK_DOUBLE_EQ macros in CL #34534 (https://codereview.chromium.org/1773513002 ). So this CL replaced the CHECK_EQ in Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 to CHECK_FLOAT_EQ/CHECK_DOUBLE_EQ for x87 can pass Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 tests. BUG= Review URL: https://codereview.chromium.org/1784813004 Cr-Commit-Position: refs/heads/master@{#34813}
2016-03-16 13:14:01 +00:00
FOR_INT64_INPUTS(i) { CHECK_FLOAT_EQ(static_cast<float>(*i), r.Call(*i)); }
}
WASM_EXEC_TEST(F32UConvertI64) {
REQUIRE(F32UConvertI64);
struct {
uint64_t input;
uint32_t expected;
} values[] = {{0x0, 0x0},
{0x1, 0x3f800000},
{0xffffffff, 0x4f800000},
{0x1b09788b, 0x4dd84bc4},
{0x4c5fce8, 0x4c98bf9d},
{0xcc0de5bf, 0x4f4c0de6},
{0x2, 0x40000000},
{0x3, 0x40400000},
{0x4, 0x40800000},
{0x5, 0x40a00000},
{0x8, 0x41000000},
{0x9, 0x41100000},
{0xffffffffffffffff, 0x5f800000},
{0xfffffffffffffffe, 0x5f800000},
{0xfffffffffffffffd, 0x5f800000},
{0x0, 0x0},
{0x100000000, 0x4f800000},
{0xffffffff00000000, 0x5f800000},
{0x1b09788b00000000, 0x5dd84bc4},
{0x4c5fce800000000, 0x5c98bf9d},
{0xcc0de5bf00000000, 0x5f4c0de6},
{0x200000000, 0x50000000},
{0x300000000, 0x50400000},
{0x400000000, 0x50800000},
{0x500000000, 0x50a00000},
{0x800000000, 0x51000000},
{0x900000000, 0x51100000},
{0x273a798e187937a3, 0x5e1ce9e6},
{0xece3af835495a16b, 0x5f6ce3b0},
{0xb668ecc11223344, 0x5d3668ed},
{0x9e, 0x431e0000},
{0x43, 0x42860000},
{0xaf73, 0x472f7300},
{0x116b, 0x458b5800},
{0x658ecc, 0x4acb1d98},
{0x2b3b4c, 0x4a2ced30},
{0x88776655, 0x4f087766},
{0x70000000, 0x4ee00000},
{0x7200000, 0x4ce40000},
{0x7fffffff, 0x4f000000},
{0x56123761, 0x4eac246f},
{0x7fffff00, 0x4efffffe},
{0x761c4761eeeeeeee, 0x5eec388f},
{0x80000000eeeeeeee, 0x5f000000},
{0x88888888dddddddd, 0x5f088889},
{0xa0000000dddddddd, 0x5f200000},
{0xddddddddaaaaaaaa, 0x5f5dddde},
{0xe0000000aaaaaaaa, 0x5f600000},
{0xeeeeeeeeeeeeeeee, 0x5f6eeeef},
{0xfffffffdeeeeeeee, 0x5f800000},
{0xf0000000dddddddd, 0x5f700000},
{0x7fffffdddddddd, 0x5b000000},
{0x3fffffaaaaaaaa, 0x5a7fffff},
{0x1fffffaaaaaaaa, 0x59fffffd},
{0xfffff, 0x497ffff0},
{0x7ffff, 0x48ffffe0},
{0x3ffff, 0x487fffc0},
{0x1ffff, 0x47ffff80},
{0xffff, 0x477fff00},
{0x7fff, 0x46fffe00},
{0x3fff, 0x467ffc00},
{0x1fff, 0x45fff800},
{0xfff, 0x457ff000},
{0x7ff, 0x44ffe000},
{0x3ff, 0x447fc000},
{0x1ff, 0x43ff8000},
{0x3fffffffffff, 0x56800000},
{0x1fffffffffff, 0x56000000},
{0xfffffffffff, 0x55800000},
{0x7ffffffffff, 0x55000000},
{0x3ffffffffff, 0x54800000},
{0x1ffffffffff, 0x54000000},
{0x8000008000000000, 0x5f000000},
{0x8000008000000001, 0x5f000001},
{0x8000000000000400, 0x5f000000},
{0x8000000000000401, 0x5f000000}};
WasmRunner<float, uint64_t> r(execution_mode);
BUILD(r, WASM_F32_UCONVERT_I64(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(bit_cast<float>(values[i].expected), r.Call(values[i].input));
}
}
WASM_EXEC_TEST(F64SConvertI64) {
REQUIRE(F64SConvertI64);
WasmRunner<double, int64_t> r(execution_mode);
BUILD(r, WASM_F64_SCONVERT_I64(WASM_GET_LOCAL(0)));
X87: Change the test case for X87 Run_WasmF32SConvertI64/Run_WasmF64SConvertI64. The CL #34701 (https://codereview.chromium.org/1779123002/) added the Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 test cases and X87 failed at them. The reason is same as the CL #33630 (Issue 1649323002: X87: Change the test case for X87 RunRoundInt32ToFloat32), please refer: https://codereview.chromium.org/1649323002. Here is the key comments from CL #33630: Some new test cases use CheckFloatEq(...) and CheckDoubleEq(...) function for result check. When GCC compiling the CheckFloatEq() and CheckDoubleEq() function, those inlined functions has different behavior comparing with GCC ia32 build and x87 build. The major difference is sse float register still has single precision rounding semantic. While X87 register has no such rounding precsion semantic when directly use register value. The V8 turbofan JITTed has exactly same result in both X87 and IA32 port. For CHECK_EQ(a, b) function, if a and b are double or float, it will has similar behaviors like CheckFloatEq(...) and CheckDoubleEq(...) function when compiled by GCC and causes the test case fail. So we add the following sentence to do type case to keep the same precision for Run_WasmF32SConvertI64/Run_WasmF64SConvertI64. Such as: volatile double expect = static_cast<float>(*i). ahaas put those codes in CHECK_FLOAT_EQ and CHECK_DOUBLE_EQ macros in CL #34534 (https://codereview.chromium.org/1773513002 ). So this CL replaced the CHECK_EQ in Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 to CHECK_FLOAT_EQ/CHECK_DOUBLE_EQ for x87 can pass Run_WasmF32SConvertI64/Run_WasmF64SConvertI64 tests. BUG= Review URL: https://codereview.chromium.org/1784813004 Cr-Commit-Position: refs/heads/master@{#34813}
2016-03-16 13:14:01 +00:00
FOR_INT64_INPUTS(i) { CHECK_DOUBLE_EQ(static_cast<double>(*i), r.Call(*i)); }
}
WASM_EXEC_TEST(F64UConvertI64) {
REQUIRE(F64UConvertI64);
struct {
uint64_t input;
uint64_t expected;
} values[] = {{0x0, 0x0},
{0x1, 0x3ff0000000000000},
{0xffffffff, 0x41efffffffe00000},
{0x1b09788b, 0x41bb09788b000000},
{0x4c5fce8, 0x419317f3a0000000},
{0xcc0de5bf, 0x41e981bcb7e00000},
{0x2, 0x4000000000000000},
{0x3, 0x4008000000000000},
{0x4, 0x4010000000000000},
{0x5, 0x4014000000000000},
{0x8, 0x4020000000000000},
{0x9, 0x4022000000000000},
{0xffffffffffffffff, 0x43f0000000000000},
{0xfffffffffffffffe, 0x43f0000000000000},
{0xfffffffffffffffd, 0x43f0000000000000},
{0x100000000, 0x41f0000000000000},
{0xffffffff00000000, 0x43efffffffe00000},
{0x1b09788b00000000, 0x43bb09788b000000},
{0x4c5fce800000000, 0x439317f3a0000000},
{0xcc0de5bf00000000, 0x43e981bcb7e00000},
{0x200000000, 0x4200000000000000},
{0x300000000, 0x4208000000000000},
{0x400000000, 0x4210000000000000},
{0x500000000, 0x4214000000000000},
{0x800000000, 0x4220000000000000},
{0x900000000, 0x4222000000000000},
{0x273a798e187937a3, 0x43c39d3cc70c3c9c},
{0xece3af835495a16b, 0x43ed9c75f06a92b4},
{0xb668ecc11223344, 0x43a6cd1d98224467},
{0x9e, 0x4063c00000000000},
{0x43, 0x4050c00000000000},
{0xaf73, 0x40e5ee6000000000},
{0x116b, 0x40b16b0000000000},
{0x658ecc, 0x415963b300000000},
{0x2b3b4c, 0x41459da600000000},
{0x88776655, 0x41e10eeccaa00000},
{0x70000000, 0x41dc000000000000},
{0x7200000, 0x419c800000000000},
{0x7fffffff, 0x41dfffffffc00000},
{0x56123761, 0x41d5848dd8400000},
{0x7fffff00, 0x41dfffffc0000000},
{0x761c4761eeeeeeee, 0x43dd8711d87bbbbc},
{0x80000000eeeeeeee, 0x43e00000001dddde},
{0x88888888dddddddd, 0x43e11111111bbbbc},
{0xa0000000dddddddd, 0x43e40000001bbbbc},
{0xddddddddaaaaaaaa, 0x43ebbbbbbbb55555},
{0xe0000000aaaaaaaa, 0x43ec000000155555},
{0xeeeeeeeeeeeeeeee, 0x43edddddddddddde},
{0xfffffffdeeeeeeee, 0x43efffffffbdddde},
{0xf0000000dddddddd, 0x43ee0000001bbbbc},
{0x7fffffdddddddd, 0x435ffffff7777777},
{0x3fffffaaaaaaaa, 0x434fffffd5555555},
{0x1fffffaaaaaaaa, 0x433fffffaaaaaaaa},
{0xfffff, 0x412ffffe00000000},
{0x7ffff, 0x411ffffc00000000},
{0x3ffff, 0x410ffff800000000},
{0x1ffff, 0x40fffff000000000},
{0xffff, 0x40efffe000000000},
{0x7fff, 0x40dfffc000000000},
{0x3fff, 0x40cfff8000000000},
{0x1fff, 0x40bfff0000000000},
{0xfff, 0x40affe0000000000},
{0x7ff, 0x409ffc0000000000},
{0x3ff, 0x408ff80000000000},
{0x1ff, 0x407ff00000000000},
{0x3fffffffffff, 0x42cfffffffffff80},
{0x1fffffffffff, 0x42bfffffffffff00},
{0xfffffffffff, 0x42affffffffffe00},
{0x7ffffffffff, 0x429ffffffffffc00},
{0x3ffffffffff, 0x428ffffffffff800},
{0x1ffffffffff, 0x427ffffffffff000},
{0x8000008000000000, 0x43e0000010000000},
{0x8000008000000001, 0x43e0000010000000},
{0x8000000000000400, 0x43e0000000000000},
{0x8000000000000401, 0x43e0000000000001}};
WasmRunner<double, uint64_t> r(execution_mode);
BUILD(r, WASM_F64_UCONVERT_I64(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(bit_cast<double>(values[i].expected), r.Call(values[i].input));
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64SConvertF32a) {
WasmRunner<int64_t, float> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<float>(std::numeric_limits<int64_t>::min())) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64SConvertF64a) {
WasmRunner<int64_t, double> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<double>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<double>(std::numeric_limits<int64_t>::min())) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64UConvertF32a) {
WasmRunner<uint64_t, float> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64UConvertF64a) {
WasmRunner<uint64_t, double> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
WASM_EXEC_TEST(CallI64Parameter) {
ValueType param_types[20];
for (int i = 0; i < 20; i++) param_types[i] = kWasmI64;
param_types[3] = kWasmI32;
param_types[4] = kWasmI32;
FunctionSig sig(1, 19, param_types);
for (int i = 0; i < 19; i++) {
if (i == 2 || i == 3) continue;
WasmRunner<int32_t> r(execution_mode);
// Build the target function.
WasmFunctionCompiler& t = r.NewFunction(&sig);
BUILD(t, WASM_GET_LOCAL(i));
// Build the calling function.
BUILD(
r,
WASM_I32_CONVERT_I64(WASM_CALL_FUNCTION(
t.function_index(), WASM_I64V_9(0xbcd12340000000b),
WASM_I64V_9(0xbcd12340000000c), WASM_I32V_1(0xd),
WASM_I32_CONVERT_I64(WASM_I64V_9(0xbcd12340000000e)),
WASM_I64V_9(0xbcd12340000000f), WASM_I64V_10(0xbcd1234000000010),
WASM_I64V_10(0xbcd1234000000011), WASM_I64V_10(0xbcd1234000000012),
WASM_I64V_10(0xbcd1234000000013), WASM_I64V_10(0xbcd1234000000014),
WASM_I64V_10(0xbcd1234000000015), WASM_I64V_10(0xbcd1234000000016),
WASM_I64V_10(0xbcd1234000000017), WASM_I64V_10(0xbcd1234000000018),
WASM_I64V_10(0xbcd1234000000019), WASM_I64V_10(0xbcd123400000001a),
WASM_I64V_10(0xbcd123400000001b), WASM_I64V_10(0xbcd123400000001c),
WASM_I64V_10(0xbcd123400000001d))));
CHECK_EQ(i + 0xb, r.Call());
}
}
void TestI64Binop(WasmExecutionMode execution_mode, WasmOpcode opcode,
int64_t expected, int64_t a, int64_t b) {
{
WasmRunner<int64_t> r(execution_mode);
// return K op K
BUILD(r, WASM_BINOP(opcode, WASM_I64V(a), WASM_I64V(b)));
CHECK_EQ(expected, r.Call());
}
{
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
// return a op b
BUILD(r, WASM_BINOP(opcode, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(expected, r.Call(a, b));
}
}
void TestI64Cmp(WasmExecutionMode execution_mode, WasmOpcode opcode,
int64_t expected, int64_t a, int64_t b) {
{
WasmRunner<int32_t> r(execution_mode);
// return K op K
BUILD(r, WASM_BINOP(opcode, WASM_I64V(a), WASM_I64V(b)));
CHECK_EQ(expected, r.Call());
}
{
WasmRunner<int32_t, int64_t, int64_t> r(execution_mode);
// return a op b
BUILD(r, WASM_BINOP(opcode, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
CHECK_EQ(expected, r.Call(a, b));
}
}
#define TEST_I64_BINOP(name, expected, a, b) \
do { \
if (WASM_64 || kSupported_##name) \
TestI64Binop(execution_mode, kExpr##name, expected, a, b); \
} while (false)
WASM_EXEC_TEST(I64Binops) {
TEST_I64_BINOP(I64Add, -5586332274295447011, 0x501b72ebabc26847,
0x625de9793d8f79d6);
TEST_I64_BINOP(I64Sub, 9001903251710731490, 0xf24fe6474640002e,
0x7562b6f711991b4c);
TEST_I64_BINOP(I64Mul, -4569547818546064176, 0x231a263c2cbc6451,
0xead44de6bd3e23d0);
TEST_I64_BINOP(I64Mul, -25963122347507043, 0x4da1fa47c9352b73,
0x91fe82317aa035af);
TEST_I64_BINOP(I64Mul, 7640290486138131960, 0x185731abe8eea47c,
0x714ec59f1380d4c2);
TEST_I64_BINOP(I64DivS, -91517, 0x93b1190a34de56a0, 0x00004d8f68863948);
TEST_I64_BINOP(I64DivU, 149016, 0xe15b3727e8a2080a, 0x0000631bfa72db8b);
TEST_I64_BINOP(I64RemS, -664128064149968, 0x9a78b4e4fe708692,
0x0003e0b6b3be7609);
TEST_I64_BINOP(I64RemU, 1742040017332765, 0x0ce84708c6258c81,
0x000a6fde82016697);
TEST_I64_BINOP(I64And, 2531040582801836054, 0xaf257d1602644a16,
0x33b290a91a10d997);
TEST_I64_BINOP(I64Ior, 8556201506536114940, 0x169d9be7bd3f0a5c,
0x66bca28d77af40e8);
TEST_I64_BINOP(I64Xor, -4605655183785456377, 0xb6ea20a5d48e85b8,
0x76ff4da6c80688bf);
TEST_I64_BINOP(I64Shl, -7240704056088331264, 0xef4dc1ed030e8ffe, 9);
TEST_I64_BINOP(I64ShrU, 12500673744059159, 0xb1a52fa7deec5d14, 10);
TEST_I64_BINOP(I64ShrS, 1725103446999874, 0x3107c791461a112b, 11);
TEST_I64_BINOP(I64Ror, -8960135652432576946, 0x73418d1717e4e83a, 12);
TEST_I64_BINOP(I64Ror, 7617662827409989779, 0xebff67cf0c126d36, 13);
TEST_I64_BINOP(I64Rol, -2097714064174346012, 0x43938b8db0b0f230, 14);
TEST_I64_BINOP(I64Rol, 8728493013947314237, 0xe07af243ac4d219d, 15);
}
#define TEST_I64_CMP(name, expected, a, b) \
do { \
if (WASM_64 || kSupported_##name) \
TestI64Cmp(execution_mode, kExpr##name, expected, a, b); \
} while (false)
WASM_EXEC_TEST(I64Compare) {
TEST_I64_CMP(I64Eq, 0, 0xB915D8FA494064F0, 0x04D700B2536019A3);
TEST_I64_CMP(I64Ne, 1, 0xC2FAFAAAB0446CDC, 0x52A3328F780C97A3);
TEST_I64_CMP(I64LtS, 0, 0x673636E6306B0578, 0x028EC9ECA78F7227);
TEST_I64_CMP(I64LeS, 1, 0xAE5214114B86A0FA, 0x7C1D21DA3DFD0CCF);
TEST_I64_CMP(I64LtU, 0, 0x7D52166381EC1CE0, 0x59F4A6A9E78CD3D8);
TEST_I64_CMP(I64LeU, 1, 0xE4169A385C7EA0E0, 0xFBDBED2C8781E5BC);
TEST_I64_CMP(I64GtS, 0, 0x9D08FF8FB5F42E81, 0xD4E5C9D7FE09F621);
TEST_I64_CMP(I64GeS, 1, 0x78DA3B2F73264E0F, 0x6FE5E2A67C501CBE);
TEST_I64_CMP(I64GtU, 0, 0x8F691284E44F7DA9, 0xD5EA9BC1EE149192);
TEST_I64_CMP(I64GeU, 0, 0x0886A0C58C7AA224, 0x5DDBE5A81FD7EE47);
}
WASM_EXEC_TEST(I64Clz) {
REQUIRE(I64Clz);
struct {
int64_t expected;
uint64_t input;
} values[] = {{0, 0x8000100000000000}, {1, 0x4000050000000000},
{2, 0x2000030000000000}, {3, 0x1000000300000000},
{4, 0x0805000000000000}, {5, 0x0400600000000000},
{6, 0x0200000000000000}, {7, 0x010000a000000000},
{8, 0x00800c0000000000}, {9, 0x0040000000000000},
{10, 0x0020000d00000000}, {11, 0x00100f0000000000},
{12, 0x0008000000000000}, {13, 0x0004100000000000},
{14, 0x0002002000000000}, {15, 0x0001030000000000},
{16, 0x0000804000000000}, {17, 0x0000400500000000},
{18, 0x0000205000000000}, {19, 0x0000170000000000},
{20, 0x0000087000000000}, {21, 0x0000040500000000},
{22, 0x0000020300000000}, {23, 0x0000010100000000},
{24, 0x0000008900000000}, {25, 0x0000004100000000},
{26, 0x0000002200000000}, {27, 0x0000001300000000},
{28, 0x0000000800000000}, {29, 0x0000000400000000},
{30, 0x0000000200000000}, {31, 0x0000000100000000},
{32, 0x0000000080001000}, {33, 0x0000000040000500},
{34, 0x0000000020000300}, {35, 0x0000000010000003},
{36, 0x0000000008050000}, {37, 0x0000000004006000},
{38, 0x0000000002000000}, {39, 0x00000000010000a0},
{40, 0x0000000000800c00}, {41, 0x0000000000400000},
{42, 0x000000000020000d}, {43, 0x0000000000100f00},
{44, 0x0000000000080000}, {45, 0x0000000000041000},
{46, 0x0000000000020020}, {47, 0x0000000000010300},
{48, 0x0000000000008040}, {49, 0x0000000000004005},
{50, 0x0000000000002050}, {51, 0x0000000000001700},
{52, 0x0000000000000870}, {53, 0x0000000000000405},
{54, 0x0000000000000203}, {55, 0x0000000000000101},
{56, 0x0000000000000089}, {57, 0x0000000000000041},
{58, 0x0000000000000022}, {59, 0x0000000000000013},
{60, 0x0000000000000008}, {61, 0x0000000000000004},
{62, 0x0000000000000002}, {63, 0x0000000000000001},
{64, 0x0000000000000000}};
WasmRunner<int64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_CLZ(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(values[i].expected, r.Call(values[i].input));
}
}
WASM_EXEC_TEST(I64Ctz) {
REQUIRE(I64Ctz);
struct {
int64_t expected;
uint64_t input;
} values[] = {{64, 0x0000000000000000}, {63, 0x8000000000000000},
{62, 0x4000000000000000}, {61, 0x2000000000000000},
{60, 0x1000000000000000}, {59, 0xa800000000000000},
{58, 0xf400000000000000}, {57, 0x6200000000000000},
{56, 0x9100000000000000}, {55, 0xcd80000000000000},
{54, 0x0940000000000000}, {53, 0xaf20000000000000},
{52, 0xac10000000000000}, {51, 0xe0b8000000000000},
{50, 0x9ce4000000000000}, {49, 0xc792000000000000},
{48, 0xb8f1000000000000}, {47, 0x3b9f800000000000},
{46, 0xdb4c400000000000}, {45, 0xe9a3200000000000},
{44, 0xfca6100000000000}, {43, 0x6c8a780000000000},
{42, 0x8ce5a40000000000}, {41, 0xcb7d020000000000},
{40, 0xcb4dc10000000000}, {39, 0xdfbec58000000000},
{38, 0x27a9db4000000000}, {37, 0xde3bcb2000000000},
{36, 0xd7e8a61000000000}, {35, 0x9afdbc8800000000},
{34, 0x9afdbc8400000000}, {33, 0x9afdbc8200000000},
{32, 0x9afdbc8100000000}, {31, 0x0000000080000000},
{30, 0x0000000040000000}, {29, 0x0000000020000000},
{28, 0x0000000010000000}, {27, 0x00000000a8000000},
{26, 0x00000000f4000000}, {25, 0x0000000062000000},
{24, 0x0000000091000000}, {23, 0x00000000cd800000},
{22, 0x0000000009400000}, {21, 0x00000000af200000},
{20, 0x00000000ac100000}, {19, 0x00000000e0b80000},
{18, 0x000000009ce40000}, {17, 0x00000000c7920000},
{16, 0x00000000b8f10000}, {15, 0x000000003b9f8000},
{14, 0x00000000db4c4000}, {13, 0x00000000e9a32000},
{12, 0x00000000fca61000}, {11, 0x000000006c8a7800},
{10, 0x000000008ce5a400}, {9, 0x00000000cb7d0200},
{8, 0x00000000cb4dc100}, {7, 0x00000000dfbec580},
{6, 0x0000000027a9db40}, {5, 0x00000000de3bcb20},
{4, 0x00000000d7e8a610}, {3, 0x000000009afdbc88},
{2, 0x000000009afdbc84}, {1, 0x000000009afdbc82},
{0, 0x000000009afdbc81}};
WasmRunner<int64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_CTZ(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(values[i].expected, r.Call(values[i].input));
}
}
WASM_EXEC_TEST(I64Popcnt2) {
REQUIRE(I64Popcnt);
struct {
int64_t expected;
uint64_t input;
} values[] = {{64, 0xffffffffffffffff},
{0, 0x0000000000000000},
{2, 0x0000080000008000},
{26, 0x1123456782345678},
{38, 0xffedcba09edcba09}};
WasmRunner<int64_t, uint64_t> r(execution_mode);
BUILD(r, WASM_I64_POPCNT(WASM_GET_LOCAL(0)));
for (size_t i = 0; i < arraysize(values); i++) {
CHECK_EQ(values[i].expected, r.Call(values[i].input));
}
}
// Test the WasmRunner with an Int64 return value and different numbers of
// Int64 parameters.
WASM_EXEC_TEST(I64WasmRunner) {
REQUIRE(I64Param);
REQUIRE(I64Xor);
{FOR_INT64_INPUTS(i){WasmRunner<int64_t> r(execution_mode);
BUILD(r, WASM_I64V(*i));
CHECK_EQ(*i, r.Call());
}
}
{
WasmRunner<int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_GET_LOCAL(0));
FOR_INT64_INPUTS(i) { CHECK_EQ(*i, r.Call(*i)); }
}
{
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_XOR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i ^ *j, r.Call(*i, *j)); }
}
}
{
WasmRunner<int64_t, int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_XOR(WASM_GET_LOCAL(0),
WASM_I64_XOR(WASM_GET_LOCAL(1), WASM_GET_LOCAL(2))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
CHECK_EQ(*i ^ *j ^ *j, r.Call(*i, *j, *j));
CHECK_EQ(*j ^ *i ^ *j, r.Call(*j, *i, *j));
CHECK_EQ(*j ^ *j ^ *i, r.Call(*j, *j, *i));
}
}
}
{
WasmRunner<int64_t, int64_t, int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_XOR(WASM_GET_LOCAL(0),
WASM_I64_XOR(WASM_GET_LOCAL(1),
WASM_I64_XOR(WASM_GET_LOCAL(2),
WASM_GET_LOCAL(3)))));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
CHECK_EQ(*i ^ *j ^ *j ^ *j, r.Call(*i, *j, *j, *j));
CHECK_EQ(*j ^ *i ^ *j ^ *j, r.Call(*j, *i, *j, *j));
CHECK_EQ(*j ^ *j ^ *i ^ *j, r.Call(*j, *j, *i, *j));
CHECK_EQ(*j ^ *j ^ *j ^ *i, r.Call(*j, *j, *j, *i));
}
}
}
}
WASM_EXEC_TEST(Call_Int64Sub) {
REQUIRE(I64Sub);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
// Build the target function.
TestSignatures sigs;
WasmFunctionCompiler& t = r.NewFunction(sigs.l_ll());
BUILD(t, WASM_I64_SUB(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
// Build the caller function.
BUILD(r, WASM_CALL_FUNCTION(t.function_index(), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1)));
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
int64_t a = static_cast<int64_t>(*i) << 32 |
(static_cast<int64_t>(*j) | 0xFFFFFFFF);
int64_t b = static_cast<int64_t>(*j) << 32 |
(static_cast<int64_t>(*i) | 0xFFFFFFFF);
int64_t expected = static_cast<int64_t>(static_cast<uint64_t>(a) -
static_cast<uint64_t>(b));
CHECK_EQ(expected, r.Call(a, b));
}
}
}
WASM_EXEC_TEST(LoadStoreI64_sx) {
REQUIRE(I64LoadStore);
REQUIRE(DepthFirst);
byte loads[] = {kExprI64LoadMem8S, kExprI64LoadMem16S, kExprI64LoadMem32S,
kExprI64LoadMem};
for (size_t m = 0; m < arraysize(loads); m++) {
WasmRunner<int64_t> r(execution_mode);
byte* memory = r.module().AddMemoryElems<byte>(16);
byte code[] = {
kExprI32Const, 8, // --
kExprI32Const, 0, // --
loads[m], // --
ZERO_ALIGNMENT, // --
ZERO_OFFSET, // --
kExprI64StoreMem, // --
ZERO_ALIGNMENT, // --
ZERO_OFFSET, // --
kExprI32Const, 0, // --
loads[m], // --
ZERO_ALIGNMENT, // --
ZERO_OFFSET, // --
};
r.Build(code, code + arraysize(code));
// Try a bunch of different negative values.
for (int i = -1; i >= -128; i -= 11) {
int size = 1 << m;
r.module().BlankMemory();
memory[size - 1] = static_cast<byte>(i); // set the high order byte.
int64_t expected = static_cast<int64_t>(i) << ((size - 1) * 8);
CHECK_EQ(expected, r.Call());
CHECK_EQ(static_cast<byte>(i), memory[8 + size - 1]);
for (int j = size; j < 8; j++) {
CHECK_EQ(255, memory[8 + j]);
}
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64SConvertF32b) {
REQUIRE(I64SConvertF32);
WasmRunner<int64_t, float> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(INT64_MAX) &&
*i >= static_cast<float>(INT64_MIN)) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64SConvertF64b) {
REQUIRE(I64SConvertF64);
WasmRunner<int64_t, double> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<double>(INT64_MAX) &&
*i >= static_cast<double>(INT64_MIN)) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64UConvertF32b) {
REQUIRE(I64UConvertF32);
WasmRunner<uint64_t, float> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(UINT64_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(I64UConvertF64b) {
REQUIRE(I64UConvertF64);
WasmRunner<uint64_t, double> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<float>(UINT64_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
WASM_EXEC_TEST(I64ReinterpretF64) {
REQUIRE(I64ReinterpretF64);
WasmRunner<int64_t> r(execution_mode);
int64_t* memory = r.module().AddMemoryElems<int64_t>(8);
BUILD(r, WASM_I64_REINTERPRET_F64(
WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO)));
FOR_INT32_INPUTS(i) {
int64_t expected = static_cast<int64_t>(*i) * 0x300010001;
r.module().WriteMemory(&memory[0], expected);
CHECK_EQ(expected, r.Call());
}
}
WASM_EXEC_TEST(SignallingNanSurvivesI64ReinterpretF64) {
REQUIRE(I64ReinterpretF64);
WasmRunner<int64_t> r(execution_mode);
BUILD(r, WASM_I64_REINTERPRET_F64(WASM_SEQ(kExprF64Const, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf4, 0x7f)));
// This is a signalling nan.
CHECK_EQ(0x7ff4000000000000, r.Call());
}
WASM_EXEC_TEST(F64ReinterpretI64) {
REQUIRE(F64ReinterpretI64);
WasmRunner<int64_t, int64_t> r(execution_mode);
int64_t* memory = r.module().AddMemoryElems<int64_t>(8);
BUILD(r, WASM_STORE_MEM(MachineType::Float64(), WASM_ZERO,
WASM_F64_REINTERPRET_I64(WASM_GET_LOCAL(0))),
WASM_GET_LOCAL(0));
FOR_INT32_INPUTS(i) {
int64_t expected = static_cast<int64_t>(*i) * 0x300010001;
CHECK_EQ(expected, r.Call(expected));
CHECK_EQ(expected, r.module().ReadMemory<int64_t>(&memory[0]));
}
}
WASM_EXEC_TEST(LoadMemI64) {
REQUIRE(I64LoadStore);
WasmRunner<int64_t> r(execution_mode);
int64_t* memory = r.module().AddMemoryElems<int64_t>(8);
r.module().RandomizeMemory(1111);
BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO));
r.module().WriteMemory<int64_t>(&memory[0], 0x1abbccdd00112233LL);
CHECK_EQ(0x1abbccdd00112233LL, r.Call());
r.module().WriteMemory<int64_t>(&memory[0], 0x33aabbccdd001122LL);
CHECK_EQ(0x33aabbccdd001122LL, r.Call());
r.module().WriteMemory<int64_t>(&memory[0], 77777777);
CHECK_EQ(77777777, r.Call());
}
WASM_EXEC_TEST(LoadMemI64_alignment) {
REQUIRE(I64LoadStore);
for (byte alignment = 0; alignment <= 3; alignment++) {
WasmRunner<int64_t> r(execution_mode);
int64_t* memory = r.module().AddMemoryElems<int64_t>(8);
r.module().RandomizeMemory(1111);
BUILD(r,
WASM_LOAD_MEM_ALIGNMENT(MachineType::Int64(), WASM_ZERO, alignment));
r.module().WriteMemory<int64_t>(&memory[0], 0x1abbccdd00112233LL);
CHECK_EQ(0x1abbccdd00112233LL, r.Call());
r.module().WriteMemory<int64_t>(&memory[0], 0x33aabbccdd001122LL);
CHECK_EQ(0x33aabbccdd001122LL, r.Call());
r.module().WriteMemory<int64_t>(&memory[0], 77777777);
CHECK_EQ(77777777, r.Call());
}
}
WASM_EXEC_TEST(MemI64_Sum) {
REQUIRE(I64LoadStore);
REQUIRE(I64Add);
REQUIRE(I64Sub);
REQUIRE(I64Phi);
const int kNumElems = 20;
WasmRunner<uint64_t, int32_t> r(execution_mode);
uint64_t* memory = r.module().AddMemoryElems<uint64_t>(kNumElems);
const byte kSum = r.AllocateLocal(kWasmI64);
BUILD(r, WASM_WHILE(
WASM_GET_LOCAL(0),
WASM_BLOCK(
WASM_SET_LOCAL(
kSum, WASM_I64_ADD(WASM_GET_LOCAL(kSum),
WASM_LOAD_MEM(MachineType::Int64(),
WASM_GET_LOCAL(0)))),
WASM_SET_LOCAL(
0, WASM_I32_SUB(WASM_GET_LOCAL(0), WASM_I32V_1(8))))),
WASM_GET_LOCAL(1));
// Run 4 trials.
for (int i = 0; i < 3; i++) {
r.module().RandomizeMemory(i * 33);
uint64_t expected = 0;
for (size_t j = kNumElems - 1; j > 0; j--) {
expected += r.module().ReadMemory(&memory[j]);
}
uint64_t result = r.Call(8 * (kNumElems - 1));
CHECK_EQ(expected, result);
}
}
WASM_EXEC_TEST(StoreMemI64_alignment) {
const int64_t kWritten = 0x12345678abcd0011ll;
for (byte i = 0; i <= 3; i++) {
WasmRunner<int64_t, int64_t> r(execution_mode);
int64_t* memory = r.module().AddMemoryElems<int64_t>(4);
BUILD(r, WASM_STORE_MEM_ALIGNMENT(MachineType::Int64(), WASM_ZERO, i,
WASM_GET_LOCAL(0)),
WASM_GET_LOCAL(0));
r.module().RandomizeMemory(1111);
r.module().WriteMemory<int64_t>(&memory[0], 0);
CHECK_EQ(kWritten, r.Call(kWritten));
CHECK_EQ(kWritten, r.module().ReadMemory(&memory[0]));
}
}
WASM_EXEC_TEST(I64Global) {
REQUIRE(I64LoadStore);
REQUIRE(I64SConvertI32);
REQUIRE(I64And);
REQUIRE(DepthFirst);
WasmRunner<int32_t, int32_t> r(execution_mode);
int64_t* global = r.module().AddGlobal<int64_t>();
// global = global + p0
BUILD(r, WASM_SET_GLOBAL(
0, WASM_I64_AND(WASM_GET_GLOBAL(0),
WASM_I64_SCONVERT_I32(WASM_GET_LOCAL(0)))),
WASM_ZERO);
r.module().WriteMemory<int64_t>(global, 0xFFFFFFFFFFFFFFFFLL);
for (int i = 9; i < 444444; i += 111111) {
int64_t expected = *global & i;
r.Call(i);
CHECK_EQ(expected, *global);
}
}
WASM_EXEC_TEST(I64Eqz) {
REQUIRE(I64Eq);
WasmRunner<int32_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_EQZ(WASM_GET_LOCAL(0)));
FOR_INT64_INPUTS(i) {
int32_t result = *i == 0 ? 1 : 0;
CHECK_EQ(result, r.Call(*i));
}
}
WASM_EXEC_TEST(I64Ror) {
REQUIRE(I64Ror);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_ROR(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
int64_t expected = bits::RotateRight64(*i, *j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
WASM_EXEC_TEST(I64Rol) {
REQUIRE(I64Rol);
WasmRunner<int64_t, int64_t, int64_t> r(execution_mode);
BUILD(r, WASM_I64_ROL(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT64_INPUTS(i) {
FOR_UINT64_INPUTS(j) {
int64_t expected = bits::RotateLeft64(*i, *j & 0x3f);
CHECK_EQ(expected, r.Call(*i, *j));
}
}
}
[wasm] Introduce the TrapIf and TrapUnless operators to generate trap code. Some instructions in WebAssembly trap for some inputs, which means that the execution is terminated and (at least at the moment) a JavaScript exception is thrown. Examples for traps are out-of-bounds memory accesses, or integer divisions by zero. Without the TrapIf and TrapUnless operators trap check in WebAssembly introduces 5 TurboFan nodes (branch, if_true, if_false, trap-reason constant, trap-position constant), in addition to the trap condition itself. Additionally, each WebAssembly function has four TurboFan nodes (merge, effect_phi, 2 phis) whose number of inputs is linear to the number of trap checks in the function. Especially for functions with high numbers of trap checks we observe a significant slowdown in compilation time, down to 0.22 MiB/s in the sqlite benchmark instead of the average of 3 MiB/s in other benchmarks. By introducing a TrapIf common operator only a single node is necessary per trap check, in addition to the trap condition. Also the nodes which are shared between trap checks (merge, effect_phi, 2 phis) would disappear. First measurements suggest a speedup of 30-50% on average. This CL only implements TrapIf and TrapUnless on x64. The implementation is also hidden behind the --wasm-trap-if flag. Please take a special look at how the source position is transfered from the instruction selector to the code generator, and at the context that is used for the runtime call. R=titzer@chromium.org Review-Url: https://codereview.chromium.org/2562393002 Cr-Commit-Position: refs/heads/master@{#41720}
2016-12-15 13:31:29 +00:00
WASM_EXEC_TEST_WITH_TRAP(StoreMem_offset_oob_i64) {
static const MachineType machineTypes[] = {
MachineType::Int8(), MachineType::Uint8(), MachineType::Int16(),
MachineType::Uint16(), MachineType::Int32(), MachineType::Uint32(),
MachineType::Int64(), MachineType::Uint64(), MachineType::Float32(),
MachineType::Float64()};
for (size_t m = 0; m < arraysize(machineTypes); m++) {
WasmRunner<int32_t, uint32_t> r(execution_mode);
byte* memory = r.module().AddMemoryElems<byte>(32);
r.module().RandomizeMemory(1119 + static_cast<int>(m));
BUILD(r, WASM_STORE_MEM_OFFSET(machineTypes[m], 8, WASM_GET_LOCAL(0),
WASM_LOAD_MEM(machineTypes[m], WASM_ZERO)),
WASM_ZERO);
byte memsize = WasmOpcodes::MemSize(machineTypes[m]);
uint32_t boundary = 24 - memsize;
CHECK_EQ(0, r.Call(boundary)); // in bounds.
CHECK_EQ(0, memcmp(&memory[0], &memory[8 + boundary], memsize));
for (uint32_t offset = boundary + 1; offset < boundary + 19; offset++) {
CHECK_TRAP(r.Call(offset)); // out of bounds.
}
}
}
#define ADD_CODE(vec, ...) \
do { \
byte __buf[] = {__VA_ARGS__}; \
for (size_t i = 0; i < sizeof(__buf); i++) vec.push_back(__buf[i]); \
} while (false)
static void CompileCallIndirectMany(ValueType param) {
// Make sure we don't run out of registers when compiling indirect calls
// with many many parameters.
TestSignatures sigs;
for (byte num_params = 0; num_params < 40; num_params++) {
WasmRunner<void> r(kExecuteCompiled);
FunctionSig* sig = sigs.many(r.zone(), kWasmStmt, param, num_params);
r.module().AddSignature(sig);
r.module().AddSignature(sig);
r.module().AddIndirectFunctionTable(nullptr, 0);
WasmFunctionCompiler& t = r.NewFunction(sig);
std::vector<byte> code;
for (byte p = 0; p < num_params; p++) {
ADD_CODE(code, kExprGetLocal, p);
}
ADD_CODE(code, kExprI32Const, 0);
ADD_CODE(code, kExprCallIndirect, 1, TABLE_ZERO);
t.Build(&code[0], &code[0] + code.size());
}
}
TEST(Compile_Wasm_CallIndirect_Many_i64) { CompileCallIndirectMany(kWasmI64); }
static void Run_WasmMixedCall_N(WasmExecutionMode execution_mode, int start) {
const int kExpected = 6333;
const int kElemSize = 8;
TestSignatures sigs;
static MachineType mixed[] = {
MachineType::Int32(), MachineType::Float32(), MachineType::Int64(),
MachineType::Float64(), MachineType::Float32(), MachineType::Int64(),
MachineType::Int32(), MachineType::Float64(), MachineType::Float32(),
MachineType::Float64(), MachineType::Int32(), MachineType::Int64(),
MachineType::Int32(), MachineType::Int32()};
int num_params = static_cast<int>(arraysize(mixed)) - start;
for (int which = 0; which < num_params; which++) {
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
WasmRunner<int32_t> r(execution_mode);
r.module().AddMemory(1024);
MachineType* memtypes = &mixed[start];
MachineType result = memtypes[which];
// =========================================================================
// Build the selector function.
// =========================================================================
FunctionSig::Builder b(&zone, 1, num_params);
b.AddReturn(WasmOpcodes::ValueTypeFor(result));
for (int i = 0; i < num_params; i++) {
b.AddParam(WasmOpcodes::ValueTypeFor(memtypes[i]));
}
WasmFunctionCompiler& t = r.NewFunction(b.Build());
BUILD(t, WASM_GET_LOCAL(which));
// =========================================================================
// Build the calling function.
// =========================================================================
std::vector<byte> code;
// Load the offset for the store.
ADD_CODE(code, WASM_ZERO);
// Load the arguments.
for (int i = 0; i < num_params; i++) {
int offset = (i + 1) * kElemSize;
ADD_CODE(code, WASM_LOAD_MEM(memtypes[i], WASM_I32V_2(offset)));
}
// Call the selector function.
ADD_CODE(code, WASM_CALL_FUNCTION0(t.function_index()));
// Store the result in memory.
ADD_CODE(code,
static_cast<byte>(WasmOpcodes::LoadStoreOpcodeOf(result, true)),
ZERO_ALIGNMENT, ZERO_OFFSET);
// Return the expected value.
ADD_CODE(code, WASM_I32V_2(kExpected));
r.Build(&code[0], &code[0] + code.size());
// Run the code.
for (int t = 0; t < 10; t++) {
r.module().RandomizeMemory();
CHECK_EQ(kExpected, r.Call());
int size = WasmOpcodes::MemSize(result);
for (int i = 0; i < size; i++) {
int base = (which + 1) * kElemSize;
byte expected = r.module().raw_mem_at<byte>(base + i);
byte result = r.module().raw_mem_at<byte>(i);
CHECK_EQ(expected, result);
}
}
}
}
WASM_EXEC_TEST(MixedCall_i64_0) { Run_WasmMixedCall_N(execution_mode, 0); }
WASM_EXEC_TEST(MixedCall_i64_1) { Run_WasmMixedCall_N(execution_mode, 1); }
WASM_EXEC_TEST(MixedCall_i64_2) { Run_WasmMixedCall_N(execution_mode, 2); }
WASM_EXEC_TEST(MixedCall_i64_3) { Run_WasmMixedCall_N(execution_mode, 3); }
WASM_EXEC_TEST(Regress5874) {
REQUIRE(I32ConvertI64);
REQUIRE(I64LoadStore);
REQUIRE(I64Const);
WasmRunner<int32_t> r(execution_mode);
r.module().AddMemoryElems<int64_t>(8);
BUILD(r, kExprI64Const, 0x00, // --
kExprI32ConvertI64, // --
kExprI64Const, 0x00, // --
kExprI64StoreMem, 0x03, 0x00, // --
kExprI32Const, 0x00); // --
r.Call();
}