-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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 struct lowering in direct P/Invoke returns #104389
[mono][swift-interop] Support for Swift struct lowering in direct P/Invoke returns #104389
Conversation
Interpreter support for Swift struct lowering in returns on arm64 + LLVM fallback for ArgSwiftError and ArgSwiftVtypeLoweredRet ArgStorage types.
Tagging subscribers to this area: @steveisok, @lambdageek |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job!
You mentioned you tested these changes on extended set of 5000 tests in Interop/Swift/SwiftRetAbiStress
. Is there any description somewhere how to get / generate this set? Could be useful for testing other changes.
src/mono/mono/mini/method-to-ir.c
Outdated
*sp++ = struct_base_address; | ||
ptype = mono_class_get_byref_type (mono_defaults.typed_reference_class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear to me why we can use mono_class_get_byref_type (mono_defaults.typed_reference_class)
instead of mono_class_get_byref_type (klass);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear to me why we can use
mono_class_get_byref_type (mono_defaults.typed_reference_class)
instead ofmono_class_get_byref_type (klass);
I'm not sure either. I did some investigating and it seems that the all byref types get changed to generic MONO_TYPE_I
which is then handled by codegen as a pointer-sized integer. I will change it to mono_class_get_byref_type (klass)
as it makes more sense.
@@ -1179,7 +1179,7 @@ mono_arch_get_interp_to_native_trampoline (MonoTrampInfo **info) | |||
|
|||
/* save all return floating registers in the CallContext */ | |||
for (i = 0; i < FLOAT_RETURN_REGS; i++) | |||
amd64_sse_movsd_membase_reg (code, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double), i); | |||
amd64_sse_movsd_membase_reg (code, AMD64_R11, MONO_STRUCT_OFFSET (CallContext, fregs) + float_return_regs [i] * sizeof (double), float_return_regs [i]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change necessary only for amd64?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On arm64, the argument and return registers are the same (x0-x7). Whereas on amd64, different registers are used for arguments and returns.
For arm64, this gets handled at
runtime/src/mono/mono/mini/tramp-arm64.c
Lines 773 to 779 in 4185f94
/* set all general purpose registers to CallContext */ | |
for (i = 0; i < PARAM_REGS; i++) | |
arm_strx (code, i, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, gregs) + i * sizeof (host_mgreg_t)); | |
/* set all floating registers to CallContext */ | |
for (i = 0; i < FP_PARAM_REGS; i++) | |
arm_strfpx (code, i, ARMREG_IP0, MONO_STRUCT_OFFSET (CallContext, fregs) + i * sizeof (double)); |
out of add_valuetype and add it to get_call_info
Is there any follow-up work we need to do for LLVM? |
@@ -176,6 +176,10 @@ struct sigcontext { | |||
* reproduceable results for benchmarks */ | |||
#define MONO_ARCH_CODE_ALIGNMENT 32 | |||
|
|||
#if defined(TARGET_OSX) || defined(TARGET_APPLE_MOBILE) | |||
#define MONO_ARCH_HAVE_SWIFTCALL 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a better place, where we could set this value for Mono instead of repeating it for each architecture? Like somewhere in cmake logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it could be set in src/mono/CMakeLists.txt
by something like:
if ((TARGET_AMD64 OR TARGET_ARM64) AND (TARGET_OSX or TARGET_APPLE_MOBILE))
set(MONO_ARCH_HAVE_SWIFTCALL 1)
endif()
However, CoreCLR also does set it in the respective arch files e.g.
runtime/src/coreclr/jit/targetamd64.h
Line 563 in a5c1c9f
#define SWIFT_SUPPORT |
What do you think is better? (cc: @kotlarmilos, @jkurdek)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be cleaner to move it to src/mono/CMakeLists.txt
, but don't have a strong opinion about it. Since this is not a new change, I suggest doing it in a follow-up PR.
At the moment no, Mono doesn't use LLVM to compile P/Invoke wrappers so it fallbacks to Mini. I just added the entries inside |
Description
This PR adds support in Mono runtime for Swift
@frozen
struct lowering in returns. The scope is limited to direct P/Invoke scenario with reverse P/Invoke following up in a separate PR. The changes cover support over all Mono supported backend engines: miniJIT, interpreter and AOT.In Swift, structures can be lowered into <= 4 primitives that are subsequently returned via registers. This is a simplified scenario in comparison to lowering of arguments where lowered elements can also be passed on stack when maximum number of registers used for arguments is exceeded.
Implementation
The lowering support for returns is handled at the codegen level (
mini-<arch>.c
) by introducing new storage type (ArgSwiftVtypeLoweredRet
/ArgSwiftValuetypeLoweredRet
) that represents a structure that is returned via combination of integer and float registers.The first step of struct lowering is handled in
get_call_info
->add_return_valuetype_swiftcall
where struct lowering is computed. In scenarios where struct can be lowered (fits in <= 4 registers) the newly introduced storage type is used, otherwise the struct is returned via reference.Returned by lowered sequence:
emit_move_return_value
where the values stored in return registers are loaded into prepared memory at specific offsets.Returned by reference:
ArgVtypeByRef
with no extra changes.ArgValuetypeAddrInIReg
with specific handling where the reference is temporarily hold inR10
reg. and moved toRAX
reg. (amd64_handle_swift_return_buffer_reg
) before emitting the call to native code. That's because Swift expects that the return buffer reference is passed inRAX
reg. but in Mono runtime, this reg. serves multiple purposes and we cannot easily preventRAX
clobbering by other parts of code.Verification
The changes are verified using the
Interop/Swift/SwiftRetAbiStress
runtime tests. The extended set of 5000 test cases was used locally to further verify the lowering support. The fullAOT functionality was only verified in local settings due to missing coverage on CI.Other changes
@frozen
struct lowering inmethod-to-ir.c
amd64_handle_swift_return_buffer_reg
)#ifdef MONO_ARCH_HAVE_SWIFTCALL
to better separate swiftcall supportArgSwiftError
entry inmono_arch_get_llvm_call_info
on arm64. This is just a placeholder because we don't compile P/Invoke wrappers by LLVM. However, on arm64, the missing entry was causing failures in AOT-llvm compilation.Contributes to #102077