1 /* -----------------------------------------------------------------------------
2 * $Id: HeapStackCheck.hc,v 1.8 1999/05/24 10:58:09 simonmar Exp $
4 * (c) The GHC Team, 1998-1999
6 * Canned Heap-Check and Stack-Check sequences.
8 * ---------------------------------------------------------------------------*/
11 #include "Storage.h" /* for CurrentTSO */
12 #include "StgRun.h" /* for StgReturn and register saving */
13 #include "Schedule.h" /* for context_switch */
14 #include "HeapStackCheck.h"
16 /* Stack/Heap Check Failure
17 * ------------------------
19 * On discovering that a stack or heap check has failed, we do the following:
21 * - If the context_switch flag is set, indicating that there are more
22 * threads waiting to run, we yield to the scheduler
23 * (return ThreadYeilding).
25 * - If Hp > HpLim, we've had a heap check failure. This means we've
26 * come to the end of the current heap block, so we try to chain
27 * another block on with ExtendNursery().
29 * - If this succeeds, we carry on without returning to the
32 * - If it fails, we return to the scheduler claiming HeapOverflow
33 * so that a garbage collection can be performed.
35 * - If Hp <= HpLim, it must have been a stack check that failed. In
36 * which case, we return to the scheduler claiming StackOverflow, the
37 * scheduler will either increase the size of our stack, or flag
38 * an error if the stack is already too big.
40 * The effect of checking for context switch only in the heap/stack check
41 * failure code is that we'll switch threads after the current thread has
42 * reached the end of its heap block. If a thread isn't allocating
43 * at all, it won't yield. Hopefully this won't be a problem in practice.
46 /* Remember that the return address is *removed* when returning to a
47 * ThreadRunGHC thread.
53 if (ExtendNursery(Hp,HpLim)) { \
54 if (context_switch) { \
55 R1.i = ThreadYielding; \
58 JMP_(ENTRY_CODE(Sp[-1])); \
61 R1.i = HeapOverflow; \
64 R1.i = StackOverflow; \
67 CurrentTSO->whatNext = ThreadRunGHC; \
72 if (ExtendNursery(Hp,HpLim)) { \
73 if (context_switch) { \
74 R1.i = ThreadYielding; \
78 JMP_(ENTRY_CODE(*R1.p)); \
81 R1.i = HeapOverflow; \
84 R1.i = StackOverflow; \
87 CurrentTSO->whatNext = ThreadEnterGHC; \
92 CurrentTSO->whatNext = ThreadRunGHC; \
93 R1.i = HeapOverflow; \
98 CurrentTSO->whatNext = ThreadRunGHC; \
99 R1.i = StackOverflow; \
102 #define YIELD_GENERIC \
104 CurrentTSO->whatNext = ThreadRunGHC; \
105 R1.i = ThreadYielding; \
108 #define YIELD_TO_HUGS \
110 CurrentTSO->whatNext = ThreadEnterHugs; \
111 R1.i = ThreadYielding; \
114 #define BLOCK_GENERIC \
116 CurrentTSO->whatNext = ThreadRunGHC; \
117 R1.i = ThreadBlocked; \
120 #define BLOCK_ENTER \
122 CurrentTSO->whatNext = ThreadEnterGHC;\
123 R1.i = ThreadBlocked; \
126 /* -----------------------------------------------------------------------------
128 -------------------------------------------------------------------------- */
131 * This one is used when we want to *enter* the top thing on the stack
132 * when we return, instead of the just returning to an address. See
133 * UpdatePAP for an example.
136 EXTFUN(stg_gc_entertop)
143 /* -----------------------------------------------------------------------------
144 Heap checks in non-top-level thunks/functions.
146 In these cases, node always points to the function closure. This gives
147 us an easy way to return to the function: just leave R1 on the top of
148 the stack, and have the scheduler enter it to return.
150 There are canned sequences for 'n' pointer values in registers.
151 -------------------------------------------------------------------------- */
153 EXTFUN(stg_gc_enter_1)
162 /*- 2 Regs--------------------------------------------------------------------*/
164 EXTFUN(stg_gc_enter_2)
174 /*- 3 Regs -------------------------------------------------------------------*/
176 EXTFUN(stg_gc_enter_3)
187 /*- 4 Regs -------------------------------------------------------------------*/
189 EXTFUN(stg_gc_enter_4)
201 /*- 5 Regs -------------------------------------------------------------------*/
203 EXTFUN(stg_gc_enter_5)
216 /*- 6 Regs -------------------------------------------------------------------*/
218 EXTFUN(stg_gc_enter_6)
232 /*- 7 Regs -------------------------------------------------------------------*/
234 EXTFUN(stg_gc_enter_7)
249 /*- 8 Regs -------------------------------------------------------------------*/
251 EXTFUN(stg_gc_enter_8)
267 /* -----------------------------------------------------------------------------
268 For a case expression on a polymorphic or function-typed object, if
269 the default branch (there can only be one branch) of the case fails
270 a heap-check, instead of using stg_gc_enter_1 as normal, we must
271 push a new SEQ frame on the stack, followed by the object returned.
273 Otherwise, if the object is a function, it won't return to the
274 correct activation record on returning from garbage collection. It will
275 assume it has some arguments and apply itself.
276 -------------------------------------------------------------------------- */
281 Sp -= 1 + sizeofW(StgSeqFrame);
282 PUSH_SEQ_FRAME(Sp+1);
288 /* -----------------------------------------------------------------------------
289 Heap checks in Primitive case alternatives
291 A primitive case alternative is entered with a value either in
292 R1, FloatReg1 or D1 depending on the return convention. All the
293 cases are covered below.
294 -------------------------------------------------------------------------- */
296 /*-- No regsiters live (probably a void return) ----------------------------- */
298 INFO_TABLE_SRT_BITMAP(stg_gc_noregs_ret_info, stg_gc_noregs_ret, 0/*BITMAP*/,
299 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
300 RET_SMALL,, EF_, 0, 0);
302 EXTFUN(stg_gc_noregs_ret)
305 JMP_(ENTRY_CODE(Sp[0]));
309 EXTFUN(stg_gc_noregs)
313 Sp[0] = (W_)&stg_gc_noregs_ret_info;
318 /*-- R1 is boxed/unpointed -------------------------------------------------- */
320 INFO_TABLE_SRT_BITMAP(stg_gc_unpt_r1_info, stg_gc_unpt_r1_entry, 0/*BITMAP*/,
321 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
322 RET_SMALL,, EF_, 0, 0);
324 EXTFUN(stg_gc_unpt_r1_entry)
329 JMP_(ENTRY_CODE(Sp[0]));
333 EXTFUN(stg_gc_unpt_r1)
338 Sp[0] = (W_)&stg_gc_unpt_r1_info;
343 /*-- R1 is unboxed -------------------------------------------------- */
345 INFO_TABLE_SRT_BITMAP(stg_gc_unbx_r1_info, stg_gc_unbx_r1_entry, 1/*BITMAP*/,
346 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
347 RET_SMALL,, EF_, 0, 0);
348 /* the 1 is a bitmap - i.e. 1 non-pointer word on the stack. */
350 EXTFUN(stg_gc_unbx_r1_entry)
355 JMP_(ENTRY_CODE(Sp[0]));
359 EXTFUN(stg_gc_unbx_r1)
364 Sp[0] = (W_)&stg_gc_unbx_r1_info;
369 /*-- F1 contains a float ------------------------------------------------- */
371 INFO_TABLE_SRT_BITMAP(stg_gc_f1_info, stg_gc_f1_entry, 1/*BITMAP*/,
372 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
373 RET_SMALL,, EF_, 0, 0);
375 EXTFUN(stg_gc_f1_entry)
380 JMP_(ENTRY_CODE(Sp[0]));
388 ASSIGN_FLT(Sp+1, F1);
389 Sp[0] = (W_)&stg_gc_f1_info;
394 /*-- D1 contains a double ------------------------------------------------- */
396 /* we support doubles of either 1 or 2 words in size */
398 #if SIZEOF_DOUBLE == SIZEOF_VOID_P
399 # define DBL_BITMAP 1
401 # define DBL_BITMAP 3
404 INFO_TABLE_SRT_BITMAP(stg_gc_d1_info, stg_gc_d1_entry, DBL_BITMAP,
405 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
406 RET_SMALL,, EF_, 0, 0);
408 EXTFUN(stg_gc_d1_entry)
412 Sp += sizeofW(StgDouble);
413 JMP_(ENTRY_CODE(Sp[0]));
420 Sp -= 1 + sizeofW(StgDouble);
422 Sp[0] = (W_)&stg_gc_d1_info;
427 /* -----------------------------------------------------------------------------
428 Heap checks for unboxed tuple case alternatives
432 - for an unboxed tuple with n components, we rearrange the components
433 with pointers first followed by non-pointers. (NB: not done yet)
435 - The first k components are allocated registers, where k is the
436 number of components that will fit in real registers.
438 - The rest are placed on the stack, with space left for tagging
439 of the non-pointer block if necessary.
441 - On failure of a heap check:
442 - the tag is filled in if necessary,
443 - we load Ri with the address of the continuation,
444 where i is the lowest unused vanilla register.
445 - jump to 'stg_gc_ut_x_y' where x is the number of pointer
446 registers and y the number of non-pointers.
447 - if the required canned sequence isn't available, it will
448 have to be generated at compile-time by the code
449 generator (this will probably happen if there are
450 floating-point values, for instance).
452 For now, just deal with R1, hence R2 contains the sequel address.
453 -------------------------------------------------------------------------- */
455 /*---- R1 contains a pointer: ------ */
457 INFO_TABLE_SRT_BITMAP(stg_gc_ut_1_0_info, stg_gc_ut_1_0_entry, 1/*BITMAP*/,
458 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
459 RET_SMALL,, EF_, 0, 0);
461 EXTFUN(stg_gc_ut_1_0_entry)
466 JMP_(ENTRY_CODE(Sp[-2]));
470 EXTFUN(stg_gc_ut_1_0)
476 Sp[0] = (W_)&stg_gc_ut_1_0_info;
481 /*---- R1 contains a non-pointer: ------ */
483 INFO_TABLE_SRT_BITMAP(stg_gc_ut_0_1_info, stg_gc_ut_0_1_entry, 3/*BITMAP*/,
484 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
485 RET_SMALL,, EF_, 0, 0);
487 EXTFUN(stg_gc_ut_0_1_entry)
492 JMP_(ENTRY_CODE(Sp[-2]));
496 EXTFUN(stg_gc_ut_0_1)
500 Sp[0] = (W_)&stg_gc_ut_0_1_info;
507 /* -----------------------------------------------------------------------------
508 Standard top-level fast-entry heap checks.
510 - we want to make the stack look like it should at the slow entry
511 point for the function. That way we can just push the slow
512 entry point on the stack and return using ThreadRunGHC.
514 - The compiler will generate code to fill in any tags on the stack,
515 in case we arrived directly at the fast entry point and these tags
518 - The rest is hopefully handled by jumping to a canned sequence.
519 We currently have canned sequences for 0-8 pointer registers. If
520 any registers contain non-pointers, we must reduce to an all-pointers
521 situation by pushing as many registers on the stack as necessary.
523 eg. if R1, R2 contain pointers and R3 contains a word, the heap check
524 failure sequence looks like this:
531 after pushing R3, we have pointers in R1 and R2 which corresponds
532 to the 2-pointer canned sequence.
534 -------------------------------------------------------------------------- */
536 /*- 0 Regs -------------------------------------------------------------------*/
547 /*- 1 Reg --------------------------------------------------------------------*/
559 /*- 1 Reg (non-ptr) ----------------------------------------------------------*/
566 Sp[1] = WORD_TAG; /* ToDo: or maybe its an int? */
572 /*- 2 Regs--------------------------------------------------------------------*/
585 /*- 3 Regs -------------------------------------------------------------------*/
599 /*- 4 Regs -------------------------------------------------------------------*/
614 /*- 5 Regs -------------------------------------------------------------------*/
630 /*- 6 Regs -------------------------------------------------------------------*/
647 /*- 7 Regs -------------------------------------------------------------------*/
665 /*- 8 Regs -------------------------------------------------------------------*/
684 /* -----------------------------------------------------------------------------
685 Generic Heap Check Code.
687 Called with Liveness mask in R9, Return address in R10.
688 Stack must be consistent (tagged, and containing all necessary info pointers
691 We also define an stg_gen_yield here, because it's very similar.
692 -------------------------------------------------------------------------- */
694 #if SIZEOF_DOUBLE > SIZEOF_VOID_P
696 #define RESTORE_EVERYTHING \
697 D2 = PK_DBL(Sp+16); \
698 D1 = PK_DBL(Sp+14); \
699 F4 = PK_FLT(Sp+13); \
700 F3 = PK_FLT(Sp+12); \
701 F2 = PK_FLT(Sp+11); \
702 F1 = PK_FLT(Sp+10); \
713 #define RET_OFFSET (-17)
715 #define SAVE_EVERYTHING \
716 ASSIGN_DBL(Sp-2,D2); \
717 ASSIGN_DBL(Sp-4,D1); \
718 ASSIGN_FLT(Sp-5,F4); \
719 ASSIGN_FLT(Sp-6,F3); \
720 ASSIGN_FLT(Sp-7,F2); \
721 ASSIGN_FLT(Sp-8,F1); \
730 Sp[-17] = R10.w; /* return address */ \
731 Sp[-18] = R9.w; /* liveness mask */ \
732 Sp[-19] = (W_)&stg_gen_chk_info; \
737 #define RESTORE_EVERYTHING \
738 D2 = PK_DBL(Sp+15); \
739 D1 = PK_DBL(Sp+14); \
740 F4 = PK_FLT(Sp+13); \
741 F3 = PK_FLT(Sp+12); \
742 F2 = PK_FLT(Sp+11); \
743 F1 = PK_FLT(Sp+10); \
754 #define RET_OFFSET (-15)
756 #define SAVE_EVERYTHING \
757 ASSIGN_DBL(Sp-1,D2); \
758 ASSIGN_DBL(Sp-2,D1); \
759 ASSIGN_FLT(Sp-3,F4); \
760 ASSIGN_FLT(Sp-4,F3); \
761 ASSIGN_FLT(Sp-5,F2); \
762 ASSIGN_FLT(Sp-6,F1); \
771 Sp[-15] = R10.w; /* return address */ \
772 Sp[-16] = R9.w; /* liveness mask */ \
773 Sp[-17] = (W_)&stg_gen_chk_info; \
778 INFO_TABLE_SRT_BITMAP(stg_gen_chk_info, stg_gen_chk_ret, 0,
779 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
780 RET_DYN,, EF_, 0, 0);
782 /* bitmap in the above info table is unused, the real one is on the stack.
789 JMP_(Sp[RET_OFFSET]); /* NO ENTRY_CODE() - this is a direct ret address */
802 * stg_gen_hp is used by MAYBE_GC, where we can't use GC_GENERIC
803 * because we've just failed doYouWantToGC(), not a standard heap
804 * check. GC_GENERIC would end up returning StackOverflow.
814 /* -----------------------------------------------------------------------------
816 -------------------------------------------------------------------------- */
826 INFO_TABLE_SRT_BITMAP(stg_yield_noregs_info, stg_yield_noregs_ret, 0/*BITMAP*/,
827 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
828 RET_SMALL,, EF_, 0, 0);
830 FN_(stg_yield_noregs_ret)
833 JMP_(ENTRY_CODE(Sp[0]));
837 FN_(stg_yield_noregs)
841 Sp[0] = (W_)&stg_yield_noregs_info;
846 FN_(stg_yield_to_Hugs)
849 /* No need to save everything - no live registers */
854 /* -----------------------------------------------------------------------------
856 -------------------------------------------------------------------------- */