{-# LANGUAGE CPP #-}
module CmmInfo (
  mkEmptyContInfoTable,
  cmmToRawCmm,
  mkInfoTable,
  srtEscape,
  
  closureInfoPtr,
  entryCode,
  getConstrTag,
  cmmGetClosureType,
  infoTable,
  infoTableConstrTag,
  infoTableSrtBitmap,
  infoTableClosureType,
  infoTablePtrs,
  infoTableNonPtrs,
  funInfoTable,
  funInfoArity,
  
  stdInfoTableSizeW,
  fixedInfoTableSizeW,
  profInfoTableSizeW,
  maxStdInfoTableSizeW,
  maxRetInfoTableSizeW,
  stdInfoTableSizeB,
  conInfoTableSizeB,
  stdSrtBitmapOffset,
  stdClosureTypeOffset,
  stdPtrsOffset, stdNonPtrsOffset,
) where
#include "HsVersions.h"
import GhcPrelude
import Cmm
import CmmUtils
import CLabel
import SMRep
import Bitmap
import Stream (Stream)
import qualified Stream
import Hoopl.Collections
import Platform
import Maybes
import DynFlags
import Panic
import UniqSupply
import MonadUtils
import Util
import Outputable
import Data.ByteString (ByteString)
import Data.Bits
mkEmptyContInfoTable :: CLabel -> CmmInfoTable
mkEmptyContInfoTable info_lbl
  = CmmInfoTable { cit_lbl  = info_lbl
                 , cit_rep  = mkStackRep []
                 , cit_prof = NoProfilingInfo
                 , cit_srt  = Nothing
                 , cit_clo  = Nothing }
cmmToRawCmm :: DynFlags -> Stream IO CmmGroup ()
            -> IO (Stream IO RawCmmGroup ())
cmmToRawCmm dflags cmms
  = do { uniqs <- mkSplitUniqSupply 'i'
       ; let do_one uniqs cmm = do
                case initUs uniqs $ concatMapM (mkInfoTable dflags) cmm of
                  (b,uniqs') -> return (uniqs',b)
                  
       ; return (Stream.mapAccumL do_one uniqs cmms >> return ())
       }
mkInfoTable :: DynFlags -> CmmDecl -> UniqSM [RawCmmDecl]
mkInfoTable _ (CmmData sec dat)
  = return [CmmData sec dat]
mkInfoTable dflags proc@(CmmProc infos entry_lbl live blocks)
  
  
  
  
  | not (tablesNextToCode dflags)
  = case topInfoTable proc of   
      
      Nothing ->
         return [CmmProc mapEmpty entry_lbl live blocks]
      Just info@CmmInfoTable { cit_lbl = info_lbl } -> do
        (top_decls, (std_info, extra_bits)) <-
             mkInfoTableContents dflags info Nothing
        let
          rel_std_info   = map (makeRelativeRefTo dflags info_lbl) std_info
          rel_extra_bits = map (makeRelativeRefTo dflags info_lbl) extra_bits
        
        
        
        
        return (top_decls ++
                [CmmProc mapEmpty entry_lbl live blocks,
                 mkRODataLits info_lbl
                    (CmmLabel entry_lbl : rel_std_info ++ rel_extra_bits)])
  
  
  
  
  
  
  | otherwise
  = do
    (top_declss, raw_infos) <-
       unzip `fmap` mapM do_one_info (mapToList (info_tbls infos))
    return (concat top_declss ++
            [CmmProc (mapFromList raw_infos) entry_lbl live blocks])
  where
   do_one_info (lbl,itbl) = do
     (top_decls, (std_info, extra_bits)) <-
         mkInfoTableContents dflags itbl Nothing
     let
        info_lbl = cit_lbl itbl
        rel_std_info   = map (makeRelativeRefTo dflags info_lbl) std_info
        rel_extra_bits = map (makeRelativeRefTo dflags info_lbl) extra_bits
     
     return (top_decls, (lbl, Statics info_lbl $ map CmmStaticLit $
                              reverse rel_extra_bits ++ rel_std_info))
type InfoTableContents = ( [CmmLit]          
                         , [CmmLit] )        
mkInfoTableContents :: DynFlags
                    -> CmmInfoTable
                    -> Maybe Int               
                    -> UniqSM ([RawCmmDecl],             
                               InfoTableContents)       
mkInfoTableContents dflags
                    info@(CmmInfoTable { cit_lbl  = info_lbl
                                       , cit_rep  = smrep
                                       , cit_prof = prof
                                       , cit_srt = srt })
                    mb_rts_tag
  | RTSRep rts_tag rep <- smrep
  = mkInfoTableContents dflags info{cit_rep = rep} (Just rts_tag)
    
    
    
  | StackRep frame <- smrep
  = do { (prof_lits, prof_data) <- mkProfLits dflags prof
       ; let (srt_label, srt_bitmap) = mkSRTLit dflags info_lbl srt
       ; (liveness_lit, liveness_data) <- mkLivenessBits dflags frame
       ; let
             std_info = mkStdInfoTable dflags prof_lits rts_tag srt_bitmap liveness_lit
             rts_tag | Just tag <- mb_rts_tag = tag
                     | null liveness_data     = rET_SMALL 
                     | otherwise              = rET_BIG   
                                                          
       ; return (prof_data ++ liveness_data, (std_info, srt_label)) }
  | HeapRep _ ptrs nonptrs closure_type <- smrep
  = do { let layout  = packIntsCLit dflags ptrs nonptrs
       ; (prof_lits, prof_data) <- mkProfLits dflags prof
       ; let (srt_label, srt_bitmap) = mkSRTLit dflags info_lbl srt
       ; (mb_srt_field, mb_layout, extra_bits, ct_data)
                                <- mk_pieces closure_type srt_label
       ; let std_info = mkStdInfoTable dflags prof_lits
                                       (mb_rts_tag   `orElse` rtsClosureType smrep)
                                       (mb_srt_field `orElse` srt_bitmap)
                                       (mb_layout    `orElse` layout)
       ; return (prof_data ++ ct_data, (std_info, extra_bits)) }
  where
    mk_pieces :: ClosureTypeInfo -> [CmmLit]
              -> UniqSM ( Maybe CmmLit  
                        , Maybe CmmLit  
                        , [CmmLit]           
                        , [RawCmmDecl])      
    mk_pieces (Constr con_tag con_descr) _no_srt    
      = do { (descr_lit, decl) <- newStringLit con_descr
           ; return ( Just (CmmInt (fromIntegral con_tag)
                                   (halfWordWidth dflags))
                    , Nothing, [descr_lit], [decl]) }
    mk_pieces Thunk srt_label
      = return (Nothing, Nothing, srt_label, [])
    mk_pieces (ThunkSelector offset) _no_srt
      = return (Just (CmmInt 0 (halfWordWidth dflags)),
                Just (mkWordCLit dflags (fromIntegral offset)), [], [])
         
    mk_pieces (Fun arity (ArgSpec fun_type)) srt_label
      = do { let extra_bits = packIntsCLit dflags fun_type arity : srt_label
           ; return (Nothing, Nothing,  extra_bits, []) }
    mk_pieces (Fun arity (ArgGen arg_bits)) srt_label
      = do { (liveness_lit, liveness_data) <- mkLivenessBits dflags arg_bits
           ; let fun_type | null liveness_data = aRG_GEN
                          | otherwise          = aRG_GEN_BIG
                 extra_bits = [ packIntsCLit dflags fun_type arity ]
                           ++ (if inlineSRT dflags then [] else [ srt_lit ])
                           ++ [ liveness_lit, slow_entry ]
           ; return (Nothing, Nothing, extra_bits, liveness_data) }
      where
        slow_entry = CmmLabel (toSlowEntryLbl info_lbl)
        srt_lit = case srt_label of
                    []          -> mkIntCLit dflags 0
                    (lit:_rest) -> ASSERT( null _rest ) lit
    mk_pieces other _ = pprPanic "mk_pieces" (ppr other)
mkInfoTableContents _ _ _ = panic "mkInfoTableContents"   
packIntsCLit :: DynFlags -> Int -> Int -> CmmLit
packIntsCLit dflags a b = packHalfWordsCLit dflags
                           (toStgHalfWord dflags (fromIntegral a))
                           (toStgHalfWord dflags (fromIntegral b))
mkSRTLit :: DynFlags
         -> CLabel
         -> Maybe CLabel
         -> ([CmmLit],    
             CmmLit)      
mkSRTLit dflags info_lbl (Just lbl)
  | inlineSRT dflags
  = ([], CmmLabelDiffOff lbl info_lbl 0 (halfWordWidth dflags))
mkSRTLit dflags _ Nothing    = ([], CmmInt 0 (halfWordWidth dflags))
mkSRTLit dflags _ (Just lbl) = ([CmmLabel lbl], CmmInt 1 (halfWordWidth dflags))
inlineSRT :: DynFlags -> Bool
inlineSRT dflags = platformArch (targetPlatform dflags) == ArchX86_64
  && tablesNextToCode dflags
makeRelativeRefTo :: DynFlags -> CLabel -> CmmLit -> CmmLit
makeRelativeRefTo dflags info_lbl (CmmLabel lbl)
  | tablesNextToCode dflags
  = CmmLabelDiffOff lbl info_lbl 0 (wordWidth dflags)
makeRelativeRefTo dflags info_lbl (CmmLabelOff lbl off)
  | tablesNextToCode dflags
  = CmmLabelDiffOff lbl info_lbl off (wordWidth dflags)
makeRelativeRefTo _ _ lit = lit
mkLivenessBits :: DynFlags -> Liveness -> UniqSM (CmmLit, [RawCmmDecl])
              
              
              
mkLivenessBits dflags liveness
  | n_bits > mAX_SMALL_BITMAP_SIZE dflags 
  = do { uniq <- getUniqueM
       ; let bitmap_lbl = mkBitmapLabel uniq
       ; return (CmmLabel bitmap_lbl,
                 [mkRODataLits bitmap_lbl lits]) }
  | otherwise 
  = return (mkStgWordCLit dflags bitmap_word, [])
  where
    n_bits = length liveness
    bitmap :: Bitmap
    bitmap = mkBitmap dflags liveness
    small_bitmap = case bitmap of
                     []  -> toStgWord dflags 0
                     [b] -> b
                     _   -> panic "mkLiveness"
    bitmap_word = toStgWord dflags (fromIntegral n_bits)
              .|. (small_bitmap `shiftL` bITMAP_BITS_SHIFT dflags)
    lits = mkWordCLit dflags (fromIntegral n_bits)
         : map (mkStgWordCLit dflags) bitmap
      
      
mkStdInfoTable
   :: DynFlags
   -> (CmmLit,CmmLit)   
   -> Int               
   -> CmmLit            
   -> CmmLit            
   -> [CmmLit]
mkStdInfoTable dflags (type_descr, closure_descr) cl_type srt layout_lit
 =      
    prof_info
        
        
 ++ [layout_lit, tag, srt]
 where
    prof_info
        | gopt Opt_SccProfilingOn dflags = [type_descr, closure_descr]
        | otherwise = []
    tag = CmmInt (fromIntegral cl_type) (halfWordWidth dflags)
mkProfLits :: DynFlags -> ProfilingInfo -> UniqSM ((CmmLit,CmmLit), [RawCmmDecl])
mkProfLits dflags NoProfilingInfo       = return ((zeroCLit dflags, zeroCLit dflags), [])
mkProfLits _ (ProfilingInfo td cd)
  = do { (td_lit, td_decl) <- newStringLit td
       ; (cd_lit, cd_decl) <- newStringLit cd
       ; return ((td_lit,cd_lit), [td_decl,cd_decl]) }
newStringLit :: ByteString -> UniqSM (CmmLit, GenCmmDecl CmmStatics info stmt)
newStringLit bytes
  = do { uniq <- getUniqueM
       ; return (mkByteStringCLit (mkStringLitLabel uniq) bytes) }
srtEscape :: DynFlags -> StgHalfWord
srtEscape dflags = toStgHalfWord dflags (-1)
wordAligned :: DynFlags -> CmmExpr -> CmmExpr
wordAligned dflags e
  | gopt Opt_AlignmentSanitisation dflags
  = CmmMachOp (MO_AlignmentCheck (wORD_SIZE dflags) (wordWidth dflags)) [e]
  | otherwise
  = e
closureInfoPtr :: DynFlags -> CmmExpr -> CmmExpr
closureInfoPtr dflags e =
    CmmLoad (wordAligned dflags e) (bWord dflags)
entryCode :: DynFlags -> CmmExpr -> CmmExpr
entryCode dflags e
 | tablesNextToCode dflags = e
 | otherwise               = CmmLoad e (bWord dflags)
getConstrTag :: DynFlags -> CmmExpr -> CmmExpr
getConstrTag dflags closure_ptr
  = CmmMachOp (MO_UU_Conv (halfWordWidth dflags) (wordWidth dflags)) [infoTableConstrTag dflags info_table]
  where
    info_table = infoTable dflags (closureInfoPtr dflags closure_ptr)
cmmGetClosureType :: DynFlags -> CmmExpr -> CmmExpr
cmmGetClosureType dflags closure_ptr
  = CmmMachOp (MO_UU_Conv (halfWordWidth dflags) (wordWidth dflags)) [infoTableClosureType dflags info_table]
  where
    info_table = infoTable dflags (closureInfoPtr dflags closure_ptr)
infoTable :: DynFlags -> CmmExpr -> CmmExpr
infoTable dflags info_ptr
  | tablesNextToCode dflags = cmmOffsetB dflags info_ptr (- stdInfoTableSizeB dflags)
  | otherwise               = cmmOffsetW dflags info_ptr 1 
infoTableConstrTag :: DynFlags -> CmmExpr -> CmmExpr
infoTableConstrTag = infoTableSrtBitmap
infoTableSrtBitmap :: DynFlags -> CmmExpr -> CmmExpr
infoTableSrtBitmap dflags info_tbl
  = CmmLoad (cmmOffsetB dflags info_tbl (stdSrtBitmapOffset dflags)) (bHalfWord dflags)
infoTableClosureType :: DynFlags -> CmmExpr -> CmmExpr
infoTableClosureType dflags info_tbl
  = CmmLoad (cmmOffsetB dflags info_tbl (stdClosureTypeOffset dflags)) (bHalfWord dflags)
infoTablePtrs :: DynFlags -> CmmExpr -> CmmExpr
infoTablePtrs dflags info_tbl
  = CmmLoad (cmmOffsetB dflags info_tbl (stdPtrsOffset dflags)) (bHalfWord dflags)
infoTableNonPtrs :: DynFlags -> CmmExpr -> CmmExpr
infoTableNonPtrs dflags info_tbl
  = CmmLoad (cmmOffsetB dflags info_tbl (stdNonPtrsOffset dflags)) (bHalfWord dflags)
funInfoTable :: DynFlags -> CmmExpr -> CmmExpr
funInfoTable dflags info_ptr
  | tablesNextToCode dflags
  = cmmOffsetB dflags info_ptr (- stdInfoTableSizeB dflags - sIZEOF_StgFunInfoExtraRev dflags)
  | otherwise
  = cmmOffsetW dflags info_ptr (1 + stdInfoTableSizeW dflags)
                                
funInfoArity :: DynFlags -> CmmExpr -> CmmExpr
funInfoArity dflags iptr
  = cmmToWord dflags (cmmLoadIndex dflags rep fun_info (offset `div` rep_bytes))
  where
   fun_info = funInfoTable dflags iptr
   rep = cmmBits (widthFromBytes rep_bytes)
   (rep_bytes, offset)
    | tablesNextToCode dflags = ( pc_REP_StgFunInfoExtraRev_arity pc
                                , oFFSET_StgFunInfoExtraRev_arity dflags )
    | otherwise               = ( pc_REP_StgFunInfoExtraFwd_arity pc
                                , oFFSET_StgFunInfoExtraFwd_arity dflags )
   pc = sPlatformConstants (settings dflags)
stdInfoTableSizeW :: DynFlags -> WordOff
stdInfoTableSizeW dflags
  = fixedInfoTableSizeW
  + if gopt Opt_SccProfilingOn dflags
       then profInfoTableSizeW
       else 0
fixedInfoTableSizeW :: WordOff
fixedInfoTableSizeW = 2 
profInfoTableSizeW :: WordOff
profInfoTableSizeW = 2
maxStdInfoTableSizeW :: WordOff
maxStdInfoTableSizeW =
  1 
  + fixedInfoTableSizeW
  + profInfoTableSizeW
maxRetInfoTableSizeW :: WordOff
maxRetInfoTableSizeW =
  maxStdInfoTableSizeW
  + 1 
stdInfoTableSizeB  :: DynFlags -> ByteOff
stdInfoTableSizeB dflags = stdInfoTableSizeW dflags * wORD_SIZE dflags
stdSrtBitmapOffset :: DynFlags -> ByteOff
stdSrtBitmapOffset dflags = stdInfoTableSizeB dflags - hALF_WORD_SIZE dflags
stdClosureTypeOffset :: DynFlags -> ByteOff
stdClosureTypeOffset dflags = stdInfoTableSizeB dflags - wORD_SIZE dflags
stdPtrsOffset, stdNonPtrsOffset :: DynFlags -> ByteOff
stdPtrsOffset    dflags = stdInfoTableSizeB dflags - 2 * wORD_SIZE dflags
stdNonPtrsOffset dflags = stdInfoTableSizeB dflags - 2 * wORD_SIZE dflags + hALF_WORD_SIZE dflags
conInfoTableSizeB :: DynFlags -> Int
conInfoTableSizeB dflags = stdInfoTableSizeB dflags + wORD_SIZE dflags