Use message-passing to implement throwTo in the RTS
[ghc-hetmet.git] / rts / HeapStackCheck.cmm
index 3c66e78..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))
@@ -551,8 +286,6 @@ INFO_TABLE_RET( stg_ut_1_0_unreg, RET_SMALL, "ptr" W_ unused )
         +---------------------+
          |      f_closure      |
         +---------------------+
-         |         tag         |
-        +- - - - - - - - - - -+
          |        size         |
         +---------------------+
          |   stg_gc_fun_info   |
@@ -569,10 +302,7 @@ __stg_gc_fun
     W_ size;
     W_ info;
     W_ type;
-    W_ tag;
-    W_ ret_fun;
 
-    tag  = GETTAG(R1);
     info = %GET_FUN_INFO(UNTAG(R1));
 
     // cache the size
@@ -596,11 +326,9 @@ __stg_gc_fun
 #ifdef NO_ARG_REGS
     // we don't have to save any registers away
     Sp_adj(-3);
+    Sp(2) = R1;
+    Sp(1) = size;
     Sp(0) = stg_gc_fun_info;
-    ret_fun = Sp;
-    StgRetFun_size(ret_fun) = HALF_W_(size);
-    StgRetFun_tag(ret_fun)  = HALF_W_(tag);
-    StgRetFun_fun(ret_fun)  = R1;
     GC_GENERIC
 #else
     W_ type;
@@ -609,11 +337,9 @@ __stg_gc_fun
     if (type == ARG_GEN || type == ARG_GEN_BIG) {
         // regs already saved by the heap check code
         Sp_adj(-3);
+        Sp(2) = R1;
+        Sp(1) = size;
         Sp(0) = stg_gc_fun_info;
-        ret_fun = Sp;
-        StgRetFun_size(ret_fun) = HALF_W_(size);
-        StgRetFun_tag(ret_fun)  = HALF_W_(tag);
-        StgRetFun_fun(ret_fun)  = R1;
         // DEBUG_ONLY(foreign "C" debugBelch("stg_fun_gc_gen(ARG_GEN)"););
         GC_GENERIC
     } else { 
@@ -633,12 +359,7 @@ __stg_gc_fun
 
 INFO_TABLE_RET( stg_gc_fun, RET_FUN )
 {
-    // Grab the fun, but remember to add in the tag.  The GC doesn't
-    // guarantee to retain the tag on the pointer, so we have to do
-    // it manually, because the function entry code assumes it.
-    W_ ret_fun;
-    ret_fun = Sp;
-    R1 = StgRetFun_fun(ret_fun) | TO_W_(StgRetFun_tag(ret_fun));
+    R1 = Sp(2);
     Sp_adj(3);
 #ifdef NO_ARG_REGS
     // Minor optimisation: there are no argument registers to load up,
@@ -824,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;
 }
@@ -849,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;
 }
@@ -883,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;
 }
@@ -896,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;
 }