{-# LANGUAGE BangPatterns, CPP #-} ----------------------------------------------------------------------------- -- -- Code generation for ticky-ticky profiling -- -- (c) The University of Glasgow 2004-2006 -- ----------------------------------------------------------------------------- {- OVERVIEW: ticky ticky profiling Please see http://ghc.haskell.org/trac/ghc/wiki/Debugging/TickyTicky and also edit it and the rest of this comment to keep them up-to-date if you change ticky-ticky. Thanks! *** All allocation ticky numbers are in bytes. *** Some of the relevant source files: ***not necessarily an exhaustive list*** * some codeGen/ modules import this one * this module imports cmm/CLabel.hs to manage labels * cmm/CmmParse.y expands some macros using generators defined in this module * includes/stg/Ticky.h declares all of the global counters * includes/rts/Ticky.h declares the C data type for an STG-declaration's counters * some macros defined in includes/Cmm.h (and used within the RTS's CMM code) update the global ticky counters * at the end of execution rts/Ticky.c generates the final report +RTS -r -RTS The rts/Ticky.c function that generates the report includes an STG-declaration's ticky counters if * that declaration was entered, or * it was allocated (if -ticky-allocd) On either of those events, the counter is "registered" by adding it to a linked list; cf the CMM generated by registerTickyCtr. Ticky-ticky profiling has evolved over many years. Many of the counters from its most sophisticated days are no longer active/accurate. As the RTS has changed, sometimes the ticky code for relevant counters was not accordingly updated. Unfortunately, neither were the comments. As of March 2013, there still exist deprecated code and comments in the code generator as well as the RTS because: * I don't know what is out-of-date versus merely commented out for momentary convenience, and * someone else might know how to repair it! -} module StgCmmTicky ( withNewTickyCounterFun, withNewTickyCounterLNE, withNewTickyCounterThunk, withNewTickyCounterStdThunk, withNewTickyCounterCon, tickyDynAlloc, tickyAllocHeap, tickyAllocPrim, tickyAllocThunk, tickyAllocPAP, tickyHeapCheck, tickyStackCheck, tickyUnknownCall, tickyDirectCall, tickyPushUpdateFrame, tickyUpdateFrameOmitted, tickyEnterDynCon, tickyEnterStaticCon, tickyEnterViaNode, tickyEnterFun, tickyEnterThunk, tickyEnterStdThunk, -- dynamic non-value -- thunks only tickyEnterLNE, tickyUpdateBhCaf, tickyBlackHole, tickyUnboxedTupleReturn, tickyReturnOldCon, tickyReturnNewCon, tickyKnownCallTooFewArgs, tickyKnownCallExact, tickyKnownCallExtraArgs, tickySlowCall, tickySlowCallPat, ) where #include "HsVersions.h" import StgCmmArgRep ( slowCallPattern , toArgRep , argRepString ) import StgCmmClosure import StgCmmUtils import StgCmmMonad import StgSyn import CmmExpr import MkGraph import CmmUtils import CLabel import SMRep import Module import Name import Id import BasicTypes import FastString import Outputable import DynFlags -- Turgid imports for showTypeCategory import PrelNames import TcType import Type import TyCon import Data.Maybe import qualified Data.Char import Control.Monad ( unless, when ) ----------------------------------------------------------------------------- -- -- Ticky-ticky profiling -- ----------------------------------------------------------------------------- data TickyClosureType = TickyFun Bool -- True <-> single entry | TickyCon | TickyThunk Bool -- True <-> updateable Bool -- True <-> standard thunk (AP or selector), has no entry counter | TickyLNE withNewTickyCounterFun :: Bool -> Name -> [NonVoid Id] -> FCode a -> FCode a withNewTickyCounterFun single_entry = withNewTickyCounter (TickyFun single_entry) withNewTickyCounterLNE :: Name -> [NonVoid Id] -> FCode a -> FCode a withNewTickyCounterLNE nm args code = do b <- tickyLNEIsOn if not b then code else withNewTickyCounter TickyLNE nm args code withNewTickyCounterThunk :: Bool -- ^ static -> Bool -- ^ updateable -> Name -> FCode a -> FCode a withNewTickyCounterThunk isStatic isUpdatable name code = do b <- tickyDynThunkIsOn if isStatic || not b -- ignore static thunks then code else withNewTickyCounter (TickyThunk isUpdatable False) name [] code withNewTickyCounterStdThunk :: Bool -- ^ updateable -> Name -> FCode a -> FCode a withNewTickyCounterStdThunk isUpdatable name code = do b <- tickyDynThunkIsOn if not b then code else withNewTickyCounter (TickyThunk isUpdatable True) name [] code withNewTickyCounterCon :: Name -> FCode a -> FCode a withNewTickyCounterCon name code = do b <- tickyDynThunkIsOn if not b then code else withNewTickyCounter TickyCon name [] code -- args does not include the void arguments withNewTickyCounter :: TickyClosureType -> Name -> [NonVoid Id] -> FCode a -> FCode a withNewTickyCounter cloType name args m = do lbl <- emitTickyCounter cloType name args setTickyCtrLabel lbl m emitTickyCounter :: TickyClosureType -> Name -> [NonVoid Id] -> FCode CLabel emitTickyCounter cloType name args = let ctr_lbl = mkRednCountsLabel name in (>> return ctr_lbl) $ ifTicky $ do { dflags <- getDynFlags ; parent <- getTickyCtrLabel ; mod_name <- getModuleName -- When printing the name of a thing in a ticky file, we -- want to give the module name even for *local* things. We -- print just "x (M)" rather that "M.x" to distinguish them -- from the global kind. ; let ppr_for_ticky_name :: SDoc ppr_for_ticky_name = let n = ppr name ext = case cloType of TickyFun single_entry -> parens $ hcat $ punctuate comma $ [text "fun"] ++ [text "se"|single_entry] TickyCon -> parens (text "con") TickyThunk upd std -> parens $ hcat $ punctuate comma $ [text "thk"] ++ [text "se"|not upd] ++ [text "std"|std] TickyLNE | isInternalName name -> parens (text "LNE") | otherwise -> panic "emitTickyCounter: how is this an external LNE?" p = case hasHaskellName parent of -- NB the default "top" ticky ctr does not -- have a Haskell name Just pname -> text "in" <+> ppr (nameUnique pname) _ -> empty in if isInternalName name then n <+> parens (ppr mod_name) <+> ext <+> p else n <+> ext <+> p ; fun_descr_lit <- newStringCLit $ showSDocDebug dflags ppr_for_ticky_name ; arg_descr_lit <- newStringCLit $ map (showTypeCategory . idType . fromNonVoid) args ; emitDataLits ctr_lbl -- Must match layout of includes/rts/Ticky.h's StgEntCounter -- -- krc: note that all the fields are I32 now; some were I16 -- before, but the code generator wasn't handling that -- properly and it led to chaos, panic and disorder. [ mkIntCLit dflags 0, -- registered? mkIntCLit dflags (length args), -- Arity mkIntCLit dflags 0, -- Heap allocated for this thing fun_descr_lit, arg_descr_lit, zeroCLit dflags, -- Entries into this thing zeroCLit dflags, -- Heap allocated by this thing zeroCLit dflags -- Link to next StgEntCounter ] } -- ----------------------------------------------------------------------------- -- Ticky stack frames tickyPushUpdateFrame, tickyUpdateFrameOmitted :: FCode () tickyPushUpdateFrame = ifTicky $ bumpTickyCounter (fsLit "UPDF_PUSHED_ctr") tickyUpdateFrameOmitted = ifTicky $ bumpTickyCounter (fsLit "UPDF_OMITTED_ctr") -- ----------------------------------------------------------------------------- -- Ticky entries -- NB the name-specific entries are only available for names that have -- dedicated Cmm code. As far as I know, this just rules out -- constructor thunks. For them, there is no CMM code block to put the -- bump of name-specific ticky counter into. On the other hand, we can -- still track allocation their allocation. tickyEnterDynCon, tickyEnterStaticCon, tickyEnterViaNode :: FCode () tickyEnterDynCon = ifTicky $ bumpTickyCounter (fsLit "ENT_DYN_CON_ctr") tickyEnterStaticCon = ifTicky $ bumpTickyCounter (fsLit "ENT_STATIC_CON_ctr") tickyEnterViaNode = ifTicky $ bumpTickyCounter (fsLit "ENT_VIA_NODE_ctr") tickyEnterThunk :: ClosureInfo -> FCode () tickyEnterThunk cl_info = ifTicky $ do { bumpTickyCounter ctr ; unless static $ do ticky_ctr_lbl <- getTickyCtrLabel registerTickyCtrAtEntryDyn ticky_ctr_lbl bumpTickyEntryCount ticky_ctr_lbl } where updatable = closureSingleEntry cl_info static = isStaticClosure cl_info ctr | static = if updatable then fsLit "ENT_STATIC_THK_SINGLE_ctr" else fsLit "ENT_STATIC_THK_MANY_ctr" | otherwise = if updatable then fsLit "ENT_DYN_THK_SINGLE_ctr" else fsLit "ENT_DYN_THK_MANY_ctr" tickyEnterStdThunk :: ClosureInfo -> FCode () tickyEnterStdThunk = tickyEnterThunk tickyBlackHole :: Bool{-updatable-} -> FCode () tickyBlackHole updatable = ifTicky (bumpTickyCounter ctr) where ctr | updatable = (fsLit "UPD_BH_SINGLE_ENTRY_ctr") | otherwise = (fsLit "UPD_BH_UPDATABLE_ctr") tickyUpdateBhCaf :: ClosureInfo -> FCode () tickyUpdateBhCaf cl_info = ifTicky (bumpTickyCounter ctr) where ctr | closureUpdReqd cl_info = (fsLit "UPD_CAF_BH_SINGLE_ENTRY_ctr") | otherwise = (fsLit "UPD_CAF_BH_UPDATABLE_ctr") tickyEnterFun :: ClosureInfo -> FCode () tickyEnterFun cl_info = ifTicky $ do ctr_lbl <- getTickyCtrLabel if isStaticClosure cl_info then do bumpTickyCounter (fsLit "ENT_STATIC_FUN_DIRECT_ctr") registerTickyCtr ctr_lbl else do bumpTickyCounter (fsLit "ENT_DYN_FUN_DIRECT_ctr") registerTickyCtrAtEntryDyn ctr_lbl bumpTickyEntryCount ctr_lbl tickyEnterLNE :: FCode () tickyEnterLNE = ifTicky $ do bumpTickyCounter (fsLit "ENT_LNE_ctr") ifTickyLNE $ do ctr_lbl <- getTickyCtrLabel registerTickyCtr ctr_lbl bumpTickyEntryCount ctr_lbl -- needn't register a counter upon entry if -- -- 1) it's for a dynamic closure, and -- -- 2) -ticky-allocd is on -- -- since the counter was registered already upon being alloc'd registerTickyCtrAtEntryDyn :: CLabel -> FCode () registerTickyCtrAtEntryDyn ctr_lbl = do already_registered <- tickyAllocdIsOn when (not already_registered) $ registerTickyCtr ctr_lbl registerTickyCtr :: CLabel -> FCode () -- Register a ticky counter -- if ( ! f_ct.registeredp ) { -- f_ct.link = ticky_entry_ctrs; /* hook this one onto the front of the list */ -- ticky_entry_ctrs = & (f_ct); /* mark it as "registered" */ -- f_ct.registeredp = 1 } registerTickyCtr ctr_lbl = do dflags <- getDynFlags let -- krc: code generator doesn't handle Not, so we test for Eq 0 instead test = CmmMachOp (MO_Eq (wordWidth dflags)) [CmmLoad (CmmLit (cmmLabelOffB ctr_lbl (oFFSET_StgEntCounter_registeredp dflags))) (bWord dflags), zeroExpr dflags] register_stmts = [ mkStore (CmmLit (cmmLabelOffB ctr_lbl (oFFSET_StgEntCounter_link dflags))) (CmmLoad ticky_entry_ctrs (bWord dflags)) , mkStore ticky_entry_ctrs (mkLblExpr ctr_lbl) , mkStore (CmmLit (cmmLabelOffB ctr_lbl (oFFSET_StgEntCounter_registeredp dflags))) (mkIntExpr dflags 1) ] ticky_entry_ctrs = mkLblExpr (mkCmmDataLabel rtsUnitId (fsLit "ticky_entry_ctrs")) emit =<< mkCmmIfThen test (catAGraphs register_stmts) tickyReturnOldCon, tickyReturnNewCon :: RepArity -> FCode () tickyReturnOldCon arity = ifTicky $ do { bumpTickyCounter (fsLit "RET_OLD_ctr") ; bumpHistogram (fsLit "RET_OLD_hst") arity } tickyReturnNewCon arity = ifTicky $ do { bumpTickyCounter (fsLit "RET_NEW_ctr") ; bumpHistogram (fsLit "RET_NEW_hst") arity } tickyUnboxedTupleReturn :: RepArity -> FCode () tickyUnboxedTupleReturn arity = ifTicky $ do { bumpTickyCounter (fsLit "RET_UNBOXED_TUP_ctr") ; bumpHistogram (fsLit "RET_UNBOXED_TUP_hst") arity } -- ----------------------------------------------------------------------------- -- Ticky calls -- Ticks at a *call site*: tickyDirectCall :: RepArity -> [StgArg] -> FCode () tickyDirectCall arity args | arity == length args = tickyKnownCallExact | otherwise = do tickyKnownCallExtraArgs tickySlowCallPat (map argPrimRep (drop arity args)) tickyKnownCallTooFewArgs :: FCode () tickyKnownCallTooFewArgs = ifTicky $ bumpTickyCounter (fsLit "KNOWN_CALL_TOO_FEW_ARGS_ctr") tickyKnownCallExact :: FCode () tickyKnownCallExact = ifTicky $ bumpTickyCounter (fsLit "KNOWN_CALL_ctr") tickyKnownCallExtraArgs :: FCode () tickyKnownCallExtraArgs = ifTicky $ bumpTickyCounter (fsLit "KNOWN_CALL_EXTRA_ARGS_ctr") tickyUnknownCall :: FCode () tickyUnknownCall = ifTicky $ bumpTickyCounter (fsLit "UNKNOWN_CALL_ctr") -- Tick for the call pattern at slow call site (i.e. in addition to -- tickyUnknownCall, tickyKnownCallExtraArgs, etc.) tickySlowCall :: LambdaFormInfo -> [StgArg] -> FCode () tickySlowCall _ [] = return () tickySlowCall lf_info args = do -- see Note [Ticky for slow calls] if isKnownFun lf_info then tickyKnownCallTooFewArgs else tickyUnknownCall tickySlowCallPat (map argPrimRep args) tickySlowCallPat :: [PrimRep] -> FCode () tickySlowCallPat args = ifTicky $ let argReps = map toArgRep args (_, n_matched) = slowCallPattern argReps in if n_matched > 0 && n_matched == length args then bumpTickyLbl $ mkRtsSlowFastTickyCtrLabel $ concatMap (map Data.Char.toLower . argRepString) argReps else bumpTickyCounter $ fsLit "VERY_SLOW_CALL_ctr" {- Note [Ticky for slow calls] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Terminology is unfortunately a bit mixed up for these calls. codeGen uses "slow call" to refer to unknown calls and under-saturated known calls. Nowadays, though (ie as of the eval/apply paper), the significantly slower calls are actually just a subset of these: the ones with no built-in argument pattern (cf StgCmmArgRep.slowCallPattern) So for ticky profiling, we split slow calls into "SLOW_CALL_fast__ctr" (those matching a built-in pattern) and VERY_SLOW_CALL_ctr (those without a built-in pattern; these are very bad for both space and time). -} -- ----------------------------------------------------------------------------- -- Ticky allocation tickyDynAlloc :: Maybe Id -> SMRep -> LambdaFormInfo -> FCode () -- Called when doing a dynamic heap allocation; the LambdaFormInfo -- used to distinguish between closure types -- -- TODO what else to count while we're here? tickyDynAlloc mb_id rep lf = ifTicky $ getDynFlags >>= \dflags -> let bytes = wORD_SIZE dflags * heapClosureSizeW dflags rep countGlobal tot ctr = do bumpTickyCounterBy tot bytes bumpTickyCounter ctr countSpecific = ifTickyAllocd $ case mb_id of Nothing -> return () Just id -> do let ctr_lbl = mkRednCountsLabel (idName id) registerTickyCtr ctr_lbl bumpTickyAllocd ctr_lbl bytes -- TODO are we still tracking "good stuff" (_gds) versus -- administrative (_adm) versus slop (_slp)? I'm going with all _gds -- for now, since I don't currently know neither if we do nor how to -- distinguish. NSF Mar 2013 in case () of _ | isConRep rep -> ifTickyDynThunk countSpecific >> countGlobal (fsLit "ALLOC_CON_gds") (fsLit "ALLOC_CON_ctr") | isThunkRep rep -> ifTickyDynThunk countSpecific >> if lfUpdatable lf then countGlobal (fsLit "ALLOC_THK_gds") (fsLit "ALLOC_UP_THK_ctr") else countGlobal (fsLit "ALLOC_THK_gds") (fsLit "ALLOC_SE_THK_ctr") | isFunRep rep -> countSpecific >> countGlobal (fsLit "ALLOC_FUN_gds") (fsLit "ALLOC_FUN_ctr") | otherwise -> panic "How is this heap object not a con, thunk, or fun?" tickyAllocHeap :: Bool -> -- is this a genuine allocation? As opposed to -- StgCmmLayout.adjustHpBackwards VirtualHpOffset -> FCode () -- Called when doing a heap check [TICK_ALLOC_HEAP] -- Must be lazy in the amount of allocation! tickyAllocHeap genuine hp = ifTicky $ do { dflags <- getDynFlags ; ticky_ctr <- getTickyCtrLabel ; emit $ catAGraphs $ -- only test hp from within the emit so that the monadic -- computation itself is not strict in hp (cf knot in -- StgCmmMonad.getHeapUsage) if hp == 0 then [] else let !bytes = wORD_SIZE dflags * hp in [ -- Bump the allocation total in the closure's StgEntCounter addToMem (rEP_StgEntCounter_allocs dflags) (CmmLit (cmmLabelOffB ticky_ctr (oFFSET_StgEntCounter_allocs dflags))) bytes, -- Bump the global allocation total ALLOC_HEAP_tot addToMemLbl (bWord dflags) (mkCmmDataLabel rtsUnitId (fsLit "ALLOC_HEAP_tot")) bytes, -- Bump the global allocation counter ALLOC_HEAP_ctr if not genuine then mkNop else addToMemLbl (bWord dflags) (mkCmmDataLabel rtsUnitId (fsLit "ALLOC_HEAP_ctr")) 1 ]} -------------------------------------------------------------------------------- -- these three are only called from CmmParse.y (ie ultimately from the RTS) -- the units are bytes tickyAllocPrim :: CmmExpr -- ^ size of the full header, in bytes -> CmmExpr -- ^ size of the payload, in bytes -> CmmExpr -> FCode () tickyAllocPrim _hdr _goods _slop = ifTicky $ do bumpTickyCounter (fsLit "ALLOC_PRIM_ctr") bumpTickyCounterByE (fsLit "ALLOC_PRIM_adm") _hdr bumpTickyCounterByE (fsLit "ALLOC_PRIM_gds") _goods bumpTickyCounterByE (fsLit "ALLOC_PRIM_slp") _slop tickyAllocThunk :: CmmExpr -> CmmExpr -> FCode () tickyAllocThunk _goods _slop = ifTicky $ do -- TODO is it ever called with a Single-Entry thunk? bumpTickyCounter (fsLit "ALLOC_UP_THK_ctr") bumpTickyCounterByE (fsLit "ALLOC_THK_gds") _goods bumpTickyCounterByE (fsLit "ALLOC_THK_slp") _slop tickyAllocPAP :: CmmExpr -> CmmExpr -> FCode () tickyAllocPAP _goods _slop = ifTicky $ do bumpTickyCounter (fsLit "ALLOC_PAP_ctr") bumpTickyCounterByE (fsLit "ALLOC_PAP_gds") _goods bumpTickyCounterByE (fsLit "ALLOC_PAP_slp") _slop tickyHeapCheck :: FCode () tickyHeapCheck = ifTicky $ bumpTickyCounter (fsLit "HEAP_CHK_ctr") tickyStackCheck :: FCode () tickyStackCheck = ifTicky $ bumpTickyCounter (fsLit "STK_CHK_ctr") -- ----------------------------------------------------------------------------- -- Ticky utils ifTicky :: FCode () -> FCode () ifTicky code = getDynFlags >>= \dflags -> when (gopt Opt_Ticky dflags) code tickyAllocdIsOn :: FCode Bool tickyAllocdIsOn = gopt Opt_Ticky_Allocd `fmap` getDynFlags tickyLNEIsOn :: FCode Bool tickyLNEIsOn = gopt Opt_Ticky_LNE `fmap` getDynFlags tickyDynThunkIsOn :: FCode Bool tickyDynThunkIsOn = gopt Opt_Ticky_Dyn_Thunk `fmap` getDynFlags ifTickyAllocd :: FCode () -> FCode () ifTickyAllocd code = tickyAllocdIsOn >>= \b -> when b code ifTickyLNE :: FCode () -> FCode () ifTickyLNE code = tickyLNEIsOn >>= \b -> when b code ifTickyDynThunk :: FCode () -> FCode () ifTickyDynThunk code = tickyDynThunkIsOn >>= \b -> when b code bumpTickyCounter :: FastString -> FCode () bumpTickyCounter lbl = bumpTickyLbl (mkCmmDataLabel rtsUnitId lbl) bumpTickyCounterBy :: FastString -> Int -> FCode () bumpTickyCounterBy lbl = bumpTickyLblBy (mkCmmDataLabel rtsUnitId lbl) bumpTickyCounterByE :: FastString -> CmmExpr -> FCode () bumpTickyCounterByE lbl = bumpTickyLblByE (mkCmmDataLabel rtsUnitId lbl) bumpTickyEntryCount :: CLabel -> FCode () bumpTickyEntryCount lbl = do dflags <- getDynFlags bumpTickyLit (cmmLabelOffB lbl (oFFSET_StgEntCounter_entry_count dflags)) bumpTickyAllocd :: CLabel -> Int -> FCode () bumpTickyAllocd lbl bytes = do dflags <- getDynFlags bumpTickyLitBy (cmmLabelOffB lbl (oFFSET_StgEntCounter_allocd dflags)) bytes bumpTickyLbl :: CLabel -> FCode () bumpTickyLbl lhs = bumpTickyLitBy (cmmLabelOffB lhs 0) 1 bumpTickyLblBy :: CLabel -> Int -> FCode () bumpTickyLblBy lhs = bumpTickyLitBy (cmmLabelOffB lhs 0) bumpTickyLblByE :: CLabel -> CmmExpr -> FCode () bumpTickyLblByE lhs = bumpTickyLitByE (cmmLabelOffB lhs 0) bumpTickyLit :: CmmLit -> FCode () bumpTickyLit lhs = bumpTickyLitBy lhs 1 bumpTickyLitBy :: CmmLit -> Int -> FCode () bumpTickyLitBy lhs n = do dflags <- getDynFlags emit (addToMem (bWord dflags) (CmmLit lhs) n) bumpTickyLitByE :: CmmLit -> CmmExpr -> FCode () bumpTickyLitByE lhs e = do dflags <- getDynFlags emit (addToMemE (bWord dflags) (CmmLit lhs) e) bumpHistogram :: FastString -> Int -> FCode () bumpHistogram lbl n = do dflags <- getDynFlags let offset = n `min` (tICKY_BIN_COUNT dflags - 1) emit (addToMem (bWord dflags) (cmmIndexExpr dflags (wordWidth dflags) (CmmLit (CmmLabel (mkCmmDataLabel rtsUnitId lbl))) (CmmLit (CmmInt (fromIntegral offset) (wordWidth dflags)))) 1) ------------------------------------------------------------------ -- Showing the "type category" for ticky-ticky profiling showTypeCategory :: Type -> Char {- + dictionary > function {C,I,F,D,W} char, int, float, double, word {c,i,f,d,w} unboxed ditto T tuple P other primitive type p unboxed ditto L list E enumeration type S other single-constructor type M other multi-constructor data-con type . other type - reserved for others to mark as "uninteresting" Accurate as of Mar 2013, but I eliminated the Array category instead of updating it, for simplicity. It's in P/p, I think --NSF -} showTypeCategory ty | isDictTy ty = '+' | otherwise = case tcSplitTyConApp_maybe ty of Nothing -> '.' Just (tycon, _) -> (if isUnliftedTyCon tycon then Data.Char.toLower else id) $ let anyOf us = getUnique tycon `elem` us in case () of _ | anyOf [funTyConKey] -> '>' | anyOf [charPrimTyConKey, charTyConKey] -> 'C' | anyOf [doublePrimTyConKey, doubleTyConKey] -> 'D' | anyOf [floatPrimTyConKey, floatTyConKey] -> 'F' | anyOf [intPrimTyConKey, int32PrimTyConKey, int64PrimTyConKey, intTyConKey, int8TyConKey, int16TyConKey, int32TyConKey, int64TyConKey ] -> 'I' | anyOf [wordPrimTyConKey, word32PrimTyConKey, word64PrimTyConKey, wordTyConKey, word8TyConKey, word16TyConKey, word32TyConKey, word64TyConKey ] -> 'W' | anyOf [listTyConKey] -> 'L' | isTupleTyCon tycon -> 'T' | isPrimTyCon tycon -> 'P' | isEnumerationTyCon tycon -> 'E' | isJust (tyConSingleDataCon_maybe tycon) -> 'S' | otherwise -> 'M' -- oh, well...