/* -----------------------------------------------------------------------------
- * $Id: PrimOps.hc,v 1.76 2001/03/25 13:52:46 qrczak Exp $
+ * $Id: PrimOps.hc,v 1.92 2002/02/28 08:53:58 sof Exp $
*
* (c) The GHC Team, 1998-2000
*
#include "Storage.h"
#include "BlockAlloc.h" /* tmp */
#include "StablePriv.h"
-#include "HeapStackCheck.h"
#include "StgRun.h"
#include "Itimer.h"
#include "Prelude.h"
FE_ \
}
+FN_(newPinnedByteArrayzh_fast) \
+ { \
+ W_ size, stuff_size, n; \
+ StgArrWords* p; \
+ FB_ \
+ MAYBE_GC(NO_PTRS,newPinnedByteArrayzh_fast); \
+ n = R1.w; \
+ stuff_size = BYTES_TO_STGWORDS(n); \
+ size = sizeofW(StgArrWords)+ stuff_size; \
+ p = (StgArrWords *)RET_STGCALL1(P_,allocatePinned,size); \
+ TICK_ALLOC_PRIM(sizeofW(StgArrWords),stuff_size,0); \
+ SET_HDR(p, &stg_ARR_WORDS_info, CCCS); \
+ p->words = stuff_size; \
+ TICK_RET_UNBOXED_TUP(1) \
+ RET_P(p); \
+ FE_ \
+ }
+
FN_(newArrayzh_fast)
{
W_ size, n, init;
/* -----------------------------------------------------------------------------
Foreign Object Primitives
-
-------------------------------------------------------------------------- */
FN_(mkForeignObjzh_fast)
}
/* kill it */
+#ifdef PROFILING
+ // @LDV profiling
+ // A weak pointer is inherently used, so we do not need to call
+ // LDV_recordDead_FILL_SLOP_DYNAMIC():
+ // LDV_recordDead_FILL_SLOP_DYNAMIC((StgClosure *)w);
+ // or, LDV_recordDead():
+ // LDV_recordDead((StgClosure *)w, sizeofW(StgWeak) - sizeofW(StgProfHeader));
+ // Furthermore, when PROFILING is turned on, dead weak pointers are exactly as
+ // large as weak pointers, so there is no need to fill the slop, either.
+ // See stg_DEAD_WEAK_info in StgMiscClosures.hc.
+#endif
+ //
+ // Todo: maybe use SET_HDR() and remove LDV_recordCreate()?
+ //
w->header.info = &stg_DEAD_WEAK_info;
+#ifdef PROFILING
+ // @LDV profiling
+ LDV_recordCreate((StgClosure *)w);
+#endif
f = ((StgWeak *)w)->finalizer;
w->link = ((StgWeak *)w)->link;
FE_
}
+FN_(deRefWeakzh_fast)
+{
+ /* R1.p = weak ptr */
+ StgWeak* w;
+ I_ code;
+ P_ val;
+ FB_
+ w = (StgWeak*)R1.p;
+ if (w->header.info == &stg_WEAK_info) {
+ code = 1;
+ val = (P_)((StgWeak *)w)->value;
+ } else {
+ code = 0;
+ val = (P_)w;
+ }
+ RET_NP(code,val);
+ FE_
+}
+
/* -----------------------------------------------------------------------------
Arbitrary-precision Integer operations.
-------------------------------------------------------------------------- */
}
-#endif /* HAVE_LONG_LONG */
+#endif /* SUPPORT_LONG_LONGS */
/* ToDo: this is shockingly inefficient */
GMP_TAKE2_RET2(quotRemIntegerzh_fast, mpz_tdiv_qr);
GMP_TAKE2_RET2(divModIntegerzh_fast, mpz_fdiv_qr);
+
+FN_(gcdIntzh_fast)
+{
+ /* R1 = the first Int#; R2 = the second Int# */
+ mp_limb_t aa;
+ I_ r;
+ FB_
+ aa = (mp_limb_t)(R1.i);
+ r = RET_STGCALL3(StgInt, mpn_gcd_1, (mp_limb_t *)(&aa), 1, (mp_limb_t)(R2.i));
+
+ R1.i = r;
+ /* Result parked in R1, return via info-pointer at TOS */
+ JMP_(ENTRY_CODE(Sp[0]));
+ FE_
+}
+
+FN_(gcdIntegerIntzh_fast)
+{
+ /* R1 = s1; R2 = d1; R3 = the int */
+ I_ r;
+ FB_
+ r = RET_STGCALL3(StgInt,mpn_gcd_1,(mp_limb_t *)(BYTE_ARR_CTS(R2.p)), R1.i, R3.i);
+ RET_N(r);
+ FE_
+}
+
+FN_(cmpIntegerIntzh_fast)
+{
+ /* R1 = s1; R2 = d1; R3 = the int */
+ I_ usize;
+ I_ vsize;
+ I_ v_digit;
+ mp_limb_t u_digit;
+ FB_
+
+ usize = R1.i;
+ vsize = 0;
+ v_digit = R3.i;
+
+ // paraphrased from mpz_cmp_si() in the GMP sources
+ if (v_digit > 0) {
+ vsize = 1;
+ } else if (v_digit < 0) {
+ vsize = -1;
+ v_digit = -v_digit;
+ }
+
+ if (usize != vsize) {
+ R1.i = usize - vsize; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ if (usize == 0) {
+ R1.i = 0; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ u_digit = *(mp_limb_t *)(BYTE_ARR_CTS(R2.p));
+
+ if (u_digit == (mp_limb_t) (unsigned long) v_digit) {
+ R1.i = 0; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ if (u_digit > (mp_limb_t) (unsigned long) v_digit) {
+ R1.i = usize;
+ } else {
+ R1.i = -usize;
+ }
+
+ JMP_(ENTRY_CODE(Sp[0]));
+ FE_
+}
+
+FN_(cmpIntegerzh_fast)
+{
+ /* R1 = s1; R2 = d1; R3 = s2; R4 = d2 */
+ I_ usize;
+ I_ vsize;
+ I_ size;
+ StgPtr up, vp;
+ int cmp;
+ FB_
+
+ // paraphrased from mpz_cmp() in the GMP sources
+ usize = R1.i;
+ vsize = R3.i;
+
+ if (usize != vsize) {
+ R1.i = usize - vsize; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ if (usize == 0) {
+ R1.i = 0; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ size = abs(usize);
+
+ up = BYTE_ARR_CTS(R2.p);
+ vp = BYTE_ARR_CTS(R4.p);
+
+ cmp = RET_STGCALL3(I_, mpn_cmp, (mp_limb_t *)up, (mp_limb_t *)vp, size);
+
+ if (cmp == 0) {
+ R1.i = 0; JMP_(ENTRY_CODE(Sp[0]));
+ }
+
+ if ((cmp < 0) == (usize < 0)) {
+ R1.i = 1;
+ } else {
+ R1.i = (-1);
+ }
+ /* Result parked in R1, return via info-pointer at TOS */
+ JMP_(ENTRY_CODE(Sp[0]));
+ FE_
+}
+
+FN_(integer2Intzh_fast)
+{
+ /* R1 = s; R2 = d */
+ I_ r, s;
+ FB_
+ s = R1.i;
+ if (s == 0)
+ r = 0;
+ else {
+ r = ((mp_limb_t *) (BYTE_ARR_CTS(R2.p)))[0];
+ if (s < 0) r = -r;
+ }
+ /* Result parked in R1, return via info-pointer at TOS */
+ R1.i = r;
+ JMP_(ENTRY_CODE(Sp[0]));
+ FE_
+}
+
+FN_(integer2Wordzh_fast)
+{
+ /* R1 = s; R2 = d */
+ I_ s;
+ W_ r;
+ FB_
+ s = R1.i;
+ if (s == 0)
+ r = 0;
+ else {
+ r = ((mp_limb_t *) (BYTE_ARR_CTS(R2.p)))[0];
+ if (s < 0) r = -r;
+ }
+ /* Result parked in R1, return via info-pointer at TOS */
+ R1.w = r;
+ JMP_(ENTRY_CODE(Sp[0]));
+ FE_
+}
+
+
FN_(decodeFloatzh_fast)
{
MP_INT mantissa;
/* create it right now, return ThreadID in R1 */
R1.t = RET_STGCALL2(StgTSO *, createIOThread,
- RtsFlags.GcFlags.initialStkSize, R1.cl);
+ RtsFlags.GcFlags.initialStkSize, R1.cl);
STGCALL1(scheduleThread, R1.t);
/* switch at the earliest opportunity */
context_switch = 1;
- JMP_(ENTRY_CODE(Sp[0]));
+ RET_N(R1.t);
FE_
}
FE_
}
+/* -----------------------------------------------------------------------------
+ * MVar primitives
+ *
+ * take & putMVar work as follows. Firstly, an important invariant:
+ *
+ * If the MVar is full, then the blocking queue contains only
+ * threads blocked on putMVar, and if the MVar is empty then the
+ * blocking queue contains only threads blocked on takeMVar.
+ *
+ * takeMvar:
+ * MVar empty : then add ourselves to the blocking queue
+ * MVar full : remove the value from the MVar, and
+ * blocking queue empty : return
+ * blocking queue non-empty : perform the first blocked putMVar
+ * from the queue, and wake up the
+ * thread (MVar is now full again)
+ *
+ * putMVar is just the dual of the above algorithm.
+ *
+ * How do we "perform a putMVar"? Well, we have to fiddle around with
+ * the stack of the thread waiting to do the putMVar. See
+ * stg_block_putmvar and stg_block_takemvar in HeapStackCheck.c for
+ * the stack layout, and the PerformPut and PerformTake macros below.
+ *
+ * It is important that a blocked take or put is woken up with the
+ * take/put already performed, because otherwise there would be a
+ * small window of vulnerability where the thread could receive an
+ * exception and never perform its take or put, and we'd end up with a
+ * deadlock.
+ *
+ * -------------------------------------------------------------------------- */
+
+FN_(isEmptyMVarzh_fast)
+{
+ /* args: R1 = MVar closure */
+ I_ r;
+ FB_
+ r = (I_)((GET_INFO((StgMVar*)(R1.p))) == &stg_EMPTY_MVAR_info);
+ RET_N(r);
+ FE_
+}
+
+
FN_(newMVarzh_fast)
{
StgMVar *mvar;
FE_
}
+#define PerformTake(tso, value) ({ \
+ (tso)->sp[1] = (W_)value; \
+ (tso)->sp[0] = (W_)&stg_gc_unpt_r1_info; \
+ })
+
+#define PerformPut(tso) ({ \
+ StgClosure *val = (StgClosure *)(tso)->sp[2]; \
+ (tso)->sp[2] = (W_)&stg_gc_noregs_info; \
+ (tso)->sp += 2; \
+ val; \
+ })
+
FN_(takeMVarzh_fast)
{
StgMVar *mvar;
/* unlock the MVar */
mvar->header.info = &stg_EMPTY_MVAR_info;
#endif
- BLOCK(R1_PTR, takeMVarzh_fast);
+ JMP_(stg_block_takemvar);
}
+ /* we got the value... */
val = mvar->value;
- mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
- /* wake up the first thread on the queue
- */
if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ /* There are putMVar(s) waiting...
+ * wake up the first thread on the queue
+ */
ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+ /* actually perform the putMVar for the thread that we just woke up */
+ mvar->value = PerformPut(mvar->head);
+
#if defined(GRAN) || defined(PAR)
/* ToDo: check 2nd arg (mvar) is right */
mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
}
+#ifdef SMP
+ /* unlock in the SMP case */
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
+#endif
+ TICK_RET_UNBOXED_TUP(1);
+ RET_P(val);
+ } else {
+ /* No further putMVars, MVar is now empty */
+
+ /* do this last... we might have locked the MVar in the SMP case,
+ * and writing the info pointer will unlock it.
+ */
+ SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+ mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
+ TICK_RET_UNBOXED_TUP(1);
+ RET_P(val);
}
-
- /* do this last... we might have locked the MVar in the SMP case,
- * and writing the info pointer will unlock it.
- */
- SET_INFO(mvar,&stg_EMPTY_MVAR_info);
-
- TICK_RET_UNBOXED_TUP(1);
- RET_P(val);
FE_
}
if (info == &stg_EMPTY_MVAR_info) {
#ifdef SMP
- /* unlock the MVar */
- mvar->header.info = &stg_EMPTY_MVAR_info;
+ /* unlock the MVar */
+ SET_INFO(mvar,&stg_EMPTY_MVAR_info);
#endif
- /* HACK: we need a pointer to pass back, so we abuse NO_FINALIZER_closure */
- RET_NP(0, &stg_NO_FINALIZER_closure);
+ /* HACK: we need a pointer to pass back,
+ * so we abuse NO_FINALIZER_closure
+ */
+ RET_NP(0, &stg_NO_FINALIZER_closure);
}
+ /* we got the value... */
val = mvar->value;
- mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
- /* wake up the first thread on the queue
- */
if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ /* There are putMVar(s) waiting...
+ * wake up the first thread on the queue
+ */
ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+ /* actually perform the putMVar for the thread that we just woke up */
+ mvar->value = PerformPut(mvar->head);
+
#if defined(GRAN) || defined(PAR)
/* ToDo: check 2nd arg (mvar) is right */
mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
}
- }
+#ifdef SMP
+ /* unlock in the SMP case */
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
+#endif
+ } else {
+ /* No further putMVars, MVar is now empty */
+ mvar->value = (StgClosure *)&stg_END_TSO_QUEUE_closure;
- /* do this last... we might have locked the MVar in the SMP case,
- * and writing the info pointer will unlock it.
- */
- SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+ /* do this last... we might have locked the MVar in the SMP case,
+ * and writing the info pointer will unlock it.
+ */
+ SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+ }
TICK_RET_UNBOXED_TUP(1);
- RET_NP(1,val);
+ RET_NP((I_)1, val);
FE_
}
#ifdef SMP
/* unlock the MVar */
- mvar->header.info = &stg_FULL_MVAR_info;
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
#endif
- BLOCK( R1_PTR | R2_PTR, putMVarzh_fast );
+ JMP_(stg_block_putmvar);
}
- mvar->value = R2.cl;
-
- /* wake up the first thread on the queue, it will continue with the
- * takeMVar operation and mark the MVar empty again.
- */
if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
- ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+ /* There are takeMVar(s) waiting: wake up the first one
+ */
+ ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+ /* actually perform the takeMVar */
+ PerformTake(mvar->head, R2.cl);
+
#if defined(GRAN) || defined(PAR)
- /* ToDo: check 2nd arg (mvar) is right */
- mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+ /* ToDo: check 2nd arg (mvar) is right */
+ mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
#else
- mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+ mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
#endif
- if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
- mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
- }
+ if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+ }
+#ifdef SMP
+ /* unlocks the MVar in the SMP case */
+ SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+#endif
+ JMP_(ENTRY_CODE(Sp[0]));
+ } else {
+ /* No further takes, the MVar is now full. */
+ mvar->value = R2.cl;
+ /* unlocks the MVar in the SMP case */
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
+ JMP_(ENTRY_CODE(Sp[0]));
}
- /* unlocks the MVar in the SMP case */
- SET_INFO(mvar,&stg_FULL_MVAR_info);
-
- /* ToDo: yield here for better communication performance? */
- JMP_(ENTRY_CODE(Sp[0]));
+ /* ToDo: yield afterward for better communication performance? */
FE_
}
RET_N(0);
}
- mvar->value = R2.cl;
-
- /* wake up the first thread on the queue, it will continue with the
- * takeMVar operation and mark the MVar empty again.
- */
if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
- ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+ /* There are takeMVar(s) waiting: wake up the first one
+ */
+ ASSERT(mvar->head->why_blocked == BlockedOnMVar);
+
+ /* actually perform the takeMVar */
+ PerformTake(mvar->head, R2.cl);
+
#if defined(GRAN) || defined(PAR)
- /* ToDo: check 2nd arg (mvar) is right */
- mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+ /* ToDo: check 2nd arg (mvar) is right */
+ mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
#else
- mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
+ mvar->head = RET_STGCALL1(StgTSO *,unblockOne,mvar->head);
#endif
- if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
- mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
- }
+ if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ mvar->tail = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+ }
+#ifdef SMP
+ /* unlocks the MVar in the SMP case */
+ SET_INFO(mvar,&stg_EMPTY_MVAR_info);
+#endif
+ JMP_(ENTRY_CODE(Sp[0]));
+ } else {
+ /* No further takes, the MVar is now full. */
+ mvar->value = R2.cl;
+ /* unlocks the MVar in the SMP case */
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
+ JMP_(ENTRY_CODE(Sp[0]));
}
- /* unlocks the MVar in the SMP case */
- SET_INFO(mvar,&stg_FULL_MVAR_info);
-
- /* ToDo: yield here for better communication performance? */
- RET_N(1);
+ /* ToDo: yield afterward for better communication performance? */
FE_
}
/* Is there already a StableName for this heap object? */
if (stable_ptr_table[index].sn_obj == NULL) {
sn_obj = (StgStableName *) (Hp - sizeofW(StgStableName) + 1);
- sn_obj->header.info = &stg_STABLE_NAME_info;
+ SET_HDR(sn_obj,&stg_STABLE_NAME_info,CCCS);
sn_obj->sn = index;
stable_ptr_table[index].sn_obj = (StgClosure *)sn_obj;
} else {
RET_P(sn_obj);
}
+
+FN_(makeStablePtrzh_fast)
+{
+ /* Args: R1 = a */
+ StgStablePtr sp;
+ FB_
+ MAYBE_GC(R1_PTR, makeStablePtrzh_fast);
+ sp = RET_STGCALL1(StgStablePtr,getStablePtr,R1.p);
+ RET_N(sp);
+ FE_
+}
+
+FN_(deRefStablePtrzh_fast)
+{
+ /* Args: R1 = the stable ptr */
+ P_ r;
+ StgStablePtr sp;
+ FB_
+ sp = (StgStablePtr)R1.w;
+ r = stable_ptr_table[(StgWord)sp].addr;
+ RET_P(r);
+ FE_
+}
+
/* -----------------------------------------------------------------------------
Bytecode object primitives
------------------------------------------------------------------------- */