module Compiler.Hoopl.Util
( gUnitOO, gUnitOC, gUnitCO, gUnitCC
, graphMapBlocks
, zblockGraph
, postorder_dfs, postorder_dfs_from, postorder_dfs_from_except
, preorder_dfs, preorder_dfs_from_except
, labelsDefined, labelsUsed, externalEntryLabels
, LabelsPtr(..)
)
where
import Control.Monad
import Compiler.Hoopl.Graph
import Compiler.Hoopl.Label
import Compiler.Hoopl.Zipper
gUnitOO :: block n O O -> Graph' block n O O
gUnitOC :: block n O C -> Graph' block n O C
gUnitCO :: block n C O -> Graph' block n C O
gUnitCC :: block n C C -> Graph' block n C C
gUnitOO b = GUnit b
gUnitOC b = GMany (JustO b) BodyEmpty NothingO
gUnitCO b = GMany NothingO BodyEmpty (JustO b)
gUnitCC b = GMany NothingO (BodyUnit b) NothingO
zblockGraph :: ZBlock n e x -> ZGraph n e x
zblockGraph b@(ZFirst {}) = gUnitCO b
zblockGraph b@(ZMiddle {}) = gUnitOO b
zblockGraph b@(ZLast {}) = gUnitOC b
zblockGraph b@(ZCat {}) = gUnitOO b
zblockGraph b@(ZHead {}) = gUnitCO b
zblockGraph b@(ZTail {}) = gUnitOC b
zblockGraph b@(ZClosed {}) = gUnitCC b
graphMapBlocks :: forall block n block' n' e x .
(forall e x . block n e x -> block' n' e x)
-> (Graph' block n e x -> Graph' block' n' e x)
bodyMapBlocks :: forall block n block' n' .
(block n C C -> block' n' C C)
-> (Body' block n -> Body' block' n')
graphMapBlocks f = map
where map :: Graph' block n e x -> Graph' block' n' e x
map GNil = GNil
map (GUnit b) = GUnit (f b)
map (GMany e b x) = GMany (fmap f e) (bodyMapBlocks f b) (fmap f x)
bodyMapBlocks f = map
where map BodyEmpty = BodyEmpty
map (BodyUnit b) = BodyUnit (f b)
map (BodyCat b1 b2) = BodyCat (map b1) (map b2)
class LabelsPtr l where
targetLabels :: l -> [Label]
instance Edges n => LabelsPtr (n e C) where
targetLabels n = successors n
instance LabelsPtr Label where
targetLabels l = [l]
instance LabelsPtr LabelSet where
targetLabels = labelSetElems
instance LabelsPtr l => LabelsPtr [l] where
targetLabels = concatMap targetLabels
postorder_dfs :: Edges (block n) => Graph' block n O x -> [block n C C]
preorder_dfs :: Edges (block n) => Graph' block n O x -> [block n C C]
graphDfs :: (Edges (block n))
=> (LabelMap (block n C C) -> block n O C -> LabelSet -> [block n C C])
-> (Graph' block n O x -> [block n C C])
graphDfs _ (GNil) = []
graphDfs _ (GUnit{}) = []
graphDfs order (GMany (JustO entry) body _) = order blockenv entry emptyLabelSet
where blockenv = bodyMap body
postorder_dfs = graphDfs postorder_dfs_from_except
preorder_dfs = graphDfs preorder_dfs_from_except
postorder_dfs_from_except :: forall block e . (Edges block, LabelsPtr e)
=> LabelMap (block C C) -> e -> LabelSet -> [block C C]
postorder_dfs_from_except blocks b visited =
vchildren (get_children b) (\acc _visited -> acc) [] visited
where
vnode :: block C C -> ([block C C] -> LabelSet -> a) -> [block C C] -> LabelSet -> a
vnode block cont acc visited =
if elemLabelSet id visited then
cont acc visited
else
let cont' acc visited = cont (block:acc) visited in
vchildren (get_children block) cont' acc (extendLabelSet visited id)
where id = entryLabel block
vchildren bs cont acc visited = next bs acc visited
where next children acc visited =
case children of [] -> cont acc visited
(b:bs) -> vnode b (next bs) acc visited
get_children block = foldr add_id [] $ targetLabels block
add_id id rst = case lookupFact blocks id of
Just b -> b : rst
Nothing -> rst
postorder_dfs_from
:: (Edges block, LabelsPtr b) => LabelMap (block C C) -> b -> [block C C]
postorder_dfs_from blocks b = postorder_dfs_from_except blocks b emptyLabelSet
data VM a = VM { unVM :: LabelSet -> (a, LabelSet) }
marked :: Label -> VM Bool
mark :: Label -> VM ()
instance Monad VM where
return a = VM $ \visited -> (a, visited)
m >>= k = VM $ \visited -> let (a, v') = unVM m visited in unVM (k a) v'
marked l = VM $ \v -> (elemLabelSet l v, v)
mark l = VM $ \v -> ((), extendLabelSet v l)
preorder_dfs_from_except :: forall block e . (Edges block, LabelsPtr e)
=> LabelMap (block C C) -> e -> LabelSet -> [block C C]
preorder_dfs_from_except blocks b visited =
(fst $ unVM (children (get_children b)) visited) []
where children [] = return id
children (b:bs) = liftM2 (.) (visit b) (children bs)
visit :: block C C -> VM (HL (block C C))
visit b = do already <- marked (entryLabel b)
if already then return id
else do mark (entryLabel b)
bs <- children $ get_children b
return $ b `cons` bs
get_children block = foldr add_id [] $ targetLabels block
add_id id rst = case lookupFact blocks id of
Just b -> b : rst
Nothing -> rst
type HL a = [a] -> [a]
cons :: a -> HL a -> HL a
cons a as tail = a : as tail
labelsDefined :: forall block n e x . Edges (block n) => Graph' block n e x -> LabelSet
labelsDefined GNil = emptyLabelSet
labelsDefined (GUnit{}) = emptyLabelSet
labelsDefined (GMany _ body x) = foldBodyBlocks addEntry body $ exitLabel x
where addEntry block labels = extendLabelSet labels (entryLabel block)
exitLabel :: MaybeO x (block n C O) -> LabelSet
exitLabel NothingO = emptyLabelSet
exitLabel (JustO b) = mkLabelSet [entryLabel b]
labelsUsed :: forall block n e x. Edges (block n) => Graph' block n e x -> LabelSet
labelsUsed GNil = emptyLabelSet
labelsUsed (GUnit{}) = emptyLabelSet
labelsUsed (GMany e body _) = foldBodyBlocks addTargets body $ entryTargets e
where addTargets block labels = foldl extendLabelSet labels (successors block)
entryTargets :: MaybeO e (block n O C) -> LabelSet
entryTargets NothingO = emptyLabelSet
entryTargets (JustO b) = addTargets b emptyLabelSet
foldBodyBlocks :: (block n C C -> a -> a) -> Body' block n -> a -> a
foldBodyBlocks _ BodyEmpty = id
foldBodyBlocks f (BodyUnit b) = f b
foldBodyBlocks f (BodyCat b b') = foldBodyBlocks f b . foldBodyBlocks f b'
externalEntryLabels :: Edges (block n) => Body' block n -> LabelSet
externalEntryLabels body = defined `minusLabelSet` used
where defined = labelsDefined g
used = labelsUsed g
g = GMany NothingO body NothingO