[wasm] Allow ref.func to initialize globals
Bug: v8:7581 Change-Id: I7ec9a7dbfb57cd6b5d985a7dc664ca3c0965969c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1605726 Reviewed-by: Michael Starzinger <mstarzinger@chromium.org> Commit-Queue: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#61480}
This commit is contained in:
parent
cd574c152d
commit
a1a7be4d90
@ -122,6 +122,8 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
|
||||
return kWasmF64;
|
||||
case WasmInitExpr::kRefNullConst:
|
||||
return kWasmNullRef;
|
||||
case WasmInitExpr::kRefFuncConst:
|
||||
return kWasmAnyFunc;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -1539,6 +1541,16 @@ class ModuleDecoderImpl : public Decoder {
|
||||
}
|
||||
V8_FALLTHROUGH;
|
||||
}
|
||||
case kExprRefFunc: {
|
||||
if (enabled_features_.anyref) {
|
||||
FunctionIndexImmediate<Decoder::kValidate> imm(this, pc() - 1);
|
||||
expr.kind = WasmInitExpr::kRefFuncConst;
|
||||
expr.val.function_index = imm.index;
|
||||
len = imm.length;
|
||||
break;
|
||||
}
|
||||
V8_FALLTHROUGH;
|
||||
}
|
||||
default: {
|
||||
error("invalid opcode in initialization expression");
|
||||
expr.kind = WasmInitExpr::kNone;
|
||||
|
@ -179,7 +179,7 @@ class InstanceBuilder {
|
||||
T* GetRawGlobalPtr(const WasmGlobal& global);
|
||||
|
||||
// Process initialization of globals.
|
||||
void InitGlobals();
|
||||
void InitGlobals(Handle<WasmInstanceObject> instance);
|
||||
|
||||
// Allocate memory for a module instance as a new JSArrayBuffer.
|
||||
Handle<JSArrayBuffer> AllocateMemory(uint32_t initial_pages,
|
||||
@ -376,7 +376,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
//--------------------------------------------------------------------------
|
||||
// Process the initialization for the module's globals.
|
||||
//--------------------------------------------------------------------------
|
||||
InitGlobals();
|
||||
InitGlobals(instance);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Initialize the indirect tables.
|
||||
@ -795,6 +795,10 @@ bool InstanceBuilder::ProcessImportedFunction(
|
||||
Address imported_target = imported_function->GetWasmCallTarget();
|
||||
ImportedFunctionEntry entry(instance, func_index);
|
||||
entry.SetWasmToWasm(*imported_instance, imported_target);
|
||||
// Also store the {WasmExportedFunction} in the instance to preserve its
|
||||
// identity.
|
||||
WasmInstanceObject::SetWasmExportedFunction(
|
||||
isolate_, instance, func_index, imported_function);
|
||||
break;
|
||||
}
|
||||
case compiler::WasmImportCallKind::kWasmToCapi: {
|
||||
@ -1211,7 +1215,7 @@ T* InstanceBuilder::GetRawGlobalPtr(const WasmGlobal& global) {
|
||||
}
|
||||
|
||||
// Process initialization of globals.
|
||||
void InstanceBuilder::InitGlobals() {
|
||||
void InstanceBuilder::InitGlobals(Handle<WasmInstanceObject> instance) {
|
||||
for (auto global : module_->globals) {
|
||||
if (global.mutability && global.imported) {
|
||||
continue;
|
||||
@ -1242,6 +1246,13 @@ void InstanceBuilder::InitGlobals() {
|
||||
ReadOnlyRoots(isolate_).null_value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
break;
|
||||
case WasmInitExpr::kRefFuncConst: {
|
||||
DCHECK(enabled_.anyref);
|
||||
auto function = WasmInstanceObject::GetOrCreateWasmExportedFunction(
|
||||
isolate_, instance, global.init.val.function_index);
|
||||
tagged_globals_->set(global.offset, *function);
|
||||
break;
|
||||
}
|
||||
case WasmInitExpr::kGlobalIndex: {
|
||||
// Initialize with another global.
|
||||
uint32_t new_offset = global.offset;
|
||||
@ -1374,6 +1385,7 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
|
||||
MaybeHandle<WasmExportedFunction> wasm_exported_function =
|
||||
WasmInstanceObject::GetOrCreateWasmExportedFunction(
|
||||
isolate_, instance, exp.index);
|
||||
|
||||
desc.set_value(wasm_exported_function.ToHandleChecked());
|
||||
export_index++;
|
||||
break;
|
||||
|
@ -615,6 +615,7 @@ struct WasmInitExpr {
|
||||
kF32Const,
|
||||
kF64Const,
|
||||
kRefNullConst,
|
||||
kRefFuncConst,
|
||||
} kind;
|
||||
|
||||
union {
|
||||
@ -623,6 +624,7 @@ struct WasmInitExpr {
|
||||
float f32_const;
|
||||
double f64_const;
|
||||
uint32_t global_index;
|
||||
uint32_t function_index;
|
||||
} val;
|
||||
|
||||
WasmInitExpr() : kind(kNone) {}
|
||||
@ -630,8 +632,15 @@ struct WasmInitExpr {
|
||||
explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
|
||||
explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
|
||||
explicit WasmInitExpr(double v) : kind(kF64Const) { val.f64_const = v; }
|
||||
WasmInitExpr(WasmInitKind kind, uint32_t global_index) : kind(kGlobalIndex) {
|
||||
val.global_index = global_index;
|
||||
WasmInitExpr(WasmInitKind kind, uint32_t index) : kind(kind) {
|
||||
if (kind == kGlobalIndex) {
|
||||
val.global_index = index;
|
||||
} else if (kind == kRefFuncConst) {
|
||||
val.function_index = index;
|
||||
} else {
|
||||
// For the other types, the other initializers should be used.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -346,6 +346,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
static_cast<byte>(bit_cast<uint64_t>(static_cast<double>(val)) >> 56)
|
||||
|
||||
#define WASM_REF_NULL kExprRefNull
|
||||
#define WASM_REF_FUNC(val) kExprRefFunc, val
|
||||
#define WASM_REF_IS_NULL(val) val, kExprRefIsNull
|
||||
|
||||
#define WASM_GET_LOCAL(index) kExprGetLocal, static_cast<byte>(index)
|
||||
|
@ -556,3 +556,61 @@ function dummy_func() {
|
||||
const i2 = builder2.addImportedGlobal('exports', 'e2', kWasmAnyRef, true);
|
||||
assertThrows(() => builder2.instantiate(instance1), WebAssembly.LinkError);
|
||||
})();
|
||||
|
||||
(function TestRefFuncGlobalInit() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
const g_ref = builder.addGlobal(kWasmAnyRef, true);
|
||||
const g_func = builder.addGlobal(kWasmAnyFunc, true);
|
||||
const f_ref = builder.addFunction('get_anyref_global', kSig_r_v)
|
||||
.addBody([kExprGetGlobal, g_ref.index])
|
||||
.exportAs('get_anyref_global');
|
||||
const f_func = builder.addFunction('get_anyfunc_global', kSig_a_v)
|
||||
.addBody([kExprGetGlobal, g_func.index])
|
||||
.exportAs('get_anyfunc_global');
|
||||
|
||||
g_ref.function_index = f_ref.index;
|
||||
g_func.function_index = f_func.index;
|
||||
|
||||
const instance = builder.instantiate();
|
||||
assertEquals(
|
||||
instance.exports.get_anyref_global, instance.exports.get_anyref_global());
|
||||
assertEquals(
|
||||
instance.exports.get_anyfunc_global,
|
||||
instance.exports.get_anyfunc_global());
|
||||
})();
|
||||
|
||||
(function TestRefFuncGlobalInitWithImport() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
const sig_index = builder.addType(kSig_i_v);
|
||||
const import_wasm = builder.addImport('m', 'wasm', sig_index);
|
||||
const import_js = builder.addImport('m', 'js', sig_index);
|
||||
const g_wasm = builder.addGlobal(kWasmAnyFunc, true);
|
||||
const g_js = builder.addGlobal(kWasmAnyFunc, true);
|
||||
g_wasm.function_index = import_wasm;
|
||||
g_js.function_index = import_js;
|
||||
builder.addFunction('get_global_wasm', kSig_a_v)
|
||||
.addBody([kExprGetGlobal, g_wasm.index])
|
||||
.exportFunc();
|
||||
builder.addFunction('get_global_js', kSig_a_v)
|
||||
.addBody([kExprGetGlobal, g_js.index])
|
||||
.exportFunc();
|
||||
|
||||
const expected_wasm = dummy_func();
|
||||
const expected_val = 27;
|
||||
// I want to test here that imported JS functions get wrapped by wasm-to-js
|
||||
// and js-to-wasm wrappers. That's why {expected_js} does not return an
|
||||
// integer directly but an object with a {valueOf} function.
|
||||
function expected_js() {
|
||||
const result = {};
|
||||
result.valueOf = () => expected_val;
|
||||
return result;
|
||||
};
|
||||
|
||||
const instance =
|
||||
builder.instantiate({m: {wasm: expected_wasm, js: expected_js}});
|
||||
|
||||
assertSame(expected_wasm, instance.exports.get_global_wasm());
|
||||
assertSame(expected_val, instance.exports.get_global_js()());
|
||||
})();
|
||||
|
@ -1089,8 +1089,15 @@ class WasmModuleBuilder {
|
||||
f64_view[0] = global.init;
|
||||
section.emit_bytes(f64_bytes_view);
|
||||
break;
|
||||
case kWasmAnyRef:
|
||||
case kWasmAnyFunc:
|
||||
case kWasmAnyRef:
|
||||
if (global.function_index !== undefined) {
|
||||
section.emit_u8(kExprRefFunc);
|
||||
section.emit_u32v(global.function_index);
|
||||
} else {
|
||||
section.emit_u8(kExprRefNull);
|
||||
}
|
||||
break;
|
||||
case kWasmExceptRef:
|
||||
section.emit_u8(kExprRefNull);
|
||||
break;
|
||||
|
@ -29,7 +29,8 @@ namespace module_decoder_unittest {
|
||||
#define WASM_INIT_EXPR_F32(val) WASM_F32(val), kExprEnd
|
||||
#define WASM_INIT_EXPR_I64(val) WASM_I64(val), kExprEnd
|
||||
#define WASM_INIT_EXPR_F64(val) WASM_F64(val), kExprEnd
|
||||
#define WASM_INIT_EXPR_ANYREF WASM_REF_NULL, kExprEnd
|
||||
#define WASM_INIT_EXPR_REF_NULL WASM_REF_NULL, kExprEnd
|
||||
#define WASM_INIT_EXPR_REF_FUNC(val) WASM_REF_FUNC(val), kExprEnd
|
||||
#define WASM_INIT_EXPR_GLOBAL(index) WASM_GET_GLOBAL(index), kExprEnd
|
||||
|
||||
#define REF_NULL_ELEMENT kExprRefNull, kExprEnd
|
||||
@ -270,26 +271,75 @@ TEST_F(WasmModuleVerifyTest, OneGlobal) {
|
||||
TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
static const byte data[] = {
|
||||
SECTION(Global, // --
|
||||
ENTRY_COUNT(1), // --
|
||||
kLocalAnyRef, // local type
|
||||
0, // immutable
|
||||
WASM_INIT_EXPR_ANYREF) // init
|
||||
};
|
||||
// sig#0 ---------------------------------------------------------------
|
||||
SIGNATURES_SECTION_VOID_VOID,
|
||||
// funcs ---------------------------------------------------------------
|
||||
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
|
||||
SECTION(Global, // --
|
||||
ENTRY_COUNT(2), // --
|
||||
kLocalAnyRef, // local type
|
||||
0, // immutable
|
||||
WASM_INIT_EXPR_REF_NULL, // init
|
||||
kLocalAnyRef, // local type
|
||||
0, // immutable
|
||||
WASM_INIT_EXPR_REF_FUNC(1)), // init
|
||||
TWO_EMPTY_BODIES};
|
||||
|
||||
{
|
||||
// Should decode to exactly one global.
|
||||
// Should decode to two globals.
|
||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||
EXPECT_OK(result);
|
||||
EXPECT_EQ(1u, result.value()->globals.size());
|
||||
EXPECT_EQ(0u, result.value()->functions.size());
|
||||
EXPECT_EQ(2u, result.value()->globals.size());
|
||||
EXPECT_EQ(2u, result.value()->functions.size());
|
||||
EXPECT_EQ(0u, result.value()->data_segments.size());
|
||||
|
||||
const WasmGlobal* global = &result.value()->globals.back();
|
||||
|
||||
const WasmGlobal* global = &result.value()->globals[0];
|
||||
EXPECT_EQ(kWasmAnyRef, global->type);
|
||||
EXPECT_FALSE(global->mutability);
|
||||
EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind);
|
||||
|
||||
global = &result.value()->globals[1];
|
||||
EXPECT_EQ(kWasmAnyRef, global->type);
|
||||
EXPECT_FALSE(global->mutability);
|
||||
EXPECT_EQ(WasmInitExpr::kRefFuncConst, global->init.kind);
|
||||
EXPECT_EQ(uint32_t{1}, global->init.val.function_index);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, AnyFuncGlobal) {
|
||||
WASM_FEATURE_SCOPE(anyref);
|
||||
static const byte data[] = {
|
||||
// sig#0 ---------------------------------------------------------------
|
||||
SIGNATURES_SECTION_VOID_VOID,
|
||||
// funcs ---------------------------------------------------------------
|
||||
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
|
||||
SECTION(Global, // --
|
||||
ENTRY_COUNT(2), // --
|
||||
kLocalAnyFunc, // local type
|
||||
0, // immutable
|
||||
WASM_INIT_EXPR_REF_NULL, // init
|
||||
kLocalAnyFunc, // local type
|
||||
0, // immutable
|
||||
WASM_INIT_EXPR_REF_FUNC(1)), // init
|
||||
TWO_EMPTY_BODIES};
|
||||
{
|
||||
// Should decode to two globals.
|
||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||
EXPECT_OK(result);
|
||||
EXPECT_EQ(2u, result.value()->globals.size());
|
||||
EXPECT_EQ(2u, result.value()->functions.size());
|
||||
EXPECT_EQ(0u, result.value()->data_segments.size());
|
||||
|
||||
const WasmGlobal* global = &result.value()->globals[0];
|
||||
EXPECT_EQ(kWasmAnyFunc, global->type);
|
||||
EXPECT_FALSE(global->mutability);
|
||||
EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind);
|
||||
|
||||
global = &result.value()->globals[1];
|
||||
EXPECT_EQ(kWasmAnyFunc, global->type);
|
||||
EXPECT_FALSE(global->mutability);
|
||||
EXPECT_EQ(WasmInitExpr::kRefFuncConst, global->init.kind);
|
||||
EXPECT_EQ(uint32_t{1}, global->init.val.function_index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2433,7 +2483,8 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
|
||||
#undef WASM_INIT_EXPR_F32
|
||||
#undef WASM_INIT_EXPR_I64
|
||||
#undef WASM_INIT_EXPR_F64
|
||||
#undef WASM_INIT_EXPR_ANYREF
|
||||
#undef WASM_INIT_EXPR_REF_NULL
|
||||
#undef WASM_INIT_EXPR_REF_FUNC
|
||||
#undef WASM_INIT_EXPR_GLOBAL
|
||||
#undef REF_NULL_ELEMENT
|
||||
#undef REF_FUNC_ELEMENT
|
||||
|
Loading…
Reference in New Issue
Block a user