summaryrefslogtreecommitdiff
path: root/compiler/utils
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/utils')
-rw-r--r--compiler/utils/GraphBase.hs111
-rw-r--r--compiler/utils/GraphColor.hs332
-rw-r--r--compiler/utils/GraphOps.hs581
-rw-r--r--compiler/utils/GraphPpr.hs169
4 files changed, 1193 insertions, 0 deletions
diff --git a/compiler/utils/GraphBase.hs b/compiler/utils/GraphBase.hs
new file mode 100644
index 0000000000..04eda96120
--- /dev/null
+++ b/compiler/utils/GraphBase.hs
@@ -0,0 +1,111 @@
+
+-- | Types for the general graph colorer.
+
+module GraphBase (
+ Triv,
+ Graph (..),
+ initGraph,
+ graphMapModify,
+
+ Node (..), newNode,
+)
+
+
+where
+
+import UniqSet
+import UniqFM
+
+
+-- | A fn to check if a node is trivially colorable
+-- For graphs who's color classes are disjoint then a node is 'trivially colorable'
+-- when it has less neighbors and exclusions than available colors for that node.
+--
+-- For graph's who's color classes overlap, ie some colors alias other colors, then
+-- this can be a bit more tricky. There is a general way to calculate this, but
+-- it's likely be too slow for use in the code. The coloring algorithm takes
+-- a canned function which can be optimised by the user to be specific to the
+-- specific graph being colored.
+--
+-- for details, see "A Generalised Algorithm for Graph-Coloring Register Allocation"
+-- Smith, Ramsey, Holloway - PLDI 2004.
+--
+type Triv k cls color
+ = cls -- ^ the class of the node we're trying to color.
+ -> UniqSet k -- ^ the node's neighbors.
+ -> UniqSet color -- ^ the node's exclusions.
+ -> Bool
+
+
+-- | The Interference graph.
+-- There used to be more fields, but they were turfed out in a previous revision.
+-- maybe we'll want more later..
+--
+data Graph k cls color
+ = Graph {
+ -- | All active nodes in the graph.
+ graphMap :: UniqFM (Node k cls color) }
+
+
+-- | An empty graph.
+initGraph :: Graph k cls color
+initGraph
+ = Graph
+ { graphMap = emptyUFM }
+
+
+-- | Modify the finite map holding the nodes in the graph.
+graphMapModify
+ :: (UniqFM (Node k cls color) -> UniqFM (Node k cls color))
+ -> Graph k cls color -> Graph k cls color
+
+graphMapModify f graph
+ = graph { graphMap = f (graphMap graph) }
+
+
+
+-- | Graph nodes.
+-- Represents a thing that can conflict with another thing.
+-- For the register allocater the nodes represent registers.
+--
+data Node k cls color
+ = Node {
+ -- | A unique identifier for this node.
+ nodeId :: k
+
+ -- | The class of this node,
+ -- determines the set of colors that can be used.
+ , nodeClass :: cls
+
+ -- | The color of this node, if any.
+ , nodeColor :: Maybe color
+
+ -- | Neighbors which must be colored differently to this node.
+ , nodeConflicts :: UniqSet k
+
+ -- | Colors that cannot be used by this node.
+ , nodeExclusions :: UniqSet color
+
+ -- | Colors that this node would prefer to be, in decending order.
+ , nodePreference :: [color]
+
+ -- | Neighbors that this node would like to be colored the same as.
+ , nodeCoalesce :: UniqSet k }
+
+
+-- | An empty node.
+newNode :: k -> cls -> Node k cls color
+newNode k cls
+ = Node
+ { nodeId = k
+ , nodeClass = cls
+ , nodeColor = Nothing
+ , nodeConflicts = emptyUniqSet
+ , nodeExclusions = emptyUniqSet
+ , nodePreference = []
+ , nodeCoalesce = emptyUniqSet }
+
+
+
+
+
diff --git a/compiler/utils/GraphColor.hs b/compiler/utils/GraphColor.hs
new file mode 100644
index 0000000000..307803a988
--- /dev/null
+++ b/compiler/utils/GraphColor.hs
@@ -0,0 +1,332 @@
+
+-- | Graph Coloring.
+-- This is a generic graph coloring library, abstracted over the type of
+-- the node keys, nodes and colors.
+--
+{-# OPTIONS -fno-warn-missing-signatures #-}
+
+module GraphColor (
+ module GraphBase,
+ module GraphOps,
+ module GraphPpr,
+ colorGraph
+)
+
+where
+
+import GraphBase
+import GraphOps
+import GraphPpr
+
+import Unique
+import UniqFM
+import UniqSet
+import Outputable
+
+import Data.Maybe
+import Data.List
+
+
+-- | Try to color a graph with this set of colors.
+-- Uses Chaitin's algorithm to color the graph.
+-- The graph is scanned for nodes which are deamed 'trivially colorable'. These nodes
+-- are pushed onto a stack and removed from the graph.
+-- Once this process is complete the graph can be colored by removing nodes from
+-- the stack (ie in reverse order) and assigning them colors different to their neighbors.
+--
+colorGraph
+ :: ( Uniquable k, Uniquable cls, Uniquable color
+ , Eq color, Eq cls, Ord k
+ , Outputable k, Outputable cls, Outputable color)
+ => Bool -- ^ whether to do iterative coalescing
+ -> UniqFM (UniqSet color) -- ^ map of (node class -> set of colors available for this class).
+ -> Triv k cls color -- ^ fn to decide whether a node is trivially colorable.
+ -> (Graph k cls color -> k) -- ^ fn to choose a node to potentially leave uncolored if nothing is trivially colorable.
+ -> Graph k cls color -- ^ the graph to color.
+
+ -> ( Graph k cls color -- the colored graph.
+ , UniqSet k -- the set of nodes that we couldn't find a color for.
+ , UniqFM k ) -- map of regs (r1 -> r2) that were coaleced
+ -- r1 should be replaced by r2 in the source
+
+colorGraph iterative colors triv spill graph0
+ = let
+ -- if we're not doing iterative coalescing, then just do a single coalescing
+ -- pass at the front. This won't be as good but should still eat up a
+ -- lot of the reg-reg moves.
+ (graph_coalesced, kksCoalesce1)
+ = if not iterative
+ then coalesceGraph False triv graph0
+ else (graph0, [])
+
+ -- run the scanner to slurp out all the trivially colorable nodes
+ -- (and do coalescing if iterative coalescing is enabled)
+ (ksTriv, ksProblems, kksCoalesce2)
+ = colorScan iterative triv spill graph_coalesced
+
+ -- If iterative coalescing is enabled, the scanner will coalesce the graph as does its business.
+ -- We need to apply all the coalescences found by the scanner to the original
+ -- graph before doing assignColors.
+ --
+ -- Because we've got the whole, non-pruned graph here we turn on aggressive coalecing
+ -- to force all the (conservative) coalescences found during scanning.
+ --
+ (graph_scan_coalesced, _)
+ = mapAccumL (coalesceNodes True triv) graph_coalesced kksCoalesce2
+
+ -- color the trivially colorable nodes
+ -- during scanning, keys of triv nodes were added to the front of the list as they were found
+ -- this colors them in the reverse order, as required by the algorithm.
+ (graph_triv, ksNoTriv)
+ = assignColors colors graph_scan_coalesced ksTriv
+
+ -- try and color the problem nodes
+ -- problem nodes are the ones that were left uncolored because they weren't triv.
+ -- theres a change we can color them here anyway.
+ (graph_prob, ksNoColor)
+ = assignColors colors graph_triv ksProblems
+
+ -- if the trivially colorable nodes didn't color then something is probably wrong
+ -- with the provided triv function.
+ --
+ in if not $ null ksNoTriv
+ then pprPanic "colorGraph: trivially colorable nodes didn't color!" empty
+{- ( empty
+ $$ text "ksTriv = " <> ppr ksTriv
+ $$ text "ksNoTriv = " <> ppr ksNoTriv
+ $$ empty
+ $$ dotGraph (\x -> text "white") triv graph1) -}
+
+ else ( graph_prob
+ , mkUniqSet ksNoColor -- the nodes that didn't color (spills)
+ , if iterative
+ then (listToUFM kksCoalesce2)
+ else (listToUFM kksCoalesce1))
+
+
+-- | Scan through the conflict graph separating out trivially colorable and
+-- potentially uncolorable (problem) nodes.
+--
+-- Checking whether a node is trivially colorable or not is a resonably expensive operation,
+-- so after a triv node is found and removed from the graph it's no good to return to the 'start'
+-- of the graph and recheck a bunch of nodes that will probably still be non-trivially colorable.
+--
+-- To ward against this, during each pass through the graph we collect up a list of triv nodes
+-- that were found, and only remove them once we've finished the pass. The more nodes we can delete
+-- at once the more likely it is that nodes we've already checked will become trivially colorable
+-- for the next pass.
+--
+-- TODO: add work lists to finding triv nodes is easier.
+-- If we've just scanned the graph, and removed triv nodes, then the only
+-- nodes that we need to rescan are the ones we've removed edges from.
+
+colorScan
+ :: ( Uniquable k, Uniquable cls, Uniquable color
+ , Ord k, Eq cls
+ , Outputable k, Outputable color)
+ => Bool -- ^ whether to do iterative coalescing
+ -> Triv k cls color -- ^ fn to decide whether a node is trivially colorable
+ -> (Graph k cls color -> k) -- ^ fn to choose a node to potentially leave uncolored if nothing is trivially colorable.
+ -> Graph k cls color -- ^ the graph to scan
+
+ -> ([k], [k], [(k, k)]) -- triv colorable nodes, problem nodes, pairs of nodes to coalesce
+
+colorScan iterative triv spill graph
+ = colorScan_spin iterative triv spill graph [] [] []
+
+colorScan_spin iterative triv spill graph
+ ksTriv ksSpill kksCoalesce
+
+ -- if the graph is empty then we're done
+ | isNullUFM $ graphMap graph
+ = (ksTriv, ksSpill, kksCoalesce)
+
+ -- Simplify:
+ -- Look for trivially colorable nodes.
+ -- If we can find some then remove them from the graph and go back for more.
+ --
+ | nsTrivFound@(_:_)
+ <- scanGraph (\node -> triv (nodeClass node) (nodeConflicts node) (nodeExclusions node)
+
+ -- for iterative coalescing we only want non-move related
+ -- nodes here
+ && (not iterative || isEmptyUniqSet (nodeCoalesce node)))
+ $ graph
+
+ , ksTrivFound <- map nodeId nsTrivFound
+ , graph3 <- foldr (\k g -> let Just g' = delNode k g
+ in g')
+ graph ksTrivFound
+
+ = colorScan_spin iterative triv spill graph3
+ (ksTrivFound ++ ksTriv)
+ ksSpill
+ kksCoalesce
+
+ -- Coalesce:
+ -- If we're doing iterative coalescing and no triv nodes are avaliable
+ -- then it's type for a coalescing pass.
+ | iterative
+ = case coalesceGraph False triv graph of
+
+ -- we were able to coalesce something
+ -- go back and see if this frees up more nodes to be trivially colorable.
+ (graph2, kksCoalesceFound @(_:_))
+ -> colorScan_spin iterative triv spill graph2
+ ksTriv ksSpill (kksCoalesceFound ++ kksCoalesce)
+
+ -- Freeze:
+ -- nothing could be coalesced (or was triv),
+ -- time to choose a node to freeze and give up on ever coalescing it.
+ (graph2, [])
+ -> case freezeOneInGraph graph2 of
+
+ -- we were able to freeze something
+ -- hopefully this will free up something for Simplify
+ (graph3, True)
+ -> colorScan_spin iterative triv spill graph3
+ ksTriv ksSpill kksCoalesce
+
+ -- we couldn't find something to freeze either
+ -- time for a spill
+ (graph3, False)
+ -> colorScan_spill iterative triv spill graph3
+ ksTriv ksSpill kksCoalesce
+
+ -- spill time
+ | otherwise
+ = colorScan_spill iterative triv spill graph
+ ksTriv ksSpill kksCoalesce
+
+
+-- Select:
+-- we couldn't find any triv nodes or things to freeze or coalesce,
+-- and the graph isn't empty yet.. We'll have to choose a spill
+-- candidate and leave it uncolored.
+--
+colorScan_spill iterative triv spill graph
+ ksTriv ksSpill kksCoalesce
+
+ = let kSpill = spill graph
+ Just graph' = delNode kSpill graph
+ in colorScan_spin iterative triv spill graph'
+ ksTriv (kSpill : ksSpill) kksCoalesce
+
+
+-- | Try to assign a color to all these nodes.
+
+assignColors
+ :: ( Uniquable k, Uniquable cls, Uniquable color, Eq color )
+ => UniqFM (UniqSet color) -- ^ map of (node class -> set of colors available for this class).
+ -> Graph k cls color -- ^ the graph
+ -> [k] -- ^ nodes to assign a color to.
+ -> ( Graph k cls color -- the colored graph
+ , [k]) -- the nodes that didn't color.
+
+assignColors colors graph ks
+ = assignColors' colors graph [] ks
+
+ where assignColors' _ graph prob []
+ = (graph, prob)
+
+ assignColors' colors graph prob (k:ks)
+ = case assignColor colors k graph of
+
+ -- couldn't color this node
+ Nothing -> assignColors' colors graph (k : prob) ks
+
+ -- this node colored ok, so do the rest
+ Just graph' -> assignColors' colors graph' prob ks
+
+
+ assignColor colors u graph
+ | Just c <- selectColor colors graph u
+ = Just (setColor u c graph)
+
+ | otherwise
+ = Nothing
+
+
+
+-- | Select a color for a certain node
+-- taking into account preferences, neighbors and exclusions.
+-- returns Nothing if no color can be assigned to this node.
+--
+selectColor
+ :: ( Uniquable k, Uniquable cls, Uniquable color, Eq color)
+ => UniqFM (UniqSet color) -- ^ map of (node class -> set of colors available for this class).
+ -> Graph k cls color -- ^ the graph
+ -> k -- ^ key of the node to select a color for.
+ -> Maybe color
+
+selectColor colors graph u
+ = let -- lookup the node
+ Just node = lookupNode graph u
+
+ -- lookup the available colors for the class of this node.
+ Just colors_avail
+ = lookupUFM colors (nodeClass node)
+
+ -- find colors we can't use because they're already being used
+ -- by a node that conflicts with this one.
+ Just nsConflicts
+ = sequence
+ $ map (lookupNode graph)
+ $ uniqSetToList
+ $ nodeConflicts node
+
+ colors_conflict = mkUniqSet
+ $ catMaybes
+ $ map nodeColor nsConflicts
+
+ -- the prefs of our neighbors
+ colors_neighbor_prefs
+ = mkUniqSet
+ $ concat $ map nodePreference nsConflicts
+
+ -- colors that are still valid for us
+ colors_ok_ex = minusUniqSet colors_avail (nodeExclusions node)
+ colors_ok = minusUniqSet colors_ok_ex colors_conflict
+
+ -- the colors that we prefer, and are still ok
+ colors_ok_pref = intersectUniqSets
+ (mkUniqSet $ nodePreference node) colors_ok
+
+ -- the colors that we could choose while being nice to our neighbors
+ colors_ok_nice = minusUniqSet
+ colors_ok colors_neighbor_prefs
+
+ -- the best of all possible worlds..
+ colors_ok_pref_nice
+ = intersectUniqSets
+ colors_ok_nice colors_ok_pref
+
+ -- make the decision
+ chooseColor
+
+ -- everyone is happy, yay!
+ | not $ isEmptyUniqSet colors_ok_pref_nice
+ , c : _ <- filter (\x -> elementOfUniqSet x colors_ok_pref_nice)
+ (nodePreference node)
+ = Just c
+
+ -- we've got one of our preferences
+ | not $ isEmptyUniqSet colors_ok_pref
+ , c : _ <- filter (\x -> elementOfUniqSet x colors_ok_pref)
+ (nodePreference node)
+ = Just c
+
+ -- it wasn't a preference, but it was still ok
+ | not $ isEmptyUniqSet colors_ok
+ , c : _ <- uniqSetToList colors_ok
+ = Just c
+
+ -- no colors were available for us this time.
+ -- looks like we're going around the loop again..
+ | otherwise
+ = Nothing
+
+ in chooseColor
+
+
+
diff --git a/compiler/utils/GraphOps.hs b/compiler/utils/GraphOps.hs
new file mode 100644
index 0000000000..414abe4efc
--- /dev/null
+++ b/compiler/utils/GraphOps.hs
@@ -0,0 +1,581 @@
+-- | Basic operations on graphs.
+--
+-- TODO: refine coalescing crieteria
+
+{-# OPTIONS -fno-warn-missing-signatures #-}
+
+module GraphOps (
+ addNode, delNode, getNode, lookupNode, modNode,
+ size,
+ union,
+ addConflict, delConflict, addConflicts,
+ addCoalesce, delCoalesce,
+ addExclusion,
+ addPreference,
+ coalesceNodes, coalesceGraph,
+ freezeNode, freezeOneInGraph, freezeAllInGraph,
+ scanGraph,
+ setColor,
+ validateGraph,
+ slurpNodeConflictCount
+)
+where
+
+import GraphBase
+
+import Outputable
+import Unique
+import UniqSet
+import UniqFM
+
+import Data.List hiding (union)
+import Data.Maybe
+
+-- | Lookup a node from the graph.
+lookupNode
+ :: Uniquable k
+ => Graph k cls color
+ -> k -> Maybe (Node k cls color)
+
+lookupNode graph k
+ = lookupUFM (graphMap graph) k
+
+
+-- | Get a node from the graph, throwing an error if it's not there
+getNode
+ :: Uniquable k
+ => Graph k cls color
+ -> k -> Node k cls color
+
+getNode graph k
+ = case lookupUFM (graphMap graph) k of
+ Just node -> node
+ Nothing -> panic "ColorOps.getNode: not found"
+
+
+-- | Add a node to the graph, linking up its edges
+addNode :: Uniquable k
+ => k -> Node k cls color
+ -> Graph k cls color -> Graph k cls color
+
+addNode k node graph
+ = let
+ -- add back conflict edges from other nodes to this one
+ map_conflict
+ = foldUniqSet
+ (adjustUFM (\n -> n { nodeConflicts = addOneToUniqSet (nodeConflicts n) k}))
+ (graphMap graph)
+ (nodeConflicts node)
+
+ -- add back coalesce edges from other nodes to this one
+ map_coalesce
+ = foldUniqSet
+ (adjustUFM (\n -> n { nodeCoalesce = addOneToUniqSet (nodeCoalesce n) k}))
+ map_conflict
+ (nodeCoalesce node)
+
+ in graph
+ { graphMap = addToUFM map_coalesce k node}
+
+
+-- | Delete a node and all its edges from the graph.
+delNode :: (Uniquable k, Outputable k)
+ => k -> Graph k cls color -> Maybe (Graph k cls color)
+
+delNode k graph
+ | Just node <- lookupNode graph k
+ = let -- delete conflict edges from other nodes to this one.
+ graph1 = foldl' (\g k1 -> let Just g' = delConflict k1 k g in g') graph
+ $ uniqSetToList (nodeConflicts node)
+
+ -- delete coalesce edge from other nodes to this one.
+ graph2 = foldl' (\g k1 -> let Just g' = delCoalesce k1 k g in g') graph1
+ $ uniqSetToList (nodeCoalesce node)
+
+ -- delete the node
+ graph3 = graphMapModify (\fm -> delFromUFM fm k) graph2
+
+ in Just graph3
+
+ | otherwise
+ = Nothing
+
+
+-- | Modify a node in the graph.
+-- returns Nothing if the node isn't present.
+--
+modNode :: Uniquable k
+ => (Node k cls color -> Node k cls color)
+ -> k -> Graph k cls color -> Maybe (Graph k cls color)
+
+modNode f k graph
+ = case lookupNode graph k of
+ Just Node{}
+ -> Just
+ $ graphMapModify
+ (\fm -> let Just node = lookupUFM fm k
+ node' = f node
+ in addToUFM fm k node')
+ graph
+
+ Nothing -> Nothing
+
+
+-- | Get the size of the graph, O(n)
+size :: Uniquable k
+ => Graph k cls color -> Int
+
+size graph
+ = sizeUFM $ graphMap graph
+
+
+-- | Union two graphs together.
+union :: Uniquable k
+ => Graph k cls color -> Graph k cls color -> Graph k cls color
+
+union graph1 graph2
+ = Graph
+ { graphMap = plusUFM (graphMap graph1) (graphMap graph2) }
+
+
+-- | Add a conflict between nodes to the graph, creating the nodes required.
+-- Conflicts are virtual regs which need to be colored differently.
+addConflict
+ :: Uniquable k
+ => (k, cls) -> (k, cls)
+ -> Graph k cls color -> Graph k cls color
+
+addConflict (u1, c1) (u2, c2)
+ = let addNeighbor u c u'
+ = adjustWithDefaultUFM
+ (\node -> node { nodeConflicts = addOneToUniqSet (nodeConflicts node) u' })
+ (newNode u c) { nodeConflicts = unitUniqSet u' }
+ u
+
+ in graphMapModify
+ ( addNeighbor u1 c1 u2
+ . addNeighbor u2 c2 u1)
+
+
+-- | Delete a conflict edge. k1 -> k2
+-- returns Nothing if the node isn't in the graph
+delConflict
+ :: Uniquable k
+ => k -> k
+ -> Graph k cls color -> Maybe (Graph k cls color)
+
+delConflict k1 k2
+ = modNode
+ (\node -> node { nodeConflicts = delOneFromUniqSet (nodeConflicts node) k2 })
+ k1
+
+
+-- | Add some conflicts to the graph, creating nodes if required.
+-- All the nodes in the set are taken to conflict with each other.
+addConflicts
+ :: Uniquable k
+ => UniqSet k -> (k -> cls)
+ -> Graph k cls color -> Graph k cls color
+
+addConflicts conflicts getClass
+
+ -- just a single node, but no conflicts, create the node anyway.
+ | (u : []) <- uniqSetToList conflicts
+ = graphMapModify
+ $ adjustWithDefaultUFM
+ id
+ (newNode u (getClass u))
+ u
+
+ | otherwise
+ = graphMapModify
+ $ (\fm -> foldl' (\g u -> addConflictSet1 u getClass conflicts g) fm
+ $ uniqSetToList conflicts)
+
+
+addConflictSet1 u getClass set
+ = case delOneFromUniqSet set u of
+ set' -> adjustWithDefaultUFM
+ (\node -> node { nodeConflicts = unionUniqSets set' (nodeConflicts node) } )
+ (newNode u (getClass u)) { nodeConflicts = set' }
+ u
+
+
+-- | Add an exclusion to the graph, creating nodes if required.
+-- These are extra colors that the node cannot use.
+addExclusion
+ :: (Uniquable k, Uniquable color)
+ => k -> (k -> cls) -> color
+ -> Graph k cls color -> Graph k cls color
+
+addExclusion u getClass color
+ = graphMapModify
+ $ adjustWithDefaultUFM
+ (\node -> node { nodeExclusions = addOneToUniqSet (nodeExclusions node) color })
+ (newNode u (getClass u)) { nodeExclusions = unitUniqSet color }
+ u
+
+
+-- | Add a coalescence edge to the graph, creating nodes if requried.
+-- It is considered adventageous to assign the same color to nodes in a coalesence.
+addCoalesce
+ :: Uniquable k
+ => (k, cls) -> (k, cls)
+ -> Graph k cls color -> Graph k cls color
+
+addCoalesce (u1, c1) (u2, c2)
+ = let addCoalesce u c u'
+ = adjustWithDefaultUFM
+ (\node -> node { nodeCoalesce = addOneToUniqSet (nodeCoalesce node) u' })
+ (newNode u c) { nodeCoalesce = unitUniqSet u' }
+ u
+
+ in graphMapModify
+ ( addCoalesce u1 c1 u2
+ . addCoalesce u2 c2 u1)
+
+
+-- | Delete a coalescence edge (k1 -> k2) from the graph.
+delCoalesce
+ :: Uniquable k
+ => k -> k
+ -> Graph k cls color -> Maybe (Graph k cls color)
+
+delCoalesce k1 k2
+ = modNode (\node -> node { nodeCoalesce = delOneFromUniqSet (nodeCoalesce node) k2 })
+ k1
+
+
+-- | Add a color preference to the graph, creating nodes if required.
+-- The most recently added preference is the most prefered.
+-- The algorithm tries to assign a node it's prefered color if possible.
+--
+addPreference
+ :: Uniquable k
+ => (k, cls) -> color
+ -> Graph k cls color -> Graph k cls color
+
+addPreference (u, c) color
+ = graphMapModify
+ $ adjustWithDefaultUFM
+ (\node -> node { nodePreference = color : (nodePreference node) })
+ (newNode u c) { nodePreference = [color] }
+ u
+
+
+-- | Do agressive coalescing on this graph.
+-- returns the new graph and the list of pairs of nodes that got coaleced together.
+-- for each pair, the resulting node will have the least key and be second in the pair.
+--
+coalesceGraph
+ :: (Uniquable k, Ord k, Eq cls, Outputable k)
+ => Bool -- ^ If True, coalesce nodes even if this might make the graph
+ -- less colorable (aggressive coalescing)
+ -> Triv k cls color
+ -> Graph k cls color
+ -> (Graph k cls color, [(k, k)])
+
+coalesceGraph aggressive triv graph
+ = coalesceGraph' aggressive triv graph []
+
+coalesceGraph' aggressive triv graph kkPairsAcc
+ = let
+ -- find all the nodes that have coalescence edges
+ cNodes = filter (\node -> not $ isEmptyUniqSet (nodeCoalesce node))
+ $ eltsUFM $ graphMap graph
+
+ -- build a list of pairs of keys for node's we'll try and coalesce
+ -- every pair of nodes will appear twice in this list
+ -- ie [(k1, k2), (k2, k1) ... ]
+ -- This is ok, GrapOps.coalesceNodes handles this and it's convenient for
+ -- build a list of what nodes get coalesced together for later on.
+ --
+ cList = [ (nodeId node1, k2)
+ | node1 <- cNodes
+ , k2 <- uniqSetToList $ nodeCoalesce node1 ]
+
+ -- do the coalescing, returning the new graph and a list of pairs of keys
+ -- that got coalesced together.
+ (graph', mPairs)
+ = mapAccumL (coalesceNodes aggressive triv) graph cList
+
+ -- keep running until there are no more coalesces can be found
+ in case catMaybes mPairs of
+ [] -> (graph', kkPairsAcc)
+ pairs -> coalesceGraph' aggressive triv graph' (pairs ++ kkPairsAcc)
+
+
+-- | Coalesce this pair of nodes unconditionally / agressively.
+-- The resulting node is the one with the least key.
+--
+-- returns: Just the pair of keys if the nodes were coalesced
+-- the second element of the pair being the least one
+--
+-- Nothing if either of the nodes weren't in the graph
+
+coalesceNodes
+ :: (Uniquable k, Ord k, Eq cls, Outputable k)
+ => Bool -- ^ If True, coalesce nodes even if this might make the graph
+ -- less colorable (aggressive coalescing)
+ -> Triv k cls color
+ -> Graph k cls color
+ -> (k, k) -- ^ keys of the nodes to be coalesced
+ -> (Graph k cls color, Maybe (k, k))
+
+coalesceNodes aggressive triv graph (k1, k2)
+ | (kMin, kMax) <- if k1 < k2
+ then (k1, k2)
+ else (k2, k1)
+
+ -- the nodes being coalesced must be in the graph
+ , Just nMin <- lookupNode graph kMin
+ , Just nMax <- lookupNode graph kMax
+
+ -- can't coalesce conflicting modes
+ , not $ elementOfUniqSet kMin (nodeConflicts nMax)
+ , not $ elementOfUniqSet kMax (nodeConflicts nMin)
+
+ = coalesceNodes_merge aggressive triv graph kMin kMax nMin nMax
+
+ -- don't do the coalescing after all
+ | otherwise
+ = (graph, Nothing)
+
+coalesceNodes_merge aggressive triv graph kMin kMax nMin nMax
+
+ -- sanity checks
+ | nodeClass nMin /= nodeClass nMax
+ = error "GraphOps.coalesceNodes: can't coalesce nodes of different classes."
+
+ | not (isNothing (nodeColor nMin) && isNothing (nodeColor nMax))
+ = error "GraphOps.coalesceNodes: can't coalesce colored nodes."
+
+ | nodeId nMin == nodeId nMax
+ = error "GraphOps.coalesceNodes: can't coalesce the same node."
+
+ ---
+ | otherwise
+ = let
+ -- the new node gets all the edges from its two components
+ node =
+ Node { nodeId = kMin
+ , nodeClass = nodeClass nMin
+ , nodeColor = Nothing
+
+ -- nodes don't conflict with themselves..
+ , nodeConflicts
+ = (unionUniqSets (nodeConflicts nMin) (nodeConflicts nMax))
+ `delOneFromUniqSet` kMin
+ `delOneFromUniqSet` kMax
+
+ , nodeExclusions = unionUniqSets (nodeExclusions nMin) (nodeExclusions nMax)
+ , nodePreference = nodePreference nMin ++ nodePreference nMax
+
+ -- nodes don't coalesce with themselves..
+ , nodeCoalesce
+ = (unionUniqSets (nodeCoalesce nMin) (nodeCoalesce nMax))
+ `delOneFromUniqSet` kMin
+ `delOneFromUniqSet` kMax
+ }
+
+ in coalesceNodes_check aggressive triv graph kMin kMax node
+
+coalesceNodes_check aggressive triv graph kMin kMax node
+
+ -- Unless we're coalescing aggressively, if the result node is not trivially
+ -- colorable then don't do the coalescing.
+ | not aggressive
+ , not $ triv (nodeClass node) (nodeConflicts node) (nodeExclusions node)
+ = (graph, Nothing)
+
+ | otherwise
+ = let -- delete the old nodes from the graph and add the new one
+ Just graph1 = delNode kMax graph
+ Just graph2 = delNode kMin graph1
+ graph3 = addNode kMin node graph2
+
+ in (graph3, Just (kMax, kMin))
+
+
+-- | Freeze a node
+-- This is for the iterative coalescer.
+-- By freezing a node we give up on ever coalescing it.
+-- Move all its coalesce edges into the frozen set - and update
+-- back edges from other nodes.
+--
+freezeNode
+ :: Uniquable k
+ => k -- ^ key of the node to freeze
+ -> Graph k cls color -- ^ the graph
+ -> Graph k cls color -- ^ graph with that node frozen
+
+freezeNode k
+ = graphMapModify
+ $ \fm ->
+ let
+ -- freeze all the edges in the node to be frozen
+ Just node = lookupUFM fm k
+ node' = node
+ { nodeCoalesce = emptyUniqSet }
+
+ fm1 = addToUFM fm k node'
+
+ -- update back edges pointing to this node
+ freezeEdge k node
+ = if elementOfUniqSet k (nodeCoalesce node)
+ then node
+ { nodeCoalesce = delOneFromUniqSet (nodeCoalesce node) k }
+ else panic "GraphOps.freezeNode: edge to freeze wasn't in the coalesce set"
+
+ fm2 = foldUniqSet (adjustUFM (freezeEdge k)) fm1
+ $ nodeCoalesce node
+
+ in fm2
+
+
+-- | Freeze one node in the graph
+-- This if for the iterative coalescer.
+-- Look for a move related node of low degree and freeze it.
+--
+-- We probably don't need to scan the whole graph looking for the node of absolute
+-- lowest degree. Just sample the first few and choose the one with the lowest
+-- degree out of those. Also, we don't make any distinction between conflicts of different
+-- classes.. this is just a heuristic, after all.
+--
+-- IDEA: freezing a node might free it up for Simplify.. would be good to check for triv
+-- right here, and add it to a worklist if known triv/non-move nodes.
+--
+freezeOneInGraph
+ :: (Uniquable k, Outputable k)
+ => Graph k cls color
+ -> ( Graph k cls color -- the new graph
+ , Bool ) -- whether we found a node to freeze
+
+freezeOneInGraph graph
+ = let compareNodeDegree n1 n2
+ = compare (sizeUniqSet $ nodeConflicts n1) (sizeUniqSet $ nodeConflicts n2)
+
+ candidates
+ = sortBy compareNodeDegree
+ $ take 5 -- 5 isn't special, it's just a small number.
+ $ scanGraph (\node -> not $ isEmptyUniqSet (nodeCoalesce node)) graph
+
+ in case candidates of
+
+ -- there wasn't anything available to freeze
+ [] -> (graph, False)
+
+ -- we found something to freeze
+ (n : _)
+ -> ( freezeNode (nodeId n) graph
+ , True)
+
+
+-- | Freeze all the nodes in the graph
+-- for debugging the iterative allocator.
+--
+freezeAllInGraph
+ :: (Uniquable k, Outputable k)
+ => Graph k cls color
+ -> Graph k cls color
+
+freezeAllInGraph graph
+ = foldr freezeNode graph
+ $ map nodeId
+ $ eltsUFM $ graphMap graph
+
+
+-- | Find all the nodes in the graph that meet some criteria
+--
+scanGraph
+ :: Uniquable k
+ => (Node k cls color -> Bool)
+ -> Graph k cls color
+ -> [Node k cls color]
+
+scanGraph match graph
+ = filter match $ eltsUFM $ graphMap graph
+
+
+-- | validate the internal structure of a graph
+-- all its edges should point to valid nodes
+-- if they don't then throw an error
+--
+validateGraph
+ :: (Uniquable k, Outputable k)
+ => SDoc
+ -> Graph k cls color
+ -> Graph k cls color
+
+validateGraph doc graph
+ = let edges = unionManyUniqSets
+ ( (map nodeConflicts $ eltsUFM $ graphMap graph)
+ ++ (map nodeCoalesce $ eltsUFM $ graphMap graph))
+
+ nodes = mkUniqSet $ map nodeId $ eltsUFM $ graphMap graph
+
+ badEdges = minusUniqSet edges nodes
+
+ in if isEmptyUniqSet badEdges
+ then graph
+ else pprPanic "GraphOps.validateGraph"
+ ( text "-- bad edges"
+ $$ vcat (map ppr $ uniqSetToList badEdges)
+ $$ text "----------------------------"
+ $$ doc)
+
+
+-- | Slurp out a map of how many nodes had a certain number of conflict neighbours
+
+slurpNodeConflictCount
+ :: Uniquable k
+ => Graph k cls color
+ -> UniqFM (Int, Int) -- ^ (conflict neighbours, num nodes with that many conflicts)
+
+slurpNodeConflictCount graph
+ = addListToUFM_C
+ (\(c1, n1) (_, n2) -> (c1, n1 + n2))
+ emptyUFM
+ $ map (\node
+ -> let count = sizeUniqSet $ nodeConflicts node
+ in (count, (count, 1)))
+ $ eltsUFM
+ $ graphMap graph
+
+
+-- | Set the color of a certain node
+setColor
+ :: Uniquable k
+ => k -> color
+ -> Graph k cls color -> Graph k cls color
+
+setColor u color
+ = graphMapModify
+ $ adjustUFM
+ (\n -> n { nodeColor = Just color })
+ u
+
+
+{-# INLINE adjustWithDefaultUFM #-}
+adjustWithDefaultUFM
+ :: Uniquable k
+ => (a -> a) -> a -> k
+ -> UniqFM a -> UniqFM a
+
+adjustWithDefaultUFM f def k map
+ = addToUFM_C
+ (\old _ -> f old)
+ map
+ k def
+
+{-# INLINE adjustUFM #-}
+adjustUFM
+ :: Uniquable k
+ => (a -> a)
+ -> k -> UniqFM a -> UniqFM a
+
+adjustUFM f k map
+ = case lookupUFM map k of
+ Nothing -> map
+ Just a -> addToUFM map k (f a)
+
diff --git a/compiler/utils/GraphPpr.hs b/compiler/utils/GraphPpr.hs
new file mode 100644
index 0000000000..1df5158dc2
--- /dev/null
+++ b/compiler/utils/GraphPpr.hs
@@ -0,0 +1,169 @@
+
+-- | Pretty printing of graphs.
+
+module GraphPpr (
+ dumpGraph,
+ dotGraph
+)
+where
+
+import GraphBase
+
+import Outputable
+import Unique
+import UniqSet
+import UniqFM
+
+import Data.List
+import Data.Maybe
+
+
+-- | Pretty print a graph in a somewhat human readable format.
+dumpGraph
+ :: (Outputable k, Outputable cls, Outputable color)
+ => Graph k cls color -> SDoc
+
+dumpGraph graph
+ = text "Graph"
+ $$ (vcat $ map dumpNode $ eltsUFM $ graphMap graph)
+
+dumpNode
+ :: (Outputable k, Outputable cls, Outputable color)
+ => Node k cls color -> SDoc
+
+dumpNode node
+ = text "Node " <> ppr (nodeId node)
+ $$ text "conflicts "
+ <> parens (int (sizeUniqSet $ nodeConflicts node))
+ <> text " = "
+ <> ppr (nodeConflicts node)
+
+ $$ text "exclusions "
+ <> parens (int (sizeUniqSet $ nodeExclusions node))
+ <> text " = "
+ <> ppr (nodeExclusions node)
+
+ $$ text "coalesce "
+ <> parens (int (sizeUniqSet $ nodeCoalesce node))
+ <> text " = "
+ <> ppr (nodeCoalesce node)
+
+ $$ space
+
+
+
+-- | Pretty print a graph in graphviz .dot format.
+-- Conflicts get solid edges.
+-- Coalescences get dashed edges.
+dotGraph
+ :: ( Uniquable k
+ , Outputable k, Outputable cls, Outputable color)
+ => (color -> SDoc) -- | What graphviz color to use for each node color
+ -- It's usually safe to return X11 style colors here,
+ -- ie "red", "green" etc or a hex triplet #aaff55 etc
+ -> Triv k cls color
+ -> Graph k cls color -> SDoc
+
+dotGraph colorMap triv graph
+ = let nodes = eltsUFM $ graphMap graph
+ in vcat
+ ( [ text "graph G {" ]
+ ++ map (dotNode colorMap triv) nodes
+ ++ (catMaybes $ snd $ mapAccumL dotNodeEdges emptyUniqSet nodes)
+ ++ [ text "}"
+ , space ])
+
+
+dotNode :: ( Uniquable k
+ , Outputable k, Outputable cls, Outputable color)
+ => (color -> SDoc)
+ -> Triv k cls color
+ -> Node k cls color -> SDoc
+
+dotNode colorMap triv node
+ = let name = ppr $ nodeId node
+ cls = ppr $ nodeClass node
+
+ excludes
+ = hcat $ punctuate space
+ $ map (\n -> text "-" <> ppr n)
+ $ uniqSetToList $ nodeExclusions node
+
+ preferences
+ = hcat $ punctuate space
+ $ map (\n -> text "+" <> ppr n)
+ $ nodePreference node
+
+ expref = if and [isEmptyUniqSet (nodeExclusions node), null (nodePreference node)]
+ then empty
+ else text "\\n" <> (excludes <+> preferences)
+
+ -- if the node has been colored then show that,
+ -- otherwise indicate whether it looks trivially colorable.
+ color
+ | Just c <- nodeColor node
+ = text "\\n(" <> ppr c <> text ")"
+
+ | triv (nodeClass node) (nodeConflicts node) (nodeExclusions node)
+ = text "\\n(" <> text "triv" <> text ")"
+
+ | otherwise
+ = text "\\n(" <> text "spill?" <> text ")"
+
+ label = name <> text " :: " <> cls
+ <> expref
+ <> color
+
+ pcolorC = case nodeColor node of
+ Nothing -> text "style=filled fillcolor=white"
+ Just c -> text "style=filled fillcolor=" <> doubleQuotes (colorMap c)
+
+
+ pout = text "node [label=" <> doubleQuotes label <> space <> pcolorC <> text "]"
+ <> space <> doubleQuotes name
+ <> text ";"
+
+ in pout
+
+
+-- | Nodes in the graph are doubly linked, but we only want one edge for each
+-- conflict if the graphviz graph. Traverse over the graph, but make sure
+-- to only print the edges for each node once.
+
+dotNodeEdges
+ :: ( Uniquable k
+ , Outputable k, Outputable cls, Outputable color)
+ => UniqSet k
+ -> Node k cls color
+ -> (UniqSet k, Maybe SDoc)
+
+dotNodeEdges visited node
+ | elementOfUniqSet (nodeId node) visited
+ = ( visited
+ , Nothing)
+
+ | otherwise
+ = let dconflicts
+ = map (dotEdgeConflict (nodeId node))
+ $ uniqSetToList
+ $ minusUniqSet (nodeConflicts node) visited
+
+ dcoalesces
+ = map (dotEdgeCoalesce (nodeId node))
+ $ uniqSetToList
+ $ minusUniqSet (nodeCoalesce node) visited
+
+ out = vcat dconflicts
+ $$ vcat dcoalesces
+
+ in ( addOneToUniqSet visited (nodeId node)
+ , Just out)
+
+ where dotEdgeConflict u1 u2
+ = doubleQuotes (ppr u1) <> text " -- " <> doubleQuotes (ppr u2)
+ <> text ";"
+
+ dotEdgeCoalesce u1 u2
+ = doubleQuotes (ppr u1) <> text " -- " <> doubleQuotes (ppr u2)
+ <> space <> text "[ style = dashed ];"
+