[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:
Andreas Haas 2019-05-13 13:35:43 +02:00 committed by Commit Bot
parent cd574c152d
commit a1a7be4d90
7 changed files with 169 additions and 19 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}
};

View File

@ -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)

View File

@ -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()());
})();

View File

@ -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;

View File

@ -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