{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

-- | Handles joining of a jump instruction to its targets.

--      The first time we encounter a jump to a particular basic block, we
--      record the assignment of temporaries.  The next time we encounter a
--      jump to the same block, we compare our current assignment to the
--      stored one.  They might be different if spilling has occurred in one
--      branch; so some fixup code will be required to match up the assignments.
--
module GHC.CmmToAsm.Reg.Linear.JoinToTargets (joinToTargets) where

import GHC.Prelude

import GHC.CmmToAsm.Reg.Linear.State
import GHC.CmmToAsm.Reg.Linear.Base
import GHC.CmmToAsm.Reg.Linear.FreeRegs
import GHC.CmmToAsm.Reg.Liveness
import GHC.CmmToAsm.Instr
import GHC.CmmToAsm.Config
import GHC.CmmToAsm.Types

import GHC.Platform.Reg

import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Collections
import GHC.Data.Graph.Directed
import GHC.Utils.Panic
import GHC.Utils.Monad (concatMapM)
import GHC.Types.Unique
import GHC.Types.Unique.FM
import GHC.Types.Unique.Set

import GHC.Utils.Outputable

-- | For a jump instruction at the end of a block, generate fixup code so its
--      vregs are in the correct regs for its destination.
--
joinToTargets
        :: (FR freeRegs, Instruction instr)
        => BlockMap RegSet              -- ^ maps the unique of the blockid to the set of vregs
                                        --      that are known to be live on the entry to each block.

        -> BlockId                      -- ^ id of the current block
        -> instr                        -- ^ branch instr on the end of the source block.

        -> RegM freeRegs ([NatBasicBlock instr] -- fresh blocks of fixup code.
                         , instr)               -- the original branch
                                                -- instruction, but maybe
                                                -- patched to jump
                                                -- to a fixup block first.

joinToTargets :: forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> BlockId -> instr -> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets BlockMap RegSet
block_live BlockId
id instr
instr

        -- we only need to worry about jump instructions.
        | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ instr -> Bool
forall instr. Instruction instr => instr -> Bool
isJumpishInstr instr
instr
        = ([NatBasicBlock instr], instr)
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([], instr
instr)

        | Bool
otherwise
        = BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
block_live [] BlockId
id instr
instr (instr -> [BlockId]
forall instr. Instruction instr => instr -> [BlockId]
jumpDestsOfInstr instr
instr)

-----
joinToTargets'
        :: (FR freeRegs, Instruction instr)
        => BlockMap RegSet              -- ^ maps the unique of the blockid to the set of vregs
                                        --      that are known to be live on the entry to each block.

        -> [NatBasicBlock instr]        -- ^ acc blocks of fixup code.

        -> BlockId                      -- ^ id of the current block
        -> instr                        -- ^ branch instr on the end of the source block.

        -> [BlockId]                    -- ^ branch destinations still to consider.

        -> RegM freeRegs ([NatBasicBlock instr], instr)

-- no more targets to consider. all done.
joinToTargets' :: forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
_          [NatBasicBlock instr]
new_blocks BlockId
_ instr
instr []
        = ([NatBasicBlock instr], instr)
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([NatBasicBlock instr]
new_blocks, instr
instr)

-- handle a branch target.
joinToTargets' BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr (BlockId
dest:[BlockId]
dests)
 = do
        -- get the map of where the vregs are stored on entry to each basic block.
        BlockAssignment freeRegs
block_assig     <- RegM freeRegs (BlockAssignment freeRegs)
forall freeRegs. RegM freeRegs (BlockAssignment freeRegs)
getBlockAssigR

        -- get the assignment on entry to the branch instruction.
        RegMap Loc
assig           <- RegM freeRegs (RegMap Loc)
forall freeRegs. RegM freeRegs (RegMap Loc)
getAssigR

        -- adjust the current assignment to remove any vregs that are not live
        -- on entry to the destination block.
        let Just RegSet
live_set       = KeyOf LabelMap -> BlockMap RegSet -> Maybe RegSet
forall a. KeyOf LabelMap -> LabelMap a -> Maybe a
forall (map :: * -> *) a.
IsMap map =>
KeyOf map -> map a -> Maybe a
mapLookup KeyOf LabelMap
BlockId
dest BlockMap RegSet
block_live
        let still_live :: Unique -> Loc -> Bool
still_live Unique
uniq Loc
_   = Unique
uniq Unique -> RegSet -> Bool
forall a. Unique -> UniqSet a -> Bool
`elemUniqSet_Directly` RegSet
live_set
        let adjusted_assig :: RegMap Loc
adjusted_assig      = (Unique -> Loc -> Bool) -> RegMap Loc -> RegMap Loc
forall elt key.
(Unique -> elt -> Bool) -> UniqFM key elt -> UniqFM key elt
filterUFM_Directly Unique -> Loc -> Bool
still_live RegMap Loc
assig

        -- and free up those registers which are now free.
        let to_free :: [RealReg]
to_free =
                [ RealReg
r     | (Unique
reg, Loc
loc) <- RegMap Loc -> [(Unique, Loc)]
forall key elt. UniqFM key elt -> [(Unique, elt)]
nonDetUFMToList RegMap Loc
assig
                        -- This is non-deterministic but we do not
                        -- currently support deterministic code-generation.
                        -- See Note [Unique Determinism and code generation]
                        , Bool -> Bool
not (Unique -> RegSet -> Bool
forall a. Unique -> UniqSet a -> Bool
elemUniqSet_Directly Unique
reg RegSet
live_set)
                        , RealReg
r          <- Loc -> [RealReg]
regsOfLoc Loc
loc ]

        case BlockId -> BlockAssignment freeRegs -> Maybe (freeRegs, RegMap Loc)
forall freeRegs.
BlockId -> BlockAssignment freeRegs -> Maybe (freeRegs, RegMap Loc)
lookupBlockAssignment  BlockId
dest BlockAssignment freeRegs
block_assig of
         Maybe (freeRegs, RegMap Loc)
Nothing
          -> BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> BlockAssignment freeRegs
-> RegMap Loc
-> [RealReg]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> BlockAssignment freeRegs
-> RegMap Loc
-> [RealReg]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_first
                        BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr BlockId
dest [BlockId]
dests
                        BlockAssignment freeRegs
block_assig RegMap Loc
adjusted_assig [RealReg]
to_free

         Just (freeRegs
_, RegMap Loc
dest_assig)
          -> BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> RegMap Loc
-> RegMap Loc
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall instr freeRegs.
(Instruction instr, FR freeRegs) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> RegMap Loc
-> RegMap Loc
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_again
                        BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr BlockId
dest [BlockId]
dests
                        RegMap Loc
adjusted_assig RegMap Loc
dest_assig


-- this is the first time we jumped to this block.
joinToTargets_first :: (FR freeRegs, Instruction instr)
                    => BlockMap RegSet
                    -> [NatBasicBlock instr]
                    -> BlockId
                    -> instr
                    -> BlockId
                    -> [BlockId]
                    -> BlockAssignment freeRegs
                    -> RegMap Loc
                    -> [RealReg]
                    -> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_first :: forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> BlockAssignment freeRegs
-> RegMap Loc
-> [RealReg]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_first BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr BlockId
dest [BlockId]
dests
        BlockAssignment freeRegs
block_assig RegMap Loc
src_assig
        [RealReg]
to_free

 = do   NCGConfig
config <- RegM freeRegs NCGConfig
forall a. RegM a NCGConfig
getConfig
        let platform :: Platform
platform = NCGConfig -> Platform
ncgPlatform NCGConfig
config

        -- free up the regs that are not live on entry to this block.
        freeRegs
freeregs        <- RegM freeRegs freeRegs
forall freeRegs. RegM freeRegs freeRegs
getFreeRegsR
        let freeregs' :: freeRegs
freeregs' = (freeRegs -> RealReg -> freeRegs)
-> freeRegs -> [RealReg] -> freeRegs
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' ((RealReg -> freeRegs -> freeRegs)
-> freeRegs -> RealReg -> freeRegs
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((RealReg -> freeRegs -> freeRegs)
 -> freeRegs -> RealReg -> freeRegs)
-> (RealReg -> freeRegs -> freeRegs)
-> freeRegs
-> RealReg
-> freeRegs
forall a b. (a -> b) -> a -> b
$ Platform -> RealReg -> freeRegs -> freeRegs
forall freeRegs.
FR freeRegs =>
Platform -> RealReg -> freeRegs -> freeRegs
frReleaseReg Platform
platform) freeRegs
freeregs [RealReg]
to_free

        -- remember the current assignment on entry to this block.
        BlockAssignment freeRegs -> RegM freeRegs ()
forall freeRegs. BlockAssignment freeRegs -> RegM freeRegs ()
setBlockAssigR (BlockId
-> (freeRegs, RegMap Loc)
-> BlockAssignment freeRegs
-> BlockAssignment freeRegs
forall freeRegs.
BlockId
-> (freeRegs, RegMap Loc)
-> BlockAssignment freeRegs
-> BlockAssignment freeRegs
updateBlockAssignment BlockId
dest (freeRegs
freeregs', RegMap Loc
src_assig) BlockAssignment freeRegs
block_assig)

        BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr [BlockId]
dests


-- we've jumped to this block before
joinToTargets_again :: (Instruction instr, FR freeRegs)
                    => BlockMap RegSet
                    -> [NatBasicBlock instr]
                    -> BlockId
                    -> instr
                    -> BlockId
                    -> [BlockId]
                    -> UniqFM Reg Loc
                    -> UniqFM Reg Loc
                    -> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_again :: forall instr freeRegs.
(Instruction instr, FR freeRegs) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> BlockId
-> [BlockId]
-> RegMap Loc
-> RegMap Loc
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets_again
    BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr BlockId
dest [BlockId]
dests
    RegMap Loc
src_assig RegMap Loc
dest_assig

        -- the assignments already match, no problem.
        | RegMap Loc -> [(Unique, Loc)]
forall key elt. UniqFM key elt -> [(Unique, elt)]
nonDetUFMToList RegMap Loc
dest_assig [(Unique, Loc)] -> [(Unique, Loc)] -> Bool
forall a. Eq a => a -> a -> Bool
== RegMap Loc -> [(Unique, Loc)]
forall key elt. UniqFM key elt -> [(Unique, elt)]
nonDetUFMToList RegMap Loc
src_assig
        -- This is non-deterministic but we do not
        -- currently support deterministic code-generation.
        -- See Note [Unique Determinism and code generation]
        = BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr [BlockId]
dests

        -- assignments don't match, need fixup code
        | Bool
otherwise
        = do

                -- make a graph of what things need to be moved where.
                let graph :: [Node Loc Unique]
graph = RegMap Loc -> RegMap Loc -> [Node Loc Unique]
makeRegMovementGraph RegMap Loc
src_assig RegMap Loc
dest_assig

                -- look for cycles in the graph. This can happen if regs need to be swapped.
                -- Note that we depend on the fact that this function does a
                --      bottom up traversal of the tree-like portions of the graph.
                --
                --  eg, if we have
                --      R1 -> R2 -> R3
                --
                --  ie move value in R1 to R2 and value in R2 to R3.
                --
                -- We need to do the R2 -> R3 move before R1 -> R2.
                --
                let sccs :: [SCC (Node Loc Unique)]
sccs  = [Node Loc Unique] -> [SCC (Node Loc Unique)]
forall key payload.
Ord key =>
[Node key payload] -> [SCC (Node key payload)]
stronglyConnCompFromEdgedVerticesOrdR [Node Loc Unique]
graph

              -- debugging
                {-
                pprTrace
                        ("joinToTargets: making fixup code")
                        (vcat   [ text "        in block: "     <> ppr block_id
                                , text " jmp instruction: "     <> ppr instr
                                , text "  src assignment: "     <> ppr src_assig
                                , text " dest assignment: "     <> ppr dest_assig
                                , text "  movement graph: "     <> ppr graph
                                , text "   sccs of graph: "     <> ppr sccs
                                , text ""])
                        (return ())
                -}
                Int
delta           <- RegM freeRegs Int
forall freeRegs. RegM freeRegs Int
getDeltaR
                [[instr]]
fixUpInstrs_    <- (SCC (Node Loc Unique) -> RegM freeRegs [instr])
-> [SCC (Node Loc Unique)] -> RegM freeRegs [[instr]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> instr -> SCC (Node Loc Unique) -> RegM freeRegs [instr]
forall instr freeRegs.
Instruction instr =>
Int -> instr -> SCC (Node Loc Unique) -> RegM freeRegs [instr]
handleComponent Int
delta instr
instr) [SCC (Node Loc Unique)]
sccs
                let fixUpInstrs :: [instr]
fixUpInstrs = [[instr]] -> [instr]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[instr]]
fixUpInstrs_

                -- make a new basic block containing the fixup code.
                --      A the end of the current block we will jump to the fixup one,
                --      then that will jump to our original destination.
                BlockId
fixup_block_id <- Unique -> BlockId
mkBlockId (Unique -> BlockId)
-> RegM freeRegs Unique -> RegM freeRegs BlockId
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RegM freeRegs Unique
forall freeRegs. RegM freeRegs Unique
getUniqueR
                let block :: NatBasicBlock instr
block = BlockId -> [instr] -> NatBasicBlock instr
forall i. BlockId -> [i] -> GenBasicBlock i
BasicBlock BlockId
fixup_block_id
                                ([instr] -> NatBasicBlock instr) -> [instr] -> NatBasicBlock instr
forall a b. (a -> b) -> a -> b
$ [instr]
fixUpInstrs [instr] -> [instr] -> [instr]
forall a. [a] -> [a] -> [a]
++ BlockId -> [instr]
forall instr. Instruction instr => BlockId -> [instr]
mkJumpInstr BlockId
dest

                -- if we didn't need any fixups, then don't include the block
                case [instr]
fixUpInstrs of
                 []     -> BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
block_live [NatBasicBlock instr]
new_blocks BlockId
block_id instr
instr [BlockId]
dests

                 -- patch the original branch instruction so it goes to our
                 --     fixup block instead.
                 [instr]
_      -> let  instr' :: instr
instr'  =  instr -> (BlockId -> BlockId) -> instr
forall instr.
Instruction instr =>
instr -> (BlockId -> BlockId) -> instr
patchJumpInstr instr
instr
                                            (\BlockId
bid -> if BlockId
bid BlockId -> BlockId -> Bool
forall a. Eq a => a -> a -> Bool
== BlockId
dest
                                                        then BlockId
fixup_block_id
                                                        else BlockId
bid) -- no change!

                           in do
                                {- --debugging
                                pprTrace "FixUpEdge info:"
                                    (
                                    text "inBlock:" <> ppr block_id $$
                                    text "instr:" <> ppr instr $$
                                    text "instr':" <> ppr instr' $$
                                    text "fixup_block_id':" <>
                                        ppr fixup_block_id $$
                                    text "dest:" <> ppr dest
                                    ) (return ())
                                -}
                                BlockId -> BlockId -> BlockId -> RegM freeRegs ()
forall freeRegs. BlockId -> BlockId -> BlockId -> RegM freeRegs ()
recordFixupBlock BlockId
block_id BlockId
fixup_block_id BlockId
dest
                                BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
forall freeRegs instr.
(FR freeRegs, Instruction instr) =>
BlockMap RegSet
-> [NatBasicBlock instr]
-> BlockId
-> instr
-> [BlockId]
-> RegM freeRegs ([NatBasicBlock instr], instr)
joinToTargets' BlockMap RegSet
block_live (NatBasicBlock instr
block NatBasicBlock instr
-> [NatBasicBlock instr] -> [NatBasicBlock instr]
forall a. a -> [a] -> [a]
: [NatBasicBlock instr]
new_blocks)
                                               BlockId
block_id instr
instr' [BlockId]
dests


-- | Construct a graph of register\/spill movements.
--
--      Cyclic components seem to occur only very rarely.
--
--      We cut some corners by not handling memory-to-memory moves.
--      This shouldn't happen because every temporary gets its own stack slot.
--
makeRegMovementGraph :: RegMap Loc -> RegMap Loc -> [Node Loc Unique]
makeRegMovementGraph :: RegMap Loc -> RegMap Loc -> [Node Loc Unique]
makeRegMovementGraph RegMap Loc
adjusted_assig RegMap Loc
dest_assig
 = [ Node Loc Unique
node       | (Unique
vreg, Loc
src) <- RegMap Loc -> [(Unique, Loc)]
forall key elt. UniqFM key elt -> [(Unique, elt)]
nonDetUFMToList RegMap Loc
adjusted_assig
                    -- This is non-deterministic but we do not
                    -- currently support deterministic code-generation.
                    -- See Note [Unique Determinism and code generation]
                    -- source reg might not be needed at the dest:
                , Just Loc
loc <- [RegMap Loc -> Unique -> Maybe Loc
forall key elt. UniqFM key elt -> Unique -> Maybe elt
lookupUFM_Directly RegMap Loc
dest_assig Unique
vreg]
                , Node Loc Unique
node <- Unique -> Loc -> Loc -> [Node Loc Unique]
forall a. a -> Loc -> Loc -> [Node Loc a]
expandNode Unique
vreg Loc
src Loc
loc ]


-- | Expand out the destination, so InBoth destinations turn into
--      a combination of InReg and InMem.

--      The InBoth handling is a little tricky here.  If the destination is
--      InBoth, then we must ensure that the value ends up in both locations.
--      An InBoth  destination must conflict with an InReg or InMem source, so
--      we expand an InBoth destination as necessary.
--
--      An InBoth source is slightly different: we only care about the register
--      that the source value is in, so that we can move it to the destinations.
--
expandNode
        :: a
        -> Loc                  -- ^ source of move
        -> Loc                  -- ^ destination of move
        -> [Node Loc a ]

expandNode :: forall a. a -> Loc -> Loc -> [Node Loc a]
expandNode a
vreg loc :: Loc
loc@(InReg RealReg
src) (InBoth RealReg
dst Int
mem)
        | RealReg
src RealReg -> RealReg -> Bool
forall a. Eq a => a -> a -> Bool
== RealReg
dst = [a -> Loc -> [Loc] -> Node Loc a
forall key payload. payload -> key -> [key] -> Node key payload
DigraphNode a
vreg Loc
loc [Int -> Loc
InMem Int
mem]]
        | Bool
otherwise  = [a -> Loc -> [Loc] -> Node Loc a
forall key payload. payload -> key -> [key] -> Node key payload
DigraphNode a
vreg Loc
loc [RealReg -> Loc
InReg RealReg
dst, Int -> Loc
InMem Int
mem]]

expandNode a
vreg loc :: Loc
loc@(InMem Int
src) (InBoth RealReg
dst Int
mem)
        | Int
src Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
mem = [a -> Loc -> [Loc] -> Node Loc a
forall key payload. payload -> key -> [key] -> Node key payload
DigraphNode a
vreg Loc
loc [RealReg -> Loc
InReg RealReg
dst]]
        | Bool
otherwise  = [a -> Loc -> [Loc] -> Node Loc a
forall key payload. payload -> key -> [key] -> Node key payload
DigraphNode a
vreg Loc
loc [RealReg -> Loc
InReg RealReg
dst, Int -> Loc
InMem Int
mem]]

expandNode a
_        (InBoth RealReg
_ Int
src) (InMem Int
dst)
        | Int
src Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
dst = [] -- guaranteed to be true

expandNode a
_        (InBoth RealReg
src Int
_) (InReg RealReg
dst)
        | RealReg
src RealReg -> RealReg -> Bool
forall a. Eq a => a -> a -> Bool
== RealReg
dst = []

expandNode a
vreg     (InBoth RealReg
src Int
_) Loc
dst
        = a -> Loc -> Loc -> [Node Loc a]
forall a. a -> Loc -> Loc -> [Node Loc a]
expandNode a
vreg (RealReg -> Loc
InReg RealReg
src) Loc
dst

expandNode a
vreg Loc
src Loc
dst
        | Loc
src Loc -> Loc -> Bool
forall a. Eq a => a -> a -> Bool
== Loc
dst = []
        | Bool
otherwise  = [a -> Loc -> [Loc] -> Node Loc a
forall key payload. payload -> key -> [key] -> Node key payload
DigraphNode a
vreg Loc
src [Loc
dst]]


-- | Generate fixup code for a particular component in the move graph
--      This component tells us what values need to be moved to what
--      destinations. We have eliminated any possibility of single-node
--      cycles in expandNode above.
--
handleComponent
        :: Instruction instr
        => Int -> instr -> SCC (Node Loc Unique)
        -> RegM freeRegs [instr]

-- If the graph is acyclic then we won't get the swapping problem below.
--      In this case we can just do the moves directly, and avoid having to
--      go via a spill slot.
--
handleComponent :: forall instr freeRegs.
Instruction instr =>
Int -> instr -> SCC (Node Loc Unique) -> RegM freeRegs [instr]
handleComponent Int
delta instr
_  (AcyclicSCC (DigraphNode Unique
vreg Loc
src [Loc]
dsts))
        = (Loc -> RegM freeRegs [instr]) -> [Loc] -> RegM freeRegs [instr]
forall (m :: * -> *) (f :: * -> *) a b.
(Monad m, Traversable f) =>
(a -> m [b]) -> f a -> m [b]
concatMapM (Int -> Unique -> Loc -> Loc -> RegM freeRegs [instr]
forall instr freeRegs.
Instruction instr =>
Int -> Unique -> Loc -> Loc -> RegM freeRegs [instr]
makeMove Int
delta Unique
vreg Loc
src) [Loc]
dsts


-- Handle some cyclic moves.
--      This can happen if we have two regs that need to be swapped.
--      eg:
--           vreg   source loc   dest loc
--          (vreg1, InReg r1,    [InReg r2])
--          (vreg2, InReg r2,    [InReg r1])
--
--      To avoid needing temp register, we just spill all the source regs, then
--      reaload them into their destination regs.
--
--      Note that we can not have cycles that involve memory locations as
--      sources as single destination because memory locations (stack slots)
--      are allocated exclusively for a virtual register and therefore can not
--      require a fixup.
--
handleComponent Int
delta instr
instr
        (CyclicSCC ((DigraphNode Unique
vreg (InReg RealReg
sreg) ((InReg RealReg
dreg: [Loc]
_))) : [Node Loc Unique]
rest))
        -- dest list may have more than one element, if the reg is also InMem.
 = do
        -- spill the source into its slot
        ([instr]
instrSpill, Int
slot)
                        <- Reg -> Unique -> RegM freeRegs ([instr], Int)
forall instr freeRegs.
Instruction instr =>
Reg -> Unique -> RegM freeRegs ([instr], Int)
spillR (RealReg -> Reg
RegReal RealReg
sreg) Unique
vreg

        -- reload into destination reg
        [instr]
instrLoad       <- Reg -> Int -> RegM freeRegs [instr]
forall instr freeRegs.
Instruction instr =>
Reg -> Int -> RegM freeRegs [instr]
loadR (RealReg -> Reg
RegReal RealReg
dreg) Int
slot

        [[instr]]
remainingFixUps <- (SCC (Node Loc Unique) -> RegM freeRegs [instr])
-> [SCC (Node Loc Unique)] -> RegM freeRegs [[instr]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (Int -> instr -> SCC (Node Loc Unique) -> RegM freeRegs [instr]
forall instr freeRegs.
Instruction instr =>
Int -> instr -> SCC (Node Loc Unique) -> RegM freeRegs [instr]
handleComponent Int
delta instr
instr)
                                ([Node Loc Unique] -> [SCC (Node Loc Unique)]
forall key payload.
Ord key =>
[Node key payload] -> [SCC (Node key payload)]
stronglyConnCompFromEdgedVerticesOrdR [Node Loc Unique]
rest)

        -- make sure to do all the reloads after all the spills,
        --      so we don't end up clobbering the source values.
        [instr] -> RegM freeRegs [instr]
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([instr]
instrSpill [instr] -> [instr] -> [instr]
forall a. [a] -> [a] -> [a]
++ [[instr]] -> [instr]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[instr]]
remainingFixUps [instr] -> [instr] -> [instr]
forall a. [a] -> [a] -> [a]
++ [instr]
instrLoad)

handleComponent Int
_ instr
_ (CyclicSCC [Node Loc Unique]
_)
 = String -> RegM freeRegs [instr]
forall a. HasCallStack => String -> a
panic String
"Register Allocator: handleComponent cyclic"


-- | Move a vreg between these two locations.
--
makeMove
    :: Instruction instr
    => Int      -- ^ current C stack delta.
    -> Unique   -- ^ unique of the vreg that we're moving.
    -> Loc      -- ^ source location.
    -> Loc      -- ^ destination location.
    -> RegM freeRegs [instr]  -- ^ move instruction.

makeMove :: forall instr freeRegs.
Instruction instr =>
Int -> Unique -> Loc -> Loc -> RegM freeRegs [instr]
makeMove Int
delta Unique
vreg Loc
src Loc
dst
 = do NCGConfig
config <- RegM freeRegs NCGConfig
forall a. RegM a NCGConfig
getConfig
      let platform :: Platform
platform = NCGConfig -> Platform
ncgPlatform NCGConfig
config

      case (Loc
src, Loc
dst) of
          (InReg RealReg
s, InReg RealReg
d) ->
              do SpillReason -> RegM freeRegs ()
forall freeRegs. SpillReason -> RegM freeRegs ()
recordSpill (Unique -> SpillReason
SpillJoinRR Unique
vreg)
                 [instr] -> RegM freeRegs [instr]
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([instr] -> RegM freeRegs [instr])
-> [instr] -> RegM freeRegs [instr]
forall a b. (a -> b) -> a -> b
$ [Platform -> Reg -> Reg -> instr
forall instr. Instruction instr => Platform -> Reg -> Reg -> instr
mkRegRegMoveInstr Platform
platform (RealReg -> Reg
RegReal RealReg
s) (RealReg -> Reg
RegReal RealReg
d)]
          (InMem Int
s, InReg RealReg
d) ->
              do SpillReason -> RegM freeRegs ()
forall freeRegs. SpillReason -> RegM freeRegs ()
recordSpill (Unique -> SpillReason
SpillJoinRM Unique
vreg)
                 [instr] -> RegM freeRegs [instr]
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([instr] -> RegM freeRegs [instr])
-> [instr] -> RegM freeRegs [instr]
forall a b. (a -> b) -> a -> b
$ NCGConfig -> Reg -> Int -> Int -> [instr]
forall instr.
Instruction instr =>
NCGConfig -> Reg -> Int -> Int -> [instr]
mkLoadInstr NCGConfig
config (RealReg -> Reg
RegReal RealReg
d) Int
delta Int
s
          (InReg RealReg
s, InMem Int
d) ->
              do SpillReason -> RegM freeRegs ()
forall freeRegs. SpillReason -> RegM freeRegs ()
recordSpill (Unique -> SpillReason
SpillJoinRM Unique
vreg)
                 [instr] -> RegM freeRegs [instr]
forall a. a -> RegM freeRegs a
forall (m :: * -> *) a. Monad m => a -> m a
return ([instr] -> RegM freeRegs [instr])
-> [instr] -> RegM freeRegs [instr]
forall a b. (a -> b) -> a -> b
$ NCGConfig -> Reg -> Int -> Int -> [instr]
forall instr.
Instruction instr =>
NCGConfig -> Reg -> Int -> Int -> [instr]
mkSpillInstr NCGConfig
config (RealReg -> Reg
RegReal RealReg
s) Int
delta Int
d
          (Loc, Loc)
_ ->
              -- we don't handle memory to memory moves.
              -- they shouldn't happen because we don't share
              -- stack slots between vregs.
              String -> SDoc -> RegM freeRegs [instr]
forall a. HasCallStack => String -> SDoc -> a
pprPanic String
"makeMove: we don't handle mem->mem moves"
                 (Unique -> SDoc
forall a. Outputable a => a -> SDoc
ppr Unique
vreg SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc
parens (Loc -> SDoc
forall a. Outputable a => a -> SDoc
ppr Loc
src) SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc
parens (Loc -> SDoc
forall a. Outputable a => a -> SDoc
ppr Loc
dst))