[project @ 2004-11-09 18:04:15 by sof]
[ghc-hetmet.git] / ghc / rts / HeapStackCheck.cmm
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The GHC Team, 1998-2004
4  *
5  * Canned Heap-Check and Stack-Check sequences.
6  *
7  * This file is written in a subset of C--, extended with various
8  * features specific to GHC.  It is compiled by GHC directly.  For the
9  * syntax of .cmm files, see the parser in ghc/compiler/cmm/CmmParse.y.
10  *
11  * ---------------------------------------------------------------------------*/
12
13 #include "Cmm.h"
14
15 /* Stack/Heap Check Failure
16  * ------------------------
17  *
18  * On discovering that a stack or heap check has failed, we do the following:
19  *
20  *    - If the context_switch flag is set, indicating that there are more
21  *      threads waiting to run, we yield to the scheduler 
22  *      (return ThreadYielding).
23  *
24  *    - If Hp > HpLim, we've had a heap check failure.  This means we've
25  *      come to the end of the current heap block, so we try to chain
26  *      another block on with ExtendNursery().  
27  *
28  *           - If this succeeds, we carry on without returning to the 
29  *             scheduler.  
30  *
31  *           - If it fails, we return to the scheduler claiming HeapOverflow
32  *             so that a garbage collection can be performed.
33  *
34  *    - If Hp <= HpLim, it must have been a stack check that failed.  In
35  *      which case, we return to the scheduler claiming StackOverflow, the
36  *      scheduler will either increase the size of our stack, or raise
37  *      an exception if the stack is already too big.
38  *
39  * The effect of checking for context switch only in the heap/stack check
40  * failure code is that we'll switch threads after the current thread has
41  * reached the end of its heap block.  If a thread isn't allocating
42  * at all, it won't yield.  Hopefully this won't be a problem in practice.
43  */
44  
45 /* Remember that the return address is *removed* when returning to a
46  * ThreadRunGHC thread.
47  */
48
49 #define GC_GENERIC                                              \
50     DEBUG_ONLY(foreign "C" heapCheckFail());                    \
51     if (Hp > HpLim) {                                           \
52         Hp = Hp - HpAlloc/*in bytes*/;                          \
53         if (HpAlloc <= BLOCK_SIZE                               \
54             && bdescr_link(CurrentNursery) != NULL) {           \
55             CLOSE_NURSERY();                                    \
56             CurrentNursery = bdescr_link(CurrentNursery);       \
57             OPEN_NURSERY();                                     \
58             if (CInt[context_switch] != 0) {                    \
59                 R1 = ThreadYielding;                            \
60                 goto sched;                                     \
61             } else {                                            \
62                 jump %ENTRY_CODE(Sp(0));                        \
63             }                                                   \
64         } else {                                                \
65             R1 = HeapOverflow;                                  \
66             goto sched;                                         \
67         }                                                       \
68     } else {                                                    \
69         R1 = StackOverflow;                                     \
70     }                                                           \
71   sched:                                                        \
72     SAVE_THREAD_STATE();                                        \
73     StgTSO_what_next(CurrentTSO) = ThreadRunGHC::I16;           \
74     jump StgReturn;
75
76 #define RETURN_TO_SCHED(why,what_next)                  \
77   SAVE_THREAD_STATE();                                  \
78   StgTSO_what_next(CurrentTSO) = what_next::I16;        \
79   R1 = why;                                             \
80   jump StgReturn;
81
82 #define HP_GENERIC           RETURN_TO_SCHED(HeapOverflow,   ThreadRunGHC)
83 #define YIELD_GENERIC        RETURN_TO_SCHED(ThreadYielding, ThreadRunGHC)
84 #define YIELD_TO_INTERPRETER RETURN_TO_SCHED(ThreadYielding, ThreadInterpret)
85 #define BLOCK_GENERIC        RETURN_TO_SCHED(ThreadBlocked,  ThreadRunGHC)
86
87 /* -----------------------------------------------------------------------------
88    Heap checks in thunks/functions.
89
90    In these cases, node always points to the function closure.  This gives
91    us an easy way to return to the function: just leave R1 on the top of
92    the stack, and have the scheduler enter it to return.
93
94    There are canned sequences for 'n' pointer values in registers.
95    -------------------------------------------------------------------------- */
96
97 INFO_TABLE_RET( stg_enter, 1/*framesize*/, 0/*bitmap*/, RET_SMALL)
98 {
99     R1 = Sp(1);
100     Sp_adj(2);
101     ENTER();
102 }
103
104 __stg_gc_enter_1
105 {
106     Sp_adj(-2);
107     Sp(1) = R1;
108     Sp(0) = stg_enter_info;
109     GC_GENERIC
110 }
111
112 #ifdef SMP
113 stg_gc_enter_1_hponly
114 {
115     Sp_adj(-1);
116     Sp(0) = R1;
117     R1 = HeapOverflow;
118     SAVE_THREAD_STATE();
119     TSO_what_next(CurrentTSO) = ThreadRunGHC::I16;
120     jump StgReturn;
121 }
122 #endif
123
124 #if defined(GRAN)
125 /*
126   ToDo: merge the block and yield macros, calling something like BLOCK(N)
127         at the end;
128 */
129
130 /* 
131    Should we actually ever do a yield in such a case?? -- HWL
132 */
133 gran_yield_0
134 {
135     SAVE_THREAD_STATE();                                        
136     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
137     R1 = ThreadYielding;
138     jump StgReturn;
139 }
140
141 gran_yield_1
142 {
143     Sp_adj(-1);
144     Sp(0) = R1;
145     SAVE_THREAD_STATE();                                        
146     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
147     R1 = ThreadYielding;
148     jump StgReturn;
149 }
150
151 /*- 2 Regs--------------------------------------------------------------------*/
152
153 gran_yield_2
154 {
155     Sp_adj(-2);
156     Sp(1) = R2;
157     Sp(0) = R1;
158     SAVE_THREAD_STATE();                                        
159     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
160     R1 = ThreadYielding;
161     jump StgReturn;
162 }
163
164 /*- 3 Regs -------------------------------------------------------------------*/
165
166 gran_yield_3
167 {
168     Sp_adj(-3);
169     Sp(2) = R3;
170     Sp(1) = R2;
171     Sp(0) = R1;
172     SAVE_THREAD_STATE();                                        
173     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
174     R1 = ThreadYielding;
175     jump StgReturn;
176 }
177
178 /*- 4 Regs -------------------------------------------------------------------*/
179
180 gran_yield_4
181 {
182     Sp_adj(-4);
183     Sp(3) = R4;
184     Sp(2) = R3;
185     Sp(1) = R2;
186     Sp(0) = R1;
187     SAVE_THREAD_STATE();                                        
188     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
189     R1 = ThreadYielding;
190     jump StgReturn;
191 }
192
193 /*- 5 Regs -------------------------------------------------------------------*/
194
195 gran_yield_5
196 {
197     Sp_adj(-5);
198     Sp(4) = R5;
199     Sp(3) = R4;
200     Sp(2) = R3;
201     Sp(1) = R2;
202     Sp(0) = R1;
203     SAVE_THREAD_STATE();                                        
204     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
205     R1 = ThreadYielding;
206     jump StgReturn;
207 }
208
209 /*- 6 Regs -------------------------------------------------------------------*/
210
211 gran_yield_6
212 {
213     Sp_adj(-6);
214     Sp(5) = R6;
215     Sp(4) = R5;
216     Sp(3) = R4;
217     Sp(2) = R3;
218     Sp(1) = R2;
219     Sp(0) = R1;
220     SAVE_THREAD_STATE();                                        
221     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
222     R1 = ThreadYielding;
223     jump StgReturn;
224 }
225
226 /*- 7 Regs -------------------------------------------------------------------*/
227
228 gran_yield_7
229 {
230     Sp_adj(-7);
231     Sp(6) = R7;
232     Sp(5) = R6;
233     Sp(4) = R5;
234     Sp(3) = R4;
235     Sp(2) = R3;
236     Sp(1) = R2;
237     Sp(0) = R1;
238     SAVE_THREAD_STATE();                                        
239     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
240     R1 = ThreadYielding;
241     jump StgReturn;
242 }
243
244 /*- 8 Regs -------------------------------------------------------------------*/
245
246 gran_yield_8
247 {
248     Sp_adj(-8);
249     Sp(7) = R8;
250     Sp(6) = R7;
251     Sp(5) = R6;
252     Sp(4) = R5;
253     Sp(3) = R4;
254     Sp(2) = R3;
255     Sp(1) = R2;
256     Sp(0) = R1;
257     SAVE_THREAD_STATE();                                        
258     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
259     R1 = ThreadYielding;
260     jump StgReturn;
261 }
262
263 // the same routines but with a block rather than a yield
264
265 gran_block_1
266 {
267     Sp_adj(-1);
268     Sp(0) = R1;
269     SAVE_THREAD_STATE();                                        
270     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
271     R1 = ThreadBlocked;
272     jump StgReturn;
273 }
274
275 /*- 2 Regs--------------------------------------------------------------------*/
276
277 gran_block_2
278 {
279     Sp_adj(-2);
280     Sp(1) = R2;
281     Sp(0) = R1;
282     SAVE_THREAD_STATE();                                        
283     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
284     R1 = ThreadBlocked;
285     jump StgReturn;
286 }
287
288 /*- 3 Regs -------------------------------------------------------------------*/
289
290 gran_block_3
291 {
292     Sp_adj(-3);
293     Sp(2) = R3;
294     Sp(1) = R2;
295     Sp(0) = R1;
296     SAVE_THREAD_STATE();                                        
297     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
298     R1 = ThreadBlocked;
299     jump StgReturn;
300 }
301
302 /*- 4 Regs -------------------------------------------------------------------*/
303
304 gran_block_4
305 {
306     Sp_adj(-4);
307     Sp(3) = R4;
308     Sp(2) = R3;
309     Sp(1) = R2;
310     Sp(0) = R1;
311     SAVE_THREAD_STATE();                                        
312     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
313     R1 = ThreadBlocked;
314     jump StgReturn;
315 }
316
317 /*- 5 Regs -------------------------------------------------------------------*/
318
319 gran_block_5
320 {
321     Sp_adj(-5);
322     Sp(4) = R5;
323     Sp(3) = R4;
324     Sp(2) = R3;
325     Sp(1) = R2;
326     Sp(0) = R1;
327     SAVE_THREAD_STATE();                                        
328     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
329     R1 = ThreadBlocked;
330     jump StgReturn;
331 }
332
333 /*- 6 Regs -------------------------------------------------------------------*/
334
335 gran_block_6
336 {
337     Sp_adj(-6);
338     Sp(5) = R6;
339     Sp(4) = R5;
340     Sp(3) = R4;
341     Sp(2) = R3;
342     Sp(1) = R2;
343     Sp(0) = R1;
344     SAVE_THREAD_STATE();                                        
345     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
346     R1 = ThreadBlocked;
347     jump StgReturn;
348 }
349
350 /*- 7 Regs -------------------------------------------------------------------*/
351
352 gran_block_7
353 {
354     Sp_adj(-7);
355     Sp(6) = R7;
356     Sp(5) = R6;
357     Sp(4) = R5;
358     Sp(3) = R4;
359     Sp(2) = R3;
360     Sp(1) = R2;
361     Sp(0) = R1;
362     SAVE_THREAD_STATE();                                        
363     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
364     R1 = ThreadBlocked;
365     jump StgReturn;
366 }
367
368 /*- 8 Regs -------------------------------------------------------------------*/
369
370 gran_block_8
371 {
372     Sp_adj(-8);
373     Sp(7) = R8;
374     Sp(6) = R7;
375     Sp(5) = R6;
376     Sp(4) = R5;
377     Sp(3) = R4;
378     Sp(2) = R3;
379     Sp(1) = R2;
380     Sp(0) = R1;
381     SAVE_THREAD_STATE();                                        
382     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
383     R1 = ThreadBlocked;
384     jump StgReturn;
385 }
386
387 #endif
388
389 #if 0 && defined(PAR)
390
391 /*
392   Similar to stg_block_1 (called via StgMacro BLOCK_NP) but separates the
393   saving of the thread state from the actual jump via an StgReturn.
394   We need this separation because we call RTS routines in blocking entry codes
395   before jumping back into the RTS (see parallel/FetchMe.hc).
396 */
397
398 par_block_1_no_jump
399 {
400     Sp_adj(-1);
401     Sp(0) = R1;
402     SAVE_THREAD_STATE();                                        
403 }
404
405 par_jump
406 {
407     TSO_what_next(CurrentTSO) = ThreadRunGHC;           
408     R1 = ThreadBlocked;
409     jump StgReturn;
410 }
411
412 #endif
413
414 /* -----------------------------------------------------------------------------
415    Heap checks in Primitive case alternatives
416
417    A primitive case alternative is entered with a value either in 
418    R1, FloatReg1 or D1 depending on the return convention.  All the
419    cases are covered below.
420    -------------------------------------------------------------------------- */
421
422 /*-- No Registers live ------------------------------------------------------ */
423
424 stg_gc_noregs
425 {
426     GC_GENERIC
427 }
428
429 /*-- void return ------------------------------------------------------------ */
430
431 INFO_TABLE_RET( stg_gc_void, 0/*framesize*/, 0/*bitmap*/, RET_SMALL)
432 {
433     Sp_adj(1);
434     jump %ENTRY_CODE(Sp(0));
435 }
436
437 /*-- R1 is boxed/unpointed -------------------------------------------------- */
438
439 INFO_TABLE_RET( stg_gc_unpt_r1, 1/*framesize*/, 0/*bitmap*/, RET_SMALL)
440 {
441     R1 = Sp(1);
442     Sp_adj(2);
443     jump %ENTRY_CODE(Sp(0));
444 }
445
446 stg_gc_unpt_r1
447 {
448     Sp_adj(-2);
449     Sp(1) = R1;
450     Sp(0) = stg_gc_unpt_r1_info;
451     GC_GENERIC
452 }
453
454 /*-- R1 is unboxed -------------------------------------------------- */
455
456 /* the 1 is a bitmap - i.e. 1 non-pointer word on the stack. */
457 INFO_TABLE_RET( stg_gc_unbx_r1, 1/*framesize*/, 1/*bitmap*/, RET_SMALL )
458 {
459     R1 = Sp(1);
460     Sp_adj(2);
461     jump %ENTRY_CODE(Sp(0));
462 }
463
464 stg_gc_unbx_r1
465 {
466     Sp_adj(-2);
467     Sp(1) = R1;
468     Sp(0) = stg_gc_unbx_r1_info;
469     GC_GENERIC
470 }
471
472 /*-- F1 contains a float ------------------------------------------------- */
473
474 INFO_TABLE_RET( stg_gc_f1, 1/*framesize*/, 1/*bitmap*/, RET_SMALL )
475 {
476     F1 = F_[Sp+WDS(1)];
477     Sp_adj(2);
478     jump %ENTRY_CODE(Sp(0));
479 }
480
481 stg_gc_f1
482 {
483     Sp_adj(-2);
484     F_[Sp + WDS(1)] = F1;
485     Sp(0) = stg_gc_f1_info;
486     GC_GENERIC
487 }
488
489 /*-- D1 contains a double ------------------------------------------------- */
490
491 /* we support doubles of either 1 or 2 words in size */
492
493 #if SIZEOF_DOUBLE == SIZEOF_VOID_P
494 #  define DBL_BITMAP 1
495 #  define DBL_WORDS  1
496 #else
497 #  define DBL_BITMAP 3
498 #  define DBL_WORDS  2
499 #endif 
500
501 INFO_TABLE_RET( stg_gc_d1, DBL_WORDS/*framesize*/, DBL_BITMAP/*bitmap*/, RET_SMALL )
502 {
503     D1 = D_[Sp + WDS(1)];
504     Sp = Sp + WDS(1) + SIZEOF_StgDouble;
505     jump %ENTRY_CODE(Sp(0));
506 }
507
508 stg_gc_d1
509 {
510     Sp = Sp - WDS(1) - SIZEOF_StgDouble;
511     D_[Sp + WDS(1)] = D1;
512     Sp(0) = stg_gc_d1_info;
513     GC_GENERIC
514 }
515
516
517 /*-- L1 contains an int64 ------------------------------------------------- */
518
519 /* we support int64s of either 1 or 2 words in size */
520
521 #if SIZEOF_VOID_P == 8
522 #  define LLI_BITMAP 1
523 #  define LLI_WORDS  1
524 #else
525 #  define LLI_BITMAP 3
526 #  define LLI_WORDS  2
527 #endif 
528
529 INFO_TABLE_RET( stg_gc_l1, LLI_WORDS/*framesize*/, LLI_BITMAP/*bitmap*/, RET_SMALL )
530 {
531     L1 = L_[Sp + WDS(1)];
532     Sp_adj(1) + SIZEOF_StgWord64;
533     jump %ENTRY_CODE(Sp(0));
534 }
535
536 stg_gc_l1
537 {
538     Sp_adj(-1) - SIZEOF_StgWord64;
539     L_[Sp + WDS(1)] = L1;
540     Sp(0) = stg_gc_l1_info;
541     GC_GENERIC
542 }
543
544 /*-- Unboxed tuple return, one pointer (unregisterised build only) ---------- */
545
546 INFO_TABLE_RET( stg_ut_1_0_unreg, 1/*size*/, 0/*BITMAP*/, RET_SMALL )
547 {
548     Sp_adj(1);
549     // one ptr is on the stack (Sp(0))
550     jump %ENTRY_CODE(Sp(1));
551 }
552
553 /* -----------------------------------------------------------------------------
554    Generic function entry heap check code.
555
556    At a function entry point, the arguments are as per the calling convention,
557    i.e. some in regs and some on the stack.  There may or may not be 
558    a pointer to the function closure in R1 - if there isn't, then the heap
559    check failure code in the function will arrange to load it.
560
561    The function's argument types are described in its info table, so we
562    can just jump to this bit of generic code to save away all the
563    registers and return to the scheduler.
564
565    This code arranges the stack like this:
566          
567          |        ....         |
568          |        args         |
569          +---------------------+
570          |      f_closure      |
571          +---------------------+
572          |        size         |
573          +---------------------+
574          |   stg_gc_fun_info   |
575          +---------------------+
576
577    The size is the number of words of arguments on the stack, and is cached
578    in the frame in order to simplify stack walking: otherwise the size of
579    this stack frame would have to be calculated by looking at f's info table.
580
581    -------------------------------------------------------------------------- */
582
583 __stg_gc_fun
584 {
585     W_ size;
586     W_ info;
587     W_ type;
588
589     info = %GET_FUN_INFO(R1);
590
591     // cache the size
592     type = TO_W_(StgFunInfoExtra_fun_type(info));
593     if (type == ARG_GEN) {
594         size = BITMAP_SIZE(StgFunInfoExtra_bitmap(info));
595     } else { 
596         if (type == ARG_GEN_BIG) {
597 #ifdef TABLES_NEXT_TO_CODE
598             // bitmap field holds an offset
599             size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info)
600                                         + %GET_ENTRY(R1) /* ### */ );
601 #else
602             size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info) );
603 #endif
604         } else {
605             size = BITMAP_SIZE(W_[stg_arg_bitmaps + WDS(type)]);
606         }
607     }
608     
609 #ifdef NO_ARG_REGS
610     // we don't have to save any registers away
611     Sp_adj(-3);
612     Sp(2) = R1;
613     Sp(1) = size;
614     Sp(0) = stg_gc_fun_info;
615     GC_GENERIC
616 #else
617     W_ type;
618     type = TO_W_(StgFunInfoExtra_fun_type(info));
619     // cache the size
620     if (type == ARG_GEN || type == ARG_GEN_BIG) {
621         // regs already saved by the heap check code
622         Sp_adj(-3);
623         Sp(2) = R1;
624         Sp(1) = size;
625         Sp(0) = stg_gc_fun_info;
626         // DEBUG_ONLY(foreign "C" fprintf(stderr, "stg_fun_gc_gen(ARG_GEN)"););
627         GC_GENERIC
628     } else { 
629         jump W_[stg_stack_save_entries + WDS(type)];
630             // jumps to stg_gc_noregs after saving stuff
631     }
632 #endif // !NO_ARG_REGS
633 }
634
635 /* -----------------------------------------------------------------------------
636    Generic Apply (return point)
637
638    The dual to stg_fun_gc_gen (above): this fragment returns to the
639    function, passing arguments in the stack and in registers
640    appropriately.  The stack layout is given above.
641    -------------------------------------------------------------------------- */
642
643 INFO_TABLE_RET( stg_gc_fun, 0/*framesize*/, 0/*bitmap*/, RET_FUN )
644 {
645     R1 = Sp(2);
646     Sp_adj(3);
647 #ifdef NO_ARG_REGS
648     // Minor optimisation: there are no argument registers to load up,
649     // so we can just jump straight to the function's entry point.
650     jump %GET_ENTRY(R1);
651 #else
652     W_ info;
653     W_ type;
654     
655     info = %GET_FUN_INFO(R1);
656     type = TO_W_(StgFunInfoExtra_fun_type(info));
657     if (type == ARG_GEN || type == ARG_GEN_BIG) {
658         jump StgFunInfoExtra_slow_apply(info);
659     } else { 
660         if (type == ARG_BCO) {
661             // cover this case just to be on the safe side
662             Sp_adj(-2);
663             Sp(1) = R1;
664             Sp(0) = stg_apply_interp_info;
665             jump stg_yield_to_interpreter;
666         } else {
667             jump W_[stg_ap_stack_entries + WDS(type)];
668         }
669     }
670 #endif
671 }
672
673 /* -----------------------------------------------------------------------------
674    Generic Heap Check Code.
675
676    Called with Liveness mask in R9,  Return address in R10.
677    Stack must be consistent (containing all necessary info pointers
678    to relevant SRTs).
679
680    See StgMacros.h for a description of the RET_DYN stack frame.
681
682    We also define an stg_gen_yield here, because it's very similar.
683    -------------------------------------------------------------------------- */
684
685 // For simplicity, we assume that SIZEOF_DOUBLE == 2*SIZEOF_VOID_P
686 // on a 64-bit machine, we'll end up wasting a couple of words, but
687 // it's not a big deal.
688
689 #define RESTORE_EVERYTHING                      \
690     L1   = L_[Sp + WDS(19)];                    \
691     D2   = D_[Sp + WDS(17)];                    \
692     D1   = D_[Sp + WDS(15)];                    \
693     F4   = F_[Sp + WDS(14)];                    \
694     F3   = F_[Sp + WDS(13)];                    \
695     F2   = F_[Sp + WDS(12)];                    \
696     F1   = F_[Sp + WDS(11)];                    \
697     R8 = Sp(10);                                \
698     R7 = Sp(9);                                 \
699     R6 = Sp(8);                                 \
700     R5 = Sp(7);                                 \
701     R4 = Sp(6);                                 \
702     R3 = Sp(5);                                 \
703     R2 = Sp(4);                                 \
704     R1 = Sp(3);                                 \
705     Sp_adj(21);
706
707 #define RET_OFFSET (-19)
708
709 #define SAVE_EVERYTHING                         \
710     Sp_adj(-21);                                \
711     L_[Sp + WDS(19)] = L1;                      \
712     D_[Sp + WDS(17)] = D2;                      \
713     D_[Sp + WDS(15)] = D1;                      \
714     F_[Sp + WDS(14)] = F4;                      \
715     F_[Sp + WDS(13)] = F3;                      \
716     F_[Sp + WDS(12)] = F2;                      \
717     F_[Sp + WDS(11)] = F1;                      \
718     Sp(10) = R8;                                \
719     Sp(9) = R7;                                 \
720     Sp(8) = R6;                                 \
721     Sp(7) = R5;                                 \
722     Sp(6) = R4;                                 \
723     Sp(5) = R3;                                 \
724     Sp(4) = R2;                                 \
725     Sp(3) = R1;                                 \
726     Sp(2) = R10.w;    /* return address */      \
727     Sp(1) = R9;     /* liveness mask  */        \
728     Sp(0) = stg_gc_gen_info;
729
730 INFO_TABLE_RET( stg_gc_gen, 0/*framesize*/, 0/*bitmap*/, RET_DYN )
731 /* bitmap in the above info table is unused, the real one is on the stack. */
732 {
733     RESTORE_EVERYTHING;
734     jump Sp(RET_OFFSET); /* No %ENTRY_CODE( - this is an actual code ptr */
735 }
736
737 stg_gc_gen
738 {
739     SAVE_EVERYTHING;
740     GC_GENERIC
741 }         
742
743 // A heap check at an unboxed tuple return point.  The return address
744 // is on the stack, and we can find it by using the offsets given
745 // to us in the liveness mask.
746 stg_gc_ut
747 {
748     R10 = %ENTRY_CODE(Sp(RET_DYN_NONPTRS(R9) + RET_DYN_PTRS(R9)));
749     SAVE_EVERYTHING;
750     GC_GENERIC
751 }
752
753 /*
754  * stg_gen_hp is used by MAYBE_GC, where we can't use GC_GENERIC
755  * because we've just failed doYouWantToGC(), not a standard heap
756  * check.  GC_GENERIC would end up returning StackOverflow.
757  */
758 stg_gc_gen_hp
759 {
760     SAVE_EVERYTHING;
761     HP_GENERIC
762 }         
763
764 /* -----------------------------------------------------------------------------
765    Yields
766    -------------------------------------------------------------------------- */
767
768 stg_gen_yield
769 {
770     SAVE_EVERYTHING;
771     YIELD_GENERIC
772 }
773
774 stg_yield_noregs
775 {
776     YIELD_GENERIC;
777 }
778
779 /* -----------------------------------------------------------------------------
780    Yielding to the interpreter... top of stack says what to do next.
781    -------------------------------------------------------------------------- */
782
783 stg_yield_to_interpreter
784 {
785     YIELD_TO_INTERPRETER;
786 }
787
788 /* -----------------------------------------------------------------------------
789    Blocks
790    -------------------------------------------------------------------------- */
791
792 stg_gen_block
793 {
794     SAVE_EVERYTHING;
795     BLOCK_GENERIC;
796 }
797
798 stg_block_noregs
799 {
800     BLOCK_GENERIC;
801 }
802
803 stg_block_1
804 {
805     Sp_adj(-2);
806     Sp(1) = R1;
807     Sp(0) = stg_enter_info;
808     BLOCK_GENERIC;
809 }
810
811 /* -----------------------------------------------------------------------------
812  * takeMVar/putMVar-specific blocks
813  *
814  * Stack layout for a thread blocked in takeMVar:
815  *      
816  *       ret. addr
817  *       ptr to MVar   (R1)
818  *       stg_block_takemvar_info
819  *
820  * Stack layout for a thread blocked in putMVar:
821  *      
822  *       ret. addr
823  *       ptr to Value  (R2)
824  *       ptr to MVar   (R1)
825  *       stg_block_putmvar_info
826  *
827  * See PrimOps.hc for a description of the workings of take/putMVar.
828  * 
829  * -------------------------------------------------------------------------- */
830
831 INFO_TABLE_RET( stg_block_takemvar, 1/*framesize*/, 0/*bitmap*/, RET_SMALL )
832 {
833     R1 = Sp(1);
834     Sp_adj(2);
835     jump takeMVarzh_fast;
836 }
837
838 stg_block_takemvar
839 {
840     Sp_adj(-2);
841     Sp(1) = R1;
842     Sp(0) = stg_block_takemvar_info;
843     BLOCK_GENERIC;
844 }
845
846 INFO_TABLE_RET( stg_block_putmvar, 2/*framesize*/, 0/*bitmap*/, RET_SMALL )
847 {
848     R2 = Sp(2);
849     R1 = Sp(1);
850     Sp_adj(3);
851     jump putMVarzh_fast;
852 }
853
854 stg_block_putmvar
855 {
856     Sp_adj(-3);
857     Sp(2) = R2;
858     Sp(1) = R1;
859     Sp(0) = stg_block_putmvar_info;
860     BLOCK_GENERIC;
861 }
862
863 #ifdef mingw32_TARGET_OS
864 INFO_TABLE_RET( stg_block_async, 0/*framesize*/, 0/*bitmap*/, RET_SMALL )
865 {
866     W_ ares;
867     W_ len, errC;
868
869     ares = StgTSO_block_info(CurrentTSO);
870     len = StgAsyncIOResult_len(ares);
871     errC = StgAsyncIOResult_errCode(ares);
872     StgTSO_block_info(CurrentTSO) = NULL;
873     foreign "C" free(ares "ptr");
874     R1 = len;
875     Sp(0) = errC;
876     jump %ENTRY_CODE(Sp(1));
877 }
878
879 stg_block_async
880 {
881     Sp_adj(-1);
882     Sp(0) = stg_block_async_info;
883     BLOCK_GENERIC;
884 }
885
886 /* Used by threadDelay implementation; it would be desirable to get rid of
887  * this free()'ing void return continuation.
888  */
889 INFO_TABLE_RET( stg_block_async_void, 0/*framesize*/, 0/*bitmap*/, RET_SMALL )
890 {
891     W_ ares;
892
893     ares = StgTSO_block_info(CurrentTSO);
894     StgTSO_block_info(CurrentTSO) = NULL;
895     foreign "C" free(ares "ptr");
896     Sp_adj(1);
897     jump %ENTRY_CODE(Sp(0));
898 }
899
900 stg_block_async_void
901 {
902     Sp_adj(-1);
903     Sp(0) = stg_block_async_void_info;
904     BLOCK_GENERIC;
905 }
906
907 #endif