Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono][swift-interop] Support for Swift frozen struct lowering in mini-JIT/AOT/interpreter #102143

Merged
Merged
72 changes: 41 additions & 31 deletions src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -3711,7 +3711,7 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
swift_error_args++;
} else if (param_klass == swift_self) {
swift_self_args++;
} else if (!m_class_is_blittable (param_klass) || m_class_is_simd_type (param_klass)) {
lambdageek marked this conversation as resolved.
Show resolved Hide resolved
} else if (!type_is_blittable (method->signature->params [i]) || m_class_is_simd_type (param_klass)) {
swift_error_args = swift_self_args = 0;
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-blittable types to a P/Invoke with the Swift calling convention is unsupported.");
break;
Expand Down Expand Up @@ -6603,7 +6603,8 @@ typedef enum {
SWIFT_DOUBLE,
} SwiftPhysicalLoweringKind;

static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind) {
static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind)
{
switch (kind) {
case SWIFT_INT64:
case SWIFT_DOUBLE:
Expand All @@ -6615,7 +6616,8 @@ static int get_swift_lowering_alignment (SwiftPhysicalLoweringKind kind) {
}
}

static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 size, SwiftPhysicalLoweringKind kind) {
static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 size, SwiftPhysicalLoweringKind kind)
{
bool force_opaque = false;

if (offset != ALIGN_TO(offset, get_swift_lowering_alignment(kind))) {
Expand Down Expand Up @@ -6646,7 +6648,8 @@ static void set_lowering_range(guint8* lowered_bytes, guint32 offset, guint32 si

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

static void record_inlinearray_struct_physical_lowering (guint8* lowered_bytes, MonoClass* klass, guint32 offset) {
static void record_inlinearray_struct_physical_lowering (guint8* lowered_bytes, MonoClass* klass, guint32 offset)
{
// Get the first field and record its physical lowering N times
MonoClassField* field = mono_class_get_fields_internal (klass, NULL);
MonoType* fieldType = field->type;
Expand All @@ -6665,17 +6668,21 @@ static void record_struct_physical_lowering (guint8* lowered_bytes, MonoClass* k
// For each field, we need to record the physical lowering of it.
gpointer iter = NULL;
MonoClassField* field;
int type_offset = MONO_ABI_SIZEOF (MonoObject);
while ((field = mono_class_get_fields_internal (klass, &iter))) {
if (field->type->attrs & FIELD_ATTRIBUTE_STATIC)
continue;
if (mono_field_is_deleted (field))
continue;

record_struct_field_physical_lowering(lowered_bytes, field->type, offset + m_field_get_offset(field));
record_struct_field_physical_lowering(lowered_bytes, field->type, (offset + m_field_get_offset(field)) - type_offset);
}
}

static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoType* type, guint32 offset) {
static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoType* type, guint32 offset)
{
int align;

// Normalize pointer types to IntPtr and resolve generic classes.
// We don't need to care about specific pointer types at this ABI level.
if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) {
Expand Down Expand Up @@ -6703,7 +6710,7 @@ static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoTy
kind = SWIFT_DOUBLE;
}

set_lowering_range(lowered_bytes, offset, mono_type_size(type, NULL), kind);
set_lowering_range(lowered_bytes, offset, mono_type_size(type, &align), kind);
}
}

Expand All @@ -6730,13 +6737,13 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
}

MonoClass *klass = mono_class_from_mono_type_internal (type);

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

if (m_class_get_instance_size(klass) > 4 * TARGET_SIZEOF_VOID_P) {
if (vtype_size > 4 * TARGET_SIZEOF_VOID_P) {
lowering.by_reference = TRUE;
return lowering;
}
Expand All @@ -6755,8 +6762,7 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
GArray* intervals = g_array_new(FALSE, TRUE, sizeof(struct _SwiftInterval));

// Now we'll build the intervals from the lowered_bytes array
int instance_size = m_class_get_instance_size(klass);
for (int i = 0; i < instance_size; ++i) {
for (int i = 0; i < vtype_size; ++i) {
// Don't create an interval for empty bytes
if (lowered_bytes[i] == SWIFT_EMPTY) {
continue;
Expand Down Expand Up @@ -6784,19 +6790,23 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
}

// Merge opaque intervals that are in the same pointer-sized block
for (int i = 0; i < intervals->len - 1; ++i) {
struct _SwiftInterval current = g_array_index(intervals, struct _SwiftInterval, i);
struct _SwiftInterval next = g_array_index(intervals, struct _SwiftInterval, i + 1);

if (current.kind == SWIFT_OPAQUE && next.kind == SWIFT_OPAQUE && current.start / TARGET_SIZEOF_VOID_P == next.start / TARGET_SIZEOF_VOID_P) {
current.size = next.start + next.size - current.start;
g_array_remove_index(intervals, i + 1);
i--;
for (int i = 0; i < intervals->len; ++i) {
struct _SwiftInterval interval = g_array_index(intervals, struct _SwiftInterval, i);

if (i != 0 && interval.kind == SWIFT_OPAQUE) {
// Merge two opaque intervals when the previous interval ends in the same pointer-sized block
struct _SwiftInterval prevInterval = g_array_index(intervals, struct _SwiftInterval, i - 1);
if (prevInterval.kind == SWIFT_OPAQUE && (prevInterval.start + prevInterval.size) / TARGET_SIZEOF_VOID_P == interval.start / TARGET_SIZEOF_VOID_P) {
(g_array_index(intervals, struct _SwiftInterval, i - 1)).size = interval.start + interval.size - prevInterval.start;
g_array_remove_index(intervals, i);
--i;
continue;
}
}
}

// Now that we have the intervals, we can calculate the lowering
MonoTypeEnum lowered_types[4];
MonoType *lowered_types[4];
guint32 offsets[4];
guint32 num_lowered_types = 0;

Expand All @@ -6814,13 +6824,13 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout

switch (interval.kind) {
case SWIFT_INT64:
lowered_types[num_lowered_types++] = MONO_TYPE_I8;
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.int64_class);
break;
case SWIFT_FLOAT:
lowered_types[num_lowered_types++] = MONO_TYPE_R4;
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.single_class);
break;
case SWIFT_DOUBLE:
lowered_types[num_lowered_types++] = MONO_TYPE_R8;
lowered_types[num_lowered_types++] = m_class_get_byval_arg (mono_defaults.double_class);
break;
case SWIFT_OPAQUE:
{
Expand Down Expand Up @@ -6853,20 +6863,20 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout

offsets[num_lowered_types] = opaque_interval_start;

if (remaining_interval_size > 8 && (opaque_interval_start % 8 == 0)) {
lowered_types[num_lowered_types] = MONO_TYPE_I8;
if (remaining_interval_size > 4 && (opaque_interval_start % 8 == 0)) {
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int64_class);
remaining_interval_size -= 8;
opaque_interval_start += 8;
} else if (remaining_interval_size > 4 && (opaque_interval_start % 4 == 0)) {
lowered_types[num_lowered_types] = MONO_TYPE_I4;
} else if (remaining_interval_size > 2 && (opaque_interval_start % 4 == 0)) {
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int32_class);
remaining_interval_size -= 4;
opaque_interval_start += 4;
} else if (remaining_interval_size > 2 && (opaque_interval_start % 2 == 0)) {
lowered_types[num_lowered_types] = MONO_TYPE_I2;
} else if (remaining_interval_size > 1 && (opaque_interval_start % 2 == 0)) {
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.int16_class);
remaining_interval_size -= 2;
opaque_interval_start += 2;
} else {
lowered_types[num_lowered_types] = MONO_TYPE_U1;
lowered_types[num_lowered_types] = m_class_get_byval_arg (mono_defaults.byte_class);
remaining_interval_size -= 1;
opaque_interval_start += 1;
}
Expand All @@ -6877,7 +6887,7 @@ mono_marshal_get_swift_physical_lowering (MonoType *type, gboolean native_layout
}
}

memcpy(lowering.lowered_elements, lowered_types, num_lowered_types * sizeof(MonoTypeEnum));
memcpy(lowering.lowered_elements, lowered_types, num_lowered_types * sizeof(MonoType*));
memcpy(lowering.offsets, offsets, num_lowered_types * sizeof(guint32));
lowering.num_lowered_elements = num_lowered_types;
lowering.by_reference = FALSE;
Expand Down
4 changes: 2 additions & 2 deletions src/mono/mono/metadata/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,8 @@ GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_error)

typedef struct {
gboolean by_reference;
int num_lowered_elements;
MonoTypeEnum lowered_elements[4];
uint32_t num_lowered_elements;
MonoType *lowered_elements[4];
uint32_t offsets[4];
} SwiftPhysicalLowering;

Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/metadata-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,7 @@ MonoMethodSignature *mono_metadata_signature_dup_mempool (MonoMemPool *mp, Mono
MonoMethodSignature *mono_metadata_signature_dup_mem_manager (MonoMemoryManager *mem_manager, MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_add_this (MonoImage *image, MonoMethodSignature *sig, MonoClass *klass);
MonoMethodSignature *mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig);
MonoMethodSignature *mono_metadata_signature_dup_new_params (MonoMemPool *mp, MonoMethodSignature *sig, uint32_t num_params, MonoType **new_params);

MonoGenericInst *
mono_get_shared_generic_inst (MonoGenericContainer *container);
Expand Down
31 changes: 31 additions & 0 deletions src/mono/mono/metadata/metadata.c
Original file line number Diff line number Diff line change
Expand Up @@ -2546,6 +2546,37 @@ mono_metadata_signature_dup_delegate_invoke_to_target (MonoMethodSignature *sig)
return res;
}

/**
* mono_metadata_signature_dup_new_params:
* @param mp The memory pool to allocate the duplicated signature from.
* @param sig The original method signature.
* @param num_params The number parameters in the new signature.
* @param new_params An array of MonoType pointers representing the new parameters.
*
* Duplicate an existing \c MonoMethodSignature but with a new set of parameters.
* This is a Mono runtime internal function.
*
* @return the new \c MonoMethodSignature structure.
*/
MonoMethodSignature*
mono_metadata_signature_dup_new_params (MonoMemPool *mp, MonoMethodSignature *sig, uint32_t num_params, MonoType **new_params)
{
size_t new_sig_size = MONO_SIZEOF_METHOD_SIGNATURE + num_params * sizeof (MonoType*);
if (sig->ret)
new_sig_size += mono_sizeof_type (sig->ret);

MonoMethodSignature *res = (MonoMethodSignature *)mono_mempool_alloc0 (mp, (unsigned int)new_sig_size);
memcpy (res, sig, MONO_SIZEOF_METHOD_SIGNATURE);
res->param_count = GUINT32_TO_UINT16 (num_params);

for (uint16_t i = 0; i < res->param_count; i++) {
res->params [i] = new_params [i];
}
res->ret = sig->ret;

return res;
}

/*
* mono_metadata_signature_size:
*
Expand Down
99 changes: 99 additions & 0 deletions src/mono/mono/mini/interp/transform.c
Original file line number Diff line number Diff line change
Expand Up @@ -3320,6 +3320,95 @@ interp_try_devirt (MonoClass *this_klass, MonoMethod *target_method)
return NULL;
}

static MonoMethodSignature*
interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *csignature)
{
// P/Invoke calls shouldn't contain 'this'
g_assert (!csignature->hasthis);

/*
* Argument reordering here doesn't handle on the fly offset allocation
* and requires the full var offset allocator pass that is only ran for optimized code
*/
g_assert (td->optimized);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument moving around that happens here will break the fast offset allocator (where we allocate storage for vars immediately as we push it on the td->sp stack). This appears however to be fine because we disable tiering for wrappers and I would imagine a swift call is only happening from a m2n wrapper. However I would assert here that td->optimized with the comment that argument reordering here doesn't handle on the fly offset allocation and requires the full var offset allocator pass that is only ran for optimized code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we only need to perform the struct lowering for m2n (and later eventually n2m) wrappers. I added the assert with the comment you suggested.

MonoMethodSignature *new_csignature;
// Save the function pointer
StackInfo sp_fp = td->sp [-1];
--td->sp;

// Save the old arguments
td->sp -= csignature->param_count;
StackInfo *sp_old_params = (StackInfo*) mono_mempool_alloc (td->mempool, sizeof (StackInfo) * csignature->param_count);
for (int i = 0; i < csignature->param_count; ++i)
sp_old_params [i] = td->sp [i];

GArray *new_params = g_array_sized_new (FALSE, FALSE, sizeof (MonoType*), csignature->param_count);
uint32_t new_param_count = 0;
int align;
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
/*
* Go through the lowered arguments, if the argument is a struct,
* we need to replace it with a sequence of lowered arguments.
* Also record the updated parameters for the new signature.
*/
for (int idx_param = 0; idx_param < csignature->param_count; ++idx_param) {
MonoType *ptype = csignature->params [idx_param];
MonoClass *klass = mono_class_from_mono_type_internal (ptype);
// SwiftSelf and SwiftError are special cases where we need to preserve the class information for the codegen to handle them correctly.
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error)) {
SwiftPhysicalLowering lowered_swift_struct = mono_marshal_get_swift_physical_lowering (ptype, FALSE);
if (!lowered_swift_struct.by_reference) {
for (uint32_t idx_lowered = 0; idx_lowered < lowered_swift_struct.num_lowered_elements; ++idx_lowered) {
int mt_lowered = mono_mint_type (lowered_swift_struct.lowered_elements [idx_lowered]);
int lowered_elem_size = mono_type_size (lowered_swift_struct.lowered_elements [idx_lowered], &align);
// Load the lowered elements of the struct
interp_add_ins (td, MINT_MOV_SRC_OFF);
interp_ins_set_sreg (td->last_ins, sp_old_params [idx_param].var);
td->last_ins->data [0] = (guint16) lowered_swift_struct.offsets [idx_lowered];
td->last_ins->data [1] = GINT_TO_UINT16 (mt_lowered);
td->last_ins->data [2] = GINT_TO_UINT16 (lowered_elem_size);
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]));
interp_ins_set_dreg (td->last_ins, td->sp [-1].var);

++new_param_count;
g_array_append_val (new_params, lowered_swift_struct.lowered_elements [idx_lowered]);
}
} else {
// For structs that cannot be lowered, we change the argument to byref type
ptype = mono_class_get_byref_type (mono_defaults.typed_reference_class);
// Load the address of the struct
interp_add_ins (td, MINT_LDLOCA_S);
interp_ins_set_sreg (td->last_ins, sp_old_params [idx_param].var);
push_simple_type (td, STACK_TYPE_I);
interp_ins_set_dreg (td->last_ins, td->sp [-1].var);

++new_param_count;
g_array_append_val (new_params, ptype);
}
} else {
// Copy over non-struct arguments
memcpy (td->sp, &sp_old_params [idx_param], sizeof (StackInfo));
++td->sp;

++new_param_count;
g_array_append_val (new_params, ptype);
}
}
// Restore the function pointer
memcpy (td->sp, &sp_fp, sizeof (StackInfo));
++td->sp;

// Create a new dummy signature with the lowered arguments
new_csignature = mono_metadata_signature_dup_new_params (td->mempool, csignature, new_param_count, (MonoType**)new_params->data);

// Deallocate temp array
g_array_free (new_params, TRUE);

return new_csignature;
}

/* Return FALSE if error, including inline failure */
static gboolean
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)
Expand Down Expand Up @@ -3404,6 +3493,16 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
return FALSE;
}

#ifdef MONO_ARCH_HAVE_SWIFTCALL
/*
* We need to modify the signature of the swiftcall calli to account for the lowering of Swift structs.
* This is done by replacing struct arguments on stack with a lowered sequence and updating the signature.
*/
if (csignature->pinvoke && mono_method_signature_has_ext_callconv (csignature, MONO_EXT_CALLCONV_SWIFTCALL)) {
csignature = interp_emit_swiftcall_struct_lowering (td, csignature);
}
#endif

if (check_visibility && target_method && !mono_method_can_access_method (method, target_method))
interp_generate_mae_throw (td, method, target_method);

Expand Down
Loading
Loading