Skip to content

Commit b1b3e85

Browse files
authored
[mono][swift-interop] Support for Swift frozen struct lowering in mini-JIT/AOT/interpreter (#102143)
* refactor + bug fixes for swift struct marshalling * mini struct lowering * interpreter swift struct lowering * enable SwiftAbiStress on Mono
1 parent 794b0c2 commit b1b3e85

File tree

7 files changed

+252
-36
lines changed

7 files changed

+252
-36
lines changed

src/mono/mono/metadata/marshal.c

+41-31
Original file line numberDiff line numberDiff line change
@@ -3711,7 +3711,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
37113711
swift_error_args++;
37123712
} else if (param_klass == swift_self) {
37133713
swift_self_args++;
3714-
} else if (!m_class_is_blittable (param_klass) || m_class_is_simd_type (param_klass)) {
3714+
} else if (!type_is_blittable (method->signature->params [i]) || m_class_is_simd_type (param_klass)) {
37153715
swift_error_args = swift_self_args = 0;
37163716
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-blittable types to a P/Invoke with the Swift calling convention is unsupported.");
37173717
break;
@@ -6693,7 +6693,8 @@ typedef enum {
66936693
SWIFT_DOUBLE,
66946694
} SwiftPhysicalLoweringKind;
66956695

6696-
static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind) {
6696+
static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind)
6697+
{
66976698
switch (kind) {
66986699
case SWIFT_INT64:
66996700
case SWIFT_DOUBLE:
@@ -6705,7 +6706,8 @@ static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind) {
67056706
}
67066707
}
67076708

6708-
static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 size, SwiftPhysicalLoweringKind kind) {
6709+
static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 size, SwiftPhysicalLoweringKind kind)
6710+
{
67096711
bool force_opaque = false;
67106712

67116713
if (offset != ALIGN_TO(offset, get_swift_lowering_alignment(kind))) {
@@ -6736,7 +6738,8 @@ static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 si
67366738

67376739
static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoType* type, guint32 offset);
67386740

6739-
static void record_inlinearray_struct_physical_lowering (guint8* lowered_bytes, MonoClass* klass, guint32 offset) {
6741+
static void record_inlinearray_struct_physical_lowering (guint8* lowered_bytes, MonoClass* klass, guint32 offset)
6742+
{
67406743
// Get the first field and record its physical lowering N times
67416744
MonoClassField* field = mono_class_get_fields_internal (klass, NULL);
67426745
MonoType* fieldType = field->type;
@@ -6755,17 +6758,21 @@ static void record_struct_physical_lowering (guint8* lowered_bytes, MonoClass* k
67556758
// For each field, we need to record the physical lowering of it.
67566759
gpointer iter = NULL;
67576760
MonoClassField* field;
6761+
int type_offset = MONO_ABI_SIZEOF (MonoObject);
67586762
while ((field = mono_class_get_fields_internal (klass, &iter))) {
67596763
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
67606764
continue;
67616765
if (mono_field_is_deleted (field))
67626766
continue;
67636767

6764-
record_struct_field_physical_lowering(lowered_bytes, field->type, offset + m_field_get_offset(field));
6768+
record_struct_field_physical_lowering(lowered_bytes, field->type, (offset + m_field_get_offset(field)) - type_offset);
67656769
}
67666770
}
67676771

6768-
static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoType* type, guint32 offset) {
6772+
static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoType* type, guint32 offset)
6773+
{
6774+
int align;
6775+
67696776
// Normalize pointer types to IntPtr and resolve generic classes.
67706777
// We don't need to care about specific pointer types at this ABI level.
67716778
if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) {
@@ -6793,7 +6800,7 @@ static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoTy
67936800
kind = SWIFT_DOUBLE;
67946801
}
67956802

6796-
set_lowering_range(lowered_bytes, offset, mono_type_size(type, NULL), kind);
6803+
set_lowering_range(lowered_bytes, offset, mono_type_size(type, &align), kind);
67976804
}
67986805
}
67996806

@@ -6820,13 +6827,13 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
68206827
}
68216828

68226829
MonoClass *klass = mono_class_from_mono_type_internal (type);
6823-
6830+
int vtype_size = mono_class_value_size (klass, NULL);
68246831
// TODO: We currently don't support vector types, so we can say that the maximum size of a non-by_reference struct
68256832
// is 4 * PointerSize.
68266833
// Strictly, this is inaccurate in the case where a struct has a fully-empty 8 bytes of padding using explicit layout,
68276834
// but that's not possible in the Swift layout algorithm.
68286835

6829-
if (m_class_get_instance_size(klass) > 4 * TARGET_SIZEOF_VOID_P) {
6836+
if (vtype_size > 4 * TARGET_SIZEOF_VOID_P) {
68306837
lowering.by_reference = TRUE;
68316838
return lowering;
68326839
}
@@ -6845,8 +6852,7 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
68456852
GArray* intervals = g_array_new(FALSE, TRUE, sizeof(struct _SwiftInterval));
68466853

68476854
// Now we'll build the intervals from the lowered_bytes array
6848-
int instance_size = m_class_get_instance_size(klass);
6849-
for (int i = 0; i < instance_size; ++i) {
6855+
for (int i = 0; i < vtype_size; ++i) {
68506856
// Don't create an interval for empty bytes
68516857
if (lowered_bytes[i] == SWIFT_EMPTY) {
68526858
continue;
@@ -6874,19 +6880,23 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
68746880
}
68756881

68766882
// Merge opaque intervals that are in the same pointer-sized block
6877-
for (int i = 0; i < intervals->len - 1; ++i) {
6878-
struct _SwiftInterval current = g_array_index(intervals, struct _SwiftInterval, i);
6879-
struct _SwiftInterval next = g_array_index(intervals, struct _SwiftInterval, i + 1);
6880-
6881-
if (current.kind == SWIFT_OPAQUE && next.kind == SWIFT_OPAQUE && current.start / TARGET_SIZEOF_VOID_P == next.start / TARGET_SIZEOF_VOID_P) {
6882-
current.size = next.start + next.size - current.start;
6883-
g_array_remove_index(intervals, i + 1);
6884-
i--;
6883+
for (int i = 0; i < intervals->len; ++i) {
6884+
struct _SwiftInterval interval = g_array_index(intervals, struct _SwiftInterval, i);
6885+
6886+
if (i != 0 && interval.kind == SWIFT_OPAQUE) {
6887+
// Merge two opaque intervals when the previous interval ends in the same pointer-sized block
6888+
struct _SwiftInterval prevInterval = g_array_index(intervals, struct _SwiftInterval, i - 1);
6889+
if (prevInterval.kind == SWIFT_OPAQUE && (prevInterval.start + prevInterval.size) / TARGET_SIZEOF_VOID_P == interval.start / TARGET_SIZEOF_VOID_P) {
6890+
(g_array_index(intervals, struct _SwiftInterval, i - 1)).size = interval.start + interval.size - prevInterval.start;
6891+
g_array_remove_index(intervals, i);
6892+
--i;
6893+
continue;
6894+
}
68856895
}
68866896
}
68876897

68886898
// Now that we have the intervals, we can calculate the lowering
6889-
MonoTypeEnum lowered_types[4];
6899+
MonoType *lowered_types[4];
68906900
guint32 offsets[4];
68916901
guint32 num_lowered_types = 0;
68926902

@@ -6904,13 +6914,13 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
69046914

69056915
switch (interval.kind) {
69066916
case SWIFT_INT64:
6907-
lowered_types[num_lowered_types++] = MONO_TYPE_I8;
6917+
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.int64_class);
69086918
break;
69096919
case SWIFT_FLOAT:
6910-
lowered_types[num_lowered_types++] = MONO_TYPE_R4;
6920+
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.single_class);
69116921
break;
69126922
case SWIFT_DOUBLE:
6913-
lowered_types[num_lowered_types++] = MONO_TYPE_R8;
6923+
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.double_class);
69146924
break;
69156925
case SWIFT_OPAQUE:
69166926
{
@@ -6943,20 +6953,20 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
69436953

69446954
offsets[num_lowered_types] = opaque_interval_start;
69456955

6946-
if (remaining_interval_size > 8 && (opaque_interval_start % 8 == 0)) {
6947-
lowered_types[num_lowered_types] = MONO_TYPE_I8;
6956+
if (remaining_interval_size > 4 && (opaque_interval_start % 8 == 0)) {
6957+
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int64_class);
69486958
remaining_interval_size -= 8;
69496959
opaque_interval_start += 8;
6950-
} else if (remaining_interval_size > 4 && (opaque_interval_start % 4 == 0)) {
6951-
lowered_types[num_lowered_types] = MONO_TYPE_I4;
6960+
} else if (remaining_interval_size > 2 && (opaque_interval_start % 4 == 0)) {
6961+
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int32_class);
69526962
remaining_interval_size -= 4;
69536963
opaque_interval_start += 4;
6954-
} else if (remaining_interval_size > 2 && (opaque_interval_start % 2 == 0)) {
6955-
lowered_types[num_lowered_types] = MONO_TYPE_I2;
6964+
} else if (remaining_interval_size > 1 && (opaque_interval_start % 2 == 0)) {
6965+
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int16_class);
69566966
remaining_interval_size -= 2;
69576967
opaque_interval_start += 2;
69586968
} else {
6959-
lowered_types[num_lowered_types] = MONO_TYPE_U1;
6969+
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.byte_class);
69606970
remaining_interval_size -= 1;
69616971
opaque_interval_start += 1;
69626972
}
@@ -6967,7 +6977,7 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
69676977
}
69686978
}
69696979

6970-
memcpy(lowering.lowered_elements, lowered_types, num_lowered_types * sizeof(MonoTypeEnum));
6980+
memcpy(lowering.lowered_elements, lowered_types, num_lowered_types * sizeof(MonoType*));
69716981
memcpy(lowering.offsets, offsets, num_lowered_types * sizeof(guint32));
69726982
lowering.num_lowered_elements = num_lowered_types;
69736983
lowering.by_reference = FALSE;

src/mono/mono/metadata/marshal.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,8 @@ GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_error)
745745

746746
typedef struct {
747747
gboolean by_reference;
748-
int num_lowered_elements;
749-
MonoTypeEnum lowered_elements[4];
748+
uint32_t num_lowered_elements;
749+
MonoType *lowered_elements[4];
750750
uint32_t offsets[4];
751751
} SwiftPhysicalLowering;
752752

src/mono/mono/metadata/metadata-internals.h

+1
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,7 @@ MonoMethodSignature *mono_metadata_signature_dup_mempool (MonoMemPool *mp, Mono
10101010
MonoMethodSignature *mono_metadata_signature_dup_mem_manager (MonoMemoryManager *mem_manager, MonoMethodSignature *sig);
10111011
MonoMethodSignature *mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass);
10121012
MonoMethodSignature *mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig);
1013+
MonoMethodSignature *mono_metadata_signature_dup_new_params (MonoMemPool *mp, MonoMethodSignature *sig, uint32_t num_params, MonoType **new_params);
10131014

10141015
MonoGenericInst *
10151016
mono_get_shared_generic_inst (MonoGenericContainer *container);

src/mono/mono/metadata/metadata.c

+31
Original file line numberDiff line numberDiff line change
@@ -2546,6 +2546,37 @@ mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig)
25462546
return res;
25472547
}
25482548

2549+
/**
2550+
* mono_metadata_signature_dup_new_params:
2551+
* @param mp The memory pool to allocate the duplicated signature from.
2552+
* @param sig The original method signature.
2553+
* @param num_params The number parameters in the new signature.
2554+
* @param new_params An array of MonoType pointers representing the new parameters.
2555+
*
2556+
* Duplicate an existing \c MonoMethodSignature but with a new set of parameters.
2557+
* This is a Mono runtime internal function.
2558+
*
2559+
* @return the new \c MonoMethodSignature structure.
2560+
*/
2561+
MonoMethodSignature*
2562+
mono_metadata_signature_dup_new_params (MonoMemPool *mp, MonoMethodSignature *sig, uint32_t num_params, MonoType **new_params)
2563+
{
2564+
size_t new_sig_size = MONO_SIZEOF_METHOD_SIGNATURE + num_params * sizeof (MonoType*);
2565+
if (sig->ret)
2566+
new_sig_size += mono_sizeof_type (sig->ret);
2567+
2568+
MonoMethodSignature *res = (MonoMethodSignature *)mono_mempool_alloc0 (mp, (unsigned int)new_sig_size);
2569+
memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
2570+
res->param_count = GUINT32_TO_UINT16 (num_params);
2571+
2572+
for (uint16_t i = 0; i < res->param_count; i++) {
2573+
res->params [i] = new_params [i];
2574+
}
2575+
res->ret = sig->ret;
2576+
2577+
return res;
2578+
}
2579+
25492580
/*
25502581
* mono_metadata_signature_size:
25512582
*

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

+99
Original file line numberDiff line numberDiff line change
@@ -3320,6 +3320,95 @@ interp_try_devirt (MonoClass *this_klass, MonoMethod *target_method)
33203320
return NULL;
33213321
}
33223322

3323+
static MonoMethodSignature*
3324+
interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *csignature)
3325+
{
3326+
// P/Invoke calls shouldn't contain 'this'
3327+
g_assert (!csignature->hasthis);
3328+
3329+
/*
3330+
* Argument reordering here doesn't handle on the fly offset allocation
3331+
* and requires the full var offset allocator pass that is only ran for optimized code
3332+
*/
3333+
g_assert (td->optimized);
3334+
3335+
MonoMethodSignature *new_csignature;
3336+
// Save the function pointer
3337+
StackInfo sp_fp = td->sp [-1];
3338+
--td->sp;
3339+
3340+
// Save the old arguments
3341+
td->sp -= csignature->param_count;
3342+
StackInfo *sp_old_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count);
3343+
for (int i = 0; i < csignature->param_count; ++i)
3344+
sp_old_params [i] = td->sp [i];
3345+
3346+
GArray *new_params = g_array_sized_new (FALSE, FALSE, sizeof (MonoType*), csignature->param_count);
3347+
uint32_t new_param_count = 0;
3348+
int align;
3349+
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
3350+
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
3351+
/*
3352+
* Go through the lowered arguments, if the argument is a struct,
3353+
* we need to replace it with a sequence of lowered arguments.
3354+
* Also record the updated parameters for the new signature.
3355+
*/
3356+
for (int idx_param = 0; idx_param < csignature->param_count; ++idx_param) {
3357+
MonoType *ptype = csignature->params [idx_param];
3358+
MonoClass *klass = mono_class_from_mono_type_internal (ptype);
3359+
// SwiftSelf and SwiftError are special cases where we need to preserve the class information for the codegen to handle them correctly.
3360+
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error)) {
3361+
SwiftPhysicalLowering lowered_swift_struct = mono_marshal_get_swift_physical_lowering (ptype, FALSE);
3362+
if (!lowered_swift_struct.by_reference) {
3363+
for (uint32_t idx_lowered = 0; idx_lowered < lowered_swift_struct.num_lowered_elements; ++idx_lowered) {
3364+
int mt_lowered = mono_mint_type (lowered_swift_struct.lowered_elements [idx_lowered]);
3365+
int lowered_elem_size = mono_type_size (lowered_swift_struct.lowered_elements [idx_lowered], &align);
3366+
// Load the lowered elements of the struct
3367+
interp_add_ins (td, MINT_MOV_SRC_OFF);
3368+
interp_ins_set_sreg (td->last_ins, sp_old_params [idx_param].var);
3369+
td->last_ins->data [0] = (guint16) lowered_swift_struct.offsets [idx_lowered];
3370+
td->last_ins->data [1] = GINT_TO_UINT16 (mt_lowered);
3371+
td->last_ins->data [2] = GINT_TO_UINT16 (lowered_elem_size);
3372+
push_mono_type (td, lowered_swift_struct.lowered_elements [idx_lowered], mt_lowered, mono_class_from_mono_type_internal (lowered_swift_struct.lowered_elements [idx_lowered]));
3373+
interp_ins_set_dreg (td->last_ins, td->sp [-1].var);
3374+
3375+
++new_param_count;
3376+
g_array_append_val (new_params, lowered_swift_struct.lowered_elements [idx_lowered]);
3377+
}
3378+
} else {
3379+
// For structs that cannot be lowered, we change the argument to byref type
3380+
ptype = mono_class_get_byref_type (mono_defaults.typed_reference_class);
3381+
// Load the address of the struct
3382+
interp_add_ins (td, MINT_LDLOCA_S);
3383+
interp_ins_set_sreg (td->last_ins, sp_old_params [idx_param].var);
3384+
push_simple_type (td, STACK_TYPE_I);
3385+
interp_ins_set_dreg (td->last_ins, td->sp [-1].var);
3386+
3387+
++new_param_count;
3388+
g_array_append_val (new_params, ptype);
3389+
}
3390+
} else {
3391+
// Copy over non-struct arguments
3392+
memcpy (td->sp, &sp_old_params [idx_param], sizeof (StackInfo));
3393+
++td->sp;
3394+
3395+
++new_param_count;
3396+
g_array_append_val (new_params, ptype);
3397+
}
3398+
}
3399+
// Restore the function pointer
3400+
memcpy (td->sp, &sp_fp, sizeof (StackInfo));
3401+
++td->sp;
3402+
3403+
// Create a new dummy signature with the lowered arguments
3404+
new_csignature = mono_metadata_signature_dup_new_params (td->mempool, csignature, new_param_count, (MonoType**)new_params->data);
3405+
3406+
// Deallocate temp array
3407+
g_array_free (new_params, TRUE);
3408+
3409+
return new_csignature;
3410+
}
3411+
33233412
/* Return FALSE if error, including inline failure */
33243413
static gboolean
33253414
interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target_method, MonoGenericContext *generic_context, MonoClass *constrained_class, gboolean readonly, MonoError *error, gboolean check_visibility, gboolean save_last_error, gboolean tailcall)
@@ -3404,6 +3493,16 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
34043493
return FALSE;
34053494
}
34063495

3496+
#ifdef MONO_ARCH_HAVE_SWIFTCALL
3497+
/*
3498+
* We need to modify the signature of the swiftcall calli to account for the lowering of Swift structs.
3499+
* This is done by replacing struct arguments on stack with a lowered sequence and updating the signature.
3500+
*/
3501+
if (csignature->pinvoke && mono_method_signature_has_ext_callconv (csignature, MONO_EXT_CALLCONV_SWIFTCALL)) {
3502+
csignature = interp_emit_swiftcall_struct_lowering (td, csignature);
3503+
}
3504+
#endif
3505+
34073506
if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
34083507
interp_generate_mae_throw (td, method, target_method);
34093508

0 commit comments

Comments
 (0)