-----------------------------------------------------------------------------
--
-- Building info tables.
--
-- (c) The University of Glasgow 2004
--
-----------------------------------------------------------------------------

module CgInfoTbls (
	emitClosureCodeAndInfoTable,
	emitInfoTableAndCode,
	dataConTagZ,
	getSRTInfo,
	emitDirectReturnTarget, emitAlgReturnTarget,
	emitDirectReturnInstr, emitVectoredReturnInstr,
	mkRetInfoTable,
	mkStdInfoTable,
	stdInfoTableSizeB,
	mkFunGenInfoExtraBits,
	entryCode, closureInfoPtr,
	getConstrTag,
	infoTable, infoTableClosureType,
	infoTablePtrs, infoTableNonPtrs,
	funInfoTable,
	retVec
  ) where


#include "HsVersions.h"

import ClosureInfo	( ClosureInfo, closureTypeDescr, closureName,
			  infoTableLabelFromCI, Liveness,
			  closureValDescr, closureSRT, closureSMRep,
			  closurePtrsSize, closureNonHdrSize, closureFunInfo,
			  C_SRT(..), needsSRT, isConstrClosure_maybe,
			  ArgDescr(..) )
import SMRep		( StgHalfWord, hALF_WORD_SIZE_IN_BITS, hALF_WORD_SIZE,
			  WordOff, ByteOff,
			  smRepClosureTypeInt, tablesNextToCode,
			  rET_BIG, rET_SMALL, rET_VEC_BIG, rET_VEC_SMALL )
import CgBindery	( getLiveStackSlots )
import CgCallConv	( isBigLiveness, mkLivenessCLit, buildContLiveness,
			  argDescrType, getSequelAmode,
			  CtrlReturnConvention(..) )
import CgUtils		( mkStringCLit, packHalfWordsCLit, mkWordCLit, 
			  cmmOffsetB, cmmOffsetExprW, cmmLabelOffW, cmmOffsetW,
			  emitDataLits, emitRODataLits, emitSwitch, cmmNegate,
			  newTemp )
import CgMonad

import CmmUtils		( mkIntCLit, zeroCLit )
import Cmm		( CmmStmt(..), CmmExpr(..), CmmLit(..), LocalReg,
			  CmmBasicBlock, nodeReg )
import MachOp		( MachOp(..), wordRep, halfWordRep )
import CLabel
import StgSyn		( SRT(..) )
import Name		( Name )
import DataCon		( DataCon, dataConTag, fIRST_TAG )
import Unique		( Uniquable(..) )
import CmdLineOpts	( opt_SccProfilingOn, DynFlags(..), HscTarget(..) )
import ListSetOps	( assocDefault )
import Maybes		( isJust )
import Constants	( wORD_SIZE, sIZEOF_StgFunInfoExtraRev )
import Outputable


-------------------------------------------------------------------------
--
--	Generating the info table and code for a closure
--
-------------------------------------------------------------------------

-- Here we make a concrete info table, represented as a list of CmmAddr
-- (it can't be simply a list of Word, because the SRT field is
-- represented by a label+offset expression).

-- With tablesNextToCode, the layout is
--	<reversed variable part>
--	<normal forward StgInfoTable, but without 
--		an entry point at the front>
--	<code>
--
-- Without tablesNextToCode, the layout of an info table is
--	<entry label>
--	<normal forward rest of StgInfoTable>
--	<forward variable part>
--
--	See includes/InfoTables.h

emitClosureCodeAndInfoTable :: ClosureInfo -> [LocalReg] -> CgStmts -> Code
emitClosureCodeAndInfoTable cl_info args body
 = do	{ ty_descr_lit <- 
		if opt_SccProfilingOn 
		   then mkStringCLit (closureTypeDescr cl_info)
		   else return (mkIntCLit 0)
  	; cl_descr_lit <- 
		if opt_SccProfilingOn 
		   then mkStringCLit cl_descr_string
		   else return (mkIntCLit 0)
	; let std_info = mkStdInfoTable ty_descr_lit cl_descr_lit 
					cl_type srt_len layout_lit

	; blks <- cgStmtsToBlocks body
	; emitInfoTableAndCode info_lbl std_info extra_bits args blks }
  where
    info_lbl  = infoTableLabelFromCI cl_info

    cl_descr_string = closureValDescr cl_info
    cl_type = smRepClosureTypeInt (closureSMRep cl_info)

    srt = closureSRT cl_info	     
    needs_srt = needsSRT srt

    mb_con = isConstrClosure_maybe  cl_info
    is_con = isJust mb_con

    (srt_label,srt_len)
	= case mb_con of
	    Just con -> -- Constructors don't have an SRT
			-- We keep the *zero-indexed* tag in the srt_len
			-- field of the info table. 
			(mkIntCLit 0, fromIntegral (dataConTagZ con)) 

	    Nothing  -> -- Not a constructor
			srtLabelAndLength srt info_lbl

    ptrs       = closurePtrsSize cl_info
    nptrs      = size - ptrs
    size       = closureNonHdrSize cl_info
    layout_lit = packHalfWordsCLit ptrs nptrs

    extra_bits
	| is_fun    = fun_extra_bits
	| is_con    = []
	| needs_srt = [srt_label]
 	| otherwise = []

    maybe_fun_stuff = closureFunInfo cl_info
    is_fun = isJust maybe_fun_stuff
    (Just (arity, arg_descr)) = maybe_fun_stuff

    fun_extra_bits
	| ArgGen liveness <- arg_descr
	= [ fun_amode,
	    srt_label,
	    makeRelativeRefTo info_lbl $ mkLivenessCLit liveness, 
	    slow_entry ]
	| needs_srt = [fun_amode, srt_label]
	| otherwise = [fun_amode]

    slow_entry = makeRelativeRefTo info_lbl (CmmLabel slow_entry_label)
    slow_entry_label = mkSlowEntryLabel (closureName cl_info)

    fun_amode = packHalfWordsCLit fun_type arity
    fun_type  = argDescrType arg_descr

-- We keep the *zero-indexed* tag in the srt_len field of the info
-- table of a data constructor.
dataConTagZ :: DataCon -> ConTagZ
dataConTagZ con = dataConTag con - fIRST_TAG

-- A low-level way to generate the variable part of a fun-style info table.
-- (must match fun_extra_bits above).  Used by the C-- parser.
mkFunGenInfoExtraBits :: Int -> Int -> CmmLit -> CmmLit -> CmmLit -> [CmmLit]
mkFunGenInfoExtraBits fun_type arity srt_label liveness slow_entry
  = [ packHalfWordsCLit fun_type arity,
      srt_label,
      liveness,
      slow_entry ]

-------------------------------------------------------------------------
--
--	Generating the info table and code for a return point
--
-------------------------------------------------------------------------

--	Here's the layout of a return-point info table
--
-- Tables next to code:
--
--			<reversed vector table>
--			<srt slot>
--			<standard info table>
--  	ret-addr -->	<entry code (if any)>
--
-- Not tables-next-to-code:
--
--	ret-addr -->	<ptr to entry code>
--			<standard info table>
--			<srt slot>
--			<forward vector table>
--
-- * The vector table is only present for vectored returns
--
-- * The SRT slot is only there if either
--	(a) there is SRT info to record, OR
--	(b) if the return is vectored
--   The latter (b) is necessary so that the vector is in a
--   predictable place

vectorSlot :: CmmExpr -> CmmExpr -> CmmExpr
-- Get the vector slot from the info pointer
vectorSlot info_amode zero_indexed_tag
  | tablesNextToCode 
  = cmmOffsetExprW (cmmOffsetW info_amode (- (stdInfoTableSizeW + 2)))
		   (cmmNegate zero_indexed_tag)
	-- The "2" is one for the SRT slot, and one more 
	-- to get to the first word of the vector

  | otherwise 
  = cmmOffsetExprW (cmmOffsetW info_amode (stdInfoTableSizeW + 2))
		   zero_indexed_tag
	-- The "2" is one for the entry-code slot and one for the SRT slot

retVec :: CmmExpr -> CmmExpr -> CmmExpr
-- Get a return vector from the info pointer
retVec info_amode zero_indexed_tag
  = let slot = vectorSlot info_amode zero_indexed_tag
        tableEntry = CmmLoad slot wordRep
    in if tablesNextToCode
           then CmmMachOp (MO_Add wordRep) [tableEntry, info_amode]
           else tableEntry
           
emitReturnTarget
   :: Name
   -> CgStmts			-- The direct-return code (if any)
				--    	(empty for vectored returns)
   -> [CmmLit]			-- Vector of return points 
				-- 	(empty for non-vectored returns)
   -> SRT
   -> FCode CLabel
emitReturnTarget name stmts vector srt
  = do	{ live_slots <- getLiveStackSlots
	; liveness   <- buildContLiveness name live_slots
	; srt_info   <- getSRTInfo name srt

	; let
	      cl_type = case (null vector, isBigLiveness liveness) of
	 		 (True,  True)  -> rET_BIG
 			 (True,  False) -> rET_SMALL
 			 (False, True)  -> rET_VEC_BIG
	 		 (False, False) -> rET_VEC_SMALL
 
	      (std_info, extra_bits) = 
		   mkRetInfoTable info_lbl liveness srt_info cl_type vector

	; blks <- cgStmtsToBlocks stmts
	; emitInfoTableAndCode info_lbl std_info extra_bits args blks
	; return info_lbl }
  where
    args = trace "emitReturnTarget: missing args" []
    uniq      = getUnique name
    info_lbl  = mkReturnInfoLabel uniq


mkRetInfoTable
  :: CLabel             -- info label
  -> Liveness		-- liveness
  -> C_SRT		-- SRT Info
  -> Int		-- type (eg. rET_SMALL)
  -> [CmmLit]		-- vector
  -> ([CmmLit],[CmmLit])
mkRetInfoTable info_lbl liveness srt_info cl_type vector
  =  (std_info, extra_bits)
  where
	(srt_label, srt_len) = srtLabelAndLength srt_info info_lbl
 
	srt_slot | need_srt  = [srt_label]
	         | otherwise = []

	need_srt = needsSRT srt_info || not (null vector)
		-- If there's a vector table then we must allocate
		-- an SRT slot, so that the vector table is at a 
		-- known offset from the info pointer
 
	liveness_lit = makeRelativeRefTo info_lbl $ mkLivenessCLit liveness
	std_info = mkStdInfoTable zeroCLit zeroCLit cl_type srt_len liveness_lit
        extra_bits = srt_slot ++ map (makeRelativeRefTo info_lbl) vector


emitDirectReturnTarget
   :: Name
   -> CgStmts		-- The direct-return code
   -> SRT
   -> FCode CLabel
emitDirectReturnTarget name code srt
  = emitReturnTarget name code [] srt

emitAlgReturnTarget
	:: Name				-- Just for its unique
	-> [(ConTagZ, CgStmts)]		-- Tagged branches
	-> Maybe CgStmts		-- Default branch (if any)
	-> SRT				-- Continuation's SRT
	-> CtrlReturnConvention
	-> FCode (CLabel, SemiTaggingStuff)

emitAlgReturnTarget name branches mb_deflt srt ret_conv
  = case ret_conv of
      UnvectoredReturn fam_sz -> do	
	{ blks <- getCgStmts $
		    emitSwitch tag_expr branches mb_deflt 0 (fam_sz - 1)
		-- NB: tag_expr is zero-based
	; lbl <- emitDirectReturnTarget name blks srt 
	; return (lbl, Nothing) }
		-- Nothing: the internal branches in the switch don't have
		-- global labels, so we can't use them at the 'call site'

      VectoredReturn fam_sz -> do
	{ let tagged_lbls = zip (map fst branches) $
	                    map (CmmLabel . mkAltLabel uniq . fst) branches
	      deflt_lbl | isJust mb_deflt = CmmLabel $ mkDefaultLabel uniq
	                | otherwise       = mkIntCLit 0
	; let vector = [ assocDefault deflt_lbl tagged_lbls i 
		       | i <- [0..fam_sz-1]]
	; lbl <- emitReturnTarget name noCgStmts vector srt 
	; mapFCs emit_alt branches
	; emit_deflt mb_deflt
	; return (lbl, Just (tagged_lbls, deflt_lbl)) }
  where
    uniq = getUnique name 
    tag_expr = getConstrTag (CmmReg nodeReg)

    emit_alt :: (Int, CgStmts) -> FCode (Int, CmmLit)
	-- Emit the code for the alternative as a top-level
	-- code block returning a label for it
    emit_alt (tag, stmts) = do	 { let lbl = mkAltLabel uniq tag
				 ; blks <- cgStmtsToBlocks stmts
				 ; emitProc [] lbl [] blks
				 ; return (tag, CmmLabel lbl) }

    emit_deflt (Just stmts) = do { let lbl = mkDefaultLabel uniq
				 ; blks <- cgStmtsToBlocks stmts
				 ; emitProc [] lbl [] blks
				 ; return (CmmLabel lbl) }
    emit_deflt Nothing = return (mkIntCLit 0)
		-- Nothing case: the simplifier might have eliminated a case
		-- 		 so we may have e.g. case xs of 
		--					 [] -> e
		-- In that situation the default should never be taken, 
		-- so we just use a NULL pointer

--------------------------------
emitDirectReturnInstr :: Code
emitDirectReturnInstr 
  = do 	{ info_amode <- getSequelAmode
	; stmtC (CmmJump (entryCode info_amode) []) }

emitVectoredReturnInstr :: CmmExpr	-- *Zero-indexed* constructor tag
			-> Code
emitVectoredReturnInstr zero_indexed_tag
  = do	{ info_amode <- getSequelAmode
		-- HACK! assign info_amode to a temp, because retVec
		-- uses it twice and the NCG doesn't have any CSE yet.
		-- Only do this for the NCG, because gcc is too stupid
		-- to optimise away the extra tmp (grrr).
	; dflags <- getDynFlags
	; x <- if hscTarget dflags == HscAsm
		   then do z <- newTemp wordRep
			   stmtC (CmmAssign z info_amode)
			   return (CmmReg z)
		   else
			return info_amode
	; let target = retVec x zero_indexed_tag
	; stmtC (CmmJump target []) }


-------------------------------------------------------------------------
--
--	Generating a standard info table
--
-------------------------------------------------------------------------

-- The standard bits of an info table.  This part of the info table
-- corresponds to the StgInfoTable type defined in InfoTables.h.
--
-- Its shape varies with ticky/profiling/tables next to code etc
-- so we can't use constant offsets from Constants

mkStdInfoTable
   :: CmmLit		-- closure type descr (profiling)
   -> CmmLit		-- closure descr (profiling)
   -> Int		-- closure type
   -> StgHalfWord	-- SRT length
   -> CmmLit		-- layout field
   -> [CmmLit]

mkStdInfoTable type_descr closure_descr cl_type srt_len layout_lit
 = 	-- Parallel revertible-black hole field
    prof_info
	-- Ticky info (none at present)
	-- Debug info (none at present)
 ++ [layout_lit, type_lit]

 where  
    prof_info 
	| opt_SccProfilingOn = [type_descr, closure_descr]
	| otherwise	     = []

    type_lit = packHalfWordsCLit cl_type srt_len
	
stdInfoTableSizeW :: WordOff
-- The size of a standard info table varies with profiling/ticky etc,
-- so we can't get it from Constants
-- It must vary in sync with mkStdInfoTable
stdInfoTableSizeW
  = size_fixed + size_prof
  where
    size_fixed = 2	-- layout, type
    size_prof | opt_SccProfilingOn = 2
	      | otherwise	   = 0

stdInfoTableSizeB = stdInfoTableSizeW * wORD_SIZE :: ByteOff

stdSrtBitmapOffset :: ByteOff
-- Byte offset of the SRT bitmap half-word which is 
-- in the *higher-addressed* part of the type_lit
stdSrtBitmapOffset = stdInfoTableSizeB - hALF_WORD_SIZE

stdClosureTypeOffset :: ByteOff
-- Byte offset of the closure type half-word 
stdClosureTypeOffset = stdInfoTableSizeB - wORD_SIZE

stdPtrsOffset, stdNonPtrsOffset :: ByteOff
stdPtrsOffset    = stdInfoTableSizeB - 2*wORD_SIZE
stdNonPtrsOffset = stdInfoTableSizeB - 2*wORD_SIZE + hALF_WORD_SIZE

-------------------------------------------------------------------------
--
--	Accessing fields of an info table
--
-------------------------------------------------------------------------

closureInfoPtr :: CmmExpr -> CmmExpr
-- Takes a closure pointer and returns the info table pointer
closureInfoPtr e = CmmLoad e wordRep

entryCode :: CmmExpr -> CmmExpr
-- Takes an info pointer (the first word of a closure)
-- and returns its entry code
entryCode e | tablesNextToCode = e
	    | otherwise	       = CmmLoad e wordRep

getConstrTag :: CmmExpr -> CmmExpr
-- Takes a closure pointer, and return the *zero-indexed*
-- constructor tag obtained from the info table
-- This lives in the SRT field of the info table
-- (constructors don't need SRTs).
getConstrTag closure_ptr 
  = CmmMachOp (MO_U_Conv halfWordRep wordRep) [infoTableConstrTag info_table]
  where
    info_table = infoTable (closureInfoPtr closure_ptr)

infoTable :: CmmExpr -> CmmExpr
-- Takes an info pointer (the first word of a closure)
-- and returns a pointer to the first word of the standard-form
-- info table, excluding the entry-code word (if present)
infoTable info_ptr
  | tablesNextToCode = cmmOffsetB info_ptr (- stdInfoTableSizeB)
  | otherwise	     = cmmOffsetW info_ptr 1	-- Past the entry code pointer

infoTableConstrTag :: CmmExpr -> CmmExpr
-- Takes an info table pointer (from infoTable) and returns the constr tag
-- field of the info table (same as the srt_bitmap field)
infoTableConstrTag = infoTableSrtBitmap

infoTableSrtBitmap :: CmmExpr -> CmmExpr
-- Takes an info table pointer (from infoTable) and returns the srt_bitmap
-- field of the info table
infoTableSrtBitmap info_tbl
  = CmmLoad (cmmOffsetB info_tbl stdSrtBitmapOffset) halfWordRep

infoTableClosureType :: CmmExpr -> CmmExpr
-- Takes an info table pointer (from infoTable) and returns the closure type
-- field of the info table.
infoTableClosureType info_tbl 
  = CmmLoad (cmmOffsetB info_tbl stdClosureTypeOffset) halfWordRep

infoTablePtrs :: CmmExpr -> CmmExpr
infoTablePtrs info_tbl 
  = CmmLoad (cmmOffsetB info_tbl stdPtrsOffset) halfWordRep

infoTableNonPtrs :: CmmExpr -> CmmExpr
infoTableNonPtrs info_tbl 
  = CmmLoad (cmmOffsetB info_tbl stdNonPtrsOffset) halfWordRep

funInfoTable :: CmmExpr -> CmmExpr
-- Takes the info pointer of a function,
-- and returns a pointer to the first word of the StgFunInfoExtra struct
-- in the info table.
funInfoTable info_ptr
  | tablesNextToCode
  = cmmOffsetB info_ptr (- stdInfoTableSizeB - sIZEOF_StgFunInfoExtraRev)
  | otherwise
  = cmmOffsetW info_ptr (1 + stdInfoTableSizeW)
				-- Past the entry code pointer

-------------------------------------------------------------------------
--
--	Emit the code for a closure (or return address)
--	and its associated info table
--
-------------------------------------------------------------------------

-- The complication here concerns whether or not we can
-- put the info table next to the code

emitInfoTableAndCode 
	:: CLabel 		-- Label of info table
	-> [CmmLit]		-- ...its invariant part
	-> [CmmLit] 		-- ...and its variant part
	-> [LocalReg]		-- ...args
	-> [CmmBasicBlock]	-- ...and body
	-> Code

emitInfoTableAndCode info_lbl std_info extra_bits args blocks
  | tablesNextToCode 	-- Reverse the extra_bits; and emit the top-level proc
  = emitProc (reverse extra_bits ++ std_info) 
	     entry_lbl args blocks
	-- NB: the info_lbl is discarded

  | null blocks -- No actual code; only the info table is significant
  =		-- Use a zero place-holder in place of the 
		-- entry-label in the info table
    emitRODataLits info_lbl (zeroCLit : std_info ++ extra_bits)

  | otherwise	-- Separately emit info table (with the function entry 
  =		-- point as first entry) and the entry code 
    do	{ emitDataLits info_lbl (CmmLabel entry_lbl : std_info ++ extra_bits)
	; emitProc [] entry_lbl args blocks }

  where
	entry_lbl = infoLblToEntryLbl info_lbl

-------------------------------------------------------------------------
--
--	Static reference tables
--
-------------------------------------------------------------------------

-- There is just one SRT for each top level binding; all the nested
-- bindings use sub-sections of this SRT.  The label is passed down to
-- the nested bindings via the monad.

getSRTInfo :: Name -> SRT -> FCode C_SRT
getSRTInfo id NoSRT = return NoC_SRT
getSRTInfo id (SRT off len bmp)
  | len > hALF_WORD_SIZE_IN_BITS || bmp == [fromIntegral srt_escape]
  = do	{ srt_lbl <- getSRTLabel
	; let srt_desc_lbl = mkSRTDescLabel id
	; emitRODataLits srt_desc_lbl
		   ( cmmLabelOffW srt_lbl off
		   : mkWordCLit (fromIntegral len)
		   : map mkWordCLit bmp)
	; return (C_SRT srt_desc_lbl 0 srt_escape) }

  | otherwise 
  = do	{ srt_lbl <- getSRTLabel
	; return (C_SRT srt_lbl off (fromIntegral (head bmp))) }
		-- The fromIntegral converts to StgHalfWord

srt_escape = (-1) :: StgHalfWord

srtLabelAndLength :: C_SRT -> CLabel -> (CmmLit, StgHalfWord)
srtLabelAndLength NoC_SRT _		
  = (zeroCLit, 0)
srtLabelAndLength (C_SRT lbl off bitmap) info_lbl
  = (makeRelativeRefTo info_lbl $ cmmLabelOffW lbl off, bitmap)

-------------------------------------------------------------------------
--
--	Position independent code
--
-------------------------------------------------------------------------
-- In order to support position independent code, we mustn't put absolute
-- references into read-only space. Info tables in the tablesNextToCode
-- case must be in .text, which is read-only, so we doctor the CmmLits
-- to use relative offsets instead.

-- Note that this is done even when the -fPIC flag is not specified,
-- as we want to keep binary compatibility between PIC and non-PIC.

makeRelativeRefTo :: CLabel -> CmmLit -> CmmLit
        
makeRelativeRefTo info_lbl (CmmLabel lbl)
  | tablesNextToCode
  = CmmLabelDiffOff lbl info_lbl 0
makeRelativeRefTo info_lbl (CmmLabelOff lbl off)
  | tablesNextToCode
  = CmmLabelDiffOff lbl info_lbl off
makeRelativeRefTo _ lit = lit
