Angelscript on Raspberry Pi
CDECL at 90%. I need to make some adjustments for when objects are passed by value, but passing all other args work fine, be it using "r", "s" or "d" registers or the stack. I´ve had to make some important changes to the existing code, so instead or trying to make it fit with the original file, I´d suggest giving it its own... in fact, I´m really curious about how the current implementation is working with Android (or iOS). Has anyone reported native calling conventions really working for those cases?
Unfortunately I do not have any development environment to certify that the code is actually working 100% on Android and iOS, but there are plenty of developers that use AngelScript on these platforms and so far I haven't heard of any problems.
If the code for Raspberry Pi becomes sufficiently different I'll probably do as you suggested and keep it separate from the rest.
If the code for Raspberry Pi becomes sufficiently different I'll probably do as you suggested and keep it separate from the rest.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Andreas:
In the code section that copies the passed object to the paramBuffer, you have this:
Since you aren´t performing any kind of bounds checking, this could cause problems if the object to be copied exceeds the capacity of the array... or maybe I´m missing something? In this kind of situations maybe the lib could raise an internal exception when something like this happens.
In the code section that copies the passed object to the paramBuffer, you have this:
// Copy the object's memory to the buffer
memcpy(¶mBuffer[dpos], *(void**)(args+spos), descr->parameterTypes[n].GetSizeInMemoryBytes());
Since you aren´t performing any kind of bounds checking, this could cause problems if the object to be copied exceeds the capacity of the array... or maybe I´m missing something? In this kind of situations maybe the lib could raise an internal exception when something like this happens.
More questions:
How do I "trigger" the usage of the different calling conventions? I mean:
What kind of functions should i register in order to be able to test every possible case in the above switch? I know the "THISCALL" ones should be called by object methods, but what´s the difference between the "RETURNINMEM" and the other ones? I´ve finished porting the armFuncR0 and I´d like to test it before going on.
Also, is there any "standard" test that the code has to pass in order to safely say that the lib supports a certain platform?
Last night I had dreams with "r" registers....
How do I "trigger" the usage of the different calling conventions? I mean:
switch( callConv )
{
case ICC_CDECL_RETURNINMEM: // fall through
case ICC_STDCALL_RETURNINMEM:
std::cout << "armFuncR0.\n";
retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)retPointer);
break;
case ICC_CDECL: // fall through
case ICC_STDCALL:
std::cout << "armFunc.\n";
retQW = armFunc(args, paramSize<<2, func);
break;
case ICC_THISCALL: // fall through
case ICC_CDECL_OBJFIRST:
std::cout << "armFuncR0.\n";
retQW = armFuncR0(args, paramSize<<2, func, (asDWORD)obj);
break;
case ICC_THISCALL_RETURNINMEM:
std::cout << "armFuncR0R1.\n";
....
What kind of functions should i register in order to be able to test every possible case in the above switch? I know the "THISCALL" ones should be called by object methods, but what´s the difference between the "RETURNINMEM" and the other ones? I´ve finished porting the armFuncR0 and I´d like to test it before going on.
Also, is there any "standard" test that the code has to pass in order to safely say that the lib supports a certain platform?
Last night I had dreams with "r" registers....
When RETURNINMEM is used depends on the ABI. Some classes will always be returned in memory regardless of their size, others will only be returned in memory if their size is bigger than some minimum. Normally the existance of explicitly declared constructors, destructors, and or assignment operator in the class declaration will determine whether the size if of importance or not. Usually these same properties also determine how the objects are passed by value, i.e. whether they are pushed on the stack or if just a reference to the object is pushed on the stack.
In the tests/test_feature project (from the SVN) you'll find the regression test suite that I use to make sure everything works ok with each release. The tests for testing the native calling convention starts on line 325 in main.cpp. I suggest you start with those tests by commenting out all other tests before that. The tests for the native calling convention are designed to test all the aspects of the ABI and will provide you with the tests you need.
In the tests/test_feature project (from the SVN) you'll find the regression test suite that I use to make sure everything works ok with each release. The tests for testing the native calling convention starts on line 325 in main.cpp. I suggest you start with those tests by commenting out all other tests before that. The tests for the native calling convention are designed to test all the aspects of the ABI and will provide you with the tests you need.
// The following tests are designed specifically to test the native calling conventions.
// These are grouped by calling convention and ordered in increasing complexity.
{
// cdecl
if( TestExecute() ) goto failed; else printf("-- TestExecute passed\n");
if( TestCDeclReturn::Test() ) goto failed; else printf("-- TestCDeclReturn passed\n");
if( TestExecute1Arg() ) goto failed; else printf("-- TestExecute1Arg passed\n");
if( TestExecute2Args() ) goto failed; else printf("-- TestExecute2Args passed\n");
if( TestExecute4Args() ) goto failed; else printf("-- TestExecute4Args passed\n");
if( TestExecute4Argsf() ) goto failed; else printf("-- TestExecute4Argsf passed\n");
if( TestExecuteMixedArgs() ) goto failed; else printf("-- TestExecuteMixedArgs passed\n");
if( TestExecute32Args() ) goto failed; else printf("-- TestExecute32Args passed\n");
if( TestExecute32MixedArgs() ) goto failed; else printf("-- TestExecute32MixedArgs passed\n");
if( TestCDecl_Class() ) goto failed; else printf("-- TestCDecl_Class passed\n");
if( TestCDecl_ClassA() ) goto failed; else printf("-- TestCDecl_ClassA passed\n");
if( TestCDecl_ClassC() ) goto failed; else printf("-- TestCDecl_ClassC passed\n");
if( TestCDecl_ClassD() ) goto failed; else printf("-- TestCDecl_ClassD passed\n");
if( TestCDecl_ClassK() ) goto failed; else printf("-- TestCDecl_ClassK passed\n");
// cdecl_objlast and cdecl_objfirst
if( TestCDeclObjLast::Test() ) goto failed; else printf("-- TestCDeclObjLast passed\n");
if( TestReturnWithCDeclObjFirst() ) goto failed; else printf("-- TestReturnWithCDeclObjFirst passed\n");
// thiscall
if( TestExecuteThis32MixedArgs() ) goto failed; else printf("-- TestExecuteThis32MixedArgs passed\n");
if( TestThiscallClass() ) goto failed; else printf("-- TestThiscallClass passed\n");
if( TestNotComplexThisCall() ) goto failed; else printf("-- TestNotComplexThisCall passed\n");
if( TestVirtualMethod() ) goto failed; else printf("-- TestVirtualMethod passed\n");
if( TestMultipleInheritance() ) goto failed; else printf("-- TestMultipleInheritance passed\n");
if( TestVirtualInheritance() ) goto failed; else printf("-- TestVirtualInheritance passed\n");
// stdcall
if( TestStdcall4Args() ) goto failed; else printf("-- TestStdcall4Args passed\n");
if( TestNotComplexStdcall() ) goto failed; else printf("-- TestNotComplexStdcall passed\n");
}
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Ok, I´ll run the tests to check how I´m progressing.
Now, one more question.
I´m having trouble with return values. When the system function returns an int, everything is fine. The problem arises when the function returns a float or a double. From the dissasemblies I´ve found that in those cases the return values are being passed back stored at registers s0 or d0 (float / double) - as opposed to when returning an int, which is stored in r0. I was wondering how do we get that value into retQW. Is it stored there automaitcally by some kind of compiler wizardry? (as a result of the "retQW = " assignment maybe?) If so, then how should we interpret the returning value? Maybe it´s some kind of alignment problem or something..... any ideas would be appreciated.
In other words..... heeeeelp!
Now, one more question.
retQW = armFunc(args, paramSize<<2, func);
I´m having trouble with return values. When the system function returns an int, everything is fine. The problem arises when the function returns a float or a double. From the dissasemblies I´ve found that in those cases the return values are being passed back stored at registers s0 or d0 (float / double) - as opposed to when returning an int, which is stored in r0. I was wondering how do we get that value into retQW. Is it stored there automaitcally by some kind of compiler wizardry? (as a result of the "retQW = " assignment maybe?) If so, then how should we interpret the returning value? Maybe it´s some kind of alignment problem or something..... any ideas would be appreciated.
In other words..... heeeeelp!
As I mentioned earlier. My suggestion is to do something similar to what is done in as_callfunc_x86.cpp. Simply create a separate assembler routine that copies the d0 register to r0,r1. Let's call it GetReturnedDouble(). Then call this function from CallSystemFunctionNative() after the armFunc() has returned with the following condition:
You can probably get the assembler instructions for doing this by disassemblying the following C function:
Regards,
Andreas
// If the return is a float value we need to get the value from the d0 register (or just s0 in case of single float)
if( sysFunc->hostReturnFloat )
retQW = GetReturnedDouble();
You can probably get the assembler instructions for doing this by disassemblying the following C function:
asQWORD GetReturnedDouble(double dbl)
{
// Return the binary representation of the double in the qword
return *(asQWORD*)&dbl;
}
Regards,
Andreas
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Oh, yes, i had forgotten about your previous post regarding the x86 implementation. I have an idea which might also work - its similar but involves less conditionals and function calling. I just wanted to know if maybe there was some "hidden" compiler feature I didn´t know about... So, I guess I have all I need to get this working.... until the next problem arises! ;)
Andreas:
I got the returning of floats/doubles in armFunc working. Now I´ve got a problem with returning objects from armFuncR0 (it fails the TestReturnStruct () test in the test bench).
What I´ve found so far:
The system registered object is created (type point) and it is getting the correct values. The problem appears when the engine receives the created object.
CallSytemFunctionNative() reports that sysFunc->hostReturnInMemory = true and sysFunc->hostReturnSize = 1. If I just let the function exit with the usual "return retQW;", the returned struct has all its members with a value of 0. (The same happens for the rect structure).
If bejore returning I do this:
Then the returned structure has its first member with the correct value. If I do it this way:
The the returned structure has its first and second members with the correct values.
Now, in the original asm file, you´ve got:
As I understand it, this copies r3 (which holds retPointer) to r0. But then you never use r3 again, and I guess r0 is going to be overwritten with the pointer to the return struct... shouldn´t I end the asm function copying r0 back to where r3 is pointing at? Any ideas?
I got the returning of floats/doubles in armFunc working. Now I´ve got a problem with returning objects from armFuncR0 (it fails the TestReturnStruct () test in the test bench).
What I´ve found so far:
The system registered object is created (type point) and it is getting the correct values. The problem appears when the engine receives the created object.
CallSytemFunctionNative() reports that sysFunc->hostReturnInMemory = true and sysFunc->hostReturnSize = 1. If I just let the function exit with the usual "return retQW;", the returned struct has all its members with a value of 0. (The same happens for the rect structure).
If bejore returning I do this:
*(asDWORD*)retPointer = (asDWORD)retQW;
Then the returned structure has its first member with the correct value. If I do it this way:
*(asQWORD*)retPointer = retQW;
The the returned structure has its first and second members with the correct values.
Now, in the original asm file, you´ve got:
mov r0, r3 // r0 explicitly set
As I understand it, this copies r3 (which holds retPointer) to r0. But then you never use r3 again, and I guess r0 is going to be overwritten with the pointer to the return struct... shouldn´t I end the asm function copying r0 back to where r3 is pointing at? Any ideas?
First of all. Disassembly the TestPoint() and TestRect() functions so we can see how the ABI expects these structures to be returned.
For both iOS and Android these functions return the value in memory, which is why armFuncR0. However, this may not be the case for Raspberry Pi, which is why we need to see the disassembly.
From what you say that retQW appears to hold the value, then it would seem the structure is returned in the registers rather than in memory. At least with the Point type. In this case the configuration in as_config.h needs to be changed according to the ABI for RPi.
I need to know how the RPi is being identified, i.e. what defines are given by default by gcc when compiling for RPi. Can you run the command "g++ -dM -E as_config.h" and show me the result? It should print the defines that are set after going through the as_config.h macros.
Most likely you'll find the following two in the output:
CDECL_RETURN_SIMPLE_IN_MEMORY
CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2
My guess is that the second needs to be changed to at least 3 for RPi, i.e. simple structs smaller than 3 DWORDs will be returned in registers, others will be returned in memory.
As for the question on the use of R0 in armFuncR0. This is for functions that return a value in memory. In this case the caller must pass a pointer to the memory where the return value should be placed. This pointer is passed as an implicit first argument to the function, i.e. in R0. The called function will take the pointer it receives in R0, and store the return value in that memory spot. As the memory is already populated by the called function, there is nothing to do by the caller once the function returns.
For both iOS and Android these functions return the value in memory, which is why armFuncR0. However, this may not be the case for Raspberry Pi, which is why we need to see the disassembly.
From what you say that retQW appears to hold the value, then it would seem the structure is returned in the registers rather than in memory. At least with the Point type. In this case the configuration in as_config.h needs to be changed according to the ABI for RPi.
I need to know how the RPi is being identified, i.e. what defines are given by default by gcc when compiling for RPi. Can you run the command "g++ -dM -E as_config.h" and show me the result? It should print the defines that are set after going through the as_config.h macros.
Most likely you'll find the following two in the output:
CDECL_RETURN_SIMPLE_IN_MEMORY
CDECL_RETURN_SIMPLE_IN_MEMORY_MIN_SIZE 2
My guess is that the second needs to be changed to at least 3 for RPi, i.e. simple structs smaller than 3 DWORDs will be returned in registers, others will be returned in memory.
As for the question on the use of R0 in armFuncR0. This is for functions that return a value in memory. In this case the caller must pass a pointer to the memory where the return value should be placed. This pointer is passed as an implicit first argument to the function, i.e. in R0. The called function will take the pointer it receives in R0, and store the return value in that memory spot. As the memory is already populated by the called function, there is nothing to do by the caller once the function returns.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement