1 /* -----------------------------------------------------------------------------
2 * $Id: HeapStackCheck.hc,v 1.12 2000/03/02 10:11:50 sewardj 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 ThreadYielding).
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 EXTFUN(stg_gc_enter_1_hponly)
169 CurrentTSO->whatNext = ThreadEnterGHC;
174 /*- 2 Regs--------------------------------------------------------------------*/
176 EXTFUN(stg_gc_enter_2)
186 /*- 3 Regs -------------------------------------------------------------------*/
188 EXTFUN(stg_gc_enter_3)
199 /*- 4 Regs -------------------------------------------------------------------*/
201 EXTFUN(stg_gc_enter_4)
213 /*- 5 Regs -------------------------------------------------------------------*/
215 EXTFUN(stg_gc_enter_5)
228 /*- 6 Regs -------------------------------------------------------------------*/
230 EXTFUN(stg_gc_enter_6)
244 /*- 7 Regs -------------------------------------------------------------------*/
246 EXTFUN(stg_gc_enter_7)
261 /*- 8 Regs -------------------------------------------------------------------*/
263 EXTFUN(stg_gc_enter_8)
281 ToDo: merge the block and yield macros, calling something like BLOCK(N)
286 Should we actually ever do a yield in such a case?? -- HWL
292 CurrentTSO->whatNext = ThreadEnterGHC;
293 R1.i = ThreadYielding;
304 CurrentTSO->whatNext = ThreadEnterGHC;
305 R1.i = ThreadYielding;
310 /*- 2 Regs--------------------------------------------------------------------*/
319 CurrentTSO->whatNext = ThreadEnterGHC;
320 R1.i = ThreadYielding;
325 /*- 3 Regs -------------------------------------------------------------------*/
335 CurrentTSO->whatNext = ThreadEnterGHC;
336 R1.i = ThreadYielding;
341 /*- 4 Regs -------------------------------------------------------------------*/
352 CurrentTSO->whatNext = ThreadEnterGHC;
353 R1.i = ThreadYielding;
358 /*- 5 Regs -------------------------------------------------------------------*/
370 CurrentTSO->whatNext = ThreadEnterGHC;
371 R1.i = ThreadYielding;
376 /*- 6 Regs -------------------------------------------------------------------*/
389 CurrentTSO->whatNext = ThreadEnterGHC;
390 R1.i = ThreadYielding;
395 /*- 7 Regs -------------------------------------------------------------------*/
409 CurrentTSO->whatNext = ThreadEnterGHC;
410 R1.i = ThreadYielding;
415 /*- 8 Regs -------------------------------------------------------------------*/
430 CurrentTSO->whatNext = ThreadEnterGHC;
431 R1.i = ThreadYielding;
436 // the same routines but with a block rather than a yield
444 CurrentTSO->whatNext = ThreadEnterGHC;
445 R1.i = ThreadBlocked;
450 /*- 2 Regs--------------------------------------------------------------------*/
459 CurrentTSO->whatNext = ThreadEnterGHC;
460 R1.i = ThreadBlocked;
465 /*- 3 Regs -------------------------------------------------------------------*/
475 CurrentTSO->whatNext = ThreadEnterGHC;
476 R1.i = ThreadBlocked;
481 /*- 4 Regs -------------------------------------------------------------------*/
492 CurrentTSO->whatNext = ThreadEnterGHC;
493 R1.i = ThreadBlocked;
498 /*- 5 Regs -------------------------------------------------------------------*/
510 CurrentTSO->whatNext = ThreadEnterGHC;
511 R1.i = ThreadBlocked;
516 /*- 6 Regs -------------------------------------------------------------------*/
529 CurrentTSO->whatNext = ThreadEnterGHC;
530 R1.i = ThreadBlocked;
535 /*- 7 Regs -------------------------------------------------------------------*/
549 CurrentTSO->whatNext = ThreadEnterGHC;
550 R1.i = ThreadBlocked;
555 /*- 8 Regs -------------------------------------------------------------------*/
570 CurrentTSO->whatNext = ThreadEnterGHC;
571 R1.i = ThreadBlocked;
578 #if 0 && defined(PAR)
581 Similar to stg_block_1 (called via StgMacro BLOCK_NP) but separates the
582 saving of the thread state from the actual jump via an StgReturn.
583 We need this separation because we call RTS routines in blocking entry codes
584 before jumping back into the RTS (see parallel/FetchMe.hc).
587 EXTFUN(par_block_1_no_jump)
599 CurrentTSO->whatNext = ThreadEnterGHC;
600 R1.i = ThreadBlocked;
607 /* -----------------------------------------------------------------------------
608 For a case expression on a polymorphic or function-typed object, if
609 the default branch (there can only be one branch) of the case fails
610 a heap-check, instead of using stg_gc_enter_1 as normal, we must
611 push a new SEQ frame on the stack, followed by the object returned.
613 Otherwise, if the object is a function, it won't return to the
614 correct activation record on returning from garbage collection. It will
615 assume it has some arguments and apply itself.
616 -------------------------------------------------------------------------- */
621 Sp -= 1 + sizeofW(StgSeqFrame);
622 PUSH_SEQ_FRAME(Sp+1);
628 /* -----------------------------------------------------------------------------
629 Heap checks in Primitive case alternatives
631 A primitive case alternative is entered with a value either in
632 R1, FloatReg1 or D1 depending on the return convention. All the
633 cases are covered below.
634 -------------------------------------------------------------------------- */
636 /*-- No regsiters live (probably a void return) ----------------------------- */
638 /* If we change the policy for thread startup to *not* remove the
639 * return address from the stack, we can get rid of this little
640 * function/info table...
642 INFO_TABLE_SRT_BITMAP(stg_gc_noregs_ret_info, stg_gc_noregs_ret, 0/*BITMAP*/,
643 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
644 RET_SMALL,, EF_, 0, 0);
646 EXTFUN(stg_gc_noregs_ret)
649 JMP_(ENTRY_CODE(Sp[0]));
653 EXTFUN(stg_gc_noregs)
657 Sp[0] = (W_)&stg_gc_noregs_ret_info;
662 /*-- R1 is boxed/unpointed -------------------------------------------------- */
664 INFO_TABLE_SRT_BITMAP(stg_gc_unpt_r1_info, stg_gc_unpt_r1_entry, 0/*BITMAP*/,
665 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
666 RET_SMALL,, EF_, 0, 0);
668 EXTFUN(stg_gc_unpt_r1_entry)
673 JMP_(ENTRY_CODE(Sp[0]));
677 EXTFUN(stg_gc_unpt_r1)
682 Sp[0] = (W_)&stg_gc_unpt_r1_info;
687 /*-- R1 is unboxed -------------------------------------------------- */
689 INFO_TABLE_SRT_BITMAP(stg_gc_unbx_r1_info, stg_gc_unbx_r1_entry, 1/*BITMAP*/,
690 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
691 RET_SMALL,, EF_, 0, 0);
692 /* the 1 is a bitmap - i.e. 1 non-pointer word on the stack. */
694 EXTFUN(stg_gc_unbx_r1_entry)
699 JMP_(ENTRY_CODE(Sp[0]));
703 EXTFUN(stg_gc_unbx_r1)
708 Sp[0] = (W_)&stg_gc_unbx_r1_info;
713 /*-- F1 contains a float ------------------------------------------------- */
715 INFO_TABLE_SRT_BITMAP(stg_gc_f1_info, stg_gc_f1_entry, 1/*BITMAP*/,
716 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
717 RET_SMALL,, EF_, 0, 0);
719 EXTFUN(stg_gc_f1_entry)
724 JMP_(ENTRY_CODE(Sp[0]));
732 ASSIGN_FLT(Sp+1, F1);
733 Sp[0] = (W_)&stg_gc_f1_info;
738 /*-- D1 contains a double ------------------------------------------------- */
740 /* we support doubles of either 1 or 2 words in size */
742 #if SIZEOF_DOUBLE == SIZEOF_VOID_P
743 # define DBL_BITMAP 1
745 # define DBL_BITMAP 3
748 INFO_TABLE_SRT_BITMAP(stg_gc_d1_info, stg_gc_d1_entry, DBL_BITMAP,
749 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
750 RET_SMALL,, EF_, 0, 0);
752 EXTFUN(stg_gc_d1_entry)
756 Sp += sizeofW(StgDouble);
757 JMP_(ENTRY_CODE(Sp[0]));
764 Sp -= 1 + sizeofW(StgDouble);
766 Sp[0] = (W_)&stg_gc_d1_info;
771 /* -----------------------------------------------------------------------------
772 Heap checks for unboxed tuple case alternatives
776 - for an unboxed tuple with n components, we rearrange the components
777 with pointers first followed by non-pointers. (NB: not done yet)
779 - The first k components are allocated registers, where k is the
780 number of components that will fit in real registers.
782 - The rest are placed on the stack, with space left for tagging
783 of the non-pointer block if necessary.
785 - On failure of a heap check:
786 - the tag is filled in if necessary,
787 - we load Ri with the address of the continuation,
788 where i is the lowest unused vanilla register.
789 - jump to 'stg_gc_ut_x_y' where x is the number of pointer
790 registers and y the number of non-pointers.
791 - if the required canned sequence isn't available, it will
792 have to be generated at compile-time by the code
793 generator (this will probably happen if there are
794 floating-point values, for instance).
796 For now, just deal with R1, hence R2 contains the sequel address.
797 -------------------------------------------------------------------------- */
799 /*---- R1 contains a pointer: ------ */
801 INFO_TABLE_SRT_BITMAP(stg_gc_ut_1_0_info, stg_gc_ut_1_0_entry, 1/*BITMAP*/,
802 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
803 RET_SMALL,, EF_, 0, 0);
805 EXTFUN(stg_gc_ut_1_0_entry)
810 JMP_(ENTRY_CODE(Sp[-2]));
814 EXTFUN(stg_gc_ut_1_0)
820 Sp[0] = (W_)&stg_gc_ut_1_0_info;
825 /*---- R1 contains a non-pointer: ------ */
827 INFO_TABLE_SRT_BITMAP(stg_gc_ut_0_1_info, stg_gc_ut_0_1_entry, 3/*BITMAP*/,
828 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
829 RET_SMALL,, EF_, 0, 0);
831 EXTFUN(stg_gc_ut_0_1_entry)
836 JMP_(ENTRY_CODE(Sp[-2]));
840 EXTFUN(stg_gc_ut_0_1)
844 Sp[0] = (W_)&stg_gc_ut_0_1_info;
851 /* -----------------------------------------------------------------------------
852 Standard top-level fast-entry heap checks.
854 - we want to make the stack look like it should at the slow entry
855 point for the function. That way we can just push the slow
856 entry point on the stack and return using ThreadRunGHC.
858 - The compiler will generate code to fill in any tags on the stack,
859 in case we arrived directly at the fast entry point and these tags
862 - The rest is hopefully handled by jumping to a canned sequence.
863 We currently have canned sequences for 0-8 pointer registers. If
864 any registers contain non-pointers, we must reduce to an all-pointers
865 situation by pushing as many registers on the stack as necessary.
867 eg. if R1, R2 contain pointers and R3 contains a word, the heap check
868 failure sequence looks like this:
875 after pushing R3, we have pointers in R1 and R2 which corresponds
876 to the 2-pointer canned sequence.
878 -------------------------------------------------------------------------- */
880 /*- 0 Regs -------------------------------------------------------------------*/
891 /*- 1 Reg --------------------------------------------------------------------*/
903 /*- 1 Reg (non-ptr) ----------------------------------------------------------*/
910 Sp[1] = WORD_TAG; /* ToDo: or maybe its an int? */
916 /*- 2 Regs--------------------------------------------------------------------*/
929 /*- 3 Regs -------------------------------------------------------------------*/
943 /*- 4 Regs -------------------------------------------------------------------*/
958 /*- 5 Regs -------------------------------------------------------------------*/
974 /*- 6 Regs -------------------------------------------------------------------*/
991 /*- 7 Regs -------------------------------------------------------------------*/
1009 /*- 8 Regs -------------------------------------------------------------------*/
1028 /* -----------------------------------------------------------------------------
1029 Generic Heap Check Code.
1031 Called with Liveness mask in R9, Return address in R10.
1032 Stack must be consistent (tagged, and containing all necessary info pointers
1035 We also define an stg_gen_yield here, because it's very similar.
1036 -------------------------------------------------------------------------- */
1038 #if SIZEOF_DOUBLE > SIZEOF_VOID_P
1040 #define RESTORE_EVERYTHING \
1041 D2 = PK_DBL(Sp+16); \
1042 D1 = PK_DBL(Sp+14); \
1043 F4 = PK_FLT(Sp+13); \
1044 F3 = PK_FLT(Sp+12); \
1045 F2 = PK_FLT(Sp+11); \
1046 F1 = PK_FLT(Sp+10); \
1057 #define RET_OFFSET (-17)
1059 #define SAVE_EVERYTHING \
1060 ASSIGN_DBL(Sp-2,D2); \
1061 ASSIGN_DBL(Sp-4,D1); \
1062 ASSIGN_FLT(Sp-5,F4); \
1063 ASSIGN_FLT(Sp-6,F3); \
1064 ASSIGN_FLT(Sp-7,F2); \
1065 ASSIGN_FLT(Sp-8,F1); \
1074 Sp[-17] = R10.w; /* return address */ \
1075 Sp[-18] = R9.w; /* liveness mask */ \
1076 Sp[-19] = (W_)&stg_gen_chk_info; \
1081 #define RESTORE_EVERYTHING \
1082 D2 = PK_DBL(Sp+15); \
1083 D1 = PK_DBL(Sp+14); \
1084 F4 = PK_FLT(Sp+13); \
1085 F3 = PK_FLT(Sp+12); \
1086 F2 = PK_FLT(Sp+11); \
1087 F1 = PK_FLT(Sp+10); \
1098 #define RET_OFFSET (-15)
1100 #define SAVE_EVERYTHING \
1101 ASSIGN_DBL(Sp-1,D2); \
1102 ASSIGN_DBL(Sp-2,D1); \
1103 ASSIGN_FLT(Sp-3,F4); \
1104 ASSIGN_FLT(Sp-4,F3); \
1105 ASSIGN_FLT(Sp-5,F2); \
1106 ASSIGN_FLT(Sp-6,F1); \
1115 Sp[-15] = R10.w; /* return address */ \
1116 Sp[-16] = R9.w; /* liveness mask */ \
1117 Sp[-17] = (W_)&stg_gen_chk_info; \
1122 INFO_TABLE_SRT_BITMAP(stg_gen_chk_info, stg_gen_chk_ret, 0,
1123 0/*SRT*/, 0/*SRT_OFF*/, 0/*SRT_LEN*/,
1124 RET_DYN,, EF_, 0, 0);
1126 /* bitmap in the above info table is unused, the real one is on the stack.
1129 FN_(stg_gen_chk_ret)
1133 JMP_(Sp[RET_OFFSET]); /* NO ENTRY_CODE() - this is a direct ret address */
1146 * stg_gen_hp is used by MAYBE_GC, where we can't use GC_GENERIC
1147 * because we've just failed doYouWantToGC(), not a standard heap
1148 * check. GC_GENERIC would end up returning StackOverflow.
1158 /* -----------------------------------------------------------------------------
1160 -------------------------------------------------------------------------- */
1170 FN_(stg_yield_noregs)
1174 Sp[0] = (W_)&stg_gc_noregs_ret_info;
1179 FN_(stg_yield_to_Hugs)
1182 /* No need to save everything - no live registers */
1187 /* -----------------------------------------------------------------------------
1189 -------------------------------------------------------------------------- */
1199 FN_(stg_block_noregs)
1203 Sp[0] = (W_)&stg_gc_noregs_ret_info;