%************************************************************************ %* * \section[CallWrap_C.lc]{``callWrapper'' stuff that can be written in C} %* * %************************************************************************ \begin{code} #define MAIN_REG_MAP /* These routines are all a bit special */ #define CALLWRAPPER_C /* Don't give standard declarations for wrappers */ #include "rtsdefs.h" \end{code} %************************************************************************ %* * \subsection[call-wrapping]{Routines to ``wrap'' special calls to C} %* * %************************************************************************ In most cases, this requires some assembly-language hacking (see the discussion in @COptWraps.lh@.) \begin{code} #if defined(__STG_GCC_REGS__) # if defined(CALLER_SAVES_SYSTEM) || defined(CALLER_SAVES_USER) void callWrapper(STG_NO_ARGS) { MAGIC_CALL_SETUP CALLER_SAVE_Base CALLER_SAVE_StkO CALLER_SAVE_R1 CALLER_SAVE_R2 CALLER_SAVE_R3 CALLER_SAVE_R4 CALLER_SAVE_R5 CALLER_SAVE_R6 CALLER_SAVE_R7 CALLER_SAVE_R8 CALLER_SAVE_FltReg1 CALLER_SAVE_FltReg2 CALLER_SAVE_FltReg3 CALLER_SAVE_FltReg4 CALLER_SAVE_DblReg1 CALLER_SAVE_DblReg2 CALLER_SAVE_LngReg1 CALLER_SAVE_LngReg2 CALLER_SAVE_Tag CALLER_SAVE_SpA CALLER_SAVE_SuA CALLER_SAVE_SpB CALLER_SAVE_SuB CALLER_SAVE_Hp CALLER_SAVE_HpLim CALLER_SAVE_Liveness CALLER_SAVE_Ret MAGIC_CALL CALLER_RESTORE_Base /* has to be first! */ CALLER_RESTORE_StkO CALLER_RESTORE_R1 CALLER_RESTORE_R2 CALLER_RESTORE_R3 CALLER_RESTORE_R4 CALLER_RESTORE_R5 CALLER_RESTORE_R6 CALLER_RESTORE_R7 CALLER_RESTORE_R8 CALLER_RESTORE_FltReg1 CALLER_RESTORE_FltReg2 CALLER_RESTORE_FltReg3 CALLER_RESTORE_FltReg4 CALLER_RESTORE_DblReg1 CALLER_RESTORE_DblReg2 CALLER_RESTORE_LngReg1 CALLER_RESTORE_LngReg2 CALLER_RESTORE_Tag CALLER_RESTORE_SpA CALLER_RESTORE_SuA CALLER_RESTORE_SpB CALLER_RESTORE_SuB CALLER_RESTORE_Hp CALLER_RESTORE_HpLim CALLER_RESTORE_Liveness CALLER_RESTORE_Ret /* These next two are restore-only */ CALLER_RESTORE_StdUpdRetVec CALLER_RESTORE_StkStub MAGIC_RETURN } # endif /* defined(CALLER_SAVES_SYSTEM) || defined(CALLER_SAVES_USER) */ # if defined(CALLER_SAVES_SYSTEM) void callWrapper_safe(STG_NO_ARGS) { MAGIC_CALL_SETUP CALLER_SAVE_Base CALLER_SAVE_StkO CALLER_SAVE_SpA CALLER_SAVE_SuA CALLER_SAVE_SpB CALLER_SAVE_SuB CALLER_SAVE_Hp CALLER_SAVE_HpLim CALLER_SAVE_Liveness CALLER_SAVE_Ret MAGIC_CALL CALLER_RESTORE_Base /* has to be first! */ CALLER_RESTORE_StkO CALLER_RESTORE_SpA CALLER_RESTORE_SuA CALLER_RESTORE_SpB CALLER_RESTORE_SuB CALLER_RESTORE_Hp CALLER_RESTORE_HpLim CALLER_RESTORE_Liveness CALLER_RESTORE_Ret /* These next two are restore-only */ CALLER_RESTORE_StdUpdRetVec CALLER_RESTORE_StkStub MAGIC_RETURN } # endif /* defined(CALLER_SAVES_SYSTEM) */ /* Nota Bene: Anyone changing the definition of @callWrapper_GC@ should make appropriate changes in the compiler (absCSyn/PprAbsC.lhs :: pprCCall). The reason is that \tr{_ccall_GC_} and \tr{_casm_GC_} generate code like this: \begin{verbatim} { R _ccall_result; SaveAllStgRegs(); inCCallGC+=1; _ccall_result = << do the call/asm>>; inCCallGC-=1; RestoreAllStgRegs(); } \end{verbatim} This avoids limiting _ccall_GC_ to 6 arguments and makes it possible to implement _ccall_GC_. (The local variable avoids the need for some of the deeper magic hidden inside @GC_SETUP@, @GC_CCALL@ and @GC_RETURN@.) ADR */ EXTFUN(EnterNodeCode); void *__temp_esp, *__temp_eax; void PerformGC_wrapper PROTO((W_)) WRAPPER_NAME(PerformGC); void PerformGC_wrapper(args) W_ args; { #if i386_TARGET_ARCH void *ret_addr; WRAPPER_SETUP(PerformGC,ret_addr,args) #else WRAPPER_SETUP(PerformGC, ignore_me, ignore_me) #endif PerformGC(args); WRAPPER_RETURN(0) } # ifdef CONCURRENT void __DISCARD__ (STG_NO_ARGS) { /*nothing*/ } void StackOverflow_wrapper PROTO((W_,W_)) WRAPPER_NAME(StackOverflow); void StackOverflow_wrapper(args1,args2) W_ args1, args2; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(StackOverflow,ret_addr,ignore_me) #else WRAPPER_SETUP(StackOverflow, ignore_me, ignore_me) #endif if(StackOverflow(args1,args2)) { WRAPPER_RETURN(1) } WRAPPER_RETURN(0) } void Yield_wrapper PROTO((W_)) WRAPPER_NAME(Yield); void Yield_wrapper(args) W_ args; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(Yield, ret_addr, ignore_me) #else WRAPPER_SETUP(Yield, ignore_me, ignore_me) #endif Yield(args); WRAPPER_RETURN(0) } #if defined(GRAN) void PerformReschedule_wrapper PROTO((W_, W_)) WRAPPER_NAME(PerformReschedule); void PerformReschedule_wrapper(liveness, always_reenter_node) W_ liveness; W_ always_reenter_node; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(PerformReschedule, ret_addr, ignore_me) #else WRAPPER_SETUP(PerformReschedule, ignore_me, ignore_me) #endif PerformReschedule(liveness, always_reenter_node); WRAPPER_RETURN(0) } /* Similar wrappers for all GrAnSim functions. */ /* NB: These are normal functions, which don't call ReSchedule. So we just */ /* have to safe/restore the registers. */ void GranSimAllocate_wrapper PROTO((I_, P_, W_)) WRAPPER_NAME(GranSimAllocate); void GranSimAllocate_wrapper(n, node, liveness) I_ n; P_ node; W_ liveness; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(GranSimAllocate, ret_addr, ignore_me) #else WRAPPER_SETUP(GranSimAllocate, ignore_me, ignore_me) #endif GranSimAllocate(n, node, liveness); WRAPPER_RETURN(0); } void GranSimUnallocate_wrapper PROTO((I_, P_, W_)) WRAPPER_NAME(GranSimUnallocate); void GranSimUnallocate_wrapper(n, node, liveness) I_ n; P_ node; W_ liveness; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(GranSimUnallocate, ret_addr, ignore_me) #else WRAPPER_SETUP(GranSimUnallocate, ignore_me, ignore_me) #endif GranSimUnallocate(n, node, liveness); WRAPPER_RETURN(0); } void GranSimFetch_wrapper PROTO((P_)) WRAPPER_NAME(GranSimFetch); void GranSimFetch_wrapper(node) P_ node; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(GranSimFetch, ret_addr, ignore_me) #else WRAPPER_SETUP(GranSimFetch, ignore_me, ignore_me) #endif GranSimFetch(node); WRAPPER_RETURN(0); } void GranSimExec_wrapper PROTO((W_, W_, W_, W_, W_)) WRAPPER_NAME(GranSimExec); void GranSimExec_wrapper(arith,branch,load,store,floats) W_ arith,branch,load,store,floats; { #if i386_TARGET_ARCH void *ret_addr, *ignore_me; WRAPPER_SETUP(GranSimExec, ret_addr, ignore_me) #else WRAPPER_SETUP(GranSimExec, ignore_me, ignore_me) #endif GranSimExec(arith,branch,load,store,floats); WRAPPER_RETURN(0); } # endif /* GRAN */ # endif /* CONCURRENT */ /* * In the threaded world, context switches may occur during one of these * wrapped calls, and when we come back, our stack will have been trashed. * If gcc, in all of its cleverness, tries to store any temporary values on * the stack, we need to separate the restoration function. See the sparc * code for an example. */ SEPARATE_WRAPPER_RESTORE #endif /* defined(__STG_GCC_REGS__) */ /* We can perform a runtime check that we have used @_ccall_GC_@ when appropriate using this flag. */ StgInt inCCallGC = 0; void checkInCCallGC() { if (inCCallGC == 0) { fprintf(stderr, "Error: entering a closure from C without using _ccall_GC_\n"); EXIT(EXIT_FAILURE); } } \end{code}