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

[ffigen] Only use objc_msgSend variants on x64 #836

Merged
merged 6 commits into from
Nov 29, 2023
Merged

Conversation

liamappelbe
Copy link
Contributor

@liamappelbe liamappelbe commented Nov 28, 2023

objc_msgSend_stret and objc_msgSend_fpret are only available on x64, so just use the normal objc_msgSend on arm64.

We can only check the ABI at runtime, so for the stret and fpret variants we need to emit both the variant and the normal objc_msgSend function. Then we can decide which to use when the method is invoked at runtime.

This runtime check is complicated by the fact that objc_msgSend_stret has a different signature than objc_msgSend has for the same method. This is because objc_msgSend_stret takes a pointer to the return type as its first arg. If it wasn't for this signature difference we could just change the function name string we pass to DynamicLibrary.lookup.

Fixes #829

Copy link
Collaborator

@dcharkes dcharkes left a comment

Choose a reason for hiding this comment

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

Thanks @liamappelbe!

I think this code could use some simplification before landing.

@@ -35168,25 +35768,46 @@ class NSString extends NSObject {

void localizedStandardRangeOfString_(
ffi.Pointer<_NSRange> stret, NSString str) {
_lib._objc_msgSend_340(
stret, _id, _lib._sel_localizedStandardRangeOfString_1, str._id);
_lib._objc_msgSend_useVariants1
Copy link
Collaborator

Choose a reason for hiding this comment

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

Note: This will probs also need treatment in #216.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are @Native annotations lazy loaded? If so this approach should work.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, they are "dynamic loading" everywhere currently.

If we'd ever to static linking, it would be after TFA. @sstrickl has been working passing the OS in to TFA, when I get to static linking, I could extend that work to cover Abi.

@@ -417,28 +427,81 @@ enum ObjCMsgSendVariant {
/// A wrapper around the objc_msgSend function, or the stret or fpret variants.
class ObjCMsgSendFunc {
final ObjCMsgSendVariant variant;
late final Func func;
final ObjCInternalGlobal useVariants;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could use documentation. Ditto for the other fields, and maybe a larger explanation on ObjCMsgSendFunc itself.

What I've been able to reverse engineer:

  • isStret whether if useVariant is true, the stret variant is used. Requires a pointer to the struct as extra param.
  • useVariant whether at runtime the ABI is x64 on iOS or MacOS.
  • variantFunc the stret or fpret variants. null if only normal func.
  • normalFunc always the normal variant.

We emit both variant and normalFunc and branch at runtime based on ABI.

The same documentation should go on the PR description.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

bool get isStret => variant == ObjCMsgSendVariant.stret;
bool get isNormal => variant == ObjCMsgSendVariant.normal;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hm, maybe the code would be more readable if the call sites had switches over the enum. (e.g. !isNormal would be both the stret and fpret cases.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

final stretCall =
_invoke(variantFunc!.name, lib, target, sel, params, stret: stret);
return '$lib.${useVariants.name} ? $stretCall : $stret.ref = $normalCall';
} else if (variantFunc != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is not so readable: variantFunc != null if !isNormal. Since we already covered isStret, this is the fpret case.

Please convert this if else if else to a switch on variant with the 3 cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Collaborator

@dcharkes dcharkes left a comment

Choose a reason for hiding this comment

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

Thanks @liamappelbe !

@auto-submit auto-submit bot merged commit 1748715 into main Nov 29, 2023
10 checks passed
@auto-submit auto-submit bot deleted the msgsendfix branch November 29, 2023 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Objective-C interop] Invalid argument(s): Failed to lookup symbol 'objc_msgSend_fpret'
2 participants