Merging in the new codegen branch
[ghc-hetmet.git] / compiler / codeGen / StgCmmHeap.hs
diff --git a/compiler/codeGen/StgCmmHeap.hs b/compiler/codeGen/StgCmmHeap.hs
new file mode 100644 (file)
index 0000000..6a8a435
--- /dev/null
@@ -0,0 +1,519 @@
+-----------------------------------------------------------------------------
+--
+-- Stg to C--: heap management functions
+--
+-- (c) The University of Glasgow 2004-2006
+--
+-----------------------------------------------------------------------------
+
+module StgCmmHeap (
+       getVirtHp, setVirtHp, setRealHp, 
+       getHpRelOffset, hpRel,
+
+       entryHeapCheck, altHeapCheck,
+
+       layOutDynConstr, layOutStaticConstr,
+       mkVirtHeapOffsets, mkStaticClosureFields, mkStaticClosure,
+
+       allocDynClosure, emitSetDynHdr
+    ) where
+
+#include "HsVersions.h"
+
+import StgSyn
+import CLabel
+import StgCmmLayout
+import StgCmmUtils
+import StgCmmMonad
+import StgCmmProf
+import StgCmmTicky
+import StgCmmGran
+import StgCmmClosure
+import StgCmmEnv
+
+import MkZipCfgCmm
+
+import SMRep
+import CmmExpr
+import CmmUtils
+import DataCon
+import TyCon
+import CostCentre
+import Outputable
+import FastString( LitString, mkFastString, sLit )
+import Constants
+import Data.List
+
+
+-----------------------------------------------------------
+--             Layout of heap objects
+-----------------------------------------------------------
+
+layOutDynConstr, layOutStaticConstr
+       :: DataCon -> [(PrimRep, a)]
+       -> (ClosureInfo, [(a, VirtualHpOffset)])
+-- No Void arguments in result
+
+layOutDynConstr    = layOutConstr False
+layOutStaticConstr = layOutConstr True
+
+layOutConstr :: Bool -> DataCon -> [(PrimRep, a)]
+            -> (ClosureInfo, [(a, VirtualHpOffset)])
+layOutConstr is_static data_con args
+   = (mkConInfo is_static data_con tot_wds ptr_wds,
+      things_w_offsets)
+  where
+    (tot_wds,           --  #ptr_wds + #nonptr_wds
+     ptr_wds,           --  #ptr_wds
+     things_w_offsets) = mkVirtHeapOffsets False{-not a thunk-} args
+
+
+-----------------------------------------------------------
+--             Initialise dynamic heap objects
+-----------------------------------------------------------
+
+allocDynClosure
+       :: ClosureInfo
+       -> CmmExpr              -- Cost Centre to stick in the object
+       -> CmmExpr              -- Cost Centre to blame for this alloc
+                               -- (usually the same; sometimes "OVERHEAD")
+
+       -> [(StgArg, VirtualHpOffset)]  -- Offsets from start of the object
+                                       -- ie Info ptr has offset zero.
+                                       -- No void args in here
+       -> FCode LocalReg
+
+-- allocDynClosure allocates the thing in the heap, 
+-- and modifies the virtual Hp to account for this.
+
+-- Note [Return a LocalReg]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- allocDynClosure returns a LocalReg, not a (Hp+8) CmmExpr.
+-- Reason:
+--     ...allocate object...
+--     obj = Hp + 8    
+--     y = f(z)
+--     ...here obj is still valid,
+--        but Hp+8 means something quite different...
+
+
+allocDynClosure cl_info use_cc _blame_cc args_w_offsets
+  = do { virt_hp <- getVirtHp
+
+       -- SAY WHAT WE ARE ABOUT TO DO
+       ; tickyDynAlloc cl_info
+       ; profDynAlloc cl_info use_cc   
+               -- ToDo: This is almost certainly wrong
+               -- We're ignoring blame_cc. But until we've
+               -- fixed the boxing hack in chooseDynCostCentres etc,
+               -- we're worried about making things worse by "fixing"
+               -- this part to use blame_cc!
+
+       -- FIND THE OFFSET OF THE INFO-PTR WORD
+       ; let   info_offset = virt_hp + 1
+               -- info_offset is the VirtualHpOffset of the first
+               -- word of the new object
+               -- Remember, virtHp points to last allocated word, 
+               -- ie 1 *before* the info-ptr word of new object.
+
+               info_ptr = CmmLit (CmmLabel (infoTableLabelFromCI cl_info))
+
+       -- ALLOCATE THE OBJECT
+       ; base <- getHpRelOffset info_offset
+        ; emit (mkComment $ mkFastString "allocDynClosure")
+       ; emitSetDynHdr base info_ptr  use_cc
+       ; let (args, offsets) = unzip args_w_offsets
+       ; cmm_args <- mapM getArgAmode args     -- No void args 
+       ; hpStore base cmm_args offsets
+
+       -- BUMP THE VIRTUAL HEAP POINTER
+       ; setVirtHp (virt_hp + closureSize cl_info)
+       
+       -- Assign to a temporary and return
+       -- Note [Return a LocalReg]
+       ; hp_rel <- getHpRelOffset info_offset
+       ; assignTemp hp_rel }
+
+emitSetDynHdr :: CmmExpr -> CmmExpr -> CmmExpr -> FCode ()
+emitSetDynHdr base info_ptr ccs 
+  = hpStore base header [0..]
+  where
+    header :: [CmmExpr]
+    header = [info_ptr] ++ dynProfHdr ccs
+       -- ToDo: Gransim stuff
+       -- ToDo: Parallel stuff
+       -- No ticky header
+
+hpStore :: CmmExpr -> [CmmExpr] -> [VirtualHpOffset] -> FCode ()
+-- Store the item (expr,off) in base[off]
+hpStore base vals offs
+  = emit (catAGraphs (zipWith mk_store vals offs))
+  where
+    mk_store val off = mkStore (cmmOffsetW base off) val 
+
+
+-----------------------------------------------------------
+--             Layout of static closures
+-----------------------------------------------------------
+
+-- Make a static closure, adding on any extra padding needed for CAFs,
+-- and adding a static link field if necessary.
+
+mkStaticClosureFields 
+       :: ClosureInfo 
+       -> CostCentreStack 
+       -> Bool                 -- Has CAF refs
+       -> [CmmLit]             -- Payload
+       -> [CmmLit]             -- The full closure
+mkStaticClosureFields cl_info ccs caf_refs payload
+  = mkStaticClosure info_lbl ccs payload padding_wds 
+       static_link_field saved_info_field
+  where
+    info_lbl = infoTableLabelFromCI cl_info
+
+    -- CAFs must have consistent layout, regardless of whether they
+    -- are actually updatable or not.  The layout of a CAF is:
+    --
+    --        3 saved_info
+    --        2 static_link
+    --        1 indirectee
+    --        0 info ptr
+    --
+    -- the static_link and saved_info fields must always be in the same
+    -- place.  So we use closureNeedsUpdSpace rather than
+    -- closureUpdReqd here:
+
+    is_caf = closureNeedsUpdSpace cl_info
+
+    padding_wds
+       | not is_caf = []
+       | otherwise  = ASSERT(null payload) [mkIntCLit 0]
+
+    static_link_field
+       | is_caf || staticClosureNeedsLink cl_info = [static_link_value]
+       | otherwise                                = []
+
+    saved_info_field
+       | is_caf     = [mkIntCLit 0]
+       | otherwise  = []
+
+       -- for a static constructor which has NoCafRefs, we set the
+       -- static link field to a non-zero value so the garbage
+       -- collector will ignore it.
+    static_link_value
+       | caf_refs      = mkIntCLit 0
+       | otherwise     = mkIntCLit 1
+
+
+mkStaticClosure :: CLabel -> CostCentreStack -> [CmmLit]
+  -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit]
+mkStaticClosure info_lbl ccs payload padding_wds static_link_field saved_info_field
+  =  [CmmLabel info_lbl]
+  ++ variable_header_words
+  ++ payload
+  ++ padding_wds
+  ++ static_link_field
+  ++ saved_info_field
+  where
+    variable_header_words
+       =  staticGranHdr
+       ++ staticParHdr
+       ++ staticProfHdr ccs
+       ++ staticTickyHdr
+
+-----------------------------------------------------------
+--             Heap overflow checking
+-----------------------------------------------------------
+
+{- Note [Heap checks]
+   ~~~~~~~~~~~~~~~~~~
+Heap checks come in various forms.  We provide the following entry
+points to the runtime system, all of which use the native C-- entry
+convention.
+
+  * gc() performs garbage collection and returns
+    nothing to its caller
+
+  * A series of canned entry points like
+       r = gc_1p( r )
+    where r is a pointer.  This performs gc, and
+    then returns its argument r to its caller.
+    
+  * A series of canned entry points like
+       gcfun_2p( f, x, y )
+    where f is a function closure of arity 2
+    This performs garbage collection, keeping alive the
+    three argument ptrs, and then tail-calls f(x,y)
+
+These are used in the following circumstances
+
+* entryHeapCheck: Function entry
+    (a) With a canned GC entry sequence
+        f( f_clo, x:ptr, y:ptr ) {
+            Hp = Hp+8
+            if Hp > HpLim goto L
+            ...
+          L: HpAlloc = 8
+             jump gcfun_2p( f_clo, x, y ) }
+     Note the tail call to the garbage collector;
+     it should do no register shuffling  
+
+    (b) No canned sequence
+        f( f_clo, x:ptr, y:ptr, ...etc... ) {
+         T: Hp = Hp+8
+            if Hp > HpLim goto L
+            ...
+          L: HpAlloc = 8
+             call gc()         -- Needs an info table
+            goto T }
+
+* altHeapCheck: Immediately following an eval
+  Started as 
+       case f x y of r { (p,q) -> rhs }
+  (a) With a canned sequence for the results of f
+       (which is the very common case since
+       all boxed cases return just one pointer
+          ...
+          r = f( x, y )
+       K:      -- K needs an info table
+          Hp = Hp+8
+          if Hp > HpLim goto L
+          ...code for rhs...
+
+       L: r = gc_1p( r )
+          goto K }
+
+       Here, the info table needed by the call 
+       to gc_1p should be the *same* as the
+       one for the call to f; the C-- optimiser 
+       spots this sharing opportunity
+
+   (b) No canned sequence for results of f
+       Note second info table
+          ...
+          (r1,r2,r3) = call f( x, y )
+       K: 
+          Hp = Hp+8
+          if Hp > HpLim goto L
+          ...code for rhs...
+
+       L: call gc()    -- Extra info table here
+          goto K
+
+* generalHeapCheck: Anywhere else
+  e.g. entry to thunk
+       case branch *not* following eval, 
+       or let-no-escape
+  Exactly the same as the previous case:
+
+       K:      -- K needs an info table
+          Hp = Hp+8
+          if Hp > HpLim goto L
+          ...
+
+       L: call gc()
+          goto K
+-}
+
+--------------------------------------------------------------
+-- A heap/stack check at a function or thunk entry point.
+
+entryHeapCheck :: LocalReg     -- Function
+              -> [LocalReg]    -- Args (empty for thunk)
+              -> C_SRT
+              -> FCode ()
+              -> FCode ()
+
+entryHeapCheck fun args srt code
+  = heapCheck gc_call code     -- The 'fun' keeps relevant CAFs alive
+  where
+    gc_call 
+       | null args = mkJump (CmmReg (CmmGlobal GCEnter1)) [CmmReg (CmmLocal fun)]
+       | otherwise = case gc_lbl args of
+                       Just lbl -> mkJump (CmmLit (CmmLabel (mkRtsCodeLabel lbl)))
+                                          (map (CmmReg . CmmLocal) (fun:args))
+                       Nothing  -> mkCmmCall generic_gc [] [] srt
+
+    gc_lbl :: [LocalReg] -> Maybe LitString
+    gc_lbl [reg] 
+       | isGcPtrType ty  = Just (sLit "stg_gc_unpt_r1") -- "stg_gc_fun_1p"
+       | isFloatType ty  = case width of
+                             W32 -> Just (sLit "stg_gc_f1") -- "stg_gc_fun_f1"
+                             W64 -> Just (sLit "stg_gc_d1") -- "stg_gc_fun_d1"
+                             _other -> Nothing
+       | otherwise       = case width of
+                             W32 -> Just (sLit "stg_gc_unbx_r1") -- "stg_gc_fun_unbx_r1"
+                             W64 -> Just (sLit "stg_gc_l1") -- "stg_gc_fun_unbx_l1"
+                             _other -> Nothing -- Narrow cases
+       where
+         ty = localRegType reg
+         width = typeWidth ty
+
+    gc_lbl regs = gc_lbl_ptrs (map (isGcPtrType . localRegType) regs)
+
+    gc_lbl_ptrs :: [Bool] -> Maybe LitString
+    -- JD: TEMPORARY -- UNTIL THOSE FUNCTIONS EXIST...
+    --gc_lbl_ptrs [True,True]      = Just (sLit "stg_gc_fun_2p")
+    --gc_lbl_ptrs [True,True,True] = Just (sLit "stg_gc_fun_3p")
+    gc_lbl_ptrs _ = Nothing
+                       
+
+altHeapCheck :: [LocalReg] -> C_SRT -> FCode a -> FCode a
+altHeapCheck regs srt code
+  = heapCheck gc_call code
+  where
+    gc_call
+       | null regs = mkCmmCall generic_gc [] [] srt
+
+       | Just gc_lbl <- rts_label regs -- Canned call
+       = mkCmmCall (CmmLit (CmmLabel (mkRtsCodeLabel gc_lbl)))
+                   regs
+                   (map (CmmReg . CmmLocal) regs)
+                   srt
+       | otherwise             -- No canned call, and non-empty live vars
+       = mkCmmCall generic_gc [] [] srt
+
+    rts_label [reg] 
+       | isGcPtrType ty  = Just (sLit "stg_gc_unpt_r1")
+       | isFloatType ty  = case width of
+                             W32 -> Just (sLit "stg_gc_f1")
+                             W64 -> Just (sLit "stg_gc_d1")
+                             _other -> Nothing
+       | otherwise       = case width of
+                             W32 -> Just (sLit "stg_gc_unbx_r1")
+                             W64 -> Just (sLit "stg_gc_unbx_l1")
+                             _other -> Nothing -- Narrow cases
+       where
+         ty = localRegType reg
+         width = typeWidth ty
+
+    rts_label _ = Nothing
+
+
+generic_gc :: CmmExpr  -- The generic GC procedure; no params, no resuls
+generic_gc = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "stg_gc_fun")))
+
+-------------------------------
+heapCheck :: CmmAGraph -> FCode a -> FCode a
+heapCheck do_gc code
+  = getHeapUsage $ \ hpHw ->
+    do { emit (do_checks hpHw do_gc)
+               -- Emit heap checks, but be sure to do it lazily so 
+               -- that the conditionals on hpHw don't cause a black hole
+       ; tickyAllocHeap hpHw
+       ; doGranAllocate hpHw
+       ; setRealHp hpHw
+       ; code }
+
+do_checks :: WordOff   -- Heap headroom
+         -> CmmAGraph  -- What to do on failure
+         -> CmmAGraph
+do_checks 0 _ 
+  = mkNop
+do_checks alloc do_gc
+  = withFreshLabel "gc" $ \ blk_id ->
+    mkLabel blk_id Nothing
+    <*>        mkAssign hpReg bump_hp
+    <*> mkCmmIfThen hp_oflo 
+               (save_alloc
+            <*> do_gc
+            <*> mkBranch blk_id)
+               -- Bump heap pointer, and test for heap exhaustion
+               -- Note that we don't move the heap pointer unless the 
+               -- stack check succeeds.  Otherwise we might end up
+               -- with slop at the end of the current block, which can 
+               -- confuse the LDV profiler.
+  where
+    alloc_lit = CmmLit (mkIntCLit (alloc*wORD_SIZE))   -- Bytes
+    bump_hp   = cmmOffsetExprB (CmmReg hpReg) alloc_lit
+
+       -- Hp overflow if (Hp > HpLim)
+       -- (Hp has been incremented by now)
+       -- HpLim points to the LAST WORD of valid allocation space.
+    hp_oflo = CmmMachOp mo_wordUGt 
+                 [CmmReg hpReg, CmmReg (CmmGlobal HpLim)]
+
+    save_alloc = mkAssign (CmmGlobal HpAlloc) alloc_lit
+
+{-
+
+{- Unboxed tuple alternatives and let-no-escapes (the two most annoying
+constructs to generate code for!)  For unboxed tuple returns, there
+are an arbitrary number of possibly unboxed return values, some of
+which will be in registers, and the others will be on the stack.  We
+always organise the stack-resident fields into pointers &
+non-pointers, and pass the number of each to the heap check code. -}
+
+unbxTupleHeapCheck 
+       :: [(Id, GlobalReg)]    -- Live registers
+       -> WordOff      -- no. of stack slots containing ptrs
+       -> WordOff      -- no. of stack slots containing nonptrs
+       -> CmmAGraph    -- code to insert in the failure path
+       -> FCode ()
+       -> FCode ()
+
+unbxTupleHeapCheck regs ptrs nptrs fail_code code
+  -- We can't manage more than 255 pointers/non-pointers 
+  -- in a generic heap check.
+  | ptrs > 255 || nptrs > 255 = panic "altHeapCheck"
+  | otherwise 
+  = initHeapUsage $ \ hpHw -> do
+       { codeOnly $ do { do_checks 0 {- no stack check -} hpHw
+                                   full_fail_code rts_label
+                       ; tickyAllocHeap hpHw }
+       ; setRealHp hpHw
+       ; code }
+  where
+    full_fail_code  = fail_code `plusStmts` oneStmt assign_liveness
+    assign_liveness = CmmAssign (CmmGlobal (VanillaReg 9))     -- Ho ho ho!
+                               (CmmLit (mkWordCLit liveness))
+    liveness       = mkRegLiveness regs ptrs nptrs
+    rts_label      = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "stg_gc_ut")))
+
+
+{- Old Gransim comment -- I have no idea whether it still makes sense (SLPJ Sep07)
+For GrAnSim the code for doing a heap check and doing a context switch
+has been separated. Especially, the HEAP_CHK macro only performs a
+heap check. THREAD_CONTEXT_SWITCH should be used for doing a context
+switch. GRAN_FETCH_AND_RESCHEDULE must be put at the beginning of
+every slow entry code in order to simulate the fetching of
+closures. If fetching is necessary (i.e. current closure is not local)
+then an automatic context switch is done. -}
+
+
+When failing a check, we save a return address on the stack and
+jump to a pre-compiled code fragment that saves the live registers
+and returns to the scheduler.
+
+The return address in most cases will be the beginning of the basic
+block in which the check resides, since we need to perform the check
+again on re-entry because someone else might have stolen the resource
+in the meantime.
+
+%************************************************************************
+%*                                                                     *
+     Generic Heap/Stack Checks - used in the RTS
+%*                                                                     *
+%************************************************************************
+
+\begin{code}
+hpChkGen :: CmmExpr -> CmmExpr -> CmmExpr -> FCode ()
+hpChkGen bytes liveness reentry
+  = do_checks' bytes True assigns stg_gc_gen
+  where
+    assigns = mkStmts [
+               CmmAssign (CmmGlobal (VanillaReg 9))  liveness,
+               CmmAssign (CmmGlobal (VanillaReg 10)) reentry
+               ]
+
+-- a heap check where R1 points to the closure to enter on return, and
+-- we want to assign to Sp[0] on failure (used in AutoApply.cmm:BUILD_PAP).
+hpChkNodePointsAssignSp0 :: CmmExpr -> CmmExpr -> FCode ()
+hpChkNodePointsAssignSp0 bytes sp0
+  = do_checks' bytes True assign stg_gc_enter1
+  where assign = oneStmt (CmmStore (CmmReg spReg) sp0)
+
+stg_gc_gen    = CmmLit (CmmLabel (mkRtsCodeLabel (sLit "stg_gc_gen")))
+\end{code}
+
+-}