{-# LANGUAGE GADTs #-}
module CmmImplementSwitchPlans
  ( cmmImplementSwitchPlans
  )
where
import GhcPrelude
import Hoopl.Block
import BlockId
import Cmm
import CmmUtils
import CmmSwitch
import UniqSupply
import DynFlags
cmmImplementSwitchPlans :: DynFlags -> CmmGraph -> UniqSM CmmGraph
cmmImplementSwitchPlans dflags g
    | targetSupportsSwitch (hscTarget dflags) = return g
    | otherwise = do
    blocks' <- concat `fmap` mapM (visitSwitches dflags) (toBlockList g)
    return $ ofBlockList (g_entry g) blocks'
visitSwitches :: DynFlags -> CmmBlock -> UniqSM [CmmBlock]
visitSwitches dflags block
  | (entry@(CmmEntry _ scope), middle, CmmSwitch expr ids) <- blockSplit block
  = do
    let plan = createSwitchPlan ids
    (newTail, newBlocks) <- implementSwitchPlan dflags scope expr plan
    let block' = entry `blockJoinHead` middle `blockAppend` newTail
    return $ block' : newBlocks
  | otherwise
  = return [block]
implementSwitchPlan :: DynFlags -> CmmTickScope -> CmmExpr -> SwitchPlan -> UniqSM (Block CmmNode O C, [CmmBlock])
implementSwitchPlan dflags scope expr = go
  where
    go (Unconditionally l)
      = return (emptyBlock `blockJoinTail` CmmBranch l, [])
    go (JumpTable ids)
      = return (emptyBlock `blockJoinTail` CmmSwitch expr ids, [])
    go (IfLT signed i ids1 ids2)
      = do
        (bid1, newBlocks1) <- go' ids1
        (bid2, newBlocks2) <- go' ids2
        let lt | signed    = cmmSLtWord
               | otherwise = cmmULtWord
            scrut = lt dflags expr $ CmmLit $ mkWordCLit dflags i
            lastNode = CmmCondBranch scrut bid1 bid2 Nothing
            lastBlock = emptyBlock `blockJoinTail` lastNode
        return (lastBlock, newBlocks1++newBlocks2)
    go (IfEqual i l ids2)
      = do
        (bid2, newBlocks2) <- go' ids2
        let scrut = cmmNeWord dflags expr $ CmmLit $ mkWordCLit dflags i
            lastNode = CmmCondBranch scrut bid2 l Nothing
            lastBlock = emptyBlock `blockJoinTail` lastNode
        return (lastBlock, newBlocks2)
    
    go' (Unconditionally l)
      = return (l, [])
    go' p
      = do
        bid <- mkBlockId `fmap` getUniqueM
        (last, newBlocks) <- go p
        let block = CmmEntry bid scope `blockJoinHead` last
        return (bid, block: newBlocks)