summaryrefslogtreecommitdiff
path: root/compiler/codeGen/CgInfoTbls.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/codeGen/CgInfoTbls.hs')
-rw-r--r--compiler/codeGen/CgInfoTbls.hs591
1 files changed, 591 insertions, 0 deletions
diff --git a/compiler/codeGen/CgInfoTbls.hs b/compiler/codeGen/CgInfoTbls.hs
new file mode 100644
index 0000000000..b769950d87
--- /dev/null
+++ b/compiler/codeGen/CgInfoTbls.hs
@@ -0,0 +1,591 @@
+-----------------------------------------------------------------------------
+--
+-- 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 DynFlags ( DynFlags(..), HscTarget(..) )
+import StaticFlags ( opt_SccProfilingOn )
+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