[LLVMdev] Request for merge: GHC/ARM calling convention.

Karel Gardas karel.gardas at centrum.cz
Fri Jun 29 14:56:40 PDT 2012


On 06/29/12 11:12 PM, Renato Golin wrote:
> On 29 June 2012 17:46, Karel Gardas<karel.gardas at centrum.cz>  wrote:
>> Yes and no. Shortly: original GHC/ARM/LLVM port was done by Stephen on
>> ARMv5/Qemu IIRC. I've later added whole VFP support and ARMv7 support. The
>> code in GHC is properly #ifdefed, so if there is no VFP available on pre
>> ARMv6, then it's not used. ie. GHC STG floating points regs are then
>> allocated in RAM instead of real hardware regs.
>
> That's fine. As long as you don't try to interoperate with EABI libraries... ;)

You makes me worried! Let me ask, am I wrong assuming Ubuntu 
11.04/11.10/12.04 are using EABI? That's at least what I thought all the 
time...

Anyway, if they are EABI, then GHC/ARM needs to interoperate well with 
EABI libs as Haskell code is using them too.

For registerised GHC build there are two important functions provided in 
GHC's RTS written in C (or gcc inline asm compiled by gcc still using 
platform ABI) which performs important task of bridging C world ABI and 
GHC own ABI. It's StgRun and StgReturn. StgRun calls Haskell (STG) 
function from the C world and StgReturn return from the Haskell world 
into C world again safely. They are defined in rts/StgCRun.c and the 
comment from the top of the file might be more useful here than my 
writing about it:

  * STG-to-C glue.
  *
  * To run an STG function from C land, call
  *
  *              rv = StgRun(f,BaseReg);
  *
  * where "f" is the STG function to call, and BaseReg is the address of the
  * RegTable for this run (we might have separate RegTables if we're running
  * multiple threads on an SMP machine).
  *
  * In the end, "f" must JMP to StgReturn (defined below), passing the
  * return-value "rv" in R1, to return to the caller of StgRun returning 
"rv" in
  * the whatever way C returns a value.
  *
  * NOTE: StgRun/StgReturn do *NOT* load or store Hp or any other registers
  * (other than saving the C callee-saves registers). Instead, the called
  * function "f" must do that in STG land.
  *
  * We also initially make sure that there are @RESERVED_C_STACK_BYTES@ 
on the
  * C-stack. This is done to reserve some space for the allocation of
  * temporaries in STG code.


If you are then curious how ARM-specific StgRun implementation looks, 
then here it is:

#ifdef arm_HOST_ARCH

#if defined(__thumb__)
#define THUMB_FUNC ".thumb\n\t.thumb_func\n\t"
#else
#define THUMB_FUNC
#endif

StgRegTable *
StgRun(StgFunPtr f, StgRegTable *basereg) {
     StgRegTable * r;
     __asm__ volatile (
         /*
          * save callee-saves registers on behalf of the STG code.
          */
         "stmfd sp!, {r4-r10, fp, ip, lr}\n\t"
#if !defined(arm_HOST_ARCH_PRE_ARMv6)
         "vstmdb sp!, {d8-d11}\n\t"
#endif
         /*
          * allocate some space for Stg machine's temporary storage.
          * Note: RESERVER_C_STACK_BYTES has to be a round number here or
          * the assembler can't assemble it.
          */
         "sub sp, sp, %3\n\t"
         /*
          * Set BaseReg
          */
         "mov r4, %2\n\t"
         /*
          * Jump to function argument.
          */
         "bx %1\n\t"

         ".global " STG_RETURN "\n\t"
         THUMB_FUNC
         ".type " STG_RETURN ", %%function\n"
         STG_RETURN ":\n\t"
         /*
          * Free the space we allocated
          */
         "add sp, sp, %3\n\t"
         /*
          * Return the new register table, taking it from Stg's R1 
(ARM's R7).
          */
         "mov %0, r7\n\t"
         /*
          * restore callee-saves registers.
          */
#if !defined(arm_HOST_ARCH_PRE_ARMv6)
         "vldmia sp!, {d8-d11}\n\t"
#endif
         "ldmfd sp!, {r4-r10, fp, ip, lr}\n\t"
       : "=r" (r)
       : "r" (f), "r" (basereg), "i" (RESERVED_C_STACK_BYTES)
#if !defined(__thumb__)
         /* In ARM mode, r11/fp is frame-pointer and so we cannot mark
            it as clobbered. If we do so, GCC complains with error. */
       : "%r4", "%r5", "%r6", "%r7", "%r8", "%r9", "%r10", "%ip", "%lr"
#else
         /* In Thumb mode r7 is frame-pointer and so we cannot mark it
            as clobbered. On the other hand we mark as clobbered also
            those regs not used in Thumb mode. Hard to judge if this is
            needed, but certainly Haskell code is using them for
            placing GHC's virtual registers there. See
            includes/stg/MachRegs.h Please note that Haskell code is
            compiled by GHC/LLVM into ARM code (not Thumb!), at least
            as of February 2012 */
       : "%r4", "%r5", "%r6", "%r8", "%r9", "%r10", "%fp", "%ip", "%lr"
#endif
     );
     return r;
}
#endif


Please see https://github.com/ghc/ghc/blob/master/rts/StgCRun.c -- if 
you are interested to see implementations of that function for another 
architectures (x86/amd64/ppc/...)

Honestly speaking I don't know if I'm making things more clear or more 
confused with this but I hope you can somehow distill it and tell me if 
there is anything wrong with our ARM/GHC support then.

Thanks a lot!
Karel



More information about the llvm-dev mailing list