Refactoring CmmSpillReload and CmmLive.
authorEdward Z. Yang <ezyang@mit.edu>
Thu, 16 Jun 2011 17:28:49 +0000 (18:28 +0100)
committerEdward Z. Yang <ezyang@mit.edu>
Fri, 17 Jun 2011 11:48:41 +0000 (12:48 +0100)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
compiler/cmm/CmmLive.hs
compiler/cmm/CmmPipeline.hs
compiler/cmm/CmmSpillReload.hs
compiler/cmm/cmm-notes

index c87a3a9..8baad04 100644 (file)
@@ -1,4 +1,5 @@
 {-# LANGUAGE GADTs #-}
+
 {-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
 
 module CmmLive
@@ -47,9 +48,6 @@ cmmLiveness graph =
   where entry = g_entry graph
         check facts = noLiveOnEntry entry (expectJust "check" $ mapLookup entry facts) facts
 
-gen_kill :: (DefinerOfLocalRegs a, UserOfLocalRegs a) => a -> CmmLive -> CmmLive
-gen_kill a = gen a . kill a
-
 -- | On entry to the procedure, there had better not be any LocalReg's live-in.
 noLiveOnEntry :: BlockId -> CmmLive -> a -> a
 noLiveOnEntry bid in_fact x =
@@ -57,19 +55,23 @@ noLiveOnEntry bid in_fact x =
   else pprPanic "LocalReg's live-in to graph" (ppr bid <+> ppr in_fact)
 
 -- | The transfer equations use the traditional 'gen' and 'kill'
--- notations, which should be familiar from the dragon book.
-gen  :: UserOfLocalRegs    a => a -> RegSet -> RegSet
-gen  a live = foldRegsUsed    extendRegSet      live a
+-- notations, which should be familiar from the Dragon Book.
+gen  :: UserOfLocalRegs a    => a -> RegSet -> RegSet
+gen  a live = foldRegsUsed extendRegSet      live a
 kill :: DefinerOfLocalRegs a => a -> RegSet -> RegSet
 kill a live = foldRegsDefd delOneFromUniqSet live a
 
--- Testing!
+gen_kill :: (DefinerOfLocalRegs a, UserOfLocalRegs a) => a -> CmmLive -> CmmLive
+gen_kill a = gen a . kill a
+
+-- | The transfer function
 xferLive :: BwdTransfer CmmNode CmmLive
 xferLive = mkBTransfer3 fst mid lst
   where fst _ f = f
         mid :: CmmNode O O -> CmmLive -> CmmLive
         mid n f = gen_kill n f
         lst :: CmmNode O C -> FactBase CmmLive -> CmmLive
-        lst n f = gen_kill n $ case n of CmmCall {}            -> emptyRegSet
-                                         CmmForeignCall {}     -> emptyRegSet
-                                         _                     -> joinOutFacts liveLattice n f
+        lst n f = gen_kill n
+                $ case n of CmmCall{}        -> emptyRegSet
+                            CmmForeignCall{} -> emptyRegSet
+                            _                -> joinOutFacts liveLattice n f
index a63413c..7cfece4 100644 (file)
@@ -108,6 +108,7 @@ cpsTop hsc_env (CmmProc h@(TopInfo {stack_info=StackInfo {arg_space=entry_off}})
 
        ----------- Eliminate dead assignments -------------------
        -- Remove redundant reloads (and any other redundant asst)
+       --   in CmmSpillReloads
        g <- runOptimization $ removeDeadAssignmentsAndReloads procPoints g
        dump Opt_D_dump_cmmz_dead "Post Dead Assignment Elimination" g
 
index 1dbfbb0..e3f631d 100644 (file)
@@ -9,11 +9,7 @@
 #endif
 
 module CmmSpillReload
-  ( DualLive(..)
-  , dualLiveLattice, dualLiveTransfers, dualLiveness
-  --, insertSpillsAndReloads  --- XXX todo check live-in at entry against formals
-  , dualLivenessWithInsertion
-
+  ( dualLivenessWithInsertion
   , removeDeadAssignmentsAndReloads
   )
 where
@@ -35,21 +31,22 @@ import Prelude hiding (succ, zip)
 
 {- Note [Overview of spill/reload]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The point of this module is to insert spills and reloads to
-establish the invariant that at a call (or at any proc point with
-an established protocol) all live variables not expected in
-registers are sitting on the stack.  We use a backward analysis to
-insert spills and reloads.  It should be followed by a
-forward transformation to sink reloads as deeply as possible, so as
-to reduce register pressure.
+The point of this module is to insert spills and reloads to establish
+the invariant that at a call or any proc point with an established
+protocol all live variables not expected in registers are sitting on the
+stack.  We use a backward dual liveness analysis (both traditional
+register liveness as well as register slot liveness on the stack) to
+insert spills and reloads.  It should be followed by a forward
+transformation to sink reloads as deeply as possible, so as to reduce
+register pressure: this transformation is performed by
+CmmRewriteAssignments.
 
 A variable can be expected to be live in a register, live on the
 stack, or both.  This analysis ensures that spills and reloads are
 inserted as needed to make sure that every live variable needed
-after a call is available on the stack.  Spills are pushed back to
-their reaching definitions, but reloads are dropped immediately after
-we return from a call and will have to be sunk by a later forward
-transformation.
+after a call is available on the stack.  Spills are placed immediately
+after their reaching definitions, but reloads are placed immediately
+after a return from a call (the entry point.)
 
 Note that we offer no guarantees about the consistency of the value
 in memory and the value in the register, except that they are
@@ -89,19 +86,26 @@ dualLivenessWithInsertion procPoints g =
                                                 (dualLiveTransfers (g_entry g) procPoints)
                                                 (insertSpillAndReloadRewrites g procPoints)
 
-dualLiveness :: BlockSet -> CmmGraph -> FuelUniqSM (BlockEnv DualLive)
-dualLiveness procPoints g =
+_dualLiveness :: BlockSet -> CmmGraph -> FuelUniqSM (BlockEnv DualLive)
+_dualLiveness procPoints g =
   liftM snd $ dataflowPassBwd g [] $ analBwd dualLiveLattice $ dualLiveTransfers (g_entry g) procPoints
 
+-- Note [Live registers on entry to procpoints]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Remember that the transfer function is only ever run on the rewritten
+-- version of a graph, and the rewrite function for spills and reloads
+-- enforces the invariant that no local registers are live on entry to
+-- a procpoint.  Accordingly, we check for this invariant here.  An old
+-- version of this code incorrectly claimed that any live registers were
+-- live on the stack before entering the function: this is wrong, but
+-- didn't cause bugs because it never actually was invoked.
+
 dualLiveTransfers :: BlockId -> BlockSet -> (BwdTransfer CmmNode DualLive)
 dualLiveTransfers entry procPoints = mkBTransfer3 first middle last
     where first :: CmmNode C O -> DualLive -> DualLive
-          first (CmmEntry id) live = check live id $  -- live at procPoint => spill
-            if id /= entry && setMember id procPoints
-               then DualLive { on_stack = on_stack live `plusRegSet` in_regs live
-                             , in_regs  = emptyRegSet }
-               else live
-            where check live id x = if id == entry then noLiveOnEntry id (in_regs live) x else x
+          first (CmmEntry id) live -- See Note [Live registers on entry to procpoints]
+            | id == entry || setMember id procPoints = noLiveOnEntry id (in_regs live) live
+            | otherwise                              = live
 
           middle :: CmmNode O O -> DualLive -> DualLive
           middle m = changeStack updSlots
@@ -114,9 +118,13 @@ dualLiveTransfers entry procPoints = mkBTransfer3 first middle last
                   spill  live _ = live
                   reload live s@(RegSlot r, _, _) = check s $ extendRegSet live r
                   reload live _ = live
+                  -- Ensure the assignment refers to the entirety of the
+                  -- register slot (and not just a slice).
                   check (RegSlot (LocalReg _ ty), o, w) x
                      | o == w && w == widthInBytes (typeWidth ty) = x
                   check _ _ = panic "middleDualLiveness unsupported: slices"
+
+          -- Differences from vanilla liveness analysis
           last :: CmmNode O C -> FactBase DualLive -> DualLive
           last l fb = case l of
             CmmBranch id                   -> lkp id
@@ -184,6 +192,8 @@ spill, reload :: LocalReg -> CmmNode O O
 spill  r = CmmStore  (regSlot r) (CmmReg $ CmmLocal r)
 reload r = CmmAssign (CmmLocal r) (CmmLoad (regSlot r) $ localRegType r)
 
+-- XXX: This should be done with generic liveness analysis and moved to
+-- its own module
 removeDeadAssignmentsAndReloads :: BlockSet -> CmmGraph -> FuelUniqSM CmmGraph
 removeDeadAssignmentsAndReloads procPoints g =
    liftM fst $ dataflowPassBwd g [] $ analRewBwd dualLiveLattice
index 4a87911..f35e72d 100644 (file)
@@ -12,9 +12,6 @@ More notes (June 11)
   insertLateReloads is now gone, every reload is reloading a live variable.\r
   Test and nuke.\r
 \r
-* Sink and inline S(RegSlot(x)) = e in precisely the same way that we\r
-  sink and inline x = e\r
-\r
 * Stack layout is very like register assignment: find non-conflicting assigments.\r
   In particular we can use colouring or linear scan (etc).\r
 \r
@@ -103,6 +100,8 @@ Things to do:
    dichotomy. Mostly this means global replace, but we also need to make\r
    Label an instance of Outputable (probably in the Outputable module).\r
 \r
+   EZY: We should use Label, since that's the terminology Hoopl uses.\r
+\r
  - NB that CmmProcPoint line 283 has a hack that works around a GADT-related\r
    bug in 6.10.\r
 \r