diff options
Diffstat (limited to 'rts/HeapStackCheck.cmm')
-rw-r--r-- | rts/HeapStackCheck.cmm | 964 |
1 files changed, 964 insertions, 0 deletions
diff --git a/rts/HeapStackCheck.cmm b/rts/HeapStackCheck.cmm new file mode 100644 index 0000000000..4e5dd24596 --- /dev/null +++ b/rts/HeapStackCheck.cmm @@ -0,0 +1,964 @@ +/* ----------------------------------------------------------------------------- + * + * (c) The GHC Team, 1998-2004 + * + * Canned Heap-Check and Stack-Check sequences. + * + * This file is written in a subset of C--, extended with various + * features specific to GHC. It is compiled by GHC directly. For the + * syntax of .cmm files, see the parser in ghc/compiler/cmm/CmmParse.y. + * + * ---------------------------------------------------------------------------*/ + +#include "Cmm.h" + +/* Stack/Heap Check Failure + * ------------------------ + * + * On discovering that a stack or heap check has failed, we do the following: + * + * - If the context_switch flag is set, indicating that there are more + * threads waiting to run, we yield to the scheduler + * (return ThreadYielding). + * + * - If Hp > HpLim, we've had a heap check failure. This means we've + * come to the end of the current heap block, so we try to chain + * another block on with ExtendNursery(). + * + * - If this succeeds, we carry on without returning to the + * scheduler. + * + * - If it fails, we return to the scheduler claiming HeapOverflow + * so that a garbage collection can be performed. + * + * - If Hp <= HpLim, it must have been a stack check that failed. In + * which case, we return to the scheduler claiming StackOverflow, the + * scheduler will either increase the size of our stack, or raise + * an exception if the stack is already too big. + * + * The effect of checking for context switch only in the heap/stack check + * failure code is that we'll switch threads after the current thread has + * reached the end of its heap block. If a thread isn't allocating + * at all, it won't yield. Hopefully this won't be a problem in practice. + */ + +#define PRE_RETURN(why,what_next) \ + StgTSO_what_next(CurrentTSO) = what_next::I16; \ + StgRegTable_rRet(BaseReg) = why; \ + R1 = BaseReg; + +/* Remember that the return address is *removed* when returning to a + * ThreadRunGHC thread. + */ + +#define GC_GENERIC \ + DEBUG_ONLY(foreign "C" heapCheckFail()); \ + if (Hp > HpLim) { \ + Hp = Hp - HpAlloc/*in bytes*/; \ + if (HpAlloc <= BLOCK_SIZE \ + && bdescr_link(CurrentNursery) != NULL) { \ + CLOSE_NURSERY(); \ + CurrentNursery = bdescr_link(CurrentNursery); \ + OPEN_NURSERY(); \ + if (CInt[context_switch] != 0 :: CInt) { \ + R1 = ThreadYielding; \ + goto sched; \ + } else { \ + jump %ENTRY_CODE(Sp(0)); \ + } \ + } else { \ + R1 = HeapOverflow; \ + goto sched; \ + } \ + } else { \ + R1 = StackOverflow; \ + } \ + sched: \ + PRE_RETURN(R1,ThreadRunGHC); \ + jump stg_returnToSched; + +#define HP_GENERIC \ + PRE_RETURN(HeapOverflow, ThreadRunGHC) \ + jump stg_returnToSched; + +#define BLOCK_GENERIC \ + PRE_RETURN(ThreadBlocked, ThreadRunGHC) \ + jump stg_returnToSched; + +#define YIELD_GENERIC \ + PRE_RETURN(ThreadYielding, ThreadRunGHC) \ + jump stg_returnToSched; + +#define BLOCK_BUT_FIRST(c) \ + PRE_RETURN(ThreadBlocked, ThreadRunGHC) \ + R2 = c; \ + jump stg_returnToSchedButFirst; + +#define YIELD_TO_INTERPRETER \ + PRE_RETURN(ThreadYielding, ThreadInterpret) \ + jump stg_returnToSchedNotPaused; + +/* ----------------------------------------------------------------------------- + Heap checks in thunks/functions. + + In these cases, node always points to the function closure. This gives + us an easy way to return to the function: just leave R1 on the top of + the stack, and have the scheduler enter it to return. + + There are canned sequences for 'n' pointer values in registers. + -------------------------------------------------------------------------- */ + +INFO_TABLE_RET( stg_enter, 1/*framesize*/, 0/*bitmap*/, RET_SMALL) +{ + R1 = Sp(1); + Sp_adj(2); + ENTER(); +} + +__stg_gc_enter_1 +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_enter_info; + GC_GENERIC +} + +#if defined(GRAN) +/* + ToDo: merge the block and yield macros, calling something like BLOCK(N) + at the end; +*/ + +/* + Should we actually ever do a yield in such a case?? -- HWL +*/ +gran_yield_0 +{ + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +gran_yield_1 +{ + Sp_adj(-1); + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 2 Regs--------------------------------------------------------------------*/ + +gran_yield_2 +{ + Sp_adj(-2); + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 3 Regs -------------------------------------------------------------------*/ + +gran_yield_3 +{ + Sp_adj(-3); + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 4 Regs -------------------------------------------------------------------*/ + +gran_yield_4 +{ + Sp_adj(-4); + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 5 Regs -------------------------------------------------------------------*/ + +gran_yield_5 +{ + Sp_adj(-5); + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 6 Regs -------------------------------------------------------------------*/ + +gran_yield_6 +{ + Sp_adj(-6); + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 7 Regs -------------------------------------------------------------------*/ + +gran_yield_7 +{ + Sp_adj(-7); + Sp(6) = R7; + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +/*- 8 Regs -------------------------------------------------------------------*/ + +gran_yield_8 +{ + Sp_adj(-8); + Sp(7) = R8; + Sp(6) = R7; + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadYielding; + jump StgReturn; +} + +// the same routines but with a block rather than a yield + +gran_block_1 +{ + Sp_adj(-1); + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 2 Regs--------------------------------------------------------------------*/ + +gran_block_2 +{ + Sp_adj(-2); + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 3 Regs -------------------------------------------------------------------*/ + +gran_block_3 +{ + Sp_adj(-3); + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 4 Regs -------------------------------------------------------------------*/ + +gran_block_4 +{ + Sp_adj(-4); + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 5 Regs -------------------------------------------------------------------*/ + +gran_block_5 +{ + Sp_adj(-5); + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 6 Regs -------------------------------------------------------------------*/ + +gran_block_6 +{ + Sp_adj(-6); + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 7 Regs -------------------------------------------------------------------*/ + +gran_block_7 +{ + Sp_adj(-7); + Sp(6) = R7; + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +/*- 8 Regs -------------------------------------------------------------------*/ + +gran_block_8 +{ + Sp_adj(-8); + Sp(7) = R8; + Sp(6) = R7; + Sp(5) = R6; + Sp(4) = R5; + Sp(3) = R4; + Sp(2) = R3; + Sp(1) = R2; + Sp(0) = R1; + SAVE_THREAD_STATE(); + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +#endif + +#if 0 && defined(PAR) + +/* + Similar to stg_block_1 (called via StgMacro BLOCK_NP) but separates the + saving of the thread state from the actual jump via an StgReturn. + We need this separation because we call RTS routines in blocking entry codes + before jumping back into the RTS (see parallel/FetchMe.hc). +*/ + +par_block_1_no_jump +{ + Sp_adj(-1); + Sp(0) = R1; + SAVE_THREAD_STATE(); +} + +par_jump +{ + TSO_what_next(CurrentTSO) = ThreadRunGHC; + R1 = ThreadBlocked; + jump StgReturn; +} + +#endif + +/* ----------------------------------------------------------------------------- + Heap checks in Primitive case alternatives + + A primitive case alternative is entered with a value either in + R1, FloatReg1 or D1 depending on the return convention. All the + cases are covered below. + -------------------------------------------------------------------------- */ + +/*-- No Registers live ------------------------------------------------------ */ + +stg_gc_noregs +{ + GC_GENERIC +} + +/*-- void return ------------------------------------------------------------ */ + +INFO_TABLE_RET( stg_gc_void, 0/*framesize*/, 0/*bitmap*/, RET_SMALL) +{ + Sp_adj(1); + jump %ENTRY_CODE(Sp(0)); +} + +/*-- R1 is boxed/unpointed -------------------------------------------------- */ + +INFO_TABLE_RET( stg_gc_unpt_r1, 1/*framesize*/, 0/*bitmap*/, RET_SMALL) +{ + R1 = Sp(1); + Sp_adj(2); + jump %ENTRY_CODE(Sp(0)); +} + +stg_gc_unpt_r1 +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_gc_unpt_r1_info; + GC_GENERIC +} + +/*-- R1 is unboxed -------------------------------------------------- */ + +/* the 1 is a bitmap - i.e. 1 non-pointer word on the stack. */ +INFO_TABLE_RET( stg_gc_unbx_r1, 1/*framesize*/, 1/*bitmap*/, RET_SMALL ) +{ + R1 = Sp(1); + Sp_adj(2); + jump %ENTRY_CODE(Sp(0)); +} + +stg_gc_unbx_r1 +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_gc_unbx_r1_info; + GC_GENERIC +} + +/*-- F1 contains a float ------------------------------------------------- */ + +INFO_TABLE_RET( stg_gc_f1, 1/*framesize*/, 1/*bitmap*/, RET_SMALL ) +{ + F1 = F_[Sp+WDS(1)]; + Sp_adj(2); + jump %ENTRY_CODE(Sp(0)); +} + +stg_gc_f1 +{ + Sp_adj(-2); + F_[Sp + WDS(1)] = F1; + Sp(0) = stg_gc_f1_info; + GC_GENERIC +} + +/*-- D1 contains a double ------------------------------------------------- */ + +/* we support doubles of either 1 or 2 words in size */ + +#if SIZEOF_DOUBLE == SIZEOF_VOID_P +# define DBL_BITMAP 1 +# define DBL_WORDS 1 +#else +# define DBL_BITMAP 3 +# define DBL_WORDS 2 +#endif + +INFO_TABLE_RET( stg_gc_d1, DBL_WORDS/*framesize*/, DBL_BITMAP/*bitmap*/, RET_SMALL ) +{ + D1 = D_[Sp + WDS(1)]; + Sp = Sp + WDS(1) + SIZEOF_StgDouble; + jump %ENTRY_CODE(Sp(0)); +} + +stg_gc_d1 +{ + Sp = Sp - WDS(1) - SIZEOF_StgDouble; + D_[Sp + WDS(1)] = D1; + Sp(0) = stg_gc_d1_info; + GC_GENERIC +} + + +/*-- L1 contains an int64 ------------------------------------------------- */ + +/* we support int64s of either 1 or 2 words in size */ + +#if SIZEOF_VOID_P == 8 +# define LLI_BITMAP 1 +# define LLI_WORDS 1 +#else +# define LLI_BITMAP 3 +# define LLI_WORDS 2 +#endif + +INFO_TABLE_RET( stg_gc_l1, LLI_WORDS/*framesize*/, LLI_BITMAP/*bitmap*/, RET_SMALL ) +{ + L1 = L_[Sp + WDS(1)]; + Sp_adj(1) + SIZEOF_StgWord64; + jump %ENTRY_CODE(Sp(0)); +} + +stg_gc_l1 +{ + Sp_adj(-1) - SIZEOF_StgWord64; + L_[Sp + WDS(1)] = L1; + Sp(0) = stg_gc_l1_info; + GC_GENERIC +} + +/*-- Unboxed tuple return, one pointer (unregisterised build only) ---------- */ + +INFO_TABLE_RET( stg_ut_1_0_unreg, 1/*size*/, 0/*BITMAP*/, RET_SMALL ) +{ + Sp_adj(1); + // one ptr is on the stack (Sp(0)) + jump %ENTRY_CODE(Sp(1)); +} + +/* ----------------------------------------------------------------------------- + Generic function entry heap check code. + + At a function entry point, the arguments are as per the calling convention, + i.e. some in regs and some on the stack. There may or may not be + a pointer to the function closure in R1 - if there isn't, then the heap + check failure code in the function will arrange to load it. + + The function's argument types are described in its info table, so we + can just jump to this bit of generic code to save away all the + registers and return to the scheduler. + + This code arranges the stack like this: + + | .... | + | args | + +---------------------+ + | f_closure | + +---------------------+ + | size | + +---------------------+ + | stg_gc_fun_info | + +---------------------+ + + The size is the number of words of arguments on the stack, and is cached + in the frame in order to simplify stack walking: otherwise the size of + this stack frame would have to be calculated by looking at f's info table. + + -------------------------------------------------------------------------- */ + +__stg_gc_fun +{ + W_ size; + W_ info; + W_ type; + + info = %GET_FUN_INFO(R1); + + // cache the size + type = TO_W_(StgFunInfoExtra_fun_type(info)); + if (type == ARG_GEN) { + size = BITMAP_SIZE(StgFunInfoExtra_bitmap(info)); + } else { + if (type == ARG_GEN_BIG) { +#ifdef TABLES_NEXT_TO_CODE + // bitmap field holds an offset + size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info) + + %GET_ENTRY(R1) /* ### */ ); +#else + size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info) ); +#endif + } else { + size = BITMAP_SIZE(W_[stg_arg_bitmaps + WDS(type)]); + } + } + +#ifdef NO_ARG_REGS + // we don't have to save any registers away + Sp_adj(-3); + Sp(2) = R1; + Sp(1) = size; + Sp(0) = stg_gc_fun_info; + GC_GENERIC +#else + W_ type; + type = TO_W_(StgFunInfoExtra_fun_type(info)); + // cache the size + if (type == ARG_GEN || type == ARG_GEN_BIG) { + // regs already saved by the heap check code + Sp_adj(-3); + Sp(2) = R1; + Sp(1) = size; + Sp(0) = stg_gc_fun_info; + // DEBUG_ONLY(foreign "C" debugBelch("stg_fun_gc_gen(ARG_GEN)");); + GC_GENERIC + } else { + jump W_[stg_stack_save_entries + WDS(type)]; + // jumps to stg_gc_noregs after saving stuff + } +#endif /* !NO_ARG_REGS */ +} + +/* ----------------------------------------------------------------------------- + Generic Apply (return point) + + The dual to stg_fun_gc_gen (above): this fragment returns to the + function, passing arguments in the stack and in registers + appropriately. The stack layout is given above. + -------------------------------------------------------------------------- */ + +INFO_TABLE_RET( stg_gc_fun, 0/*framesize*/, 0/*bitmap*/, RET_FUN ) +{ + R1 = Sp(2); + Sp_adj(3); +#ifdef NO_ARG_REGS + // Minor optimisation: there are no argument registers to load up, + // so we can just jump straight to the function's entry point. + jump %GET_ENTRY(R1); +#else + W_ info; + W_ type; + + info = %GET_FUN_INFO(R1); + type = TO_W_(StgFunInfoExtra_fun_type(info)); + if (type == ARG_GEN || type == ARG_GEN_BIG) { + jump StgFunInfoExtra_slow_apply(info); + } else { + if (type == ARG_BCO) { + // cover this case just to be on the safe side + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_apply_interp_info; + jump stg_yield_to_interpreter; + } else { + jump W_[stg_ap_stack_entries + WDS(type)]; + } + } +#endif +} + +/* ----------------------------------------------------------------------------- + Generic Heap Check Code. + + Called with Liveness mask in R9, Return address in R10. + Stack must be consistent (containing all necessary info pointers + to relevant SRTs). + + See StgMacros.h for a description of the RET_DYN stack frame. + + We also define an stg_gen_yield here, because it's very similar. + -------------------------------------------------------------------------- */ + +// For simplicity, we assume that SIZEOF_DOUBLE == 2*SIZEOF_VOID_P +// on a 64-bit machine, we'll end up wasting a couple of words, but +// it's not a big deal. + +#define RESTORE_EVERYTHING \ + L1 = L_[Sp + WDS(19)]; \ + D2 = D_[Sp + WDS(17)]; \ + D1 = D_[Sp + WDS(15)]; \ + F4 = F_[Sp + WDS(14)]; \ + F3 = F_[Sp + WDS(13)]; \ + F2 = F_[Sp + WDS(12)]; \ + F1 = F_[Sp + WDS(11)]; \ + R8 = Sp(10); \ + R7 = Sp(9); \ + R6 = Sp(8); \ + R5 = Sp(7); \ + R4 = Sp(6); \ + R3 = Sp(5); \ + R2 = Sp(4); \ + R1 = Sp(3); \ + Sp_adj(21); + +#define RET_OFFSET (-19) + +#define SAVE_EVERYTHING \ + Sp_adj(-21); \ + L_[Sp + WDS(19)] = L1; \ + D_[Sp + WDS(17)] = D2; \ + D_[Sp + WDS(15)] = D1; \ + F_[Sp + WDS(14)] = F4; \ + F_[Sp + WDS(13)] = F3; \ + F_[Sp + WDS(12)] = F2; \ + F_[Sp + WDS(11)] = F1; \ + Sp(10) = R8; \ + Sp(9) = R7; \ + Sp(8) = R6; \ + Sp(7) = R5; \ + Sp(6) = R4; \ + Sp(5) = R3; \ + Sp(4) = R2; \ + Sp(3) = R1; \ + Sp(2) = R10; /* return address */ \ + Sp(1) = R9; /* liveness mask */ \ + Sp(0) = stg_gc_gen_info; + +INFO_TABLE_RET( stg_gc_gen, 0/*framesize*/, 0/*bitmap*/, RET_DYN ) +/* bitmap in the above info table is unused, the real one is on the stack. */ +{ + RESTORE_EVERYTHING; + jump Sp(RET_OFFSET); /* No %ENTRY_CODE( - this is an actual code ptr */ +} + +stg_gc_gen +{ + SAVE_EVERYTHING; + GC_GENERIC +} + +// A heap check at an unboxed tuple return point. The return address +// is on the stack, and we can find it by using the offsets given +// to us in the liveness mask. +stg_gc_ut +{ + R10 = %ENTRY_CODE(Sp(RET_DYN_NONPTRS(R9) + RET_DYN_PTRS(R9))); + SAVE_EVERYTHING; + GC_GENERIC +} + +/* + * stg_gen_hp is used by MAYBE_GC, where we can't use GC_GENERIC + * because we've just failed doYouWantToGC(), not a standard heap + * check. GC_GENERIC would end up returning StackOverflow. + */ +stg_gc_gen_hp +{ + SAVE_EVERYTHING; + HP_GENERIC +} + +/* ----------------------------------------------------------------------------- + Yields + -------------------------------------------------------------------------- */ + +stg_gen_yield +{ + SAVE_EVERYTHING; + YIELD_GENERIC +} + +stg_yield_noregs +{ + YIELD_GENERIC; +} + +/* ----------------------------------------------------------------------------- + Yielding to the interpreter... top of stack says what to do next. + -------------------------------------------------------------------------- */ + +stg_yield_to_interpreter +{ + YIELD_TO_INTERPRETER; +} + +/* ----------------------------------------------------------------------------- + Blocks + -------------------------------------------------------------------------- */ + +stg_gen_block +{ + SAVE_EVERYTHING; + BLOCK_GENERIC; +} + +stg_block_noregs +{ + BLOCK_GENERIC; +} + +stg_block_1 +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_enter_info; + BLOCK_GENERIC; +} + +/* ----------------------------------------------------------------------------- + * takeMVar/putMVar-specific blocks + * + * Stack layout for a thread blocked in takeMVar: + * + * ret. addr + * ptr to MVar (R1) + * stg_block_takemvar_info + * + * Stack layout for a thread blocked in putMVar: + * + * ret. addr + * ptr to Value (R2) + * ptr to MVar (R1) + * stg_block_putmvar_info + * + * See PrimOps.hc for a description of the workings of take/putMVar. + * + * -------------------------------------------------------------------------- */ + +INFO_TABLE_RET( stg_block_takemvar, 1/*framesize*/, 0/*bitmap*/, RET_SMALL ) +{ + R1 = Sp(1); + Sp_adj(2); + jump takeMVarzh_fast; +} + +// code fragment executed just before we return to the scheduler +stg_block_takemvar_finally +{ +#ifdef THREADED_RTS + foreign "C" unlockClosure(R3 "ptr", stg_EMPTY_MVAR_info); +#endif + jump StgReturn; +} + +stg_block_takemvar +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_block_takemvar_info; + R3 = R1; + BLOCK_BUT_FIRST(stg_block_takemvar_finally); +} + +INFO_TABLE_RET( stg_block_putmvar, 2/*framesize*/, 0/*bitmap*/, RET_SMALL ) +{ + R2 = Sp(2); + R1 = Sp(1); + Sp_adj(3); + jump putMVarzh_fast; +} + +// code fragment executed just before we return to the scheduler +stg_block_putmvar_finally +{ +#ifdef THREADED_RTS + foreign "C" unlockClosure(R3 "ptr", stg_FULL_MVAR_info); +#endif + jump StgReturn; +} + +stg_block_putmvar +{ + Sp_adj(-3); + Sp(2) = R2; + Sp(1) = R1; + Sp(0) = stg_block_putmvar_info; + R3 = R1; + BLOCK_BUT_FIRST(stg_block_putmvar_finally); +} + +// code fragment executed just before we return to the scheduler +stg_block_blackhole_finally +{ +#if defined(THREADED_RTS) + // The last thing we do is release sched_lock, which is + // preventing other threads from accessing blackhole_queue and + // picking up this thread before we are finished with it. + foreign "C" RELEASE_LOCK(sched_mutex "ptr"); +#endif + jump StgReturn; +} + +stg_block_blackhole +{ + Sp_adj(-2); + Sp(1) = R1; + Sp(0) = stg_enter_info; + BLOCK_BUT_FIRST(stg_block_blackhole_finally); +} + +#ifdef mingw32_HOST_OS +INFO_TABLE_RET( stg_block_async, 0/*framesize*/, 0/*bitmap*/, RET_SMALL ) +{ + W_ ares; + W_ len, errC; + + ares = StgTSO_block_info(CurrentTSO); + len = StgAsyncIOResult_len(ares); + errC = StgAsyncIOResult_errCode(ares); + StgTSO_block_info(CurrentTSO) = NULL; + foreign "C" free(ares "ptr"); + R1 = len; + Sp(0) = errC; + jump %ENTRY_CODE(Sp(1)); +} + +stg_block_async +{ + Sp_adj(-1); + Sp(0) = stg_block_async_info; + BLOCK_GENERIC; +} + +/* Used by threadDelay implementation; it would be desirable to get rid of + * this free()'ing void return continuation. + */ +INFO_TABLE_RET( stg_block_async_void, 0/*framesize*/, 0/*bitmap*/, RET_SMALL ) +{ + W_ ares; + + ares = StgTSO_block_info(CurrentTSO); + StgTSO_block_info(CurrentTSO) = NULL; + foreign "C" free(ares "ptr"); + Sp_adj(1); + jump %ENTRY_CODE(Sp(0)); +} + +stg_block_async_void +{ + Sp_adj(-1); + Sp(0) = stg_block_async_void_info; + BLOCK_GENERIC; +} + +#endif + +/* ----------------------------------------------------------------------------- + STM-specific waiting + -------------------------------------------------------------------------- */ + +stg_block_stmwait_finally +{ + foreign "C" stmWaitUnlock(MyCapability() "ptr", R3 "ptr"); + jump StgReturn; +} + +stg_block_stmwait +{ + BLOCK_BUT_FIRST(stg_block_stmwait_finally); +} |