1 /* -----------------------------------------------------------------------------
2 * $Id: StgMacros.h,v 1.3 1999/01/21 10:31:43 simonm Exp $
4 * Macros used for writing STG-ish C code.
6 * ---------------------------------------------------------------------------*/
11 /* -----------------------------------------------------------------------------
12 The following macros create function headers.
14 Each basic block is represented by a C function with no arguments.
15 We therefore always begin with either
23 The macros can be used either to define the function itself, or to provide
24 prototypes (by following with a ';').
25 --------------------------------------------------------------------------- */
27 #define STGFUN(f) StgFunPtr f(void)
28 #define STATICFUN(f) static StgFunPtr f(void)
29 #define EXTFUN(f) extern StgFunPtr f(void)
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)
36 #define ED_RO_ extern const
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
44 /* -----------------------------------------------------------------------------
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
50 -------------------------------------------------------------------------- */
53 #define ARGTAG_MAX 16 /* probably arbitrary */
54 #define ARG_TAG(n) (n)
55 #define ARG_SIZE(n) stgCast(StgWord,n)
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),
69 #else /* DEBUG_EXTRA */
82 ARGTAG_MAX = DOUBLE_TAG
85 /* putting this in a .h file generates many copies - but its only a
88 static StgWord stg_arg_size[] = {
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)
100 #define ARG_SIZE(tag) stg_arg_size[stgCast(StgWord,tag)]
102 #endif /* DEBUG_EXTRA */
104 static inline int IS_ARG_TAG( StgWord p );
105 static inline int IS_ARG_TAG( StgWord p ) { return p <= ARGTAG_MAX; }
107 /* -----------------------------------------------------------------------------
110 If (Sp + <n_args>) > Su { JMP_(stg_updatePAP); }
112 Sp points to the topmost used word on the stack, and Su points to
113 the most recently pushed update frame.
115 Remember that <n_args> must include any tagging of unboxed values.
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 -------------------------------------------------------------------------- */
123 #define ARGS_CHK(n) \
124 if ((P_)(Sp + (n)) > (P_)Su) { \
125 JMP_(stg_update_PAP); \
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); \
134 /* -----------------------------------------------------------------------------
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.
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
145 ------------------------------------------------------------------------- */
147 #define STK_CHK(headroom,ret,r,layout,tag_assts) \
148 if (Sp - headroom < SpLim) { \
149 EXTFUN(stg_chk_##layout); \
152 JMP_(stg_chk_##layout); \
155 #define HP_CHK(headroom,ret,r,layout,tag_assts) \
156 if ((Hp += headroom) > HpLim) { \
157 EXTFUN(stg_chk_##layout); \
160 JMP_(stg_chk_##layout); \
162 TICK_ALLOC_HEAP(headroom);
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); \
169 JMP_(stg_chk_##layout); \
171 TICK_ALLOC_HEAP(hp_headroom);
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.
178 Just push R1 and return to the scheduler saying 'EnterGHC'
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').
185 HpLim points to the LAST WORD of valid allocation space.
186 -------------------------------------------------------------------------- */
188 #define STK_CHK_NP(headroom,ptrs,tag_assts) \
189 if ((Sp - (headroom)) < SpLim) { \
190 EXTFUN(stg_gc_enter_##ptrs); \
192 JMP_(stg_gc_enter_##ptrs); \
195 #define HP_CHK_NP(headroom,ptrs,tag_assts) \
196 if ((Hp += (headroom)) > HpLim) { \
197 EXTFUN(stg_gc_enter_##ptrs); \
199 JMP_(stg_gc_enter_##ptrs); \
201 TICK_ALLOC_HEAP(headroom);
203 #define HP_CHK_SEQ_NP(headroom,ptrs,tag_assts) \
204 if ((Hp += (headroom)) > HpLim) { \
205 EXTFUN(stg_gc_seq_##ptrs); \
207 JMP_(stg_gc_seq_##ptrs); \
209 TICK_ALLOC_HEAP(headroom);
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); \
215 JMP_(stg_gc_enter_##ptrs); \
217 TICK_ALLOC_HEAP(hp_headroom);
219 /* Heap checks for branches of a primitive case / unboxed tuple return */
221 #define GEN_HP_CHK_ALT(headroom,lbl,tag_assts) \
222 if ((Hp += (headroom)) > HpLim) { \
227 TICK_ALLOC_HEAP(headroom);
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);
240 #define HP_CHK_L1(headroom,tag_assts) \
241 GEN_HP_CHK_ALT(headroom,stg_gc_d1,tag_assts);
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;)
247 /* -----------------------------------------------------------------------------
250 These are slow, but have the advantage of being usable in a variety
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.
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.
261 Good places to use a generic heap check:
263 - case alternatives (the return address with an SRT is already
266 - primitives (no SRT required).
268 The stack layout is like this:
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).
280 -------------------------------------------------------------------------- */
282 /* VERY MAGIC CONSTANTS!
283 * must agree with code in HeapStackCheck.c, stg_gen_chk
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
292 #define LIVENESS_MASK(ptr_regs) (ALL_NON_PTRS ^ (ptr_regs))
304 #define HP_CHK_GEN(headroom,liveness,reentry,tag_assts) \
305 if ((Hp += (headroom)) > HpLim ) { \
308 R9.w = (W_)LIVENESS_MASK(liveness); \
309 R10.w = (W_)reentry; \
312 TICK_ALLOC_HEAP(headroom);
314 #define STK_CHK_GEN(headroom,liveness,reentry,tag_assts) \
315 if ((Sp - (headroom)) < SpLim) { \
318 R9.w = (W_)LIVENESS_MASK(liveness); \
319 R10.w = (W_)reentry; \
322 TICK_ALLOC_HEAP(headroom);
324 #define MAYBE_GC(liveness,reentry) \
325 if (doYouWantToGC()) { \
327 R9.w = (W_)LIVENESS_MASK(liveness); \
328 R10.w = (W_)reentry; \
332 /* -----------------------------------------------------------------------------
333 Voluntary Yields/Blocks
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 -------------------------------------------------------------------------- */
339 #define YIELD(liveness,reentry) \
341 EF_(stg_gen_yield); \
342 R9.w = (W_)LIVENESS_MASK(liveness); \
343 R10.w = (W_)reentry; \
344 JMP_(stg_gen_yield); \
347 #define BLOCK(liveness,reentry) \
349 EF_(stg_gen_block); \
350 R9.w = (W_)LIVENESS_MASK(liveness); \
351 R10.w = (W_)reentry; \
352 JMP_(stg_gen_block); \
355 #define BLOCK_NP(ptrs) \
357 EF_(stg_bock_##ptrs); \
358 JMP_(stg_block_##ptrs); \
361 /* -----------------------------------------------------------------------------
362 CCall_GC needs to push a dummy stack frame containing the contents
363 of volatile registers and variables.
365 We use a RET_DYN frame the same as for a dynamic heap check.
366 ------------------------------------------------------------------------- */
368 EI_(stg_gen_chk_info);
370 /* -----------------------------------------------------------------------------
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
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 -------------------------------------------------------------------------- */
383 #ifdef USE_MINIINTERPRETER
384 #define RET_VEC(p,t) ((*(stgCast(StgInfoTable*,p)->vector))[t])
386 #define RET_VEC(p,t) (*((P_)(p) - sizeofW(StgInfoTable) - t - 1))
389 /* -----------------------------------------------------------------------------
391 -------------------------------------------------------------------------- */
393 /* set the tag register (if we have one) */
394 #define SET_TAG(t) /* nothing */
396 /* don't do eager blackholing for now */
397 #define UPD_BH_UPDATABLE(thunk) /* nothing */
398 #define UPD_BH_SINGLE_ENTRY(thunk) /* nothing */
400 /* -----------------------------------------------------------------------------
401 Moving Floats and Doubles
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)).
407 PK_FLT is for pulling a float out of memory. The memory is
408 guaranteed to be StgWord aligned.
409 -------------------------------------------------------------------------- */
411 static inline void ASSIGN_FLT (W_ [], StgFloat);
412 static inline StgFloat PK_FLT (W_ []);
414 #if ALIGNMENT_FLOAT <= ALIGNMENT_LONG
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; }
419 #else /* ALIGNMENT_FLOAT > ALIGNMENT_UNSIGNED_INT */
421 static inline void ASSIGN_FLT(W_ p_dest[], StgFloat src)
428 static inline StgFloat PK_FLT(W_ p_src[])
435 #endif /* ALIGNMENT_FLOAT > ALIGNMENT_LONG */
437 #if ALIGNMENT_DOUBLE <= ALIGNMENT_LONG
439 static inline void ASSIGN_DBL (W_ [], StgDouble);
440 static inline StgDouble PK_DBL (W_ []);
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; }
445 #else /* ALIGNMENT_DOUBLE > ALIGNMENT_LONG */
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.
452 #if sparc_TARGET_ARCH
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));
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; \
464 #else /* ! sparc_TARGET_ARCH */
466 static inline void ASSIGN_DBL (W_ [], StgDouble);
467 static inline StgDouble PK_DBL (W_ []);
479 static inline void ASSIGN_DBL(W_ p_dest[], StgDouble src)
483 p_dest[0] = y.du.dhi;
484 p_dest[1] = y.du.dlo;
487 /* GCC also works with this version, but it generates
488 the same code as the previous one, and is not ANSI
490 #define ASSIGN_DBL( p_dest, src ) \
491 *p_dest = ((double_thing) src).du.dhi; \
492 *(p_dest+1) = ((double_thing) src).du.dlo \
495 static inline StgDouble PK_DBL(W_ p_src[])
503 #endif /* ! sparc_TARGET_ARCH */
505 #endif /* ALIGNMENT_DOUBLE > ALIGNMENT_UNSIGNED_INT */
507 #ifdef SUPPORT_LONG_LONGS
512 } unpacked_double_word;
516 unpacked_double_word iu;
521 unpacked_double_word wu;
524 static inline void ASSIGN_Word64(W_ p_dest[], StgNat64 src)
528 p_dest[0] = y.wu.dhi;
529 p_dest[1] = y.wu.dlo;
532 static inline StgNat64 PK_Word64(W_ p_src[])
540 static inline void ASSIGN_Int64(W_ p_dest[], StgInt64 src)
544 p_dest[0] = y.iu.dhi;
545 p_dest[1] = y.iu.dlo;
548 static inline StgInt64 PK_Int64(W_ p_src[])
557 /* -----------------------------------------------------------------------------
559 -------------------------------------------------------------------------- */
561 extern const StgPolyInfoTable catch_frame_info;
563 /* -----------------------------------------------------------------------------
566 A seq frame is very like an update frame, except that it doesn't do
568 -------------------------------------------------------------------------- */
570 extern const StgPolyInfoTable seq_frame_info;
572 #define PUSH_SEQ_FRAME(sp) \
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; \
582 /* -----------------------------------------------------------------------------
584 -------------------------------------------------------------------------- */
586 #if defined(USE_SPLIT_MARKERS)
587 #define __STG_SPLIT_MARKER(n) FN_(__stg_split_marker##n) { }
589 #define __STG_SPLIT_MARKER(n) /* nothing */
592 /* -----------------------------------------------------------------------------
593 Closure and Info Macros with casting.
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 -------------------------------------------------------------------------- */
599 #define SET_HDR_(c,info,ccs) \
600 SET_HDR((StgClosure *)(c),(StgInfoTable *)(info),ccs)
602 /* -----------------------------------------------------------------------------
603 Saving context for exit from the STG world, and loading up context
604 on entry to STG code.
606 We save all the STG registers (that is, the ones that are mapped to
607 machine registers) in their places in the TSO.
609 The stack registers go into the current stack object, and the heap
610 registers are saved in global locations.
611 -------------------------------------------------------------------------- */
613 static __inline__ void
614 SaveThreadState(void)
616 /* Don't need to save REG_Base, it won't have changed. */
620 CurrentTSO->splim = SpLim;
623 #if defined(PROFILING)
624 CurrentTSO->prof.CCCS = CCCS;
628 static __inline__ void
629 LoadThreadState (void)
632 BaseReg = &MainRegTable;
637 SpLim = CurrentTSO->splim;
638 OpenNursery(Hp,HpLim);
640 # if defined(PROFILING)
641 CCCS = CurrentTSO->prof.CCCS;
645 #endif /* STGMACROS_H */