{-# LANGUAGE CPP, GADTs, RankNTypes, ScopedTypeVariables, TypeFamilies #-} #if __GLASGOW_HASKELL__ >= 709 {-# LANGUAGE Safe #-} #elif __GLASGOW_HASKELL__ >= 701 {-# LANGUAGE Trustworthy #-} #endif -- | Utilities for clients of Hoopl, not used internally. module Compiler.Hoopl.XUtil ( -- * Utilities for clients firstXfer, distributeXfer , distributeFact, distributeFactBwd , successorFacts , joinFacts , joinOutFacts -- deprecated , joinMaps , analyzeAndRewriteFwdBody, analyzeAndRewriteBwdBody , analyzeAndRewriteFwdOx, analyzeAndRewriteBwdOx ) where import qualified Data.Map as M import Data.Maybe import Compiler.Hoopl.Collections import Compiler.Hoopl.Checkpoint import Compiler.Hoopl.Dataflow import Compiler.Hoopl.Block import Compiler.Hoopl.Graph import Compiler.Hoopl.Label ----------------------------------------------------------------------------- -- | Forward dataflow analysis and rewriting for the special case of a Body. -- A set of entry points must be supplied; blocks not reachable from -- the set are thrown away. analyzeAndRewriteFwdBody :: forall m n f entries. (CheckpointMonad m, NonLocal n, LabelsPtr entries) => FwdPass m n f -> entries -> Body n -> FactBase f -> m (Body n, FactBase f) -- | Backward dataflow analysis and rewriting for the special case of a Body. -- A set of entry points must be supplied; blocks not reachable from -- the set are thrown away. analyzeAndRewriteBwdBody :: forall m n f entries. (CheckpointMonad m, NonLocal n, LabelsPtr entries) => BwdPass m n f -> entries -> Body n -> FactBase f -> m (Body n, FactBase f) analyzeAndRewriteFwdBody pass en = mapBodyFacts (analyzeAndRewriteFwd pass (JustC en)) analyzeAndRewriteBwdBody pass en = mapBodyFacts (analyzeAndRewriteBwd pass (JustC en)) mapBodyFacts :: (Monad m) => (Graph n C C -> Fact C f -> m (Graph n C C, Fact C f, MaybeO C f)) -> (Body n -> FactBase f -> m (Body n, FactBase f)) -- ^ Internal utility; should not escape mapBodyFacts anal b f = anal (GMany NothingO b NothingO) f >>= bodyFacts where -- the type constraint is needed for the pattern match; -- if it were not, we would use do-notation here. bodyFacts :: Monad m => (Graph n C C, Fact C f, MaybeO C f) -> m (Body n, Fact C f) bodyFacts (GMany NothingO body NothingO, fb, NothingO) = return (body, fb) {- Can't write: do (GMany NothingO body NothingO, fb, NothingO) <- anal (....) f return (body, fb) because we need an explicit type signature in order to do the GADT pattern matches on NothingO -} -- | Forward dataflow analysis and rewriting for the special case of a -- graph open at the entry. This special case relieves the client -- from having to specify a type signature for 'NothingO', which beginners -- might find confusing and experts might find annoying. analyzeAndRewriteFwdOx :: forall m n f x. (CheckpointMonad m, NonLocal n) => FwdPass m n f -> Graph n O x -> f -> m (Graph n O x, FactBase f, MaybeO x f) -- | Backward dataflow analysis and rewriting for the special case of a -- graph open at the entry. This special case relieves the client -- from having to specify a type signature for 'NothingO', which beginners -- might find confusing and experts might find annoying. analyzeAndRewriteBwdOx :: forall m n f x. (CheckpointMonad m, NonLocal n) => BwdPass m n f -> Graph n O x -> Fact x f -> m (Graph n O x, FactBase f, f) -- | A value that can be used for the entry point of a graph open at the entry. noEntries :: MaybeC O Label noEntries = NothingC analyzeAndRewriteFwdOx pass g f = analyzeAndRewriteFwd pass noEntries g f analyzeAndRewriteBwdOx pass g fb = analyzeAndRewriteBwd pass noEntries g fb >>= strip where strip :: forall m a b c . Monad m => (a, b, MaybeO O c) -> m (a, b, c) strip (a, b, JustO c) = return (a, b, c) -- | A utility function so that a transfer function for a first -- node can be given just a fact; we handle the lookup. This -- function is planned to be made obsolete by changes in the dataflow -- interface. firstXfer :: NonLocal n => (n C O -> f -> f) -> (n C O -> FactBase f -> f) firstXfer xfer n fb = xfer n $ fromJust $ lookupFact (entryLabel n) fb -- | This utility function handles a common case in which a transfer function -- produces a single fact out of a last node, which is then distributed -- over the outgoing edges. distributeXfer :: NonLocal n => DataflowLattice f -> (n O C -> f -> f) -> (n O C -> f -> FactBase f) distributeXfer lattice xfer n f = mkFactBase lattice [ (l, xfer n f) | l <- successors n ] -- | This utility function handles a common case in which a transfer function -- for a last node takes the incoming fact unchanged and simply distributes -- that fact over the outgoing edges. distributeFact :: NonLocal n => n O C -> f -> FactBase f distributeFact n f = mapFromList [ (l, f) | l <- successors n ] -- because the same fact goes out on every edge, -- there's no need for 'mkFactBase' here. -- | This utility function handles a common case in which a backward transfer -- function takes the incoming fact unchanged and tags it with the node's label. distributeFactBwd :: NonLocal n => n C O -> f -> FactBase f distributeFactBwd n f = mapSingleton (entryLabel n) f -- | List of (unlabelled) facts from the successors of a last node successorFacts :: NonLocal n => n O C -> FactBase f -> [f] successorFacts n fb = [ f | id <- successors n, let Just f = lookupFact id fb ] -- | Join a list of facts. joinFacts :: DataflowLattice f -> Label -> [f] -> f joinFacts lat inBlock = foldr extend (fact_bot lat) where extend new old = snd $ fact_join lat inBlock (OldFact old) (NewFact new) {-# DEPRECATED joinOutFacts "should be replaced by 'joinFacts lat l (successorFacts n f)'; as is, it uses the wrong Label" #-} joinOutFacts :: (NonLocal node) => DataflowLattice f -> node O C -> FactBase f -> f joinOutFacts lat n f = foldr join (fact_bot lat) facts where join (lbl, new) old = snd $ fact_join lat lbl (OldFact old) (NewFact new) facts = [(s, fromJust fact) | s <- successors n, let fact = lookupFact s f, isJust fact] -- | It's common to represent dataflow facts as a map from variables -- to some fact about the locations. For these maps, the join -- operation on the map can be expressed in terms of the join on each -- element of the codomain: joinMaps :: Ord k => JoinFun v -> JoinFun (M.Map k v) joinMaps eltJoin l (OldFact old) (NewFact new) = M.foldrWithKey add (NoChange, old) new where add k new_v (ch, joinmap) = case M.lookup k joinmap of Nothing -> (SomeChange, M.insert k new_v joinmap) Just old_v -> case eltJoin l (OldFact old_v) (NewFact new_v) of (SomeChange, v') -> (SomeChange, M.insert k v' joinmap) (NoChange, _) -> (ch, joinmap)