{-# LANGUAGE ScopedTypeVariables #-}
module RegAlloc.Graph.Main (
        regAlloc
) where
import GhcPrelude
import qualified GraphColor as Color
import RegAlloc.Liveness
import RegAlloc.Graph.Spill
import RegAlloc.Graph.SpillClean
import RegAlloc.Graph.SpillCost
import RegAlloc.Graph.Stats
import RegAlloc.Graph.TrivColorable
import Instruction
import TargetReg
import RegClass
import Reg
import Bag
import DynFlags
import Outputable
import Platform
import UniqFM
import UniqSet
import UniqSupply
import Util (seqList)
import CFG
import Data.Maybe
import Control.Monad
maxSpinCount    :: Int
maxSpinCount    = 10
regAlloc
        :: (Outputable statics, Outputable instr, Instruction instr)
        => DynFlags
        -> UniqFM (UniqSet RealReg)     
        -> UniqSet Int                  
        -> Int                          
        -> [LiveCmmDecl statics instr]  
        -> Maybe CFG                    
        -> UniqSM ( [NatCmmDecl statics instr]
                  , Maybe Int, [RegAllocStats statics instr] )
           
           
regAlloc dflags regsFree slotsFree slotsCount code cfg
 = do
        
        
        
        let platform = targetPlatform dflags
            triv = trivColorable platform
                        (targetVirtualRegSqueeze platform)
                        (targetRealRegSqueeze platform)
        (code_final, debug_codeGraphs, slotsCount', _)
                <- regAlloc_spin dflags 0
                        triv
                        regsFree slotsFree slotsCount [] code cfg
        let needStack
                | slotsCount == slotsCount'
                = Nothing
                | otherwise
                = Just slotsCount'
        return  ( code_final
                , needStack
                , reverse debug_codeGraphs )
regAlloc_spin
        :: forall instr statics.
           (Instruction instr,
            Outputable instr,
            Outputable statics)
        => DynFlags
        -> Int  
        -> Color.Triv VirtualReg RegClass RealReg
                
                
        -> UniqFM (UniqSet RealReg)      
        -> UniqSet Int                   
        -> Int                           
        -> [RegAllocStats statics instr] 
        -> [LiveCmmDecl statics instr]   
        -> Maybe CFG
        -> UniqSM ( [NatCmmDecl statics instr]
                  , [RegAllocStats statics instr]
                  , Int                  
                  , Color.Graph VirtualReg RegClass RealReg)
regAlloc_spin dflags spinCount triv regsFree slotsFree slotsCount debug_codeGraphs code cfg
 = do
        let platform = targetPlatform dflags
        
        
        
        let dump = or
                [ dopt Opt_D_dump_asm_regalloc_stages dflags
                , dopt Opt_D_dump_asm_stats dflags
                , dopt Opt_D_dump_asm_conflicts dflags ]
        
        when (spinCount > maxSpinCount)
         $ pprPanic "regAlloc_spin: max build/spill cycle count exceeded."
           (  text "It looks like the register allocator is stuck in an infinite loop."
           $$ text "max cycles  = " <> int maxSpinCount
           $$ text "regsFree    = " <> (hcat $ punctuate space $ map ppr
                                             $ nonDetEltsUniqSet $ unionManyUniqSets
                                             $ nonDetEltsUFM regsFree)
              
              
              
           $$ text "slotsFree   = " <> ppr (sizeUniqSet slotsFree))
        
        (graph  :: Color.Graph VirtualReg RegClass RealReg)
                <- {-# SCC "BuildGraph" #-} buildGraph code
        
        
        
        
        
        seqGraph graph `seq` return ()
        
        
        
        let spillCosts  = foldl' plusSpillCostInfo zeroSpillCostInfo
                        $ map (slurpSpillCostInfo platform cfg) code
        
        let spill       = chooseSpill spillCosts
        
        let stat1
             = if spinCount == 0
                 then   Just $ RegAllocStatsStart
                        { raLiveCmm     = code
                        , raGraph       = graph
                        , raSpillCosts  = spillCosts }
                 else   Nothing
        
        let (graph_colored, rsSpill, rmCoalesce)
                = {-# SCC "ColorGraph" #-}
                  Color.colorGraph
                       (gopt Opt_RegsIterative dflags)
                       spinCount
                       regsFree triv spill graph
        
        let patchF reg
                | RegVirtual vr <- reg
                = case lookupUFM rmCoalesce vr of
                        Just vr'        -> patchF (RegVirtual vr')
                        Nothing         -> reg
                | otherwise
                = reg
        let (code_coalesced :: [LiveCmmDecl statics instr])
                = map (patchEraseLive patchF) code
        
        if isEmptyUniqSet rsSpill
         
         then do
                
                
                let graph_colored_lint  =
                        if gopt Opt_DoAsmLinting dflags
                                then Color.validateGraph (text "")
                                        True    
                                        graph_colored
                                else graph_colored
                
                let code_patched
                        = map (patchRegsFromGraph platform graph_colored_lint)
                              code_coalesced
                
                
                
                
                let code_spillclean
                        = map (cleanSpills platform) code_patched
                
                
                
                let code_final
                        = map (stripLive dflags) code_spillclean
                
                let stat
                     =  RegAllocStatsColored
                        { raCode                = code
                        , raGraph               = graph
                        , raGraphColored        = graph_colored_lint
                        , raCoalesced           = rmCoalesce
                        , raCodeCoalesced       = code_coalesced
                        , raPatched             = code_patched
                        , raSpillClean          = code_spillclean
                        , raFinal               = code_final
                        , raSRMs                = foldl' addSRM (0, 0, 0)
                                                $ map countSRMs code_spillclean }
                
                
                
                let statList =
                        if dump then [stat] ++ maybeToList stat1 ++ debug_codeGraphs
                                else []
                
                seqList statList (return ())
                return  ( code_final
                        , statList
                        , slotsCount
                        , graph_colored_lint)
         
         
         else do
                
                let graph_colored_lint  =
                        if gopt Opt_DoAsmLinting dflags
                                then Color.validateGraph (text "")
                                        False   
                                        graph_colored
                                else graph_colored
                
                (code_spilled, slotsFree', slotsCount', spillStats)
                        <- regSpill platform code_coalesced slotsFree slotsCount rsSpill
                
                
                
                
                code_relive     <- mapM (regLiveness platform . reverseBlocksInTops)
                                        code_spilled
                
                let stat        =
                        RegAllocStatsSpill
                        { raCode        = code
                        , raGraph       = graph_colored_lint
                        , raCoalesced   = rmCoalesce
                        , raSpillStats  = spillStats
                        , raSpillCosts  = spillCosts
                        , raSpilled     = code_spilled }
                
                
                
                let statList =
                        if dump
                                then [stat] ++ maybeToList stat1 ++ debug_codeGraphs
                                else []
                
                seqList statList (return ())
                regAlloc_spin dflags (spinCount + 1) triv regsFree slotsFree'
                              slotsCount' statList code_relive cfg
buildGraph
        :: Instruction instr
        => [LiveCmmDecl statics instr]
        -> UniqSM (Color.Graph VirtualReg RegClass RealReg)
buildGraph code
 = do
        
        let (conflictList, moveList) =
                unzip $ map slurpConflicts code
        
        let moveList2           = map slurpReloadCoalesce code
        
        let conflictBag         = unionManyBags conflictList
        let graph_conflict
                = foldrBag graphAddConflictSet Color.initGraph conflictBag
        
        let moveBag
                = unionBags (unionManyBags moveList2)
                            (unionManyBags moveList)
        let graph_coalesce
                = foldrBag graphAddCoalesce graph_conflict moveBag
        return  graph_coalesce
graphAddConflictSet
        :: UniqSet Reg
        -> Color.Graph VirtualReg RegClass RealReg
        -> Color.Graph VirtualReg RegClass RealReg
graphAddConflictSet set graph
 = let  virtuals        = mkUniqSet
                        [ vr | RegVirtual vr <- nonDetEltsUniqSet set ]
        graph1  = Color.addConflicts virtuals classOfVirtualReg graph
        graph2  = foldr (\(r1, r2) -> Color.addExclusion r1 classOfVirtualReg r2)
                        graph1
                        [ (vr, rr)
                                | RegVirtual vr <- nonDetEltsUniqSet set
                                , RegReal    rr <- nonDetEltsUniqSet set]
                          
   in   graph2
graphAddCoalesce
        :: (Reg, Reg)
        -> Color.Graph VirtualReg RegClass RealReg
        -> Color.Graph VirtualReg RegClass RealReg
graphAddCoalesce (r1, r2) graph
        | RegReal rr            <- r1
        , RegVirtual vr         <- r2
        = Color.addPreference (vr, classOfVirtualReg vr) rr graph
        | RegReal rr            <- r2
        , RegVirtual vr         <- r1
        = Color.addPreference (vr, classOfVirtualReg vr) rr graph
        | RegVirtual vr1        <- r1
        , RegVirtual vr2        <- r2
        = Color.addCoalesce
                (vr1, classOfVirtualReg vr1)
                (vr2, classOfVirtualReg vr2)
                graph
        
        
        
        | RegReal _             <- r1
        , RegReal _             <- r2
        = graph
        | otherwise
        = panic "graphAddCoalesce"
patchRegsFromGraph
        :: (Outputable statics, Outputable instr, Instruction instr)
        => Platform -> Color.Graph VirtualReg RegClass RealReg
        -> LiveCmmDecl statics instr -> LiveCmmDecl statics instr
patchRegsFromGraph platform graph code
 = patchEraseLive patchF code
 where
        
        patchF reg
                
                | RegReal{}     <- reg
                = reg
                
                | RegVirtual vr <- reg
                , Just node     <- Color.lookupNode graph vr
                = case Color.nodeColor node of
                        Just color      -> RegReal    color
                        Nothing         -> RegVirtual vr
                
                | otherwise
                = pprPanic "patchRegsFromGraph: register mapping failed."
                        (  text "There is no node in the graph for register "
                                <> ppr reg
                        $$ ppr code
                        $$ Color.dotGraph
                                (\_ -> text "white")
                                (trivColorable platform
                                        (targetVirtualRegSqueeze platform)
                                        (targetRealRegSqueeze platform))
                                graph)
seqGraph :: Color.Graph VirtualReg RegClass RealReg -> ()
seqGraph graph          = seqNodes (nonDetEltsUFM (Color.graphMap graph))
   
seqNodes :: [Color.Node VirtualReg RegClass RealReg] -> ()
seqNodes ns
 = case ns of
        []              -> ()
        (n : ns)        -> seqNode n `seq` seqNodes ns
seqNode :: Color.Node VirtualReg RegClass RealReg -> ()
seqNode node
        =     seqVirtualReg     (Color.nodeId node)
        `seq` seqRegClass       (Color.nodeClass node)
        `seq` seqMaybeRealReg   (Color.nodeColor node)
        `seq` (seqVirtualRegList (nonDetEltsUniqSet (Color.nodeConflicts node)))
        `seq` (seqRealRegList    (nonDetEltsUniqSet (Color.nodeExclusions node)))
        `seq` (seqRealRegList (Color.nodePreference node))
        `seq` (seqVirtualRegList (nonDetEltsUniqSet (Color.nodeCoalesce node)))
              
seqVirtualReg :: VirtualReg -> ()
seqVirtualReg reg = reg `seq` ()
seqRealReg :: RealReg -> ()
seqRealReg reg = reg `seq` ()
seqRegClass :: RegClass -> ()
seqRegClass c = c `seq` ()
seqMaybeRealReg :: Maybe RealReg -> ()
seqMaybeRealReg mr
 = case mr of
        Nothing         -> ()
        Just r          -> seqRealReg r
seqVirtualRegList :: [VirtualReg] -> ()
seqVirtualRegList rs
 = case rs of
        []              -> ()
        (r : rs)        -> seqVirtualReg r `seq` seqVirtualRegList rs
seqRealRegList :: [RealReg] -> ()
seqRealRegList rs
 = case rs of
        []              -> ()
        (r : rs)        -> seqRealReg r `seq` seqRealRegList rs