diff --git a/asterius/rts/rts.constants.mjs b/asterius/rts/rts.constants.mjs index 31a6a9641d..b4a4e5001e 100644 --- a/asterius/rts/rts.constants.mjs +++ b/asterius/rts/rts.constants.mjs @@ -13,6 +13,7 @@ export const sizeof_first_mblock = 0xfc000; export const offset_bdescr_start = 0x0; export const offset_bdescr_free = 0x8; export const offset_bdescr_link = 0x10; +export const offset_bdescr_gen_no = 0x28; export const offset_bdescr_node = 0x2c; export const offset_bdescr_flags = 0x2e; export const offset_bdescr_blocks = 0x30; diff --git a/asterius/src/Asterius/Builtins.hs b/asterius/src/Asterius/Builtins.hs index 3f1ebbb508..f0e90ca5e2 100644 --- a/asterius/src/Asterius/Builtins.hs +++ b/asterius/src/Asterius/Builtins.hs @@ -170,11 +170,9 @@ rtsAsteriusModule opts = <> fromJSArrayFunction opts <> threadPausedFunction opts <> dirtyMutVarFunction opts - <> dirtyMVarFunction opts - <> updateMVarFunction opts <> dirtyStackFunction opts - <> recordMutableCapFunction opts - <> recordClosureMutatedFunction opts + <> dirtyTSOFunction opts + <> recordMutatedFunction opts <> tryWakeupThreadFunction opts <> raiseExceptionHelperFunction opts <> barfFunction opts @@ -937,11 +935,9 @@ hsInitFunction, fromJSArrayFunction, threadPausedFunction, dirtyMutVarFunction, - dirtyMVarFunction, - updateMVarFunction, dirtyStackFunction, - recordMutableCapFunction, - recordClosureMutatedFunction, + dirtyTSOFunction, + recordMutatedFunction, raiseExceptionHelperFunction, barfFunction, getProgArgvFunction, @@ -1018,26 +1014,6 @@ rtsCheckSchedStatusFunction _ = runEDSL "rts_checkSchedStatus" $ do $ emit $ emitErrorMessage [] "IllegalSchedulerStatusCode" -dirtyTSO :: Expression -> Expression -> EDSL () -dirtyTSO _ tso = - if' - [] - (eqZInt32 $ loadI32 tso offset_StgTSO_dirty) - (do - storeI32 tso offset_StgTSO_dirty $ constI32 1 - recordMutated tso) - mempty - -dirtySTACK :: Expression -> Expression -> EDSL () -dirtySTACK _ stack = - if' - [] - (eqZInt32 $ loadI32 stack offset_StgStack_dirty) - (do - storeI32 stack offset_StgStack_dirty $ constI32 1 - recordMutated stack) - mempty - -- `_scheduleTSO(tso,func)` executes the given tso starting at the given -- function scheduleTSOFunction BuiltinsOptions {} = runEDSL "scheduleTSO" $ do @@ -1052,8 +1028,8 @@ scheduleTSOFunction BuiltinsOptions {} = runEDSL "scheduleTSO" $ do tso storeI32 mainCapability offset_Capability_interrupt $ constI32 0 storeI32 mainCapability offset_Capability_idle $ constI32 0 - dirtyTSO mainCapability tso - dirtySTACK mainCapability (loadI64 tso offset_StgTSO_stackobj) + dirtyTSO tso + dirtySTACK (loadI64 tso offset_StgTSO_stackobj) -- execute the TSO (using stgRun trampolining machinery) stgRun func -- indicate in the Capability that we are not running anything @@ -1448,41 +1424,63 @@ threadPausedFunction _ = runEDSL "threadPaused" $ do _ <- params [I64, I64] pure () -recordMutated :: Expression -> EDSL () +-- | Helper functions for the write barrier. +-- | See also Cmm.h, Capability.h, Updates.h, PrimOps.cmm. + recordMutated p = callImport "__asterius_recordMutated" [convertUInt64ToFloat64 p] -dirtyMutVarFunction _ = runEDSL "dirty_MUT_VAR" $ do - [_, p] <- params [I64, I64] - if' [] (loadI64 p 0 `eqInt64` symbol "stg_MUT_VAR_CLEAN_info") +recordMutatedFunction _ = + runEDSL "recordMutated" $ param I64 >>= recordMutated + +recordMutatedCheckGen p = do + -- Duplicate of recordMutatedCheckGen macro in Cmm.h + -- Records the closure at p as mutated, + -- but only after checking that it belong + -- to the old generation. + -- 1. get block descriptor + let mb = p `subInt64` (p `andInt64` (constI64 $ mblock_size - 1)) + let bd = mb `addInt64` constI64 offset_first_bdescr + -- 2. get generation number + let gen_no = loadI16 bd offset_bdescr_gen_no + if' [] (eqZInt32 $ gen_no) + mempty + (recordMutated p) + +dirtyTSO tso = + if' + [] + (eqZInt32 $ loadI32 tso offset_StgTSO_dirty) (do - storeI64 p 0 $ symbol "stg_MUT_VAR_DIRTY_info" - recordMutated p) + storeI32 tso offset_StgTSO_dirty $ constI32 1 + recordMutatedCheckGen tso) mempty -dirtyMVarFunction _ = runEDSL "dirty_MVAR" $ do - [reg, p, old] <- params [I64, I64, I64] - call "update_MVAR" [reg, p, old] +dirtyTSOFunction _ = + runEDSL "dirty_TSO" $ param I64 >>= dirtyTSO -updateMVarFunction _ = runEDSL "update_MVAR" $ do - [_, p, _old] <- params [I64, I64, I64] - if' [] (loadI64 p 0 `eqInt64` symbol "stg_MVAR_CLEAN_info") +dirtySTACK stack = + if' + [] + (eqZInt32 $ loadI32 stack offset_StgStack_dirty) (do - storeI64 p 0 $ symbol "stg_MVAR_DIRTY_info" - recordMutated p) + storeI32 stack offset_StgStack_dirty $ constI32 1 + recordMutatedCheckGen stack) mempty -dirtyStackFunction _ = runEDSL "dirty_STACK" $ do - [cap, stack] <- params [I64, I64] - dirtySTACK cap stack - -recordClosureMutatedFunction _ = runEDSL "recordClosureMutated" $ do - [_cap, p] <- params [I64, I64] - recordMutated p +dirtyStackFunction _ = + runEDSL "dirty_STACK" $ param I64 >>= dirtySTACK -recordMutableCapFunction _ = runEDSL "recordMutableCap" $ do - [p, _cap, _gen] <- params [I64, I64, I64] - recordMutated p +dirtyMutVarFunction _ = runEDSL "dirty_MUT_VAR" $ do + -- duplicate of dirty_MUT_VAR macro in Cmm.h + [_, p] <- params [I64, I64] + if' + [] + (loadI64 p 0 `eqInt64` symbol "stg_MUT_VAR_CLEAN_info") + (do + storeI64 p 0 $ symbol "stg_MUT_VAR_DIRTY_info" + recordMutatedCheckGen p) + mempty tryWakeupThreadFunction _ = runEDSL "tryWakeupThread" $ do [_cap, tso] <- params [I64, I64] diff --git a/asterius/src/Asterius/JSGen/Constants.hs b/asterius/src/Asterius/JSGen/Constants.hs index be91a7c808..5b971daff1 100644 --- a/asterius/src/Asterius/JSGen/Constants.hs +++ b/asterius/src/Asterius/JSGen/Constants.hs @@ -45,6 +45,8 @@ rtsConstants = intHex offset_bdescr_free, ";\nexport const offset_bdescr_link = ", intHex offset_bdescr_link, + ";\nexport const offset_bdescr_gen_no = ", + intHex offset_bdescr_gen_no, ";\nexport const offset_bdescr_node = ", intHex offset_bdescr_node, ";\nexport const offset_bdescr_flags = ", diff --git a/ghc-toolkit/boot-libs/rts/PrimOps.cmm b/ghc-toolkit/boot-libs/rts/PrimOps.cmm index 51765bcb05..38c88f7866 100644 --- a/ghc-toolkit/boot-libs/rts/PrimOps.cmm +++ b/ghc-toolkit/boot-libs/rts/PrimOps.cmm @@ -278,19 +278,8 @@ stg_newArrayzh ( W_ n /* words */, gcptr init ) stg_unsafeThawArrayzh ( gcptr arr ) { - // To decide whether to add the thawed array to a mut_list we check - // the info table. MUT_ARR_PTRS_FROZEN_DIRTY means it's already on a - // mut_list so no need to add it again. MUT_ARR_PTRS_FROZEN_CLEAN means it's - // not and we should add it to a mut_list. - if (StgHeader_info(arr) != stg_MUT_ARR_PTRS_FROZEN_DIRTY_info) { - SET_INFO(arr,stg_MUT_ARR_PTRS_DIRTY_info); - // must be done after SET_INFO, because it ASSERTs closure_MUTABLE(): - recordMutable(arr); - return (arr); - } else { - SET_INFO(arr,stg_MUT_ARR_PTRS_DIRTY_info); - return (arr); - } + dirty_MUT_ARR_PTRS_FROZEN(arr) + return (arr); } stg_copyArrayzh ( gcptr src, W_ src_off, gcptr dst, W_ dst_off, W_ n ) @@ -349,11 +338,7 @@ stg_casArrayzh ( gcptr arr, W_ ind, gcptr old, gcptr new ) return (1,h); } else { // Compare and Swap Succeeded: - if (StgHeader_info(arr) == stg_MUT_ARR_PTRS_CLEAN_info) { - // GC write barrier - SET_INFO(arr,stg_MUT_ARR_PTRS_DIRTY_info); - recordMutable(arr); - } + dirty_MUT_ARR_PTRS(arr) len = StgMutArrPtrs_ptrs(arr); // The write barrier. We must write a byte into the mark table: I8[arr + SIZEOF_StgMutArrPtrs + WDS(len) + (ind >> MUT_ARR_PTRS_CARD_BITS )] = 1; @@ -434,14 +419,8 @@ stg_newSmallArrayzh ( W_ n /* words */, gcptr init ) stg_unsafeThawSmallArrayzh ( gcptr arr ) { // See stg_unsafeThawArrayzh - if (StgHeader_info(arr) != stg_SMALL_MUT_ARR_PTRS_FROZEN_DIRTY_info) { - SET_INFO(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); - recordMutable(arr); - return (arr); - } else { - SET_INFO(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); - return (arr); - } + dirty_SMALL_MUT_ARR_PTRS_FROZEN(arr) + return (arr); } stg_cloneSmallArrayzh ( gcptr src, W_ offset, W_ n ) @@ -469,10 +448,7 @@ stg_copySmallArrayzh ( gcptr src, W_ src_off, gcptr dst, W_ dst_off, W_ n) { W_ dst_p, src_p, bytes; - if (StgHeader_info(dst) != stg_SMALL_MUT_ARR_PTRS_DIRTY_info) { - SET_INFO(dst, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); - recordMutable(dst); - } + dirty_SMALL_MUT_ARR_PTRS(dst) dst_p = dst + SIZEOF_StgSmallMutArrPtrs + WDS(dst_off); src_p = src + SIZEOF_StgSmallMutArrPtrs + WDS(src_off); @@ -486,11 +462,7 @@ stg_copySmallMutableArrayzh ( gcptr src, W_ src_off, gcptr dst, W_ dst_off, W_ n { W_ dst_p, src_p, bytes; - - if (StgHeader_info(dst) != stg_SMALL_MUT_ARR_PTRS_DIRTY_info) { - SET_INFO(dst, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); - recordMutable(dst); - } + dirty_SMALL_MUT_ARR_PTRS(dst) dst_p = dst + SIZEOF_StgSmallMutArrPtrs + WDS(dst_off); src_p = src + SIZEOF_StgSmallMutArrPtrs + WDS(src_off); @@ -519,12 +491,7 @@ stg_casSmallArrayzh ( gcptr arr, W_ ind, gcptr old, gcptr new ) return (1,h); } else { // Compare and Swap Succeeded: - if (StgHeader_info(arr) != stg_SMALL_MUT_ARR_PTRS_DIRTY_info) { - // Set `arr` as mutated - SET_INFO(arr, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); - recordMutable(arr); - } - + dirty_SMALL_MUT_ARR_PTRS(arr) return (0,new); } } @@ -564,7 +531,7 @@ stg_casMutVarzh ( gcptr mv, gcptr old, gcptr new ) return (1,h); } else { if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) { - ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr"); + dirty_MUT_VAR(mv); } return (0,new); } @@ -577,7 +544,7 @@ stg_casMutVarzh ( gcptr mv, gcptr old, gcptr new ) } else { StgMutVar_var(mv) = new; if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) { - ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr"); + dirty_MUT_VAR(mv); } return (0,new); } @@ -648,7 +615,7 @@ stg_atomicModifyMutVar2zh ( gcptr mv, gcptr f ) #endif if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) { - ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr"); + dirty_MUT_VAR(mv); } return (x,z); @@ -699,9 +666,7 @@ stg_atomicModifyMutVarzuzh ( gcptr mv, gcptr f ) StgMutVar_var(mv) = z; #endif - if (GET_INFO(mv) == stg_MUT_VAR_CLEAN_info) { - ccall dirty_MUT_VAR(BaseReg "ptr", mv "ptr"); - } + dirty_MUT_VAR(mv); return (x,z); } @@ -775,7 +740,7 @@ stg_addCFinalizzerToWeakzh ( W_ fptr, // finalizer unlockClosure(w, info); - recordMutable(w); + recordMutatedCheckGen(w) IF_DEBUG(weak, ccall debugBelch("Adding a finalizer to %p\n",w)); @@ -1505,7 +1470,7 @@ stg_newMVarzh () mvar = Hp - SIZEOF_StgMVar + WDS(1); // No memory barrier needed as this is a new allocation. SET_HDR(mvar,stg_MVAR_DIRTY_info,CCCS); - // MVARs start dirty: generation 0 has no mutable list + // MVARs start dirty: generation 0 has no mutable list StgMVar_head(mvar) = stg_END_TSO_QUEUE_closure; StgMVar_tail(mvar) = stg_END_TSO_QUEUE_closure; StgMVar_value(mvar) = stg_END_TSO_QUEUE_closure; @@ -1536,9 +1501,7 @@ stg_takeMVarzh ( P_ mvar /* :: MVar a */ ) * and wait until we're woken up. */ if (StgMVar_value(mvar) == stg_END_TSO_QUEUE_closure) { - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) // We want to put the heap check down here in the slow path, // but be careful to unlock the closure before returning to @@ -1561,8 +1524,7 @@ stg_takeMVarzh ( P_ mvar /* :: MVar a */ ) StgMVar_head(mvar) = q; } else { StgMVarTSOQueue_link(StgMVar_tail(mvar)) = q; - ccall recordClosureMutated(MyCapability() "ptr", - StgMVar_tail(mvar)); + recordMutatedCheckGen(StgMVar_tail(mvar)) } StgTSO__link(CurrentTSO) = q; StgTSO_block_info(CurrentTSO) = mvar; @@ -1595,9 +1557,7 @@ loop: // There are putMVar(s) waiting... wake up the first thread on the queue - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", val "ptr"); - } + dirty_MVAR(mvar, info) tso = StgMVarTSOQueue_tso(q); StgMVar_head(mvar) = StgMVarTSOQueue_link(q); @@ -1664,9 +1624,7 @@ loop: // There are putMVar(s) waiting... wake up the first thread on the queue - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", val "ptr"); - } + dirty_MVAR(mvar, info) tso = StgMVarTSOQueue_tso(q); StgMVar_head(mvar) = StgMVarTSOQueue_link(q); @@ -1702,9 +1660,7 @@ stg_putMVarzh ( P_ mvar, /* :: MVar a */ if (StgMVar_value(mvar) != stg_END_TSO_QUEUE_closure) { - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) // We want to put the heap check down here in the slow path, // but be careful to unlock the closure before returning to @@ -1726,8 +1682,7 @@ stg_putMVarzh ( P_ mvar, /* :: MVar a */ StgMVar_head(mvar) = q; } else { StgMVarTSOQueue_link(StgMVar_tail(mvar)) = q; - ccall recordClosureMutated(MyCapability() "ptr", - StgMVar_tail(mvar)); + recordMutatedCheckGen(StgMVar_tail(mvar)) } StgTSO__link(CurrentTSO) = q; StgTSO_block_info(CurrentTSO) = mvar; @@ -1739,18 +1694,14 @@ stg_putMVarzh ( P_ mvar, /* :: MVar a */ // We are going to mutate the closure, make sure its current pointers // are marked. - if (info == stg_MVAR_CLEAN_info) { - ccall update_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) q = StgMVar_head(mvar); loop: if (q == stg_END_TSO_QUEUE_closure) { /* No further takes, the MVar is now full. */ StgMVar_value(mvar) = val; - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) unlockClosure(mvar, stg_MVAR_DIRTY_info); return (); } @@ -1787,7 +1738,7 @@ loop: StgTSO__link(tso) = stg_END_TSO_QUEUE_closure; if (TO_W_(StgStack_dirty(stack)) == 0) { - ccall dirty_STACK(MyCapability() "ptr", stack "ptr"); + dirty_STACK(stack) } ccall tryWakeupThread(MyCapability() "ptr", tso); @@ -1831,9 +1782,7 @@ stg_tryPutMVarzh ( P_ mvar, /* :: MVar a */ loop: if (q == stg_END_TSO_QUEUE_closure) { /* No further takes, the MVar is now full. */ - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) StgMVar_value(mvar) = val; unlockClosure(mvar, stg_MVAR_DIRTY_info); @@ -1872,7 +1821,7 @@ loop: StgTSO__link(tso) = stg_END_TSO_QUEUE_closure; if (TO_W_(StgStack_dirty(stack)) == 0) { - ccall dirty_STACK(MyCapability() "ptr", stack "ptr"); + dirty_STACK(stack) } ccall tryWakeupThread(MyCapability() "ptr", tso); @@ -1902,9 +1851,7 @@ stg_readMVarzh ( P_ mvar, /* :: MVar a */ ) */ if (StgMVar_value(mvar) == stg_END_TSO_QUEUE_closure) { - if (info == stg_MVAR_CLEAN_info) { - ccall dirty_MVAR(BaseReg "ptr", mvar "ptr", StgMVar_value(mvar) "ptr"); - } + dirty_MVAR(mvar, info) ALLOC_PRIM_WITH_CUSTOM_FAILURE (SIZEOF_StgMVarTSOQueue, diff --git a/ghc-toolkit/boot-libs/rts/Updates.h b/ghc-toolkit/boot-libs/rts/Updates.h index 1bd3e065af..b89094a5b3 100644 --- a/ghc-toolkit/boot-libs/rts/Updates.h +++ b/ghc-toolkit/boot-libs/rts/Updates.h @@ -23,17 +23,14 @@ * field. So, we call LDV_RECORD_CREATE(). */ +#if defined(CMINUSMINUS) + /* - * We have two versions of this macro (sadly), one for use in C-- code, - * and the other for C. - * * The and_then argument is a performance hack so that we can paste in * the continuation code directly. It helps shave a couple of * instructions off the common case in the update code, which is * worthwhile (the update code is often part of the inner loop). */ -#if defined(CMINUSMINUS) - #define UPDATE_FRAME_FIELDS(w_,p_,info_ptr,ccs,p2,updatee) \ w_ info_ptr, \ PROF_HDR_FIELDS(w_,ccs,p2) \ @@ -56,7 +53,7 @@ LDV_RECORD_CREATE(p1); \ bd = Bdescr(p1); \ if (bdescr_gen_no(bd) != 0 :: bits16) { \ - recordMutableCap(p1, TO_W_(bdescr_gen_no(bd))); \ + recordMutated(p1); \ TICK_UPD_OLD_IND(); \ and_then; \ } else { \ @@ -66,30 +63,10 @@ #else /* !CMINUSMINUS */ -INLINE_HEADER void updateWithIndirection (Capability *cap, - StgClosure *p1, - StgClosure *p2) -{ - bdescr *bd; - - ASSERT( (P_)p1 != (P_)p2 ); - /* not necessarily true: ASSERT( !closure_IND(p1) ); */ - /* occurs in RaiseAsync.c:raiseAsync() */ - /* See Note [Heap memory barriers] in SMP.h */ - write_barrier(); - OVERWRITING_CLOSURE(p1); - ((StgInd *)p1)->indirectee = p2; - write_barrier(); - SET_INFO(p1, &stg_BLACKHOLE_info); - LDV_RECORD_CREATE(p1); - bd = Bdescr((StgPtr)p1); - if (bd->gen_no != 0) { - recordMutableCap(p1, cap, bd->gen_no); - TICK_UPD_OLD_IND(); - } else { - TICK_UPD_NEW_IND(); - } -} +// Note (asterius): removed C version of updateWithIndirection +INLINE_HEADER void updateWithIndirection (Capability *cap, + StgClosure *p1, + StgClosure *p2) {} #endif /* CMINUSMINUS */ diff --git a/ghc-toolkit/cbits/ghc_constants.c b/ghc-toolkit/cbits/ghc_constants.c index 4828c71e21..c3d3196c23 100644 --- a/ghc-toolkit/cbits/ghc_constants.c +++ b/ghc-toolkit/cbits/ghc_constants.c @@ -32,6 +32,8 @@ HsInt offset_bdescr_free() { return offsetof(bdescr, free); } HsInt offset_bdescr_link() { return offsetof(bdescr, link); } +HsInt offset_bdescr_gen_no() { return offsetof(bdescr, gen_no); } + HsInt offset_bdescr_node() { return offsetof(bdescr, node); } HsInt offset_bdescr_flags() { return offsetof(bdescr, flags); } diff --git a/ghc-toolkit/ghc-libdir/include/Cmm.h b/ghc-toolkit/ghc-libdir/include/Cmm.h index 3112ebb1ef..4e37f58247 100644 --- a/ghc-toolkit/ghc-libdir/include/Cmm.h +++ b/ghc-toolkit/ghc-libdir/include/Cmm.h @@ -815,17 +815,66 @@ #define END_TSO_QUEUE stg_END_TSO_QUEUE_closure #define STM_AWOKEN stg_STM_AWOKEN_closure -#define recordMutableCap(p, gen) \ - ccall recordMutableCap(p "ptr", 0, gen "ptr") +/* ----------------------------------------------------------------------------- + Write barriers + -------------------------------------------------------------------------- */ + +// [asterius] Helper macros to manage clean/dirty marks and +// the remebered set for generational GC. +// Note: the original GHC macros `recordMutable` and `recordMutableCap` +// have been removed, and the following added instead. -#define recordMutable(p) \ - P_ __p; \ - W_ __bd; \ +#define recordMutated(p) \ + ccall recordMutated(p "ptr") + +#define recordMutatedCheckGen(p) \ W_ __gen; \ - __p = p; \ - __bd = Bdescr(__p); \ - __gen = TO_W_(bdescr_gen_no(__bd)); \ - if (__gen > 0) { recordMutableCap(__p, __gen); } + __gen = TO_W_(bdescr_gen_no(Bdescr(p))); \ + if (__gen > 0) { recordMutated(p); } + +#define dirty_MUT_ARR_PTRS_FROZEN(p) \ + if (StgHeader_info(p) != stg_MUT_ARR_PTRS_FROZEN_DIRTY_info) { \ + SET_INFO(p, stg_MUT_ARR_PTRS_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } + +#define dirty_SMALL_MUT_ARR_PTRS_FROZEN(p) \ + if (StgHeader_info(p) != stg_SMALL_MUT_ARR_PTRS_FROZEN_DIRTY_info) { \ + SET_INFO(p, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } else { \ + SET_INFO(p, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); \ + } + +#define dirty_SMALL_MUT_ARR_PTRS(p) \ + if (StgHeader_info(p) != stg_SMALL_MUT_ARR_PTRS_DIRTY_info) { \ + SET_INFO(p, stg_SMALL_MUT_ARR_PTRS_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } + +#define dirty_MUT_ARR_PTRS(p) \ + if (StgHeader_info(p) == stg_MUT_ARR_PTRS_CLEAN_info) { \ + SET_INFO(p, stg_MUT_ARR_PTRS_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } + +#define dirty_TSO(p) \ + ccall dirty_TSO(p "ptr"); + +#define dirty_STACK(p) \ + ccall dirty_STACK(p "ptr"); + +#define dirty_MVAR(p, info) \ + if (info == stg_MVAR_CLEAN_info) { \ + SET_INFO(p, stg_MVAR_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } + +#define dirty_MUT_VAR(p) \ + if (GET_INFO(p) == stg_MUT_VAR_CLEAN_info) { \ + SET_INFO(p, stg_MUT_VAR_DIRTY_info); \ + recordMutatedCheckGen(p) \ + } /* ----------------------------------------------------------------------------- Arrays diff --git a/ghc-toolkit/ghc-libdir/include/rts/storage/GC.h b/ghc-toolkit/ghc-libdir/include/rts/storage/GC.h index 1571975852..8cb093ed34 100644 --- a/ghc-toolkit/ghc-libdir/include/rts/storage/GC.h +++ b/ghc-toolkit/ghc-libdir/include/rts/storage/GC.h @@ -214,6 +214,19 @@ extern W_ large_alloc_lim; void performGC(void); void performMajorGC(void); +/* ----------------------------------------------------------------------------- + Write barriers + -------------------------------------------------------------------------- */ + +// Note (asterius): the real write barrier is only `recordMutated`. +// The `dirty_*` functions (and the macros in Cmm.h) call that function, +// but only after dirtying the header and checking the generation number. + +void recordMutated(StgClosure *p); +void dirty_MUT_VAR(StgRegTable *reg, StgClosure *p); +void dirty_TSO(StgClosure *tso); +void dirty_STACK(StgClosure *stack); + /* ----------------------------------------------------------------------------- The CAF table - used to let us revert CAFs in GHCi -------------------------------------------------------------------------- */ @@ -227,15 +240,6 @@ void revertCAFs (void); // (preferably use RtsConfig.keep_cafs instead) void setKeepCAFs (void); -/* ----------------------------------------------------------------------------- - This is the write barrier for MUT_VARs, a.k.a. IORefs. A - MUT_VAR_CLEAN object is not on the mutable list; a MUT_VAR_DIRTY - is. When written to, a MUT_VAR_CLEAN turns into a MUT_VAR_DIRTY - and is put on the mutable list. - -------------------------------------------------------------------------- */ - -void dirty_MUT_VAR(StgRegTable *reg, StgClosure *p); - /* set to disable CAF garbage collection in GHCi. */ /* (needed when dynamic libraries are used). */ extern bool keepCAFs; @@ -245,4 +249,4 @@ INLINE_HEADER void initBdescr(bdescr *bd, generation *gen, generation *dest) bd->gen = gen; bd->gen_no = gen->no; bd->dest_no = dest->no; -} +} \ No newline at end of file diff --git a/ghc-toolkit/ghc-libdir/include/rts/storage/TSO.h b/ghc-toolkit/ghc-libdir/include/rts/storage/TSO.h index 5f0e777f30..f20509425b 100644 --- a/ghc-toolkit/ghc-libdir/include/rts/storage/TSO.h +++ b/ghc-toolkit/ghc-libdir/include/rts/storage/TSO.h @@ -219,12 +219,9 @@ INLINE_HEADER StgPtr tso_SpLim (StgTSO* tso) functions -------------------------------------------------------------------------- */ -void dirty_TSO (Capability *cap, StgTSO *tso); void setTSOLink (Capability *cap, StgTSO *tso, StgTSO *target); void setTSOPrev (Capability *cap, StgTSO *tso, StgTSO *target); -void dirty_STACK (Capability *cap, StgStack *stack); - /* ----------------------------------------------------------------------------- Invariants: diff --git a/ghc-toolkit/include-private/Capability.h b/ghc-toolkit/include-private/Capability.h index 05a827c7b9..a595262a1f 100644 --- a/ghc-toolkit/include-private/Capability.h +++ b/ghc-toolkit/include-private/Capability.h @@ -289,11 +289,6 @@ extern PendingSync * volatile pending_sync; // void waitForCapability (Capability **cap/*in/out*/, Task *task); -EXTERN_INLINE void recordMutableCap (const StgClosure *p, Capability *cap, - uint32_t gen); - -EXTERN_INLINE void recordClosureMutated (Capability *cap, StgClosure *p); - #if defined(THREADED_RTS) // Gives up the current capability IFF there is a higher-priority @@ -399,34 +394,6 @@ INLINE_HEADER bool emptyInbox(Capability *cap); * INLINE functions... private below here * -------------------------------------------------------------------------- */ -EXTERN_INLINE void -recordMutableCap (const StgClosure *p, Capability *cap, uint32_t gen) -{ - bdescr *bd; - - // We must own this Capability in order to modify its mutable list. - // ASSERT(cap->running_task == myTask()); - // NO: assertion is violated by performPendingThrowTos() - bd = cap->mut_lists[gen]; - if (bd->free >= bd->start + BLOCK_SIZE_W) { - bdescr *new_bd; - new_bd = allocBlockOnNode_lock(cap->node); - new_bd->link = bd; - bd = new_bd; - cap->mut_lists[gen] = bd; - } - *bd->free++ = (StgWord)p; -} - -EXTERN_INLINE void -recordClosureMutated (Capability *cap, StgClosure *p) -{ - bdescr *bd; - bd = Bdescr((StgPtr)p); - if (bd->gen_no != 0) recordMutableCap(p,cap,bd->gen_no); -} - - #if defined(THREADED_RTS) INLINE_HEADER bool emptySparkPoolCap (Capability *cap) diff --git a/ghc-toolkit/src/Language/Haskell/GHC/Toolkit/Constants.hs b/ghc-toolkit/src/Language/Haskell/GHC/Toolkit/Constants.hs index 6d2e5e3977..5b4fe9bb3b 100644 --- a/ghc-toolkit/src/Language/Haskell/GHC/Toolkit/Constants.hs +++ b/ghc-toolkit/src/Language/Haskell/GHC/Toolkit/Constants.hs @@ -35,6 +35,8 @@ foreign import ccall unsafe "offset_bdescr_free" offset_bdescr_free :: Int foreign import ccall unsafe "offset_bdescr_link" offset_bdescr_link :: Int +foreign import ccall unsafe "offset_bdescr_gen_no" offset_bdescr_gen_no :: Int + foreign import ccall unsafe "offset_bdescr_node" offset_bdescr_node :: Int foreign import ccall unsafe "offset_bdescr_flags" offset_bdescr_flags :: Int