Rodrigo Mesquita pushed to branch wip/romes/27401-2 at Glasgow Haskell Compiler / GHC Commits: 8c210db9 by Rodrigo Mesquita at 2026-06-25T20:34:22+01:00 perf: Share Module in Iface Symbol Table This commit modifies the structure of the serialized `SymbolTable Name` to then re-use and share the `Module` (both on disk and in memory) across all `Name`s from the same module. The new structure looks like: <total name count> $modules.size for (mod, names) in $modules: $mod $names.size for table_ix, occ in $names $table_ix $occ i.e. we put the module just once, followed by all names in that module. When deserializing, we deserialize the module just once, and all the following `Name`s are constructed with a pointer to that same decoded `Module`. In `hoogle-test`, we must use `DNameEnv` rather than `Map Name`, otherwise the output fixities order was susceptible to changes in the uniques assigned to each Names, which is not stable. Fixes #27401 ------------------------- Metric Decrease: InstanceMatching LinkableUsage01 LinkableUsage02 hard_hole_fits ------------------------- - - - - - 12 changed files: - compiler/GHC/Iface/Binary.hs - compiler/GHC/Unit/Module/Env.hs - testsuite/tests/overloadedrecflds/should_compile/DRFPatSynExport.stdout - testsuite/tests/rename/should_compile/T1792_imports.stdout - testsuite/tests/rename/should_compile/T18264.stdout - testsuite/tests/rename/should_compile/T4239.stdout - testsuite/tests/showIface/DocsInHiFile1.stdout - testsuite/tests/showIface/DocsInHiFileTH.stdout - testsuite/tests/showIface/NoExportList.stdout - testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr - utils/haddock/haddock-api/src/Haddock/Interface/AttachInstances.hs - utils/haddock/haddock-api/src/Haddock/InterfaceFile.hs Changes: ===================================== compiler/GHC/Iface/Binary.hs ===================================== @@ -37,7 +37,6 @@ import GHC.Unit import GHC.Unit.Module.ModIface import GHC.Types.Name import GHC.Platform.Profile -import GHC.Types.Unique.FM import GHC.Utils.Panic import GHC.Utils.Binary as Binary import GHC.Data.FastMutInt @@ -61,7 +60,8 @@ import System.IO.Unsafe import Data.Typeable (Typeable) import qualified GHC.Data.Strict as Strict import Data.Function ((&)) - +import GHC.Types.Name.Env +import Data.Maybe -- --------------------------------------------------------------------------- -- Reading and writing binary interface files @@ -618,25 +618,27 @@ initNameReaderTable cache = do } data BinSymbolTable = BinSymbolTable { - bin_symtab_next :: !FastMutInt, -- The next index to use - bin_symtab_map :: !(IORef (UniqFM Name (Int,Name))) - -- indexed by Name + bin_symtab_next :: !FastMutInt, + -- ^ The next index to use + bin_symtab_map :: !(IORef (NameEnv Int, ModuleEnv [(Int,Name)])) + -- ^ Deduplication indexed by Name + -- ; Group table data by module for serialization } initNameWriterTable :: IO (WriterTable, BinaryWriter Name) initNameWriterTable = do symtab_next <- newFastMutInt 0 - symtab_map <- newIORef emptyUFM + symtab_map <- newIORef (emptyNameEnv, emptyModuleEnv) let bin_symtab = BinSymbolTable { bin_symtab_next = symtab_next - , bin_symtab_map = symtab_map + , bin_symtab_map = symtab_map } let put_symtab bh = do name_count <- readFastMutInt symtab_next - symtab_map <- readIORef symtab_map - putSymbolTable bh name_count symtab_map + (_, symtab_tbl) <- readIORef symtab_map + putSymbolTable bh name_count symtab_tbl pure name_count return @@ -647,40 +649,53 @@ initNameWriterTable = do ) -putSymbolTable :: WriteBinHandle -> Int -> UniqFM Name (Int,Name) -> IO () +{- | +The symbol table payload will look like: + + <total name count> + $modules.size + for (mod, names) in $modules: + $mod + $names.size + for table_ix, occ in $names + $table_ix + $occ +-} +putSymbolTable :: WriteBinHandle -> Int -> ModuleEnv [(Int, Name)] -> IO () putSymbolTable bh name_count symtab = do put_ bh name_count - let names = elems (array (0,name_count-1) (nonDetEltsUFM symtab)) - -- It's OK to use nonDetEltsUFM here because the elements have - -- indices that array uses to create order - mapM_ (\n -> serialiseName bh n symtab) names - - + put_ bh (sizeModuleEnv symtab) + forM_ (moduleEnvToList symtab) $ \(mod,names) -> do + put_ bh mod + put_ bh (length names) + forM_ names $ \(table_ix, name) -> do + let occ = assertPpr (isExternalName name) (ppr name) (nameOccName name) + put_ bh table_ix + put_ bh occ + +-- | Decode the symbol table -- layout set by 'putSymbolTable'. getSymbolTable :: ReadBinHandle -> NameCache -> IO (SymbolTable Name) getSymbolTable bh name_cache = do sz <- get bh :: IO Int -- create an array of Names for the symbols and add them to the NameCache updateNameCache' name_cache $ \cache0 -> do - mut_arr <- newArray_ (0, sz-1) :: IO (IOArray Int Name) - cache <- foldGet' (fromIntegral sz) bh cache0 $ \i (uid, mod_name, occ) cache -> do - let mod = mkModule uid mod_name - case lookupOrigNameCache cache mod occ of - Just name -> do - writeArray mut_arr (fromIntegral i) name - return cache - Nothing -> do - uniq <- takeUniqFromNameCache name_cache - let name = mkExternalName uniq mod occ noSrcSpan - new_cache = extendOrigNameCache cache mod occ name - writeArray mut_arr (fromIntegral i) name - return new_cache - arr <- unsafeFreeze mut_arr - return (cache, arr) - -serialiseName :: WriteBinHandle -> Name -> UniqFM key (Int,Name) -> IO () -serialiseName bh name _ = do - let mod = assertPpr (isExternalName name) (ppr name) (nameModule name) - put_ bh (moduleUnit mod, moduleName mod, nameOccName name) + mut_arr <- newArray_ (0, sz-1) :: IO (IOArray Int Name) + mods_sz <- get bh :: IO Int + cache <- + foldGet' (fromIntegral mods_sz) bh cache0 $ \_mod_ix (mod,mod_nms_sz) cache1 -> do + foldGet' (fromIntegral (mod_nms_sz::Int)) bh cache1 $ \_mod_nms_ix (table_ix, occ) cache2 -> do + case lookupOrigNameCache cache2 mod occ of + Just name -> do + writeArray mut_arr table_ix name + return cache2 + Nothing -> do + uniq <- takeUniqFromNameCache name_cache + let name = mkExternalName uniq mod occ noSrcSpan + cache3 = extendOrigNameCache cache2 mod occ name + writeArray mut_arr table_ix name + return cache3 + arr <- unsafeFreeze mut_arr + return (cache, arr) -- Note [Symbol table representation of names] @@ -714,16 +729,26 @@ putName BinSymbolTable{ .|. (fromIntegral u :: Word32)) | otherwise - = do symtab_map <- readIORef symtab_map_ref - case lookupUFM symtab_map name of - Just (off,_) -> put_ bh (fromIntegral off :: Word32) + = do (symtab_map,symtab_tbl) <- readIORef symtab_map_ref + case lookupNameEnv symtab_map name of + Just off -> put_ bh (fromIntegral off :: Word32) Nothing -> do - off <- readFastMutInt symtab_next - -- massert (off < 2^(30 :: Int)) - writeFastMutInt symtab_next (off+1) - writeIORef symtab_map_ref - $! addToUFM symtab_map name (off,name) - put_ bh (fromIntegral off :: Word32) + off <- freshIndex + let mod = nameModule name + let mod_nms = fromMaybe [] (lookupModuleEnv symtab_tbl mod) + + let !symtab_map' = extendNameEnv symtab_map name off + let !symtab_tbl' = extendModuleEnv symtab_tbl mod ((off,name):mod_nms) + writeIORef symtab_map_ref $! ( symtab_map', symtab_tbl' ) + + put_ bh (fromIntegral off :: Word32) + where + freshIndex :: IO Int + freshIndex = do + off <- readFastMutInt symtab_next + -- massert (off < 2^(30 :: Int)) + writeFastMutInt symtab_next (off+1) + return off -- See Note [Symbol table representation of names] getSymtabName :: SymbolTable Name ===================================== compiler/GHC/Unit/Module/Env.hs ===================================== @@ -9,7 +9,7 @@ module GHC.Unit.Module.Env , alterModuleEnv , partitionModuleEnv , moduleEnvKeys, moduleEnvElts, moduleEnvToList - , unitModuleEnv, isEmptyModuleEnv + , unitModuleEnv, isEmptyModuleEnv, sizeModuleEnv , extendModuleEnvWith, filterModuleEnv, mapMaybeModuleEnv -- * ModuleName mappings @@ -176,6 +176,9 @@ unitModuleEnv m x = ModuleEnv (Map.singleton (NDModule m) x) isEmptyModuleEnv :: ModuleEnv a -> Bool isEmptyModuleEnv (ModuleEnv e) = Map.null e +sizeModuleEnv :: ModuleEnv a -> Int +sizeModuleEnv (ModuleEnv e) = Map.size e + -- | A set of 'Module's type ModuleSet = Set NDModule ===================================== testsuite/tests/overloadedrecflds/should_compile/DRFPatSynExport.stdout ===================================== @@ -1 +1 @@ -import DRFPatSynExport_A ( MkT, m ) +import DRFPatSynExport_A ( m, MkT ) ===================================== testsuite/tests/rename/should_compile/T1792_imports.stdout ===================================== @@ -1 +1 @@ -import qualified Data.ByteString as B ( putStr, readFile ) +import qualified Data.ByteString as B ( readFile, putStr ) ===================================== testsuite/tests/rename/should_compile/T18264.stdout ===================================== @@ -1,6 +1,6 @@ import Data.Char ( isDigit, isLetter ) import Data.List ( sortOn ) -import Data.Maybe ( fromJust, isJust ) +import Data.Maybe ( isJust, fromJust ) import qualified Data.Char as C ( isLetter, isDigit ) import qualified Data.List as S ( sort ) import qualified Data.List as T ( nub ) ===================================== testsuite/tests/rename/should_compile/T4239.stdout ===================================== @@ -1 +1 @@ -import T4239A ( (·), type (:+++)((:---), (:+++), X) ) +import T4239A ( type (:+++)((:---), (:+++), X), (·) ) ===================================== testsuite/tests/showIface/DocsInHiFile1.stdout ===================================== @@ -19,49 +19,53 @@ docs: export docs: [] declaration docs: - [elem -> [text: - -- | '()', 'elem'. - identifiers: - {DocsInHiFile.hs:14:13-16} - GHC.Internal.Data.Foldable.elem - {DocsInHiFile.hs:14:13-16} - elem], - D -> [text: - -- | A datatype. + [F -> [text: + -- | A type family identifiers:], - D0 -> [text: - -- ^ A constructor for 'D'. ' - identifiers: - {DocsInHiFile.hs:20:32} - D], - D1 -> [text: - -- ^ Another constructor + D:R:FInt -> [text: + -- | A type family instance + identifiers:], + D' -> [text: + -- | Another datatype... + identifiers:, + text: + -- ^ ...with two docstrings. identifiers:], - P -> [text: - -- | A class - identifiers:], - p -> [text: - -- | A class method - identifiers:], $fShowD -> [text: -- ^ 'Show' instance identifiers: {DocsInHiFile.hs:22:25-28} GHC.Internal.Show.Show], - D' -> [text: - -- | Another datatype... - identifiers:, - text: - -- ^ ...with two docstrings. + p -> [text: + -- | A class method + identifiers:], + P -> [text: + -- | A class + identifiers:], + D1 -> [text: + -- ^ Another constructor identifiers:], - D:R:FInt -> [text: - -- | A type family instance - identifiers:], - F -> [text: - -- | A type family - identifiers:]] + D0 -> [text: + -- ^ A constructor for 'D'. ' + identifiers: + {DocsInHiFile.hs:20:32} + D], + D -> [text: + -- | A datatype. + identifiers:], + elem -> [text: + -- | '()', 'elem'. + identifiers: + {DocsInHiFile.hs:14:13-16} + GHC.Internal.Data.Foldable.elem + {DocsInHiFile.hs:14:13-16} + elem]] arg docs: - [add -> 0: + [p -> 0: + text: + -- ^ An argument + identifiers:, + add -> 0: text: -- ^ First summand for 'add' identifiers: @@ -74,11 +78,7 @@ docs: 2: text: -- ^ Sum - identifiers:, - p -> 0: - text: - -- ^ An argument - identifiers:] + identifiers:] documentation structure: avails: [elem] ===================================== testsuite/tests/showIface/DocsInHiFileTH.stdout ===================================== @@ -6,137 +6,153 @@ docs: export docs: [] declaration docs: - [Tup2 -> [text: - -- |Matches a tuple of (a, a) - identifiers:], - f -> [text: - -- |The meaning of life - identifiers:], - g -> [text: - -- |Some documentation - identifiers:], - qux -> [text: - -- |This is qux + [D:R:WD13Foo -> [text: + -- |13 + identifiers:], + D:R:WD11Int0 -> [text: + -- |This is a data instance + identifiers:], + D:R:WD11Foo0 -> [text: + -- |11 + identifiers:], + D:R:WD11Bool0 -> [text: + -- |This is a newtype instance + identifiers:], + D:R:EBool -> [text: + -- |A type family instance + identifiers:], + $fF -> [text: + -- |14 identifiers:], - sin -> [text: - -- |15 + $fDka -> [text: + -- |Another new instance + identifiers:], + $fCTYPEList -> [text: + -- |Another new instance + identifiers:], + $fCTYPEInt -> [text: + -- |A new instance + identifiers:], + $fCTYPEFoo -> [text: + -- |7 + identifiers:], + WD6 -> [text: + -- |6 identifiers:], - wd1 -> [text: - -- |1 + WD5 -> [text: + -- |5 identifiers:], - wd17 -> [text: - -- |17 + WD4 -> [text: + -- |4 + identifiers:], + WD3 -> [text: + -- |3 + identifiers:], + WD12 -> [text: + -- |12 identifiers:], - wd18 -> [text: - -- |18 + WD11Int -> [text: + -- |This is a data instance constructor + identifiers:], + WD11Bool -> [text: + -- |This is a newtype instance constructor + identifiers:], + WD10 -> [text: + -- |10 identifiers:], - wd2 -> [text: - -- |2 - identifiers:], - wd20 -> [text: - -- |20 + quuz1_a -> [text: + -- |This is the record constructor's argument + identifiers:], + Quuz -> [text: + -- |This is a record constructor identifiers:], - wd8 -> [text: - -- |8 + Quux2 -> [text: + -- |This is Quux2 + identifiers:], + Quux1 -> [text: + -- |This is Quux1 + identifiers:], + Quux -> [text: + -- |This is Quux + identifiers:], + prettyPrint -> [text: + -- |Prettily prints the object + identifiers:], + Pretty -> [text: + -- |My cool class + identifiers:], + Foo -> [text: + -- |A new constructor identifiers:], - C -> [text: - -- |A new class + Foo -> [text: + -- |A new data type + identifiers:], + E -> [text: + -- |A type family identifiers:], - Corge -> [text: - -- |This is a newtype record constructor - identifiers:], runCorge -> [text: -- |This is the newtype record constructor's argument identifiers:], - E -> [text: - -- |A type family + Corge -> [text: + -- |This is a newtype record constructor + identifiers:], + C -> [text: + -- |A new class identifiers:], - Foo -> [text: - -- |A new data type - identifiers:], - Foo -> [text: - -- |A new constructor + wd8 -> [text: + -- |8 identifiers:], - Pretty -> [text: - -- |My cool class - identifiers:], - prettyPrint -> [text: - -- |Prettily prints the object - identifiers:], - Quux -> [text: - -- |This is Quux - identifiers:], - Quux1 -> [text: - -- |This is Quux1 - identifiers:], - Quux2 -> [text: - -- |This is Quux2 - identifiers:], - Quuz -> [text: - -- |This is a record constructor + wd20 -> [text: + -- |20 identifiers:], - quuz1_a -> [text: - -- |This is the record constructor's argument - identifiers:], - WD10 -> [text: - -- |10 + wd2 -> [text: + -- |2 + identifiers:], + wd18 -> [text: + -- |18 identifiers:], - WD11Bool -> [text: - -- |This is a newtype instance constructor - identifiers:], - WD11Int -> [text: - -- |This is a data instance constructor - identifiers:], - WD12 -> [text: - -- |12 + wd17 -> [text: + -- |17 identifiers:], - WD3 -> [text: - -- |3 - identifiers:], - WD4 -> [text: - -- |4 - identifiers:], - WD5 -> [text: - -- |5 + wd1 -> [text: + -- |1 identifiers:], - WD6 -> [text: - -- |6 + sin -> [text: + -- |15 identifiers:], - $fCTYPEFoo -> [text: - -- |7 - identifiers:], - $fCTYPEInt -> [text: - -- |A new instance - identifiers:], - $fCTYPEList -> [text: - -- |Another new instance - identifiers:], - $fDka -> [text: - -- |Another new instance - identifiers:], - $fF -> [text: - -- |14 + qux -> [text: + -- |This is qux identifiers:], - D:R:EBool -> [text: - -- |A type family instance - identifiers:], - D:R:WD11Bool0 -> [text: - -- |This is a newtype instance - identifiers:], - D:R:WD11Foo0 -> [text: - -- |11 - identifiers:], - D:R:WD11Int0 -> [text: - -- |This is a data instance - identifiers:], - D:R:WD13Foo -> [text: - -- |13 - identifiers:]] + g -> [text: + -- |Some documentation + identifiers:], + f -> [text: + -- |The meaning of life + identifiers:], + Tup2 -> [text: + -- |Matches a tuple of (a, a) + identifiers:]] arg docs: - [Tup2 -> 0: - text: - -- |The thing to match twice - identifiers:, + [WD11Int -> 0: + text: + -- |This is a data instance constructor argument + identifiers:, + WD11Bool -> 0: + text: + -- |This is a newtype instance constructor argument + identifiers:, + Quux2 -> 1: + text: + -- |I am a bool + identifiers:, + Quux1 -> 0: + text: + -- |I am an integer + identifiers:, + qux -> 1: + text: + -- |Arg dos + identifiers:, h -> 0: text: -- ^Your favourite number @@ -149,26 +165,10 @@ docs: text: -- ^A return value identifiers:, - qux -> 1: - text: - -- |Arg dos - identifiers:, - Quux1 -> 0: - text: - -- |I am an integer - identifiers:, - Quux2 -> 1: - text: - -- |I am a bool - identifiers:, - WD11Bool -> 0: - text: - -- |This is a newtype instance constructor argument - identifiers:, - WD11Int -> 0: - text: - -- |This is a data instance constructor argument - identifiers:] + Tup2 -> 0: + text: + -- |The thing to match twice + identifiers:] documentation structure: avails: [f] ===================================== testsuite/tests/showIface/NoExportList.stdout ===================================== @@ -6,7 +6,10 @@ docs: export docs: [] declaration docs: - [fα -> [text: + [$fEqR -> [text: + -- | A very lazy Eq instance + identifiers:], + fα -> [text: -- ^ Documentation for 'R'\'s 'fα' field. identifiers: {NoExportList.hs:14:38} @@ -14,10 +17,7 @@ docs: {NoExportList.hs:14:38} R {NoExportList.hs:14:45-46} - fα], - $fEqR -> [text: - -- | A very lazy Eq instance - identifiers:]] + fα]] arg docs: [] documentation structure: @@ -99,3 +99,4 @@ docs: ListTuplePuns ImplicitStagePersistence extensible fields: + ===================================== testsuite/tests/typecheck/should_compile/subsumption_sort_hole_fits.stderr ===================================== @@ -5,10 +5,10 @@ subsumption_sort_hole_fits.hs:2:5: warning: [GHC-88464] [-Wtyped-holes (in -Wdef • Relevant bindings include f :: [String] (bound at subsumption_sort_hole_fits.hs:2:1) Valid hole fits include - lines :: String -> [String] + words :: String -> [String] (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.Data.OldList’)) - words :: String -> [String] + lines :: String -> [String] (imported from ‘Prelude’ (and originally defined in ‘GHC.Internal.Data.OldList’)) repeat :: forall a. a -> [a] ===================================== utils/haddock/haddock-api/src/Haddock/Interface/AttachInstances.hs ===================================== @@ -251,10 +251,12 @@ attachToExportItem cls_index fam_index index expInfo getInstDoc getFixity getIns } where fixities :: [(Name, Fixity)] - !fixities = force . Map.toList $ List.foldl' f Map.empty all_names + -- Use DNameEnv to guarantee a deterministic output regardless of the + -- uniques assigned to each_name e.g. off of the interface file. + !fixities = force . eltsDNameEnv $ List.foldl' f emptyDNameEnv all_names - f :: Map.Map Name Fixity -> Name -> Map.Map Name Fixity - f !fs n = Map.alter (<|> getFixity n) n fs + f :: DNameEnv (Name, Fixity) -> Name -> DNameEnv (Name, Fixity) + f !fs n = alterDNameEnv (<|> ((,) n <$> getFixity n)) fs n patsyn_names :: [Name] patsyn_names = concatMap (getMainDeclBinder emptyOccEnv . fst) patsyns ===================================== utils/haddock/haddock-api/src/Haddock/InterfaceFile.hs ===================================== @@ -40,6 +40,7 @@ import Data.Coerce (coerce) import Data.Function ((&)) import Data.IORef import Data.Map (Map) +import Data.Maybe (fromMaybe) import Data.Version import Data.Word import GHC hiding (NoLink) @@ -50,7 +51,9 @@ import GHC.Iface.Type (IfaceType, putIfaceType) import GHC.Types.Name.Cache import GHC.Types.Unique import GHC.Types.Unique.FM +import GHC.Types.Name.Env import GHC.Unit.State +import GHC.Unit.Module.Env import GHC.Utils.Binary import Haddock.Types import Text.ParserCombinators.ReadP (readP_to_S) @@ -139,7 +142,7 @@ binaryInterfaceMagic = 0xD0Cface -- binaryInterfaceVersion :: Word16 #if MIN_VERSION_ghc(9,11,0) && !MIN_VERSION_ghc(10,2,0) -binaryInterfaceVersion = 46 +binaryInterfaceVersion = 47 binaryInterfaceVersionCompatibility :: [Word16] binaryInterfaceVersionCompatibility = [binaryInterfaceVersion] @@ -170,7 +173,7 @@ writeInterfaceFile filename iface = do -- Make some intial state symtab_next <- newFastMutInt 0 - symtab_map <- newIORef emptyUFM + symtab_map <- newIORef (emptyNameEnv, emptyModuleEnv) let bin_symtab = BinSymbolTable { bin_symtab_next = symtab_next @@ -210,8 +213,8 @@ writeInterfaceFile filename iface = do -- write the symbol table itself symtab_next' <- readFastMutInt symtab_next - symtab_map' <- readIORef symtab_map - putSymbolTable bh symtab_next' symtab_map' + (_, symtab_tbl') <- readIORef symtab_map + putSymbolTable bh symtab_next' symtab_tbl' -- write the dictionary pointer at the fornt of the file dict_p <- tellBinWriter bh @@ -268,20 +271,30 @@ putName bh name = do - symtab_map <- readIORef symtab_map_ref - case lookupUFM symtab_map name of - Just (off, _) -> put_ bh (fromIntegral off :: Word32) + (symtab_map, symtab_tbl) <- readIORef symtab_map_ref + case lookupNameEnv symtab_map name of + Just off -> put_ bh (fromIntegral off :: Word32) Nothing -> do - off <- readFastMutInt symtab_next - writeFastMutInt symtab_next (off + 1) - writeIORef symtab_map_ref $! - addToUFM symtab_map name (off, name) + off <- freshIndex + let mod' = nameModule name + let mod_nms = fromMaybe [] (lookupModuleEnv symtab_tbl mod') + + let !symtab_map' = extendNameEnv symtab_map name off + let !symtab_tbl' = extendModuleEnv symtab_tbl mod' ((off, name):mod_nms) + writeIORef symtab_map_ref $! (symtab_map', symtab_tbl') put_ bh (fromIntegral off :: Word32) + where + freshIndex :: IO Int + freshIndex = do + off <- readFastMutInt symtab_next + writeFastMutInt symtab_next (off + 1) + return off data BinSymbolTable = BinSymbolTable { bin_symtab_next :: !FastMutInt -- The next index to use - , bin_symtab_map :: !(IORef (UniqFM Name (Int, Name))) - -- indexed by Name + , bin_symtab_map :: !(IORef (NameEnv Int, ModuleEnv [(Int, Name)])) + -- ^ Deduplication indexed by Name + -- ; Group table data by module for serialization } putFastString :: BinDictionary -> WriteBinHandle -> FastString -> IO () View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/8c210db9467d61f61423ba8bb5104be8... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/8c210db9467d61f61423ba8bb5104be8... You're receiving this email because of your account on gitlab.haskell.org.