Skip to content

Commit ffcdf42

Browse files
[mono] Add support for SwiftIndirectResult in Mono JIT and Interpreter (#104111)
* Add support for SwiftIndirectResult at P/Invoke boundary * Update struct lowering * Introduce arm64 support * Introduce amd64 support * Enable runtime tests * Free RAX if used for the call * Update src/mono/mono/mini/mini-amd64.c --------- Co-authored-by: Matous Kozak <[email protected]>
1 parent 3392893 commit ffcdf42

File tree

9 files changed

+69
-15
lines changed

9 files changed

+69
-15
lines changed

src/mono/mono/metadata/marshal.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ static GENERATE_TRY_GET_CLASS_WITH_CACHE (unmanaged_callconv_attribute, "System.
132132

133133
GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_error, "System.Runtime.InteropServices.Swift", "SwiftError")
134134
GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_self, "System.Runtime.InteropServices.Swift", "SwiftSelf")
135+
GENERATE_TRY_GET_CLASS_WITH_CACHE (swift_indirect_result, "System.Runtime.InteropServices.Swift", "SwiftIndirectResult")
135136

136137
static gboolean type_is_blittable (MonoType *type);
137138

@@ -3698,8 +3699,9 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
36983699
if (mono_method_signature_has_ext_callconv (csig, MONO_EXT_CALLCONV_SWIFTCALL)) {
36993700
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
37003701
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
3702+
MonoClass *swift_indirect_result = mono_class_try_get_swift_indirect_result_class ();
37013703
MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error));
3702-
int swift_error_args = 0, swift_self_args = 0;
3704+
int swift_error_args = 0, swift_self_args = 0, swift_indirect_result_args = 0;
37033705
for (int i = 0; i < method->signature->param_count; ++i) {
37043706
MonoClass *param_klass = mono_class_from_mono_type_internal (method->signature->params [i]);
37053707
if (param_klass) {
@@ -3711,16 +3713,18 @@ mono_marshal_get_native_wrapper (MonoMethod *method, gboolean check_exceptions,
37113713
swift_error_args++;
37123714
} else if (param_klass == swift_self) {
37133715
swift_self_args++;
3716+
} else if (param_klass == swift_indirect_result) {
3717+
swift_indirect_result_args++;
37143718
} else if (!type_is_blittable (method->signature->params [i]) || m_class_is_simd_type (param_klass)) {
3715-
swift_error_args = swift_self_args = 0;
3719+
swift_error_args = swift_self_args = swift_indirect_result_args = 0;
37163720
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Passing non-blittable types to a P/Invoke with the Swift calling convention is unsupported.");
37173721
break;
37183722
}
37193723
}
37203724
}
37213725

3722-
if (swift_self_args > 1 || swift_error_args > 1) {
3723-
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Method signature contains multiple SwiftSelf or SwiftError arguments.");
3726+
if (swift_self_args > 1 || swift_error_args > 1 || swift_indirect_result_args > 1) {
3727+
mono_error_set_generic_error (emitted_error, "System", "InvalidProgramException", "Method signature contains multiple SwiftSelf, SwiftError, SwiftIndirectResult arguments.");
37243728
}
37253729

37263730
if (!is_ok (emitted_error)) {

src/mono/mono/metadata/marshal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,7 @@ mono_marshal_get_mono_callbacks_for_ilgen (void);
742742

743743
GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_self)
744744
GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_error)
745+
GENERATE_TRY_GET_CLASS_WITH_CACHE_DECL (swift_indirect_result)
745746

746747
typedef struct {
747748
gboolean by_reference;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3407,6 +3407,7 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c
34073407
int align;
34083408
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
34093409
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
3410+
MonoClass *swift_indirect_result = mono_class_try_get_swift_indirect_result_class ();
34103411
/*
34113412
* Go through the lowered arguments, if the argument is a struct,
34123413
* we need to replace it with a sequence of lowered arguments.
@@ -3415,8 +3416,8 @@ interp_emit_swiftcall_struct_lowering (TransformData *td, MonoMethodSignature *c
34153416
for (int idx_param = 0; idx_param < csignature->param_count; ++idx_param) {
34163417
MonoType *ptype = csignature->params [idx_param];
34173418
MonoClass *klass = mono_class_from_mono_type_internal (ptype);
3418-
// SwiftSelf and SwiftError are special cases where we need to preserve the class information for the codegen to handle them correctly.
3419-
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error)) {
3419+
// SwiftSelf, SwiftError, and SwiftIndirectResult are special cases where we need to preserve the class information for the codegen to handle them correctly.
3420+
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error || klass == swift_indirect_result)) {
34203421
SwiftPhysicalLowering lowered_swift_struct = mono_marshal_get_swift_physical_lowering (ptype, FALSE);
34213422
if (!lowered_swift_struct.by_reference) {
34223423
for (uint32_t idx_lowered = 0; idx_lowered < lowered_swift_struct.num_lowered_elements; ++idx_lowered) {

src/mono/mono/mini/method-to-ir.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7534,6 +7534,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
75347534
uint32_t new_param_count = 0;
75357535
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
75367536
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
7537+
MonoClass *swift_indirect_result = mono_class_try_get_swift_indirect_result_class ();
75377538
/*
75387539
* Go through the lowered arguments, if the argument is a struct,
75397540
* we need to replace it with a sequence of lowered arguments.
@@ -7543,8 +7544,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
75437544
MonoType *ptype = fsig->params [idx_param];
75447545
MonoClass *klass = mono_class_from_mono_type_internal (ptype);
75457546

7546-
// SwiftSelf and SwiftError are special cases where we need to preserve the class information for the codegen to handle them correctly.
7547-
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error)) {
7547+
// SwiftSelf, SwiftError, and SwiftIndirectResult are special cases where we need to preserve the class information for the codegen to handle them correctly.
7548+
if (mono_type_is_struct (ptype) && !(klass == swift_self || klass == swift_error || klass == swift_indirect_result)) {
75487549
SwiftPhysicalLowering lowered_swift_struct = mono_marshal_get_swift_physical_lowering (ptype, FALSE);
75497550
if (!lowered_swift_struct.by_reference) {
75507551
// Create a new local variable to store the base address of the struct

src/mono/mono/mini/mini-amd64.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,7 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
876876
cinfo->nargs = n;
877877
cinfo->gsharedvt = mini_is_gsharedvt_variable_signature (sig);
878878
cinfo->swift_error_index = -1;
879+
cinfo->swift_indirect_result_index = -1;
879880

880881
gr = 0;
881882
fr = 0;
@@ -1014,17 +1015,23 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
10141015
if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL)) {
10151016
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
10161017
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
1018+
MonoClass *swift_indirect_result = mono_class_try_get_swift_indirect_result_class ();
10171019
MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error));
10181020
MonoClass *klass = mono_class_from_mono_type_internal (sig->params [i]);
1019-
if (klass == swift_self && sig->pinvoke) {
1021+
if (klass == swift_indirect_result)
1022+
cinfo->swift_indirect_result_index = i;
1023+
1024+
if ((klass == swift_self || klass == swift_indirect_result) && sig->pinvoke) {
10201025
guint32 size = mini_type_stack_size_full (m_class_get_byval_arg (klass), NULL, sig->pinvoke && !sig->marshalling_disabled);
10211026
g_assert (size == 8);
10221027

10231028
ainfo->storage = ArgValuetypeInReg;
10241029
ainfo->pair_storage [0] = ArgInIReg;
10251030
ainfo->pair_storage [1] = ArgNone;
10261031
ainfo->nregs = 1;
1027-
ainfo->pair_regs [0] = GINT32_TO_UINT8 (AMD64_R13);
1032+
// The indirect result is passed in RAX on AMD64. However, since we don't prevent reg allocator to use RAX in m2n wrappers, the RAX is used to pass the function pointer.
1033+
// We use the R10 register instead and before the native call, the value is moved from R10 to RAX.
1034+
ainfo->pair_regs [0] = (klass == swift_self) ? GINT32_TO_UINT8 (AMD64_R13) : GINT32_TO_UINT8 (AMD64_R10);
10281035
ainfo->pair_size [0] = size;
10291036
continue;
10301037
} else if (klass == swift_error || klass == swift_error_ptr) {
@@ -4634,6 +4641,25 @@ amd64_handle_varargs_call (MonoCompile *cfg, guint8 *code, MonoCallInst *call, g
46344641
#endif
46354642
}
46364643

4644+
static guint8*
4645+
amd64_handle_swift_indirect_result (MonoCompile *cfg, guint8 *code, MonoCallInst *call)
4646+
{
4647+
// Ideally, this should be in mono_arch_emit_prolog, but RAX may be used for the call, and it is required to free RAX.
4648+
if (mono_method_signature_has_ext_callconv (cfg->method->signature, MONO_EXT_CALLCONV_SWIFTCALL) &&
4649+
cfg->arch.cinfo->swift_indirect_result_index > -1) {
4650+
4651+
MonoInst *ins = (MonoInst*)call;
4652+
if (ins->sreg1 == AMD64_RAX) {
4653+
amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, 8);
4654+
ins->sreg1 = AMD64_R11;
4655+
}
4656+
amd64_mov_reg_reg (code, AMD64_RAX, AMD64_R10, 8);
4657+
}
4658+
4659+
return code;
4660+
}
4661+
4662+
46374663
void
46384664
mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
46394665
{
@@ -5619,6 +5645,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
56195645
// FIXME Just like NT the direct cases are are not ideal.
56205646
amd64_mov_membase_reg (code, AMD64_RSP, -8, AMD64_RAX, 8);
56215647
code = amd64_handle_varargs_call (cfg, code, call, FALSE);
5648+
#ifdef MONO_ARCH_HAVE_SWIFTCALL
5649+
code = amd64_handle_swift_indirect_result (cfg, code, call);
5650+
#endif
56225651
amd64_jump_membase (code, AMD64_RSP, -8);
56235652
#endif
56245653
ins->flags |= MONO_INST_GC_CALLSITE;
@@ -5644,6 +5673,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
56445673
call = (MonoCallInst*)ins;
56455674

56465675
code = amd64_handle_varargs_call (cfg, code, call, FALSE);
5676+
#ifdef MONO_ARCH_HAVE_SWIFTCALL
5677+
code = amd64_handle_swift_indirect_result (cfg, code, call);
5678+
#endif
56475679
code = emit_call (cfg, call, code, MONO_JIT_ICALL_ZeroIsReserved);
56485680
ins->flags |= MONO_INST_GC_CALLSITE;
56495681
ins->backend.pc_offset = GPTRDIFF_TO_INT (code - cfg->native_code);
@@ -5664,6 +5696,9 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
56645696
}
56655697

56665698
code = amd64_handle_varargs_call (cfg, code, call, TRUE);
5699+
#ifdef MONO_ARCH_HAVE_SWIFTCALL
5700+
code = amd64_handle_swift_indirect_result (cfg, code, call);
5701+
#endif
56675702
amd64_call_reg (code, ins->sreg1);
56685703
ins->flags |= MONO_INST_GC_CALLSITE;
56695704
ins->backend.pc_offset = GPTRDIFF_TO_INT (code - cfg->native_code);
@@ -8206,6 +8241,14 @@ MONO_RESTORE_WARNING
82068241
amd64_mov_membase_reg (code, cfg->vret_addr->inst_basereg, cfg->vret_addr->inst_offset, cinfo->ret.reg, 8);
82078242
}
82088243

8244+
#ifdef MONO_ARCH_HAVE_SWIFTCALL
8245+
if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL) &&
8246+
cfg->method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED &&
8247+
cfg->arch.cinfo->swift_indirect_result_index > -1) {
8248+
amd64_mov_reg_reg (code, AMD64_R10, AMD64_RAX, 8);
8249+
}
8250+
#endif
8251+
82098252
/* Keep this in sync with emit_load_volatile_arguments */
82108253
for (guint i = 0; i < sig->param_count + sig->hasthis; ++i) {
82118254
ArgInfo *ainfo = cinfo->args + i;

src/mono/mono/mini/mini-amd64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ struct CallInfo {
330330
guint32 reg_usage;
331331
guint32 freg_usage;
332332
gint32 swift_error_index;
333+
gint32 swift_indirect_result_index;
333334
gboolean need_stack_align;
334335
gboolean gsharedvt;
335336
/* The index of the vret arg in the argument list */

src/mono/mono/mini/mini-arm64.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,16 +1862,17 @@ get_call_info (MonoMemPool *mp, MonoMethodSignature *sig)
18621862
if (mono_method_signature_has_ext_callconv (sig, MONO_EXT_CALLCONV_SWIFTCALL)) {
18631863
MonoClass *swift_self = mono_class_try_get_swift_self_class ();
18641864
MonoClass *swift_error = mono_class_try_get_swift_error_class ();
1865+
MonoClass *swift_indirect_result = mono_class_try_get_swift_indirect_result_class ();
18651866
MonoClass *swift_error_ptr = mono_class_create_ptr (m_class_get_this_arg (swift_error));
18661867
MonoClass *klass = mono_class_from_mono_type_internal (sig->params [pindex]);
1867-
if (klass == swift_self && sig->pinvoke) {
1868+
if ((klass == swift_self || klass == swift_indirect_result) && sig->pinvoke) {
18681869
guint32 align;
18691870
MonoType *ptype = mini_get_underlying_type (sig->params [pindex]);
18701871
int size = mini_type_stack_size_full (ptype, &align, cinfo->pinvoke);
18711872
g_assert (size == 8);
18721873

18731874
ainfo->storage = ArgVtypeInIRegs;
1874-
ainfo->reg = ARMREG_R20;
1875+
ainfo->reg = (klass == swift_self) ? ARMREG_R20 : ARMREG_R8;
18751876
ainfo->nregs = 1;
18761877
ainfo->size = size;
18771878
continue;

src/mono/mono/mini/tramp-amd64.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,6 +1162,8 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info)
11621162
amd64_mov_membase_reg (code, AMD64_RBP, off_ctxregs - i * sizeof (target_mgreg_t), i + CTX_REGS_OFFSET, sizeof (target_mgreg_t));
11631163
amd64_mov_reg_membase (code, i + CTX_REGS_OFFSET, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, gregs) + (i + CTX_REGS_OFFSET) * sizeof (target_mgreg_t), sizeof (target_mgreg_t));
11641164
}
1165+
// Move R10 to RAX before the native call as R10 is used as a proxy for SwiftIndirectResult.
1166+
amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, gregs) + 10 * sizeof (target_mgreg_t), sizeof (target_mgreg_t));
11651167
#endif
11661168

11671169
/* load target addr */
@@ -1267,6 +1269,9 @@ mono_arch_get_native_to_interp_trampoline (MonoTrampInfo **info)
12671269
/* set context registers to CallContext */
12681270
for (i = 0; i < CTX_REGS; i++)
12691271
amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + MONO_STRUCT_OFFSET (CallContext, gregs) + (i + CTX_REGS_OFFSET) * sizeof (target_mgreg_t), i + CTX_REGS_OFFSET, sizeof (target_mgreg_t));
1272+
1273+
// Move RAX to R10 before the managed call as R10 is used as a proxy for SwiftIndirectResult.
1274+
amd64_mov_membase_reg (code, AMD64_RSP, ctx_offset + MONO_STRUCT_OFFSET (CallContext, gregs) + 10 * sizeof (target_mgreg_t), AMD64_RAX, sizeof (target_mgreg_t));
12701275
#endif
12711276

12721277
/* set the stack pointer to the value at call site */

src/tests/issues.targets

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,9 +1917,6 @@
19171917
<ExcludeList Include="$(XunitTestBinBase)/Interop/Swift/SwiftCallbackAbiStress/**">
19181918
<Issue>https://github.com/dotnet/runtime/issues/93631: Swift reverse pinvokes are not implemented on Mono yet</Issue>
19191919
</ExcludeList>
1920-
<ExcludeList Include="$(XunitTestBinBase)/Interop/Swift/SwiftIndirectResult/**">
1921-
<Issue>https://github.com/dotnet/runtime/issues/93631: SwiftIndirectResult is not implemented on Mono yet</Issue>
1922-
</ExcludeList>
19231920
</ItemGroup>
19241921

19251922
<!-- Known failures for mono runtime on Windows -->

0 commit comments

Comments
 (0)