[wasm] Introduce special bytecodes for asm.js division/remainder instead of relying on module state.

R=ahaas@chromium.org, bradnelson@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/1968493002
Cr-Commit-Position: refs/heads/master@{#36148}
This commit is contained in:
titzer 2016-05-10 10:58:03 -07:00 committed by Commit bot
parent ab3f008b8a
commit 067a0d6c61
7 changed files with 226 additions and 188 deletions

View File

@ -614,15 +614,20 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
return BuildF32Max(left, right);
case wasm::kExprF64Max:
return BuildF64Max(left, right);
case wasm::kExprF64Pow: {
case wasm::kExprF64Pow:
return BuildF64Pow(left, right);
}
case wasm::kExprF64Atan2: {
case wasm::kExprF64Atan2:
return BuildF64Atan2(left, right);
}
case wasm::kExprF64Mod: {
case wasm::kExprF64Mod:
return BuildF64Mod(left, right);
}
case wasm::kExprI32AsmjsDivS:
return BuildI32AsmjsDivS(left, right);
case wasm::kExprI32AsmjsDivU:
return BuildI32AsmjsDivU(left, right);
case wasm::kExprI32AsmjsRemS:
return BuildI32AsmjsRemS(left, right);
case wasm::kExprI32AsmjsRemU:
return BuildI32AsmjsRemU(left, right);
default:
op = UnsupportedOpcode(opcode);
}
@ -1591,34 +1596,6 @@ Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction(
Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
wasm::WasmCodePosition position) {
MachineOperatorBuilder* m = jsgraph()->machine();
if (module_ && module_->asm_js()) {
// asm.js semantics return 0 on divide or mod by zero.
if (m->Int32DivIsSafe()) {
// The hardware instruction does the right thing (e.g. arm).
return graph()->NewNode(m->Int32Div(), left, right, graph()->start());
}
// Check denominator for zero.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
// Check numerator for -1. (avoid minint / -1 case).
Diamond n(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)),
BranchHint::kFalse);
Node* div = graph()->NewNode(m->Int32Div(), left, right, z.if_false);
Node* neg =
graph()->NewNode(m->Int32Sub(), jsgraph()->Int32Constant(0), left);
return n.Phi(MachineRepresentation::kWord32, neg,
z.Phi(MachineRepresentation::kWord32,
jsgraph()->Int32Constant(0), div));
}
trap_->ZeroCheck32(wasm::kTrapDivByZero, right, position);
Node* before = *control_;
Node* denom_is_m1;
@ -1640,26 +1617,6 @@ Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
Node* WasmGraphBuilder::BuildI32RemS(Node* left, Node* right,
wasm::WasmCodePosition position) {
MachineOperatorBuilder* m = jsgraph()->machine();
if (module_ && module_->asm_js()) {
// asm.js semantics return 0 on divide or mod by zero.
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
// Explicit check for x % -1.
Diamond d(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)),
BranchHint::kFalse);
d.Chain(z.if_false);
return z.Phi(
MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
graph()->NewNode(m->Int32Mod(), left, right, d.if_false)));
}
trap_->ZeroCheck32(wasm::kTrapRemByZero, right, position);
@ -1676,23 +1633,6 @@ Node* WasmGraphBuilder::BuildI32RemS(Node* left, Node* right,
Node* WasmGraphBuilder::BuildI32DivU(Node* left, Node* right,
wasm::WasmCodePosition position) {
MachineOperatorBuilder* m = jsgraph()->machine();
if (module_ && module_->asm_js()) {
// asm.js semantics return 0 on divide or mod by zero.
if (m->Uint32DivIsSafe()) {
// The hardware instruction does the right thing (e.g. arm).
return graph()->NewNode(m->Uint32Div(), left, right, graph()->start());
}
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
graph()->NewNode(jsgraph()->machine()->Uint32Div(), left,
right, z.if_false));
}
return graph()->NewNode(
m->Uint32Div(), left, right,
trap_->ZeroCheck32(wasm::kTrapDivByZero, right, position));
@ -1701,25 +1641,96 @@ Node* WasmGraphBuilder::BuildI32DivU(Node* left, Node* right,
Node* WasmGraphBuilder::BuildI32RemU(Node* left, Node* right,
wasm::WasmCodePosition position) {
MachineOperatorBuilder* m = jsgraph()->machine();
if (module_ && module_->asm_js()) {
// asm.js semantics return 0 on divide or mod by zero.
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
Node* rem = graph()->NewNode(jsgraph()->machine()->Uint32Mod(), left, right,
z.if_false);
return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
rem);
}
return graph()->NewNode(
m->Uint32Mod(), left, right,
trap_->ZeroCheck32(wasm::kTrapRemByZero, right, position));
}
Node* WasmGraphBuilder::BuildI32AsmjsDivS(Node* left, Node* right) {
MachineOperatorBuilder* m = jsgraph()->machine();
// asm.js semantics return 0 on divide or mod by zero.
if (m->Int32DivIsSafe()) {
// The hardware instruction does the right thing (e.g. arm).
return graph()->NewNode(m->Int32Div(), left, right, graph()->start());
}
// Check denominator for zero.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
// Check numerator for -1. (avoid minint / -1 case).
Diamond n(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)),
BranchHint::kFalse);
Node* div = graph()->NewNode(m->Int32Div(), left, right, z.if_false);
Node* neg =
graph()->NewNode(m->Int32Sub(), jsgraph()->Int32Constant(0), left);
return n.Phi(
MachineRepresentation::kWord32, neg,
z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0), div));
}
Node* WasmGraphBuilder::BuildI32AsmjsRemS(Node* left, Node* right) {
MachineOperatorBuilder* m = jsgraph()->machine();
// asm.js semantics return 0 on divide or mod by zero.
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
// Explicit check for x % -1.
Diamond d(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(-1)),
BranchHint::kFalse);
d.Chain(z.if_false);
return z.Phi(
MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
d.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
graph()->NewNode(m->Int32Mod(), left, right, d.if_false)));
}
Node* WasmGraphBuilder::BuildI32AsmjsDivU(Node* left, Node* right) {
MachineOperatorBuilder* m = jsgraph()->machine();
// asm.js semantics return 0 on divide or mod by zero.
if (m->Uint32DivIsSafe()) {
// The hardware instruction does the right thing (e.g. arm).
return graph()->NewNode(m->Uint32Div(), left, right, graph()->start());
}
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
graph()->NewNode(jsgraph()->machine()->Uint32Div(), left, right,
z.if_false));
}
Node* WasmGraphBuilder::BuildI32AsmjsRemU(Node* left, Node* right) {
MachineOperatorBuilder* m = jsgraph()->machine();
// asm.js semantics return 0 on divide or mod by zero.
// Explicit check for x % 0.
Diamond z(
graph(), jsgraph()->common(),
graph()->NewNode(m->Word32Equal(), right, jsgraph()->Int32Constant(0)),
BranchHint::kFalse);
Node* rem = graph()->NewNode(jsgraph()->machine()->Uint32Mod(), left, right,
z.if_false);
return z.Phi(MachineRepresentation::kWord32, jsgraph()->Int32Constant(0),
rem);
}
Node* WasmGraphBuilder::BuildI64DivS(Node* left, Node* right,
wasm::WasmCodePosition position) {
if (jsgraph()->machine()->Is32()) {

View File

@ -267,6 +267,11 @@ class WasmGraphBuilder {
Node* BuildI32DivU(Node* left, Node* right, wasm::WasmCodePosition position);
Node* BuildI32RemU(Node* left, Node* right, wasm::WasmCodePosition position);
Node* BuildI32AsmjsDivS(Node* left, Node* right);
Node* BuildI32AsmjsRemS(Node* left, Node* right);
Node* BuildI32AsmjsDivU(Node* left, Node* right);
Node* BuildI32AsmjsRemU(Node* left, Node* right);
Node* BuildI64DivS(Node* left, Node* right, wasm::WasmCodePosition position);
Node* BuildI64RemS(Node* left, Node* right, wasm::WasmCodePosition position);
Node* BuildI64DivU(Node* left, Node* right, wasm::WasmCodePosition position);

View File

@ -1383,9 +1383,6 @@ class AsmWasmBuilderImpl : public AstVisitor {
#ifdef Mul
#undef Mul
#endif
#ifdef Div
#undef Div
#endif
#define NON_SIGNED_BINOP(op) \
static WasmOpcode opcodes[] = { \
@ -1464,19 +1461,25 @@ class AsmWasmBuilderImpl : public AstVisitor {
BINOP_CASE(Token::ADD, Add, NON_SIGNED_BINOP, true);
BINOP_CASE(Token::SUB, Sub, NON_SIGNED_BINOP, true);
BINOP_CASE(Token::MUL, Mul, NON_SIGNED_BINOP, true);
BINOP_CASE(Token::DIV, Div, SIGNED_BINOP, false);
BINOP_CASE(Token::BIT_OR, Ior, NON_SIGNED_INT_BINOP, true);
BINOP_CASE(Token::BIT_AND, And, NON_SIGNED_INT_BINOP, true);
BINOP_CASE(Token::BIT_XOR, Xor, NON_SIGNED_INT_BINOP, true);
BINOP_CASE(Token::SHL, Shl, NON_SIGNED_INT_BINOP, true);
BINOP_CASE(Token::SAR, ShrS, NON_SIGNED_INT_BINOP, true);
BINOP_CASE(Token::SHR, ShrU, NON_SIGNED_INT_BINOP, true);
case Token::DIV: {
static WasmOpcode opcodes[] = {kExprI32AsmjsDivS, kExprI32AsmjsDivU,
kExprF32Div, kExprF64Div};
int type = TypeIndexOf(expr->left(), expr->right(), false);
current_function_builder_->Emit(opcodes[type]);
break;
}
case Token::MOD: {
TypeIndex type = TypeIndexOf(expr->left(), expr->right(), false);
if (type == kInt32) {
current_function_builder_->Emit(kExprI32RemS);
current_function_builder_->Emit(kExprI32AsmjsRemS);
} else if (type == kUint32) {
current_function_builder_->Emit(kExprI32RemU);
current_function_builder_->Emit(kExprI32AsmjsRemU);
} else if (type == kFloat64) {
current_function_builder_->Emit(kExprF64Mod);
return;

View File

@ -256,29 +256,30 @@ const WasmCodePosition kNoCodePosition = -1;
V(F64Log, 0xc7, d_d) \
V(F64Atan2, 0xc8, d_dd) \
V(F64Pow, 0xc9, d_dd) \
V(F64Mod, 0xca, d_dd)
V(F64Mod, 0xca, d_dd) \
V(I32AsmjsDivS, 0xd0, i_ii) \
V(I32AsmjsDivU, 0xd1, i_ii) \
V(I32AsmjsRemS, 0xd2, i_ii) \
V(I32AsmjsRemU, 0xd3, i_ii)
// TODO(titzer): sketch of asm-js compatibility bytecodes
/* V(I32AsmjsDivS, 0xd0, i_ii) \ */
/* V(I32AsmjsDivU, 0xd1, i_ii) \ */
/* V(I32AsmjsRemS, 0xd2, i_ii) \ */
/* V(I32AsmjsRemU, 0xd3, i_ii) \ */
/* V(I32AsmjsLoad8S, 0xd4, i_i) \ */
/* V(I32AsmjsLoad8U, 0xd5, i_i) \ */
/* V(I32AsmjsLoad16S, 0xd6, i_i) \ */
/* V(I32AsmjsLoad16U, 0xd7, i_i) \ */
/* V(I32AsmjsLoad, 0xd8, i_i) \ */
/* V(F32AsmjsLoad, 0xd9, f_i) \ */
/* V(F64AsmjsLoad, 0xda, d_i) \ */
/* V(I32AsmjsStore8, 0xdb, i_i) \ */
/* V(I32AsmjsStore16, 0xdc, i_i) \ */
/* V(I32AsmjsStore, 0xdd, i_ii) \ */
/* V(F32AsmjsStore, 0xde, i_if) \ */
/* V(F64AsmjsStore, 0xdf, i_id) \ */
/* V(I32SAsmjsConvertF32, 0xe0, i_f) \ */
/* V(I32UAsmjsConvertF32, 0xe1, i_f) \ */
/* V(I32SAsmjsConvertF64, 0xe2, i_d) \ */
/* V(I32SAsmjsConvertF64, 0xe3, i_d) */
/* TODO(titzer): introduce compatibility opcodes for these asm.js ops \
V(I32AsmjsLoad8S, 0xd4, i_i) \ \
V(I32AsmjsLoad8U, 0xd5, i_i) \ \
V(I32AsmjsLoad16S, 0xd6, i_i) \ \
V(I32AsmjsLoad16U, 0xd7, i_i) \ \
V(I32AsmjsLoad, 0xd8, i_i) \ \
V(F32AsmjsLoad, 0xd9, f_i) \ \
V(F64AsmjsLoad, 0xda, d_i) \ \
V(I32AsmjsStore8, 0xdb, i_i) \ \
V(I32AsmjsStore16, 0xdc, i_i) \ \
V(I32AsmjsStore, 0xdd, i_ii) \ \
V(F32AsmjsStore, 0xde, i_if) \ \
V(F64AsmjsStore, 0xdf, i_id) \ \
V(I32SAsmjsConvertF32, 0xe0, i_f) \ \
V(I32UAsmjsConvertF32, 0xe1, i_f) \ \
V(I32SAsmjsConvertF64, 0xe2, i_d) \ \
V(I32SAsmjsConvertF64, 0xe3, i_d) \
*/
// All opcodes.
#define FOREACH_OPCODE(V) \

View File

@ -188,6 +188,7 @@
'trace-extension.cc',
'wasm/test-run-wasm.cc',
'wasm/test-run-wasm-64.cc',
'wasm/test-run-wasm-asmjs.cc',
'wasm/test-run-wasm-js.cc',
'wasm/test-run-wasm-module.cc',
'wasm/test-signatures.h',

View File

@ -0,0 +1,92 @@
// 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/base/platform/elapsed-timer.h"
#include "src/wasm/wasm-macro-gen.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/value-helper.h"
#include "test/cctest/wasm/test-signatures.h"
#include "test/cctest/wasm/wasm-run-utils.h"
using namespace v8::base;
using namespace v8::internal;
using namespace v8::internal::compiler;
using namespace v8::internal::wasm;
// for even shorter tests.
#define B2(a, b) kExprBlock, a, b, kExprEnd
#define B1(a) kExprBlock, a, kExprEnd
#define RET(x) x, kExprReturn, 1
#define RET_I8(x) kExprI8Const, x, kExprReturn, 1
TEST(Run_WASM_Int32AsmjsDivS) {
WasmRunner<int32_t> r(MachineType::Int32(), MachineType::Int32());
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));
}
TEST(Run_WASM_Int32AsmjsRemS) {
WasmRunner<int32_t> r(MachineType::Int32(), MachineType::Int32());
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));
}
TEST(Run_WASM_Int32AsmjsDivU) {
WasmRunner<int32_t> r(MachineType::Int32(), MachineType::Int32());
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));
}
TEST(Run_WASM_Int32AsmjsRemU) {
WasmRunner<int32_t> r(MachineType::Int32(), MachineType::Int32());
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));
}
TEST(Run_Wasm_LoadMemI32_oob_asm) {
TestingModule module;
module.origin = kAsmJsOrigin;
int32_t* memory = module.AddMemoryElems<int32_t>(8);
WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_LOAD_MEM(MachineType::Int32(), 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));
}
}

View File

@ -409,59 +409,6 @@ TEST(Run_WASM_Int32RemU_trap) {
CHECK_EQ(kMin, r.Call(kMin, -1));
}
TEST(Run_WASM_Int32DivS_asmjs) {
TestingModule module;
module.origin = kAsmJsOrigin;
WasmRunner<int32_t> r(&module, MachineType::Int32(), MachineType::Int32());
BUILD(r, WASM_I32_DIVS(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));
}
TEST(Run_WASM_Int32RemS_asmjs) {
TestingModule module;
module.origin = kAsmJsOrigin;
WasmRunner<int32_t> r(&module, MachineType::Int32(), MachineType::Int32());
BUILD(r, WASM_I32_REMS(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));
}
TEST(Run_WASM_Int32DivU_asmjs) {
TestingModule module;
module.origin = kAsmJsOrigin;
WasmRunner<int32_t> r(&module, MachineType::Int32(), MachineType::Int32());
BUILD(r, WASM_I32_DIVU(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));
}
TEST(Run_WASM_Int32RemU_asmjs) {
TestingModule module;
module.origin = kAsmJsOrigin;
WasmRunner<int32_t> r(&module, MachineType::Int32(), MachineType::Int32());
BUILD(r, WASM_I32_REMU(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));
}
TEST(Run_WASM_Int32DivS_byzero_const) {
for (int8_t denom = -2; denom < 8; denom++) {
WasmRunner<int32_t> r(MachineType::Int32());
@ -1403,28 +1350,6 @@ TEST(Run_Wasm_LoadMemI32_oob) {
}
TEST(Run_Wasm_LoadMemI32_oob_asm) {
TestingModule module;
module.origin = kAsmJsOrigin;
int32_t* memory = module.AddMemoryElems<int32_t>(8);
WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_LOAD_MEM(MachineType::Int32(), 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));
}
}
TEST(Run_Wasm_LoadMem_offset_oob) {
TestingModule module;
module.AddMemoryElems<int32_t>(8);