Skip to content

Commit b8d5346

Browse files
authored
[wasm] pinvoke improvements part 2 (#98250)
* InlineArray is supported in wasm pinvokes, and in general works correctly in mono marshaling now (it was broken on all targets) * fixed arrays are supported in wasm pinvokes *struct arguments and return values are supported in more wasm pinvoke scenarios * wasm struct scalarization is more intelligent about noticing padding and size mismatches in structs (without this, some inlinearrays would be scalarized incorrectly) * Prevent infinite recursion in build task's TypeToChar * expand WBT coverage
1 parent 95af2bc commit b8d5346

File tree

10 files changed

+248
-41
lines changed

10 files changed

+248
-41
lines changed

src/mono/mono/metadata/class-init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2272,6 +2272,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
22722272
}
22732273

22742274
size = mono_type_size (field->type, &align);
2275+
// keep in sync with marshal.c mono_marshal_load_type_info
22752276
if (m_class_is_inlinearray (klass)) {
22762277
// Limit the max size of array instance to 1MiB
22772278
const guint32 struct_max_size = 1024 * 1024;

src/mono/mono/metadata/icall.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3028,7 +3028,7 @@ ves_icall_RuntimeType_GetNamespace (MonoQCallTypeHandle type_handle, MonoObjectH
30283028

30293029
MonoClass *klass = mono_class_from_mono_type_internal (type);
30303030
MonoClass *elem;
3031-
while (!m_class_is_enumtype (klass) &&
3031+
while (!m_class_is_enumtype (klass) &&
30323032
!mono_class_is_nullable (klass) &&
30333033
(klass != (elem = m_class_get_element_class (klass))))
30343034
klass = elem;

src/mono/mono/metadata/marshal.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3294,7 +3294,7 @@ mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t,
32943294
return mono_emit_disabled_marshal (m, argnum, t, spec, conv_arg, conv_arg_type, action);
32953295

32963296
return mono_component_marshal_ilgen()->emit_marshal_ilgen(m, argnum, t, spec, conv_arg, conv_arg_type, action, get_marshal_cb());
3297-
}
3297+
}
32983298

32993299
static void
33003300
mono_marshal_set_callconv_for_type(MonoType *type, MonoMethodSignature *csig, gboolean *skip_gc_trans /*out*/)
@@ -3577,7 +3577,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
35773577

35783578
if (G_UNLIKELY (pinvoke && mono_method_has_unmanaged_callers_only_attribute (method))) {
35793579
/*
3580-
* In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation.
3580+
* In AOT mode and embedding scenarios, it is possible that the icall is not registered in the runtime doing the AOT compilation.
35813581
* Emit a wrapper that throws a NotSupportedException.
35823582
*/
35833583
get_marshal_cb ()->mb_emit_exception (mb, "System", "NotSupportedException", "Method canot be marked with both DllImportAttribute and UnmanagedCallersOnlyAttribute");
@@ -3757,7 +3757,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
37573757
}
37583758

37593759
goto leave;
3760-
3760+
37613761
emit_exception_for_error:
37623762
mono_error_cleanup (emitted_error);
37633763
info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
@@ -5231,7 +5231,7 @@ mono_marshal_get_unsafe_accessor_wrapper (MonoMethod *accessor_method, MonoUnsaf
52315231

52325232
if (member_name == NULL && kind != MONO_UNSAFE_ACCESSOR_CTOR)
52335233
member_name = accessor_method->name;
5234-
5234+
52355235
/*
52365236
* Check cache
52375237
*/
@@ -5827,11 +5827,20 @@ mono_marshal_load_type_info (MonoClass* klass)
58275827
continue;
58285828
}
58295829

5830+
size = mono_marshal_type_size (field->type, info->fields [j].mspec,
5831+
&align, TRUE, m_class_is_unicode (klass));
5832+
5833+
// Keep in sync with class-init.c mono_class_layout_fields
5834+
if (m_class_is_inlinearray (klass)) {
5835+
// Limit the max size of array instance to 1MiB
5836+
const int struct_max_size = 1024 * 1024;
5837+
size *= m_class_inlinearray_value (klass);
5838+
g_assert ((size > 0) && (size <= struct_max_size));
5839+
}
5840+
58305841
switch (layout) {
58315842
case TYPE_ATTRIBUTE_AUTO_LAYOUT:
58325843
case TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT:
5833-
size = mono_marshal_type_size (field->type, info->fields [j].mspec,
5834-
&align, TRUE, m_class_is_unicode (klass));
58355844
align = m_class_get_packing_size (klass) ? MIN (m_class_get_packing_size (klass), align): align;
58365845
min_align = MAX (align, min_align);
58375846
info->fields [j].offset = info->native_size;
@@ -5840,8 +5849,6 @@ mono_marshal_load_type_info (MonoClass* klass)
58405849
info->native_size = info->fields [j].offset + size;
58415850
break;
58425851
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT:
5843-
size = mono_marshal_type_size (field->type, info->fields [j].mspec,
5844-
&align, TRUE, m_class_is_unicode (klass));
58455852
min_align = MAX (align, min_align);
58465853
info->fields [j].offset = m_field_get_offset (field) - MONO_ABI_SIZEOF (MonoObject);
58475854
info->native_size = MAX (info->native_size, info->fields [j].offset + size);

src/mono/mono/mini/aot-runtime-wasm.c

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
#ifdef HOST_WASM
1616

1717
static char
18-
type_to_c (MonoType *t)
18+
type_to_c (MonoType *t, gboolean *is_byref_return)
1919
{
20+
g_assert (t);
21+
22+
if (is_byref_return)
23+
*is_byref_return = 0;
2024
if (m_type_is_byref (t))
2125
return 'I';
2226

@@ -48,7 +52,7 @@ type_to_c (MonoType *t)
4852
return 'L';
4953
case MONO_TYPE_VOID:
5054
return 'V';
51-
case MONO_TYPE_VALUETYPE:
55+
case MONO_TYPE_VALUETYPE: {
5256
if (m_class_is_enumtype (t->data.klass)) {
5357
t = mono_class_enum_basetype_internal (t->data.klass);
5458
goto handle_enum;
@@ -60,13 +64,27 @@ type_to_c (MonoType *t)
6064
// FIXME: Handle the scenario where there are fields of struct types that contain no members
6165
MonoType *scalar_vtype;
6266
if (mini_wasm_is_scalar_vtype (t, &scalar_vtype))
63-
return type_to_c (scalar_vtype);
67+
return type_to_c (scalar_vtype, NULL);
68+
69+
if (is_byref_return)
70+
*is_byref_return = 1;
6471

6572
return 'I';
66-
case MONO_TYPE_GENERICINST:
67-
if (m_class_is_valuetype (t->data.klass))
73+
}
74+
case MONO_TYPE_GENERICINST: {
75+
if (m_class_is_valuetype (t->data.klass)) {
76+
MonoType *scalar_vtype;
77+
if (mini_wasm_is_scalar_vtype (t, &scalar_vtype))
78+
return type_to_c (scalar_vtype, NULL);
79+
80+
if (is_byref_return)
81+
*is_byref_return = 1;
82+
6883
return 'S';
84+
}
85+
6986
return 'I';
87+
}
7088
default:
7189
g_warning ("CANT TRANSLATE %s", mono_type_full_name (t));
7290
return 'X';
@@ -140,18 +158,29 @@ gpointer
140158
mono_wasm_get_interp_to_native_trampoline (MonoMethodSignature *sig)
141159
{
142160
char cookie [32];
143-
int c_count;
161+
int c_count, offset = 1;
162+
gboolean is_byref_return = 0;
163+
164+
memset (cookie, 0, 32);
165+
cookie [0] = type_to_c (sig->ret, &is_byref_return);
144166

145-
c_count = sig->param_count + sig->hasthis + 1;
167+
c_count = sig->param_count + sig->hasthis + is_byref_return + 1;
146168
g_assert (c_count < sizeof (cookie)); //ensure we don't overflow the local
147169

148-
cookie [0] = type_to_c (sig->ret);
149-
if (sig->hasthis)
150-
cookie [1] = 'I';
170+
if (is_byref_return) {
171+
cookie[0] = 'V';
172+
// return value address goes in arg0
173+
cookie[1] = 'I';
174+
offset += 1;
175+
}
176+
if (sig->hasthis) {
177+
// thisptr goes in arg0/arg1 depending on return type
178+
cookie [offset] = 'I';
179+
offset += 1;
180+
}
151181
for (int i = 0; i < sig->param_count; ++i) {
152-
cookie [1 + sig->hasthis + i] = type_to_c (sig->params [i]);
182+
cookie [offset + i] = type_to_c (sig->params [i], NULL);
153183
}
154-
cookie [c_count] = 0;
155184

156185
void *p = mono_wasm_interp_to_native_callback (cookie);
157186
if (!p)

src/mono/mono/mini/interp/interp.c

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,10 @@ typedef enum {
13401340
PINVOKE_ARG_R8 = 3,
13411341
PINVOKE_ARG_R4 = 4,
13421342
PINVOKE_ARG_VTYPE = 5,
1343-
PINVOKE_ARG_SCALAR_VTYPE = 6
1343+
PINVOKE_ARG_SCALAR_VTYPE = 6,
1344+
// This isn't ifdefed so it's easier to write code that handles it without sprinkling
1345+
// 800 ifdefs in this file
1346+
PINVOKE_ARG_WASM_VALUETYPE_RESULT = 7,
13441347
} PInvokeArgType;
13451348

13461349
typedef struct {
@@ -1436,6 +1439,7 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur
14361439
ilen++;
14371440
break;
14381441
case MONO_TYPE_GENERICINST: {
1442+
// FIXME: Should mini_wasm_is_scalar_vtype stuff go in here?
14391443
MonoClass *container_class = type->data.generic_class->container_class;
14401444
type = m_class_get_byval_arg (container_class);
14411445
goto retry;
@@ -1473,11 +1477,32 @@ get_build_args_from_sig_info (MonoMemoryManager *mem_manager, MonoMethodSignatur
14731477
case MONO_TYPE_CLASS:
14741478
case MONO_TYPE_OBJECT:
14751479
case MONO_TYPE_STRING:
1480+
info->ret_pinvoke_type = PINVOKE_ARG_INT;
1481+
break;
1482+
#if SIZEOF_VOID_P == 8
1483+
case MONO_TYPE_I8:
1484+
case MONO_TYPE_U8:
1485+
#endif
1486+
info->ret_pinvoke_type = PINVOKE_ARG_INT;
1487+
break;
1488+
#if SIZEOF_VOID_P == 4
14761489
case MONO_TYPE_I8:
14771490
case MONO_TYPE_U8:
1491+
info->ret_pinvoke_type = PINVOKE_ARG_INT;
1492+
break;
1493+
#endif
14781494
case MONO_TYPE_VALUETYPE:
14791495
case MONO_TYPE_GENERICINST:
14801496
info->ret_pinvoke_type = PINVOKE_ARG_INT;
1497+
#ifdef HOST_WASM
1498+
// This ISSTRUCT check is important, because the type could be an enum
1499+
if (MONO_TYPE_ISSTRUCT (info->ret_mono_type)) {
1500+
// The return type was already filtered previously, so if we get here
1501+
// we're returning a struct byref instead of as a scalar
1502+
info->ret_pinvoke_type = PINVOKE_ARG_WASM_VALUETYPE_RESULT;
1503+
info->ilen++;
1504+
}
1505+
#endif
14811506
break;
14821507
case MONO_TYPE_R4:
14831508
case MONO_TYPE_R8:
@@ -1503,6 +1528,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
15031528
margs->ilen = info->ilen;
15041529
margs->flen = info->flen;
15051530

1531+
size_t int_i = 0;
1532+
size_t int_f = 0;
1533+
1534+
if (info->ret_pinvoke_type == PINVOKE_ARG_WASM_VALUETYPE_RESULT) {
1535+
// Allocate an empty arg0 for the address of the return value
1536+
// info->ilen was already increased earlier
1537+
int_i++;
1538+
}
1539+
15061540
if (margs->ilen > 0) {
15071541
if (margs->ilen <= 8)
15081542
margs->iargs = margs->iargs_buf;
@@ -1517,9 +1551,6 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
15171551
margs->fargs = g_malloc0 (sizeof (double) * margs->flen);
15181552
}
15191553

1520-
size_t int_i = 0;
1521-
size_t int_f = 0;
1522-
15231554
for (int i = 0; i < sig->param_count; i++) {
15241555
guint32 offset = get_arg_offset (frame->imethod, sig, i);
15251556
stackval *sp_arg = STACK_ADD_BYTES (frame->stack, offset);
@@ -1578,6 +1609,15 @@ build_args_from_sig (InterpMethodArguments *margs, MonoMethodSignature *sig, Bui
15781609
}
15791610

15801611
switch (info->ret_pinvoke_type) {
1612+
case PINVOKE_ARG_WASM_VALUETYPE_RESULT:
1613+
// We pass the return value address in arg0 so fill it in, we already
1614+
// reserved space for it earlier.
1615+
g_assert (frame->retval);
1616+
margs->iargs[0] = (gpointer*)frame->retval;
1617+
// The return type is void so retval should be NULL
1618+
margs->retval = NULL;
1619+
margs->is_float_ret = 0;
1620+
break;
15811621
case PINVOKE_ARG_INT:
15821622
margs->retval = (gpointer*)frame->retval;
15831623
margs->is_float_ret = 0;
@@ -1795,8 +1835,10 @@ ves_pinvoke_method (
17951835
g_free (ccontext.stack);
17961836
#else
17971837
// Only the vt address has been returned, we need to copy the entire content on interp stack
1798-
if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type))
1799-
stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled);
1838+
if (!context->has_resume_state && MONO_TYPE_ISSTRUCT (call_info->ret_mono_type)) {
1839+
if (call_info->ret_pinvoke_type != PINVOKE_ARG_WASM_VALUETYPE_RESULT)
1840+
stackval_from_data (call_info->ret_mono_type, frame.retval, (char*)frame.retval->data.p, sig->pinvoke && !sig->marshalling_disabled);
1841+
}
18001842

18011843
if (margs.iargs != margs.iargs_buf)
18021844
g_free (margs.iargs);

src/mono/mono/mini/mini-wasm.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,23 @@ get_storage (MonoType *type, MonoType **etype, gboolean is_return)
7575
case MONO_TYPE_R8:
7676
return ArgOnStack;
7777

78-
case MONO_TYPE_GENERICINST:
78+
case MONO_TYPE_GENERICINST: {
7979
if (!mono_type_generic_inst_is_valuetype (type))
8080
return ArgOnStack;
8181

8282
if (mini_is_gsharedvt_variable_type (type))
8383
return ArgGsharedVTOnStack;
84-
/* fall through */
84+
85+
if (mini_wasm_is_scalar_vtype (type, etype))
86+
return ArgVtypeAsScalar;
87+
88+
return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
89+
}
8590
case MONO_TYPE_VALUETYPE:
8691
case MONO_TYPE_TYPEDBYREF: {
8792
if (mini_wasm_is_scalar_vtype (type, etype))
8893
return ArgVtypeAsScalar;
94+
8995
return is_return ? ArgValuetypeAddrInIReg : ArgValuetypeAddrOnStack;
9096
}
9197
case MONO_TYPE_VAR:
@@ -771,7 +777,12 @@ mini_wasm_is_scalar_vtype (MonoType *type, MonoType **etype)
771777
if (nfields > 1)
772778
return FALSE;
773779
MonoType *t = mini_get_underlying_type (field->type);
774-
if (MONO_TYPE_ISSTRUCT (t)) {
780+
int align, field_size = mono_type_size (t, &align);
781+
// inlinearray and fixed both work by having a single field that is bigger than its element type.
782+
// we also don't want to scalarize a struct that has padding in its metadata, even if it would fit.
783+
if (field_size != size) {
784+
return FALSE;
785+
} else if (MONO_TYPE_ISSTRUCT (t)) {
775786
if (!mini_wasm_is_scalar_vtype (t, etype))
776787
return FALSE;
777788
} else if (!((MONO_TYPE_IS_PRIMITIVE (t) || MONO_TYPE_IS_REFERENCE (t) || MONO_TYPE_IS_POINTER (t)))) {

0 commit comments

Comments
 (0)