Andreas Klebinger pushed to branch wip/apk/iface-encoding at Glasgow Haskell Compiler / GHC

Commits:

14 changed files:

Changes:

  • changelog.d/T27808-iface-encoding-improvements
    1
    +section: compiler
    
    2
    +synopsis: Improved ``.hi`` file encoding resulting in less space used on disk.
    
    3
    +issues: #27808
    
    4
    +mrs: !16683
    
    5
    +description: {
    
    6
    +  This was mostly done by moving from simple pointwise encoding to adding
    
    7
    +  special cases for common cases.
    
    8
    +}

  • compiler/GHC/CoreToIface.hs
    ... ... @@ -651,16 +651,19 @@ toIfaceApp (Var v) as
    651 651
     toIfaceApp e as = mkIfaceApps (toIfaceExpr e) as
    
    652 652
     
    
    653 653
     mkIfaceApps :: IfaceExpr -> [CoreExpr] -> IfaceExpr
    
    654
    -mkIfaceApps f as = foldl' (\f a -> IfaceApp f (toIfaceExpr a)) f as
    
    654
    +-- `mkIfaceApp` is just a smart constructor for the IfaceApp[s] constructors.
    
    655
    +-- See Note [Iface applications] in GHC.Iface.Syntax
    
    656
    +mkIfaceApps f as = mkIfaceApp f (map toIfaceExpr as)
    
    655 657
     
    
    656 658
     ---------------------
    
    657 659
     toIfaceVar :: Id -> IfaceExpr
    
    658 660
     toIfaceVar v
    
    659 661
         | isBootUnfolding (idUnfolding v)
    
    660 662
         = -- See Note [Inlining and hs-boot files]
    
    661
    -      IfaceApp (IfaceApp (IfaceExt noinline_id)
    
    662
    -                         (IfaceType (toIfaceType ty)))
    
    663
    -               (IfaceExt name) -- don't use mkIfaceApps, or infinite loop
    
    663
    +      IfaceApps (IfaceExt noinline_id)
    
    664
    +                [IfaceType (toIfaceType ty), IfaceExt name]
    
    665
    +                -- don't use mkIfaceApps, or infinite loop since it ends up calling
    
    666
    +                -- toIfaceVar indirectly again.
    
    664 667
     
    
    665 668
         | Just fcall <- isFCallId_maybe v = IfaceFCall fcall (toIfaceType (idType v))
    
    666 669
                                           -- Foreign calls have special syntax
    

  • compiler/GHC/Iface/Binary.hs
    ... ... @@ -741,25 +741,38 @@ In more detail:
    741 741
       Tuples aren't included in the wired-in names map: see (ST1) below
    
    742 742
     
    
    743 743
     * Serialisation is done by `putName`:
    
    744
    -  - When we serialise a compact Name,
    
    745
    -    we serialise it as a single 32-bit word:
    
    746
    -      10xxxxxx xxyyyyyy yyyyyyyy yyyyyyyy
    
    747
    -    where xxxx is the tag, and yyyy is the payload.
    
    748
    -    The function `wiredInNamesOkay` checks that the wired-in names all have
    
    749
    -    uniques that fit into the `yyy` field.
    
    744
    +  - When we serialise a compact Name, we serialise its Unique, split by
    
    745
    +    `unpkUniqueGrimily` into the tag character and the payload:
    
    746
    +
    
    747
    +      yyyyyyyy yyyyyyyy yyyyyyyx xxxxxxx1
    
    748
    +      \________ payload _______/\_ tag _/^ marker bit
    
    749
    +
    
    750
    +    Why are we storing the marker/tag in the low rather than high bits? Because
    
    751
    +    we LEB128 encode the whole word when writing to disk so we want to keep as
    
    752
    +    many of the high bits zero as possible to allow for shorter encodings. See
    
    753
    +    also wrinkle ST3.
    
    754
    +
    
    755
    +    Tags are 8 bits by construction, and there is a check that the actual unique
    
    756
    +    part fits in 22 bits which `wiredInNamesOkay` (in GHC.Builtin) checks for all
    
    757
    +    known-key names.
    
    750 758
     
    
    751 759
       - When we serialise a non-compact name:
    
    752 760
           - We look it up in the (stateful, growing) symbol table
    
    753
    -      - If it not there we add it to the symbol table
    
    754
    -      - We serialise the occurrenc to a single 32-bit word:
    
    755
    -          00xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx
    
    756
    -        where `xxxxx` is an index into the symbol table.
    
    761
    +      - If it is not there we add it to the symbol table
    
    762
    +      - We serialise the occurrence as
    
    757 763
     
    
    758
    -* Deserialision is done by `getName`.  We read a 32-bit word
    
    759
    -  - If the MSB is `10` it must be a compact name, so we use
    
    764
    +          0xxxxxxx xxxxxxxx xxxxxxxx xxxxxxx0
    
    765
    +
    
    766
    +        In other words we simply shift the index by a bit.
    
    767
    +        The high bit is currently unused. But `putSymtabNameRef`
    
    768
    +        asserts that `ix` fits in 30 bits.
    
    769
    +
    
    770
    +* Deserialisation is done by `getSymtabName`, which dispatches on the low bit
    
    771
    +  of the word it reads:
    
    772
    +  - If it is 1 it must be a compact name, so we reassemble the Unique and use
    
    760 773
         `lookupCompactName` to get from the Unique to the Name.
    
    761
    -  - If the MSB is `00` it must be a non-compact Name,
    
    762
    -    so we look it up in the symbol table.
    
    774
    +  - If it is 0 it must be a non-compact Name, so we look it up in the
    
    775
    +    symbol table.
    
    763 776
     
    
    764 777
     Wrinkles:
    
    765 778
     
    
    ... ... @@ -785,6 +798,20 @@ Wrinkles:
    785 798
       `isCompactName` that tests for `knownUniqueTupleName` and then the
    
    786 799
       TyConRepNames would be serialised as non-compact names, and everything would
    
    787 800
       work. Fewer tests, but Typeable-heavy code might have bigger interface files.
    
    801
    +
    
    802
    +(ST3) Both kinds of Name are serialised as a single `Word32`, which is serialized to
    
    803
    +  disk in it's ULEB128 encoded variable-length form (see `putULEB128`).
    
    804
    +  This has consequences as it means we want to keep the high bits zero where possible
    
    805
    +  to allow for a shorter ULEB128 encoding.
    
    806
    +
    
    807
    +  This is why we put both the tag and the marker bit at the LSB end of the word. They
    
    808
    +  are always present. But by putting them at the low end we ensure LEB128 encoding
    
    809
    +  still works as expected, producing smaller encodings for compact names with small
    
    810
    +  uniques.
    
    811
    +
    
    812
    +  The downside is that we steal one bit from non-compact names for which the marker
    
    813
    +  bit and tag would have been zero either way. But in practice this matters far less
    
    814
    +  than ensuring built in (compact) names encode well.
    
    788 815
     -}
    
    789 816
     
    
    790 817
     isCompactName :: Name -> Bool
    
    ... ... @@ -803,6 +830,31 @@ lookupCompactName u
    803 830
       where
    
    804 831
          (tag, ix) = unpkUniqueGrimily u
    
    805 832
     
    
    833
    +-- | Write a reference to a symbol table index.
    
    834
    +-- See Note [Symbol table representation of names]
    
    835
    +putSymtabNameRef :: WriteBinHandle -> Int -> IO ()
    
    836
    +{-# INLINE putSymtabNameRef #-}
    
    837
    +putSymtabNameRef bh ix
    
    838
    +  = assertPpr (ix >= 0 && ix < (1 `shiftL` 30))
    
    839
    +              (text "putSymtabNameRef: symbol table index out of range:" <+> int ix) $
    
    840
    +    -- Bit 0 == False marks a symbol table reference
    
    841
    +    put_ bh ((fromIntegral ix `shiftL` 1) :: Word32)
    
    842
    +
    
    843
    +-- | Write a reference to a compact (known-key) 'Name'.
    
    844
    +-- See Note [Symbol table representation of names]
    
    845
    +putCompactNameRef :: WriteBinHandle -> Unique -> IO ()
    
    846
    +{-# INLINE putCompactNameRef #-}
    
    847
    +putCompactNameRef bh uniq
    
    848
    +  = -- INVARIANTS:
    
    849
    +    --    * 8 bits tag (true by construction)
    
    850
    +    --    * the payload fits in 22 bits (checked for all known keys elsewhere)
    
    851
    +    -- Bit 0 == True marks a compact (known-key) name
    
    852
    +    put_ bh (   (fromIntegral payload `shiftL` 9)
    
    853
    +            .|. (fromIntegral (ord tag) `shiftL` 1)
    
    854
    +            .|. 1 :: Word32)
    
    855
    +  where
    
    856
    +    (tag, payload) = unpkUniqueGrimily uniq
    
    857
    +
    
    806 858
     -- See Note [Symbol table representation of names]
    
    807 859
     putName :: BinSymbolTable -> WriteBinHandle -> Name -> IO ()
    
    808 860
     putName BinSymbolTable{
    
    ... ... @@ -810,16 +862,12 @@ putName BinSymbolTable{
    810 862
                    bin_symtab_next = symtab_next }
    
    811 863
             bh name
    
    812 864
       | isCompactName name
    
    813
    -  , let (c, u) = unpkUniqueGrimily (nameUnique name) -- INVARIANT: (ord c) fits in 8 bits
    
    814
    -  = -- assert (u < 2^(22 :: Int))
    
    815
    -    put_ bh (0x80000000
    
    816
    -             .|. (fromIntegral (ord c) `shiftL` 22)
    
    817
    -             .|. (fromIntegral u :: Word32))
    
    865
    +  = putCompactNameRef bh (nameUnique name)
    
    818 866
     
    
    819 867
       | otherwise
    
    820 868
       = do (symtab_map,symtab_tbl) <- readIORef symtab_map_ref
    
    821 869
            case lookupNameEnv symtab_map name of
    
    822
    -         Just off -> put_ bh (fromIntegral off :: Word32)
    
    870
    +         Just off -> putSymtabNameRef bh off
    
    823 871
              Nothing -> do
    
    824 872
               off <- freshIndex
    
    825 873
               let mod = nameModule name
    
    ... ... @@ -829,12 +877,11 @@ putName BinSymbolTable{
    829 877
               let !symtab_tbl' = extendModuleEnv symtab_tbl mod ((off,name):mod_nms)
    
    830 878
               writeIORef symtab_map_ref $! ( symtab_map',  symtab_tbl' )
    
    831 879
     
    
    832
    -          put_ bh (fromIntegral off :: Word32)
    
    880
    +          putSymtabNameRef bh off
    
    833 881
       where
    
    834 882
         freshIndex :: IO Int
    
    835 883
         freshIndex = do
    
    836 884
           off <- readFastMutInt symtab_next
    
    837
    -      -- massert (off < 2^(30 :: Int))
    
    838 885
           writeFastMutInt symtab_next (off+1)
    
    839 886
           return off
    
    840 887
     
    
    ... ... @@ -843,12 +890,10 @@ getSymtabName :: SymbolTable Name
    843 890
                   -> ReadBinHandle -> IO Name
    
    844 891
     getSymtabName symtab bh = do
    
    845 892
         i :: Word32 <- get bh
    
    846
    -    case i .&. 0xC0000000 of
    
    847
    -      0x00000000 -> return $! symtab ! fromIntegral i
    
    848
    -      0x80000000 -> return $! lookupCompactName u
    
    849
    -        where
    
    850
    -          tag = chr (fromIntegral ((i .&. 0x3FC00000) `shiftR` 22))
    
    851
    -          ix  = fromIntegral i .&. 0x003FFFFF
    
    852
    -          u   = mkUniqueGrimilyWithTag tag ix
    
    853
    -
    
    854
    -      _ -> pprPanic "getSymtabName:unknown name tag" (ppr i)
    893
    +    if i .&. 1 == 0
    
    894
    +      then -- Symbol table reference, written by putSymtabNameRef
    
    895
    +           return $! symtab ! fromIntegral (i `shiftR` 1)
    
    896
    +      else -- Compact name, written by putCompactNameRef
    
    897
    +           let tag     = chr (fromIntegral ((i `shiftR` 1) .&. 0xFF))
    
    898
    +               payload = fromIntegral (i `shiftR` 9) :: Word64
    
    899
    +           in return $! lookupCompactName (mkUniqueGrimilyWithTag tag payload)

  • compiler/GHC/Iface/Rename.hs
    ... ... @@ -828,6 +828,8 @@ rnIfaceExpr (IfaceLam lam_bndr expr)
    828 828
         = IfaceLam <$> rnIfaceLamBndr lam_bndr <*> rnIfaceExpr expr
    
    829 829
     rnIfaceExpr (IfaceApp fun arg)
    
    830 830
         = IfaceApp <$> rnIfaceExpr fun <*> rnIfaceExpr arg
    
    831
    +rnIfaceExpr (IfaceApps fun args)
    
    832
    +    = IfaceApps <$> rnIfaceExpr fun <*> rnIfaceExprs args
    
    831 833
     rnIfaceExpr (IfaceCase scrut case_bndr alts)
    
    832 834
         = IfaceCase <$> rnIfaceExpr scrut
    
    833 835
                     <*> pure case_bndr
    

  • compiler/GHC/Iface/Syntax.hs
    ... ... @@ -10,7 +10,8 @@ module GHC.Iface.Syntax (
    10 10
     
    
    11 11
             IfaceDecl(..), IfaceFamTyConFlav(..), IfaceClassOp(..), IfaceAT(..),
    
    12 12
             IfaceConDecl(..), IfaceConDecls(..), IfaceEqSpec,
    
    13
    -        IfaceExpr(..), IfaceAlt(..), IfaceLetBndr(..), IfaceBinding,
    
    13
    +        IfaceExpr(..), mkIfaceApp,
    
    14
    +        IfaceAlt(..), IfaceLetBndr(..), IfaceBinding,
    
    14 15
             IfaceBindingX(..), IfaceMaybeRhs(..), IfaceConAlt(..),
    
    15 16
             IfaceIdInfo, IfaceIdDetails(..), IfaceUnfolding(..), IfGuidance(..),
    
    16 17
             IfaceInfoItem(..), IfaceRule(..), IfaceAnnotation(..), IfaceAnnTarget,
    
    ... ... @@ -96,8 +97,8 @@ import GHC.Utils.Fingerprint
    96 97
     import GHC.Utils.Binary
    
    97 98
     import GHC.Utils.Outputable as Outputable
    
    98 99
     import GHC.Utils.Panic
    
    99
    -import GHC.Utils.Misc( dropList, filterByList, notNull, unzipWith,
    
    100
    -                       zipWithEqual )
    
    100
    +import GHC.Utils.Misc( dropList, filterByList, notNull,
    
    101
    +                       unzipWith, zipWithEqual )
    
    101 102
     
    
    102 103
     import GHC.Data.FastString
    
    103 104
     import GHC.Data.BooleanFormula (pprBooleanFormula, isTrue)
    
    ... ... @@ -700,6 +701,11 @@ data IfaceExpr
    700 701
       | IfaceTuple  TupleSort [IfaceExpr]   -- Saturated; type arguments omitted
    
    701 702
       | IfaceLam    IfaceLamBndr IfaceExpr
    
    702 703
       | IfaceApp    IfaceExpr IfaceExpr
    
    704
    +        -- ^ Application to exactly one argument.
    
    705
    +        -- See Note [Iface applications]
    
    706
    +  | IfaceApps   IfaceExpr [IfaceExpr]
    
    707
    +        -- ^ Application to two or more arguments.
    
    708
    +        -- See Note [Iface applications]
    
    703 709
       | IfaceCase   IfaceExpr IfLclName [IfaceAlt]
    
    704 710
       | IfaceECase  IfaceExpr IfaceType     -- See Note [Empty case alternatives]
    
    705 711
       | IfaceLet    (IfaceBinding IfaceLetBndr) IfaceExpr
    
    ... ... @@ -710,6 +716,18 @@ data IfaceExpr
    710 716
       | IfaceFCall  ForeignCall IfaceType
    
    711 717
       | IfaceTick   IfaceTickish IfaceExpr    -- from Tick tickish E
    
    712 718
     
    
    719
    +-- | Apply an expression to a (possibly empty) list of arguments, maintaining
    
    720
    +-- the invariants of 'IfaceApp' and 'IfaceApps'.
    
    721
    +-- See Note [Iface applications].
    
    722
    +mkIfaceApp :: IfaceExpr -> [IfaceExpr] -> IfaceExpr
    
    723
    +mkIfaceApp fun args = go fun args
    
    724
    +  where
    
    725
    +    go (IfaceApp f a)   as = go f (a : as)
    
    726
    +    go (IfaceApps f fs) as = go f (fs ++ as)
    
    727
    +
    
    728
    +    go f []  = f
    
    729
    +    go f [a] = IfaceApp f a
    
    730
    +    go f as  = IfaceApps f as
    
    713 731
     
    
    714 732
     data IfaceTickish
    
    715 733
       = IfaceHpcTick    Module Int               -- from HpcTick x
    
    ... ... @@ -745,6 +763,30 @@ data IfaceTopBndrInfo = IfLclTopBndr IfLclName IfaceType IfaceIdInfo IfaceIdDeta
    745 763
     data IfaceMaybeRhs = IfUseUnfoldingRhs | IfRhs IfaceExpr
    
    746 764
     
    
    747 765
     {-
    
    766
    +Note [Iface applications]
    
    767
    +~~~~~~~~~~~~~~~~~~~~~~~~~
    
    768
    +A Core application chain (f a1 a2 ... an) could be represented by a chain of
    
    769
    +n nested IfaceApp nodes like Core does. However this is generally a worse
    
    770
    +representation for *serialization* which is the main purpose of the Iface type.
    
    771
    +
    
    772
    +So we keep the single argument constructor as it's fairly common, and add one
    
    773
    +to represent multiple arguments:
    
    774
    +
    
    775
    +  * IfaceApp  f a        -- exactly one argument
    
    776
    +  * IfaceApps f [a1,..]  -- two or more arguments
    
    777
    +
    
    778
    +with two invariants:
    
    779
    +
    
    780
    +  (1) The argument list of an IfaceApps has at least two elements.
    
    781
    +      (A one-argument application is an IfaceApp, and a zero-argument
    
    782
    +      "application" is just the head itself.)
    
    783
    +
    
    784
    +  (2) The head of an IfaceApp or IfaceApps is never itself an IfaceApp or
    
    785
    +      IfaceApps: application chains are fully flattened.
    
    786
    +
    
    787
    +The smart constructor 'mkIfaceApp' establishes both invariants; producers
    
    788
    +should use it rather than building IfaceApps directly.
    
    789
    +
    
    748 790
     Note [Empty case alternatives]
    
    749 791
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    750 792
     In Iface syntax an IfaceCase does not record the types of the alternatives,
    
    ... ... @@ -1797,7 +1839,8 @@ pprIfaceExpr _ (IfaceLitRubbish tc r)
    1797 1839
         <> (case tc of { TypeLike -> empty; ConstraintLike -> text "[c]" })
    
    1798 1840
         <> parens (ppr r)
    
    1799 1841
     
    
    1800
    -pprIfaceExpr add_par app@(IfaceApp _ _) = add_par (pprIfaceApp app [])
    
    1842
    +pprIfaceExpr add_par app@(IfaceApp _ _)  = add_par (pprIfaceApp app [])
    
    1843
    +pprIfaceExpr add_par app@(IfaceApps _ _) = add_par (pprIfaceApp app [])
    
    1801 1844
     
    
    1802 1845
     pprIfaceExpr add_par i@(IfaceLam _ _)
    
    1803 1846
       = add_par (sep [char '\\' <+> sep (map pprIfaceLamBndr bndrs) <+> arrow,
    
    ... ... @@ -1869,9 +1912,13 @@ pprIfaceTickish (IfaceBreakpoint (BreakpointId m ix) fvs)
    1869 1912
     
    
    1870 1913
     ------------------
    
    1871 1914
     pprIfaceApp :: IfaceExpr -> [SDoc] -> SDoc
    
    1872
    -pprIfaceApp (IfaceApp fun arg) args = pprIfaceApp fun $
    
    1915
    +-- NB: IfaceApps must print exactly like the equivalent IfaceApp chain, so
    
    1916
    +-- that --show-iface output does not depend on which one the producer emitted.
    
    1917
    +pprIfaceApp (IfaceApp fun arg)   args = pprIfaceApp fun $
    
    1873 1918
                                               nest 2 (pprParendIfaceExpr arg) : args
    
    1874
    -pprIfaceApp fun                args = sep (pprParendIfaceExpr fun : args)
    
    1919
    +pprIfaceApp (IfaceApps fun as)   args = pprIfaceApp fun $
    
    1920
    +                                          map (nest 2 . pprParendIfaceExpr) as ++ args
    
    1921
    +pprIfaceApp fun                  args = sep (pprParendIfaceExpr fun : args)
    
    1875 1922
     
    
    1876 1923
     ------------------
    
    1877 1924
     instance Outputable IfaceConAlt where
    
    ... ... @@ -2170,6 +2217,7 @@ freeNamesIfExpr (IfaceCo co) = freeNamesIfCoercion co
    2170 2217
     freeNamesIfExpr (IfaceTuple _ as)     = fnList freeNamesIfExpr as
    
    2171 2218
     freeNamesIfExpr (IfaceLam (b,_) body) = freeNamesIfBndr b &&& freeNamesIfExpr body
    
    2172 2219
     freeNamesIfExpr (IfaceApp f a)        = freeNamesIfExpr f &&& freeNamesIfExpr a
    
    2220
    +freeNamesIfExpr (IfaceApps f as)      = freeNamesIfExpr f &&& fnList freeNamesIfExpr as
    
    2173 2221
     freeNamesIfExpr (IfaceCast e co)      = freeNamesIfExpr e &&& freeNamesIfCoercion co
    
    2174 2222
     freeNamesIfExpr (IfaceTick t e)       = freeNamesIfTickish t &&& freeNamesIfExpr e
    
    2175 2223
     freeNamesIfExpr (IfaceECase e ty)     = freeNamesIfExpr e &&& freeNamesIfType ty
    
    ... ... @@ -2830,17 +2878,46 @@ infixl 9 .<<|.
    2830 2878
     x .<<|. b = (if b then (`setBit` 0) else id) (x `shiftL` 1)
    
    2831 2879
     {-# INLINE (.<<|.) #-}
    
    2832 2880
     
    
    2881
    +-- Encoding shortcuts:
    
    2882
    +-- Since only IfaceDataAlt can have binders
    
    2883
    +-- we can skip the binder list for DEFAULT and Literal alternatives.
    
    2833 2884
     instance Binary IfaceAlt where
    
    2834 2885
         put_ bh (IfaceAlt a b c) = do
    
    2835 2886
             put_ bh a
    
    2836
    -        put_ bh b
    
    2887
    +        case a of
    
    2888
    +          IfaceDataAlt {}  -> put_ bh b
    
    2889
    +          _                -> assertPpr (null b) (ppr a $$ ppr b) $ return ()
    
    2837 2890
             put_ bh c
    
    2838 2891
         get bh = do
    
    2839 2892
             a <- get bh
    
    2840
    -        b <- get bh
    
    2893
    +        b <- case a of
    
    2894
    +               IfaceDataAlt {}  -> get bh
    
    2895
    +               _                -> return []
    
    2841 2896
             c <- get bh
    
    2842 2897
             return (IfaceAlt a b c)
    
    2843 2898
     
    
    2899
    +{- Note [IfaceExpr encoding shortcuts]
    
    2900
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    2901
    +We use a full byte to encode the constructor tag for `IfaceExpr`.
    
    2902
    +This leaves room to encode additional information. Concretely we
    
    2903
    +use:
    
    2904
    +
    
    2905
    +0  .. 14: "Simple" constructor tags.
    
    2906
    +15 .. 22: "IfaceApps", encoding the constructor *and* arity.
    
    2907
    +      23: "IfaceCase" for a case with a single default alternative.
    
    2908
    +
    
    2909
    +Note [Binary encoding of IfaceApps]
    
    2910
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    2911
    +For IfaceApps we use the following scheme:
    
    2912
    +
    
    2913
    +  * arity 2..8:  one byte encoding the arity as (15 + (n-2))
    
    2914
    +                 Which is followed by the head expression and then exactly `arity` arguments.
    
    2915
    +
    
    2916
    +  * arity > 8:   tag 22, and we serialize the argument count as a ULEB128, followed by the
    
    2917
    +                 head expression and arguments.
    
    2918
    +
    
    2919
    +This saves us one byte per application with `2 <= arity <= 8`.
    
    2920
    +-}
    
    2844 2921
     instance Binary IfaceExpr where
    
    2845 2922
         put_ bh (IfaceLcl aa) = do
    
    2846 2923
             putByte bh 0
    
    ... ... @@ -2864,6 +2941,12 @@ instance Binary IfaceExpr where
    2864 2941
             putByte bh 5
    
    2865 2942
             put_ bh ag
    
    2866 2943
             put_ bh ah
    
    2944
    +    -- See Note [IfaceExpr encoding shortcuts]
    
    2945
    +    put_ bh (IfaceCase ai aj [IfaceAlt IfaceDefaultAlt [] ak]) = do
    
    2946
    +        putByte bh 23
    
    2947
    +        put_ bh ai
    
    2948
    +        put_ bh aj
    
    2949
    +        put_ bh ak
    
    2867 2950
         put_ bh (IfaceCase ai aj ak) = do
    
    2868 2951
             putByte bh 6
    
    2869 2952
             put_ bh ai
    
    ... ... @@ -2899,6 +2982,17 @@ instance Binary IfaceExpr where
    2899 2982
             putByte bh 14
    
    2900 2983
             put_ bh r
    
    2901 2984
             put_ bh torc
    
    2985
    +    -- See Note [Iface applications] and Note [Binary encoding of IfaceApps]
    
    2986
    +    -- and Note [IfaceExpr encoding shortcuts]
    
    2987
    +    put_ bh (IfaceApps fun args) = do
    
    2988
    +        let !n = length args
    
    2989
    +        massertPpr (n >= 2) (text "put_ IfaceApps" <+> ppr n)
    
    2990
    +        if n <= maxIfaceAppsTagArity
    
    2991
    +          then putByte bh (fromIntegral (ifaceAppsTag0 + n - 2))
    
    2992
    +          else do putByte bh (fromIntegral ifaceAppsBigTag)
    
    2993
    +                  put_ bh n
    
    2994
    +        put_ bh fun
    
    2995
    +        mapM_ (put_ bh) args
    
    2902 2996
         get bh = do
    
    2903 2997
             h <- getByte bh
    
    2904 2998
             case h of
    
    ... ... @@ -2944,7 +3038,41 @@ instance Binary IfaceExpr where
    2944 3038
                 14 -> do r <- get bh
    
    2945 3039
                          torc <- get bh
    
    2946 3040
                          return (IfaceLitRubbish torc r)
    
    3041
    +            -- Tags 15..21 encode an IfaceApps of arity 2..8 in the tag itself;
    
    3042
    +            -- tag 22 is followed by an explicit (LEB128) argument count.
    
    3043
    +            -- See Note [Binary encoding of IfaceApps]
    
    3044
    +            15 -> getApps 2
    
    3045
    +            16 -> getApps 3
    
    3046
    +            17 -> getApps 4
    
    3047
    +            18 -> getApps 5
    
    3048
    +            19 -> getApps 6
    
    3049
    +            20 -> getApps 7
    
    3050
    +            21 -> getApps 8
    
    3051
    +            22 -> do n <- get bh
    
    3052
    +                     getApps n
    
    3053
    +            -- case scrut of bndr { DEFAULT -> rhs}
    
    3054
    +            23 -> do ai <- get bh
    
    3055
    +                     aj <- get bh
    
    3056
    +                     ak <- get bh
    
    3057
    +                     return (IfaceCase ai aj [IfaceAlt IfaceDefaultAlt [] ak])
    
    2947 3058
                 _ -> panic ("get IfaceExpr " ++ show h)
    
    3059
    +      where
    
    3060
    +        getApps :: Int -> IO IfaceExpr
    
    3061
    +        getApps n = do fun  <- get bh
    
    3062
    +                       args <- replicateM n (get bh)
    
    3063
    +                       return (IfaceApps fun args)
    
    3064
    +-- | Tag used for an 'IfaceApps' with exactly two arguments and start
    
    3065
    +-- of the ifaceApps tag range.
    
    3066
    +ifaceAppsTag0 :: Int
    
    3067
    +ifaceAppsTag0 = 15
    
    3068
    +
    
    3069
    +-- | Highest arity encoded directly in tag byte.
    
    3070
    +maxIfaceAppsTagArity :: Int
    
    3071
    +maxIfaceAppsTagArity = 8
    
    3072
    +
    
    3073
    +-- | Tag for an 'IfaceApps' whose arity is serialized as ULEB128.
    
    3074
    +ifaceAppsBigTag :: Int
    
    3075
    +ifaceAppsBigTag = 22
    
    2948 3076
     
    
    2949 3077
     instance Binary IfaceTickish where
    
    2950 3078
         put_ bh (IfaceHpcTick m ix) = do
    
    ... ... @@ -3211,6 +3339,7 @@ instance NFData IfaceExpr where
    3211 3339
         IfaceTuple sort exprs -> rnf sort `seq` rnf exprs
    
    3212 3340
         IfaceLam bndr expr -> rnf bndr `seq` rnf expr
    
    3213 3341
         IfaceApp e1 e2 -> rnf e1 `seq` rnf e2
    
    3342
    +    IfaceApps e es -> rnf e `seq` rnf es
    
    3214 3343
         IfaceCase e nm alts -> rnf e `seq` rnf nm `seq` rnf alts
    
    3215 3344
         IfaceECase e ty -> rnf e `seq` rnf ty
    
    3216 3345
         IfaceLet bind e -> rnf bind `seq` rnf e
    

  • compiler/GHC/Iface/Type.hs
    ... ... @@ -1073,7 +1073,18 @@ pprIfaceTyConBinders suppress_sig = sep . map go
    1073 1073
           where
    
    1074 1074
             ppr_bndr = pprIfaceTvBndr bndr suppress_sig
    
    1075 1075
     
    
    1076
    +-- | IfaceBndr shortcuts:
    
    1077
    +--
    
    1078
    +-- In the vast majority of cases binder multiplicity is `Many` so storing it is
    
    1079
    +-- a pure waste of space. Instead of storing (Many, Name, Ty) we simply store
    
    1080
    +-- (Name,Ty) in the common case where multiplicity == Many.
    
    1076 1081
     instance Binary IfaceBndr where
    
    1082
    +    put_ bh (IfaceIdBndr (mult, name, ty))
    
    1083
    +      -- The implicit Many shortcut.
    
    1084
    +      | mult == many_ty = do
    
    1085
    +            putByte bh 2
    
    1086
    +            put_ bh name
    
    1087
    +            put_ bh ty
    
    1077 1088
         put_ bh (IfaceIdBndr aa) = do
    
    1078 1089
                 putByte bh 0
    
    1079 1090
                 put_ bh aa
    
    ... ... @@ -1085,8 +1096,11 @@ instance Binary IfaceBndr where
    1085 1096
                 case h of
    
    1086 1097
                   0 -> do aa <- get bh
    
    1087 1098
                           return (IfaceIdBndr aa)
    
    1088
    -              _ -> do ab <- get bh
    
    1099
    +              1 -> do ab <- get bh
    
    1089 1100
                           return (IfaceTvBndr ab)
    
    1101
    +              _ -> do name <- get bh
    
    1102
    +                      ty <- get bh
    
    1103
    +                      return (IfaceIdBndr (many_ty, name, ty))
    
    1090 1104
     
    
    1091 1105
     instance Binary IfaceOneShot where
    
    1092 1106
         put_ bh IfaceNoOneShot =
    

  • compiler/GHC/IfaceToCore.hs
    ... ... @@ -1446,6 +1446,7 @@ tcIfaceRule (IfaceRule {ifRuleName = name, ifActivation = act, ifRuleBndrs = bnd
    1446 1446
         ifTopFreeName (IfaceType (IfaceTyConApp tc _ )) = Just (ifaceTyConName tc)
    
    1447 1447
         ifTopFreeName (IfaceType (IfaceTupleTy s _ ts)) = Just (tupleTyConName s (length (appArgsIfaceTypes ts)))
    
    1448 1448
         ifTopFreeName (IfaceApp f _)                    = ifTopFreeName f
    
    1449
    +    ifTopFreeName (IfaceApps f _)                   = ifTopFreeName f
    
    1449 1450
         ifTopFreeName (IfaceExt n)                      = Just n
    
    1450 1451
         ifTopFreeName _                                 = Nothing
    
    1451 1452
     
    
    ... ... @@ -1682,6 +1683,9 @@ tcIfaceExpr (IfaceLam (bndr, os) body)
    1682 1683
     tcIfaceExpr (IfaceApp fun arg)
    
    1683 1684
       = App <$> tcIfaceExpr fun <*> tcIfaceExpr arg
    
    1684 1685
     
    
    1686
    +tcIfaceExpr (IfaceApps fun args)
    
    1687
    +  = mkApps <$> tcIfaceExpr fun <*> mapM tcIfaceExpr args
    
    1688
    +
    
    1685 1689
     tcIfaceExpr (IfaceECase scrut ty)
    
    1686 1690
       = do { scrut' <- tcIfaceExpr scrut
    
    1687 1691
            ; ty' <- tcIfaceType ty
    

  • compiler/GHC/Types/Literal.hs
    ... ... @@ -167,7 +167,7 @@ data LitNumType
    167 167
       | LitNumWord16  -- ^ @Word16#@ - exactly 16 bits
    
    168 168
       | LitNumWord32  -- ^ @Word32#@ - exactly 32 bits
    
    169 169
       | LitNumWord64  -- ^ @Word64#@ - exactly 64 bits
    
    170
    -  deriving (Data,Enum,Eq,Ord)
    
    170
    +  deriving (Data,Enum,Eq,Ord,Bounded)
    
    171 171
     
    
    172 172
     -- | Indicate if a numeric literal type supports negative numbers
    
    173 173
     litNumIsSigned :: LitNumType -> Bool
    
    ... ... @@ -259,6 +259,38 @@ for more details.
    259 259
     
    
    260 260
     -}
    
    261 261
     
    
    262
    +{-
    
    263
    +Note [Binary Literal encoding]
    
    264
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    265
    +Rather than write `LitNumType` into it's own tag byte we encode it in the
    
    266
    +surplus space of the `Literal` tag space.
    
    267
    +
    
    268
    +This means for `Literal` tag 0 .. 5 are the non-num literals.
    
    269
    +Literals 6 .. (maxBound LitNumType) encode the LitNumType.
    
    270
    +
    
    271
    +We could use the `LitNumType` information to slightly improve the encoding of
    
    272
    +the actual values too. But we just write/read them at Integer for simplicity for
    
    273
    +now.
    
    274
    +-}
    
    275
    +
    
    276
    +-- | The 'Binary' tag byte of @'LitNumber' nt _@.
    
    277
    +--
    
    278
    +-- These continue the tags of the non-numeric 'Literal' constructors. The
    
    279
    +-- mapping is total and part of the interface file format.
    
    280
    +-- See Note [Binary Literal encoding].
    
    281
    +litNumTypeTag :: LitNumType -> Word8
    
    282
    +litNumTypeTag nt =
    
    283
    +  -- 6 .. 16
    
    284
    +  6 + (fromIntegral $ fromEnum nt)
    
    285
    +
    
    286
    +-- | The inverse of 'litNumTypeTag'. 'Nothing' for a tag which isn't the tag
    
    287
    +-- of a numeric literal. See Note [Binary Literal encoding].
    
    288
    +litNumTypeOfTag :: Word8 -> Maybe LitNumType
    
    289
    +litNumTypeOfTag tag
    
    290
    +  | tag >= 6 && tag <= 16
    
    291
    +  = Just (toEnum $ (fromIntegral tag) - 6)
    
    292
    +  | otherwise = Nothing
    
    293
    +
    
    262 294
     instance Binary Literal where
    
    263 295
         put_ bh (LitChar aa)     = do putByte bh 0; put_ bh aa
    
    264 296
         put_ bh (LitString ab)   = do putByte bh 1; put_ bh ab
    
    ... ... @@ -269,9 +301,10 @@ instance Binary Literal where
    269 301
             = do putByte bh 5
    
    270 302
                  put_ bh aj
    
    271 303
                  put_ bh fod
    
    304
    +    -- The LitNumType is part of the tag byte.
    
    305
    +    -- See Note [Binary Literal encoding]
    
    272 306
         put_ bh (LitNumber nt i)
    
    273
    -        = do putByte bh 6
    
    274
    -             put_ bh nt
    
    307
    +        = do putByte bh (litNumTypeTag nt)
    
    275 308
                  put_ bh i
    
    276 309
         put_ _ lit@(LitRubbish {}) = pprPanic "Binary LitRubbish" (ppr lit)
    
    277 310
          -- We use IfaceLitRubbish; see Note [Rubbish literals], item (6)
    
    ... ... @@ -296,11 +329,11 @@ instance Binary Literal where
    296 329
                         aj <- get bh
    
    297 330
                         fod <- get bh
    
    298 331
                         return (LitLabel aj fod)
    
    299
    -              6 -> do
    
    300
    -                    nt <- get bh
    
    301
    -                    i  <- get bh
    
    302
    -                    return (LitNumber nt i)
    
    303
    -              _ -> pprPanic "Binary:Literal" (int (fromIntegral h))
    
    332
    +              _ | Just nt <- litNumTypeOfTag h
    
    333
    +                -> do i <- get bh
    
    334
    +                      return (LitNumber nt i)
    
    335
    +                | otherwise
    
    336
    +                -> pprPanic "Binary:Literal" (int (fromIntegral h))
    
    304 337
     
    
    305 338
     instance NFData Literal where
    
    306 339
         rnf (LitChar c) = rnf c
    

  • compiler/GHC/Types/Unique.hs
    ... ... @@ -393,7 +393,7 @@ unpkUnique u = case unpkUniqueGrimily u of
    393 393
     isValidKnownKeyUnique :: Unique -> Bool
    
    394 394
     isValidKnownKeyUnique u =
    
    395 395
         case unpkUniqueGrimily u of
    
    396
    -      (c, x) -> ord c < 0xff && x <= (1 `shiftL` 22)
    
    396
    +      (c, x) -> ord c < 0xff && x < (1 `shiftL` 22)
    
    397 397
     
    
    398 398
     {-
    
    399 399
     ************************************************************************
    

  • compiler/GHC/Utils/Binary.hs
    ... ... @@ -148,7 +148,7 @@ import GHCi.FFI
    148 148
     import GHCi.Message
    
    149 149
     
    
    150 150
     import Control.DeepSeq
    
    151
    -import Control.Monad            ( when, (<$!>), unless, forM_, void )
    
    151
    +import Control.Monad            ( when, unless, forM_, void )
    
    152 152
     import Foreign hiding (bit, setBit, clearBit, shiftL, shiftR, void)
    
    153 153
     import Data.Array
    
    154 154
     import Data.Array.Base (unsafeFreezeIOArray)
    
    ... ... @@ -173,7 +173,6 @@ import Data.Proxy
    173 173
     import Data.Set                 ( Set )
    
    174 174
     import qualified Data.Set as Set
    
    175 175
     import Data.Time hiding ( Nominal )
    
    176
    -import Data.List (unfoldr)
    
    177 176
     import System.IO as IO
    
    178 177
     import System.IO.Error          ( mkIOError, eofErrorType )
    
    179 178
     import Type.Reflection          ( Typeable, SomeTypeRep(..) )
    
    ... ... @@ -188,6 +187,7 @@ import GHC.ForeignPtr ( unsafeWithForeignPtr )
    188 187
     import GHC.Exts
    
    189 188
     import GHC.IO
    
    190 189
     import GHC.Word
    
    190
    +import GHC.Num (Integer(IS))
    
    191 191
     
    
    192 192
     import Unsafe.Coerce (unsafeCoerce)
    
    193 193
     import GHC.Serialized
    
    ... ... @@ -805,6 +805,7 @@ getULEB128 bh =
    805 805
     {-# SPECIALISE putSLEB128 :: WriteBinHandle -> Int64 -> IO () #-}
    
    806 806
     {-# SPECIALISE putSLEB128 :: WriteBinHandle -> Int32 -> IO () #-}
    
    807 807
     {-# SPECIALISE putSLEB128 :: WriteBinHandle -> Int16 -> IO () #-}
    
    808
    +{-# SPECIALISE putSLEB128 :: WriteBinHandle -> Integer -> IO () #-}
    
    808 809
     putSLEB128 :: forall a. (Integral a, Bits a) => WriteBinHandle -> a -> IO ()
    
    809 810
     putSLEB128 bh initial = go initial
    
    810 811
       where
    
    ... ... @@ -1123,86 +1124,91 @@ instance Binary IsBootInterface where
    1123 1124
                             False -> NotBoot
    
    1124 1125
     
    
    1125 1126
     {-
    
    1126
    -Finally - a reasonable portable Integer instance.
    
    1127
    +Note [Integer serialisation]
    
    1128
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    1129
    +We simply encode Integer as SLEB128 unconditionally.
    
    1127 1130
     
    
    1128
    -We used to encode values in the Int32 range as such,
    
    1129
    -falling back to a string of all things. In either case
    
    1130
    -we stored a tag byte to discriminate between the two cases.
    
    1131
    +This is a tradeoff:
    
    1132
    +It allows shorter encodings for the common case of small values. And we don't need
    
    1133
    +to prefix the string with a byte carrying any information about size or sign.
    
    1131 1134
     
    
    1132
    -This made some sense as it's highly portable but also not very
    
    1133
    -efficient.
    
    1135
    +However it means large Integer values will pay some overhead. Their encoding can
    
    1136
    +go from 9 (1 prefix, 8 value) to 10 bytes. Such values will also end up encoding
    
    1137
    +into a `Integer` accumulator rather than a simple Word64#.
    
    1134 1138
     
    
    1135
    -However GHC stores a surprisingly large number of large Integer
    
    1136
    -values. In the examples looked at between 25% and 50% of Integers
    
    1137
    -serialized were outside of the Int32 range.
    
    1139
    +In practice interface files have enough small values to make this tradeoff worthwhile.
    
    1140
    +We could also restore this benefit by using one or two *bits* rather than a full
    
    1141
    +byte for the prefix. I imagine this would be worthwhile in runtime, but I've not
    
    1142
    +gone as for for the sake of avoiding complexity.
    
    1138 1143
     
    
    1139
    -Consider a value like `2724268014499746065`, some sort of hash
    
    1140
    -actually generated by GHC.
    
    1141
    -In the old scheme this was encoded as a list of 19 chars. This
    
    1142
    -gave a size of 77 Bytes, one for the length of the list and 76
    
    1143
    -since we encode chars as Word32 as well.
    
    1144 1144
     
    
    1145
    -We can easily do better. The new plan is:
    
    1146
    -
    
    1147
    -* Start with a tag byte
    
    1148
    -  * 0 => Int64 (LEB128 encoded)
    
    1149
    -  * 1 => Negative large integer
    
    1150
    -  * 2 => Positive large integer
    
    1151
    -* Followed by the value:
    
    1152
    -  * Int64 is encoded as usual
    
    1153
    -  * Large integers are encoded as a list of bytes (Word8).
    
    1154
    -    We use Data.Bits which defines a bit order independent of the representation.
    
    1155
    -    Values are stored LSB first.
    
    1156
    -
    
    1157
    -This means our example value `2724268014499746065` is now only 10 bytes large.
    
    1158
    -* One byte tag
    
    1159
    -* One byte for the length of the [Word8] list.
    
    1160
    -* 8 bytes for the actual date.
    
    1161
    -
    
    1162
    -The new scheme also does not depend in any way on
    
    1163
    -architecture specific details.
    
    1164
    -
    
    1165
    -We still use this scheme even with LEB128 available,
    
    1166
    -as it has less overhead for truly large numbers. (> maxBound :: Int64)
    
    1167
    -
    
    1168
    -The instance is used for in Binary Integer and Binary Rational in GHC.Types.Literal
    
    1169 1145
     -}
    
    1170 1146
     
    
    1171 1147
     instance Binary Integer where
    
    1172
    -    put_ bh i
    
    1173
    -      | i >= lo64 && i <= hi64 = do
    
    1174
    -          putWord8 bh 0
    
    1175
    -          put_ bh (fromIntegral i :: Int64)
    
    1176
    -      | otherwise = do
    
    1177
    -          if i < 0
    
    1178
    -            then putWord8 bh 1
    
    1179
    -            else putWord8 bh 2
    
    1180
    -          put_ bh (unroll $ abs i)
    
    1148
    +    -- See Note [Integer serialisation]
    
    1149
    +    put_ bh (IS i)
    
    1150
    +      = putSLEB128 bh (I# i)
    
    1151
    +    put_ bh large_i
    
    1152
    +      = putSLEB128 bh large_i
    
    1181 1153
           where
    
    1182
    -        lo64 = fromIntegral (minBound :: Int64)
    
    1183
    -        hi64 = fromIntegral (maxBound :: Int64)
    
    1184
    -    get bh = do
    
    1185
    -      int_kind <- getWord8 bh
    
    1186
    -      case int_kind of
    
    1187
    -        0 -> fromIntegral <$!> (get bh :: IO Int64)
    
    1188
    -        -- Large integer
    
    1189
    -        1 -> negate <$!> getInt
    
    1190
    -        2 -> getInt
    
    1191
    -        _ -> panic "Binary Integer - Invalid byte"
    
    1192
    -        where
    
    1193
    -          getInt :: IO Integer
    
    1194
    -          getInt = roll <$!> (get bh :: IO [Word8])
    
    1195
    -
    
    1196
    -unroll :: Integer -> [Word8]
    
    1197
    -unroll = unfoldr step
    
    1198
    -  where
    
    1199
    -    step 0 = Nothing
    
    1200
    -    step i = Just (fromIntegral i, i `shiftR` 8)
    
    1154
    +    get bh = getSLEB128Integer bh
    
    1201 1155
     
    
    1202
    -roll :: [Word8] -> Integer
    
    1203
    -roll   = foldl' unstep 0 . reverse
    
    1156
    +-- | Read an SLEB128 encoded 'Integer'.
    
    1157
    +--
    
    1158
    +-- Unlike 'getSLEB128' this doesn't require a 'FiniteBits' instance, which
    
    1159
    +-- 'Integer' lacks. See Note [Integer serialisation].
    
    1160
    +getSLEB128Integer :: ReadBinHandle -> IO Integer
    
    1161
    +getSLEB128Integer bh = go_word 0 0
    
    1204 1162
       where
    
    1205
    -    unstep a b = a `shiftL` 8 .|. fromIntegral b
    
    1163
    +    -- Accumulate in a Word64 for as long as possible
    
    1164
    +    go_word :: Int -> Word64 -> IO Integer
    
    1165
    +    go_word !shift !acc = do
    
    1166
    +        byte <- getByte bh
    
    1167
    +        let !byteVal = clearBit byte 7
    
    1168
    +        let more = testBit byte 7
    
    1169
    +        let !shift' = shift + 7 -- bits read *after* this step
    
    1170
    +        -- Check if the payload still fits in the accumulator,
    
    1171
    +        -- if not swap to a Integer accumulator.
    
    1172
    +        if shift' <= 64
    
    1173
    +          then do
    
    1174
    +            let !acc' = acc .|. (fromIntegral byteVal `unsafeShiftL` shift)
    
    1175
    +            if more
    
    1176
    +              then go_word shift' acc'
    
    1177
    +              else return $! signExtendWord shift' acc' (testBit byte 6)
    
    1178
    +          else do
    
    1179
    +            -- They don't, so from here on out we use Integer arithmetic.
    
    1180
    +            let !acc' = toInteger acc .|. (toInteger byteVal `shiftL` shift)
    
    1181
    +            if more
    
    1182
    +              then go_big shift' acc'
    
    1183
    +              else return $! signExtendInteger shift' acc' (testBit byte 6)
    
    1184
    +
    
    1185
    +    go_big :: Int -> Integer -> IO Integer
    
    1186
    +    go_big !shift !acc = do
    
    1187
    +        byte <- getByte bh
    
    1188
    +        let !acc' = acc .|. (toInteger (clearBit byte 7) `shiftL` shift)
    
    1189
    +        let !more = testBit byte 7
    
    1190
    +        let !shift' = shift + 7
    
    1191
    +        if more
    
    1192
    +          then go_big shift' acc'
    
    1193
    +          else return $! signExtendInteger shift' acc' (testBit byte 6)
    
    1194
    +
    
    1195
    +    -- Sign extend a value of which we read `shift` bits into a Word64.
    
    1196
    +    -- `shift` is always <= 64 here, so the result always fits into an Int64.
    
    1197
    +    signExtendWord :: Int -> Word64 -> Bool -> Integer
    
    1198
    +    signExtendWord !shift !acc signed
    
    1199
    +      | not signed
    
    1200
    +      = toInteger acc
    
    1201
    +      | shift < 64
    
    1202
    +      -- set high bits not encoded in the payload
    
    1203
    +      = toInteger (fromIntegral (acc .|. (complement 0 `unsafeShiftL` shift)) :: Int64)
    
    1204
    +      | otherwise
    
    1205
    +      = toInteger (fromIntegral acc :: Int64)
    
    1206
    +
    
    1207
    +    -- Sign extend into an Integer.
    
    1208
    +    signExtendInteger :: Int -> Integer -> Bool -> Integer
    
    1209
    +    signExtendInteger !shift !acc signed
    
    1210
    +      | signed = acc - (1 `shiftL` shift)
    
    1211
    +      | otherwise = acc
    
    1206 1212
     
    
    1207 1213
     
    
    1208 1214
         {-
    

  • testsuite/tests/utils/should_run/Binary_Literal.hs
    1
    +{-# LANGUAGE ScopedTypeVariables #-}
    
    2
    +{-# LANGUAGE TypeApplications    #-}
    
    3
    +-- LLM generated test. So if it's weird it's for no good reason.
    
    4
    +--
    
    5
    +-- Property tests for the 'Binary' instance of 'Literal' in GHC.Types.Literal.
    
    6
    +--
    
    7
    +-- We check that
    
    8
    +--  * arbitrary literals round trip through 'put_' and 'get',
    
    9
    +--  * a whole batch of literals written into one buffer reads back in order,
    
    10
    +--    that is the reader consumes exactly the bytes the writer produced,
    
    11
    +--  * and, since numeric literals carry an 'Integer', that the SLEB128 based
    
    12
    +--    'Binary Integer' instance agrees with a reference implementation.
    
    13
    +module Main (main) where
    
    14
    +
    
    15
    +import GHC.Data.FastString
    
    16
    +import GHC.Platform ( genericPlatform )
    
    17
    +import GHC.Types.Basic ( FunctionOrData(..) )
    
    18
    +import GHC.Types.Literal
    
    19
    +import GHC.Types.Literal.Floating
    
    20
    +import GHC.Utils.Binary
    
    21
    +
    
    22
    +import Control.Monad ( replicateM )
    
    23
    +import Data.Bits
    
    24
    +import qualified Data.ByteString as BS
    
    25
    +import Data.Ratio ( (%) )
    
    26
    +import Data.Word
    
    27
    +import GHC.Float ( castFloatToWord32, castWord32ToFloat
    
    28
    +                 , castDoubleToWord64, castWord64ToDouble )
    
    29
    +import Numeric ( showHex )
    
    30
    +import System.IO.Unsafe ( unsafePerformIO )
    
    31
    +
    
    32
    +import MiniQuickCheck
    
    33
    +
    
    34
    +--------------------------------------------------------------------------------
    
    35
    +-- Encoding and decoding
    
    36
    +
    
    37
    +-- | 'LitLabel' contains a 'FastString', which the 'Binary' instance writes
    
    38
    +-- through a table in the handle's user data. Interface files fill this in with
    
    39
    +-- a deduplication table. We only need something that round trips, so we write
    
    40
    +-- the bytes of the string inline.
    
    41
    +withFastStringWriter :: WriteBinHandle -> WriteBinHandle
    
    42
    +withFastStringWriter = addWriterToUserData (BinaryWriter (\bh fs -> put_ bh (bytesFS fs)))
    
    43
    +
    
    44
    +withFastStringReader :: ReadBinHandle -> ReadBinHandle
    
    45
    +withFastStringReader = addReaderToUserData (BinaryReader (\bh -> mkFastStringByteString <$> get bh))
    
    46
    +
    
    47
    +-- | Serialise the values and also return the position after the last of them.
    
    48
    +encodeAll :: Binary a => [a] -> (BS.ByteString, Bin ())
    
    49
    +encodeAll xs = unsafePerformIO $ do
    
    50
    +  bh <- withFastStringWriter <$> openBinMem 1024
    
    51
    +  mapM_ (put_ bh) xs
    
    52
    +  end <- tellBinWriter bh
    
    53
    +  bs <- withBinBuffer bh (return . BS.copy)
    
    54
    +  return (bs, end)
    
    55
    +
    
    56
    +encode :: Binary a => a -> BS.ByteString
    
    57
    +encode x = fst (encodeAll [x])
    
    58
    +
    
    59
    +-- | Read back @n@ values and check that doing so consumed exactly the bytes
    
    60
    +-- the writer produced, no more and no less.
    
    61
    +decodeAll :: Binary a => Int -> (BS.ByteString, Bin ()) -> [a]
    
    62
    +decodeAll n (bs, end) = unsafePerformIO $ do
    
    63
    +  bh <- withFastStringReader <$> unsafeUnpackBinBuffer bs
    
    64
    +  xs <- replicateM n (get bh)
    
    65
    +  end' <- tellBinReader bh
    
    66
    +  if end' == end
    
    67
    +    then return xs
    
    68
    +    else fail $ "reader stopped at " ++ show end' ++ ", writer at " ++ show end
    
    69
    +
    
    70
    +roundTrip :: Binary a => [a] -> [a]
    
    71
    +roundTrip xs = decodeAll (length xs) (encodeAll xs)
    
    72
    +
    
    73
    +roundTrip1 :: Binary a => a -> a
    
    74
    +roundTrip1 x = case roundTrip [x] of
    
    75
    +  [x'] -> x'
    
    76
    +  _    -> error "roundTrip1"
    
    77
    +
    
    78
    +--------------------------------------------------------------------------------
    
    79
    +-- Literals with structural equality and a Show instance
    
    80
    +
    
    81
    +-- | 'Literal' has neither a 'Show' instance nor an 'Eq' instance which compares
    
    82
    +-- all fields: 'LitLabel' ignores the 'FunctionOrData' and 'LitFloating'
    
    83
    +-- identifies the different representations of the same value. For a
    
    84
    +-- serialisation test we want the stricter notion.
    
    85
    +newtype Lit = Lit Literal
    
    86
    +
    
    87
    +instance Show Lit where
    
    88
    +  show (Lit l) = showLit l
    
    89
    +
    
    90
    +instance Eq Lit where
    
    91
    +  Lit a == Lit b = eqLit a b
    
    92
    +
    
    93
    +eqLit :: Literal -> Literal -> Bool
    
    94
    +eqLit (LitLabel fs1 fod1) (LitLabel fs2 fod2)
    
    95
    +  = fs1 == fs2 && fod1 == fod2
    
    96
    +eqLit (LitFloating ty1 v1) (LitFloating ty2 v2)
    
    97
    +  -- 'Eq LitFloating' compares NaNs bitwise but identifies different
    
    98
    +  -- representations of the same value; the derived 'Show' distinguishes the
    
    99
    +  -- representations but not NaN payloads. Together they compare structurally.
    
    100
    +  = ty1 == ty2 && v1 == v2 && show v1 == show v2
    
    101
    +eqLit a b = a == b
    
    102
    +
    
    103
    +showLit :: Literal -> String
    
    104
    +showLit lit = case lit of
    
    105
    +  LitChar c        -> "LitChar " ++ show c
    
    106
    +  LitNumber nt i   -> "LitNumber " ++ showLitNumType nt ++ " " ++ show i
    
    107
    +  LitString bs     -> "LitString " ++ show bs
    
    108
    +  LitNullAddr      -> "LitNullAddr"
    
    109
    +  LitRubbish {}    -> "LitRubbish"
    
    110
    +  LitFloating ty v -> "LitFloating " ++ show ty ++ " (" ++ show v ++ ") " ++ bits ty v
    
    111
    +  LitLabel fs fod  -> "LitLabel " ++ show (bytesFS fs) ++ " " ++ showFod fod
    
    112
    +  where
    
    113
    +    -- The bit pattern is needed to tell apart NaNs.
    
    114
    +    bits LitFloat  v = "0x" ++ showHex (castFloatToWord32  (litFloatingToHostFloat  v)) ""
    
    115
    +    bits LitDouble v = "0x" ++ showHex (castDoubleToWord64 (litFloatingToHostDouble v)) ""
    
    116
    +
    
    117
    +    showFod IsFunction = "IsFunction"
    
    118
    +    showFod IsData     = "IsData"
    
    119
    +
    
    120
    +showLitNumType :: LitNumType -> String
    
    121
    +showLitNumType nt = case nt of
    
    122
    +  LitNumBigNat -> "LitNumBigNat"
    
    123
    +  LitNumInt    -> "LitNumInt"
    
    124
    +  LitNumInt8   -> "LitNumInt8"
    
    125
    +  LitNumInt16  -> "LitNumInt16"
    
    126
    +  LitNumInt32  -> "LitNumInt32"
    
    127
    +  LitNumInt64  -> "LitNumInt64"
    
    128
    +  LitNumWord   -> "LitNumWord"
    
    129
    +  LitNumWord8  -> "LitNumWord8"
    
    130
    +  LitNumWord16 -> "LitNumWord16"
    
    131
    +  LitNumWord32 -> "LitNumWord32"
    
    132
    +  LitNumWord64 -> "LitNumWord64"
    
    133
    +
    
    134
    +--------------------------------------------------------------------------------
    
    135
    +-- Generators
    
    136
    +
    
    137
    +-- | A number in @[0, n)@. Uses the high bits of the LCG state, which are the
    
    138
    +-- more random ones.
    
    139
    +choose :: Int -> Gen Int
    
    140
    +choose n = (`mod` n) . fromIntegral . (`shiftR` 32) <$> arbitraryWord64
    
    141
    +
    
    142
    +oneOf :: [Gen a] -> Gen a
    
    143
    +oneOf gens = do
    
    144
    +  i <- choose (length gens)
    
    145
    +  gens !! i
    
    146
    +
    
    147
    +listOf :: Int -> Gen a -> Gen [a]
    
    148
    +listOf maxLen gen = do
    
    149
    +  n <- choose (maxLen + 1)
    
    150
    +  replicateM n gen
    
    151
    +
    
    152
    +-- | 'MiniQuickCheck's 'Integer' instance generates values of up to 192 bits,
    
    153
    +-- which rarely hit the boundaries of the SLEB128 encoding. So we mix in small
    
    154
    +-- values and values around powers of two.
    
    155
    +genInteger :: Gen Integer
    
    156
    +genInteger = oneOf
    
    157
    +  [ arbitrary
    
    158
    +  , fromIntegral . subtract 300 <$> choose 601
    
    159
    +  , do k <- choose 200
    
    160
    +       d <- subtract 2 <$> choose 5
    
    161
    +       neg <- arbitrary
    
    162
    +       let v = 2 ^ k + toInteger d
    
    163
    +       return (if neg then negate v else v)
    
    164
    +  ]
    
    165
    +
    
    166
    +genLitNumType :: Gen LitNumType
    
    167
    +genLitNumType = oneOf (map pure [LitNumBigNat ..])
    
    168
    +
    
    169
    +-- | Numeric literals are always in range for their type, see
    
    170
    +-- Note [Word/Int underflow/overflow] in GHC.Types.Literal. The encoding is
    
    171
    +-- free to rely on that, so we generate only such literals.
    
    172
    +genLitNumber :: Gen Literal
    
    173
    +genLitNumber = do
    
    174
    +  nt <- genLitNumType
    
    175
    +  i  <- genInteger
    
    176
    +  -- 'mkLitNumberWrap' wraps into the range of the fixed width types but
    
    177
    +  -- refuses negative 'BigNat's.
    
    178
    +  let i' | LitNumBigNat <- nt = abs i
    
    179
    +         | otherwise          = i
    
    180
    +  return (mkLitNumberWrap genericPlatform nt i')
    
    181
    +
    
    182
    +-- | Random bit patterns, so that we also get infinities, negative zero,
    
    183
    +-- subnormals and NaNs with various payloads.
    
    184
    +genFloat :: Gen Float
    
    185
    +genFloat = castWord32ToFloat <$> arbitrary
    
    186
    +
    
    187
    +genDouble :: Gen Double
    
    188
    +genDouble = castWord64ToDouble <$> arbitrary
    
    189
    +
    
    190
    +genRational :: Gen Rational
    
    191
    +genRational = do
    
    192
    +  n <- genInteger
    
    193
    +  NonZero d <- arbitrary @(NonZero Integer)
    
    194
    +  return (n % d)
    
    195
    +
    
    196
    +genLitFloating :: Gen LitFloating
    
    197
    +genLitFloating = oneOf
    
    198
    +  [ floatToLitFloating <$> genFloat
    
    199
    +  , doubleToLitFloating <$> genDouble
    
    200
    +  , rationalToLitFloating <$> genRational
    
    201
    +  ]
    
    202
    +
    
    203
    +genLitFloatingType :: Gen LitFloatingType
    
    204
    +genLitFloatingType = oneOf [ pure LitFloat, pure LitDouble ]
    
    205
    +
    
    206
    +genByteString :: Gen BS.ByteString
    
    207
    +genByteString = BS.pack <$> listOf 64 arbitrary
    
    208
    +
    
    209
    +genFunctionOrData :: Gen FunctionOrData
    
    210
    +genFunctionOrData = oneOf [ pure IsFunction, pure IsData ]
    
    211
    +
    
    212
    +-- | Any literal except 'LitRubbish', which has no 'Binary' encoding, see
    
    213
    +-- Note [Rubbish literals] in GHC.Types.Literal.
    
    214
    +genLiteral :: Gen Literal
    
    215
    +genLiteral = oneOf
    
    216
    +  [ LitChar <$> arbitrary
    
    217
    +  , genLitNumber
    
    218
    +  , LitString <$> genByteString
    
    219
    +  , pure LitNullAddr
    
    220
    +  , LitFloating <$> genLitFloatingType <*> genLitFloating
    
    221
    +  , LitLabel <$> (mkFastStringByteString <$> genByteString) <*> genFunctionOrData
    
    222
    +  ]
    
    223
    +
    
    224
    +instance Arbitrary Lit where
    
    225
    +  arbitrary = Lit <$> genLiteral
    
    226
    +
    
    227
    +newtype Lits = Lits [Lit]
    
    228
    +  deriving (Eq, Show)
    
    229
    +
    
    230
    +instance Arbitrary Lits where
    
    231
    +  arbitrary = Lits <$> listOf 32 arbitrary
    
    232
    +
    
    233
    +newtype I = I Integer
    
    234
    +  deriving (Eq, Show)
    
    235
    +
    
    236
    +instance Arbitrary I where
    
    237
    +  arbitrary = I <$> genInteger
    
    238
    +
    
    239
    +--------------------------------------------------------------------------------
    
    240
    +-- Properties
    
    241
    +
    
    242
    +-- | Reference implementation of the SLEB128 encoding.
    
    243
    +slebRef :: Integer -> [Word8]
    
    244
    +slebRef = go
    
    245
    +  where
    
    246
    +    go val =
    
    247
    +      let byte = fromIntegral (val .&. 0x7f) :: Word8
    
    248
    +          val' = val `shiftR` 7
    
    249
    +          signBit = testBit byte 6
    
    250
    +          done = (val' == 0 && not signBit) || (val' == -1 && signBit)
    
    251
    +      in if done
    
    252
    +          then [byte]
    
    253
    +          else setBit byte 7 : go val'
    
    254
    +
    
    255
    +prop_literalRoundTrip :: Lit -> PropertyCheck
    
    256
    +prop_literalRoundTrip (Lit l) = Lit (roundTrip1 l) === Lit l
    
    257
    +
    
    258
    +prop_literalBatchRoundTrip :: Lits -> PropertyCheck
    
    259
    +prop_literalBatchRoundTrip (Lits ls) = Lits (map Lit (roundTrip [ l | Lit l <- ls ])) === Lits ls
    
    260
    +
    
    261
    +prop_integerRoundTrip :: I -> PropertyCheck
    
    262
    +prop_integerRoundTrip (I i) = roundTrip1 i === i
    
    263
    +
    
    264
    +prop_integerEncoding :: I -> PropertyCheck
    
    265
    +prop_integerEncoding (I i) = BS.unpack (encode i) === slebRef i
    
    266
    +
    
    267
    +tests :: Test
    
    268
    +tests = Group "Binary"
    
    269
    +  [ Group "Literal"
    
    270
    +      [ Property "round trip" prop_literalRoundTrip
    
    271
    +      , Property "batch round trip" prop_literalBatchRoundTrip
    
    272
    +      ]
    
    273
    +  , Group "Integer"
    
    274
    +      [ Property "round trip" prop_integerRoundTrip
    
    275
    +      , Property "SLEB128 encoding" prop_integerEncoding
    
    276
    +      ]
    
    277
    +  ]
    
    278
    +
    
    279
    +main :: IO ()
    
    280
    +main = runTestsMain (Iterations 1000) tests

  • testsuite/tests/utils/should_run/Binary_Literal.stdout
    1
    +Group Binary
    
    2
    +  Group Literal
    
    3
    +    Running round trip
    
    4
    +      Passed 1000 iterations
    
    5
    +    Running batch round trip
    
    6
    +      Passed 1000 iterations
    
    7
    +  Group Integer
    
    8
    +    Running round trip
    
    9
    +      Passed 1000 iterations
    
    10
    +    Running SLEB128 encoding
    
    11
    +      Passed 1000 iterations

  • testsuite/tests/utils/should_run/all.T
    1 1
     test('T15953', [ignore_stdout, js_skip], makefile_test, [])
    
    2
    +
    
    3
    +# Property tests for the 'Binary Literal' instance, which also exercises
    
    4
    +# GHCs 'Binary Integer' instance.
    
    5
    +test('Binary_Literal', [mini_quickcheck], multimod_compile_and_run,
    
    6
    +     ['Binary_Literal', '-package ghc'])

  • utils/haddock/haddock-api/src/Haddock/InterfaceFile.hs
    ... ... @@ -60,6 +60,7 @@ import Text.ParserCombinators.ReadP (readP_to_S)
    60 60
     import qualified Data.Text as T
    
    61 61
     
    
    62 62
     import Haddock.Options (Visibility (..))
    
    63
    +import qualified Data.Bits as Bits
    
    63 64
     
    
    64 65
     data InterfaceFile = InterfaceFile
    
    65 66
       { ifLinkEnv :: LinkEnv
    
    ... ... @@ -143,7 +144,7 @@ binaryInterfaceMagic = 0xD0Cface
    143 144
     --
    
    144 145
     binaryInterfaceVersion :: Word16
    
    145 146
     #if MIN_VERSION_ghc(9,11,0) && !MIN_VERSION_ghc(10,2,0)
    
    146
    -binaryInterfaceVersion = 47
    
    147
    +binaryInterfaceVersion = 48
    
    147 148
     
    
    148 149
     binaryInterfaceVersionCompatibility :: [Word16]
    
    149 150
     binaryInterfaceVersionCompatibility = [binaryInterfaceVersion]
    
    ... ... @@ -274,7 +275,7 @@ putName
    274 275
         do
    
    275 276
           (symtab_map, symtab_tbl) <- readIORef symtab_map_ref
    
    276 277
           case lookupNameEnv symtab_map name of
    
    277
    -        Just off -> put_ bh (fromIntegral off :: Word32)
    
    278
    +        Just off -> putNameIndex (fromIntegral off :: Word32)
    
    278 279
             Nothing -> do
    
    279 280
               off <- freshIndex
    
    280 281
               let mod' = nameModule name
    
    ... ... @@ -283,8 +284,10 @@ putName
    283 284
               let !symtab_map' = extendNameEnv symtab_map name off
    
    284 285
               let !symtab_tbl' = extendModuleEnv symtab_tbl mod' ((off, name):mod_nms)
    
    285 286
               writeIORef symtab_map_ref $! (symtab_map', symtab_tbl')
    
    286
    -          put_ bh (fromIntegral off :: Word32)
    
    287
    +          putNameIndex (fromIntegral off)
    
    287 288
       where
    
    289
    +    putNameIndex :: Word32 -> IO ()
    
    290
    +    putNameIndex off = put_ bh (off `Bits.shiftL` 1)
    
    288 291
         freshIndex :: IO Int
    
    289 292
         freshIndex = do
    
    290 293
           off <- readFastMutInt symtab_next