0
votes

I have the following piece of code that works on x86, and I need to convert it to gcc/arm7 inline assembly.

It seems replacing the stack would be a simple matter of moving the stack pointer into r15 instead of esp, but I can't figure out how I would call the function, or get the actual variables into the arm assembly code.

Object *c;

unsigned long _stack = (unsigned long)c->stack + 65535;
void (Object::*_run)() = &Object::_run;

__asm
{
    mov ecx, _this // store pointer to this Object
    mov edx, _run  // store address of _run() function
    mov esp, stack // replace stack pointer with Object's internal stack
    call edx       // call the _run() function
}


edit:

so far, I have this:

unsigned long _stack = (unsigned long)c->stack + 65535;
void (Object::*_run)() = &Object::_run;

asm volatile("mov %[__this], %r0\n\t"
             "mov %[__run], %r5\n\t"
             "mov %[__stack], %%sp\n\t"
             "blx %r5\n\t"
             : /* no output operands */
             : [__stack] "r" (_stack), 
               [__this] "r" (_this), 
               [__run] "r" (_run)
             : /* no clobbers */);

Q: in x86 asm, using thiscall calling convention, the "this" pointer goes in ecx. Where does it go in arm?

edit:

A: For C++, an implicit this parameter is passed as an extra argument that immediately precedes the first user argument. Other rules for marshalling C++ arguments are described in CPPABI.

EDIT:

the full implementation of what I am trying to do is here: http://pastebin.com/6mrUC7td

it works perfectly in visual studio, but I can't get it to work right in XCode/iOS

5

5 Answers

3
votes

The arm abi specifies that the registers r0 to r3 are used for parameter passing. The this pointer is always in the first register, this would be r0.

2
votes

It's not particularly safe to call anything from gcc inline assembler, because there's no way to tell the compiler about it (no constraint to gcc inline asm instruction can express "this thing contains a function call, do all that's necessary to save/restore whatever you keep in registers that may change when making a function call").

You can theoretically do that "manually" by giving a clobber list specifying all registers that are potentially changed when making a function call as per the platform ABI (quite a lot on ARM, I think it's pretty much all of them excluding sp ...). But doing that frequently creates the situation that gcc tells you the constraint cannot be met (i.e. it doesn't have any registers left to stuff "things it needs to preserve" into), depending on the context where your inline asm is used.

Why exactly do you need to call a function from within the inline assembly ? Couldn't you use a static inline function instead that combines ordinary C code (which does the function call) with inline assembly to do what can only be done in inline assembly ?

If the entire function is inline assembly only, gcc on ARM offers a way out - namely, the __attribute__((naked)) option. That tells the compiler not to create function prologues / epilogues for it, and you're obligated to write these yourself, i.e. add code that saves/restores registers used by this function but declared preserved by the EABI. By "outsourcing" your inline assembly that does function calls into a static inline __attribute__((naked)) function, you can get around having to specify extensive clobber lists, as the compiler will recognize it's a function call (and preserve/restore as necessary around it, even when inlining the code).

0
votes

Take a look at this. Although it uses x86 assembly, the same works for arm assembly.

0
votes

I'm not quite sure what you're doing with the stack pointer there - is this an x86 idiom that you don't need on ARM or are you intentionally trying to set up a new stack pointer?

In any case, the stack pointer is in r13 - the Program counter is r15 - LDR or MOV into PC effects a branch. Outside the function prologue, SP already points to the next location on the stack, so you'd only ever need to modify it if you needed to pass arguments on the stack due to the registers r0-r3 not being sufficient - which is not the case here.

If you do modify the stack pointer, you need to restore it after the function call or you'll get a nice little crash on exit of your function.

Finally, you will need to declare all registers that the ARM [E]ABI allows callees to modify on function call as clobbered. This is r0-r3, r9 {maybe} and r12.

If you're doing this on iOS, ISTR there is subtle variation on register usage.

0
votes

Easy solution to get the required info for ABI (like in which registers gets what value passed): Just write a couple of lines dummy code where a c++ method is called, and disassemble the result (on your target platform).