-- -----------------------------------------------------------------------------
-- * Misc
--

-- | Find CmmRegs that get assigned and allocate them on the stack
--
-- Any register that gets written needs to be allcoated on the
-- stack. This avoids having to map a CmmReg to an equivalent SSA form
-- and avoids having to deal with Phi node insertion.  This is also
-- the approach recommended by LLVM developers.
--
-- On the other hand, this is unnecessarily verbose if the register in
-- question is never written. Therefore we skip it where we can to
-- save a few lines in the output and hopefully speed compilation up a
-- bit.
funPrologue :: LiveGlobalRegs -> [CmmBlock] -> LlvmM StmtData
funPrologue live cmmBlocks = do

  trash <- getTrashRegs
  let getAssignedRegs :: CmmNode O O -> [CmmReg]
      getAssignedRegs (CmmAssign reg _)  = [reg]
      -- Calls will trash all registers. Unfortunately, this needs them to
      -- be stack-allocated in the first place.
      getAssignedRegs (CmmUnsafeForeignCall _ rs _) = map CmmGlobal trash ++ map CmmLocal rs
      getAssignedRegs _                  = []
      getRegsBlock (_, body, _)          = concatMap getAssignedRegs $ blockToList body
      assignedRegs = nub $ concatMap (getRegsBlock . blockSplit) cmmBlocks
      isLive r     = r `elem` alwaysLive || r `elem` live

  dflags <- getDynFlags
  stmtss <- flip mapM assignedRegs $ \reg ->
    case reg of
      CmmLocal (LocalReg un _) -> do
        let (newv, stmts) = allocReg reg
        varInsert un (pLower $ getVarType newv)
        return stmts
      CmmGlobal r -> do
        let reg   = lmGlobalRegVar dflags r
            arg   = lmGlobalRegArg dflags r
            ty    = (pLower . getVarType) reg
            trash = LMLitVar $ LMUndefLit ty
            rval  = if isLive r then arg else trash
            alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1
        markStackReg r
        return $ toOL [alloc, Store rval reg]

  return (concatOL stmtss, [])

-- | Function epilogue. Load STG variables to use as argument for call.
-- STG Liveness optimisation done here.
funPrologue :: LiveGlobalRegs -> [CmmBlock] -> LlvmM StmtData
funPrologue live cmmBlocks = do

  trash <- getTrashRegs
  let getAssignedRegs :: CmmNode O O -> [CmmReg]
      getAssignedRegs (CmmAssign reg _)  = [reg]
      -- Calls will trash all registers. Unfortunately, this needs them to
      -- be stack-allocated in the first place.
      getAssignedRegs (CmmUnsafeForeignCall _ rs _) = map CmmGlobal trash ++ map CmmLocal rs
      getAssignedRegs _                  = []
      getRegsBlock (_, body, _)          = concatMap getAssignedRegs $ blockToList body
      assignedRegs = nub $ concatMap (getRegsBlock . blockSplit) cmmBlocks
      isLive r     = r `elem` alwaysLive || r `elem` live

  dflags <- getDynFlags
  stmtss <- flip mapM assignedRegs $ \reg ->
    case reg of
      CmmLocal (LocalReg un _) -> do
        let (newv, stmts) = allocReg reg
        varInsert un (pLower $ getVarType newv)
        return stmts
      CmmGlobal r -> do
        let reg   = lmGlobalRegVar dflags r
            arg   = lmGlobalRegArg dflags r
            ty    = (pLower . getVarType) reg
            trash = LMLitVar $ LMUndefLit ty
            rval  = if isLive r then arg else trash
            alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1
        markStackReg r
        return $ toOL [alloc, Store rval reg]

  return (concatOL stmtss, [])

-- | Function epilogue. Load STG variables to use as argument for call.
-- STG Liveness optimisation done here.
funPrologue live cmmBlocks = do

  trash <- getTrashRegs
  let getAssignedRegs :: CmmNode O O -> [CmmReg]
      getAssignedRegs (CmmAssign reg _)  = [reg]
      -- Calls will trash all registers. Unfortunately, this needs them to
      -- be stack-allocated in the first place.
      getAssignedRegs (CmmUnsafeForeignCall _ rs _) = map CmmGlobal trash ++ map CmmLocal rs
      getAssignedRegs _                  = []
      getRegsBlock (_, body, _)          = concatMap getAssignedRegs $ blockToList body
      assignedRegs = nub $ concatMap (getRegsBlock . blockSplit) cmmBlocks
      isLive r     = r `elem` alwaysLive || r `elem` live

  dflags <- getDynFlags
  stmtss <- flip mapM assignedRegs $ \reg ->
    case reg of
      CmmLocal (LocalReg un _) -> do
        let (newv, stmts) = allocReg reg
        varInsert un (pLower $ getVarType newv)
        return stmts
      CmmGlobal r -> do
        let reg   = lmGlobalRegVar dflags r
            arg   = lmGlobalRegArg dflags r
            ty    = (pLower . getVarType) reg
            trash = LMLitVar $ LMUndefLit ty
            rval  = if isLive r then arg else trash
            alloc = Assignment reg $ Alloca (pLower $ getVarType reg) 1
        markStackReg r
        return $ toOL [alloc, Store rval reg]

  return (concatOL stmtss, [])

-- | Function epilogue. Load STG variables to use as argument for call.
-- STG Liveness optimisation done here.