Commit 57c8f1da authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Refactor GC tests

Add some abstractions to make it easier to define more tests.

Bug: v8:7748
Change-Id: Ia5605aa10963228eb4bfba37e2b412fc5af860d8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2224212
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68147}
parent dc618604
......@@ -25,389 +25,348 @@ namespace internal {
namespace wasm {
namespace test_gc {
WASM_EXEC_TEST(BasicStruct) {
// TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return;
if (execution_tier == ExecutionTier::kInterpreter) return;
using F = std::pair<ValueType, bool>;
class WasmGCTester {
public:
WasmGCTester()
: flag_gc(&v8::internal::FLAG_experimental_wasm_gc, true),
flag_reftypes(&v8::internal::FLAG_experimental_wasm_anyref, true),
flag_typedfuns(&v8::internal::FLAG_experimental_wasm_typed_funcref,
true),
allocator(),
zone(&allocator, ZONE_NAME),
builder(&zone),
isolate(CcTest::InitIsolateOnce()),
scope(isolate),
instance(),
thrower(isolate, "Test wasm GC") {
testing::SetupIsolateForWasmModule(isolate);
}
uint32_t AddGlobal(ValueType type, bool mutability, WasmInitExpr init) {
return builder.AddGlobal(type, mutability, init);
}
void DefineFunction(const char* name, FunctionSig* sig,
std::initializer_list<ValueType> locals,
std::initializer_list<byte> code) {
WasmFunctionBuilder* fun = builder.AddFunction(sig);
builder.AddExport(CStrVector(name), fun);
for (ValueType local : locals) {
fun->AddLocal(local);
}
fun->EmitCode(code.begin(), static_cast<uint32_t>(code.size()));
}
uint32_t DefineStruct(std::initializer_list<F> fields) {
StructType::Builder type_builder(&zone,
static_cast<uint32_t>(fields.size()));
for (F field : fields) {
type_builder.AddField(field.first, field.second);
}
return builder.AddStructType(type_builder.Build());
}
uint32_t DefineArray(ValueType element_type, bool mutability) {
return builder.AddArrayType(new (&zone)
ArrayType(element_type, mutability));
}
void CompileModule() {
ZoneBuffer buffer(&zone);
builder.WriteTo(&buffer);
MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg());
instance = maybe_instance.ToHandleChecked();
}
void CheckResult(const char* function, int32_t expected,
std::initializer_list<Object> args) {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0;
for (Object arg : args) {
argv[i++] = handle(arg, isolate);
}
CHECK_EQ(expected, testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, function,
static_cast<uint32_t>(args.size()), argv));
}
// TODO(7748): This uses the JavaScript interface to retrieve the plain
// WasmStruct. Once the JS interaction story is settled, this may well
// need to be changed.
MaybeHandle<Object> GetJSResult(const char* function,
std::initializer_list<Object> args) {
Handle<Object>* argv = zone.NewArray<Handle<Object>>(args.size());
int i = 0;
for (Object arg : args) {
argv[i++] = handle(arg, isolate);
}
Handle<WasmExportedFunction> exported =
testing::GetExportedFunction(isolate, instance, function)
.ToHandleChecked();
return Execution::Call(isolate, exported,
isolate->factory()->undefined_value(),
static_cast<uint32_t>(args.size()), argv);
}
void CheckHasThrown(const char* function,
std::initializer_list<Object> args) {
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
MaybeHandle<Object> result = GetJSResult(function, args);
CHECK(result.is_null());
CHECK(try_catch.HasCaught());
isolate->clear_pending_exception();
}
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
StructType::Builder type_builder(&zone, 2);
type_builder.AddField(kWasmI32, true);
type_builder.AddField(kWasmI32, true);
int32_t type_index = builder->AddStructType(type_builder.Build());
private:
const FlagScope<bool> flag_gc;
const FlagScope<bool> flag_reftypes;
const FlagScope<bool> flag_typedfuns;
v8::internal::AccountingAllocator allocator;
Zone zone;
WasmModuleBuilder builder;
Isolate* const isolate;
const HandleScope scope;
Handle<WasmInstanceObject> instance;
ErrorThrower thrower;
};
// TODO(7748): Use WASM_EXEC_TEST once interpreter and liftoff are supported
TEST(WasmBasicStruct) {
WasmGCTester tester;
uint32_t type_index =
tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
ValueType kOptRefType = ValueType(ValueType::kOptRef, type_index);
FunctionSig sig_q_v(1, 0, kRefTypes);
// Test struct.new and struct.get.
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
f->builder()->AddExport(CStrVector("f"), f);
byte f_code[] = {WASM_STRUCT_GET(type_index, 0,
WASM_STRUCT_NEW(type_index, WASM_I32V(42),
WASM_I32V(64))),
kExprEnd};
f->EmitCode(f_code, sizeof(f_code));
tester.DefineFunction(
"f", tester.sigs.i_v(), {},
{WASM_STRUCT_GET(
type_index, 0,
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64))),
kExprEnd});
// Test struct.new and struct.get.
WasmFunctionBuilder* g = builder->AddFunction(sigs.i_v());
g->builder()->AddExport(CStrVector("g"), g);
byte g_code[] = {WASM_STRUCT_GET(type_index, 1,
WASM_STRUCT_NEW(type_index, WASM_I32V(42),
WASM_I32V(64))),
kExprEnd};
g->EmitCode(g_code, sizeof(g_code));
tester.DefineFunction(
"g", tester.sigs.i_v(), {},
{WASM_STRUCT_GET(
type_index, 1,
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64))),
kExprEnd});
// Test struct.new, returning struct references to JS.
WasmFunctionBuilder* h = builder->AddFunction(&sig_q_v);
h->builder()->AddExport(CStrVector("h"), h);
byte h_code[] = {WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64)),
kExprEnd};
h->EmitCode(h_code, sizeof(h_code));
tester.DefineFunction(
"h", &sig_q_v, {},
{WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64)), kExprEnd});
// Test struct.set, struct refs types in locals.
WasmFunctionBuilder* j = builder->AddFunction(sigs.i_v());
uint32_t j_local_index = j->AddLocal(kOptRefType);
uint32_t j_local_index = 0;
uint32_t j_field_index = 0;
j->builder()->AddExport(CStrVector("j"), j);
byte j_code[] = {
WASM_SET_LOCAL(j_local_index,
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(64))),
WASM_STRUCT_SET(type_index, j_field_index, WASM_GET_LOCAL(j_local_index),
WASM_I32V(-99)),
WASM_STRUCT_GET(type_index, j_field_index, WASM_GET_LOCAL(j_local_index)),
kExprEnd};
j->EmitCode(j_code, sizeof(j_code));
tester.DefineFunction(
"j", tester.sigs.i_v(), {kOptRefType},
{WASM_SET_LOCAL(j_local_index, WASM_STRUCT_NEW(type_index, WASM_I32V(42),
WASM_I32V(64))),
WASM_STRUCT_SET(type_index, j_field_index, WASM_GET_LOCAL(j_local_index),
WASM_I32V(-99)),
WASM_STRUCT_GET(type_index, j_field_index,
WASM_GET_LOCAL(j_local_index)),
kExprEnd});
// Test struct.set, ref.as_non_null,
// struct refs types in globals and if-results.
uint32_t k_global_index = builder->AddGlobal(
uint32_t k_global_index = tester.AddGlobal(
kOptRefType, true, WasmInitExpr(WasmInitExpr::kRefNullConst));
WasmFunctionBuilder* k = builder->AddFunction(sigs.i_v());
uint32_t k_field_index = 0;
k->builder()->AddExport(CStrVector("k"), k);
byte k_code[] = {
WASM_SET_GLOBAL(k_global_index, WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_STRUCT_GET(
type_index, k_field_index,
WASM_REF_AS_NON_NULL(WASM_IF_ELSE_R(kOptRefType, WASM_I32V(1),
WASM_GET_GLOBAL(k_global_index),
WASM_REF_NULL_GC(type_index)))),
kExprEnd};
k->EmitCode(k_code, sizeof(k_code));
tester.DefineFunction(
"k", tester.sigs.i_v(), {},
{WASM_SET_GLOBAL(
k_global_index,
WASM_STRUCT_NEW(type_index, WASM_I32V(55), WASM_I32V(66))),
WASM_STRUCT_GET(
type_index, k_field_index,
WASM_REF_AS_NON_NULL(WASM_IF_ELSE_R(kOptRefType, WASM_I32V(1),
WASM_GET_GLOBAL(k_global_index),
WASM_REF_NULL_GC(type_index)))),
kExprEnd});
// Test br_on_null 1.
WasmFunctionBuilder* l = builder->AddFunction(sigs.i_v());
uint32_t l_local_index = l->AddLocal(kOptRefType);
l->builder()->AddExport(CStrVector("l"), l);
byte l_code[] = {
WASM_BLOCK_I(WASM_I32V(42),
// Branch will be taken.
// 42 left on stack outside the block (not 52).
WASM_BR_ON_NULL(0, WASM_GET_LOCAL(l_local_index)),
WASM_I32V(52), WASM_BR(0)),
kExprEnd};
l->EmitCode(l_code, sizeof(l_code));
uint32_t l_local_index = 0;
tester.DefineFunction(
"l", tester.sigs.i_v(), {kOptRefType},
{WASM_BLOCK_I(WASM_I32V(42),
// Branch will be taken.
// 42 left on stack outside the block (not 52).
WASM_BR_ON_NULL(0, WASM_GET_LOCAL(l_local_index)),
WASM_I32V(52), WASM_BR(0)),
kExprEnd});
// Test br_on_null 2.
WasmFunctionBuilder* m = builder->AddFunction(sigs.i_v());
uint32_t m_field_index = 0;
m->builder()->AddExport(CStrVector("m"), m);
byte m_code[] = {
WASM_BLOCK_I(
WASM_I32V(42),
WASM_STRUCT_GET(
type_index, m_field_index,
// Branch will not be taken.
// 52 left on stack outside the block (not 42).
WASM_BR_ON_NULL(0, WASM_STRUCT_NEW(type_index, WASM_I32V(52),
WASM_I32V(62)))),
WASM_BR(0)),
kExprEnd};
m->EmitCode(m_code, sizeof(m_code));
tester.DefineFunction(
"m", tester.sigs.i_v(), {},
{WASM_BLOCK_I(
WASM_I32V(42),
WASM_STRUCT_GET(
type_index, m_field_index,
// Branch will not be taken.
// 52 left on stack outside the block (not 42).
WASM_BR_ON_NULL(0, WASM_STRUCT_NEW(type_index, WASM_I32V(52),
WASM_I32V(62)))),
WASM_BR(0)),
kExprEnd});
// Test ref.eq
WasmFunctionBuilder* n = builder->AddFunction(sigs.i_v());
uint32_t n_local_index = n->AddLocal(kOptRefType);
n->builder()->AddExport(CStrVector("n"), n);
byte n_code[] = {
WASM_SET_LOCAL(n_local_index,
WASM_STRUCT_NEW(type_index, WASM_I32V(55), WASM_I32V(66))),
WASM_I32_ADD(
WASM_I32_SHL(
WASM_REF_EQ( // true
WASM_GET_LOCAL(n_local_index), WASM_GET_LOCAL(n_local_index)),
WASM_I32V(0)),
WASM_I32_ADD(
WASM_I32_SHL(WASM_REF_EQ( // false
WASM_GET_LOCAL(n_local_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_I32V(1)),
WASM_I32_ADD(WASM_I32_SHL( // false
WASM_REF_EQ(WASM_GET_LOCAL(n_local_index),
WASM_REF_NULL_GC(type_index)),
WASM_I32V(2)),
WASM_I32_SHL(WASM_REF_EQ( // true
WASM_REF_NULL_GC(type_index),
uint32_t n_local_index = 0;
tester.DefineFunction(
"n", tester.sigs.i_v(), {kOptRefType},
{WASM_SET_LOCAL(n_local_index, WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_I32_ADD(
WASM_I32_SHL(WASM_REF_EQ( // true
WASM_GET_LOCAL(n_local_index),
WASM_GET_LOCAL(n_local_index)),
WASM_I32V(0)),
WASM_I32_ADD(
WASM_I32_SHL(WASM_REF_EQ( // false
WASM_GET_LOCAL(n_local_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_I32V(1)),
WASM_I32_ADD(WASM_I32_SHL( // false
WASM_REF_EQ(WASM_GET_LOCAL(n_local_index),
WASM_REF_NULL_GC(type_index)),
WASM_I32V(3))))),
kExprEnd};
n->EmitCode(n_code, sizeof(n_code));
WASM_I32V(2)),
WASM_I32_SHL(WASM_REF_EQ( // true
WASM_REF_NULL_GC(type_index),
WASM_REF_NULL_GC(type_index)),
WASM_I32V(3))))),
kExprEnd});
// Result: 0b1001
/************************* End of test definitions *************************/
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test");
MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg());
Handle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
tester.CompileModule();
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"f", 0, nullptr));
CHECK_EQ(64, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"g", 0, nullptr));
tester.CheckResult("f", 42, {});
tester.CheckResult("g", 64, {});
// TODO(7748): This uses the JavaScript interface to retrieve the plain
// WasmStruct. Once the JS interaction story is settled, this may well
// need to be changed.
Handle<WasmExportedFunction> h_export =
testing::GetExportedFunction(isolate, instance, "h").ToHandleChecked();
Handle<Object> undefined = isolate->factory()->undefined_value();
Handle<Object> ref_result =
Execution::Call(isolate, h_export, undefined, 0, nullptr)
.ToHandleChecked();
CHECK(ref_result->IsWasmStruct());
CHECK_EQ(-99, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"j", 0, nullptr));
CHECK_EQ(55, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"k", 0, nullptr));
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"l", 0, nullptr));
CHECK_EQ(52, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"m", 0, nullptr));
CHECK_EQ(0b1001, testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, "n", 0, nullptr));
CHECK(tester.GetJSResult("h", {}).ToHandleChecked()->IsWasmStruct());
tester.CheckResult("j", -99, {});
tester.CheckResult("k", 55, {});
tester.CheckResult("l", 42, {});
tester.CheckResult("m", 52, {});
tester.CheckResult("n", 0b1001, {});
}
WASM_EXEC_TEST(LetInstruction) {
// TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return;
if (execution_tier == ExecutionTier::kInterpreter) return;
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(typed_funcref);
EXPERIMENTAL_FLAG_SCOPE(anyref);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
TEST(WasmLetInstruction) {
WasmGCTester tester;
uint32_t type_index =
tester.DefineStruct({F(kWasmI32, true), F(kWasmI32, true)});
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
StructType::Builder type_builder(&zone, 2);
type_builder.AddField(kWasmI32, true);
type_builder.AddField(kWasmI32, true);
int32_t type_index = builder->AddStructType(type_builder.Build());
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes);
WasmFunctionBuilder* let_test_1 = builder->AddFunction(sigs.i_v());
let_test_1->builder()->AddExport(CStrVector("let_test_1"), let_test_1);
uint32_t let_local_index = 0;
uint32_t let_field_index = 0;
byte let_code[] = {
WASM_LET_1_I(WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_STRUCT_GET(type_index, let_field_index,
WASM_GET_LOCAL(let_local_index))),
kExprEnd};
let_test_1->EmitCode(let_code, sizeof(let_code));
WasmFunctionBuilder* let_test_2 = builder->AddFunction(sigs.i_v());
let_test_2->builder()->AddExport(CStrVector("let_test_2"), let_test_2);
tester.DefineFunction(
"let_test_1", tester.sigs.i_v(), {},
{WASM_LET_1_I(WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_STRUCT_GET(type_index, let_field_index,
WASM_GET_LOCAL(let_local_index))),
kExprEnd});
uint32_t let_2_field_index = 0;
byte let_code_2[] = {
WASM_LET_2_I(kLocalI32, WASM_I32_ADD(WASM_I32V(42), WASM_I32V(-32)),
WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_I32_MUL(WASM_STRUCT_GET(type_index, let_2_field_index,
WASM_GET_LOCAL(1)),
WASM_GET_LOCAL(0))),
kExprEnd};
let_test_2->EmitCode(let_code_2, sizeof(let_code_2));
WasmFunctionBuilder* let_test_locals = builder->AddFunction(sigs.i_i());
let_test_locals->builder()->AddExport(CStrVector("let_test_locals"),
let_test_locals);
let_test_locals->AddLocal(kWasmI32);
byte let_code_locals[] = {
WASM_SET_LOCAL(1, WASM_I32V(100)),
WASM_LET_2_I(
kLocalI32, WASM_I32V(1), kLocalI32, WASM_I32V(10),
WASM_I32_SUB(WASM_I32_ADD(WASM_GET_LOCAL(0), // 1st let-local
WASM_GET_LOCAL(2)), // Parameter
WASM_I32_ADD(WASM_GET_LOCAL(1), // 2nd let-local
WASM_GET_LOCAL(3)))), // Function local
kExprEnd};
tester.DefineFunction(
"let_test_2", tester.sigs.i_v(), {},
{WASM_LET_2_I(kLocalI32, WASM_I32_ADD(WASM_I32V(42), WASM_I32V(-32)),
WASM_REF_TYPE(type_index),
WASM_STRUCT_NEW(type_index, WASM_I32V(42), WASM_I32V(52)),
WASM_I32_MUL(WASM_STRUCT_GET(type_index, let_2_field_index,
WASM_GET_LOCAL(1)),
WASM_GET_LOCAL(0))),
kExprEnd});
tester.DefineFunction(
"let_test_locals", tester.sigs.i_i(), {kWasmI32},
{WASM_SET_LOCAL(1, WASM_I32V(100)),
WASM_LET_2_I(
kLocalI32, WASM_I32V(1), kLocalI32, WASM_I32V(10),
WASM_I32_SUB(WASM_I32_ADD(WASM_GET_LOCAL(0), // 1st let-local
WASM_GET_LOCAL(2)), // Parameter
WASM_I32_ADD(WASM_GET_LOCAL(1), // 2nd let-local
WASM_GET_LOCAL(3)))), // Function local
kExprEnd});
// Result: (1 + 1000) - (10 + 100) = 891
let_test_locals->EmitCode(let_code_locals, sizeof(let_code_locals));
WasmFunctionBuilder* let_test_erase = builder->AddFunction(sigs.i_v());
let_test_erase->builder()->AddExport(CStrVector("let_test_erase"),
let_test_erase);
uint32_t let_erase_local_index = let_test_erase->AddLocal(kWasmI32);
byte let_code_erase[] = {WASM_SET_LOCAL(let_erase_local_index, WASM_I32V(0)),
WASM_LET_1_V(kLocalI32, WASM_I32V(1), WASM_NOP),
WASM_GET_LOCAL(let_erase_local_index), kExprEnd};
uint32_t let_erase_local_index = 0;
tester.DefineFunction("let_test_erase", tester.sigs.i_v(), {kWasmI32},
{WASM_SET_LOCAL(let_erase_local_index, WASM_I32V(0)),
WASM_LET_1_V(kLocalI32, WASM_I32V(1), WASM_NOP),
WASM_GET_LOCAL(let_erase_local_index), kExprEnd});
// The result should be 0 and not 1, as local_get(0) refers to the original
// local.
let_test_erase->EmitCode(let_code_erase, sizeof(let_code_erase));
/************************* End of test definitions *************************/
tester.CompileModule();
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test");
MaybeHandle<WasmInstanceObject> maybe_instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()));
if (thrower.error()) FATAL("%s", thrower.error_msg());
Handle<WasmInstanceObject> instance = maybe_instance.ToHandleChecked();
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_1", 0, nullptr));
CHECK_EQ(420, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_2", 0, nullptr));
Handle<Object> let_local_args[] = {handle(Smi::FromInt(1000), isolate)};
CHECK_EQ(891, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"let_test_locals", 1,
let_local_args));
CHECK_EQ(0, testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, "let_test_erase", 0, nullptr));
tester.CheckResult("let_test_1", 42, {});
tester.CheckResult("let_test_2", 420, {});
tester.CheckResult("let_test_locals", 891, {Smi::FromInt(1000)});
tester.CheckResult("let_test_erase", 0, {});
}
WASM_EXEC_TEST(BasicArray) {
// TODO(7748): Implement support in other tiers.
if (execution_tier == ExecutionTier::kLiftoff) return;
if (execution_tier == ExecutionTier::kInterpreter) return;
TestSignatures sigs;
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref);
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
WasmModuleBuilder* builder = new (&zone) WasmModuleBuilder(&zone);
ArrayType type(wasm::kWasmI32, true);
int32_t type_index = builder->AddArrayType(&type);
TEST(WasmBasicArray) {
WasmGCTester tester;
uint32_t type_index = tester.DefineArray(wasm::kWasmI32, true);
ValueType kRefTypes[] = {ValueType(ValueType::kRef, type_index)};
FunctionSig sig_q_v(1, 0, kRefTypes);
ValueType kOptRefType = ValueType(ValueType::kOptRef, type_index);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
uint32_t local_index = f->AddLocal(kOptRefType);
f->builder()->AddExport(CStrVector("f"), f);
// f: a = [12, 12, 12]; a[1] = 42; return a[arg0]
byte f_code[] = {
WASM_SET_LOCAL(local_index,
WASM_ARRAY_NEW(type_index, WASM_I32V(12), WASM_I32V(3))),
WASM_ARRAY_SET(type_index, WASM_GET_LOCAL(local_index), WASM_I32V(1),
WASM_I32V(42)),
WASM_ARRAY_GET(type_index, WASM_GET_LOCAL(local_index),
WASM_GET_LOCAL(0)),
kExprEnd};
f->EmitCode(f_code, sizeof(f_code));
uint32_t local_index = 1;
tester.DefineFunction(
"f", tester.sigs.i_i(), {kOptRefType},
{WASM_SET_LOCAL(local_index,
WASM_ARRAY_NEW(type_index, WASM_I32V(12), WASM_I32V(3))),
WASM_ARRAY_SET(type_index, WASM_GET_LOCAL(local_index), WASM_I32V(1),
WASM_I32V(42)),
WASM_ARRAY_GET(type_index, WASM_GET_LOCAL(local_index),
WASM_GET_LOCAL(0)),
kExprEnd});
// Reads and returns an array's length.
WasmFunctionBuilder* g = builder->AddFunction(sigs.i_v());
f->builder()->AddExport(CStrVector("g"), g);
byte g_code[] = {
WASM_ARRAY_LEN(type_index,
WASM_ARRAY_NEW(type_index, WASM_I32V(0), WASM_I32V(42))),
kExprEnd};
g->EmitCode(g_code, sizeof(g_code));
WasmFunctionBuilder* h = builder->AddFunction(&sig_q_v);
h->builder()->AddExport(CStrVector("h"), h);
tester.DefineFunction(
"g", tester.sigs.i_v(), {},
{WASM_ARRAY_LEN(type_index,
WASM_ARRAY_NEW(type_index, WASM_I32V(0), WASM_I32V(42))),
kExprEnd});
// Create an array of length 2, initialized to [42, 42].
byte h_code[] = {WASM_ARRAY_NEW(type_index, WASM_I32V(42), WASM_I32V(2)),
kExprEnd};
h->EmitCode(h_code, sizeof(h_code));
ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "Test");
Handle<WasmInstanceObject> instance =
testing::CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(buffer.begin(), buffer.end()))
.ToHandleChecked();
Handle<Object> argv[] = {handle(Smi::FromInt(0), isolate)};
CHECK_EQ(12, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"f", 1, argv));
argv[0] = handle(Smi::FromInt(1), isolate);
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"f", 1, argv));
argv[0] = handle(Smi::FromInt(2), isolate);
CHECK_EQ(12, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"f", 1, argv));
Handle<Object> undefined = isolate->factory()->undefined_value();
{
Handle<WasmExportedFunction> f_export =
testing::GetExportedFunction(isolate, instance, "f").ToHandleChecked();
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
argv[0] = handle(Smi::FromInt(3), isolate);
MaybeHandle<Object> no_result =
Execution::Call(isolate, f_export, undefined, 1, argv);
CHECK(no_result.is_null());
CHECK(try_catch.HasCaught());
isolate->clear_pending_exception();
argv[0] = handle(Smi::FromInt(-1), isolate);
no_result = Execution::Call(isolate, f_export, undefined, 1, argv);
CHECK(no_result.is_null());
CHECK(try_catch.HasCaught());
isolate->clear_pending_exception();
}
tester.DefineFunction(
"h", &sig_q_v, {},
{WASM_ARRAY_NEW(type_index, WASM_I32V(42), WASM_I32V(2)), kExprEnd});
CHECK_EQ(42, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"g", 0, nullptr));
tester.CompileModule();
// TODO(7748): This uses the JavaScript interface to retrieve the plain
// WasmArray. Once the JS interaction story is settled, this may well
// need to be changed.
Handle<WasmExportedFunction> h_export =
testing::GetExportedFunction(isolate, instance, "h").ToHandleChecked();
Handle<Object> ref_result =
Execution::Call(isolate, h_export, undefined, 0, nullptr)
.ToHandleChecked();
CHECK(ref_result->IsWasmArray());
tester.CheckResult("f", 12, {Smi::FromInt(0)});
tester.CheckResult("f", 42, {Smi::FromInt(1)});
tester.CheckResult("f", 12, {Smi::FromInt(2)});
tester.CheckHasThrown("f", {Smi::FromInt(3)});
tester.CheckHasThrown("f", {Smi::FromInt(-1)});
tester.CheckResult("g", 42, {});
MaybeHandle<Object> h_result = tester.GetJSResult("h", {});
CHECK(h_result.ToHandleChecked()->IsWasmArray());
#if OBJECT_PRINT
ref_result->Print();
h_result.ToHandleChecked()->Print();
#endif
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment