/* -----------------------------------------------------------------------------
- * $Id: PrimOps.hc,v 1.58 2000/11/13 14:40:37 simonmar Exp $
+ * $Id: PrimOps.hc,v 1.84 2001/11/08 12:46:31 simonmar Exp $
*
* (c) The GHC Team, 1998-2000
*
*
* ---------------------------------------------------------------------------*/
+#include "Stg.h"
#include "Rts.h"
#include "RtsFlags.h"
#include "Storage.h"
#include "BlockAlloc.h" /* tmp */
#include "StablePriv.h"
-#include "HeapStackCheck.h"
#include "StgRun.h"
#include "Itimer.h"
#include "Prelude.h"
classes CCallable and CReturnable don't really exist, but the
compiler insists on generating dictionaries containing references
to GHC_ZcCCallable_static_info etc., so we provide dummy symbols
- for these.
+ for these. Some C compilers can't cope with zero-length static arrays,
+ so we have to make these one element long.
*/
-W_ GHC_ZCCCallable_static_info[0];
-W_ GHC_ZCCReturnable_static_info[0];
-
-
+StgWord GHC_ZCCCallable_static_info[1];
+StgWord GHC_ZCCReturnable_static_info[1];
+
/* -----------------------------------------------------------------------------
Macros for Hand-written primitives.
-------------------------------------------------------------------------- */
#define BYTES_TO_STGWORDS(n) ((n) + sizeof(W_) - 1)/sizeof(W_)
-#define newByteArray(ty,scale) \
- FN_(new##ty##Arrayzh_fast) \
+FN_(newByteArrayzh_fast) \
{ \
- W_ stuff_size, size, n; \
+ W_ size, stuff_size, n; \
StgArrWords* p; \
FB_ \
- MAYBE_GC(NO_PTRS,new##ty##Arrayzh_fast); \
+ MAYBE_GC(NO_PTRS,newByteArrayzh_fast); \
n = R1.w; \
- stuff_size = BYTES_TO_STGWORDS(n*scale); \
+ stuff_size = BYTES_TO_STGWORDS(n); \
size = sizeofW(StgArrWords)+ stuff_size; \
p = (StgArrWords *)RET_STGCALL1(P_,allocate,size); \
TICK_ALLOC_PRIM(sizeofW(StgArrWords),stuff_size,0); \
FE_ \
}
-newByteArray(Char, 1)
-/* Char arrays really contain only 8-bit bytes for compatibility. */
-newByteArray(Int, sizeof(I_))
-newByteArray(Word, sizeof(W_))
-newByteArray(Addr, sizeof(P_))
-newByteArray(Float, sizeof(StgFloat))
-newByteArray(Double, sizeof(StgDouble))
-newByteArray(StablePtr, sizeof(StgStablePtr))
+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)
{
-------------------------------------------------------------------------- */
-#ifndef PAR
FN_(mkForeignObjzh_fast)
{
/* R1.p = ptr to foreign object,
RET_P(result);
FE_
}
-#endif
/* These two are out-of-line for the benefit of the NCG */
FN_(unsafeThawArrayzh_fast)
Weak Pointer Primitives
-------------------------------------------------------------------------- */
-#ifndef PAR
-
FN_(mkWeakzh_fast)
{
/* R1.p = key
FE_
}
-#endif /* !PAR */
-
/* -----------------------------------------------------------------------------
Arbitrary-precision Integer operations.
-------------------------------------------------------------------------- */
if ( val < 0LL ) {
neg = 1;
val = -val;
- }
+ }
hi = (W_)((LW_)val / 0x100000000ULL);
if ( words_needed == 2 ) {
- s = 2;
+ s = 2;
Hp[-1] = (W_)val;
Hp[0] = hi;
} else if ( val != 0 ) {
}
-#endif /* HAVE_LONG_LONG */
+#endif /* SUPPORT_LONG_LONGS */
/* ToDo: this is shockingly inefficient */
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_(newMVarzh_fast)
{
StgMVar *mvar;
FE_
}
+#define PerformTake(tso, value) ({ \
+ (tso)->sp[1] = (W_)value; \
+ (tso)->sp[0] = (W_)&stg_gc_unpt_r1_ret_info; \
+ })
+
+#define PerformPut(tso) ({ \
+ StgClosure *val = (StgClosure *)(tso)->sp[2]; \
+ (tso)->sp[2] = (W_)&stg_gc_noregs_ret_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;
- /* 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);
+ 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);
- TICK_RET_UNBOXED_TUP(1);
- RET_P(val);
+ /* 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);
+#else
+ 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;
+ }
+#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);
+ }
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;
- /* 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);
+ 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);
- TICK_RET_UNBOXED_TUP(1);
- RET_NP(1,val);
+ /* 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);
+#else
+ 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;
+ }
+#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);
+ }
FE_
}
#endif
if (info == &stg_FULL_MVAR_info) {
-#ifdef INTERPRETER
- fprintf(stderr, "fatal: put on a full MVar in Hugs; aborting\n" );
- exit(1);
-#else
- R1.cl = (StgClosure *)PutFullMVar_closure;
- JMP_(raisezh_fast);
+ if (mvar->head == (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ mvar->head = CurrentTSO;
+ } else {
+ mvar->tail->link = CurrentTSO;
+ }
+ CurrentTSO->link = (StgTSO *)&stg_END_TSO_QUEUE_closure;
+ CurrentTSO->why_blocked = BlockedOnMVar;
+ CurrentTSO->block_info.closure = (StgClosure *)mvar;
+ mvar->tail = CurrentTSO;
+
+#ifdef SMP
+ /* unlock the MVar */
+ SET_INFO(mvar,&stg_FULL_MVAR_info);
#endif
+ 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);
-#if defined(GRAN)
- mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
-#elif defined(PAR)
- // ToDo: check 2nd arg (mvar) is right
- mvar->head = RET_STGCALL2(StgTSO *,unblockOne,mvar->head,mvar);
+ /* 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);
#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 afterward for better communication performance? */
+ FE_
+}
+
+FN_(tryPutMVarzh_fast)
+{
+ StgMVar *mvar;
+ const StgInfoTable *info;
+
+ FB_
+ /* args: R1 = MVar, R2 = value */
- /* yield, to give the newly woken thread a chance to take the MVar */
- JMP_(stg_yield_noregs);
+ mvar = (StgMVar *)R1.p;
+
+#ifdef SMP
+ info = LOCK_CLOSURE(mvar);
+#else
+ info = GET_INFO(mvar);
+#endif
+
+ if (info == &stg_FULL_MVAR_info) {
+
+#ifdef SMP
+ /* unlock the MVar */
+ mvar->header.info = &stg_FULL_MVAR_info;
+#endif
+
+ RET_N(0);
}
+
+ if (mvar->head != (StgTSO *)&stg_END_TSO_QUEUE_closure) {
+ /* There are takeMVar(s) waiting: wake up the first one
+ */
+ ASSERT(mvar->head->why_blocked == BlockedOnMVar);
- /* unlocks the MVar in the SMP case */
- SET_INFO(mvar,&stg_FULL_MVAR_info);
+ /* 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);
+#else
+ 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;
+ }
+#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]));
+ }
- JMP_(ENTRY_CODE(Sp[0]));
+ /* 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 {
}
/* -----------------------------------------------------------------------------
+ Bytecode object primitives
+ ------------------------------------------------------------------------- */
+
+FN_(newBCOzh_fast)
+{
+ /* R1.p = instrs
+ R2.p = literals
+ R3.p = ptrs
+ R4.p = itbls
+ */
+ StgBCO *bco;
+ FB_
+
+ HP_CHK_GEN_TICKY(sizeofW(StgBCO),R1_PTR|R2_PTR|R3_PTR|R4_PTR, newBCOzh_fast,);
+ TICK_ALLOC_PRIM(sizeofW(StgHeader), sizeofW(StgBCO)-sizeofW(StgHeader), 0);
+ CCS_ALLOC(CCCS,sizeofW(StgBCO)); /* ccs prof */
+ bco = (StgBCO *) (Hp + 1 - sizeofW(StgBCO));
+ SET_HDR(bco, &stg_BCO_info, CCCS);
+
+ bco->instrs = (StgArrWords*)R1.cl;
+ bco->literals = (StgArrWords*)R2.cl;
+ bco->ptrs = (StgMutArrPtrs*)R3.cl;
+ bco->itbls = (StgArrWords*)R4.cl;
+
+ TICK_RET_UNBOXED_TUP(1);
+ RET_P(bco);
+ FE_
+}
+
+FN_(mkApUpd0zh_fast)
+{
+ /* R1.p = the fn for the AP_UPD
+ */
+ StgAP_UPD* ap;
+ FB_
+ HP_CHK_GEN_TICKY(AP_sizeW(0), R1_PTR, mkApUpd0zh_fast,);
+ TICK_ALLOC_PRIM(sizeofW(StgHeader), AP_sizeW(0)-sizeofW(StgHeader), 0);
+ CCS_ALLOC(CCCS,AP_sizeW(0)); /* ccs prof */
+ ap = (StgAP_UPD *) (Hp + 1 - AP_sizeW(0));
+ SET_HDR(ap, &stg_AP_UPD_info, CCCS);
+
+ ap->n_args = 0;
+ ap->fun = R1.cl;
+
+ TICK_RET_UNBOXED_TUP(1);
+ RET_P(ap);
+ FE_
+}
+
+/* -----------------------------------------------------------------------------
Thread I/O blocking primitives
-------------------------------------------------------------------------- */
ACQUIRE_LOCK(&sched_mutex);
- target = (R1.i / (TICK_MILLISECS*1000)) + timestamp + ticks_since_timestamp;
+ target = (R1.i / (TICK_MILLISECS*1000)) + getourtimeofday();
CurrentTSO->block_info.target = target;
/* Insert the new thread in the sleeping queue. */