Commit 4672b865 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Implement the ref.func instruction

I did the implementation with a runtime function. I extracted some code
from the implementation of table.get.

By accident I formatted anyfunc.js. However, since it's an improvement,
I don't want to undo it. I didn't change anything in the older tests
though, I only added new tests at the end.

R=mstarzinger@chromium.org

Bug: v8:7581
Change-Id: I31832ccc817e1e7989f486d6487108c14d21bbea
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1602701
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61442}
parent c6201bc0
......@@ -280,6 +280,14 @@ Node* WasmGraphBuilder::RefNull() {
MachineType::TypeCompressedTaggedPointer());
}
Node* WasmGraphBuilder::RefFunc(uint32_t function_index) {
Node* args[] = {
graph()->NewNode(mcgraph()->common()->NumberConstant(function_index))};
Node* result =
BuildCallToRuntime(Runtime::kWasmRefFunc, args, arraysize(args));
return result;
}
Node* WasmGraphBuilder::NoContextConstant() {
// TODO(titzer): avoiding a dependency on JSGraph here. Refactor.
return mcgraph()->IntPtrConstant(0);
......
......@@ -199,6 +199,7 @@ class WasmGraphBuilder {
Node* CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode, Node* fnode);
Node* EffectPhi(unsigned count, Node** effects, Node* control);
Node* RefNull();
Node* RefFunc(uint32_t function_index);
Node* Uint32Constant(uint32_t value);
Node* Int32Constant(int32_t value);
Node* Int64Constant(int64_t value);
......
......@@ -415,6 +415,24 @@ Object ThrowTableOutOfBounds(Isolate* isolate,
}
} // namespace
RUNTIME_FUNCTION(Runtime_WasmRefFunc) {
// This runtime function is always being called from wasm code.
ClearThreadInWasmScope flag_scope;
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
auto instance =
Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
DCHECK(isolate->context().is_null());
isolate->set_context(instance->native_context());
CONVERT_UINT32_ARG_CHECKED(function_index, 0);
Handle<WasmExportedFunction> function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance,
function_index);
return *function;
}
RUNTIME_FUNCTION(Runtime_WasmFunctionTableGet) {
// This runtime function is always being called from wasm code.
ClearThreadInWasmScope flag_scope;
......
......@@ -543,6 +543,7 @@ namespace internal {
F(WasmStackGuard, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmRefFunc, 1, 1) \
F(WasmFunctionTableGet, 3, 1) \
F(WasmFunctionTableSet, 4, 1) \
F(WasmTableInit, 5, 1) \
......
......@@ -1157,6 +1157,10 @@ class LiftoffCompiler {
unsupported(decoder, "ref_null");
}
void RefFunc(FullDecoder* decoder, uint32_t function_index, Value* result) {
unsupported(decoder, "func");
}
void Drop(FullDecoder* decoder, const Value& value) {
auto& slot = __ cache_state()->stack_state.back();
// If the dropped slot contains a register, decrement it's use count.
......
......@@ -341,6 +341,15 @@ struct CallFunctionImmediate {
}
};
template <Decoder::ValidateFlag validate>
struct FunctionIndexImmediate {
uint32_t index = 0;
uint32_t length = 1;
inline FunctionIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "function index");
}
};
template <Decoder::ValidateFlag validate>
struct MemoryIndexImmediate {
uint32_t index = 0;
......@@ -675,6 +684,7 @@ struct ControlBase {
F(F32Const, Value* result, float value) \
F(F64Const, Value* result, double value) \
F(RefNull, Value* result) \
F(RefFunc, uint32_t function_index, Value* result) \
F(Drop, const Value& value) \
F(DoReturn, Vector<Value> values) \
F(GetLocal, Value* result, const LocalIndexImmediate<validate>& imm) \
......@@ -1103,6 +1113,15 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(const byte* pc, FunctionIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr &&
imm.index < module_->functions.size())) {
errorf(pc, "invalid function index: %u", imm.index);
return false;
}
return true;
}
inline bool Validate(const byte* pc, MemoryIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && module_->has_memory)) {
errorf(pc + 1, "memory instruction with no memory");
......@@ -1250,6 +1269,10 @@ class WasmDecoder : public Decoder {
case kExprRefNull: {
return 1;
}
case kExprRefFunc: {
FunctionIndexImmediate<validate> imm(decoder, pc);
return 1 + imm.length;
}
case kExprMemoryGrow:
case kExprMemorySize: {
MemoryIndexImmediate<validate> imm(decoder, pc);
......@@ -1398,6 +1421,7 @@ class WasmDecoder : public Decoder {
case kExprF32Const:
case kExprF64Const:
case kExprRefNull:
case kExprRefFunc:
case kExprMemorySize:
return {0, 1};
case kExprCallFunction: {
......@@ -2011,6 +2035,15 @@ class WasmFullDecoder : public WasmDecoder<validate> {
len = 1;
break;
}
case kExprRefFunc: {
CHECK_PROTOTYPE_OPCODE(anyref);
FunctionIndexImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
auto* value = Push(kWasmAnyFunc);
CALL_INTERFACE_IF_REACHABLE(RefFunc, imm.index, value);
len = 1 + imm.length;
break;
}
case kExprGetLocal: {
LocalIndexImmediate<validate> imm(this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
......
......@@ -251,6 +251,10 @@ class WasmGraphBuildingInterface {
result->node = builder_->RefNull();
}
void RefFunc(FullDecoder* decoder, uint32_t function_index, Value* result) {
result->node = BUILD(RefFunc, function_index);
}
void Drop(FullDecoder* decoder, const Value& value) {}
void DoReturn(FullDecoder* decoder, Vector<Value> values) {
......
......@@ -492,7 +492,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// TODO(clemensh): Don't generate an exported function for the start
// function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New(
isolate_, instance, MaybeHandle<String>(), start_index,
isolate_, instance, start_index,
static_cast<int>(function.sig->parameter_count()), wrapper_code);
}
......@@ -1371,31 +1371,9 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
case kExternalFunction: {
// Wrap and export the code as a JSFunction.
// TODO(wasm): reduce duplication with LoadElemSegment() further below
const WasmFunction& function = module_->functions[exp.index];
MaybeHandle<WasmExportedFunction> wasm_exported_function =
WasmInstanceObject::GetWasmExportedFunction(isolate_, instance,
exp.index);
if (wasm_exported_function.is_null()) {
// Wrap the exported code as a JSFunction.
Handle<Code> export_code =
export_wrappers->GetValueChecked<Code>(isolate_, export_index);
MaybeHandle<String> func_name;
if (is_asm_js) {
// For modules arising from asm.js, honor the names section.
WireBytesRef func_name_ref = module_->LookupFunctionName(
ModuleWireBytes(module_object_->native_module()->wire_bytes()),
function.func_index);
func_name = WasmModuleObject::ExtractUtf8StringFromModuleBytes(
isolate_, module_object_, func_name_ref)
.ToHandleChecked();
}
wasm_exported_function = WasmExportedFunction::New(
isolate_, instance, func_name, function.func_index,
static_cast<int>(function.sig->parameter_count()), export_code);
WasmInstanceObject::SetWasmExportedFunction(
isolate_, instance, exp.index,
wasm_exported_function.ToHandleChecked());
}
WasmInstanceObject::GetOrCreateWasmExportedFunction(
isolate_, instance, exp.index);
desc.set_value(wasm_exported_function.ToHandleChecked());
export_index++;
break;
......
......@@ -2902,6 +2902,18 @@ class ThreadImpl {
Push(WasmValue(isolate_->factory()->null_value()));
break;
}
case kExprRefFunc: {
FunctionIndexImmediate<Decoder::kNoValidate> imm(&decoder,
code->at(pc));
HandleScope handle_scope(isolate_); // Avoid leaking handles.
Handle<WasmExportedFunction> function =
WasmInstanceObject::GetOrCreateWasmExportedFunction(
isolate_, instance_object_, imm.index);
Push(WasmValue(function));
len = 1 + imm.length;
break;
}
case kExprGetLocal: {
LocalIndexImmediate<Decoder::kNoValidate> imm(&decoder, code->at(pc));
HandleScope handle_scope(isolate_); // Avoid leaking handles.
......
......@@ -973,32 +973,10 @@ Handle<Object> WasmTableObject::Get(Isolate* isolate,
// Check if we already compiled a wrapper for the function but did not store
// it in the table slot yet.
MaybeHandle<Object> maybe_entry = WasmInstanceObject::GetWasmExportedFunction(
isolate, instance, function_index);
if (maybe_entry.ToHandle(&entry)) {
entries->set(entry_index, *entry);
return entry;
}
const WasmModule* module = instance->module_object()->module();
const WasmFunction& function = module->functions[function_index];
// Exported functions got their wrapper compiled during instantiation.
CHECK(!function.exported);
Handle<Code> wrapper_code =
compiler::CompileJSToWasmWrapper(isolate, function.sig, function.imported)
.ToHandleChecked();
MaybeHandle<String> function_name = WasmModuleObject::GetFunctionNameOrNull(
isolate, handle(instance->module_object(), isolate), function_index);
Handle<WasmExportedFunction> result = WasmExportedFunction::New(
isolate, instance, function_name, function_index,
static_cast<int>(function.sig->parameter_count()), wrapper_code);
entries->set(entry_index, *result);
WasmInstanceObject::SetWasmExportedFunction(isolate, instance, function_index,
result);
return result;
entry = WasmInstanceObject::GetOrCreateWasmExportedFunction(isolate, instance,
function_index);
entries->set(entry_index, *entry);
return entry;
}
void WasmTableObject::Fill(Isolate* isolate, Handle<WasmTableObject> table,
......@@ -1824,6 +1802,33 @@ MaybeHandle<WasmExportedFunction> WasmInstanceObject::GetWasmExportedFunction(
return result;
}
Handle<WasmExportedFunction>
WasmInstanceObject::GetOrCreateWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int function_index) {
MaybeHandle<WasmExportedFunction> maybe_result =
WasmInstanceObject::GetWasmExportedFunction(isolate, instance,
function_index);
Handle<WasmExportedFunction> result;
if (maybe_result.ToHandle(&result)) {
return result;
}
const WasmModule* module = instance->module_object()->module();
const WasmFunction& function = module->functions[function_index];
Handle<Code> wrapper_code =
compiler::CompileJSToWasmWrapper(isolate, function.sig, function.imported)
.ToHandleChecked();
result = WasmExportedFunction::New(
isolate, instance, function_index,
static_cast<int>(function.sig->parameter_count()), wrapper_code);
WasmInstanceObject::SetWasmExportedFunction(isolate, instance, function_index,
result);
return result;
}
void WasmInstanceObject::SetWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index,
Handle<WasmExportedFunction> val) {
......@@ -2054,9 +2059,8 @@ int WasmExportedFunction::function_index() {
}
Handle<WasmExportedFunction> WasmExportedFunction::New(
Isolate* isolate, Handle<WasmInstanceObject> instance,
MaybeHandle<String> maybe_name, int func_index, int arity,
Handle<Code> export_wrapper) {
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
int arity, Handle<Code> export_wrapper) {
DCHECK_EQ(Code::JS_TO_WASM_FUNCTION, export_wrapper->kind());
int num_imported_functions = instance->module()->num_imported_functions;
int jump_table_offset = -1;
......@@ -2074,6 +2078,14 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
function_data->set_instance(*instance);
function_data->set_jump_table_offset(jump_table_offset);
function_data->set_function_index(func_index);
MaybeHandle<String> maybe_name;
if (instance->module()->origin == wasm::kAsmJsOrigin) {
// We can use the function name only for asm.js. For WebAssembly, the
// function name is specified as the function_index.toString().
maybe_name = WasmModuleObject::GetFunctionNameOrNull(
isolate, handle(instance->module_object(), isolate), func_index);
}
Handle<String> name;
if (!maybe_name.ToHandle(&name)) {
EmbeddedVector<char, 16> buffer;
......
......@@ -582,6 +582,15 @@ class WasmInstanceObject : public JSObject {
static MaybeHandle<WasmExportedFunction> GetWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance, int index);
// Acquires the {WasmExportedFunction} for a given {function_index} from the
// cache of the given {instance}, or creates a new {WasmExportedFunction} if
// it does not exist yet. The new {WasmExportedFunction} is added to the
// cache of the {instance} immediately.
static Handle<WasmExportedFunction> GetOrCreateWasmExportedFunction(
Isolate* isolate, Handle<WasmInstanceObject> instance,
int function_index);
static void SetWasmExportedFunction(Isolate* isolate,
Handle<WasmInstanceObject> instance,
int index,
......@@ -650,9 +659,8 @@ class WasmExportedFunction : public JSFunction {
V8_EXPORT_PRIVATE static bool IsWasmExportedFunction(Object object);
V8_EXPORT_PRIVATE static Handle<WasmExportedFunction> New(
Isolate* isolate, Handle<WasmInstanceObject> instance,
MaybeHandle<String> maybe_name, int func_index, int arity,
Handle<Code> export_wrapper);
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
int arity, Handle<Code> export_wrapper);
Address GetWasmCallTarget();
......
......@@ -370,6 +370,7 @@ bool WasmOpcodes::IsAnyRefOpcode(WasmOpcode opcode) {
switch (opcode) {
case kExprRefNull:
case kExprRefIsNull:
case kExprRefFunc:
return true;
default:
return false;
......
......@@ -144,9 +144,8 @@ Handle<JSFunction> TestingModuleBuilder::WrapCode(uint32_t index) {
compiler::CompileJSToWasmWrapper(isolate_, sig, false);
Handle<Code> ret_code = maybe_ret_code.ToHandleChecked();
Handle<JSFunction> ret = WasmExportedFunction::New(
isolate_, instance_object(), MaybeHandle<String>(),
static_cast<int>(index), static_cast<int>(sig->parameter_count()),
ret_code);
isolate_, instance_object(), static_cast<int>(index),
static_cast<int>(sig->parameter_count()), ret_code);
// Add reference to the exported wrapper code.
Handle<WasmModuleObject> module_object(instance_object()->module_object(),
......
......@@ -37,6 +37,7 @@ class TestSignatures {
sig_d_d(1, 1, kDoubleTypes4),
sig_d_dd(1, 2, kDoubleTypes4),
sig_r_v(1, 0, kRefTypes4),
sig_a_v(1, 0, kFuncTypes4),
sig_v_v(0, 0, kIntTypes4),
sig_v_i(0, 1, kIntTypes4),
sig_v_ii(0, 2, kIntTypes4),
......@@ -91,6 +92,7 @@ class TestSignatures {
FunctionSig* d_dd() { return &sig_d_dd; }
FunctionSig* r_v() { return &sig_r_v; }
FunctionSig* a_v() { return &sig_a_v; }
FunctionSig* v_v() { return &sig_v_v; }
FunctionSig* v_i() { return &sig_v_i; }
......@@ -150,6 +152,7 @@ class TestSignatures {
FunctionSig sig_d_dd;
FunctionSig sig_r_v;
FunctionSig sig_a_v;
FunctionSig sig_v_v;
FunctionSig sig_v_i;
......
......@@ -4,7 +4,7 @@
// Flags: --expose-wasm --experimental-wasm-anyref --expose-gc
load("test/mjsunit/wasm/wasm-module-builder.js");
load('test/mjsunit/wasm/wasm-module-builder.js');
(function testAnyFuncIdentityFunction() {
print(arguments.callee.name);
......@@ -13,11 +13,10 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
.addBody([kExprGetLocal, 0])
.exportFunc();
const instance = builder.instantiate();
assertThrows(() => instance.exports.main(print), TypeError);
assertThrows(() => instance.exports.main({'hello' : 'world'}), TypeError);
assertThrows(() => instance.exports.main({'hello': 'world'}), TypeError);
assertSame(
instance.exports.main, instance.exports.main(instance.exports.main));
})();
......@@ -26,10 +25,9 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_v_a);
const imp_index = builder.addImport("q", "func", sig_index);
const imp_index = builder.addImport('q', 'func', sig_index);
builder.addFunction('main', sig_index)
.addBody([kExprGetLocal, 0,
kExprCallFunction, imp_index])
.addBody([kExprGetLocal, 0, kExprCallFunction, imp_index])
.exportFunc();
const main = builder.instantiate({q: {func: checkFunction}}).exports.main;
......@@ -46,24 +44,35 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
const ref_sig = builder.addType(kSig_v_a);
const void_sig = builder.addType(kSig_v_v);
const imp_index = builder.addImport("q", "func", ref_sig);
const gc_index = builder.addImport("q", "gc", void_sig);
const imp_index = builder.addImport('q', 'func', ref_sig);
const gc_index = builder.addImport('q', 'gc', void_sig);
// First call the gc, then check if the object still exists.
builder.addFunction('main', ref_sig)
.addLocals({anyfunc_count: 10})
.addBody([
kExprGetLocal, 0, kExprSetLocal, 1, // Set local
kExprGetLocal, 0, kExprSetLocal, 2, // Set local
kExprGetLocal, 0, kExprSetLocal, 3, // Set local
kExprGetLocal, 0, kExprSetLocal, 4, // Set local
kExprGetLocal, 0, kExprSetLocal, 5, // Set local
kExprGetLocal, 0, kExprSetLocal, 6, // Set local
kExprGetLocal, 0, kExprSetLocal, 7, // Set local
kExprGetLocal, 0, kExprSetLocal, 8, // Set local
kExprGetLocal, 0, kExprSetLocal, 9, // Set local
kExprGetLocal, 0, kExprSetLocal, 10, // Set local
kExprCallFunction, gc_index, // call gc
kExprGetLocal, 9, kExprCallFunction, imp_index // call import
kExprGetLocal, 0,
kExprSetLocal, 1, // Set local
kExprGetLocal, 0,
kExprSetLocal, 2, // Set local
kExprGetLocal, 0,
kExprSetLocal, 3, // Set local
kExprGetLocal, 0,
kExprSetLocal, 4, // Set local
kExprGetLocal, 0,
kExprSetLocal, 5, // Set local
kExprGetLocal, 0,
kExprSetLocal, 6, // Set local
kExprGetLocal, 0,
kExprSetLocal, 7, // Set local
kExprGetLocal, 0,
kExprSetLocal, 8, // Set local
kExprGetLocal, 0,
kExprSetLocal, 9, // Set local
kExprGetLocal, 0,
kExprSetLocal, 10, // Set local
kExprCallFunction, gc_index, // call gc
kExprGetLocal, 9,
kExprCallFunction, imp_index // call import
])
.exportFunc();
......@@ -82,8 +91,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
const ref_sig = builder.addType(kSig_v_a);
const void_sig = builder.addType(kSig_v_v);
const imp_index = builder.addImport("q", "func", ref_sig);
const gc_index = builder.addImport("q", "gc", void_sig);
const imp_index = builder.addImport('q', 'func', ref_sig);
const gc_index = builder.addImport('q', 'gc', void_sig);
// First call the gc, then check if the object still exists.
builder.addFunction('main', ref_sig)
.addBody([
......@@ -96,7 +105,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertSame(main, value);
}
const main = builder.instantiate({q: {func: checkFunction, gc: gc}}).exports.main;
const main =
builder.instantiate({q: {func: checkFunction, gc: gc}}).exports.main;
main(main);
})();
......@@ -104,7 +114,8 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function testPassAnyFuncWithGCInWrapper() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const kSig_a_iai = makeSig([kWasmI32, kWasmAnyFunc, kWasmI32], [kWasmAnyFunc]);
const kSig_a_iai =
makeSig([kWasmI32, kWasmAnyFunc, kWasmI32], [kWasmAnyFunc]);
const sig_index = builder.addType(kSig_a_iai);
builder.addFunction('main', sig_index)
.addBody([kExprGetLocal, 1])
......@@ -152,9 +163,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull])
.exportFunc();
builder.addFunction('main', sig_index).addBody([kExprRefNull]).exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
......@@ -197,3 +206,41 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testRefFuncOutOfBounds() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_a_v).addBody([kExprRefFunc, 10]);
assertThrows(() => builder.toModule(), WebAssembly.CompileError);
})();
(function testRefFuncIsCallable() {
print(arguments.callee.name);
const expected = 54;
const builder = new WasmModuleBuilder();
const function_index = builder.addFunction('hidden', kSig_i_v)
.addBody([kExprI32Const, expected])
.index;
builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, function_index])
.exportFunc();
const instance = builder.instantiate();
assertEquals(expected, instance.exports.main()());
})();
(function testRefFuncPreservesIdentity() {
print(arguments.callee.name);
const expected = 54;
const builder = new WasmModuleBuilder();
const foo = builder.addFunction('foo', kSig_i_v)
.addBody([kExprI32Const, expected])
.exportFunc();
builder.addFunction('main', kSig_a_v)
.addBody([kExprRefFunc, foo.index])
.exportFunc();
const instance = builder.instantiate();
assertSame(instance.exports.foo, instance.exports.main());
})();
......@@ -127,3 +127,29 @@ const dummy_func = exports.set_table_func1;
assertEquals(value3, instance.exports.get_t2(offset2)());
assertEquals(value1, instance.exports.get_t2(offset2 + 1)());
})();
(function testRefFuncInTableIsCallable() {
print(arguments.callee.name);
const expected = 54;
const index = 3;
const builder = new WasmModuleBuilder();
const table_index = builder.addTable(kWasmAnyFunc, 15, 15).index;
const sig_index = builder.addType(kSig_i_v);
const function_index = builder.addFunction('hidden', sig_index)
.addBody([kExprI32Const, expected])
.index;
builder.addFunction('main', kSig_i_v)
.addBody([
kExprI32Const, index, // entry index
kExprRefFunc, function_index, // function reference
kExprSetTable, table_index, // --
kExprI32Const, index, // entry index
kExprCallIndirect, sig_index, table_index // --
])
.exportFunc();
const instance = builder.instantiate();
assertEquals(expected, instance.exports.main());
})();
......@@ -304,6 +304,16 @@ TEST_F(FunctionBodyDecoderTest, RefNull) {
ExpectValidates(sigs.r_v(), {kExprRefNull});
}
TEST_F(FunctionBodyDecoderTest, RefFunc) {
WASM_FEATURE_SCOPE(anyref);
TestModuleBuilder builder;
module = builder.module();
builder.AddFunction(sigs.v_ii());
builder.AddFunction(sigs.ii_v());
ExpectValidates(sigs.a_v(), {kExprRefFunc, 1});
}
TEST_F(FunctionBodyDecoderTest, EmptyFunction) {
ExpectValidates(sigs.v_v(), {});
ExpectFailure(sigs.i_i(), {});
......@@ -984,6 +994,7 @@ TEST_F(FunctionBodyDecoderTest, ReturnVoid3) {
ExpectFailure(sigs.v_v(), {kExprF32Const, 0, 0, 0, 0});
ExpectFailure(sigs.v_v(), {kExprF64Const, 0, 0, 0, 0, 0, 0, 0, 0});
ExpectFailure(sigs.v_v(), {kExprRefNull});
ExpectFailure(sigs.v_v(), {kExprRefFunc, 0});
ExpectFailure(sigs.v_i(), {kExprGetLocal, 0});
}
......@@ -3397,6 +3408,12 @@ TEST_F(WasmOpcodeLengthTest, VariableLength) {
ExpectLength(4, kExprGetGlobal, U32V_3(44));
ExpectLength(5, kExprGetGlobal, U32V_4(66));
ExpectLength(6, kExprGetGlobal, U32V_5(77));
ExpectLength(2, kExprRefFunc, U32V_1(1));
ExpectLength(3, kExprRefFunc, U32V_2(33));
ExpectLength(4, kExprRefFunc, U32V_3(44));
ExpectLength(5, kExprRefFunc, U32V_4(66));
ExpectLength(6, kExprRefFunc, U32V_5(77));
}
TEST_F(WasmOpcodeLengthTest, LoadsAndStores) {
......
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