[project @ 1999-01-21 10:31:41 by simonm]
[ghc-hetmet.git] / ghc / includes / StgMacros.h
1 /* -----------------------------------------------------------------------------
2  * $Id: StgMacros.h,v 1.3 1999/01/21 10:31:43 simonm Exp $
3  *
4  * Macros used for writing STG-ish C code.
5  *
6  * ---------------------------------------------------------------------------*/
7
8 #ifndef STGMACROS_H
9 #define STGMACROS_H
10
11 /* -----------------------------------------------------------------------------
12   The following macros create function headers.
13
14   Each basic block is represented by a C function with no arguments.
15   We therefore always begin with either
16
17   extern F_ f(void)
18
19   or
20   
21   static F_ f(void)
22
23   The macros can be used either to define the function itself, or to provide
24   prototypes (by following with a ';').
25   --------------------------------------------------------------------------- */
26
27 #define STGFUN(f)       StgFunPtr f(void)
28 #define STATICFUN(f)    static StgFunPtr f(void)
29 #define EXTFUN(f)       extern StgFunPtr f(void)
30
31 #define FN_(f)          F_ f(void)
32 #define IFN_(f)         static F_ f(void)
33 #define IF_(f)          static F_ f(void)
34 #define EF_(f)          extern F_ f(void)
35 #define ED_             extern
36 #define ED_RO_          extern const
37 #define ID_             extern
38 #define ID_RO_          extern const
39 #define EI_             extern const StgInfoTable
40 #define II_             extern const StgInfoTable
41 #define EC_             extern StgClosure
42 #define IC_             extern StgClosure
43
44 /* -----------------------------------------------------------------------------
45    Stack Tagging.
46
47    For a  block of non-pointer words on the stack, we precede the
48    block with a small-integer tag giving the number of non-pointer
49    words in the block.
50    -------------------------------------------------------------------------- */
51
52 #ifndef DEBUG_EXTRA
53 #define ARGTAG_MAX 16           /* probably arbitrary */
54 #define ARG_TAG(n)  (n)
55 #define ARG_SIZE(n) stgCast(StgWord,n)
56
57 typedef enum {
58     REALWORLD_TAG = 0,
59     INT_TAG    = sizeofW(StgInt), 
60     INT64_TAG  = sizeofW(StgInt64), 
61     WORD_TAG   = sizeofW(StgWord), 
62     ADDR_TAG   = sizeofW(StgAddr), 
63     CHAR_TAG   = sizeofW(StgChar),
64     FLOAT_TAG  = sizeofW(StgFloat), 
65     DOUBLE_TAG = sizeofW(StgDouble), 
66     STABLE_TAG = sizeofW(StgWord), 
67 } StackTag;
68
69 #else /* DEBUG_EXTRA */
70
71 typedef enum {
72     ILLEGAL_TAG,
73     REALWORLD_TAG,
74     INT_TAG    ,
75     INT64_TAG  ,
76     WORD_TAG   ,
77     ADDR_TAG   ,
78     CHAR_TAG   ,
79     FLOAT_TAG  ,
80     DOUBLE_TAG ,
81     STABLE_TAG ,
82     ARGTAG_MAX = DOUBLE_TAG
83 } StackTag;
84
85 /* putting this in a .h file generates many copies - but its only a 
86  * debugging build.
87  */
88 static StgWord stg_arg_size[] = {
89     [REALWORLD_TAG] = 0,
90     [INT_TAG   ] = sizeofW(StgInt), 
91     [INT64_TAG ] = sizeofW(StgInt64), 
92     [WORD_TAG  ] = sizeofW(StgWord), 
93     [ADDR_TAG  ] = sizeofW(StgAddr), 
94     [CHAR_TAG  ] = sizeofW(StgChar),
95     [FLOAT_TAG ] = sizeofW(StgFloat), 
96     [DOUBLE_TAG] = sizeofW(StgDouble),
97     [STABLE_TAG] = sizeofW(StgWord)
98 };
99
100 #define ARG_SIZE(tag) stg_arg_size[stgCast(StgWord,tag)]
101
102 #endif /* DEBUG_EXTRA */
103
104 static inline int IS_ARG_TAG( StgWord p );
105 static inline int IS_ARG_TAG( StgWord p ) { return p <= ARGTAG_MAX; }
106
107 /* -----------------------------------------------------------------------------
108    Argument checks.
109    
110    If (Sp + <n_args>) > Su { JMP_(stg_updatePAP); }
111    
112    Sp points to the topmost used word on the stack, and Su points to
113    the most recently pushed update frame.
114
115    Remember that <n_args> must include any tagging of unboxed values.
116
117    ARGS_CHK_LOAD_NODE is for top-level functions, whose entry
118    convention doesn't require that Node is loaded with a pointer to
119    the closure.  Thus we must load node before calling stg_updatePAP if
120    the argument check fails. 
121    -------------------------------------------------------------------------- */
122
123 #define ARGS_CHK(n)                             \
124         if ((P_)(Sp + (n)) > (P_)Su) {          \
125                 JMP_(stg_update_PAP);           \
126         }
127
128 #define ARGS_CHK_LOAD_NODE(n,closure)           \
129         if ((P_)(Sp + (n)) > (P_)Su) {          \
130                 R1.p = (P_)closure;             \
131                 JMP_(stg_update_PAP);           \
132         }
133
134 /* -----------------------------------------------------------------------------
135    Heap/Stack Checks.
136
137    When failing a check, we save a return address on the stack and
138    jump to a pre-compiled code fragment that saves the live registers
139    and returns to the scheduler.
140
141    The return address in most cases will be the beginning of the basic
142    block in which the check resides, since we need to perform the check
143    again on re-entry because someone else might have stolen the resource
144    in the meantime.
145    ------------------------------------------------------------------------- */
146
147 #define STK_CHK(headroom,ret,r,layout,tag_assts)                \
148         if (Sp - headroom < SpLim) {                            \
149             EXTFUN(stg_chk_##layout);                           \
150             tag_assts                                           \
151             (r) = (P_)ret;                                      \
152             JMP_(stg_chk_##layout);                             \
153         }
154        
155 #define HP_CHK(headroom,ret,r,layout,tag_assts)                 \
156         if ((Hp += headroom) > HpLim) {                         \
157             EXTFUN(stg_chk_##layout);                           \
158             tag_assts                                           \
159             (r) = (P_)ret;                                      \
160             JMP_(stg_chk_##layout);                             \
161         }                                                       \
162         TICK_ALLOC_HEAP(headroom);
163
164 #define HP_STK_CHK(stk_headroom,hp_headroom,ret,r,layout,tag_assts) \
165         if (Sp - stk_headroom < SpLim || (Hp += hp_headroom) > HpLim) { \
166             EXTFUN(stg_chk_##layout);                           \
167             tag_assts                                           \
168             (r) = (P_)ret;                                      \
169             JMP_(stg_chk_##layout);                             \
170         }                                                       \
171         TICK_ALLOC_HEAP(hp_headroom);
172
173 /* -----------------------------------------------------------------------------
174    A Heap Check in a case alternative are much simpler: everything is
175    on the stack and covered by a liveness mask already, and there is
176    even a return address with an SRT info table there as well.  
177
178    Just push R1 and return to the scheduler saying 'EnterGHC'
179
180    {STK,HP,HP_STK}_CHK_NP are the various checking macros for
181    bog-standard case alternatives, thunks, and non-top-level
182    functions.  In all these cases, node points to a closure that we
183    can just enter to restart the heap check (the NP stands for 'node points').
184
185    HpLim points to the LAST WORD of valid allocation space.
186    -------------------------------------------------------------------------- */
187
188 #define STK_CHK_NP(headroom,ptrs,tag_assts)                     \
189         if ((Sp - (headroom)) < SpLim) {                        \
190             EXTFUN(stg_gc_enter_##ptrs);                        \
191             tag_assts                                           \
192             JMP_(stg_gc_enter_##ptrs);                          \
193         }
194
195 #define HP_CHK_NP(headroom,ptrs,tag_assts)                      \
196         if ((Hp += (headroom)) > HpLim) {                       \
197             EXTFUN(stg_gc_enter_##ptrs);                        \
198             tag_assts                                           \
199             JMP_(stg_gc_enter_##ptrs);                          \
200         }                                                       \
201         TICK_ALLOC_HEAP(headroom);
202
203 #define HP_CHK_SEQ_NP(headroom,ptrs,tag_assts)                  \
204         if ((Hp += (headroom)) > HpLim) {                       \
205             EXTFUN(stg_gc_seq_##ptrs);                          \
206             tag_assts                                           \
207             JMP_(stg_gc_seq_##ptrs);                            \
208         }                                                       \
209         TICK_ALLOC_HEAP(headroom);
210
211 #define HP_STK_CHK_NP(stk_headroom, hp_headroom, ptrs, tag_assts) \
212         if ((Sp - (stk_headroom)) < SpLim || (Hp += (hp_headroom)) > HpLim) { \
213             EXTFUN(stg_gc_enter_##ptrs);                        \
214             tag_assts                                           \
215             JMP_(stg_gc_enter_##ptrs);                          \
216         }                                                       \
217         TICK_ALLOC_HEAP(hp_headroom);
218
219 /* Heap checks for branches of a primitive case / unboxed tuple return */
220
221 #define GEN_HP_CHK_ALT(headroom,lbl,tag_assts)                  \
222         if ((Hp += (headroom)) > HpLim) {                       \
223             EXTFUN(lbl);                                        \
224             tag_assts                                           \
225             JMP_(lbl);                                          \
226         }                                                       \
227         TICK_ALLOC_HEAP(headroom);
228
229 #define HP_CHK_NOREGS(headroom,tag_assts) \
230     GEN_HP_CHK_ALT(headroom,stg_gc_noregs,tag_assts);
231 #define HP_CHK_UNPT_R1(headroom,tag_assts)  \
232     GEN_HP_CHK_ALT(headroom,stg_gc_unpt_r1,tag_assts);
233 #define HP_CHK_UNBX_R1(headroom,tag_assts)  \
234     GEN_HP_CHK_ALT(headroom,stg_gc_unbx_r1,tag_assts);
235 #define HP_CHK_F1(headroom,tag_assts)       \
236     GEN_HP_CHK_ALT(headroom,stg_gc_f1,tag_assts);
237 #define HP_CHK_D1(headroom,tag_assts)       \
238     GEN_HP_CHK_ALT(headroom,stg_gc_d1,tag_assts);
239
240 #define HP_CHK_L1(headroom,tag_assts)       \
241     GEN_HP_CHK_ALT(headroom,stg_gc_d1,tag_assts);
242
243 #define HP_CHK_UT_ALT(headroom, ptrs, nptrs, r, ret, tag_assts) \
244     GEN_HP_CHK_ALT(headroom, stg_gc_ut_##ptrs##_##nptrs, \
245                      tag_assts r = (P_)ret;)
246
247 /* -----------------------------------------------------------------------------
248    Generic Heap checks.
249
250    These are slow, but have the advantage of being usable in a variety
251    of situations.  
252
253    The one restriction is that any relevant SRTs must already be pointed
254    to from the stack.  The return address doesn't need to have an info
255    table attached: hence it can be any old code pointer.
256
257    The liveness mask is a logical 'XOR' of NO_PTRS and zero or more
258    Rn_PTR constants defined below.  All registers will be saved, but
259    the garbage collector needs to know which ones contain pointers.
260
261    Good places to use a generic heap check: 
262
263         - case alternatives (the return address with an SRT is already
264           on the stack).
265
266         - primitives (no SRT required).
267
268    The stack layout is like this:
269
270           DblReg1-2
271           FltReg1-4
272           R1-8
273           return address
274           liveness mask
275           stg_gen_chk_info
276
277    so the liveness mask depends on the size of an StgDouble (FltRegs
278    and R<n> are guaranteed to be 1 word in size).
279
280    -------------------------------------------------------------------------- */
281
282 /* VERY MAGIC CONSTANTS! 
283  * must agree with code in HeapStackCheck.c, stg_gen_chk
284  */
285
286 #if SIZEOF_DOUBLE > SIZEOF_VOID_P
287 #define ALL_NON_PTRS   0xffff
288 #else /* SIZEOF_DOUBLE == SIZEOF_VOID_P */
289 #define ALL_NON_PTRS   0x3fff
290 #endif
291
292 #define LIVENESS_MASK(ptr_regs)  (ALL_NON_PTRS ^ (ptr_regs))
293
294 #define NO_PTRS   0
295 #define R1_PTR    1<<0
296 #define R2_PTR    1<<1
297 #define R3_PTR    1<<2
298 #define R4_PTR    1<<3
299 #define R5_PTR    1<<4
300 #define R6_PTR    1<<5
301 #define R7_PTR    1<<6
302 #define R8_PTR    1<<7
303
304 #define HP_CHK_GEN(headroom,liveness,reentry,tag_assts) \
305    if ((Hp += (headroom)) > HpLim ) {                   \
306         EF_(stg_gen_chk);                               \
307         tag_assts                                       \
308         R9.w = (W_)LIVENESS_MASK(liveness);             \
309         R10.w = (W_)reentry;                            \
310         JMP_(stg_gen_chk);                              \
311    }                                                    \
312    TICK_ALLOC_HEAP(headroom);
313
314 #define STK_CHK_GEN(headroom,liveness,reentry,tag_assts)        \
315    if ((Sp - (headroom)) < SpLim) {                             \
316         EF_(stg_gen_chk);                                       \
317         tag_assts                                               \
318         R9.w = (W_)LIVENESS_MASK(liveness);                     \
319         R10.w = (W_)reentry;                                    \
320         JMP_(stg_gen_chk);                                      \
321    }                                                            \
322    TICK_ALLOC_HEAP(headroom);
323
324 #define MAYBE_GC(liveness,reentry)              \
325    if (doYouWantToGC()) {                       \
326         EF_(stg_gen_hp);                        \
327         R9.w = (W_)LIVENESS_MASK(liveness);     \
328         R10.w = (W_)reentry;                    \
329         JMP_(stg_gen_hp);                       \
330    }
331
332 /* -----------------------------------------------------------------------------
333    Voluntary Yields/Blocks
334
335    We only have a generic version of this at the moment - if it turns
336    out to be slowing us down we can make specialised ones.
337    -------------------------------------------------------------------------- */
338
339 #define YIELD(liveness,reentry)                 \
340   {                                             \
341    EF_(stg_gen_yield);                          \
342    R9.w  = (W_)LIVENESS_MASK(liveness);         \
343    R10.w = (W_)reentry;                         \
344    JMP_(stg_gen_yield);                         \
345   }
346
347 #define BLOCK(liveness,reentry)                 \
348   {                                             \
349    EF_(stg_gen_block);                          \
350    R9.w  = (W_)LIVENESS_MASK(liveness);         \
351    R10.w = (W_)reentry;                         \
352    JMP_(stg_gen_block);                         \
353   }
354
355 #define BLOCK_NP(ptrs)                          \
356   {                                             \
357     EF_(stg_bock_##ptrs);                       \
358     JMP_(stg_block_##ptrs);                     \
359   }
360
361 /* -----------------------------------------------------------------------------
362    CCall_GC needs to push a dummy stack frame containing the contents
363    of volatile registers and variables.  
364
365    We use a RET_DYN frame the same as for a dynamic heap check.
366    ------------------------------------------------------------------------- */
367
368 EI_(stg_gen_chk_info);
369
370 /* -----------------------------------------------------------------------------
371    Vectored Returns
372
373    RETVEC(p,t) where 'p' is a pointer to the info table for a
374    vectored return address, returns the address of the return code for
375    tag 't'.
376
377    Return vectors are placed in *reverse order* immediately before the info
378    table for the return address.  Hence the formula for computing the
379    actual return address is (addr - sizeof(InfoTable) - tag - 1).
380    The extra subtraction of one word is because tags start at zero.
381    -------------------------------------------------------------------------- */
382
383 #ifdef USE_MINIINTERPRETER
384 #define RET_VEC(p,t) ((*(stgCast(StgInfoTable*,p)->vector))[t])
385 #else
386 #define RET_VEC(p,t) (*((P_)(p) - sizeofW(StgInfoTable) - t - 1))
387 #endif
388
389 /* -----------------------------------------------------------------------------
390    Misc
391    -------------------------------------------------------------------------- */
392
393 /* set the tag register (if we have one) */
394 #define SET_TAG(t)  /* nothing */
395
396 /* don't do eager blackholing for now */
397 #define UPD_BH_UPDATABLE(thunk)  /* nothing */
398 #define UPD_BH_SINGLE_ENTRY(thunk)  /* nothing */
399
400 /* -----------------------------------------------------------------------------
401    Moving Floats and Doubles
402
403    ASSIGN_FLT is for assigning a float to memory (usually the
404               stack/heap).  The memory address is guaranteed to be
405               StgWord aligned (currently == sizeof(long)).
406
407    PK_FLT     is for pulling a float out of memory.  The memory is
408               guaranteed to be StgWord aligned.
409    -------------------------------------------------------------------------- */
410
411 static inline void        ASSIGN_FLT (W_ [], StgFloat);
412 static inline StgFloat    PK_FLT     (W_ []);
413
414 #if ALIGNMENT_FLOAT <= ALIGNMENT_LONG
415
416 static inline void     ASSIGN_FLT(W_ p_dest[], StgFloat src) { *(StgFloat *)p_dest = src; }
417 static inline StgFloat PK_FLT    (W_ p_src[])                { return *(StgFloat *)p_src; }
418
419 #else  /* ALIGNMENT_FLOAT > ALIGNMENT_UNSIGNED_INT */
420
421 static inline void ASSIGN_FLT(W_ p_dest[], StgFloat src)
422 {
423     float_thing y;
424     y.f = src;
425     *p_dest = y.fu;
426 }
427
428 static inline StgFloat PK_FLT(W_ p_src[])
429 {
430     float_thing y;
431     y.fu = *p_src;
432     return(y.f);
433 }
434
435 #endif /* ALIGNMENT_FLOAT > ALIGNMENT_LONG */
436
437 #if ALIGNMENT_DOUBLE <= ALIGNMENT_LONG
438
439 static inline void        ASSIGN_DBL (W_ [], StgDouble);
440 static inline StgDouble   PK_DBL     (W_ []);
441
442 static inline void      ASSIGN_DBL(W_ p_dest[], StgDouble src) { *(StgDouble *)p_dest = src; }
443 static inline StgDouble PK_DBL    (W_ p_src[])                 { return *(StgDouble *)p_src; }
444
445 #else   /* ALIGNMENT_DOUBLE > ALIGNMENT_LONG */
446
447 /* Sparc uses two floating point registers to hold a double.  We can
448  * write ASSIGN_DBL and PK_DBL by directly accessing the registers
449  * independently - unfortunately this code isn't writable in C, we
450  * have to use inline assembler.
451  */
452 #if sparc_TARGET_ARCH
453
454 #define ASSIGN_DBL(dst,src) \
455       __asm__("st %2,%0\n\tst %R2,%1" : "=m" (((P_)(dst))[0]), \
456         "=m" (((P_)(dst))[1]) : "f" (src));
457
458 #define PK_DBL(src) \
459     ( { register double d; \
460       __asm__("ld %1,%0\n\tld %2,%R0" : "=f" (d) : \
461         "m" (((P_)(src))[0]), "m" (((P_)(src))[1])); d; \
462     } )
463
464 #else /* ! sparc_TARGET_ARCH */
465
466 static inline void        ASSIGN_DBL (W_ [], StgDouble);
467 static inline StgDouble   PK_DBL     (W_ []);
468
469 typedef struct
470   { StgWord dhi;
471     StgWord dlo;
472   } unpacked_double;
473
474 typedef union
475   { StgDouble d;
476     unpacked_double du;
477   } double_thing;
478
479 static inline void ASSIGN_DBL(W_ p_dest[], StgDouble src)
480 {
481     double_thing y;
482     y.d = src;
483     p_dest[0] = y.du.dhi;
484     p_dest[1] = y.du.dlo;
485 }
486
487 /* GCC also works with this version, but it generates
488    the same code as the previous one, and is not ANSI
489
490 #define ASSIGN_DBL( p_dest, src ) \
491         *p_dest = ((double_thing) src).du.dhi; \
492         *(p_dest+1) = ((double_thing) src).du.dlo \
493 */
494
495 static inline StgDouble PK_DBL(W_ p_src[])
496 {
497     double_thing y;
498     y.du.dhi = p_src[0];
499     y.du.dlo = p_src[1];
500     return(y.d);
501 }
502
503 #endif /* ! sparc_TARGET_ARCH */
504
505 #endif /* ALIGNMENT_DOUBLE > ALIGNMENT_UNSIGNED_INT */
506
507 #ifdef SUPPORT_LONG_LONGS
508
509 typedef struct
510   { StgWord dhi;
511     StgWord dlo;
512   } unpacked_double_word;
513
514 typedef union
515   { StgInt64 i;
516     unpacked_double_word iu;
517   } int64_thing;
518
519 typedef union
520   { StgNat64 w;
521     unpacked_double_word wu;
522   } word64_thing;
523
524 static inline void ASSIGN_Word64(W_ p_dest[], StgNat64 src)
525 {
526     word64_thing y;
527     y.w = src;
528     p_dest[0] = y.wu.dhi;
529     p_dest[1] = y.wu.dlo;
530 }
531
532 static inline StgNat64 PK_Word64(W_ p_src[])
533 {
534     word64_thing y;
535     y.wu.dhi = p_src[0];
536     y.wu.dlo = p_src[1];
537     return(y.w);
538 }
539
540 static inline void ASSIGN_Int64(W_ p_dest[], StgInt64 src)
541 {
542     int64_thing y;
543     y.i = src;
544     p_dest[0] = y.iu.dhi;
545     p_dest[1] = y.iu.dlo;
546 }
547
548 static inline StgInt64 PK_Int64(W_ p_src[])
549 {
550     int64_thing y;
551     y.iu.dhi = p_src[0];
552     y.iu.dlo = p_src[1];
553     return(y.i);
554 }
555 #endif
556
557 /* -----------------------------------------------------------------------------
558    Catch frames
559    -------------------------------------------------------------------------- */
560
561 extern const StgPolyInfoTable catch_frame_info;
562
563 /* -----------------------------------------------------------------------------
564    Seq frames
565
566    A seq frame is very like an update frame, except that it doesn't do
567    an update...
568    -------------------------------------------------------------------------- */
569
570 extern const StgPolyInfoTable seq_frame_info;
571
572 #define PUSH_SEQ_FRAME(sp)                                      \
573         {                                                       \
574                 StgSeqFrame *__frame;                           \
575                 TICK_SEQF_PUSHED();                             \
576                 __frame = (StgSeqFrame *)(sp);                  \
577                 SET_HDR_(__frame,&seq_frame_info,CCCS);         \
578                 __frame->link = Su;                             \
579                 Su = (StgUpdateFrame *)__frame;                 \
580         }
581
582 /* -----------------------------------------------------------------------------
583    Split markers
584    -------------------------------------------------------------------------- */
585
586 #if defined(USE_SPLIT_MARKERS)
587 #define __STG_SPLIT_MARKER(n) FN_(__stg_split_marker##n) { }
588 #else
589 #define __STG_SPLIT_MARKER(n) /* nothing */
590 #endif
591
592 /* -----------------------------------------------------------------------------
593    Closure and Info Macros with casting.
594
595    We don't want to mess around with casts in the generated C code, so
596    we use these casting versions of the closure/info tables macros.
597    -------------------------------------------------------------------------- */
598
599 #define SET_HDR_(c,info,ccs) \
600    SET_HDR((StgClosure *)(c),(StgInfoTable *)(info),ccs)
601
602 /* -----------------------------------------------------------------------------
603    Saving context for exit from the STG world, and loading up context
604    on entry to STG code.
605
606    We save all the STG registers (that is, the ones that are mapped to
607    machine registers) in their places in the TSO.  
608
609    The stack registers go into the current stack object, and the heap
610    registers are saved in global locations.
611    -------------------------------------------------------------------------- */
612
613 static __inline__ void
614 SaveThreadState(void)
615 {
616   /* Don't need to save REG_Base, it won't have changed. */
617
618   CurrentTSO->sp       = Sp;
619   CurrentTSO->su       = Su;
620   CurrentTSO->splim    = SpLim;
621   CloseNursery(Hp);
622
623 #if defined(PROFILING)
624   CurrentTSO->prof.CCCS = CCCS;
625 #endif
626 }
627
628 static __inline__ void 
629 LoadThreadState (void)
630 {
631 #ifdef REG_Base
632   BaseReg = &MainRegTable;
633 #endif
634
635   Sp    = CurrentTSO->sp;
636   Su    = CurrentTSO->su;
637   SpLim = CurrentTSO->splim;
638   OpenNursery(Hp,HpLim);
639
640 # if defined(PROFILING)
641   CCCS = CurrentTSO->prof.CCCS;
642 # endif
643 }
644
645 #endif /* STGMACROS_H */
646