/* ----------------------------------------------------------------------------- * Alpha architecture adjustor thunk logic. * ---------------------------------------------------------------------------*/ #include "PosixSource.h" #include "Rts.h" #include "RtsUtils.h" #include "StablePtr.h" /* To get the definition of PAL_imb: */ #if defined(linux_HOST_OS) # include #else # include #endif void* createAdjustor(int cconv, StgStablePtr hptr, StgFunPtr wptr, char *typeString STG_UNUSED ) { switch (cconv) { case 1: /* _ccall */ /* Magic constant computed by inspecting the code length of the following assembly language snippet (offset and machine code prefixed; note that the machine code shown is longwords stored in little-endian order): <00>: 46520414 mov a2, a4 <04>: 46100412 mov a0, a2 <08>: a61b0020 ldq a0, 0x20(pv) # load up hptr <0c>: 46730415 mov a3, a5 <10>: a77b0028 ldq pv, 0x28(pv) # load up wptr <14>: 46310413 mov a1, a3 <18>: 6bfb---- jmp (pv), # jump to wptr (with hint) <1c>: 00000000 # padding for alignment <20>: [8 bytes for hptr quadword] <28>: [8 bytes for wptr quadword] The "computed" jump at <08> above is really a jump to a fixed location. Accordingly, we place an always-correct hint in the jump instruction, namely the address offset from <0c> to wptr, divided by 4, taking the lowest 14 bits. We only support passing 4 or fewer argument words, for the same reason described under sparc_HOST_ARCH above by JRS, 21 Aug 01. On the Alpha the first 6 integer arguments are in a0 through a5, and the rest on the stack. Hence we want to shuffle the original caller's arguments by two. On the Alpha the calling convention is so complex and dependent on the callee's signature -- for example, the stack pointer has to be a multiple of 16 -- that it seems impossible to me [ccshan] to handle the general case correctly without changing how the adjustor is called from C. For now, our solution of shuffling registers only and ignoring the stack only works if the original caller passed 4 or fewer argument words. TODO: Depending on how much allocation overhead stgMallocBytes uses for header information (more precisely, if the overhead is no more than 4 bytes), we should move the first three instructions above down by 4 bytes (getting rid of the nop), hence saving memory. [ccshan] */ { ASSERT(((StgWord64)wptr & 3) == 0); ExecPage *page = allocateExecPage(); StgWord64 *const code = (StgWord64 *) page; code[0] = 0x4610041246520414L; code[1] = 0x46730415a61b0020L; code[2] = 0x46310413a77b0028L; code[3] = 0x000000006bfb0000L | (((StgWord32*)(wptr) - (StgWord32*)(code) - 3) & 0x3fff); code[4] = (StgWord64)hptr; code[5] = (StgWord64)wptr; freezeExecPage(page); /* Ensure that instruction cache is consistent with our new code */ __asm__ volatile("call_pal %0" : : "i" (PAL_imb)); return code; } default: barf("createAdjustor: Unsupported calling convention"); } } void freeHaskellFunctionPtr(void* ptr) { if ( *(StgWord64*)ptr != 0xa77b0018a61b0010L ) { errorBelch("freeHaskellFunctionPtr: not for me, guv! %p\n", ptr); return; } /* Free the stable pointer first..*/ freeStablePtr(*((StgStablePtr*)((unsigned char*)ptr + 0x10))); freeExecPage((ExecPage *) ptr); }