    {-# 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.Label
    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.
            block_assig     <- RegM freeRegs (BlockAssignment freeRegs)
    forall freeRegs. RegM freeRegs (BlockAssignment freeRegs)
    getBlockAssigR

            -- get the assignment on entry to the branch instruction.
            assig           <- getAssigR

            -- adjust the current assignment to remove any vregs that are not live
            -- on entry to the destination block.
            let Just live_set       = mapLookup dest block_live
            let still_live Unique
    uniq Loc
    _   = Unique
    uniq Unique -> RegSet -> Bool
    forall a. Unique -> UniqSet a -> Bool
    `elemUniqSet_Directly` RegSet
    live_set
            let adjusted_assig      = (Unique -> Loc -> Bool) -> RegMap Loc -> RegMap Loc
    forall {k} elt (key :: k).
    (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
    r     | (Unique
    reg, Loc
    loc) <- RegMap Loc -> [(Unique, Loc)]
    forall {k} (key :: k) 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 lookupBlockAssignment  dest 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   config <- RegM freeRegs NCGConfig
    forall a. RegM a NCGConfig
    getConfig
            let platform = NCGConfig -> Platform
    ncgPlatform NCGConfig
    config

            -- free up the regs that are not live on entry to this block.
            freeregs        <- getFreeRegsR
            let 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.
            setBlockAssigR (updateBlockAssignment dest (freeregs', src_assig) block_assig)

            joinToTargets' block_live new_blocks block_id instr 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 {k} (key :: k) 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 {k} (key :: k) 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 ())
                    -}
                    delta           <- RegM freeRegs Int
    forall freeRegs. RegM freeRegs Int
    getDeltaR
                    fixUpInstrs     <- concatMapM (handleComponent delta instr) sccs

                    -- 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.
                    fixup_block_id <- mkBlockId <$> getUniqueR
                    let 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 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 {k} (key :: k) 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 {k} (key :: k) 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
            (instrSpill, 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
            instrLoad       <- loadR (RegReal dreg) slot

            remainingFixUps <- mapM (handleComponent delta instr)
                                    (stronglyConnCompFromEdgedVerticesOrdR rest)

            -- make sure to do all the reloads after all the spills,
            --      so we don't end up clobbering the source values.
            return (instrSpill ++ concat remainingFixUps ++ 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 config <- RegM freeRegs NCGConfig
    forall a. RegM a NCGConfig
    getConfig
          let platform = NCGConfig -> Platform
    ncgPlatform NCGConfig
    config

          case (src, 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))