adding new files to do with new cmm functionality
[ghc-hetmet.git] / compiler / cmm / DFMonad.hs
diff --git a/compiler/cmm/DFMonad.hs b/compiler/cmm/DFMonad.hs
new file mode 100644 (file)
index 0000000..789b401
--- /dev/null
@@ -0,0 +1,287 @@
+{-# OPTIONS -Wall -fno-warn-name-shadowing #-}
+module DFMonad
+    ( Txlimit
+    , DFTx, runDFTx, lastTxPass, txDecrement, txRemaining, txExhausted
+
+    , DataflowLattice(..)
+    , DataflowAnalysis
+    , markFactsUnchanged, factsStatus, getFact, setFact, botFact
+                        , forgetFact, allFacts, factsEnv, checkFactMatch
+    , addLastOutFact, lastOutFacts, forgetLastOutFacts
+    , subAnalysis
+
+    , DFA, runDFA
+    , DFM, runDFM, liftTx, liftAnal
+    , markGraphRewritten
+    , freshBlockId
+    , liftUSM
+    )
+where
+
+import CmmTx
+import Control.Monad
+import Maybes
+import PprCmm()
+import UniqFM
+import UniqSupply
+import ZipCfg hiding (freshBlockId)
+import qualified ZipCfg as G
+
+import Outputable
+
+{-
+
+A dataflow monad maintains a mapping from BlockIds to dataflow facts,
+where a dataflow fact is a value of type [[a]].  Values of type [[a]]
+must form a lattice, as described by type [[Fact a]].
+
+The dataflow engine uses the lattice structure to compute a least
+solution to a set of dataflow equations.  To compute a greatest
+solution, flip the lattice over.
+
+The engine works by starting at the bottom and iterating to a fixed
+point, so in principle we require the bottom element, a join (least
+upper bound) operation, and a comparison to find out if a value has
+changed (grown).  In practice, the comparison is only ever used in
+conjunction with the join, so we have [[fact_add_to]]:
+
+  fact_add_to new old =
+     let j = join new old in
+     if j <= old then noTx old -- nothing changed
+     else aTx j                -- the fact changed
+
+-}
+
+data DataflowLattice a = DataflowLattice  { 
+  fact_name    :: String,                 -- documentation
+  fact_bot     :: a,                      -- lattice bottom element
+  fact_add_to  :: a -> a -> TxRes a,      -- lattice join and compare
+    -- ^ compute join of two args; something changed iff join is greater than 2nd arg
+  fact_do_logging :: Bool  -- log changes
+}
+
+
+-- There are three monads here:
+--   1. DFTx, the monad of transactions, to be carried through all
+--      graph-changing computations in the program
+--   2. DFA, the monad of analysis, which never changes anything
+--   3. DFM, the monad of combined analysis and transformation,
+--      which needs a UniqSupply and may consume transactions
+
+data DFAState f = DFAState { df_facts :: BlockEnv f
+                           , df_facts_change :: ChangeFlag
+                           }
+
+data DFTxState = DFTxState { df_txlimit :: Txlimit, df_lastpass :: String }
+
+data DFState f = DFState { df_uniqs :: UniqSupply
+                         , df_rewritten :: ChangeFlag
+                         , df_astate :: DFAState f
+                         , df_txstate :: DFTxState
+                         , df_last_outs :: [(BlockId, f)]
+                         }
+
+newtype DFTx a     = DFTx (DFTxState     -> (a, DFTxState))
+newtype DFA fact a = DFA  (DataflowLattice fact -> DFAState fact -> (a, DFAState fact))
+newtype DFM fact a = DFM  (DataflowLattice fact -> DFState  fact -> (a, DFState  fact))
+
+
+liftAnal :: DFA f a -> DFM f a
+liftAnal (DFA f) = DFM f'
+    where f' l s = let (a, anal) = f l (df_astate s)
+                   in  (a, s {df_astate = anal})
+
+liftTx :: DFTx a -> DFM f a
+liftTx (DFTx f) = DFM f'
+    where f' _ s = let (a, txs) = f (df_txstate s)
+                   in  (a, s {df_txstate = txs})
+
+newtype Txlimit = Txlimit Int
+  deriving (Ord, Eq, Num, Show, Bounded)
+
+initDFAState :: DFAState f
+initDFAState = DFAState emptyBlockEnv NoChange
+
+runDFA :: DataflowLattice f -> DFA f a -> a
+runDFA lattice (DFA f) = fst $ f lattice initDFAState
+
+-- XXX DFTx really needs to be in IO, so we can dump programs in
+-- intermediate states of optimization ---NR
+
+runDFTx :: Txlimit -> DFTx a -> a  --- should only be called once per program!
+runDFTx lim (DFTx f) = fst $ f $ DFTxState lim "<none>"
+
+lastTxPass :: DFTx String
+lastTxPass = DFTx f
+    where f s = (df_lastpass s, s)
+
+runDFM :: UniqSupply -> DataflowLattice f -> DFM f a -> DFTx a
+runDFM uniqs lattice (DFM f) = DFTx f'
+    where f' txs =
+            let (a, s) = f lattice $ DFState uniqs NoChange initDFAState txs [] in
+            (a, df_txstate s)
+
+txExhausted :: DFTx Bool
+txExhausted = DFTx f
+    where f s = (df_txlimit s <= 0, s)
+
+txRemaining :: DFTx Txlimit
+txRemaining = DFTx f
+    where f s = (df_txlimit s, s)
+
+txDecrement :: String -> Txlimit -> Txlimit -> DFTx ()
+txDecrement optimizer old new = DFTx f
+    where f s = ((), s { df_txlimit = lim s, df_lastpass = optimizer })
+          lim s = if old == df_txlimit s then new
+                  else panic $ concat ["lost track of ", optimizer, "'s transactions"]
+
+
+class DataflowAnalysis m where
+  markFactsUnchanged :: m f ()   -- ^ Useful for starting a new iteration
+  factsStatus :: m f ChangeFlag
+  subAnalysis :: m f a -> m f a  -- ^ Do a new analysis and then throw away
+                                 -- *all* the related state.  Even the Uniques
+                                 -- will be reused.
+
+  getFact :: BlockId -> m f f
+  setFact :: Outputable f => BlockId -> f -> m f ()
+  checkFactMatch :: Outputable f =>
+                    BlockId -> f -> m f () -- ^ assert fact already at this val
+  botFact :: m f f
+  forgetFact :: BlockId -> m f ()
+  forgetLastOutFacts :: m f ()
+  allFacts :: m f (BlockEnv f)
+  factsEnv :: Monad (m f) => m f (BlockId -> f)
+
+  lattice :: m f (DataflowLattice f)
+  factsEnv = do { map <- allFacts
+                ; bot <- botFact
+                ; return $ \id -> lookupBlockEnv map id `orElse` bot }
+
+instance DataflowAnalysis DFA where
+  markFactsUnchanged = DFA f
+    where f _ s = ((), s {df_facts_change = NoChange}) 
+  factsStatus = DFA f'
+    where f' _ s = (df_facts_change s, s)
+  subAnalysis (DFA f) = DFA f'
+    where f' l s = let (a, _) = f l (subAnalysisState s) in (a, s)
+  getFact id = DFA get
+    where get lattice s = (lookupBlockEnv (df_facts s) id `orElse` fact_bot lattice, s)
+  setFact id a =
+    do old <- getFact id
+       DataflowLattice { fact_add_to = add_fact
+                       , fact_name = name, fact_do_logging = log } <- lattice
+       case add_fact a old of
+         TxRes NoChange _ -> return ()
+         TxRes SomeChange join -> DFA $ \_ s ->
+             let facts' = extendBlockEnv (df_facts s) id join
+                 debug = if log then pprTrace else \_ _ a -> a
+             in  debug name (pprSetFact id old a join) $
+                 ((), s { df_facts = facts', df_facts_change = SomeChange })
+  botFact = DFA f
+    where f lattice s = (fact_bot lattice, s)
+  forgetFact id = DFA f 
+    where f _ s = ((), s { df_facts = delFromUFM (df_facts s) id })
+  forgetLastOutFacts = return ()
+  allFacts = DFA f
+    where f _ s = (df_facts s, s)
+  checkFactMatch id a =
+      do { fact <- lattice
+         ; old_a <- getFact id
+         ; case fact_add_to fact a old_a of
+             TxRes NoChange _ -> return ()
+             TxRes SomeChange new ->
+               do { facts <- allFacts
+                  ; pprPanic "checkFactMatch"
+                            (f4sep [text (fact_name fact), text "at id" <+> ppr id,
+                                    text "changed from", nest 4 (ppr old_a), text "to",
+                                    nest 4 (ppr new),
+                                    text "after supposedly reaching fixed point;",
+                                    text "env is", pprFacts facts]) 
+                  ; setFact id a }
+         }
+    where pprFacts env = vcat (map pprFact (ufmToList env))
+          pprFact (id, a) = hang (ppr id <> colon) 4 (ppr a)
+
+  lattice = DFA f
+    where f l s = (l, s)
+
+subAnalysisState :: DFAState f -> DFAState f
+subAnalysisState s = s {df_facts_change = NoChange}
+
+
+instance DataflowAnalysis DFM where
+  markFactsUnchanged  = liftAnal $ markFactsUnchanged
+  factsStatus         = liftAnal $ factsStatus
+  subAnalysis         = dfmSubAnalysis
+  getFact id          = liftAnal $ getFact id
+  setFact id new      = liftAnal $ setFact id new
+  botFact             = liftAnal $ botFact
+  forgetFact id       = liftAnal $ forgetFact id
+  forgetLastOutFacts  = dfmForgetLastOutFacts
+  allFacts            = liftAnal $ allFacts
+  checkFactMatch id a = liftAnal $ checkFactMatch id a
+
+  lattice             = liftAnal $ lattice
+
+dfmSubAnalysis :: DFM f a -> DFM f a
+dfmSubAnalysis (DFM f) = DFM f'
+    where f' l s = let s' = s { df_astate = subAnalysisState (df_astate s) }
+                       (a, _) = f l s'
+                   in  (a, s)
+
+dfmForgetLastOutFacts :: DFM f ()
+dfmForgetLastOutFacts = DFM f
+    where f _ s = ((), s { df_last_outs = [] })
+
+addLastOutFact :: (BlockId, f) -> DFM f ()
+addLastOutFact pair = DFM f
+    where f _ s = ((), s { df_last_outs = pair : df_last_outs s })
+
+lastOutFacts :: DFM f [(BlockId, f)]
+lastOutFacts = DFM f
+    where f _ s = (df_last_outs s, s)
+
+markGraphRewritten :: DFM f ()
+markGraphRewritten = DFM f
+    where f _ s = ((), s {df_rewritten = SomeChange})
+
+freshBlockId :: String -> DFM f BlockId
+freshBlockId s = liftUSM $ G.freshBlockId s
+
+liftUSM :: UniqSM a -> DFM f a
+liftUSM uc = DFM f
+    where f _ s = let (a, us') = initUs (df_uniqs s) uc
+                  in (a, s {df_uniqs = us'})
+
+instance Monad (DFA f) where
+  DFA f >>= k = DFA (\l s -> let (a, s') = f l s
+                                 DFA f' = k a
+                             in  f' l s')
+  return a = DFA (\_ s -> (a, s))
+
+instance Monad (DFM f) where
+  DFM f >>= k = DFM (\l s -> let (a, s') = f l s
+                                 DFM f' = k a
+                             in  f' l s')
+  return a = DFM (\_ s -> (a, s))
+
+instance Monad (DFTx) where
+  DFTx f >>= k = DFTx (\s -> let (a, s') = f s
+                                 DFTx f' = k a
+                             in  f' s')
+  return a = DFTx (\s -> (a, s))
+
+pprSetFact :: Outputable f => BlockId -> f -> f -> f -> SDoc
+pprSetFact id old a join =
+    f4sep [text "at" <+> text (show id),
+           text "added" <+> ppr a, text "to" <+> ppr old,
+           text "yielding" <+> ppr join]
+
+f4sep :: [SDoc] -> SDoc
+f4sep [] = fsep []
+f4sep (d:ds) = fsep (d : map (nest 4) ds)
+
+
+_I_am_abstract :: Int -> Txlimit
+_I_am_abstract = Txlimit -- prevents a warning about Txlimit being unused