[wasm-gc] Implement table-with-initializer encoding

See https://github.com/WebAssembly/function-references/pull/65.

Drive-by: Lower gc nodes also if typed-funcref is enabled.

Bug: v8:9495
Change-Id: I19cb67cdbdedae24b9460bc7d5b280a21a946b21
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3784590
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81956}
This commit is contained in:
Manos Koukoutos 2022-07-26 06:03:39 +00:00 committed by V8 LUCI CQ
parent dc0be4e376
commit cb5c1b8a1f
6 changed files with 76 additions and 13 deletions

View File

@ -3327,6 +3327,11 @@ void Pipeline::GenerateCodeForWasmFunction(
pipeline.Run<WasmGCOptimizationPhase>(module);
pipeline.RunPrintAndVerify(WasmGCOptimizationPhase::phase_name(), true);
}
}
// These proposals use gc nodes.
if (FLAG_experimental_wasm_gc || FLAG_experimental_wasm_typed_funcref ||
FLAG_experimental_wasm_stringref) {
pipeline.Run<WasmGCLoweringPhase>();
pipeline.RunPrintAndVerify(WasmGCLoweringPhase::phase_name(), true);
}

View File

@ -905,6 +905,15 @@ class ModuleDecoderTemplate : public Decoder {
module_->tables.emplace_back();
WasmTable* table = &module_->tables.back();
const byte* type_position = pc();
bool has_initializer = false;
if (enabled_features_.has_typed_funcref() &&
read_u8<Decoder::kFullValidation>(
pc(), "table-with-initializer byte") == 0x40) {
consume_bytes(1, "table-with-initializer byte");
has_initializer = true;
}
ValueType table_type = consume_reference_type();
if (!WasmTable::IsValidTableType(table_type, module_.get())) {
error(type_position,
@ -912,13 +921,21 @@ class ModuleDecoderTemplate : public Decoder {
"as table types");
continue;
}
if (!has_initializer && !table_type.is_defaultable()) {
errorf(type_position,
"Table of non-defaultable table %s needs initial value",
table_type.name().c_str());
continue;
}
table->type = table_type;
uint8_t flags = validate_table_flags("table elements");
consume_resizable_limits(
"table elements", "elements", std::numeric_limits<uint32_t>::max(),
&table->initial_size, &table->has_maximum_size,
std::numeric_limits<uint32_t>::max(), &table->maximum_size, flags);
if (!table_type.is_defaultable()) {
if (has_initializer) {
table->initial_value = consume_init_expr(module_.get(), table_type);
}
}

View File

@ -405,7 +405,7 @@ class InstanceBuilder {
// and globals.
void ProcessExports(Handle<WasmInstanceObject> instance);
void InitializeNonDefaultableTables(Handle<WasmInstanceObject> instance);
void SetTableInitialValues(Handle<WasmInstanceObject> instance);
void LoadTableSegments(Handle<WasmInstanceObject> instance);
@ -618,7 +618,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
for (int i = module_->num_imported_tables; i < table_count; i++) {
const WasmTable& table = module_->tables[i];
// Initialize tables with null for now. We will initialize non-defaultable
// tables later, in {InitializeNonDefaultableTables}.
// tables later, in {SetTableInitialValues}.
Handle<WasmTableObject> table_obj = WasmTableObject::New(
isolate_, instance, table.type, table.initial_size,
table.has_maximum_size, table.maximum_size, nullptr,
@ -730,7 +730,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// Initialize non-defaultable tables.
//--------------------------------------------------------------------------
if (FLAG_experimental_wasm_typed_funcref) {
InitializeNonDefaultableTables(instance);
SetTableInitialValues(instance);
}
//--------------------------------------------------------------------------
@ -1907,12 +1907,12 @@ V8_INLINE void SetFunctionTableNullEntry(Isolate* isolate,
}
} // namespace
void InstanceBuilder::InitializeNonDefaultableTables(
void InstanceBuilder::SetTableInitialValues(
Handle<WasmInstanceObject> instance) {
for (int table_index = 0;
table_index < static_cast<int>(module_->tables.size()); ++table_index) {
const WasmTable& table = module_->tables[table_index];
if (!table.type.is_defaultable()) {
if (table.initial_value.is_set()) {
auto table_object = handle(
WasmTableObject::cast(instance->tables().get(table_index)), isolate_);
bool is_function_table = IsSubtypeOf(table.type, kWasmFuncRef, module_);
@ -1940,9 +1940,9 @@ void InstanceBuilder::InitializeNonDefaultableTables(
to_value(result).to_ref());
}
}
}
}
}
}
namespace {
// If the operation succeeds, returns an empty {Optional}. Otherwise, returns an

View File

@ -83,6 +83,30 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
assertTraps(kTrapElementSegmentOutOfBounds, () => instance.exports.init());
})();
(function TestTableInitializer() {
print(arguments.callee.name);
let test = function(is_nullable) {
const builder = new WasmModuleBuilder();
const sig = builder.addType(kSig_i_i);
const func = builder.addFunction("func", kSig_i_i)
.addBody([kExprLocalGet, 0]);
builder.addTable(is_nullable ? wasmRefNullType(sig) : wasmRefType(sig),
10, 10, [kExprRefFunc, func.index]);
builder.addFunction("main", kSig_i_ii)
.addBody([kExprLocalGet, 1, kExprLocalGet, 0, kExprTableGet, 0,
kExprCallRef])
.exportFunc();
const instance = builder.instantiate();
assertEquals(1, instance.exports.main(0, 1));
assertEquals(33, instance.exports.main(5, 33));
}
test(true);
test(false);
})();
(function TestExternRefTableConstructorWithDefaultValue() {
print(arguments.callee.name);

View File

@ -1715,13 +1715,12 @@ class WasmModuleBuilder {
binary.emit_section(kTableSectionCode, section => {
section.emit_u32v(wasm.tables.length);
for (let table of wasm.tables) {
if (table.has_init) section.emit_u8(0x40);
section.emit_type(table.type);
section.emit_u8(table.has_max);
section.emit_u32v(table.initial_size);
if (table.has_max) section.emit_u32v(table.max_size);
if (table.has_init) {
section.emit_init_expr(table.init_expr);
}
if (table.has_init) section.emit_init_expr(table.init_expr);
}
});
}

View File

@ -4,8 +4,6 @@
#include "src/wasm/module-decoder.h"
#include "src/handles/handles.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/branch-hint-map.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-features.h"
@ -2101,6 +2099,24 @@ TEST_F(WasmModuleVerifyTest, IllegalTableTypes) {
}
}
TEST_F(WasmModuleVerifyTest, TableWithInitializer) {
WASM_FEATURE_SCOPE(typed_funcref);
static const byte data[] = {
SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_v), // type section
ONE_EMPTY_FUNCTION(0), // function section
SECTION(Table, // table section
ENTRY_COUNT(1), // 1 table
0x40, // table 0: has initializer
kRefNullCode, 0, // table 0: type
0, 10, // table 0: limits
kExprRefFunc, 0, kExprEnd), // table 0: initial value
SECTION(Code, ENTRY_COUNT(1), NOP_BODY)};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(ValueType::RefNull(0), result.value()->tables[0].type);
}
TEST_F(WasmModuleVerifyTest, NonNullableTable) {
WASM_FEATURE_SCOPE(typed_funcref);
@ -2109,6 +2125,7 @@ TEST_F(WasmModuleVerifyTest, NonNullableTable) {
ONE_EMPTY_FUNCTION(0), // function section
SECTION(Table, // table section
ENTRY_COUNT(1), // 1 table
0x40, // table 0: has initializer
kRefCode, 0, // table 0: type
0, 10, // table 0: limits
kExprRefFunc, 0, kExprEnd), // table 0: initial value
@ -2130,7 +2147,8 @@ TEST_F(WasmModuleVerifyTest, NonNullableTableNoInitializer) {
kRefCode, 0, // table 1: type
5, 6)}; // table 1: limits
EXPECT_FAILURE(data);
EXPECT_FAILURE_WITH_MSG(
data, "Table of non-defaultable table (ref 0) needs initial value");
}
TEST_F(WasmModuleVerifyTest, TieringCompilationHints) {