module LinearScan.LiveSets where


import Debug.Trace (trace, traceShow)
import qualified Prelude
import qualified Data.IntMap
import qualified Data.IntSet
import qualified Data.List
import qualified Data.Ord
import qualified Data.Functor.Identity
import qualified LinearScan.Utils

import qualified LinearScan.Blocks as Blocks
import qualified LinearScan.IntMap as IntMap
import qualified LinearScan.Lib as Lib
import qualified LinearScan.Seq as Seq
import qualified LinearScan.Ssrnat as Ssrnat


data BlockLiveSets =
   Build_BlockLiveSets Data.IntSet.IntSet Data.IntSet.IntSet Data.IntSet.IntSet 
 Data.IntSet.IntSet Blocks.OpId Blocks.OpId

blockLiveGen :: BlockLiveSets -> Data.IntSet.IntSet
blockLiveGen b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockLiveGen0}

blockLiveKill :: BlockLiveSets -> Data.IntSet.IntSet
blockLiveKill b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockLiveKill0}

blockLiveIn :: BlockLiveSets -> Data.IntSet.IntSet
blockLiveIn b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockLiveIn0}

blockLiveOut :: BlockLiveSets -> Data.IntSet.IntSet
blockLiveOut b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockLiveOut0}

blockFirstOpId :: BlockLiveSets -> Blocks.OpId
blockFirstOpId b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockFirstOpId0}

blockLastOpId :: BlockLiveSets -> Blocks.OpId
blockLastOpId b =
  case b of {
   Build_BlockLiveSets blockLiveGen0 blockLiveKill0 blockLiveIn0
    blockLiveOut0 blockFirstOpId0 blockLastOpId0 -> blockLastOpId0}

computeLocalLiveSets :: Prelude.Int -> (Blocks.BlockInfo a1 a2 a3 a4) ->
                        (Blocks.OpInfo a5 a3 a4) -> ([] a1) ->
                        Data.IntMap.IntMap BlockLiveSets
computeLocalLiveSets maxReg binfo oinfo blocks =
  Prelude.snd
    (Lib.forFold ((,) ((Prelude.succ) 0) IntMap.emptyIntMap) blocks
      (\acc b ->
      case acc of {
       (,) idx m ->
        case Blocks.blockOps binfo b of {
         (,) p opse ->
          case p of {
           (,) opsb opsm ->
            let {
             liveSet = Build_BlockLiveSets Data.IntSet.empty
              Data.IntSet.empty Data.IntSet.empty Data.IntSet.empty
              ((Prelude.+) idx (Ssrnat.double (Data.List.length opsb))) idx}
            in
            case Lib.forFold ((,) idx liveSet)
                   ((Prelude.++) opsb ((Prelude.++) opsm opse)) (\acc0 o ->
                   case acc0 of {
                    (,) lastIdx liveSet1 -> (,) ((Prelude.succ)
                     ((Prelude.succ) lastIdx))
                     (Lib.forFold liveSet1 (Blocks.opRefs maxReg oinfo o)
                       (\liveSet2 v ->
                       case Blocks.varId maxReg v of {
                        Prelude.Left p0 -> liveSet2;
                        Prelude.Right vid ->
                         case Blocks.varKind maxReg v of {
                          Blocks.Input ->
                           case Prelude.not
                                  (Data.IntSet.member vid
                                    (blockLiveKill liveSet2)) of {
                            Prelude.True -> Build_BlockLiveSets
                             (Data.IntSet.insert vid (blockLiveGen liveSet2))
                             (blockLiveKill liveSet2) (blockLiveIn liveSet2)
                             (blockLiveOut liveSet2)
                             (blockFirstOpId liveSet2) lastIdx;
                            Prelude.False -> liveSet2};
                          _ -> Build_BlockLiveSets (blockLiveGen liveSet2)
                           (Data.IntSet.insert vid (blockLiveKill liveSet2))
                           (blockLiveIn liveSet2) (blockLiveOut liveSet2)
                           (blockFirstOpId liveSet2) lastIdx}}))}) of {
             (,) lastIdx' liveSet3 -> (,) lastIdx'
              (Data.IntMap.insert (Blocks.blockId binfo b) liveSet3 m)}}}}))

computeGlobalLiveSets :: (Blocks.BlockInfo a1 a2 a3 a4) -> ([] a1) ->
                         (Data.IntMap.IntMap BlockLiveSets) ->
                         Data.IntMap.IntMap BlockLiveSets
computeGlobalLiveSets binfo blocks liveSets =
  Lib.forFold liveSets (Seq.rev blocks) (\liveSets1 b ->
    let {bid = Blocks.blockId binfo b} in
    case Data.IntMap.lookup bid liveSets1 of {
     Prelude.Just liveSet ->
      let {
       liveSet2 = Lib.forFold liveSet (Blocks.blockSuccessors binfo b)
                    (\liveSet1 s_bid ->
                    case Data.IntMap.lookup s_bid liveSets1 of {
                     Prelude.Just sux -> Build_BlockLiveSets
                      (blockLiveGen liveSet1) (blockLiveKill liveSet1)
                      (blockLiveIn liveSet1)
                      (Data.IntSet.union (blockLiveOut liveSet1)
                        (blockLiveIn sux)) (blockFirstOpId liveSet1)
                      (blockLastOpId liveSet1);
                     Prelude.Nothing -> liveSet1})}
      in
      Data.IntMap.insert bid (Build_BlockLiveSets (blockLiveGen liveSet2)
        (blockLiveKill liveSet2)
        (Data.IntSet.union
          (Data.IntSet.difference (blockLiveOut liveSet2)
            (blockLiveKill liveSet2)) (blockLiveGen liveSet2))
        (blockLiveOut liveSet2) (blockFirstOpId liveSet2)
        (blockLastOpId liveSet2)) liveSets1;
     Prelude.Nothing -> liveSets1})