Skip to main content
GameDev.net gamedev.net
🔒 Locked

Creating 64bit iOS native calling support

Started by Pto Apr 8, 2019 at 5:02 AM 12 replies 8.4k views
Original Post
Pto
Pto

I have a little game running on windows that I've used AngelScript for and I'm now trying to port to iOS. A bit of googling showed native calling isn't currently supporting on 64bit arm devices so I looked into the generic calling convention. However since I don't want to give up native calling on windows I'm going to either have to re-write all my existing code to try to maintain two different calling conventions (native+generic).... or take you up on this very old offer:

Quote

unfortunately native calling conventions on iOS 64bit is not yet supported (nor is any other 64bit ARM platform). I don't have access to any 64bit ARM platform so I can't implement this support myself, but if you want to give it a try on your own I can guide you through it. It usually doesn't take more than a week or two to get it to work. Although it involves assembler code it doesn't require a lot of understanding of assembler code since most of the time it is just a matter of copying existing code obtained by disassembling C++ compiled functions :).


My assembly skills are very limited but I can follow instructions. If the offer to guide someone through the process is still available I'd love to try. Just let me know where to start. I have a mac with xCode installed and an iPhone 8 that I can install builds on (and PC with VC 2017 if that's at all helpful).


If however you're not able to help at the moment for any reason, no worries at all. Thanks for creating angel script in the first place I've had a blast using it recently. If however you are, hopefully this can help out anyone else who wants/needs this in future.

WitchLord
WitchLord

I have very little time at the moment, but I'm very interested in seeing iOS 64bit native calling convention support (as well as Android 64bit).

It's not really necessary to have a lot of assembly skills. I don't have it either. Most of the assembler code to support native calling convention on other platforms was written by doing disassembly of C++ functions, and then piecing together the related code.

I can certainly help you point out the direction on how to proceed.

  1. Download the latest code revision from the SVN to get the regression test suite not included in the SDK
  2. Compile the test_feature app for iOS 64bit. There is a rather old xcode project included. You can try to upgrade it to the latest xcode version, or just start from scratch. (I'd like to get the updated xcode projects to check them into the SVN later)
  3. Take a look at the as_config.h file, and update it to enable native calling convention on iOS 64bit. You probably need to change the condition for #if defined(__GNUC__) => #if defined(__APPLE__) => #if defined(__arm64__) to remove #define AS_MAX_PORTABILITY and include AS_ARM64 (AS_ARM64 is not used yet, we'll use it to tell the compiler to include the code needed for arm64 native calling convention)
  4. Create a new as_callfunc_arm64.cpp (you can use as_callfunc_arm.cpp as the base to begin with).
  5. Change the #ifdef AS_ARM to #ifdef_AS_ARM64 so it will only be compiled on arm64
  6. You will probably need a as_callfunc_arm64_xcode.S assembly file too (similar to as_callfunc_arm_xcode.S)
  7. Change the main.cpp in the test_feature app to comment out all the test cases before main() => TestExecute() which is the first test case designed specifically for testing the native calling convention.
  8. Make the necessary changes to allow you to compile and debug the test_feature app on iOS 64bit.

At the end of these steps you'll have what is needed to get started on adding the support for iOS 64bit. Next up is an iteration of compile, debug, disassembly, etc to understand the underlying ABI used by Apple on iOS and creating the necessary code to support the native calling convention.

The test_feature test cases are ordered by the complexity of the native calls, so the first TestExecute() scenario just calls a C function without any arguments, just to see the call being made. The next scenario add an integer argument, then more, then more. After functions with integer arguments is working, it goes on to float and double arguments, that usually work differently. After that it goes on to class methods and passing/returning objects. Only when all the scenarios that are designed for testing the native calling convention are working, should you uncomment the rest of the test suite to do the full regression test.

If you share your progress here on the forum, others may also be able to join in and help progress even faster.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
Miss
Miss

You might also find some useful information in the source code for dyncall. It seems to support arm64 as well.

Sidenote - would it be interesting to implement dyncall into Angelscript directly so that Angelscript itself doesn't need to maintain architecture calling code? I don't know exactly if there are any massive differences.

Pto
Pto

@WitchLord : Thanks for the steps, I'll give it a try and post back any results or issues. Hopefully I can get somewhere useful without taking up to much of anybodies time.

@Miss : Thanks for the link - I'll definitely check that out.

WitchLord
WitchLord
21 hours ago, Miss said:

Sidenote - would it be interesting to implement dyncall into Angelscript directly so that Angelscript itself doesn't need to maintain architecture calling code?

While dyncall is an interesting library, it is out of the question to add any dependency to thirdparty libraries from AngelScript. It is one of the main principles that I follow.

It can still be an additional source for gaining understanding on the arm64 ABI though.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
Miss
Miss
Just now, WitchLord said:

While dyncall is an interesting library, it is out of the question to add any dependency to thirdparty libraries from AngelScript. It is one of the main principles that I follow.

Ah yes, that is indeed the impression I've always had and one that I really appreciate a lot. :)

Pto
Pto

Sadly I'm still not up to native calling, I'm just trying to get the whole thing working with AS_MAX_PORTABILITY to ensure I'm starting from a solid base. I've got the 64bit iOS app setup, it's building and running on device and all but one of the tests pass. Annoyingly though TestExecuteString() fails on an assert:

assert(*reinterpret_cast(ref) != 0);

on line 193 of scripthelper.cpp. The assert is not even part of the test - just part of the Script Helper add-on. 'ref' is a pointer to a std::string so I'm not sure what this assert is even doing here. Casting to a double pointer then de-referencing back to it's original single pointer to check if it's null? The code works if I remove this, so not sure why the assert is triggering, but I'm clearly not understanding something. For now I'm just going to move on to native calling next week, but if that assert is in some way important let me know.

MrFloat
MrFloat

line 193 seems to be for when the execution return type is not a handle (pointer).
You can see that the handle version has the opposite assert.
I guess it just assumes that the first 64bits of the return type should not be all 0 and tries to catch instances where people incorrectly used return values by handle.
I think the return address will be treated as a fully constructed object and some form of assignment will be used on it.
Make sure that having the first 64bits of 0 in your object instance is valid before removing that assert.

WitchLord
WitchLord
On 4/13/2019 at 9:08 AM, Pto said:

assert(*reinterpret_cast(ref) != 0);

This assert may very well be invalid and should be removed. What it does is that it checks if the first PWORD of the object referred to by ref is not null, in an attempt to validate if the object is seemingly a valid object.

Interestingly it doesn't trigger on any other platform though that I've tested on.

Which test is triggering this assert?

On 4/13/2019 at 9:08 AM, Pto said:

I've got the 64bit iOS app setup, it's building and running on device and all but one of the tests pass.

Which test doesn't pass?



AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
Pto
Pto
On 4/15/2019 at 11:20 PM, WitchLord said:

Which test is triggering this assert?

Which test doesn't pass?

Sorry I wasn't clear in my initial post. TestExecuteString() is the test that trigger the assert. The assert triggering inside TestExecuteString() is the only issue I had when AS_MAX_PORTABILITY defined. I'm not sure if triggering an assert is technically a failure or not but that's what I meant when I said a test failed. As I mentioned if I simply comment out the assert it all worked and everything passed including TestExecuteString().


After Easter break I got back to it and I removed AS_MAX_PORTABILITY and set up the two Arm64 files ('as_callfunc_x64_arm.cpp' and 'as_callfunc_x64_arm_xcode.S') as instructed. In the .cpp I was able to get it compiling based on what I saw in the other x64 files, and fixing a few other minor compiler issues (mostly just changing any asDWORD types to asQWORD ). So the .cpp now compiles and I think it's doing what it's meant to do.

I'm left with a whole heap of assembly errors for the .S. Currently it's just a cut+paste of the original ARM native call without any changes for 64bit. This is where my programming knowledge ends. I don't know any arm assembly and certainly not enough to re-write the .S.

I've attached my two new files and the compiler errors generated by the assembly. This is kind of as far as I can go on my own. Any help appreciated and if anyone wants I try to zip up my test project for someone else to look at (but to get it working I had to change a few things so it's not a simple as just getting a new version of the xcode project sadly).



as_callfunc_x64_arm_xcode.S

as_callfunc_x64_arm.cpp

compile_errors.txt

WitchLord
WitchLord
On 4/29/2019 at 9:52 AM, Pto said:

Sorry I wasn't clear in my initial post. TestExecuteString() is the test that trigger the assert. The assert triggering inside TestExecuteString() is the only issue I had when AS_MAX_PORTABILITY defined.

Thanks for clarifying. It's good to know everything is working as it should on iOS 64bit with AS_MAX_PORTABILITY too. (I still need to check in the code to remove the invalid assert statement though)

On 4/29/2019 at 9:52 AM, Pto said:

After Easter break I got back to it and I removed AS_MAX_PORTABILITY and set up the two Arm64 files ('as_callfunc_x64_arm.cpp' and 'as_callfunc_x64_arm_xcode.S') as instructed.

The files would be better named as as_callfunc_arm64.cpp and as_callfunc_arm64_xcode.S. x64 is usually used to refer to Intel/AMD CPUs, and not ARM CPUs.

On 4/29/2019 at 9:52 AM, Pto said:

I've attached my two new files and the compiler errors generated by the assembly. This is kind of as far as I can go on my own. Any help appreciated and if anyone wants I try to zip up my test project for someone else to look at (but to get it working I had to change a few things so it's not a simple as just getting a new version of the xcode project sadly).

You've done the most important step already; getting the regression test suite up and running on iOS 64bit. :) The next step is to get AngelScript to call a trivial function without any arguments using native calling convention.

To do that first step I would write a C++ function that does what is needed, and then compile it to assembler (there is probably an option for this in xcode) or else you can debug the function and show the disassembly and get the instructions from there.

A function like this would be a good start:


void CallFunc(void (*ptr)())
{
	ptr();
}

The assembly of this function ought to show the basics of setting up the stack frame, and calling a function from a function pointer and then cleaning up the stack frame before returning to caller.

Once you have this implemented the test TestExecute() should pass successfully. After that take a look at the next test case and do the same steps (write a C++ function, disassemble, test) to get an understanding on how to build the common assembler routine for marshalling the arguments from AngelScript to the C++ native function.

Quickly browsing through the ABI specification (link above) I can see that the ABI used on iOS 64bit is quite similar to the ABI used on Linux with 32bit ARM CPU. So the C++ part of the as_callfunc_arm64 should probably be built similar to the second CallSystemFunctionNative in as_callfunc_arm.cpp (the one that compiles when AS_SOFTFP is not defined).

Don't try to write all at once though. Comment out the parts that you're uncertain of, and start small with getting one scenario of the regression test suite working at a time.



AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel
Miss
Miss

Perhaps Compiler Explorer is useful? https://godbolt.org/z/oZNb-Y

That link is ARM64 on gcc though, not clang. Don't know if there's any big differences there.

WitchLord
WitchLord

I've fixed the invalid assert statement in revision 2580.

1 hour ago, Miss said:

Perhaps Compiler Explorer is useful? https://godbolt.org/z/oZNb-Y

Interesting site.

Unfortunately I don't think it considers the correct ABI for iOS 64bit. It is most likely considering the standard ARM 64bit ABI, not Apple's custom ABI. Also unfortunately it doesn't provide the correct description of the assembler routines on ARM, otherwise it could be helpful in understanding the assembler output.





AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - <a href="http://www.angelcode.com/tower" rel

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.