summaryrefslogtreecommitdiff
path: root/ghc/includes/StgMacros.lh
diff options
context:
space:
mode:
Diffstat (limited to 'ghc/includes/StgMacros.lh')
-rw-r--r--ghc/includes/StgMacros.lh2103
1 files changed, 2103 insertions, 0 deletions
diff --git a/ghc/includes/StgMacros.lh b/ghc/includes/StgMacros.lh
new file mode 100644
index 0000000000..f74d18a0e0
--- /dev/null
+++ b/ghc/includes/StgMacros.lh
@@ -0,0 +1,2103 @@
+%
+% (c) The GRASP/AQUA Project, Glasgow University, 1993-1994
+%
+\section[StgMacros]{C macros used in GHC-generated \tr{.hc} files}
+
+\begin{code}
+#ifndef STGMACROS_H
+#define STGMACROS_H
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[StgMacros-abbrev]{Abbreviatory(?) and general macros}
+%* *
+%************************************************************************
+
+Mere abbreviations:
+\begin{code}
+/* for function declarations */
+#define STGFUN(f) F_ f(STG_NO_ARGS)
+#define STATICFUN(f) static F_ f(STG_NO_ARGS)
+
+/* for functions/data that are really external to this module */
+#define EXTFUN(f) extern F_ f(STG_NO_ARGS)
+#define EXTDATA(d) extern W_ d[]
+#define EXTDATA_RO(d) extern const W_ d[] /* read-only */
+
+/* for fwd decls to functions/data somewhere else in this module */
+/* (identical for the mo') */
+#define INTFUN(f) static F_ f(STG_NO_ARGS)
+#define INTDATA(d) extern W_ d[]
+#define INTDATA_RO(d) extern const W_ d[] /* read-only */
+
+/* short forms of most of the above */
+
+#define FN_(f) F_ f(STG_NO_ARGS)
+#define IFN_(f) static F_ f(STG_NO_ARGS)
+#define EF_(f) extern F_ f(STG_NO_ARGS)
+#define ED_(d) extern W_ d[]
+#define ED_RO_(d) extern const W_ d[] /* read-only */
+#define IF_(f) static F_ f(STG_NO_ARGS)
+
+/* GCC is uncooperative about the next one: */
+/* But, the "extern" prevents initialisation... ADR */
+#if defined(__GNUC__)
+#define ID_(d) extern W_ d[]
+#define ID_RO_(d) extern const W_ d[] /* read-only */
+#else
+#define ID_(d) static W_ d[]
+#define ID_RO_(d) static const W_ d[] /* read-only */
+#endif /* not GCC */
+\end{code}
+
+General things; note: general-but-``machine-dependent'' macros are
+given in \tr{StgMachDeps.lh}.
+\begin{code}
+#define STG_MAX(a,b) (((a)>=(b)) ? (a) : (b))
+
+/*
+Macros to combine two short words into a single
+word and split such a word back into two.
+
+Dependent on machine word size :-)
+*/
+
+#define COMBINE_WORDS(word,short1,short2) \
+ do { \
+ ((packed_shorts *)&(word))->wu.s1 = short1; \
+ ((packed_shorts *)&(word))->wu.s2 = short2; \
+ } while(0)
+
+#define SPLIT_WORD(word,short1,short2) \
+ do { \
+ short1 = ((packed_shorts *)&(word))->wu.s1; \
+ short2 = ((packed_shorts *)&(word))->wu.s2; \
+ } while(0)
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[StgMacros-gen-stg]{General STGish macros}
+%* *
+%************************************************************************
+
+Common sizes of vector-return tables.
+
+Claim: don't need fwd decls for return pts in \tr{VECTBL*}, because
+the AbsC flattener ensures that things come out sufficiently
+``backwards''.
+
+\begin{code}
+#ifdef __STG_REV_TBLS__
+#define UNVECTBL(staticp,label,a) /* nothing */
+#else
+#define UNVECTBL(staticp,label,a) \
+EXTFUN(a); \
+staticp const W_ label[] = { \
+ (W_) a \
+};
+#endif
+\end{code}
+
+\begin{code}
+#if defined(USE_SPLIT_MARKERS)
+#define __STG_SPLIT_MARKER(n) FN_(CAT2(__stg_split_marker,n)){ }
+#else
+#define __STG_SPLIT_MARKER(n) /* nothing */
+#endif
+\end{code}
+
+%************************************************************************
+%* *
+\subsection[StgMacros-exceptions]{Exception-checking macros}
+%* *
+%************************************************************************
+
+Argument-satisfaction check, stack(s) overflow check, heap overflow
+check.
+
+The @SUBTRACT(upper, lower)@ macros return a positive result in words
+indicating the amount by which upper is above lower on the stack.
+
+\begin{code}
+#define SUBTRACT_A_STK( upper, lower ) AREL( (lower) - (upper) )
+#define SUBTRACT_B_STK( upper, lower ) BREL( (lower) - (upper) )
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-arg-satis]{Argument-satisfaction checks}
+%* *
+%************************************************************************
+
+@ARGS_CHK(n)@ sees of there are @n@ words of args on the A/B stack.
+If not, it jumps to @UpdatePAP@.
+
+@ARGS_CHK@ args are pre-directionified.
+Notice that we do the comparisons in the form (x < a+n), for
+some constant n. This generates more efficient code (with GCC at least)
+than (x-a < n).
+
+\begin{code}
+#define ARGS_CHK_A(n) \
+ SET_ACTIVITY(ACT_ARGS_CHK); /* SPAT counting */ \
+ if (SuA /*SUBTRACT_A_STK( SpA, SuA )*/ < (SpA+(n))) { \
+ JMP_( UpdatePAP ); \
+ } \
+ SET_ACTIVITY(ACT_TAILCALL)
+
+#define ARGS_CHK_A_LOAD_NODE(n, closure_addr) \
+ SET_ACTIVITY(ACT_ARGS_CHK); /* SPAT counting */ \
+ if (SuA /*SUBTRACT_A_STK( SpA, SuA )*/ < (SpA+(n))) { \
+ Node = (P_) closure_addr; \
+ JMP_( UpdatePAP ); \
+ } \
+ SET_ACTIVITY(ACT_TAILCALL)
+
+
+#define ARGS_CHK_B(n) \
+ SET_ACTIVITY(ACT_ARGS_CHK); /* SPAT counting */ \
+ if (SpB /*SUBTRACT_B_STK( SpB, SuB )*/ < (SuB-(n))) { \
+ JMP_( UpdatePAP ); \
+ } \
+ SET_ACTIVITY(ACT_TAILCALL)
+
+
+#define ARGS_CHK_B_LOAD_NODE(n, closure_addr) \
+ SET_ACTIVITY(ACT_ARGS_CHK); /* SPAT counting */ \
+ if (SpB /*SUBTRACT_B_STK( SpB, SuB )*/ < (SuB-(n))) { \
+ Node = (P_) closure_addr; \
+ JMP_( UpdatePAP ); \
+ } \
+ SET_ACTIVITY(ACT_TAILCALL)
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-stk-chks]{Stack-overflow check}
+%* *
+%************************************************************************
+
+@STK_CHK(a,b)@ [misc args omitted...] checks that we can allocate @a@
+words of A stack and @b@ words of B stack. If not, it calls
+@StackOverflow@ (which dies).
+
+(It will be different in the parallel case.)
+
+NB: args @a@ and @b@ are pre-direction-ified!
+\begin{code}
+extern I_ SqueezeUpdateFrames PROTO((P_, P_, P_));
+
+#if ! defined(CONCURRENT)
+
+extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
+
+#if STACK_CHECK_BY_PAGE_FAULT
+
+#define STACK_OVERFLOW(liveness,hda,hdb,spa,spb,rtype,reenter) \
+ /* use memory protection instead; still need ticky-ness */
+
+#else
+
+#define STACK_OVERFLOW(liveness,hda,hdb,spa,spb,rtype,reenter) \
+ ULTRASAFESTGCALL0(void,(void *),StackOverflow)
+
+#endif /* not using page-faulting */
+
+#else /* threaded */
+
+extern I_ StackOverflow PROTO((W_, W_));
+
+/*
+ * On a uniprocessor, we do *NOT* context switch on a stack overflow
+ * (though we may GC). Therefore, we never have to reenter node.
+ */
+
+#define STACK_OVERFLOW(liveness,hda,hdb,spa,spb,rtype,reenter) \
+ DO_STACKOVERFLOW((hda+hdb)<<2|((rtype)<<1)|(reenter),((spa)<<20)|((spb)<<8)|(liveness))
+
+#define STACK_OVERFLOW_HEADROOM(args,y) ((args) >> 2)
+#define STACK_OVERFLOW_PRIM_RETURN(args,y) ((args) & 2)
+#define STACK_OVERFLOW_REENTER(args,y) ((args) & 1)
+
+#define STACK_OVERFLOW_AWORDS(x,args) (((args) >> 20) & 0x0fff)
+#define STACK_OVERFLOW_BWORDS(x,args) (((args) >> 8) & 0x0fff)
+#define STACK_OVERFLOW_LIVENESS(x,args) ((args) & 0xff)
+
+#endif /* CONCURRENT */
+
+#define STK_CHK(liveness_mask,a_headroom,b_headroom,spa,spb,ret_type,reenter)\
+do { \
+ DO_ASTK_HWM(); /* ticky-ticky profiling */ \
+ DO_BSTK_HWM(); \
+ /* SET_ACTIVITY(ACT_STK_CHK); /? SPAT counting -- no, using page faulting */ \
+ if (STKS_OVERFLOW_OP((a_headroom) + 1, (b_headroom) + 1)) { \
+ STACK_OVERFLOW(liveness_mask,a_headroom,b_headroom,spa,spb,ret_type,reenter);\
+ } \
+}while(0)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-arity-chks]{Arity checks (for debugging)}
+%* *
+%************************************************************************
+
+This is a debugging feature. Each call to fast-entry-point code sets
+@ExpectedArity@ to some value, and the callee then checks that the
+value is as expected.
+
+\begin{code}
+#if defined(__DO_ARITY_CHKS__)
+
+extern I_ ExpectedArity;
+extern void ArityError PROTO((I_)) STG_NORETURN;
+
+#define SET_ARITY(n) do { ExpectedArity = (n); } while(0)
+#define CHK_ARITY(n) \
+ do { \
+ if (ExpectedArity != (n)) { \
+ ULTRASAFESTGCALL1(void,(void *, I_),ArityError,n); \
+ }}while(0)
+
+#else /* ! __DO_ARITY_CHKS__: normal case */
+
+#define SET_ARITY(n) /* nothing */
+#define CHK_ARITY(n) /* nothing */
+
+#endif /* ! __DO_ARITY_CHKS__ */
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-heap-chks]{Heap-overflow checks}
+%* *
+%************************************************************************
+
+Please see the general discussion/commentary about ``what really
+happens in a GC,'' in \tr{SMinterface.lh}.
+
+\begin{code}
+extern void PerformGC PROTO((W_));
+void RealPerformGC PROTO((W_ liveness, W_ reqsize, W_ always_reenter_node, rtsBool do_full_collection));
+void checkInCCallGC(STG_NO_ARGS);
+
+#ifndef PAR
+void StgPerformGarbageCollection(STG_NO_ARGS);
+#endif
+
+#ifndef CONCURRENT
+
+#define OR_MSG_PENDING /* never */
+
+#define HEAP_OVERFLOW(liveness,n,reenter) \
+ do { \
+ SET_ACTIVITY(ACT_GC); /* SPAT profiling */ \
+ DO_GC((((W_)n)<<8)|(liveness)); \
+ SET_ACTIVITY(ACT_GC_STOP); \
+ } while (0)
+
+#define REQSIZE_BITMASK ((1L << ((BITS_IN(W_) - 8 + 1))) - 1)
+#define HEAP_OVERFLOW_REQSIZE(args) (((args) >> 8) & REQSIZE_BITMASK)
+#define HEAP_OVERFLOW_REENTER(args) 0
+#define HEAP_OVERFLOW_LIVENESS(args) ((args) & 0xff)
+
+#else /* CONCURRENT */
+
+extern void ReallyPerformThreadGC PROTO((W_, rtsBool));
+
+#define HEAP_OVERFLOW(liveness,n,reenter) \
+ do { \
+ SET_ACTIVITY(ACT_GC); /* SPAT profiling */ \
+ DO_GC((((W_)(n))<<9)|((reenter)<<8)|(liveness)); \
+ SET_ACTIVITY(ACT_GC_STOP); \
+ } while (0)
+
+#define REQSIZE_BITMASK ((1L << ((BITS_IN(W_) - 9 + 1))) - 1)
+#define HEAP_OVERFLOW_REQSIZE(args) (((args) >> 9) & REQSIZE_BITMASK)
+#define HEAP_OVERFLOW_REENTER(args) (((args) >> 8) & 0x1)
+#define HEAP_OVERFLOW_LIVENESS(args) ((args) & 0xff)
+
+#ifndef PAR
+
+#define OR_MSG_PENDING /* never */
+
+#else
+
+extern int PacketsWaiting; /*Probes for incoming messages*/
+extern int heapChkCounter; /*Not currently used! We check for messages when*/
+ /*a thread is resheduled PWT*/
+/* #define OR_MSG_PENDING || (--heapChkCounter == 0 && PacketsWaiting())*/
+#define OR_MSG_PENDING /* never */
+
+#endif /* PAR */
+#endif /* CONCURRENT */
+
+#if 0 /* alpha_TARGET_ARCH */
+#define CACHE_LINE 4 /* words */
+#define LINES_AHEAD 3
+#define PRE_FETCH(n) \
+do { \
+ StgInt j; \
+ j = ((STG_VOLATILE StgInt *) Hp)[LINES_AHEAD * CACHE_LINE]; \
+} while(0);
+#define EXTRA_HEAP_WORDS (CACHE_LINE * LINES_AHEAD)
+#else
+#define PRE_FETCH(reg)
+#define EXTRA_HEAP_WORDS 0
+#endif
+
+#if defined(GRAN)
+#define HEAP_CHK(liveness_mask,n,reenter) \
+ do { \
+ /* TICKY_PARANOIA(__FILE__, __LINE__); */ \
+ /* THREAD_CONTEXT_SWITCH(liveness_mask,reenter); */ \
+ ALLOC_HEAP(n); /* ticky profiling */ \
+ GRAN_ALLOC_HEAP(n,liveness_mask); /* Granularity Simulation */ \
+ SET_ACTIVITY(ACT_HEAP_CHK); /* SPAT counting */ \
+ if (((Hp = Hp + (n)) > HpLim)) { \
+ /* Old: STGCALL3_GC(PerformGC,liveness_mask,n,StgFalse); */\
+ HEAP_OVERFLOW(liveness_mask,n,StgFalse); \
+ } \
+ SET_ACTIVITY(ACT_REDN); /* back to normal reduction */ \
+ }while(0)
+
+#else
+
+#define HEAP_CHK(liveness_mask,n,reenter) \
+do { \
+ /* TICKY_PARANOIA(__FILE__, __LINE__); */ \
+ PRE_FETCH(n); \
+ ALLOC_HEAP(n); /* ticky profiling */ \
+ SET_ACTIVITY(ACT_HEAP_CHK); /* SPAT counting */ \
+ if (((Hp = Hp + (n)) > HpLim) OR_INTERVAL_EXPIRED OR_CONTEXT_SWITCH OR_MSG_PENDING) { \
+ HEAP_OVERFLOW(liveness_mask,n,reenter); \
+ } \
+} while(0)
+
+#endif /* GRAN */
+
+#ifdef CONCURRENT
+
+#define HEAP_CHK_AND_RESTORE_N(liveness_mask,n,reenter) \
+do { \
+ /* TICKY_PARANOIA(__FILE__, __LINE__); */ \
+ PRE_FETCH(n); \
+ ALLOC_HEAP(n); /* ticky profiling */ \
+ SET_ACTIVITY(ACT_HEAP_CHK); /* SPAT counting */ \
+ if (((Hp = Hp + (n)) > HpLim) OR_INTERVAL_EXPIRED OR_CONTEXT_SWITCH OR_MSG_PENDING) { \
+ HEAP_OVERFLOW(liveness_mask,n,reenter); \
+ n = TSO_ARG1(CurrentTSO); \
+ } \
+ SET_ACTIVITY(ACT_REDN); /* back to normal reduction */\
+} while(0)
+
+#else
+
+#define HEAP_CHK_AND_RESTORE_N(liveness_mask,n,reenter) \
+ HEAP_CHK(liveness_mask,n,reenter)
+
+#endif
+
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[StgMacros-prim-ops]{Primitive operations}
+%* *
+%************************************************************************
+
+One thing to be {\em very careful about} with these macros that assign
+to results is that the assignment must come {\em last}. Some of the
+other arguments may be in terms of addressing modes that get clobbered
+by the assignment. (Dirty imperative programming RULES!)
+
+The order here is roughly that in \tr{compiler/prelude/PrimOps.lhs}.
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-compare-primops]{Primitive comparison ops on basic types}
+%* *
+%************************************************************************
+
+We cast the chars in case one of them is a literal (so C things work right
+even for 8-bit chars).
+\begin{code}
+#define gtCharZh(r,a,b) r=(I_)((a)> (b))
+#define geCharZh(r,a,b) r=(I_)((a)>=(b))
+#define eqCharZh(r,a,b) r=(I_)((a)==(b))
+#define neCharZh(r,a,b) r=(I_)((a)!=(b))
+#define ltCharZh(r,a,b) r=(I_)((a)< (b))
+#define leCharZh(r,a,b) r=(I_)((a)<=(b))
+
+#define gtIntZh(r,a,b) r=(I_)((a) >(b))
+#define geIntZh(r,a,b) r=(I_)((a)>=(b))
+#define eqIntZh(r,a,b) r=(I_)((a)==(b))
+#define neIntZh(r,a,b) r=(I_)((a)!=(b))
+#define ltIntZh(r,a,b) r=(I_)((a) <(b))
+#define leIntZh(r,a,b) r=(I_)((a)<=(b))
+
+#define gtWordZh(r,a,b) r=(I_)((a) >(b))
+#define geWordZh(r,a,b) r=(I_)((a)>=(b))
+#define eqWordZh(r,a,b) r=(I_)((a)==(b))
+#define neWordZh(r,a,b) r=(I_)((a)!=(b))
+#define ltWordZh(r,a,b) r=(I_)((a) <(b))
+#define leWordZh(r,a,b) r=(I_)((a)<=(b))
+
+#define gtAddrZh(r,a,b) r=(I_)((a) >(b))
+#define geAddrZh(r,a,b) r=(I_)((a)>=(b))
+#define eqAddrZh(r,a,b) r=(I_)((a)==(b))
+#define neAddrZh(r,a,b) r=(I_)((a)!=(b))
+#define ltAddrZh(r,a,b) r=(I_)((a) <(b))
+#define leAddrZh(r,a,b) r=(I_)((a)<=(b))
+
+#define gtFloatZh(r,a,b) r=(I_)((a)> (b))
+#define geFloatZh(r,a,b) r=(I_)((a)>=(b))
+#define eqFloatZh(r,a,b) r=(I_)((a)==(b))
+#define neFloatZh(r,a,b) r=(I_)((a)!=(b))
+#define ltFloatZh(r,a,b) r=(I_)((a)< (b))
+#define leFloatZh(r,a,b) r=(I_)((a)<=(b))
+
+#define gtDoubleZh(r,a,b) r=(I_)((a)> (b))
+#define geDoubleZh(r,a,b) r=(I_)((a)>=(b))
+#define eqDoubleZh(r,a,b) r=(I_)((a)==(b))
+#define neDoubleZh(r,a,b) r=(I_)((a)!=(b))
+#define ltDoubleZh(r,a,b) r=(I_)((a)< (b))
+#define leDoubleZh(r,a,b) r=(I_)((a)<=(b))
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-char-primops]{Primitive @Char#@ ops (and @LitString#@ish things, too)}
+%* *
+%************************************************************************
+
+We cast the chars in case one of them is a literal (so C things work right
+even for 8-bit chars).
+\begin{code}
+#define ordZh(r,a) r=(I_)((W_) (a))
+#define chrZh(r,a) r=(StgChar)((W_)(a))
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-int-primops]{Primitive @Int#@ ops}
+%* *
+%************************************************************************
+
+\begin{code}
+I_ stg_div PROTO((I_ a, I_ b));
+
+#define plusIntZh(r,a,b) r=(a)+(b)
+#define minusIntZh(r,a,b) r=(a)-(b)
+#define timesIntZh(r,a,b) r=(a)*(b)
+#define quotIntZh(r,a,b) r=(a)/(b)
+#define divIntZh(r,a,b) r=ULTRASAFESTGCALL2(I_,(void *, I_, I_),stg_div,(a),(b))
+#define remIntZh(r,a,b) r=(a)%(b)
+#define negateIntZh(r,a) r=-(a)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-word-primops]{Primitive @Word#@ ops}
+%* *
+%************************************************************************
+
+\begin{code}
+#define andZh(r,a,b) r=(a)&(b)
+#define orZh(r,a,b) r=(a)|(b)
+#define notZh(r,a) r=~(a)
+
+#define shiftLZh(r,a,b) r=(a)<<(b)
+#define shiftRAZh(r,a,b) r=(a)>>(b)
+#define shiftRLZh(r,a,b) r=(a)>>(b)
+#define iShiftLZh(r,a,b) r=(a)<<(b)
+#define iShiftRAZh(r,a,b) r=(a)>>(b)
+#define iShiftRLZh(r,a,b) r=(a)>>(b)
+
+#define int2WordZh(r,a) r=(W_)(a)
+#define word2IntZh(r,a) r=(I_)(a)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-addr-primops]{Primitive @Addr#@ ops}
+%* *
+%************************************************************************
+
+\begin{code}
+#define int2AddrZh(r,a) r=(A_)(a)
+#define addr2IntZh(r,a) r=(I_)(a)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-float-primops]{Primitive @Float#@ ops}
+%* *
+%************************************************************************
+
+\begin{code}
+#define plusFloatZh(r,a,b) r=(a)+(b)
+#define minusFloatZh(r,a,b) r=(a)-(b)
+#define timesFloatZh(r,a,b) r=(a)*(b)
+#define divideFloatZh(r,a,b) r=(a)/(b)
+#define negateFloatZh(r,a) r=-(a)
+
+#define int2FloatZh(r,a) r=(StgFloat)(a)
+#define float2IntZh(r,a) r=(I_)(a)
+
+#define expFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),exp,a)
+#define logFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),log,a)
+#define sqrtFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),sqrt,a)
+#define sinFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),sin,a)
+#define cosFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),cos,a)
+#define tanFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),tan,a)
+#define asinFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),asin,a)
+#define acosFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),acos,a)
+#define atanFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),atan,a)
+#define sinhFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),sinh,a)
+#define coshFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),cosh,a)
+#define tanhFloatZh(r,a) r=(StgFloat) SAFESTGCALL1(StgDouble,(void *, StgDouble),tanh,a)
+#define powerFloatZh(r,a,b) r=(StgFloat) SAFESTGCALL2(StgDouble,(void *, StgDouble,StgDouble),pow,a,b)
+
+/* encoding/decoding given w/ Integer stuff */
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-double-primops]{Primitive @Double#@ ops}
+%* *
+%************************************************************************
+
+\begin{code}
+#define plusDoubleZh(r,a,b) r=(a)+(b)
+#define minusDoubleZh(r,a,b) r=(a)-(b)
+#define timesDoubleZh(r,a,b) r=(a)*(b)
+#define divideDoubleZh(r,a,b) r=(a)/(b)
+#define negateDoubleZh(r,a) r=-(a)
+
+#define int2DoubleZh(r,a) r=(StgDouble)(a)
+#define double2IntZh(r,a) r=(I_)(a)
+
+#define float2DoubleZh(r,a) r=(StgDouble)(a)
+#define double2FloatZh(r,a) r=(StgFloat)(a)
+
+#define expDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),exp,a)
+#define logDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),log,a)
+#define sqrtDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),sqrt,a)
+#define sinDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),sin,a)
+#define cosDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),cos,a)
+#define tanDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),tan,a)
+#define asinDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),asin,a)
+#define acosDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),acos,a)
+#define atanDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),atan,a)
+#define sinhDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),sinh,a)
+#define coshDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),cosh,a)
+#define tanhDoubleZh(r,a) r=(StgDouble) SAFESTGCALL1(StgDouble,(void *, StgDouble),tanh,a)
+#define powerDoubleZh(r,a,b) r=(StgDouble) SAFESTGCALL2(StgDouble,(void *, StgDouble,StgDouble),pow,a,b)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-integer-primops]{Primitive @Integer@-related ops (GMP stuff)}
+%* *
+%************************************************************************
+
+Dirty macros we use for the real business.
+
+INVARIANT: When one of these macros is called, the only live data is
+tidily on the STG stacks or in the STG registers (the code generator
+ensures this). If there are any pointer-arguments, they will be in
+the first \tr{Ret*} registers (e.g., \tr{da} arg of \tr{gmpTake1Return1}).
+
+OK, here are the real macros:
+\begin{code}
+#define gmpTake1Return1(size_chk_macro, liveness, mpz_op, ar,sr,dr, aa,sa,da) \
+{ MP_INT arg; \
+ MP_INT result; \
+ I_ space = size_chk_macro(sa); \
+ \
+ /* Check that there will be enough heap & make Hp visible to GMP allocator */ \
+ GMP_HEAP_LOOKAHEAD(liveness,space); \
+ \
+ /* Now we can initialise (post possible GC) */ \
+ arg.alloc = (aa); \
+ arg.size = (sa); \
+ arg.d = (unsigned long int *) (BYTE_ARR_CTS(da)); \
+ \
+ SAFESTGCALL1(void,(void *, MP_INT *),mpz_init,&result); \
+ \
+ /* Perform the operation */ \
+ SAFESTGCALL2(void,(void *, MP_INT *, MP_INT *),mpz_op,&result,&arg); \
+ \
+ GMP_HEAP_HANDBACK(); /* restore Hp */ \
+ (ar) = result.alloc; \
+ (sr) = result.size; \
+ (dr) = (B_) (result.d - DATA_HS); \
+ /* pt to *beginning* of object (GMP has been monkeying around in the middle) */ \
+}
+
+
+#define gmpTake2Return1(size_chk_macro, liveness, mpz_op, ar,sr,dr, a1,s1,d1, a2,s2,d2)\
+{ MP_INT arg1; \
+ MP_INT arg2; \
+ MP_INT result; \
+ I_ space = size_chk_macro(s1,s2); \
+ \
+ /* Check that there will be enough heap & make Hp visible to GMP allocator */ \
+ GMP_HEAP_LOOKAHEAD(liveness,space); \
+ \
+ /* Now we can initialise (post possible GC) */ \
+ arg1.alloc = (a1); \
+ arg1.size = (s1); \
+ arg1.d = (unsigned long int *) (BYTE_ARR_CTS(d1)); \
+ arg2.alloc = (a2); \
+ arg2.size = (s2); \
+ arg2.d = (unsigned long int *) (BYTE_ARR_CTS(d2)); \
+ \
+ SAFESTGCALL1(void,(void *, MP_INT *),mpz_init,&result); \
+ \
+ /* Perform the operation */ \
+ SAFESTGCALL3(void,(void *, MP_INT *, MP_INT *, MP_INT *),mpz_op,&result,&arg1,&arg2); \
+ \
+ GMP_HEAP_HANDBACK(); /* restore Hp */ \
+ (ar) = result.alloc; \
+ (sr) = result.size; \
+ (dr) = (B_) (result.d - DATA_HS); \
+ /* pt to *beginning* of object (GMP has been monkeying around in the middle) */ \
+}
+
+#define gmpTake2Return2(size_chk_macro, liveness, mpz_op, ar1,sr1,dr1, ar2,sr2,dr2, a1,s1,d1, a2,s2,d2) \
+{ MP_INT arg1; \
+ MP_INT arg2; \
+ MP_INT result1; \
+ MP_INT result2; \
+ I_ space = size_chk_macro(s1,s2); \
+ \
+ /* Check that there will be enough heap & make Hp visible to GMP allocator */ \
+ GMP_HEAP_LOOKAHEAD(liveness,space); \
+ \
+ /* Now we can initialise (post possible GC) */ \
+ arg1.alloc = (a1); \
+ arg1.size = (s1); \
+ arg1.d = (unsigned long int *) (BYTE_ARR_CTS(d1)); \
+ arg2.alloc = (a2); \
+ arg2.size = (s2); \
+ arg2.d = (unsigned long int *) (BYTE_ARR_CTS(d2)); \
+ \
+ SAFESTGCALL1(void,(void *, MP_INT *),mpz_init,&result1); \
+ SAFESTGCALL1(void,(void *, MP_INT *),mpz_init,&result2); \
+ \
+ /* Perform the operation */ \
+ SAFESTGCALL4(void,(void *, MP_INT *, MP_INT *, MP_INT *, MP_INT *),mpz_op,&result1,&result2,&arg1,&arg2); \
+ \
+ GMP_HEAP_HANDBACK(); /* restore Hp */ \
+ (ar1) = result1.alloc; \
+ (sr1) = result1.size; \
+ (dr1) = (B_) (result1.d - DATA_HS); \
+ (ar2) = result2.alloc; \
+ (sr2) = result2.size; \
+ (dr2) = (B_) (result2.d - DATA_HS); \
+}
+\end{code}
+
+Some handy size-munging macros: sometimes gratuitously {\em conservative}.
+The \tr{+16} is to allow for the initial allocation of \tr{MP_INT} results.
+The \tr{__abs} stuff is because negative-ness of GMP things is encoded
+in their ``size''...
+\begin{code}
+#define __abs(a) (( (a) >= 0 ) ? (a) : (-(a)))
+#define GMP_SIZE_ONE() (2 + DATA_HS + 16)
+#define GMP_SAME_SIZE(a) (__abs(a) + DATA_HS + 16)
+#define GMP_MAX_SIZE(a,b) ((__abs(a) > __abs(b) ? __abs(a) : __abs(b)) + 1 + DATA_HS + 16)
+ /* NB: the +1 is for the carry (or whatever) */
+#define GMP_2MAX_SIZE(a,b) (2 * GMP_MAX_SIZE(a,b))
+#define GMP_ADD_SIZES(a,b) (__abs(a) + __abs(b) + 1 + DATA_HS + 16)
+ /* the +1 may just be paranoia */
+\end{code}
+
+For the Integer/GMP stuff, we have macros that {\em look ahead} for
+some space, but don't actually grab it.
+
+If there are live pointers at the time of the lookahead, the caller
+must make sure they are in \tr{Ret1}, \tr{Ret2}, ..., so they can be
+handled normally. We achieve this by having the code generator {\em
+always} pass args to may-invoke-GC primitives in registers, using the
+normal pointers-first policy. This means that, if we do go to garbage
+collection, everything is already in the Right Place.
+
+Saving and restoring Hp register so the MP allocator can see them. If we are
+performing liftime profiling need to save and restore HpLim as well so that
+it can be bumped if allocation occurs.
+
+The second argument to @GMP_HEAP_LOOKAHEAD@ must be an lvalue so that
+it can be restored from @TSO_ARG1@ after a failed @HEAP_CHK@ in
+threaded land.
+
+\begin{code}
+#define GMP_HEAP_LOOKAHEAD(liveness,n) \
+ do { \
+ HEAP_CHK_AND_RESTORE_N(liveness,n,0); \
+ Hp = Hp - (n); \
+ UN_ALLOC_HEAP(n); /* Undo ticky-ticky */ \
+ SAVE_Hp = Hp; /* Hand over the hp */ \
+ DEBUG_SetGMPAllocBudget(n) \
+ OptSaveHpLimRegister() \
+ }while(0)
+
+#define GMP_HEAP_HANDBACK() \
+ Hp = SAVE_Hp; \
+ DEBUG_ResetGMPAllocBudget() \
+ OptRestoreHpLimRegister()
+\end{code}
+
+\begin{code}
+void *stgAllocForGMP PROTO((size_t size_in_bytes));
+void *stgReallocForGMP PROTO((void *ptr, size_t old_size, size_t new_size));
+void stgDeallocForGMP PROTO((void *ptr, size_t size));
+
+#ifdef ALLOC_DEBUG
+extern StgInt DEBUG_GMPAllocBudget;
+#define DEBUG_SetGMPAllocBudget(n) DEBUG_GMPAllocBudget = (n);
+#define DEBUG_ResetGMPAllocBudget() DEBUG_GMPAllocBudget = 0;
+#else
+#define DEBUG_SetGMPAllocBudget(n) /*nothing*/
+#define DEBUG_ResetGMPAllocBudget() /*nothing*/
+#endif
+\end{code}
+
+\begin{code}
+#if defined (LIFE_PROFILE)
+
+#define OptSaveHpLimRegister() \
+ SAVE_HpLim = HpLim
+#define OptRestoreHpLimRegister() \
+ HpLim = SAVE_HpLim
+
+#else /* ! LIFE_PROFILE */
+
+#define OptSaveHpLimRegister() /* nothing */
+#define OptRestoreHpLimRegister() /* nothing */
+
+#endif /* ! LIFE_PROFILE */
+\end{code}
+
+The real business (defining Integer primops):
+\begin{code}
+#define negateIntegerZh(ar,sr,dr, liveness, aa,sa,da) \
+ gmpTake1Return1(GMP_SAME_SIZE, liveness, mpz_neg, ar,sr,dr, aa,sa,da)
+
+#define plusIntegerZh(ar,sr,dr, liveness, a1,s1,d1, a2,s2,d2) \
+ gmpTake2Return1(GMP_MAX_SIZE, liveness, mpz_add, ar,sr,dr, a1,s1,d1, a2,s2,d2)
+#define minusIntegerZh(ar,sr,dr, liveness, a1,s1,d1, a2,s2,d2) \
+ gmpTake2Return1(GMP_MAX_SIZE, liveness, mpz_sub, ar,sr,dr, a1,s1,d1, a2,s2,d2)
+#define timesIntegerZh(ar,sr,dr, liveness, a1,s1,d1, a2,s2,d2) \
+ gmpTake2Return1(GMP_ADD_SIZES, liveness, mpz_mul, ar,sr,dr, a1,s1,d1, a2,s2,d2)
+
+/* div, mod, quot, rem are defined w/ quotRem & divMod */
+
+#define quotRemIntegerZh(ar1,sr1,dr1, ar2,sr2,dr2, liveness, a1,s1,d1, a2,s2,d2) \
+ gmpTake2Return2(GMP_2MAX_SIZE, liveness, mpz_divmod, ar1,sr1,dr1, ar2,sr2,dr2, a1,s1,d1, a2,s2,d2)
+#define divModIntegerZh(ar1,sr1,dr1, ar2,sr2,dr2, liveness, a1,s1,d1, a2,s2,d2) \
+ gmpTake2Return2(GMP_2MAX_SIZE, liveness, mpz_mdivmod, ar1,sr1,dr1, ar2,sr2,dr2, a1,s1,d1, a2,s2,d2)
+\end{code}
+
+Comparison ops (@<@, @>=@, etc.) are defined in terms of the cmp
+fellow (returns -ve, 0, or +ve).
+\begin{code}
+#define cmpIntegerZh(r, hp, a1,s1,d1, a2,s2,d2) /* calls mpz_cmp */ \
+{ MP_INT arg1; \
+ MP_INT arg2; \
+ /* Does not allocate memory */ \
+ \
+ arg1.alloc = (a1); \
+ arg1.size = (s1); \
+ arg1.d = (unsigned long int *) (BYTE_ARR_CTS(d1)); \
+ arg2.alloc = (a2); \
+ arg2.size = (s2); \
+ arg2.d = (unsigned long int *) (BYTE_ARR_CTS(d2)); \
+ \
+ (r) = SAFESTGCALL2(I_,(void *, MP_INT *, MP_INT *),mpz_cmp,&arg1,&arg2); \
+}
+\end{code}
+
+Coercions:
+
+\begin{code}
+#define integer2IntZh(r, hp, aa,sa,da) \
+{ MP_INT arg; \
+ /* Does not allocate memory */ \
+ \
+ arg.alloc = (aa); \
+ arg.size = (sa); \
+ arg.d = (unsigned long int *) (BYTE_ARR_CTS(da)); \
+ \
+ (r) = SAFESTGCALL1(I_,(void *, MP_INT *),mpz_get_si,&arg); \
+}
+
+/* Since we're forced to know a little bit about MP_INT layout to do this with
+ pre-allocated heap, we just inline the whole of mpz_init_set_si here.
+ ** DIRE WARNING. if mpz_init_set_si changes, so does this! ***
+*/
+
+#define int2IntegerZh(ar,sr,dr, hp, i) \
+{ StgInt val; /* to snaffle arg to avoid aliasing */ \
+ \
+ val = (i); /* snaffle... */ \
+ \
+ SET_DATA_HDR((hp),ArrayOfData_info,CCC,DATA_VHS+MIN_MP_INT_SIZE,0); \
+ \
+ if ((val) < 0) { (sr) = -1; (hp)[DATA_HS] = -(val); } \
+ else if ((val) > 0) { (sr) = 1; (hp)[DATA_HS] = (val); } \
+ else /* val==0 */ { (sr) = 0; } \
+ (ar) = 1; \
+ (dr) = (B_)(hp); /* dr is an StgByteArray */ \
+}
+
+#define word2IntegerZh(ar,sr,dr, hp, i) \
+{ StgWord val; /* to snaffle arg to avoid aliasing */ \
+ \
+ val = (i); /* snaffle... */ \
+ \
+ SET_DATA_HDR((hp),ArrayOfData_info,CCC,DATA_VHS+MIN_MP_INT_SIZE,0); \
+ \
+ if ((val) != 0) { (sr) = 1; (hp)[DATA_HS] = (val); } \
+ else /* val==0 */ { (sr) = 0; } \
+ (ar) = 1; \
+ (dr) = (B_)(hp); /* dr is an StgByteArray */ \
+}
+
+\end{code}
+
+Then there are a few oddments to make life easier:
+\begin{code}
+/*
+ DIRE WARNING.
+ The "str" argument must be a literal C string.
+
+ addr2Integer( ..., "foo") OK!
+
+ x = "foo";
+ addr2Integer( ..., x) NO! NO!
+*/
+
+#define addr2IntegerZh(ar,sr,dr, liveness, str) \
+{ MP_INT result; \
+ /* taking the number of bytes/8 as the number of words of lookahead \
+ is plenty conservative */ \
+ I_ space = GMP_SAME_SIZE(sizeof(str) / 8 + 1); \
+ \
+ GMP_HEAP_LOOKAHEAD(liveness, space); \
+ \
+ /* Perform the operation */ \
+ if (SAFESTGCALL3(I_,(void *, MP_INT *, char *, int), mpz_init_set_str,&result,(str),/*base*/10)) \
+ abort(); \
+ \
+ GMP_HEAP_HANDBACK(); /* restore Hp */ \
+ (ar) = result.alloc; \
+ (sr) = result.size; \
+ (dr) = (B_) (result.d - DATA_HS); \
+ /* pt to *beginning* of object (GMP has been monkeying around in the middle) */ \
+}
+\end{code}
+
+Encoding and decoding float-ish things is pretty Integer-ish. We use
+these pretty magical support functions, essentially stolen from Lennart:
+\begin{code}
+StgFloat __encodeFloat PROTO((MP_INT *, I_));
+void __decodeFloat PROTO((MP_INT * /*result1*/,
+ I_ * /*result2*/,
+ StgFloat));
+
+StgDouble __encodeDouble PROTO((MP_INT *, I_));
+void __decodeDouble PROTO((MP_INT * /*result1*/,
+ I_ * /*result2*/,
+ StgDouble));
+\end{code}
+
+Some floating-point format info, made with the \tr{enquire} program
+(version~4.3) [comes with gcc].
+\begin{code}
+/* this should be done by CPU architecture, insofar as possible [WDP] */
+
+#if sparc_TARGET_ARCH \
+ || alpha_TARGET_ARCH \
+ || hppa1_1_TARGET_ARCH \
+ || i386_TARGET_ARCH \
+ || i486_TARGET_ARCH \
+ || m68k_TARGET_ARCH \
+ || mipsel_TARGET_ARCH \
+ || mipseb_TARGET_ARCH \
+ || rs6000_TARGET_ARCH
+
+/* yes, it is IEEE floating point */
+#include "ieee-flpt.h"
+
+#if alpha_dec_osf1_TARGET \
+ || i386_TARGET_ARCH \
+ || i486_TARGET_ARCH \
+ || mipsel_TARGET_ARCH
+
+#undef BIGENDIAN /* little-endian weirdos... */
+#else
+#define BIGENDIAN 1
+#endif
+
+#else /* unknown floating-point format */
+
+******* ERROR *********** Any ideas about floating-point format?
+
+#endif /* unknown floating-point */
+\end{code}
+
+\begin{code}
+#if alpha_dec_osf1_TARGET
+#define encodeFloatZh(r, hp, aa,sa,da, expon) encodeDoubleZh(r, hp, aa,sa,da, expon)
+#else
+#define encodeFloatZh(r, hp, aa,sa,da, expon) \
+{ MP_INT arg; \
+ /* Does not allocate memory */ \
+ \
+ arg.alloc = aa; \
+ arg.size = sa; \
+ arg.d = (unsigned long int *) (BYTE_ARR_CTS(da)); \
+ \
+ r = SAFESTGCALL2(StgFloat,(void *, MP_INT *, I_), __encodeFloat,&arg,(expon)); \
+}
+#endif /* ! alpha */
+
+#define encodeDoubleZh(r, hp, aa,sa,da, expon) \
+{ MP_INT arg; \
+ /* Does not allocate memory */ \
+ \
+ arg.alloc = aa; \
+ arg.size = sa; \
+ arg.d = (unsigned long int *) (BYTE_ARR_CTS(da)); \
+ \
+ r = SAFESTGCALL2(StgDouble,(void *, MP_INT *, I_), __encodeDouble,&arg,(expon));\
+}
+
+#if alpha_dec_osf1_TARGET
+#define decodeFloatZh(exponr, ar,sr,dr, hp, f) decodeDoubleZh(exponr, ar,sr,dr, hp, f)
+#else
+#define decodeFloatZh(exponr, ar,sr,dr, hp, f) \
+{ MP_INT mantissa; \
+ I_ exponent; \
+ StgFloat arg = (f); \
+ \
+ /* Be prepared to tell Lennart-coded __decodeFloat */ \
+ /* where mantissa.d can be put (it does not care about the rest) */ \
+ SET_DATA_HDR(hp,ArrayOfData_info,CCC,DATA_VHS+MIN_MP_INT_SIZE,0); \
+ mantissa.d = (hp) + DATA_HS; \
+ \
+ /* Perform the operation */ \
+ SAFESTGCALL3(void,(void *, MP_INT *, I_ *, StgFloat),__decodeFloat,&mantissa,&exponent,arg); \
+ exponr= exponent; \
+ ar = mantissa.alloc; \
+ sr = mantissa.size; \
+ dr = (B_)(hp); \
+}
+#endif /* !alpha */
+
+#define decodeDoubleZh(exponr, ar,sr,dr, hp, f) \
+{ MP_INT mantissa; \
+ I_ exponent; \
+ StgDouble arg = (f); \
+ \
+ /* Be prepared to tell Lennart-coded __decodeDouble */ \
+ /* where mantissa.d can be put (it does not care about the rest) */ \
+ SET_DATA_HDR(hp,ArrayOfData_info,CCC,DATA_VHS+MIN_MP_INT_SIZE,0); \
+ mantissa.d = (hp) + DATA_HS; \
+ \
+ /* Perform the operation */ \
+ SAFESTGCALL3(void,(void *, MP_INT *, I_ *, StgDouble),__decodeDouble,&mantissa,&exponent,arg); \
+ exponr= exponent; \
+ ar = mantissa.alloc; \
+ sr = mantissa.size; \
+ dr = (B_)(hp); \
+}
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-mv-floats]{Moving floats and doubles around (e.g., to/from stacks)}
+%* *
+%************************************************************************
+
+With GCC, we use magic non-standard inlining; for other compilers, we
+just use functions (see also \tr{runtime/prims/PrimArith.lc}).
+
+(The @OMIT_...@ is only used in compiling some of the RTS, none of
+which uses these anyway.)
+
+\begin{code}
+#if alpha_TARGET_ARCH \
+ || i386_TARGET_ARCH \
+ || i486_TARGET_ARCH \
+ || m68k_TARGET_ARCH
+
+#define ASSIGN_FLT(dst, src) *(StgFloat *)(dst) = (src);
+#define PK_FLT(src) (*(StgFloat *)(src))
+
+#define ASSIGN_DBL(dst, src) *(StgDouble *)(dst) = (src);
+#define PK_DBL(src) (*(StgDouble *)(src))
+
+#else /* not m68k || alpha || i[34]86 */
+
+/* Special handling for machines with troublesome alignment constraints */
+
+#define FLOAT_ALIGNMENT_TROUBLES TRUE
+
+#if ! defined(__GNUC__) || ! defined(__STG_GCC_REGS__)
+
+extern void ASSIGN_DBL PROTO((W_ [], StgDouble));
+extern StgDouble PK_DBL PROTO((W_ []));
+extern void ASSIGN_FLT PROTO((W_ [], StgFloat));
+extern StgFloat PK_FLT PROTO((W_ []));
+
+#else /* yes, its __GNUC__ && we really want them */
+
+#if sparc_TARGET_ARCH
+
+#define ASSIGN_FLT(dst, src) *(StgFloat *)(dst) = (src);
+#define PK_FLT(src) (*(StgFloat *)(src))
+
+#define ASSIGN_DBL(dst,src) \
+ __asm__("st %2,%0\n\tst %R2,%1" : "=m" (((P_)(dst))[0]), \
+ "=m" (((P_)(dst))[1]) : "f" (src));
+
+#define PK_DBL(src) \
+ ( { register double d; \
+ __asm__("ld %1,%0\n\tld %2,%R0" : "=f" (d) : \
+ "m" (((P_)(src))[0]), "m" (((P_)(src))[1])); d; \
+ } )
+
+#else /* ! sparc */
+
+extern STG_INLINE
+void
+ASSIGN_DBL(W_ p_dest[], StgDouble src)
+{
+ double_thing y;
+ y.d = src;
+ p_dest[0] = y.du.dhi;
+ p_dest[1] = y.du.dlo;
+}
+
+/* GCC also works with this version, but it generates
+ the same code as the previous one, and is not ANSI
+
+#define ASSIGN_DBL( p_dest, src ) \
+ *p_dest = ((double_thing) src).du.dhi; \
+ *(p_dest+1) = ((double_thing) src).du.dlo \
+*/
+
+extern STG_INLINE
+StgDouble
+PK_DBL(W_ p_src[])
+{
+ double_thing y;
+ y.du.dhi = p_src[0];
+ y.du.dlo = p_src[1];
+ return(y.d);
+}
+
+extern STG_INLINE
+void
+ASSIGN_FLT(W_ p_dest[], StgFloat src)
+{
+ float_thing y;
+ y.f = src;
+ *p_dest = y.fu;
+}
+
+extern STG_INLINE
+StgFloat
+PK_FLT(W_ p_src[])
+{
+ float_thing y;
+ y.fu = *p_src;
+ return(y.f);
+}
+
+#endif /* ! sparc */
+
+#endif /* __GNUC__ */
+
+#endif /* not __m68k__ */
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-array-primops]{Primitive arrays}
+%* *
+%************************************************************************
+
+We regularly use this macro to fish the ``contents'' part
+out of a DATA or TUPLE closure, which is what is used for
+non-ptr and ptr arrays (respectively).
+
+BYTE_ARR_CTS returns a @C_ *@!
+
+We {\em ASSUME} we can use the same macro for both!!
+\begin{code}
+
+#ifdef DEBUG
+#define BYTE_ARR_CTS(a) \
+ ({ ASSERT(INFO_PTR(a) == (W_) ArrayOfData_info); \
+ ((C_ *) (((StgPtr) (a))+DATA_HS)); })
+#define PTRS_ARR_CTS(a) \
+ ({ ASSERT((INFO_PTR(a) == (W_) ArrayOfPtrs_info) \
+ || (INFO_PTR(a) == (W_) ImMutArrayOfPtrs_info));\
+ ((a)+MUTUPLE_HS);} )
+#else
+#define BYTE_ARR_CTS(a) ((char *) (((StgPtr) (a))+DATA_HS))
+#define PTRS_ARR_CTS(a) ((a)+MUTUPLE_HS)
+#endif
+
+/* sigh */
+extern I_ genSymZh(STG_NO_ARGS);
+extern I_ resetGenSymZh(STG_NO_ARGS);
+extern I_ incSeqWorldZh(STG_NO_ARGS);
+
+/* sigh again: without these some (notably "float") willnae work */
+extern I_ long2bytes__ PROTO((long, unsigned char *));
+extern I_ int2bytes__ PROTO((int, unsigned char *));
+extern I_ short2bytes__ PROTO((short, unsigned char *));
+extern I_ float2bytes__ PROTO((float, unsigned char *));
+extern I_ double2bytes__ PROTO((double, unsigned char *));
+
+/* these may not be necessary; and they create warnings (WDP) */
+extern I_ bytes2long__ PROTO((P_, I_ *));
+extern I_ bytes2int__ PROTO((P_, I_ *));
+extern I_ bytes2short__ PROTO((P_, I_ *));
+extern I_ bytes2float__ PROTO((P_, StgFloat *));
+extern I_ bytes2double__ PROTO((P_, StgDouble *));
+
+extern I_ byteArrayHasNUL__ PROTO((const char *, I_));
+\end{code}
+
+OK, the easy ops first: (all except \tr{newArr*}:
+
+VERY IMPORTANT! The read/write/index primitive ops
+on @ByteArray#@s index the array using a {\em BYTE} offset, even
+if the thing begin gotten out is a multi-byte @Int#@, @Float#@ etc.
+This is because you might be trying to take apart a C struct, where
+the offset from the start of the struct isn't a multiple of the
+size of the thing you're getting. Hence the @(char *)@ casts.
+
+In the case of messing with @StgAddrs@ (@A_@), which are really \tr{void *},
+we cast to @P_@, because you can't index off an uncast \tr{void *}.
+
+In the case of @Array#@ (which contain pointers), the offset is in units
+of one ptr (not bytes).
+
+\begin{code}
+#define sameMutableArrayZh(r,a,b) r=(I_)((a)==(b))
+#define sameMutableByteArrayZh(r,a,b) r=(I_)((B_)(a)==(B_)(b))
+
+#define readArrayZh(r,a,i) r=((PP_) PTRS_ARR_CTS(a))[(i)]
+
+#define readCharArrayZh(r,a,i) indexCharOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define readIntArrayZh(r,a,i) indexIntOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define readAddrArrayZh(r,a,i) indexAddrOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define readFloatArrayZh(r,a,i) indexFloatOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define readDoubleArrayZh(r,a,i) indexDoubleOffAddrZh(r,BYTE_ARR_CTS(a),i)
+
+/* result ("r") arg ignored in write macros! */
+#define writeArrayZh(a,i,v) ((PP_) PTRS_ARR_CTS(a))[(i)]=(v)
+
+#define writeCharArrayZh(a,i,v) ((C_ *)(BYTE_ARR_CTS(a)))[i] = (v)
+#define writeIntArrayZh(a,i,v) ((I_ *)(BYTE_ARR_CTS(a)))[i] = (v)
+#define writeAddrArrayZh(a,i,v) ((PP_)(BYTE_ARR_CTS(a)))[i] = (v)
+#define writeFloatArrayZh(a,i,v) \
+ ASSIGN_FLT((P_) (((StgFloat *)(BYTE_ARR_CTS(a))) + i),v)
+#define writeDoubleArrayZh(a,i,v) \
+ ASSIGN_DBL((P_) (((StgDouble *)(BYTE_ARR_CTS(a))) + i),v)
+
+#define indexArrayZh(r,a,i) r=((PP_) PTRS_ARR_CTS(a))[(i)]
+
+#define indexCharArrayZh(r,a,i) indexCharOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define indexIntArrayZh(r,a,i) indexIntOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define indexAddrArrayZh(r,a,i) indexAddrOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define indexFloatArrayZh(r,a,i) indexFloatOffAddrZh(r,BYTE_ARR_CTS(a),i)
+#define indexDoubleArrayZh(r,a,i) indexDoubleOffAddrZh(r,BYTE_ARR_CTS(a),i)
+
+#define indexCharOffAddrZh(r,a,i) r= ((C_ *)(a))[i]
+#define indexIntOffAddrZh(r,a,i) r= ((I_ *)(a))[i]
+#define indexAddrOffAddrZh(r,a,i) r= ((PP_)(a))[i]
+#define indexFloatOffAddrZh(r,a,i) r= PK_FLT((P_) (((StgFloat *)(a)) + i))
+#define indexDoubleOffAddrZh(r,a,i) r= PK_DBL((P_) (((StgDouble *)(a)) + i))
+
+/* Freezing arrays-of-ptrs requires changing an info table, for the
+ benefit of the generational collector. It needs to scavenge mutable
+ objects, even if they are in old space. When they become immutable,
+ they can be removed from this scavenge list. */
+#define unsafeFreezeArrayZh(r,a) \
+ do { \
+ P_ result; \
+ result=(P_) (a); \
+ FREEZE_MUT_HDR(result,ImMutArrayOfPtrs_info); \
+ r = result; \
+ }while(0)
+
+#define unsafeFreezeByteArrayZh(r,a) r=(B_)(a)
+\end{code}
+
+Now the \tr{newArr*} ops:
+
+\begin{code}
+/*
+--------------------
+Will: ToDo: we need to find suitable places to put this comment, and the
+"in-general" one which follows.
+
+************ Nota Bene. The "n" in this macro is guaranteed to
+be a register, *not* (say) Node[1]. That means that it is guaranteed
+to survive GC, provided only that the register is kept unaltered.
+This is important, because "n" is used after the HEAP_CHK.
+
+In general, *all* parameters to these primitive-op macros are always
+registers. (Will: For exactly *which* primitive-op macros is this guaranteed?
+Exactly those which can trigger GC?)
+------------------------
+
+NOTE: the above may now be OLD (WDP 94/02/10)
+*/
+\end{code}
+
+For char arrays, the size is in {\em BYTES}.
+
+\begin{code}
+#define newCharArrayZh(r,liveness,n) newByteArray(r,liveness,(n) * sizeof(C_))
+#define newIntArrayZh(r,liveness,n) newByteArray(r,liveness,(n) * sizeof(I_))
+#define newAddrArrayZh(r,liveness,n) newByteArray(r,liveness,(n) * sizeof(P_))
+#define newFloatArrayZh(r,liveness,n) newByteArray(r,liveness,(n) * sizeof(StgFloat))
+#define newDoubleArrayZh(r,liveness,n) newByteArray(r,liveness,(n) * sizeof(StgDouble))
+
+#define newByteArray(r,liveness,n) \
+{ \
+ P_ result; \
+ I_ size; \
+ \
+ HEAP_CHK(liveness,DATA_HS+BYTES_TO_STGWORDS(n),0); \
+ size = BYTES_TO_STGWORDS(n); \
+ ALLOC_PRIM(DATA_HS,size,0,DATA_HS+size) /* ticky ticky */; \
+ CC_ALLOC(CCC,DATA_HS+size,ARR_K); \
+ \
+ result = Hp-(DATA_HS+size)+1; \
+ SET_DATA_HDR(result,ArrayOfData_info,CCC,DATA_VHS+size,0); \
+ r = (B_) result; \
+}
+\end{code}
+
+Arrays of pointers need to be initialised; uses \tr{TUPLES}!
+The initialisation value is guaranteed to be in a register,
+and will be indicated by the liveness mask, so it's ok to do
+a \tr{HEAP_CHK}, which may trigger GC.
+
+\begin{code}
+/* The new array initialization routine for the NCG */
+void newArrZh_init PROTO((P_ result, I_ n, P_ init));
+
+#define newArrayZh(r,liveness,n,init) \
+{ \
+ P_ p; \
+ P_ result; \
+ \
+ HEAP_CHK(liveness, MUTUPLE_HS+(n),0); \
+ ALLOC_PRIM(MUTUPLE_HS,(n),0,MUTUPLE_HS+(n)) /* ticky ticky */; \
+ CC_ALLOC(CCC,MUTUPLE_HS+(n),ARR_K); /* cc prof */ \
+ \
+ result = Hp + 1 - (MUTUPLE_HS+(n)); \
+ SET_MUTUPLE_HDR(result,ArrayOfPtrs_info,CCC,MUTUPLE_VHS+(n),0) \
+ for (p = result+MUTUPLE_HS; p < (result+MUTUPLE_HS+(n)); p++) { \
+ *p = (W_) (init); \
+ } \
+ SET_ACTIVITY(ACT_REDN); /* back to normal reduction */\
+ \
+ r = result; \
+}
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-SynchVar-primops]{Synchronizing Variables PrimOps}
+%* *
+%************************************************************************
+
+\begin{code}
+ED_(Nil_closure);
+
+#define newSynchVarZh(r, hp) \
+{ \
+ ALLOC_PRIM(MUTUPLE_HS,3,0,MUTUPLE_HS+3) /* ticky ticky */; \
+ CC_ALLOC(CCC,MUTUPLE_HS+3,ARR_K); /* cc prof */ \
+ SET_SVAR_HDR(hp,EmptySVar_info,CCC); \
+ SVAR_HEAD(hp) = SVAR_TAIL(hp) = SVAR_VALUE(hp) = Nil_closure; \
+ r = hp; \
+}
+\end{code}
+
+\begin{code}
+#ifdef CONCURRENT
+
+extern void Yield PROTO((W_));
+
+#define takeMVarZh(r, liveness, node) \
+{ \
+ while (INFO_PTR(node) != (W_) FullSVar_info) { \
+ if (SVAR_HEAD(node) == Nil_closure) \
+ SVAR_HEAD(node) = CurrentTSO; \
+ else \
+ TSO_LINK(SVAR_TAIL(node)) = CurrentTSO; \
+ TSO_LINK(CurrentTSO) = (P_) Nil_closure; \
+ SVAR_TAIL(node) = CurrentTSO; \
+ DO_YIELD(liveness << 1); \
+ } \
+ SET_INFO_PTR(node, EmptySVar_info); \
+ r = SVAR_VALUE(node); \
+ SVAR_VALUE(node) = Nil_closure; \
+}
+
+#else
+
+#define takeMVarZh(r, liveness, node) \
+{ \
+ if (INFO_PTR(node) != (W_) FullSVar_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "takeMVar#: MVar is empty.\n"); \
+ EXIT(EXIT_FAILURE); \
+ } \
+ SET_INFO_PTR(node, EmptySVar_info); \
+ r = SVAR_VALUE(node); \
+ SVAR_VALUE(node) = Nil_closure; \
+}
+
+#endif
+\end{code}
+
+\begin{code}
+#ifdef CONCURRENT
+
+#ifdef GRAN
+
+/* Only difference to the !GRAN def: RunnableThreadsHd has been replaced by */
+/* ThreadQueueHd i.e. the tso is added at the end of the thread queue on */
+/* the CurrentProc. This means we have an implicit context switch after */
+/* putMVar even if unfair scheduling is used in GranSim (default)! -- HWL */
+
+#define putMVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) FullSVar_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "putMVar#: MVar already full.\n"); \
+ EXIT(EXIT_FAILURE); \
+ } \
+ SET_INFO_PTR(node, FullSVar_info); \
+ SVAR_VALUE(node) = value; \
+ tso = SVAR_HEAD(node); \
+ if (tso != (P_) Nil_closure) { \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ if (ThreadQueueHd == Nil_closure) \
+ ThreadQueueHd = tso; \
+ else \
+ TSO_LINK(ThreadQueueTl) = tso; \
+ ThreadQueueTl = tso; \
+ SVAR_HEAD(node) = TSO_LINK(tso); \
+ TSO_LINK(tso) = (P_) Nil_closure; \
+ if(SVAR_HEAD(node) == (P_) Nil_closure) \
+ SVAR_TAIL(node) = (P_) Nil_closure; \
+ } \
+}
+
+#else /* !GRAN */
+
+#define putMVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) FullSVar_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "putMVar#: MVar already full.\n"); \
+ EXIT(EXIT_FAILURE); \
+ } \
+ SET_INFO_PTR(node, FullSVar_info); \
+ SVAR_VALUE(node) = value; \
+ tso = SVAR_HEAD(node); \
+ if (tso != (P_) Nil_closure) { \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ if (RunnableThreadsHd == Nil_closure) \
+ RunnableThreadsHd = tso; \
+ else \
+ TSO_LINK(RunnableThreadsTl) = tso; \
+ RunnableThreadsTl = tso; \
+ SVAR_HEAD(node) = TSO_LINK(tso); \
+ TSO_LINK(tso) = (P_) Nil_closure; \
+ if(SVAR_HEAD(node) == (P_) Nil_closure) \
+ SVAR_TAIL(node) = (P_) Nil_closure; \
+ } \
+}
+
+#endif /* GRAN */
+
+#else
+
+#define putMVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) FullSVar_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "putMVar#: MVar already full.\n"); \
+ EXIT(EXIT_FAILURE); \
+ } \
+ SET_INFO_PTR(node, FullSVar_info); \
+ SVAR_VALUE(node) = value; \
+}
+
+#endif
+\end{code}
+
+\begin{code}
+#ifdef CONCURRENT
+
+#define readIVarZh(r, liveness, node) \
+{ \
+ if (INFO_PTR(node) != (W_) ImMutArrayOfPtrs_info) { \
+ if (SVAR_HEAD(node) == Nil_closure) \
+ SVAR_HEAD(node) = CurrentTSO; \
+ else \
+ TSO_LINK(SVAR_TAIL(node)) = CurrentTSO; \
+ TSO_LINK(CurrentTSO) = (P_) Nil_closure; \
+ SVAR_TAIL(node) = CurrentTSO; \
+ DO_YIELD(liveness << 1); \
+ } \
+ r = SVAR_VALUE(node); \
+}
+
+#else
+
+#define readIVarZh(r, liveness, node) \
+{ \
+ if (INFO_PTR(node) != (W_) ImMutArrayOfPtrs_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "readIVar#: IVar is empty.\n"); \
+ EXIT(EXIT_FAILURE); \
+ } \
+ r = SVAR_VALUE(node); \
+}
+
+#endif
+\end{code}
+
+\begin{code}
+#ifdef CONCURRENT
+
+#ifdef GRAN
+
+/* Only difference to the !GRAN def: RunnableThreadsHd has been replaced by */
+/* ThreadQueueHd i.e. the tso is added at the end of the thread queue on */
+/* the CurrentProc. This means we have an implicit context switch after */
+/* writeIVar even if unfair scheduling is used in GranSim (default)! -- HWL */
+
+#define writeIVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) ImMutArrayOfPtrs_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "writeIVar#: IVar already full.\n");\
+ EXIT(EXIT_FAILURE); \
+ } \
+ tso = SVAR_HEAD(node); \
+ if (tso != (P_) Nil_closure) { \
+ if (ThreadQueueHd == Nil_closure) \
+ ThreadQueueHd = tso; \
+ else \
+ TSO_LINK(ThreadQueueTl) = tso; \
+ while(TSO_LINK(tso) != Nil_closure) { \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ tso = TSO_LINK(tso); \
+ } \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ ThreadQueueTl = tso; \
+ } \
+ /* Don't use freeze, since it's conditional on GC */ \
+ SET_INFO_PTR(node, ImMutArrayOfPtrs_info); \
+ MUTUPLE_CLOSURE_SIZE(node) = (MUTUPLE_VHS+1); \
+ SVAR_VALUE(node) = value; \
+}
+
+#else /* !GRAN */
+
+#define writeIVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) ImMutArrayOfPtrs_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "writeIVar#: IVar already full.\n");\
+ EXIT(EXIT_FAILURE); \
+ } \
+ tso = SVAR_HEAD(node); \
+ if (tso != (P_) Nil_closure) { \
+ if (RunnableThreadsHd == Nil_closure) \
+ RunnableThreadsHd = tso; \
+ else \
+ TSO_LINK(RunnableThreadsTl) = tso; \
+ while(TSO_LINK(tso) != Nil_closure) { \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ tso = TSO_LINK(tso); \
+ } \
+ if (DO_QP_PROF) \
+ STGCALL3(void,(void *, char *, P_, P_),QP_Event2,do_qp_prof > 1 ? "RA" : "RG",tso,CurrentTSO); \
+ RunnableThreadsTl = tso; \
+ } \
+ /* Don't use freeze, since it's conditional on GC */ \
+ SET_INFO_PTR(node, ImMutArrayOfPtrs_info); \
+ MUTUPLE_CLOSURE_SIZE(node) = (MUTUPLE_VHS+1); \
+ SVAR_VALUE(node) = value; \
+}
+
+#endif /* GRAN */
+
+#else
+
+#define writeIVarZh(node, value) \
+{ \
+ P_ tso; \
+ if (INFO_PTR(node) == (W_) ImMutArrayOfPtrs_info) { \
+ /* Don't wrap the calls; we're done with STG land */\
+ fflush(stdout); \
+ fprintf(stderr, "writeIVar#: IVar already full.\n");\
+ EXIT(EXIT_FAILURE); \
+ } \
+ /* Don't use freeze, since it's conditional on GC */ \
+ SET_INFO_PTR(node, ImMutArrayOfPtrs_info); \
+ MUTUPLE_CLOSURE_SIZE(node) = (MUTUPLE_VHS+1); \
+ SVAR_VALUE(node) = value; \
+}
+
+#endif
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-Wait-primops]{Delay/Wait PrimOps}
+%* *
+%************************************************************************
+
+\begin{code}
+#ifdef CONCURRENT
+
+/* ToDo: for GRAN */
+
+#define delayZh(liveness, us) \
+ { \
+ if (WaitingThreadsTl == Nil_closure) \
+ WaitingThreadsHd = CurrentTSO; \
+ else \
+ TSO_LINK(WaitingThreadsTl) = CurrentTSO; \
+ WaitingThreadsTl = CurrentTSO; \
+ TSO_LINK(CurrentTSO) = Nil_closure; \
+ TSO_EVENT(CurrentTSO) = (W_) ((us) < 1 ? 1 : (us)); \
+ DO_YIELD(liveness << 1); \
+ }
+
+#else
+
+#define delayZh(liveness, us) \
+ { \
+ fflush(stdout); \
+ fprintf(stderr, "delay#: unthreaded build.\n"); \
+ EXIT(EXIT_FAILURE); \
+ }
+
+#endif
+
+#ifdef CONCURRENT
+
+/* ToDo: something for GRAN */
+
+#define waitZh(liveness, fd) \
+ { \
+ if (WaitingThreadsTl == Nil_closure) \
+ WaitingThreadsHd = CurrentTSO; \
+ else \
+ TSO_LINK(WaitingThreadsTl) = CurrentTSO; \
+ WaitingThreadsTl = CurrentTSO; \
+ TSO_LINK(CurrentTSO) = Nil_closure; \
+ TSO_EVENT(CurrentTSO) = (W_) (-(fd)); \
+ DO_YIELD(liveness << 1); \
+ }
+
+#else
+
+#define waitZh(liveness, fd) \
+ { \
+ fflush(stdout); \
+ fprintf(stderr, "wait#: unthreaded build.\n"); \
+ EXIT(EXIT_FAILURE); \
+ }
+
+#endif
+
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-IO-primops]{Primitive I/O, error-handling primops}
+%* *
+%************************************************************************
+
+\begin{code}
+extern P_ TopClosure;
+EXTFUN(ErrorIO_innards);
+EXTFUN(__std_entry_error__);
+
+#define errorIOZh(a) \
+ do { TopClosure=(a); \
+ (void) SAFESTGCALL1(I_,(void *, FILE *),fflush,stdout); \
+ (void) SAFESTGCALL1(I_,(void *, FILE *),fflush,stderr); \
+ JMP_(ErrorIO_innards); \
+ } while(0)
+
+#if !defined(CALLER_SAVES_SYSTEM)
+/* can use the macros */
+#define stg_getc(stream) getc((FILE *) (stream))
+#define stg_putc(c,stream) putc((c),((FILE *) (stream)))
+#else
+/* must not use the macros (they contain embedded calls to _filbuf/whatnot) */
+#define stg_getc(stream) SAFESTGCALL1(I_,(void *, FILE *),fgetc,(FILE *) (stream))
+#define stg_putc(c,stream) SAFESTGCALL2(I_,(void *, char, FILE *),fputc,(c),((FILE *) (stream)))
+#endif
+
+int initialize_virtual_timer(int us);
+int install_segv_handler(STG_NO_ARGS);
+int install_vtalrm_handler(STG_NO_ARGS);
+void initUserSignals(STG_NO_ARGS);
+void blockUserSignals(STG_NO_ARGS);
+void unblockUserSignals(STG_NO_ARGS);
+IF_RTS(void blockVtAlrmSignal(STG_NO_ARGS);)
+IF_RTS(void unblockVtAlrmSignal(STG_NO_ARGS);)
+
+#ifdef _POSIX_SOURCE
+extern I_ sig_install PROTO((I_, I_, sigset_t *));
+#define stg_sig_ignore(s,m) SAFESTGCALL3(I_,(void *, I_, I_),sig_install,s,STG_SIG_IGN,(sigset_t *)m)
+#define stg_sig_default(s,m) SAFESTGCALL3(I_,(void *, I_, I_),sig_install,s,STG_SIG_DFL,(sigset_t *)m)
+#define stg_sig_catch(s,sp,m) SAFESTGCALL3(I_,(void *, I_, I_),sig_install,s,sp,(sigset_t *)m)
+#else
+extern I_ sig_install PROTO((I_, I_));
+#define stg_sig_ignore(s,m) SAFESTGCALL2(I_,(void *, I_, I_),sig_install,s,STG_SIG_IGN)
+#define stg_sig_default(s,m) SAFESTGCALL2(I_,(void *, I_, I_),sig_install,s,STG_SIG_DFL)
+#define stg_sig_catch(s,sp,m) SAFESTGCALL2(I_,(void *, I_, I_),sig_install,s,sp)
+#endif
+
+#define STG_SIG_DFL (-1)
+#define STG_SIG_IGN (-2)
+#define STG_SIG_ERR (-3)
+
+StgInt getErrorHandler(STG_NO_ARGS);
+#ifndef PAR
+void raiseError PROTO((StgStablePtr handler));
+StgInt catchError PROTO((StgStablePtr newErrorHandler));
+#endif
+void decrementErrorCount(STG_NO_ARGS);
+
+#define stg_catchError(sp) SAFESTGCALL1(I_,(void *, StgStablePtr),catchError,sp)
+#define stg_decrementErrorCount() SAFESTGCALL0(void,(void *),decrementErrorCount)
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-stable-ptr]{Primitive ops for manipulating stable pointers}
+%* *
+%************************************************************************
+
+
+The type of these should be:
+
+\begin{verbatim}
+makeStablePointer# :: a -> State# _RealWorld -> StateAndStablePtr# _RealWorld a
+deRefStablePointer# :: StablePtr# a -> State# _RealWorld -> StateAndPtr _RealWorld a
+\end{verbatim}
+
+Since world-tokens are no longer explicitly passed around, the
+implementations have a few less arguments/results.
+
+The simpler one is @deRefStablePointer#@ (which is only a primop
+because it is more polymorphic than is allowed of a ccall).
+
+\begin{code}
+#ifdef PAR
+
+#define deRefStablePtrZh(ri,sp) \
+do { \
+ fflush(stdout); \
+ fprintf(stderr, "deRefStablePtr#: no stable pointer support.\n");\
+ EXIT(EXIT_FAILURE); \
+} while(0)
+
+#else /* !PAR */
+
+extern StgPtr _deRefStablePointer PROTO((StgInt, StgPtr));
+
+#define deRefStablePtrZh(ri,sp) \
+ ri = SAFESTGCALL2(I_,(void *, I_, P_),_deRefStablePointer,sp,StorageMgrInfo.StablePointerTable);
+
+\end{code}
+
+Declarations for other stable pointer operations.
+
+\begin{code}
+void freeStablePointer PROTO((I_ stablePtr));
+
+void enterStablePtr PROTO((StgStablePtr, StgFunPtr));
+void performIO PROTO((StgStablePtr));
+I_ enterInt PROTO((StgStablePtr));
+I_ enterFloat PROTO((StgStablePtr));
+P_ deRefStablePointer PROTO((StgStablePtr));
+IF_RTS(I_ catchSoftHeapOverflow PROTO((StgStablePtr, I_));)
+IF_RTS(I_ getSoftHeapOverflowHandler(STG_NO_ARGS);)
+IF_RTS(extern StgStablePtr softHeapOverflowHandler;)
+IF_RTS(void shutdownHaskell(STG_NO_ARGS);)
+IF_RTS(extern I_ noBlackHoles;)
+IF_RTS(extern I_ SM_word_stk_size;)
+
+EXTFUN(stopPerformIODirectReturn);
+EXTFUN(startPerformIO);
+EXTFUN(stopEnterIntDirectReturn);
+EXTFUN(startEnterInt);
+EXTFUN(stopEnterFloatDirectReturn);
+EXTFUN(startEnterFloat);
+
+void enterStablePtr PROTO((StgStablePtr stableIndex, StgFunPtr startCode));
+
+#endif /* !PAR */
+
+IF_RTS(extern I_ ErrorIO_call_count;)
+\end{code}
+
+Somewhat harder is @makeStablePointer#@ --- it is usually simple but
+if we're unlucky, it will have to allocate a new table and copy the
+old bit over. Since we might, very occasionally, have to call the
+garbage collector, this has to be a macro... sigh!
+
+NB @newSP@ is required because it is entirely possible that
+@stablePtr@ and @unstablePtr@ are aliases and so we can't do the
+assignment to @stablePtr@ until we've finished with @unstablePtr@.
+
+Another obscure piece of coding is the recalculation of the size of
+the table. We do this just in case Jim's threads decide they want to
+context switch---in which case any stack-allocated variables may get
+trashed. (If only there was a special heap check which didn't
+consider context switching...)
+
+\begin{code}
+#ifndef PAR
+
+/* Calculate SP Table size from number of pointers */
+#define SPTSizeFromNoPtrs( newNP ) (DYN_VHS + 1 + 2 * (newNP))
+
+/* Calculate number of pointers in new table from number in old table:
+ any strictly increasing expression will do here */
+#define CalcNewNoSPtrs( i ) ((i)*2 + 100)
+
+extern void enlargeSPTable PROTO((P_, P_));
+
+#define makeStablePtrZh(stablePtr,liveness,unstablePtr) \
+do { \
+ EXTDATA_RO(StablePointerTable_info); \
+ EXTDATA(UnusedSP); \
+ StgStablePtr newSP; \
+ \
+ if (SPT_EMPTY(StorageMgrInfo.StablePointerTable)) { /* free stack is empty */ \
+ { /* Variables used before the heap check */ \
+ I_ OldNoPtrs = SPT_NoPTRS( StorageMgrInfo.StablePointerTable ); \
+ I_ NewNoPtrs = CalcNewNoSPtrs( OldNoPtrs ); \
+ I_ NewSize = SPTSizeFromNoPtrs( NewNoPtrs ); \
+ HEAP_CHK(liveness, _FHS+NewSize, 0); \
+ } \
+ { /* Variables used after the heap check - same values */ \
+ I_ OldNoPtrs = SPT_NoPTRS( StorageMgrInfo.StablePointerTable ); \
+ I_ NewNoPtrs = CalcNewNoSPtrs( OldNoPtrs ); \
+ I_ NewSize = SPTSizeFromNoPtrs( NewNoPtrs ); \
+ P_ SPTable = Hp + 1 - (_FHS + NewSize); \
+ \
+ CC_ALLOC(CCC, _FHS+NewSize, SPT_K); /* cc prof */ \
+ SET_DYN_HDR(SPTable,StablePointerTable_info,CCC,NewSize,NewNoPtrs);\
+ SAFESTGCALL2(void, (void *, P_, P_), enlargeSPTable, SPTable, StorageMgrInfo.StablePointerTable); \
+ StorageMgrInfo.StablePointerTable = SPTable; \
+ } \
+ } \
+ \
+ newSP = SPT_POP(StorageMgrInfo.StablePointerTable); \
+ SPT_SPTR(StorageMgrInfo.StablePointerTable, newSP) = unstablePtr; \
+ stablePtr = newSP; \
+} while (0)
+
+#else
+
+#define makeStablePtrZh(stablePtr,liveness,unstablePtr) \
+do { \
+ fflush(stdout); \
+ fprintf(stderr, "makeStablePtr#: no stable pointer support.\n");\
+ EXIT(EXIT_FAILURE); \
+} while(0)
+
+#endif /* !PAR */
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-unsafePointerEquality]{Primitive `op' for breaking referential transparency}
+%* *
+%************************************************************************
+
+The type of this is @reallyUnsafePtrEquality :: a -> a -> Int#@ so we
+can expect three parameters: the two arguments and a "register" to put
+the result into.
+
+Message to Will: This primop breaks referential transparency so badly
+you might want to leave it out. On the other hand, if you hide it
+away in an appropriate monad, it's perfectly safe. [ADR]
+
+Note that this primop is non-deterministic: different results can be
+obtained depending on just what the garbage collector (and code
+optimiser??) has done. However, we can guarantee that if two objects
+are pointer-equal, they have the same denotation --- the converse most
+certainly doesn't hold.
+
+ToDo ADR: The degree of non-determinism could be greatly reduced by
+following indirections.
+
+\begin{code}
+#define reallyUnsafePtrEqualityZh(r,a,b) r=((StgPtr)(a) == (StgPtr)(b))
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-parallel-primop]{Primitive `op' for sparking (etc)}
+%* *
+%************************************************************************
+
+Assuming local sparking in some form, we can now inline the spark request.
+
+We build a doubly-linked list in the heap, so that we can handle FIFO
+or LIFO scheduling as we please.
+
+Anything with tag >= 0 is in WHNF, so we discard it.
+
+\begin{code}
+#ifdef CONCURRENT
+
+ED_(Nil_closure);
+ED_(True_closure);
+
+#if defined(GRAN)
+#define parZh(r,hp,node,rest) \
+ PARZh(r,hp,node,rest,0,0)
+
+#define parAtZh(r,hp,node,where,identifier,rest) \
+ parATZh(r,hp,node,where,identifier,rest,1)
+
+#define parAtForNowZh(r,hp,node,where,identifier,rest) \
+ parATZh(r,hp,node,where,identifier,rest,0)
+
+#define parATZh(r,hp,node,where,identifier,rest,local) \
+{ \
+ sparkq result; \
+ if (SHOULD_SPARK(node)) { \
+ result = NewSpark((P_)node,identifier,local); \
+ SAFESTGCALL3(void,(W_),GranSimSparkAt,result,where,identifier); \
+ } else if (do_qp_prof) { \
+ I_ tid = threadId++; \
+ SAFESTGCALL2(void,(I_, P_),QP_Event0,tid,node); \
+ } \
+ r = (rest); \
+}
+
+#define parLocalZh(r,hp,node,identifier,rest) \
+ PARZh(r,hp,node,rest,identifier,1)
+
+#define parGlobalZh(r,hp,node,identifier,rest) \
+ PARZh(r,hp,node,rest,identifier,0)
+
+#define PARZh(r,hp,node,rest,identifier,local) \
+{ \
+ sparkq result; \
+ if (SHOULD_SPARK(node)) { \
+ result = NewSpark((P_)node,identifier,local); \
+ ADD_TO_SPARK_QUEUE(result); \
+ SAFESTGCALL2(void,(W_),GranSimSpark,local,(P_)node); \
+ /* context_switch = 1; not needed any more -- HWL */ \
+ } else if (do_qp_prof) { \
+ I_ tid = threadId++; \
+ SAFESTGCALL2(void,(I_, P_),QP_Event0,tid,node); \
+ } \
+ r = (rest); \
+}
+
+#else /* !GRAN */
+
+extern I_ required_thread_count;
+
+#ifdef PAR
+#define COUNT_SPARK TSO_GLOBALSPARKS(CurrentTSO)++
+#else
+#define COUNT_SPARK
+#endif
+
+/*
+ Note that we must bump the required thread count NOW, rather
+ than when the thread is actually created.
+ */
+
+#define forkZh(r,liveness,node) \
+{ \
+ while (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL]) \
+ DO_YIELD((liveness << 1) | 1); \
+ COUNT_SPARK; \
+ if (SHOULD_SPARK(node)) { \
+ *PendingSparksTl[REQUIRED_POOL]++ = (P_)(node); \
+ } else if (DO_QP_PROF) { \
+ I_ tid = threadId++; \
+ SAFESTGCALL2(void,(I_, P_),QP_Event0,tid,node); \
+ } \
+ required_thread_count++; \
+ context_switch = 1; \
+ r = 1; /* Should not be necessary */ \
+}
+
+#define parZh(r,node) \
+{ \
+ COUNT_SPARK; \
+ if (SHOULD_SPARK(node) && \
+ PendingSparksTl[ADVISORY_POOL] < PendingSparksLim[ADVISORY_POOL]) { \
+ *PendingSparksTl[ADVISORY_POOL]++ = (P_)(node); \
+ } else if (DO_QP_PROF) { \
+ I_ tid = threadId++; \
+ SAFESTGCALL2(void,(I_, P_),QP_Event0,tid,node); \
+ } \
+ r = 1; /* Should not be necessary */ \
+}
+
+\end{code}
+
+The following seq# code should only be used in unoptimized code.
+Be warned: it's a potential bug-farm.
+
+First we push two words on the B stack: the current value of RetReg
+(which may or may not be live), and a continuation snatched largely out
+of thin air (it's a point within this code block). Then we set RetReg
+to the special polymorphic return code for seq, load up Node with the
+closure to be evaluated, and we're off. When the eval returns to the
+polymorphic seq return point, the two words are popped off the B stack,
+RetReg is restored, and we jump to the continuation, completing the
+primop and going on our merry way.
+
+\begin{code}
+
+ED_RO_(vtbl_seq);
+
+#define seqZh(r,liveness,node) \
+ ({ \
+ __label__ cont; \
+ STK_CHK(liveness,0,2,0,0,0,0); \
+ SpB -= BREL(2); \
+ SpB[BREL(0)] = (W_) RetReg; \
+ SpB[BREL(1)] = (W_) &&cont; \
+ RetReg = (StgRetAddr) vtbl_seq; \
+ Node = node; \
+ ENT_VIA_NODE(); \
+ InfoPtr = (D_)(INFO_PTR(Node)); \
+ JMP_(ENTRY_CODE(InfoPtr)); \
+ cont: \
+ r = 1; /* Should be unnecessary */ \
+ })
+
+#endif /* GRAN */
+#endif /* CONCURRENT */
+\end{code}
+
+%************************************************************************
+%* *
+\subsubsection[StgMacros-malloc-ptrs]{Malloc Pointers}
+%* *
+%************************************************************************
+
+This macro is used to construct a MallocPtr on the heap after a ccall.
+Since MallocPtr's are like arrays in many ways, this is heavily based
+on the stuff for arrays above.
+
+What this does is plug the pointer (which will be in a local
+variable), into a fresh heap object and then sets a result (which will
+be a register) to point to the fresh heap object.
+
+Question: what's this "SET_ACTIVITY" stuff - should I be doing this
+too? (It's if you want to use the SPAT profiling tools to
+characterize program behavior by ``activity'' -- tail-calling,
+heap-checking, etc. -- see RednCounts.lh. It is quite specialized.
+WDP 95/1)
+
+\begin{code}
+#ifndef PAR
+
+StgInt eqMallocPtr PROTO((StgMallocPtr p1, StgMallocPtr p2));
+void FreeMallocPtr PROTO((StgMallocPtr mp));
+
+#define constructMallocPtr(liveness, r, mptr) \
+do { \
+ P_ result; \
+ \
+ HEAP_CHK(liveness, _FHS + MallocPtr_SIZE,0); \
+ CC_ALLOC(CCC,_FHS + MallocPtr_SIZE,MallocPtr_K); /* cc prof */ \
+ \
+ result = Hp + 1 - (_FHS + MallocPtr_SIZE); \
+ SET_MallocPtr_HDR(result,MallocPtr_info,CCC,_FHS + MallocPtr_SIZE,0); \
+ MallocPtr_CLOSURE_DATA(result) = mptr; \
+ MallocPtr_CLOSURE_LINK(result) = StorageMgrInfo.MallocPtrList; \
+ StorageMgrInfo.MallocPtrList = result; \
+ \
+/* \
+ printf("DEBUG: MallocPtr(0x%x) = <0x%x, 0x%x, 0x%x, 0x%x>\n", \
+ result, \
+ result[0],result[1], \
+ result[2],result[3]); \
+*/ \
+ CHECK_MallocPtr_CLOSURE( result ); \
+ VALIDATE_MallocPtrList( StorageMgrInfo.MallocPtrList ); \
+ \
+ (r) = (P_) result; \
+} while (0)
+
+#else
+#define constructMallocPtr(liveness, r, mptr) \
+do { \
+ fflush(stdout); \
+ fprintf(stderr, "constructMallocPtr: no malloc pointer support.\n");\
+ EXIT(EXIT_FAILURE); \
+} while(0)
+
+#endif /* !PAR */
+\end{code}
+
+
+End-of-file's multi-slurp protection:
+\begin{code}
+#endif /* ! STGMACROS_H */
+\end{code}