/* ----------------------------------------------------------------------------- * * (c) The GHC Team, 1998-2004 * * Code to perform updates. * * This file is written in a subset of C--, extended with various * features specific to GHC. It is compiled by GHC directly. For the * syntax of .cmm files, see the parser in ghc/compiler/GHC/Cmm/Parser.y. * * ---------------------------------------------------------------------------*/ #include "Cmm.h" #include "rts/prof/LDV.h" #include "Updates.h" #if !defined(UnregisterisedCompiler) import CLOSURE UPD_CON_IN_NEW_ctr; import CLOSURE UPD_OLD_IND_ctr; import CLOSURE UPD_NEW_IND_ctr; import CLOSURE stg_BLACKHOLE_info; #endif /* * The update code is PERFORMANCE CRITICAL, if you make any changes * here make sure you eyeball the assembly and check that the fast * path (update in generation 0) is optimal. * * The return(ret) bit is passed down and pinned on the end of each * branch (there end up being two major branches in the code), since * we don't mind duplicating this jump. */ INFO_TABLE_RET ( stg_upd_frame, UPDATE_FRAME, UPDATE_FRAME_FIELDS(W_,P_,info_ptr,_ccs,_unused,updatee) ) return (P_ ret) /* the closure being returned */ { ASSERT(HpAlloc == 0); // Note [HpAlloc] /* ToDo: it might be a PAP, so we should check... */ TICK_UPD_CON_IN_NEW(sizeW_fromITBL(%GET_STD_INFO(updatee))); updateWithIndirection(updatee, ret, return (ret)); } /* * An update frame where the updatee has been replaced by a BLACKHOLE * closure by threadPaused. We may have threads to wake up, and we * also have to check whether the blackhole has been updated by * another thread in the meantime. */ INFO_TABLE_RET ( stg_marked_upd_frame, UPDATE_FRAME, UPDATE_FRAME_FIELDS(W_,P_,info_ptr,_ccs,_unused,updatee) ) return (P_ ret) /* the closure being returned */ { W_ v; ASSERT(HpAlloc == 0); // Note [HpAlloc] // we know the closure is a BLACKHOLE v = StgInd_indirectee(updatee); if (GETTAG(v) != 0) (likely: False) { // updated by someone else: discard our value and use the // other one to increase sharing, but check the blocking // queues to see if any threads were waiting on this BLACKHOLE. ccall checkBlockingQueues(MyCapability() "ptr", CurrentTSO "ptr"); return (v); } // common case: it is still our BLACKHOLE if (v == CurrentTSO) (likely: True) { updateWithIndirection(updatee, ret, return (ret)); } // The other cases are all handled by the generic code ccall updateThunk (MyCapability() "ptr", CurrentTSO "ptr", updatee "ptr", ret "ptr"); return (ret); } /* * Special update frame code for CAFs and eager-blackholed thunks: it * knows how to update blackholes, but is distinct from * stg_marked_upd_frame so that lazy blackholing won't treat it as the * high watermark. */ INFO_TABLE_RET ( stg_bh_upd_frame, UPDATE_FRAME, UPDATE_FRAME_FIELDS(W_,P_,info_ptr,ccs,_unused,updatee) ) return (P_ ret) /* the closure being returned */ { // This all compiles away to a single jump instruction (sigh) jump RET_LBL(stg_marked_upd_frame) ( UPDATE_FRAME_FIELDS(,,info_ptr,ccs,_unused,updatee) ) (ret); } /* Note [HpAlloc] * ~~~~~~~~~~~~~~ * HpAlloc is required to be zero unless we just bumped Hp and failed * the heap check: see HeapStackCheck.cmm. Failures that result from * HpAlloc being non-zero are very hard to track down, because they * manifest as spurious heap corruption that happens only with +RTS * -N2 or greater (because then we have a lot more * interruptCapability() calls happening). Hence, we assert * HpAlloc==0 as often as possible, and in the update code is a good * place to do that. */