Angelscript on Raspberry Pi

Started by
130 comments, last by WitchLord 11 years, 3 months ago
Well, the function should truly be called the way I suspected. Now we need to understand why AS decides to use armFuncR0R1 when it should be using armFuncR0.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Advertisement
Well, there´s something strange going on here... When this line (1276) is executed:

r = ExecuteString(engine, "dim2f video_res(800,600);\n"
"dim2f total_res;\n"
"total_res = video_res + video_res;\n"
"assert( total_res.x == 1600 && total_res.y == 1200 );\n"
"ByValue(dim2f(1,2)); \n");


The constructor for video_res and total_res is called (armFuncObjLast), then operator+ is called (armFuncR0R1) but the function receives wrong values (which is to be expected since it isn´t being called by the appropriate asm function) but then opAssign (operator=) is scheduled for being called using armFuncR0, and in fact the asm function is called but for some reason the program never reaches the actual code for operator+.The "blx r4" line is executed, there´s a jump to some other place in the code, then the program returns to the line after "blx r4" and continues to finally display the assert failure.
I'm out of ideas. I think I'll finally have to log in to your machine and do some debugging on my own to figure out what's going on.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Where does AS decides the calling convention to be used? Doesn´t it respect the one used when you make the binding?

Anyway, you have the details for logging into my RPi so feel free to check if you can find the problem. Meanwhile I´ll try to fix the other failing tests.
Part of it happens when the function is registered, in DetectCallingConvention(). The other part happens as a step before compiling the scripts when the full application interface is registered, in PrepareSystemFunction(). Both of these are located in the as_callfunc.cpp file, and the configurations in as_config.h control how they work on each platform.

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

I logged and reviewed the code changes you had made and I see the problem with the dim2f.

The logic you put in the end to treat returned objects that have the flag asOBJ_APP_CLASS_ALLFLOATS. Also needs to be added in the beginning of the function where the condition if( sysFunc->hostReturnInMemory ) is made.

If this condition is true, but the returned object in memory is a non-complex object with asOBJ_APP_CLASS_ALLFLOATS and it is smaller than or equal to 8 bytes, then callConv should not be incremented.

It is this increment that is making the code use armFuncR0R1 instead of just armFuncR0 that it should.

---

EDIT. I didn't make any changes to the code as I didn't have much time to spend on this at the moment.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

I changed that to:

if( sysFunc->hostReturnInMemory )
{
int vv=1;
if ( !( descr->returnType.GetObjectType()->flags & COMPLEX_RETURN_MASK ) &&
( descr->returnType.GetObjectType()->flags & asOBJ_APP_CLASS_ALLFLOATS ) &&
descr->returnType.GetSizeInMemoryBytes() <= 8 )
vv--;

// The return is made in memory
callConv += vv;
}


And the call to operator+ is competed succesfully, using the armFuncR0 function. Then operator= is called, but not the one defined in the class´ definition - nevertheless the assertion passes. Now I´m getting an error in the ByValue(...) function. I´ll investigate a little to see what´s going on here. BTW, ByValue(...) is being called with the armFunc function.
Without looking at the code I can imagine that ByValue() has a similar need.

By default the configuration in as_config.h will make dim2f be identified as a large class due to having a size larger than 4 bytes and thus AngelScript will pass it as a reference. However, due to being a structure of 2 floats, (asOBJ_APP_CLASS_ALLFLOATS) it should be treated as a simple class and passed in the S registers.

At least, this is how I think this needs to be handled. A disassembly of the ByValue function can confirm that.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

Disassemblies. For this code:

code:


#include <iostream>
#include <string>
typedef unsigned long asDWORD;
template <class T>
class dimension2d
{
public:
dimension2d() : Width(0), Height(0) {}
dimension2d(const T& width, const T& height) :
Width(width), Height(height) {}
template <class U>
explicit dimension2d(const dimension2d<U>& other) :
Width((T)other.Width), Height((T)other.Height) { }
template <class U>
dimension2d<T>& operator=(const dimension2d<U>& other)
{
Width = (T) other.Width;
Height = (T) other.Height;
return *this;
}

dimension2d<T> operator+(const dimension2d<T>& other) const
{
return dimension2d<T>(Width+other.Width, Height+other.Height);
}
T Width;
T Height;
};
void ByValue(dimension2d<float> val)
{
std::cout << "X: " << val.Width << "\n";
std::cout << "Y: " << val.Height << "\n";
}

int main()
{
ByValue(dimension2d<float>(1,2));
return 0;
}

main:
0x86b4 push {r11, lr}
0x86b8 add r11, sp, #4
0x86bc sub sp, sp, #16
0x86c0 ldr r3, [pc, #72] ; 0x8710 <main()+92>
0x86c4 str r3, [r11, #-12]
0x86c8 ldr r3, [pc, #68] ; 0x8714 <main()+96>
0x86cc str r3, [r11, #-8]
0x86d0 sub r1, r11, #20
0x86d4 sub r2, r11, #12
0x86d8 sub r3, r11, #8
0x86dc mov r0, r1
0x86e0 mov r1, r2
0x86e4 mov r2, r3
0x86e8 bl 0x8798 <dimension2d<float>::dimension2d(float const&, float const&)>
0x86ec vldr s14, [r11, #-20] ; 0xffffffec
0x86f0 vldr s15, [r11, #-16]
0x86f4 vmov.f32 s0, s14
0x86f8 vmov.f32 s1, s15
0x86fc bl 0x8620 <ByValue(dimension2d<float>)>
0x8700 mov r3, #0
0x8704 mov r0, r3
0x8708 sub sp, r11, #4
0x870c pop {r11, pc}
0x8710 svccc 0x00800000
0x8714 andmi r0, r0, r0

ByValue:
0x8620 push {r11, lr}
0x8624 add r11, sp, #4
0x8628 sub sp, sp, #8
0x862c vmov.f32 s14, s0
0x8630 vmov.f32 s15, s1
0x8634 vstr s14, [r11, #-12]
0x8638 vstr s15, [r11, #-8]
0x863c ldr r0, [pc, #96] ; 0x86a4 <ByValue(dimension2d<float>)+132>
0x8640 ldr r1, [pc, #96] ; 0x86a8 <ByValue(dimension2d<float>)+136>
0x8644 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8648 mov r3, r0
0x864c vldr s15, [r11, #-12]
0x8650 mov r0, r3
0x8654 vmov.f32 s0, s15
0x8658 bl 0x8568 <std::ostream::operator<<(float)>
0x865c mov r3, r0
0x8660 mov r0, r3
0x8664 ldr r1, [pc, #64] ; 0x86ac <ByValue(dimension2d<float>)+140>
0x8668 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x866c ldr r0, [pc, #48] ; 0x86a4 <ByValue(dimension2d<float>)+132>
0x8670 ldr r1, [pc, #56] ; 0x86b0 <ByValue(dimension2d<float>)+144>
0x8674 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x8678 mov r3, r0
0x867c vldr s15, [r11, #-8]
0x8680 mov r0, r3
0x8684 vmov.f32 s0, s15
0x8688 bl 0x8568 <std::ostream::operator<<(float)>
0x868c mov r3, r0
0x8690 mov r0, r3
0x8694 ldr r1, [pc, #16] ; 0x86ac <ByValue(dimension2d<float>)+140>
0x8698 bl 0x8550 <std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)>
0x869c sub sp, r11, #4
0x86a0 pop {r11, pc}
0x86a4 strdeq r0, [r1], -r0 ; <UNPREDICTABLE>
0x86a8 andeq r8, r0, r4, asr r8
0x86ac andeq r8, r0, r8, asr r8
0x86b0 andeq r8, r0, r12, asr r8


So yes, it is being passed in the "s" registers. So I guess I have to add some logic to the part where the object memory is copied to the buffer to detect if it is an asOBJ_APP_CLASS_ALLFLOATS and if so then I copy the object to the s registers, right? (Taking care, of course, to not overwrite anything that has already been stored in s or d registers - oh god...). What happens if I need more space than registers are available? Use the stack? I guess a disassembly of this case should show me how to proceed in such case.

In this case there´s no need to mess with the "if( sysFunc->hostReturnInMemory )" part, right?
Isn't it fun to figure out how ABIs work? :)

My guess (hope) is that if there is not enough space to fit the entire object in the available registers, the entire object will be pushed on the stack. It would also be necessary to determine if the two floats needs to be aligned on even registers, i.e. similar to a double.

I believe we have already confirmed that if the type is larger than 8 bytes it will not be passed in the registers, even if it is asOBJ_APP_CLASS_ALLFLOATS, but it may be a good idea to do a disassembly of that case too.






Once everything is working I'll do some refactoring on the code you're implementing now. This special treatment for classes with asOBJ_APP_CLASS_ALLFLOATS should be identified in as_config.h, so these checks whether the type is passed in registers or by reference is done at compile time in PrepareSystemFunction, rather than at runtime. But you don't need to worry about that, I'll make this myself later, and I'll just ask you to test it or perhaps I'll do the testing myself if you allow me to use your RPi remotely for this.

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