Commit dd152527 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement call_ref, return_call_ref, add some basic tests

Drive-by: Add flag implications for wasm experimental features:
  gc -> typed_funcref, typed_funcref -> reftypes.

Bug: v8:9495
Change-Id: Ia6054886935d68e79b8f463289aa9e1e9d6484f2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2352777Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69403}
parent 3380977d
......@@ -2976,6 +2976,153 @@ Node* WasmGraphBuilder::BuildLoadFunctionIndexFromExportedFunctionData(
return function_index;
}
Node* HasInstanceType(WasmGraphAssembler* gasm, Node* object,
InstanceType type) {
Node* map = gasm->Load(MachineType::TaggedPointer(), object,
wasm::ObjectAccess::ToTagged(HeapObject::kMapOffset));
Node* instance_type =
gasm->Load(MachineType::Uint16(), map,
wasm::ObjectAccess::ToTagged(Map::kInstanceTypeOffset));
return gasm->Word32Equal(instance_type, gasm->Int32Constant(type));
}
Node* WasmGraphBuilder::BuildCallRef(uint32_t sig_index, Vector<Node*> args,
Vector<Node*> rets,
CheckForNull null_check,
IsReturnCall continuation,
wasm::WasmCodePosition position) {
if (null_check == kWithNullCheck) {
TrapIfTrue(wasm::kTrapNullDereference, gasm_->WordEqual(args[0], RefNull()),
position);
}
const wasm::FunctionSig* sig = env_->module->signature(sig_index);
Node* function_data = BuildLoadFunctionDataFromExportedFunction(args[0]);
Node* is_js_function =
HasInstanceType(gasm_.get(), function_data, WASM_JS_FUNCTION_DATA_TYPE);
auto js_label = gasm_->MakeLabel();
auto end_label = gasm_->MakeLabel(MachineRepresentation::kTaggedPointer,
MachineRepresentation::kTaggedPointer);
gasm_->GotoIf(is_js_function, &js_label);
{
// Call to a WasmExportedFunction.
// Load instance object corresponding to module where callee is defined.
Node* callee_instance =
gasm_->Load(MachineType::TaggedPointer(), function_data,
wasm::ObjectAccess::ToTagged(
WasmExportedFunctionData::kInstanceOffset));
Node* function_index =
gasm_->Load(MachineType::TaggedPointer(), function_data,
wasm::ObjectAccess::ToTagged(
WasmExportedFunctionData::kFunctionIndexOffset));
auto imported_label = gasm_->MakeLabel();
// Check if callee is a locally defined or imported function it its module.
Node* imported_function_refs =
gasm_->Load(MachineType::TaggedPointer(), callee_instance,
wasm::ObjectAccess::ToTagged(
WasmInstanceObject::kImportedFunctionRefsOffset));
Node* imported_functions_num =
gasm_->Load(MachineType::TaggedPointer(), imported_function_refs,
wasm::ObjectAccess::ToTagged(FixedArray::kLengthOffset));
gasm_->GotoIf(gasm_->SmiLessThan(function_index, imported_functions_num),
&imported_label);
{
// Function locally defined in module.
Node* jump_table_start =
gasm_->Load(MachineType::Pointer(), callee_instance,
wasm::ObjectAccess::ToTagged(
WasmInstanceObject::kJumpTableStartOffset));
Node* jump_table_offset =
BuildLoadJumpTableOffsetFromExportedFunctionData(function_data);
Node* jump_table_slot =
gasm_->IntAdd(jump_table_start, jump_table_offset);
gasm_->Goto(&end_label, jump_table_slot,
callee_instance /* Unused, dummy value */);
}
{
// Function imported to module.
// TODO(9495): Make sure it works with functions imported from other
// modules. Currently, this will never happen: Since functions have to be
// tunneled through JS, and we currently do not have a JS API to pass
// specific function types, we habe to export/import function references
// as funcref. Then, we cannot cast down to the type of the function,
// because we do not have access to the defining module's types. This
// could be fixed either by building a richer JS API, or by implementing
// the type import proposal. That said, this code should work for those
// cases too.
gasm_->Bind(&imported_label);
Node* imported_instance = gasm_->Load(
MachineType::TaggedPointer(), imported_function_refs,
gasm_->Int32Add(
gasm_->Int32Mul(BuildChangeSmiToInt32(function_index),
gasm_->Int32Constant(kTaggedSize)),
gasm_->Int32Constant(FixedArray::kHeaderSize - kHeapObjectTag)));
Node* imported_function_targets =
gasm_->Load(MachineType::Pointer(), callee_instance,
wasm::ObjectAccess::ToTagged(
WasmInstanceObject::kImportedFunctionTargetsOffset));
Node* target_node =
gasm_->Load(MachineType::Pointer(), imported_function_targets,
gasm_->IntMul(BuildChangeSmiToIntPtr(function_index),
gasm_->IntPtrConstant(kSystemPointerSize)));
gasm_->Goto(&end_label, target_node, imported_instance);
}
}
{
// Call to a WasmJSFunction.
// The call target is the wasm-to-js wrapper code.
gasm_->Bind(&js_label);
// TODO(7748): Implement.
TrapIfTrue(wasm::kTrapUnreachable, gasm_->Int32Constant(1), position);
gasm_->Goto(&end_label, args[0], RefNull() /* Dummy value */);
}
gasm_->Bind(&end_label);
args[0] = end_label.PhiAt(0);
Node* instance_node = end_label.PhiAt(1);
const UseRetpoline use_retpoline =
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline;
Node* call = continuation == kCallContinues
? BuildWasmCall(sig, args, rets, position, instance_node,
use_retpoline)
: BuildWasmReturnCall(sig, args, position, instance_node,
use_retpoline);
return call;
}
Node* WasmGraphBuilder::CallRef(uint32_t sig_index, Vector<Node*> args,
Vector<Node*> rets,
WasmGraphBuilder::CheckForNull null_check,
wasm::WasmCodePosition position) {
return BuildCallRef(sig_index, args, rets, null_check,
IsReturnCall::kCallContinues, position);
}
Node* WasmGraphBuilder::ReturnCallRef(uint32_t sig_index, Vector<Node*> args,
WasmGraphBuilder::CheckForNull null_check,
wasm::WasmCodePosition position) {
return BuildCallRef(sig_index, args, {}, null_check,
IsReturnCall::kReturnCall, position);
}
Node* WasmGraphBuilder::ReturnCall(uint32_t index, Vector<Node*> args,
wasm::WasmCodePosition position) {
DCHECK_NULL(args[0]);
......
......@@ -274,11 +274,16 @@ class WasmGraphBuilder {
Node* CallIndirect(uint32_t table_index, uint32_t sig_index,
Vector<Node*> args, Vector<Node*> rets,
wasm::WasmCodePosition position);
Node* CallRef(uint32_t sig_index, Vector<Node*> args, Vector<Node*> rets,
CheckForNull null_check, wasm::WasmCodePosition position);
Node* ReturnCall(uint32_t index, Vector<Node*> args,
wasm::WasmCodePosition position);
Node* ReturnCallIndirect(uint32_t table_index, uint32_t sig_index,
Vector<Node*> args, wasm::WasmCodePosition position);
Node* ReturnCallRef(uint32_t sig_index, Vector<Node*> args,
CheckForNull null_check, wasm::WasmCodePosition position);
// Return value is not expected to be used,
// but we need it for compatibility with graph-builder-interface.
Node* BrOnNull(Node* ref_object, Node** non_null_node, Node** null_node);
......@@ -501,6 +506,9 @@ class WasmGraphBuilder {
Node* BuildImportCall(const wasm::FunctionSig* sig, Vector<Node*> args,
Vector<Node*> rets, wasm::WasmCodePosition position,
Node* func_index, IsReturnCall continuation);
Node* BuildCallRef(uint32_t sig_index, Vector<Node*> args, Vector<Node*> rets,
CheckForNull null_check, IsReturnCall continuation,
wasm::WasmCodePosition position);
Node* GetBuiltinPointerTarget(int builtin_id);
Node* BuildF32CopySign(Node* left, Node* right);
......
......@@ -1797,6 +1797,7 @@ void WasmExportedFunctionData::WasmExportedFunctionDataPrint(
void WasmJSFunctionData::WasmJSFunctionDataPrint(std::ostream& os) { // NOLINT
PrintHeader(os, "WasmJSFunctionData");
os << "\n - callable: " << Brief(callable());
os << "\n - wrapper_code: " << Brief(wrapper_code());
os << "\n";
}
......
......@@ -786,6 +786,9 @@ DEFINE_STRING(dump_wasm_module_path, nullptr,
FOREACH_WASM_FEATURE_FLAG(DECL_WASM_FLAG)
#undef DECL_WASM_FLAG
DEFINE_IMPLICATION(experimental_wasm_gc, experimental_wasm_typed_funcref)
DEFINE_IMPLICATION(experimental_wasm_typed_funcref, experimental_wasm_reftypes)
DEFINE_BOOL(wasm_staging, false, "enable staged wasm features")
#define WASM_STAGING_IMPLICATION(feat, desc, val) \
......
......@@ -2309,6 +2309,12 @@ class LiftoffCompiler {
CallIndirect(decoder, index_val, imm, kNoReturnCall);
}
void CallRef(FullDecoder* decoder, const Value& func_ref,
const FunctionSig* sig, uint32_t sig_index, const Value args[],
Value returns[]) {
unsupported(decoder, kRefTypes, "call_ref");
}
void ReturnCall(FullDecoder* decoder,
const CallFunctionImmediate<validate>& imm,
const Value args[]) {
......@@ -2321,6 +2327,12 @@ class LiftoffCompiler {
CallIndirect(decoder, index_val, imm, kReturnCall);
}
void ReturnCallRef(FullDecoder* decoder, const Value& func_ref,
const FunctionSig* sig, uint32_t sig_index,
const Value args[]) {
unsupported(decoder, kRefTypes, "call_ref");
}
void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
unsupported(decoder, kRefTypes, "br_on_null");
}
......
......@@ -47,7 +47,10 @@ struct WasmException;
#define CHECK_PROTOTYPE_OPCODE(feat) \
DCHECK(this->module_->origin == kWasmOrigin); \
if (!VALIDATE(this->enabled_.has_##feat())) { \
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
this->errorf(this->pc(), \
"Invalid opcode 0x%x (enable with --experimental-wasm-" #feat \
")", \
opcode); \
return 0; \
} \
this->detected_->Add(kFeature_##feat);
......@@ -910,6 +913,10 @@ struct ControlBase {
F(CallIndirect, const Value& index, \
const CallIndirectImmediate<validate>& imm, const Value args[], \
Value returns[]) \
F(CallRef, const Value& func_ref, const FunctionSig* sig, \
uint32_t sig_index, const Value args[], const Value returns[]) \
F(ReturnCallRef, const Value& func_ref, const FunctionSig* sig, \
uint32_t sig_index, const Value args[]) \
F(ReturnCall, const CallFunctionImmediate<validate>& imm, \
const Value args[]) \
F(ReturnCallIndirect, const Value& index, \
......@@ -2865,6 +2872,47 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 1 + imm.length;
}
DECODE(CallRef) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
Value func_ref = Pop(0);
ValueType func_type = func_ref.type;
if (!func_type.is_object_reference_type() || !func_type.has_index() ||
!this->module_->has_signature(func_type.ref_index())) {
this->errorf(this->pc_,
"call_ref: Expected function reference on top of stack, "
"found %s of type %s instead",
SafeOpcodeNameAt(func_ref.pc), func_type.name().c_str());
return 0;
}
const FunctionSig* sig = this->module_->signature(func_type.ref_index());
ArgVector args = PopArgs(sig);
Value* returns = PushReturns(sig);
CALL_INTERFACE_IF_REACHABLE(CallRef, func_ref, sig, func_type.ref_index(),
args.begin(), returns);
return 1;
}
DECODE(ReturnCallRef) {
CHECK_PROTOTYPE_OPCODE(typed_funcref);
CHECK_PROTOTYPE_OPCODE(return_call);
Value func_ref = Pop(0);
ValueType func_type = func_ref.type;
if (!func_type.is_object_reference_type() || !func_type.has_index() ||
!this->module_->has_signature(func_type.ref_index())) {
this->errorf(this->pc_,
"return_call_ref: Expected function reference on top of "
"found %s of type %s instead",
SafeOpcodeNameAt(func_ref.pc), func_type.name().c_str());
return 0;
}
const FunctionSig* sig = this->module_->signature(func_type.ref_index());
ArgVector args = PopArgs(sig);
CALL_INTERFACE_IF_REACHABLE(ReturnCallRef, func_ref, sig,
func_type.ref_index(), args.begin());
EndControl();
return 1;
}
DECODE(Numeric) {
byte numeric_index =
this->template read_u8<validate>(this->pc_ + 1, "numeric index");
......@@ -2990,6 +3038,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DECODE_IMPL(CallIndirect);
DECODE_IMPL(ReturnCall);
DECODE_IMPL(ReturnCallIndirect);
DECODE_IMPL(CallRef);
DECODE_IMPL(ReturnCallRef);
DECODE_IMPL2(kNumericPrefix, Numeric);
DECODE_IMPL2(kSimdPrefix, Simd);
DECODE_IMPL2(kAtomicPrefix, Atomic);
......
......@@ -484,6 +484,26 @@ class WasmGraphBuildingInterface {
imm.sig_index, args);
}
void CallRef(FullDecoder* decoder, const Value& func_ref,
const FunctionSig* sig, uint32_t sig_index, const Value args[],
Value returns[]) {
CheckForNull null_check = func_ref.type.is_nullable()
? CheckForNull::kWithNullCheck
: CheckForNull::kWithoutNullCheck;
DoCall(decoder, kRef, 0, null_check, func_ref.node, sig, sig_index, args,
returns);
}
void ReturnCallRef(FullDecoder* decoder, const Value& func_ref,
const FunctionSig* sig, uint32_t sig_index,
const Value args[]) {
CheckForNull null_check = func_ref.type.is_nullable()
? CheckForNull::kWithNullCheck
: CheckForNull::kWithoutNullCheck;
DoReturnCall(decoder, kRef, 0, null_check, func_ref.node, sig, sig_index,
args);
}
void BrOnNull(FullDecoder* decoder, const Value& ref_object, uint32_t depth) {
SsaEnv* non_null_env = ssa_env_;
SsaEnv* null_env = Split(decoder->zone(), non_null_env);
......@@ -1134,7 +1154,9 @@ class WasmGraphBuildingInterface {
VectorOf(return_nodes), decoder->position());
break;
case kRef:
UNREACHABLE();
BUILD(CallRef, sig_index, VectorOf(arg_nodes), VectorOf(return_nodes),
null_check, decoder->position());
break;
}
for (size_t i = 0; i < return_count; ++i) {
returns[i].node = return_nodes[i];
......@@ -1163,7 +1185,8 @@ class WasmGraphBuildingInterface {
BUILD(ReturnCall, sig_index, VectorOf(arg_nodes), decoder->position());
break;
case kRef:
UNREACHABLE();
BUILD(ReturnCallRef, sig_index, VectorOf(arg_nodes), null_check,
decoder->position());
}
}
};
......
......@@ -192,7 +192,7 @@ Handle<Map> AllocateSubRtt(Isolate* isolate,
// TODO(7748): Canonicalize rtts to make them work for identical function
// types.
rtt = Map::Copy(isolate, isolate->wasm_exported_function_map(),
"fresh function map");
"fresh function map for AllocateSubRtt");
}
cache = RttSubtypes::Insert(isolate, cache, type, rtt);
parent->wasm_type_info().set_subtypes(*cache);
......@@ -603,10 +603,11 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
CreateArrayMap(isolate_, module_, map_index, anyref_sentinel_map);
break;
case kWasmFunctionTypeCode:
// TODO(7748): Canonicalize rtts to make them work for identical
// function types.
// TODO(7748): Think about canonicalizing rtts to make them work for
// identical function types.
map = Map::Copy(isolate_, isolate_->wasm_exported_function_map(),
"fresh function map");
"fresh function map for function type canonical rtt "
"initialization");
break;
}
maps->set(map_index, *map);
......
......@@ -1894,6 +1894,8 @@ bool WasmJSFunction::IsWasmJSFunction(Object object) {
return js_function.shared().HasWasmJSFunctionData();
}
// TODO(7748): WasmJSFunctions should compile/find and store an import wrapper
// in case they are called from within wasm.
Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
const wasm::FunctionSig* sig,
Handle<JSReceiver> callable) {
......@@ -1923,7 +1925,9 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
name = JSFunction::GetName(Handle<JSFunction>::cast(callable));
name = String::Flatten(isolate, name);
}
Handle<Map> function_map = isolate->wasm_exported_function_map();
Handle<Map> function_map =
Map::Copy(isolate, isolate->wasm_exported_function_map(),
"fresh function map for WasmJSFunction::New");
NewFunctionArgs args =
NewFunctionArgs::ForWasm(name, function_data, function_map);
Handle<JSFunction> js_function = isolate->factory()->NewFunction(args);
......
......@@ -153,6 +153,8 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_OP(CallIndirect, "call_indirect")
CASE_OP(ReturnCall, "return_call")
CASE_OP(ReturnCallIndirect, "return_call_indirect")
CASE_OP(CallRef, "call_ref")
CASE_OP(ReturnCallRef, "return_call_ref")
CASE_OP(BrOnNull, "br_on_null")
CASE_OP(Drop, "drop")
CASE_OP(Select, "select")
......
......@@ -23,24 +23,26 @@ std::ostream& operator<<(std::ostream& os, const FunctionSig& function);
bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
// Control expressions and blocks.
#define FOREACH_CONTROL_OPCODE(V) \
V(Unreachable, 0x00, _) \
V(Nop, 0x01, _) \
V(Block, 0x02, _) \
V(Loop, 0x03, _) \
V(If, 0x04, _) \
V(Else, 0x05, _) \
V(Try, 0x06, _ /* eh_prototype */) \
V(Catch, 0x07, _ /* eh_prototype */) \
V(Throw, 0x08, _ /* eh_prototype */) \
V(Rethrow, 0x09, _ /* eh_prototype */) \
V(BrOnExn, 0x0a, _ /* eh prototype */) \
V(End, 0x0b, _) \
V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \
V(BrTable, 0x0e, _) \
V(Return, 0x0f, _) \
V(Let, 0x17, _ /* typed_funcref prototype */) \
#define FOREACH_CONTROL_OPCODE(V) \
V(Unreachable, 0x00, _) \
V(Nop, 0x01, _) \
V(Block, 0x02, _) \
V(Loop, 0x03, _) \
V(If, 0x04, _) \
V(Else, 0x05, _) \
V(Try, 0x06, _ /* eh_prototype */) \
V(Catch, 0x07, _ /* eh_prototype */) \
V(Throw, 0x08, _ /* eh_prototype */) \
V(Rethrow, 0x09, _ /* eh_prototype */) \
V(BrOnExn, 0x0a, _ /* eh prototype */) \
V(End, 0x0b, _) \
V(Br, 0x0c, _) \
V(BrIf, 0x0d, _) \
V(BrTable, 0x0e, _) \
V(Return, 0x0f, _) \
V(CallRef, 0x14, _ /* typed_funcref prototype */) \
V(ReturnCallRef, 0x15, _ /* typed_funcref prototype */) \
V(Let, 0x17, _ /* typed_funcref prototype */) \
V(BrOnNull, 0xd4, _ /* gc prototype */)
// Constants, locals, globals, and calls.
......
......@@ -900,6 +900,25 @@ TEST(FunctionRefs) {
tester.CheckResult(test, 0);
}
TEST(CallRef) {
WasmGCTester tester;
byte callee = tester.DefineFunction(
tester.sigs.i_ii(), {},
{WASM_I32_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprEnd});
byte caller = tester.DefineFunction(
tester.sigs.i_i(), {},
{WASM_CALL_REF(WASM_REF_FUNC(callee), WASM_I32V(42), WASM_GET_LOCAL(0)),
kExprEnd});
// This is just so func_index counts as "declared".
tester.AddGlobal(ValueType::Ref(0, kNullable), false,
WasmInitExpr::RefFuncConst(callee));
tester.CompileModule();
tester.CheckResult(caller, 47, 5);
}
TEST(RefTestCastNull) {
WasmGCTester tester;
byte type_index = tester.DefineStruct({F(wasm::kWasmI32, true)});
......
......@@ -472,6 +472,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_ARRAY_LEN(typeidx, array) \
array, WASM_GC_OP(kExprArrayLen), static_cast<byte>(typeidx)
#define WASM_RTT(depth, typeidx) kLocalRtt, U32V_1(depth), U32V_1(typeidx)
#define WASM_RTT_CANON(typeidx) \
WASM_GC_OP(kExprRttCanon), static_cast<byte>(typeidx)
#define WASM_RTT_SUB(typeidx, supertype) \
......@@ -483,6 +484,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_BR_ON_NULL(depth, ref_object) \
ref_object, kExprBrOnNull, static_cast<byte>(depth)
// Pass: sig_index, ...args, func_index
#define WASM_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
......@@ -492,7 +494,10 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_RETURN_CALL_INDIRECT(sig_index, ...) \
__VA_ARGS__, kExprReturnCallIndirect, static_cast<byte>(sig_index), TABLE_ZERO
#define WASM_RTT(depth, typeidx) kLocalRtt, U32V_1(depth), U32V_1(typeidx)
#define WASM_CALL_REF(func_ref, ...) __VA_ARGS__, func_ref, kExprCallRef
#define WASM_RETURN_CALL_REF(func_ref, ...) \
__VA_ARGS__, func_ref, kExprReturnCallRef
// shift locals by 1; let (locals[0]: local_type) = value in ...
#define WASM_LET_1_V(local_type, value, ...) \
......
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-type-reflection --experimental-wasm-gc
load("test/mjsunit/wasm/wasm-module-builder.js");
(function Test1() {
var instance = (function () {
var builder = new WasmModuleBuilder();
var sig_index = builder.addType(kSig_i_ii);
var imported_webassembly_function_index =
builder.addImport("imports", "mul", sig_index);
var imported_js_function_index =
builder.addImport("imports", "add", sig_index);
builder.addExport("reexported_js_function",
imported_js_function_index);
builder.addExport("reexported_webassembly_function",
imported_webassembly_function_index);
var locally_defined_function =
builder.addFunction("sub", sig_index)
.addBody([
kExprLocalGet, 0,
kExprLocalGet, 1,
kExprI32Sub
])
.exportFunc();
builder.addFunction("main", makeSig([kWasmAnyFunc, kWasmI32, kWasmI32],
[kWasmI32]))
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 2,
kExprLocalGet, 0,
kGCPrefix, kExprRttCanon, 0,
kGCPrefix, kExprRefCast, kWasmAnyFunc, 0,
kExprCallRef
])
.exportFunc();
builder.addFunction("test_local", makeSig([], [kWasmI32]))
.addBody([
kExprI32Const, 55,
kExprI32Const, 42,
kExprRefFunc, locally_defined_function.index,
kExprCallRef
])
.exportFunc();
builder.addFunction("test_js_import", makeSig([], [kWasmI32]))
.addBody([
kExprI32Const, 15,
kExprI32Const, 42,
kExprRefFunc, imported_js_function_index,
kExprCallRef
])
.exportFunc();
builder.addFunction("test_webassembly_import", makeSig([], [kWasmI32]))
.addBody([
kExprI32Const, 3,
kExprI32Const, 7,
kExprRefFunc, imported_webassembly_function_index,
kExprCallRef
])
.exportFunc();
return builder.instantiate({imports: {
add: function(a, b) { return a + b; },
mul: new WebAssembly.Function({parameters:['i32', 'i32'],
results: ['i32']},
function(a, b) { return a * b; })
}});
})();
// Check the modules exist.
assertFalse(instance === undefined);
assertFalse(instance === null);
assertFalse(instance === 0);
assertEquals("object", typeof instance.exports);
assertEquals("function", typeof instance.exports.main);
print("--locally defined func--");
assertEquals(13, instance.exports.test_local());
print("--locally defined exported func--")
assertEquals(5, instance.exports.main(instance.exports.sub, 12, 7));
print("--imported js func--");
assertEquals(57, instance.exports.test_js_import());
print("--imported and reexported js func--")
assertEquals(19, instance.exports.main(
instance.exports.reexported_js_function, 12, 7));
// TODO(7748): Make this work.
//print("--imported WebAssembly.Function--")
//assertEquals(21, instance.exports.test_webassembly_import());
//print(" --not imported WebAssembly.Function--")
})();
......@@ -210,6 +210,8 @@ let kExprCallFunction = 0x10;
let kExprCallIndirect = 0x11;
let kExprReturnCall = 0x12;
let kExprReturnCallIndirect = 0x13;
let kExprCallRef = 0x14;
let kExprReturnCallRef = 0x15;
let kExprDrop = 0x1a;
let kExprSelect = 0x1b;
let kExprSelectWithType = 0x1c;
......@@ -382,10 +384,15 @@ let kExprRefIsNull = 0xd1;
let kExprRefFunc = 0xd2;
// Prefix opcodes
let kGCPrefix = 0xfb;
let kNumericPrefix = 0xfc;
let kSimdPrefix = 0xfd;
let kAtomicPrefix = 0xfe;
// GC opcodes
let kExprRttCanon = 0x30;
let kExprRefCast = 0x41;
// Numeric opcodes.
let kExprMemoryInit = 0x08;
let kExprDataDrop = 0x09;
......
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