Use message-passing to implement throwTo in the RTS
[ghc-hetmet.git] / rts / HeapStackCheck.cmm
index 75f1418..a528a3f 100644 (file)
 
 #include "Cmm.h"
 
+#ifdef __PIC__
+import pthread_mutex_unlock;
+#endif
+import EnterCriticalSection;
+import LeaveCriticalSection;
+
 /* Stack/Heap Check Failure
  * ------------------------
  *
  * On discovering that a stack or heap check has failed, we do the following:
  *
- *    - If the context_switch flag is set, indicating that there are more
- *      threads waiting to run, we yield to the scheduler 
+ *    - If HpLim==0, indicating that we should context-switch, we yield
+ *      to the scheduler (return ThreadYielding).
+ *
+ * Note that we must leave no slop in the heap (this is a requirement
+ * for LDV profiling, at least), so if we just had a heap-check
+ * failure, then we must retract Hp by HpAlloc.  How do we know
+ * whether there was a heap-check failure?  HpLim might be zero, and
+ * yet we got here as a result of a stack-check failure.  Hence, we
+ * require that HpAlloc is only non-zero if there was a heap-check
+ * failure, otherwise it is zero, so we can always safely subtract
+ * HpAlloc from Hp.
+ *
+ * Hence, HpAlloc is zeroed in LOAD_THREAD_STATE().
+ *
+ *    - If the context_switch flag is set (the backup plan if setting HpLim
+ *      to 0 didn't trigger a context switch), we yield to the scheduler
  *     (return ThreadYielding).
  *
  *    - If Hp > HpLim, we've had a heap check failure.  This means we've
     DEBUG_ONLY(foreign "C" heapCheckFail());                   \
     if (Hp > HpLim) {                                          \
         Hp = Hp - HpAlloc/*in bytes*/;                         \
+        if (HpLim == 0) { \
+                R1 = ThreadYielding;                           \
+                goto sched;                                    \
+        }                                              \
         if (HpAlloc <= BLOCK_SIZE                              \
             && bdescr_link(CurrentNursery) != NULL) {          \
+            HpAlloc = 0;                                        \
             CLOSE_NURSERY();                                   \
             CurrentNursery = bdescr_link(CurrentNursery);      \
             OPEN_NURSERY();                                    \
-            if (CInt[context_switch] != 0 :: CInt) {           \
+            if (Capability_context_switch(MyCapability()) != 0 :: CInt) { \
                 R1 = ThreadYielding;                           \
                 goto sched;                                    \
             } else {                                           \
    There are canned sequences for 'n' pointer values in registers.
    -------------------------------------------------------------------------- */
 
-INFO_TABLE_RET( stg_enter, RET_SMALL, "ptr" W_ unused)
+INFO_TABLE_RET( stg_enter, RET_SMALL, P_ unused)
 {
     R1 = Sp(1);
     Sp_adj(2);
@@ -123,296 +148,6 @@ __stg_gc_enter_1
     GC_GENERIC
 }
 
-#if defined(GRAN)
-/*
-  ToDo: merge the block and yield macros, calling something like BLOCK(N)
-        at the end;
-*/
-
-/* 
-   Should we actually ever do a yield in such a case?? -- HWL
-*/
-gran_yield_0
-{
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-gran_yield_1
-{
-    Sp_adj(-1);
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 2 Regs--------------------------------------------------------------------*/
-
-gran_yield_2
-{
-    Sp_adj(-2);
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 3 Regs -------------------------------------------------------------------*/
-
-gran_yield_3
-{
-    Sp_adj(-3);
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 4 Regs -------------------------------------------------------------------*/
-
-gran_yield_4
-{
-    Sp_adj(-4);
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 5 Regs -------------------------------------------------------------------*/
-
-gran_yield_5
-{
-    Sp_adj(-5);
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 6 Regs -------------------------------------------------------------------*/
-
-gran_yield_6
-{
-    Sp_adj(-6);
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 7 Regs -------------------------------------------------------------------*/
-
-gran_yield_7
-{
-    Sp_adj(-7);
-    Sp(6) = R7;
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-/*- 8 Regs -------------------------------------------------------------------*/
-
-gran_yield_8
-{
-    Sp_adj(-8);
-    Sp(7) = R8;
-    Sp(6) = R7;
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadYielding;
-    jump StgReturn;
-}
-
-// the same routines but with a block rather than a yield
-
-gran_block_1
-{
-    Sp_adj(-1);
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 2 Regs--------------------------------------------------------------------*/
-
-gran_block_2
-{
-    Sp_adj(-2);
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 3 Regs -------------------------------------------------------------------*/
-
-gran_block_3
-{
-    Sp_adj(-3);
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 4 Regs -------------------------------------------------------------------*/
-
-gran_block_4
-{
-    Sp_adj(-4);
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 5 Regs -------------------------------------------------------------------*/
-
-gran_block_5
-{
-    Sp_adj(-5);
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 6 Regs -------------------------------------------------------------------*/
-
-gran_block_6
-{
-    Sp_adj(-6);
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 7 Regs -------------------------------------------------------------------*/
-
-gran_block_7
-{
-    Sp_adj(-7);
-    Sp(6) = R7;
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-/*- 8 Regs -------------------------------------------------------------------*/
-
-gran_block_8
-{
-    Sp_adj(-8);
-    Sp(7) = R8;
-    Sp(6) = R7;
-    Sp(5) = R6;
-    Sp(4) = R5;
-    Sp(3) = R4;
-    Sp(2) = R3;
-    Sp(1) = R2;
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-#endif
-
-#if 0 && defined(PAR)
-
-/*
-  Similar to stg_block_1 (called via StgMacro BLOCK_NP) but separates the
-  saving of the thread state from the actual jump via an StgReturn.
-  We need this separation because we call RTS routines in blocking entry codes
-  before jumping back into the RTS (see parallel/FetchMe.hc).
-*/
-
-par_block_1_no_jump
-{
-    Sp_adj(-1);
-    Sp(0) = R1;
-    SAVE_THREAD_STATE();                                       
-}
-
-par_jump
-{
-    TSO_what_next(CurrentTSO) = ThreadRunGHC;          
-    R1 = ThreadBlocked;
-    jump StgReturn;
-}
-
-#endif
-
 /* -----------------------------------------------------------------------------
    Heap checks in Primitive case alternatives
 
@@ -438,7 +173,7 @@ INFO_TABLE_RET( stg_gc_void, RET_SMALL)
 
 /*-- R1 is boxed/unpointed -------------------------------------------------- */
 
-INFO_TABLE_RET( stg_gc_unpt_r1, RET_SMALL, "ptr" W_ unused)
+INFO_TABLE_RET( stg_gc_unpt_r1, RET_SMALL, P_ unused)
 {
     R1 = Sp(1);
     Sp_adj(2);
@@ -525,7 +260,7 @@ stg_gc_l1
 
 /*-- Unboxed tuple return, one pointer (unregisterised build only) ---------- */
 
-INFO_TABLE_RET( stg_ut_1_0_unreg, RET_SMALL, "ptr" W_ unused )
+INFO_TABLE_RET( stg_ut_1_0_unreg, RET_SMALL, P_ unused )
 {
     Sp_adj(1);
     // one ptr is on the stack (Sp(0))
@@ -568,7 +303,7 @@ __stg_gc_fun
     W_ info;
     W_ type;
 
-    info = %GET_FUN_INFO(R1);
+    info = %GET_FUN_INFO(UNTAG(R1));
 
     // cache the size
     type = TO_W_(StgFunInfoExtra_fun_type(info));
@@ -579,7 +314,7 @@ __stg_gc_fun
 #ifdef TABLES_NEXT_TO_CODE
             // bitmap field holds an offset
             size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info)
-                                        + %GET_ENTRY(R1) /* ### */ );
+                                        + %GET_ENTRY(UNTAG(R1)) /* ### */ );
 #else
            size = StgLargeBitmap_size( StgFunInfoExtra_bitmap(info) );
 #endif
@@ -629,12 +364,12 @@ INFO_TABLE_RET( stg_gc_fun, RET_FUN )
 #ifdef NO_ARG_REGS
     // Minor optimisation: there are no argument registers to load up,
     // so we can just jump straight to the function's entry point.
-    jump %GET_ENTRY(R1);
+    jump %GET_ENTRY(UNTAG(R1));
 #else
     W_ info;
     W_ type;
     
-    info = %GET_FUN_INFO(R1);
+    info = %GET_FUN_INFO(UNTAG(R1));
     type = TO_W_(StgFunInfoExtra_fun_type(info));
     if (type == ARG_GEN || type == ARG_GEN_BIG) {
        jump StgFunInfoExtra_slow_apply(info);
@@ -810,18 +545,20 @@ stg_block_1
  * 
  * -------------------------------------------------------------------------- */
 
-INFO_TABLE_RET( stg_block_takemvar, RET_SMALL, "ptr" W_ unused )
+INFO_TABLE_RET( stg_block_takemvar, RET_SMALL, P_ unused )
 {
     R1 = Sp(1);
     Sp_adj(2);
-    jump takeMVarzh_fast;
+    jump stg_takeMVarzh;
 }
 
 // code fragment executed just before we return to the scheduler
 stg_block_takemvar_finally
 {
 #ifdef THREADED_RTS
-    unlockClosure(R3, stg_EMPTY_MVAR_info);
+    unlockClosure(R3, stg_MVAR_DIRTY_info);
+#else
+    SET_INFO(R3, stg_MVAR_DIRTY_info);
 #endif
     jump StgReturn;
 }
@@ -835,19 +572,21 @@ stg_block_takemvar
     BLOCK_BUT_FIRST(stg_block_takemvar_finally);
 }
 
-INFO_TABLE_RET( stg_block_putmvar, RET_SMALL, "ptr" W_ unused1, "ptr" W_ unused2 )
+INFO_TABLE_RET( stg_block_putmvar, RET_SMALL, P_ unused1, P_ unused2 )
 {
     R2 = Sp(2);
     R1 = Sp(1);
     Sp_adj(3);
-    jump putMVarzh_fast;
+    jump stg_putMVarzh;
 }
 
 // code fragment executed just before we return to the scheduler
 stg_block_putmvar_finally
 {
 #ifdef THREADED_RTS
-    unlockClosure(R3, stg_FULL_MVAR_info);
+    unlockClosure(R3, stg_MVAR_DIRTY_info);
+#else
+    SET_INFO(R3, stg_MVAR_DIRTY_info);
 #endif
     jump StgReturn;
 }
@@ -869,7 +608,7 @@ stg_block_blackhole_finally
     // The last thing we do is release sched_lock, which is
     // preventing other threads from accessing blackhole_queue and
     // picking up this thread before we are finished with it.
-    foreign "C" RELEASE_LOCK(sched_mutex "ptr");
+    RELEASE_LOCK(sched_mutex "ptr");
 #endif
     jump StgReturn;
 }
@@ -882,19 +621,18 @@ stg_block_blackhole
     BLOCK_BUT_FIRST(stg_block_blackhole_finally);
 }
 
-INFO_TABLE_RET( stg_block_throwto, RET_SMALL, "ptr" W_ unused, "ptr" W_ unused )
+INFO_TABLE_RET( stg_block_throwto, RET_SMALL, P_ unused, P_ unused )
 {
     R2 = Sp(2);
     R1 = Sp(1);
     Sp_adj(3);
-    jump killThreadzh_fast;
+    jump stg_killThreadzh;
 }
 
 stg_block_throwto_finally
 {
-#ifdef THREADED_RTS
-    foreign "C" throwToReleaseTarget (R3 "ptr");
-#endif
+    // unlock the throwto message
+    unlockClosure(StgTSO_block_info(CurrentTSO), stg_MSG_THROWTO_info);
     jump StgReturn;
 }
 
@@ -933,7 +671,7 @@ stg_block_async
 /* Used by threadDelay implementation; it would be desirable to get rid of
  * this free()'ing void return continuation.
  */
-INFO_TABLE_RET( stg_block_async_void, 0/*framesize*/, 0/*bitmap*/, RET_SMALL )
+INFO_TABLE_RET( stg_block_async_void, RET_SMALL )
 {
     W_ ares;