    -- | Code generation for the Static Pointer Table
    --
    -- (c) 2014 I/O Tweag
    --
    -- Each module that uses 'static' keyword declares an initialization function of
    -- the form hs_spt_init_<module>() which is emitted into the _stub.c file and
    -- annotated with __attribute__((constructor)) so that it gets executed at
    -- startup time.
    --
    -- The function's purpose is to call hs_spt_insert to insert the static
    -- pointers of this module in the hashtable of the RTS, and it looks something
    -- like this:
    --
    -- > static void hs_hpc_init_Main(void) __attribute__((constructor));
    -- > static void hs_hpc_init_Main(void) {
    -- >
    -- >   static StgWord64 k0[2] = {16252233372134256ULL,7370534374096082ULL};
    -- >   extern StgPtr Main_sptEntryZC0_closure;
    -- >   hs_spt_insert(k0, &Main_sptEntryZC0_closure);
    -- >
    -- >   static StgWord64 k1[2] = {12545634534567898ULL,5409674567544151ULL};
    -- >   extern StgPtr Main_sptEntryZC1_closure;
    -- >   hs_spt_insert(k1, &Main_sptEntryZC1_closure);
    -- >
    -- > }
    --
    -- where the constants are fingerprints produced from the static forms.
    --
    -- There is also a finalization function for the time when the module is
    -- unloaded.
    --
    -- > static void hs_hpc_fini_Main(void) __attribute__((destructor));
    -- > static void hs_hpc_fini_Main(void) {
    -- >
    -- >   static StgWord64 k0[2] = {16252233372134256ULL,7370534374096082ULL};
    -- >   hs_spt_remove(k0);
    -- >
    -- >   static StgWord64 k1[2] = {12545634534567898ULL,5409674567544151ULL};
    -- >   hs_spt_remove(k1);
    -- >
    -- > }
    --
    module StaticPtrTable (sptInitCode) where

    import CoreSyn
    import Module
    import Outputable
    import Id
    import CLabel
    import GHC.Fingerprint

    -- | @sptInitCode module statics@ is a C stub to insert the static entries
    -- @statics@ of @module@ into the static pointer table.
    --
    -- Each entry contains the fingerprint used to locate the entry and the
    -- top-level binding for the entry.
    --
    sptInitCode :: Module -> [(Fingerprint, (Id,CoreExpr))] -> SDoc
    sptInitCode _ [] = Outputable.empty
    sptInitCode this_mod entries = vcat
        [ text "static void hs_spt_init_" <> ppr this_mod
               <> text "(void) __attribute__((constructor));"
        , text "static void hs_spt_init_" <> ppr this_mod <> text "(void)"
        , braces $ vcat $
            [  text "static StgWord64 k" <> int i <> text "[2] = "
               <> pprFingerprint fp <> semi
            $$ text "extern StgPtr "
               <> (ppr $ mkClosureLabel (idName n) (idCafInfo n)) <> semi
            $$ text "hs_spt_insert" <> parens
                 (hcat $ punctuate comma
                    [ char 'k' <> int i
                    , char '&' <> ppr (mkClosureLabel (idName n) (idCafInfo n))
                    ]
                 )
            <> semi
            |  (i, (fp, (n, _))) <- zip [0..] entries
            ]
        , text "static void hs_spt_fini_" <> ppr this_mod
               <> text "(void) __attribute__((destructor));"
        , text "static void hs_spt_fini_" <> ppr this_mod <> text "(void)"
        , braces $ vcat $
            [  text "StgWord64 k" <> int i <> text "[2] = "
               <> pprFingerprint fp <> semi
            $$ text "hs_spt_remove" <> parens (char 'k' <> int i) <> semi
            | (i, (fp, _)) <- zip [0..] entries
            ]
        ]

      where

        pprFingerprint :: Fingerprint -> SDoc
        pprFingerprint (Fingerprint w1 w2) =
          braces $ hcat $ punctuate comma
                     [ integer (fromIntegral w1) <> text "ULL"
                     , integer (fromIntegral w2) <> text "ULL"
                     ]