    {-# LANGUAGE BangPatterns, DeriveDataTypeable #-}
    module CostCentre (
            CostCentre(..), CcName, IsCafCC(..),
                    -- All abstract except to friend: ParseIface.y

            CostCentreStack,
            CollectedCCs,
            noCCS, currentCCS, dontCareCCS,
            noCCSAttached, isCurrentCCS,
            maybeSingletonCCS,

            mkUserCC, mkAutoCC, mkAllCafsCC,
            mkSingletonCCS,
            isCafCCS, isCafCC, isSccCountCC, sccAbleCC, ccFromThisModule,

            pprCostCentreCore,
            costCentreUserName, costCentreUserNameFS,
            costCentreSrcSpan,

            cmpCostCentre   -- used for removing dups in a list
        ) where

    import Binary
    import Var
    import Name
    import Module
    import Unique
    import Outputable
    import FastTypes
    import SrcLoc
    import FastString
    import Util

    import Data.Data

    -----------------------------------------------------------------------------
    -- Cost Centres

    -- | A Cost Centre is a single @{-# SCC #-}@ annotation.

    data CostCentre
      = NormalCC {
                    cc_key  :: {-# UNPACK #-} !Int,
                     -- ^ Two cost centres may have the same name and
                     -- module but different SrcSpans, so we need a way to
                     -- distinguish them easily and give them different
                     -- object-code labels.  So every CostCentre has a
                     -- Unique that is distinct from every other
                     -- CostCentre in the same module.
                     --
                     -- XXX: should really be using Unique here, but we
                     -- need to derive Data below and there's no Data
                     -- instance for Unique.
                    cc_name :: CcName,      -- ^ Name of the cost centre itself
                    cc_mod  :: Module,      -- ^ Name of module defining this CC.
                    cc_loc  :: SrcSpan,
                    cc_is_caf  :: IsCafCC   -- see below
        }

      | AllCafsCC {
                    cc_mod  :: Module,      -- Name of module defining this CC.
                    cc_loc  :: SrcSpan
        }
      deriving (Data, Typeable)

    type CcName = FastString

    data IsCafCC = NotCafCC | CafCC
      deriving (Eq, Ord, Data, Typeable)

    instance Eq CostCentre where
            c1 == c2 = case c1 `cmpCostCentre` c2 of { EQ -> True; _ -> False }

    instance Ord CostCentre where
            compare = cmpCostCentre

    cmpCostCentre :: CostCentre -> CostCentre -> Ordering

    cmpCostCentre (AllCafsCC  {cc_mod = m1}) (AllCafsCC  {cc_mod = m2})
      = m1 `compare` m2

    cmpCostCentre NormalCC {cc_key = n1, cc_mod =  m1}
                  NormalCC {cc_key = n2, cc_mod =  m2}
        -- first key is module name, then the integer key
      = (m1 `compare` m2) `thenCmp` (n1 `compare` n2)

    cmpCostCentre other_1 other_2
      = let
            !tag1 = tag_CC other_1
            !tag2 = tag_CC other_2
        in
        if tag1 <# tag2 then LT else GT
      where
        tag_CC (NormalCC   {}) = _ILIT(0)
        tag_CC (AllCafsCC  {}) = _ILIT(1)

    -----------------------------------------------------------------------------
    -- Predicates on CostCentre

    isCafCC :: CostCentre -> Bool
    isCafCC (AllCafsCC {})                 = True
    isCafCC (NormalCC {cc_is_caf = CafCC}) = True
    isCafCC _                              = False

    -- | Is this a cost-centre which records scc counts
    isSccCountCC :: CostCentre -> Bool
    isSccCountCC cc | isCafCC cc  = False
                    | otherwise   = True

    -- | Is this a cost-centre which can be sccd ?
    sccAbleCC :: CostCentre -> Bool
    sccAbleCC cc | isCafCC cc = False
                 | otherwise  = True

    ccFromThisModule :: CostCentre -> Module -> Bool
    ccFromThisModule cc m = cc_mod cc == m

    -----------------------------------------------------------------------------
    -- Building cost centres

    mkUserCC :: FastString -> Module -> SrcSpan -> Unique -> CostCentre
    mkUserCC cc_name mod loc key
      = NormalCC { cc_key = getKey key, cc_name = cc_name, cc_mod =  mod, cc_loc = loc,
                   cc_is_caf = NotCafCC {-might be changed-}
        }

    mkAutoCC :: Id -> Module -> IsCafCC -> CostCentre
    mkAutoCC id mod is_caf
      = NormalCC { cc_key = getKey (getUnique id),
                   cc_name = str, cc_mod =  mod,
                   cc_loc = nameSrcSpan (getName id),
                   cc_is_caf = is_caf
        }
      where
            name = getName id
            -- beware: only external names are guaranteed to have unique
            -- Occnames.  If the name is not external, we must append its
            -- Unique.
            -- See bug #249, tests prof001, prof002,  also #2411
            str | isExternalName name = occNameFS (getOccName id)
                | otherwise           = occNameFS (getOccName id)
                                        `appendFS`
                                        mkFastString ('_' : show (getUnique name))
    mkAllCafsCC :: Module -> SrcSpan -> CostCentre
    mkAllCafsCC m loc = AllCafsCC { cc_mod = m, cc_loc = loc }

    -----------------------------------------------------------------------------
    -- Cost Centre Stacks

    -- | A Cost Centre Stack is something that can be attached to a closure.
    -- This is either:
    --
    --      * the current cost centre stack (CCCS)
    --      * a pre-defined cost centre stack (there are several
    --        pre-defined CCSs, see below).

    data CostCentreStack
      = NoCCS

      | CurrentCCS          -- Pinned on a let(rec)-bound
                            -- thunk/function/constructor, this says that the
                            -- cost centre to be attached to the object, when it
                            -- is allocated, is whatever is in the
                            -- current-cost-centre-stack register.

      | DontCareCCS         -- We need a CCS to stick in static closures
                            -- (for data), but we *don't* expect them to
                            -- accumulate any costs.  But we still need
                            -- the placeholder.  This CCS is it.

      | SingletonCCS CostCentre

      deriving (Eq, Ord)    -- needed for Ord on CLabel

    -- synonym for triple which describes the cost centre info in the generated
    -- code for a module.
    type CollectedCCs
      = ( [CostCentre]       -- local cost-centres that need to be decl'd
        , [CostCentre]       -- "extern" cost-centres
        , [CostCentreStack]  -- pre-defined "singleton" cost centre stacks
        )

    noCCS, currentCCS, dontCareCCS :: CostCentreStack

    noCCS                   = NoCCS
    currentCCS              = CurrentCCS
    dontCareCCS             = DontCareCCS

    -----------------------------------------------------------------------------
    -- Predicates on Cost-Centre Stacks

    noCCSAttached :: CostCentreStack -> Bool
    noCCSAttached NoCCS                     = True
    noCCSAttached _                         = False

    isCurrentCCS :: CostCentreStack -> Bool
    isCurrentCCS CurrentCCS                 = True
    isCurrentCCS _                          = False

    isCafCCS :: CostCentreStack -> Bool
    isCafCCS (SingletonCCS cc)              = isCafCC cc
    isCafCCS _                              = False

    maybeSingletonCCS :: CostCentreStack -> Maybe CostCentre
    maybeSingletonCCS (SingletonCCS cc)     = Just cc
    maybeSingletonCCS _                     = Nothing

    mkSingletonCCS :: CostCentre -> CostCentreStack
    mkSingletonCCS cc = SingletonCCS cc

    -----------------------------------------------------------------------------
    -- Printing Cost Centre Stacks.

    -- The outputable instance for CostCentreStack prints the CCS as a C
    -- expression.

    instance Outputable CostCentreStack where
      ppr NoCCS             = ptext (sLit "NO_CCS")
      ppr CurrentCCS        = ptext (sLit "CCCS")
      ppr DontCareCCS       = ptext (sLit "CCS_DONT_CARE")
      ppr (SingletonCCS cc) = ppr cc <> ptext (sLit "_ccs")

    -----------------------------------------------------------------------------
    -- Printing Cost Centres
    --
    -- There are several different ways in which we might want to print a
    -- cost centre:
    --
    --      - the name of the cost centre, for profiling output (a C string)
    --      - the label, i.e. C label for cost centre in .hc file.
    --      - the debugging name, for output in -ddump things
    --      - the interface name, for printing in _scc_ exprs in iface files.
    --
    -- The last 3 are derived from costCentreStr below.  The first is given
    -- by costCentreName.

    instance Outputable CostCentre where
      ppr cc = getPprStyle $ \ sty ->
               if codeStyle sty
               then ppCostCentreLbl cc
               else text (costCentreUserName cc)

    -- Printing in Core
    pprCostCentreCore :: CostCentre -> SDoc
    pprCostCentreCore (AllCafsCC {cc_mod = m})
      = text "__sccC" <+> braces (ppr m)
    pprCostCentreCore (NormalCC {cc_key = key, cc_name = n, cc_mod = m, cc_loc = loc,
                                 cc_is_caf = caf})
      = text "__scc" <+> braces (hsep [
            ppr m <> char '.' <> ftext n,
            ifPprDebug (ppr key),
            pp_caf caf,
            ifPprDebug (ppr loc)
        ])

    pp_caf :: IsCafCC -> SDoc
    pp_caf CafCC = text "__C"
    pp_caf _     = empty

    -- Printing as a C label
    ppCostCentreLbl :: CostCentre -> SDoc
    ppCostCentreLbl (AllCafsCC  {cc_mod = m}) = ppr m <> text "_CAFs_cc"
    ppCostCentreLbl (NormalCC {cc_key = k, cc_name = n, cc_mod = m,
                               cc_is_caf = is_caf})
      = ppr m <> char '_' <> ztext (zEncodeFS n) <> char '_' <>
            case is_caf of { CafCC -> ptext (sLit "CAF"); _ -> ppr (mkUniqueGrimily k)} <> text "_cc"

    -- This is the name to go in the user-displayed string,
    -- recorded in the cost centre declaration
    costCentreUserName :: CostCentre -> String
    costCentreUserName = unpackFS . costCentreUserNameFS

    costCentreUserNameFS :: CostCentre -> FastString
    costCentreUserNameFS (AllCafsCC {})  = mkFastString "CAF"
    costCentreUserNameFS (NormalCC {cc_name = name, cc_is_caf = is_caf})
      =  case is_caf of
          CafCC -> mkFastString "CAF:" `appendFS` name
          _     -> name

    costCentreSrcSpan :: CostCentre -> SrcSpan
    costCentreSrcSpan = cc_loc

    instance Binary IsCafCC where
        put_ bh CafCC = do
                putByte bh 0
        put_ bh NotCafCC = do
                putByte bh 1
        get bh = do
                h <- getByte bh
                case h of
                  0 -> do return CafCC
                  _ -> do return NotCafCC

    instance Binary CostCentre where
        put_ bh (NormalCC aa ab ac _ad ae) = do
                putByte bh 0
                put_ bh aa
                put_ bh ab
                put_ bh ac
                put_ bh ae
        put_ bh (AllCafsCC ae _af) = do
                putByte bh 1
                put_ bh ae
        get bh = do
                h <- getByte bh
                case h of
                  0 -> do aa <- get bh
                          ab <- get bh
                          ac <- get bh
                          ae <- get bh
                          return (NormalCC aa ab ac noSrcSpan ae)
                  _ -> do ae <- get bh
                          return (AllCafsCC ae noSrcSpan)

        -- We ignore the SrcSpans in CostCentres when we serialise them,
        -- and set the SrcSpans to noSrcSpan when deserialising.  This is
        -- ok, because we only need the SrcSpan when declaring the
        -- CostCentre in the original module, it is not used by importing
        -- modules.