Skip to content

Commit 7a24aec

Browse files
liamappelbecommit-bot@chromium.org
authored andcommitted
[vm] Wasm function imports
Bug: #37882 Change-Id: Ia8aa9a87803d5e8b899ddab5479b8cd93e03fd71 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118204 Commit-Queue: Liam Appelbe <[email protected]> Reviewed-by: Ryan Macnak <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent 1375e26 commit 7a24aec

File tree

11 files changed

+478
-17
lines changed

11 files changed

+478
-17
lines changed

runtime/lib/wasm.cc

+255-15
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,54 @@ static bool ToWasmValue(const Number& value,
8585
}
8686
}
8787

88+
static Dart_Handle ToWasmValue(Dart_Handle value,
89+
wasmer_value_tag type,
90+
wasmer_value* out) {
91+
switch (type) {
92+
case wasmer_value_tag::WASM_I32: {
93+
int64_t i64;
94+
Dart_Handle result = Dart_IntegerToInt64(value, &i64);
95+
out->I32 = i64;
96+
if (out->I32 != i64) {
97+
return Dart_NewApiError("Int doesn't fit into 32-bits");
98+
}
99+
return result;
100+
}
101+
case wasmer_value_tag::WASM_I64:
102+
return Dart_IntegerToInt64(value, &out->I64);
103+
case wasmer_value_tag::WASM_F32: {
104+
double f64;
105+
Dart_Handle result = Dart_DoubleValue(value, &f64);
106+
out->F32 = f64;
107+
return result;
108+
}
109+
case wasmer_value_tag::WASM_F64:
110+
return Dart_DoubleValue(value, &out->F64);
111+
default:
112+
FATAL("Unknown WASM type");
113+
return nullptr;
114+
}
115+
}
116+
117+
static bool ToWasmValueTag(classid_t type, wasmer_value_tag* out) {
118+
switch (type) {
119+
case kWasmInt32Cid:
120+
*out = wasmer_value_tag::WASM_I32;
121+
return true;
122+
case kWasmInt64Cid:
123+
*out = wasmer_value_tag::WASM_I64;
124+
return true;
125+
case kWasmFloatCid:
126+
*out = wasmer_value_tag::WASM_F32;
127+
return true;
128+
case kWasmDoubleCid:
129+
*out = wasmer_value_tag::WASM_F64;
130+
return true;
131+
default:
132+
return false;
133+
}
134+
}
135+
88136
static RawObject* ToDartObject(wasmer_value_t ret) {
89137
switch (ret.tag) {
90138
case wasmer_value_tag::WASM_I32:
@@ -101,6 +149,22 @@ static RawObject* ToDartObject(wasmer_value_t ret) {
101149
}
102150
}
103151

152+
static Dart_Handle ToDartApiObject(wasmer_value value, wasmer_value_tag type) {
153+
switch (type) {
154+
case wasmer_value_tag::WASM_I32:
155+
return Dart_NewInteger(value.I32);
156+
case wasmer_value_tag::WASM_I64:
157+
return Dart_NewInteger(value.I64);
158+
case wasmer_value_tag::WASM_F32:
159+
return Dart_NewDouble(value.F32);
160+
case wasmer_value_tag::WASM_F64:
161+
return Dart_NewDouble(value.F64);
162+
default:
163+
FATAL("Unknown WASM type");
164+
return nullptr;
165+
}
166+
}
167+
104168
RawExternalTypedData* WasmMemoryToExternalTypedData(wasmer_memory_t* memory) {
105169
uint8_t* data = wasmer_memory_data(memory);
106170
uint32_t size = wasmer_memory_data_length(memory);
@@ -158,6 +222,41 @@ RawString* DescribeModule(const wasmer_module_t* module) {
158222
return String::New(desc.str().c_str());
159223
}
160224

225+
class WasmImports;
226+
227+
struct WasmFunctionImport {
228+
WasmImports* imports;
229+
std::unique_ptr<wasmer_value_tag[]> args;
230+
intptr_t num_args;
231+
wasmer_value_tag ret;
232+
intptr_t num_rets;
233+
int64_t fn_id;
234+
wasmer_import_func_t* wasm_fn;
235+
wasmer_trampoline_buffer_t* buffer;
236+
WasmFunctionImport(WasmImports* imports_,
237+
std::unique_ptr<wasmer_value_tag[]> args_,
238+
intptr_t num_args_,
239+
wasmer_value_tag ret_,
240+
intptr_t num_rets_,
241+
int64_t fn_id_)
242+
: imports(imports_),
243+
args(std::move(args_)),
244+
num_args(num_args_),
245+
ret(ret_),
246+
num_rets(num_rets_),
247+
fn_id(fn_id),
248+
wasm_fn(nullptr),
249+
buffer(nullptr) {}
250+
~WasmFunctionImport() {
251+
wasmer_trampoline_buffer_destroy(buffer);
252+
wasmer_import_func_destroy(wasm_fn);
253+
}
254+
};
255+
256+
extern "C" {
257+
int64_t Trampoline(void* context, int64_t* args);
258+
}
259+
161260
class WasmImports {
162261
public:
163262
explicit WasmImports(std::unique_ptr<char[]> module_name)
@@ -167,11 +266,15 @@ class WasmImports {
167266
for (wasmer_global_t* global : _globals) {
168267
wasmer_global_destroy(global);
169268
}
269+
for (WasmFunctionImport* fn_imp : _functions) {
270+
delete fn_imp;
271+
}
170272
for (const char* name : _import_names) {
171273
delete[] name;
172274
}
173275
}
174276

277+
void SetHandle(FinalizablePersistentHandle* handle) { _handle = handle; }
175278
size_t NumImports() const { return _imports.length(); }
176279
wasmer_import_t* RawImports() { return _imports.data(); }
177280

@@ -189,10 +292,82 @@ class WasmImports {
189292
global;
190293
}
191294

295+
void AddFunction(std::unique_ptr<char[]> name,
296+
int64_t fn_id,
297+
std::unique_ptr<wasmer_value_tag[]> args,
298+
intptr_t num_args,
299+
wasmer_value_tag ret,
300+
intptr_t num_rets) {
301+
// Trampoline args include the context pointer.
302+
const intptr_t num_trampoline_args = num_args + 1;
303+
304+
WasmFunctionImport* fn_imp = new WasmFunctionImport(
305+
this, std::move(args), num_args, ret, num_rets, fn_id);
306+
_functions.Add(fn_imp);
307+
308+
wasmer_trampoline_buffer_builder_t* builder =
309+
wasmer_trampoline_buffer_builder_new();
310+
uintptr_t trampoline_id =
311+
wasmer_trampoline_buffer_builder_add_callinfo_trampoline(
312+
builder,
313+
reinterpret_cast<wasmer_trampoline_callable_t*>(Trampoline),
314+
reinterpret_cast<void*>(fn_imp), num_trampoline_args);
315+
fn_imp->buffer = wasmer_trampoline_buffer_builder_build(builder);
316+
317+
const wasmer_trampoline_callable_t* trampoline =
318+
wasmer_trampoline_buffer_get_trampoline(fn_imp->buffer, trampoline_id);
319+
fn_imp->wasm_fn = wasmer_import_func_new(
320+
reinterpret_cast<void (*)(void*)>(
321+
const_cast<wasmer_trampoline_callable_t*>(trampoline)),
322+
fn_imp->args.get(), num_args, &ret, num_rets);
323+
324+
AddImport(std::move(name), wasmer_import_export_kind::WASM_FUNCTION)->func =
325+
fn_imp->wasm_fn;
326+
}
327+
328+
int64_t CallImportedFunction(WasmFunctionImport* fn_imp, int64_t* raw_args) {
329+
wasmer_value* wasm_args = reinterpret_cast<wasmer_value*>(raw_args);
330+
Dart_Handle fn_id = Dart_NewInteger(fn_imp->fn_id);
331+
Dart_Handle closure =
332+
Dart_Invoke(Dart_HandleFromWeakPersistent(_handle->apiHandle()),
333+
Dart_NewStringFromCString("getFunction"), 1, &fn_id);
334+
if (Dart_IsError(closure)) {
335+
Dart_ThrowException(closure);
336+
UNREACHABLE();
337+
}
338+
Dart_Handle result;
339+
{
340+
auto args =
341+
std::unique_ptr<Dart_Handle[]>(new Dart_Handle[fn_imp->num_args]);
342+
for (intptr_t i = 0; i < fn_imp->num_args; ++i) {
343+
args[i] = ToDartApiObject(wasm_args[i], fn_imp->args[i]);
344+
}
345+
result = Dart_InvokeClosure(closure, fn_imp->num_args, args.get());
346+
}
347+
if (Dart_IsError(result)) {
348+
Dart_ThrowException(result);
349+
UNREACHABLE();
350+
}
351+
if (fn_imp->num_rets == 0) {
352+
// Wasmer ignores the result of this function if it expects no results,
353+
// so skip the converters below (we get errors if we run them).
354+
return 0;
355+
}
356+
wasmer_value wasm_result;
357+
result = ToWasmValue(result, fn_imp->ret, &wasm_result);
358+
if (Dart_IsError(result)) {
359+
Dart_ThrowException(result);
360+
UNREACHABLE();
361+
}
362+
return wasm_result.I64;
363+
}
364+
192365
private:
366+
FinalizablePersistentHandle* _handle;
193367
std::unique_ptr<char[]> _module_name;
194368
MallocGrowableArray<const char*> _import_names;
195369
MallocGrowableArray<wasmer_global_t*> _globals;
370+
MallocGrowableArray<WasmFunctionImport*> _functions;
196371
MallocGrowableArray<wasmer_import_t> _imports;
197372

198373
wasmer_import_export_value* AddImport(std::unique_ptr<char[]> name,
@@ -212,6 +387,14 @@ class WasmImports {
212387
DISALLOW_COPY_AND_ASSIGN(WasmImports);
213388
};
214389

390+
extern "C" {
391+
int64_t Trampoline(void* context, int64_t* args) {
392+
WasmFunctionImport* fn_imp = reinterpret_cast<WasmFunctionImport*>(context);
393+
// Skip the first arg (it's another context pointer).
394+
return fn_imp->imports->CallImportedFunction(fn_imp, args + 1);
395+
}
396+
}
397+
215398
class WasmFunction {
216399
public:
217400
WasmFunction(MallocGrowableArray<classid_t> args,
@@ -468,8 +651,9 @@ DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 2) {
468651
WasmImports* imports = new WasmImports(ToUTF8(module_name));
469652

470653
imp_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(imports));
471-
FinalizablePersistentHandle::New(thread->isolate(), imp_wrap, imports,
472-
Finalize<WasmImports>, sizeof(WasmImports));
654+
imports->SetHandle(FinalizablePersistentHandle::New(
655+
thread->isolate(), imp_wrap, imports, Finalize<WasmImports>,
656+
sizeof(WasmImports)));
473657

474658
return Object::null();
475659
}
@@ -505,8 +689,9 @@ DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 5) {
505689
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
506690
wasmer_value_t wasm_value;
507691
if (!ToWasmValue(value, type.type_class_id(), &wasm_value)) {
508-
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
509-
"Can't convert dart value to WASM global variable")));
692+
Exceptions::ThrowArgumentError(String::Handle(
693+
zone, String::NewFormatted(
694+
"Can't convert dart value to WASM global variable")));
510695
UNREACHABLE();
511696
}
512697

@@ -515,6 +700,53 @@ DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 5) {
515700
return Object::null();
516701
}
517702

703+
DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 4) {
704+
GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
705+
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1));
706+
GET_NON_NULL_NATIVE_ARGUMENT(Integer, fn_id, arguments->NativeArgAt(2));
707+
GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(3));
708+
709+
ASSERT(imp_wrap.NumNativeFields() == 1);
710+
711+
Function& sig = Function::Handle(zone, fn_type.signature());
712+
713+
classid_t ret = AbstractType::Handle(zone, sig.result_type()).type_class_id();
714+
intptr_t num_rets = ret == kWasmVoidCid ? 0 : 1;
715+
wasmer_value_tag wasm_ret = wasmer_value_tag::WASM_I64;
716+
if (num_rets != 0) {
717+
if (!ToWasmValueTag(ret, &wasm_ret)) {
718+
Exceptions::ThrowArgumentError(String::Handle(
719+
zone, String::NewFormatted("Return type is not a valid WASM type")));
720+
UNREACHABLE();
721+
}
722+
}
723+
724+
Array& args = Array::Handle(zone, sig.parameter_types());
725+
AbstractType& arg_type = AbstractType::Handle(zone);
726+
intptr_t first_arg_index = sig.NumImplicitParameters();
727+
intptr_t num_args = args.Length() - first_arg_index;
728+
auto wasm_args =
729+
std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]);
730+
for (intptr_t i = 0; i < num_args; ++i) {
731+
arg_type ^= args.At(i + first_arg_index);
732+
classid_t dart_arg = arg_type.type_class_id();
733+
if (!ToWasmValueTag(dart_arg, &wasm_args[i])) {
734+
wasm_args.reset();
735+
Exceptions::ThrowArgumentError(String::Handle(
736+
zone, String::NewFormatted(
737+
"Type of arg %" Pd " is not a valid WASM type", i)));
738+
UNREACHABLE();
739+
}
740+
}
741+
742+
WasmImports* imports =
743+
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
744+
imports->AddFunction(ToUTF8(name), fn_id.AsInt64Value(), std::move(wasm_args),
745+
num_args, wasm_ret, num_rets);
746+
747+
return Object::null();
748+
}
749+
518750
DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
519751
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
520752
GET_NON_NULL_NATIVE_ARGUMENT(Integer, init, arguments->NativeArgAt(1));
@@ -610,15 +842,16 @@ DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
610842
String& error = String::Handle(zone);
611843

612844
{
613-
Function& sig = Function::Handle(fn_type.signature());
614-
Array& args = Array::Handle(sig.parameter_types());
845+
Function& sig = Function::Handle(zone, fn_type.signature());
846+
Array& args = Array::Handle(zone, sig.parameter_types());
847+
AbstractType& arg_type = AbstractType::Handle(zone);
615848
MallocGrowableArray<classid_t> dart_args;
616849
for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) {
617-
dart_args.Add(
618-
AbstractType::Cast(Object::Handle(args.At(i))).type_class_id());
850+
arg_type ^= args.At(i);
851+
dart_args.Add(arg_type.type_class_id());
619852
}
620853
classid_t dart_ret =
621-
AbstractType::Handle(sig.result_type()).type_class_id();
854+
AbstractType::Handle(zone, sig.result_type()).type_class_id();
622855

623856
std::unique_ptr<char[]> name_raw = ToUTF8(name);
624857
fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret, &error);
@@ -643,19 +876,21 @@ DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
643876
WasmFunction* fn = reinterpret_cast<WasmFunction*>(fn_wrap.GetNativeField(0));
644877

645878
if (args.Length() != fn->args().length()) {
646-
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
647-
"Wrong number of args. Expected %" Pu " but found %" Pd ".",
648-
fn->args().length(), args.Length())));
879+
Exceptions::ThrowArgumentError(String::Handle(
880+
zone, String::NewFormatted("Wrong number of args. Expected %" Pu
881+
" but found %" Pd ".",
882+
fn->args().length(), args.Length())));
649883
UNREACHABLE();
650884
}
651885
auto params = std::unique_ptr<wasmer_value_t[]>(
652886
new wasmer_value_t[fn->args().length()]);
887+
Number& arg_num = Number::Handle(zone);
653888
for (intptr_t i = 0; i < args.Length(); ++i) {
654-
if (!ToWasmValue(Number::Cast(Object::Handle(args.At(i))), fn->args()[i],
655-
&params[i])) {
889+
arg_num ^= args.At(i);
890+
if (!ToWasmValue(arg_num, fn->args()[i], &params[i])) {
656891
params.reset();
657892
Exceptions::ThrowArgumentError(String::Handle(
658-
String::NewFormatted("Arg %" Pd " is the wrong type.", i)));
893+
zone, String::NewFormatted("Arg %" Pd " is the wrong type.", i)));
659894
UNREACHABLE();
660895
}
661896
}
@@ -709,6 +944,11 @@ DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 5) {
709944
return nullptr;
710945
}
711946

947+
DEFINE_NATIVE_ENTRY(Wasm_addFunctionImport, 0, 4) {
948+
Exceptions::ThrowUnsupportedError("WASM is disabled");
949+
return nullptr;
950+
}
951+
712952
DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
713953
Exceptions::ThrowUnsupportedError("WASM is disabled");
714954
return nullptr;

runtime/vm/bootstrap_natives.h

+1
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ namespace dart {
396396
V(Wasm_initImports, 2) \
397397
V(Wasm_addMemoryImport, 3) \
398398
V(Wasm_addGlobalImport, 5) \
399+
V(Wasm_addFunctionImport, 4) \
399400
V(Wasm_initMemory, 3) \
400401
V(Wasm_growMemory, 2) \
401402
V(Wasm_initInstance, 3) \

0 commit comments

Comments
 (0)