Hannes Siebenhandl pushed to branch wip/fendor/external-unit-db-cache at Glasgow Haskell Compiler / GHC
Commits:
-
85ec1752
by fendor at 2026-07-22T13:18:01+02:00
16 changed files:
- compiler/GHC.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/Env.hs
- compiler/GHC/Driver/Session/Units.hs
- compiler/GHC/Unit/Env.hs
- compiler/GHC/Unit/External/Database.hs
- compiler/GHC/Unit/External/Index.hs
- compiler/GHC/Unit/External/ModuleOrigin.hs
- compiler/GHC/Unit/State.hs
- ghc/GHCi/UI.hs
- testsuite/tests/driver/TUnitInfo/Makefile
- testsuite/tests/driver/TUnitInfo/all.T
- + testsuite/tests/driver/TUnitInfo/genMhu.sh
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-mhu.stdout
- + testsuite/tests/driver/TUnitInfo/generic-unit-info-space-single.stdout
- testsuite/tests/driver/TUnitInfo/generic-unit-info-space.hs
Changes:
| ... | ... | @@ -671,7 +671,7 @@ setUnitDynFlagsNoCheck uid dflags1 = do |
| 671 | 671 | logger <- getLogger
|
| 672 | 672 | hsc_env <- getSession
|
| 673 | 673 | |
| 674 | - (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags1 (hscUIC hsc_env) (hscEUDC hsc_env) (hsc_all_home_unit_ids hsc_env)
|
|
| 674 | + (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags1 (hscUIC hsc_env) (hsc_all_home_unit_ids hsc_env)
|
|
| 675 | 675 | updated_dflags <- liftIO $ updatePlatformConstants dflags1 mconstants
|
| 676 | 676 | |
| 677 | 677 | let upd hue =
|
| ... | ... | @@ -760,7 +760,7 @@ setProgramDynFlags_ invalidate_needed dflags = do |
| 760 | 760 | old_hpt = homeUnitEnv_hpt homeUnitEnv
|
| 761 | 761 | home_units = HUG.allUnits (ue_home_unit_graph old_unit_env)
|
| 762 | 762 | |
| 763 | - (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags (ue_uic old_unit_env) (ue_eud old_unit_env) home_units
|
|
| 763 | + (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags (ue_uic old_unit_env) home_units
|
|
| 764 | 764 | |
| 765 | 765 | updated_dflags <- liftIO $ updatePlatformConstants dflags0 mconstants
|
| 766 | 766 | pure HomeUnitEnv
|
| ... | ... | @@ -778,7 +778,6 @@ setProgramDynFlags_ invalidate_needed dflags = do |
| 778 | 778 | , ue_current_unit = ue_currentUnit old_unit_env
|
| 779 | 779 | , ue_module_graph = ue_module_graph old_unit_env
|
| 780 | 780 | , ue_eps = ue_eps old_unit_env
|
| 781 | - , ue_eud = ue_eud old_unit_env
|
|
| 782 | 781 | , ue_uic = ue_uic old_unit_env
|
| 783 | 782 | }
|
| 784 | 783 | modifySession $ \h -> hscSetFlags dflags1 h{ hsc_unit_env = unit_env }
|
| ... | ... | @@ -837,7 +836,6 @@ setProgramHUG_ invalidate_needed new_hug0 = do |
| 837 | 836 | , ue_current_unit = ue_currentUnit unit_env0
|
| 838 | 837 | , ue_eps = ue_eps unit_env0
|
| 839 | 838 | , ue_module_graph = ue_module_graph unit_env0
|
| 840 | - , ue_eud = ue_eud unit_env0
|
|
| 841 | 839 | , ue_uic = ue_uic unit_env0
|
| 842 | 840 | }
|
| 843 | 841 | modifySession $ \h ->
|
| ... | ... | @@ -886,7 +884,7 @@ setProgramHUG_ invalidate_needed new_hug0 = do |
| 886 | 884 | old_hpt = homeUnitEnv_hpt homeUnitEnv
|
| 887 | 885 | home_units = HUG.allUnits (ue_home_unit_graph unit_env)
|
| 888 | 886 | |
| 889 | - (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags (ue_uic unit_env) (ue_eud unit_env) home_units
|
|
| 887 | + (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags (ue_uic unit_env) home_units
|
|
| 890 | 888 | |
| 891 | 889 | updated_dflags <- liftIO $ updatePlatformConstants dflags mconstants
|
| 892 | 890 | pure HomeUnitEnv
|
| ... | ... | @@ -92,7 +92,7 @@ import GHC.Types.Error (mkUnknownDiagnostic) |
| 92 | 92 | import qualified GHC.Unit.Home.Graph as HUG
|
| 93 | 93 | import GHC.Unit.Home.ModInfo
|
| 94 | 94 | import GHC.Unit.Home.PackageTable
|
| 95 | -import GHC.Unit.External.Database (cacheExternalUnitDatabase)
|
|
| 95 | +import GHC.Unit.External.Index (cacheExternalUnitDatabase)
|
|
| 96 | 96 | |
| 97 | 97 | -- | Entry point to compile a Backpack file.
|
| 98 | 98 | doBackpack :: [FilePath] -> Ghc ()
|
| ... | ... | @@ -442,8 +442,8 @@ addInMemoryDatabase dflags u = do |
| 442 | 442 | { unitDatabasePath = unsafeEncodeUtf $ "(in memory " ++ showSDoc dflags (ppr (unitId u)) ++ ")"
|
| 443 | 443 | , unitDatabaseUnits = [u]
|
| 444 | 444 | }
|
| 445 | - let eud = hscEUDC hsc_env
|
|
| 446 | - liftIO $ cacheExternalUnitDatabase eud newdb
|
|
| 445 | + let uic = hscUIC hsc_env
|
|
| 446 | + liftIO $ cacheExternalUnitDatabase uic newdb
|
|
| 447 | 447 | -- added at the end because ordering matters
|
| 448 | 448 | pure dflags
|
| 449 | 449 | { packageDBFlags = packageDBFlags dflags ++ [PackageDB (PkgDbPath (unitDatabasePath newdb))]
|
| ... | ... | @@ -456,11 +456,10 @@ addUnit u = do |
| 456 | 456 | logger <- getLogger
|
| 457 | 457 | let dflags0 = hsc_dflags hsc_env
|
| 458 | 458 | let old_unit_env = hsc_unit_env hsc_env
|
| 459 | - let eud = hscEUDC hsc_env
|
|
| 460 | 459 | |
| 461 | 460 | dflags1 <- addInMemoryDatabase dflags0 u
|
| 462 | 461 | |
| 463 | - (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags1 (ue_uic old_unit_env) eud (hsc_all_home_unit_ids hsc_env)
|
|
| 462 | + (unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags1 (ue_uic old_unit_env) (hsc_all_home_unit_ids hsc_env)
|
|
| 464 | 463 | |
| 465 | 464 | |
| 466 | 465 | -- update platform constants
|
| ... | ... | @@ -477,7 +476,6 @@ addUnit u = do |
| 477 | 476 | (HUG.mkHomeUnitEnv unit_state dflags (ue_hpt old_unit_env) (Just home_unit))
|
| 478 | 477 | , ue_eps = ue_eps old_unit_env
|
| 479 | 478 | , ue_module_graph = ue_module_graph old_unit_env
|
| 480 | - , ue_eud = ue_eud old_unit_env
|
|
| 481 | 479 | , ue_uic = ue_uic old_unit_env
|
| 482 | 480 | }
|
| 483 | 481 | setSession $ hscSetFlags dflags $ hsc_env { hsc_unit_env = unit_env }
|
| ... | ... | @@ -14,7 +14,6 @@ module GHC.Driver.Env |
| 14 | 14 | , hsc_HUG
|
| 15 | 15 | , hsc_all_home_unit_ids
|
| 16 | 16 | , hscUnitIndex
|
| 17 | - , hscUIC
|
|
| 18 | 17 | , hscUpdateLoggerFlags
|
| 19 | 18 | , hscUpdateHUG
|
| 20 | 19 | , hscInsertHPT
|
| ... | ... | @@ -27,7 +26,7 @@ module GHC.Driver.Env |
| 27 | 26 | , runInteractiveHsc
|
| 28 | 27 | , hscEPS
|
| 29 | 28 | , hscEUD
|
| 30 | - , hscEUDC
|
|
| 29 | + , hscUIC
|
|
| 31 | 30 | , hscInterp
|
| 32 | 31 | , prepareAnnotations
|
| 33 | 32 | , discardIC
|
| ... | ... | @@ -228,10 +227,7 @@ hscEPS :: HscEnv -> IO ExternalPackageState |
| 228 | 227 | hscEPS hsc_env = readIORef (euc_eps (ue_eps (hsc_unit_env hsc_env)))
|
| 229 | 228 | |
| 230 | 229 | hscEUD :: HscEnv -> IO (ExternalUnitDatabases UnitId)
|
| 231 | -hscEUD = readExternalUnitDatabases . hscEUDC
|
|
| 232 | - |
|
| 233 | -hscEUDC :: HscEnv -> ExternalUnitDatabaseCache UnitId
|
|
| 234 | -hscEUDC hsc_env = ue_eud (hsc_unit_env hsc_env)
|
|
| 230 | +hscEUD = readExternalUnitDatabases . hscUIC
|
|
| 235 | 231 | |
| 236 | 232 | hscUnitIndex :: HscEnv -> IO UnitIndex
|
| 237 | 233 | hscUnitIndex hsc_env = ueUI (hsc_unit_env hsc_env)
|
| ... | ... | @@ -131,7 +131,7 @@ initMulti unitArgsFiles lintDynFlagsAndSrcs = do |
| 131 | 131 | home_unit_graph <- forM initial_home_graph $ \homeUnitEnv -> do
|
| 132 | 132 | let hue_flags = homeUnitEnv_dflags homeUnitEnv
|
| 133 | 133 | dflags = homeUnitEnv_dflags homeUnitEnv
|
| 134 | - (unit_state,home_unit,mconstants) <- liftIO $ State.initUnits logger hue_flags (hscUIC hsc_env) (hscEUDC hsc_env) home_units
|
|
| 134 | + (unit_state,home_unit,mconstants) <- liftIO $ State.initUnits logger hue_flags (hscUIC hsc_env) home_units
|
|
| 135 | 135 | |
| 136 | 136 | updated_dflags <- liftIO $ updatePlatformConstants dflags mconstants
|
| 137 | 137 | emptyHpt <- liftIO $ emptyHomePackageTable
|
| ... | ... | @@ -178,19 +178,21 @@ data UnitEnv = UnitEnv |
| 178 | 178 | , ue_namever :: !GhcNameVersion
|
| 179 | 179 | -- ^ GHC name/version (used for dynamic library suffix)
|
| 180 | 180 | |
| 181 | - , ue_eud :: {-# UNPACK #-} !(ExternalUnitDatabaseCache UnitId)
|
|
| 182 | - -- ^ Global cache of already read package databases
|
|
| 183 | - |
|
| 184 | 181 | , ue_uic :: {-# UNPACK #-} !UnitIndexCache
|
| 185 | - -- ^ Index of already processed 'UnitInfo's.
|
|
| 186 | - -- Shares state over all 'UnitState' in the 'HomeUnitGraph'.
|
|
| 182 | + -- ^ Global index of already processed external units.
|
|
| 183 | + -- Shares state over all 'UnitState's in the 'HomeUnitGraph'.
|
|
| 184 | + --
|
|
| 185 | + -- Allows sharing of 'UnitInfo's, ensuring each individual 'UnitInfo'
|
|
| 186 | + -- is retained a constant number of times.
|
|
| 187 | + --
|
|
| 188 | + -- See Note [Sharing 'UnitInfo's across the 'UnitEnv'] for details.
|
|
| 187 | 189 | }
|
| 188 | 190 | |
| 189 | 191 | ueEPS :: UnitEnv -> IO ExternalPackageState
|
| 190 | 192 | ueEPS = eucEPS . ue_eps
|
| 191 | 193 | |
| 192 | 194 | ueEUD :: UnitEnv -> IO (ExternalUnitDatabases UnitId)
|
| 193 | -ueEUD = readExternalUnitDatabases . ue_eud
|
|
| 195 | +ueEUD = readExternalUnitDatabases . ue_uic
|
|
| 194 | 196 | |
| 195 | 197 | ueUI :: UnitEnv -> IO UnitIndex
|
| 196 | 198 | ueUI = readUnitIndex . ue_uic
|
| ... | ... | @@ -199,7 +201,6 @@ ueUI = readUnitIndex . ue_uic |
| 199 | 201 | initUnitEnv :: UnitId -> HomeUnitGraph -> GhcNameVersion -> Platform -> IO UnitEnv
|
| 200 | 202 | initUnitEnv cur_unit hug namever platform = do
|
| 201 | 203 | eps <- initExternalUnitCache
|
| 202 | - eud <- initExternalUnitDatabaseCache
|
|
| 203 | 204 | uic <- initUnitIndexCache
|
| 204 | 205 | return $ UnitEnv
|
| 205 | 206 | { ue_eps = eps
|
| ... | ... | @@ -208,7 +209,6 @@ initUnitEnv cur_unit hug namever platform = do |
| 208 | 209 | , ue_current_unit = cur_unit
|
| 209 | 210 | , ue_platform = platform
|
| 210 | 211 | , ue_namever = namever
|
| 211 | - , ue_eud = eud
|
|
| 212 | 212 | , ue_uic = uic
|
| 213 | 213 | }
|
| 214 | 214 |
| 1 | 1 | module GHC.Unit.External.Database (
|
| 2 | - -- * Mutable cache for 'ExternalUnitDatabases'
|
|
| 3 | - ExternalUnitDatabaseCache (..),
|
|
| 4 | - initExternalUnitDatabaseCache,
|
|
| 5 | - readExternalUnitDatabases,
|
|
| 6 | - readExternalUnitDatabase,
|
|
| 7 | - cacheExternalUnitDatabase,
|
|
| 8 | - clearExternalUnitDatabaseCache,
|
|
| 9 | 2 | -- * 'ExternalUnitDatabases'
|
| 10 | 3 | ExternalUnitDatabases,
|
| 11 | 4 | emptyExternalUnitDatabases,
|
| 12 | 5 | insertExternalUnitDatabases,
|
| 13 | 6 | deleteExternalUnitDatabases,
|
| 14 | 7 | lookupExternalUnitDatabases,
|
| 15 | - -- * 'UnitDatabase'
|
|
| 8 | + -- * 'UnitDatabase' and how to merge them.
|
|
| 16 | 9 | UnitDatabase (..),
|
| 17 | 10 | mergeDatabases,
|
| 18 | 11 | UnitPrecedenceMap,
|
| ... | ... | @@ -20,8 +13,6 @@ module GHC.Unit.External.Database ( |
| 20 | 13 | compareByPreference,
|
| 21 | 14 | -- * Reading packages from disk.
|
| 22 | 15 | UnitDbConfig (..),
|
| 23 | - readOrGetUnitDatabase,
|
|
| 24 | - readUnitDatabases,
|
|
| 25 | 16 | readUnitDatabase,
|
| 26 | 17 | getUnitDbRefs,
|
| 27 | 18 | resolveUnitDatabase,
|
| ... | ... | @@ -48,8 +39,6 @@ import GHC.Utils.Panic |
| 48 | 39 | |
| 49 | 40 | import Control.Monad
|
| 50 | 41 | import Data.Char
|
| 51 | -import Data.IORef
|
|
| 52 | -import Data.IORef qualified as IORef
|
|
| 53 | 42 | import Data.List (sortBy)
|
| 54 | 43 | import Data.Map.Strict (Map)
|
| 55 | 44 | import Data.Map.Strict qualified as Map
|
| ... | ... | @@ -60,40 +49,6 @@ import System.Directory |
| 60 | 49 | import System.Environment (getEnv)
|
| 61 | 50 | import System.FilePath as FilePath
|
| 62 | 51 | |
| 63 | --- ----------------------------------------------------------------------------
|
|
| 64 | --- ExternalUnitDatabaseCache
|
|
| 65 | --- ----------------------------------------------------------------------------
|
|
| 66 | - |
|
| 67 | --- | Mutable wrapper around 'ExternalUnitDatabases'.
|
|
| 68 | -newtype ExternalUnitDatabaseCache unit = ExternalUnitDatabaseCache
|
|
| 69 | - { eudc_databases :: IORef (ExternalUnitDatabases unit)
|
|
| 70 | - }
|
|
| 71 | - |
|
| 72 | -initExternalUnitDatabaseCache :: IO (ExternalUnitDatabaseCache unit)
|
|
| 73 | -initExternalUnitDatabaseCache =
|
|
| 74 | - ExternalUnitDatabaseCache <$> IORef.newIORef emptyExternalUnitDatabases
|
|
| 75 | - |
|
| 76 | -readExternalUnitDatabases :: ExternalUnitDatabaseCache unit -> IO (ExternalUnitDatabases unit)
|
|
| 77 | -readExternalUnitDatabases eudc =
|
|
| 78 | - IORef.readIORef (eudc_databases eudc)
|
|
| 79 | - |
|
| 80 | -modifyExternalUnitDatabaseCache :: ExternalUnitDatabaseCache unit -> (ExternalUnitDatabases unit -> ExternalUnitDatabases unit) -> IO ()
|
|
| 81 | -modifyExternalUnitDatabaseCache eudc f =
|
|
| 82 | - IORef.modifyIORef' (eudc_databases eudc) f
|
|
| 83 | - |
|
| 84 | -readExternalUnitDatabase :: ExternalUnitDatabaseCache unit -> OsPath -> IO (Maybe (UnitDatabase unit))
|
|
| 85 | -readExternalUnitDatabase eudc path = do
|
|
| 86 | - dbs <- readExternalUnitDatabases eudc
|
|
| 87 | - pure $ lookupExternalUnitDatabases path dbs
|
|
| 88 | - |
|
| 89 | -cacheExternalUnitDatabase :: ExternalUnitDatabaseCache unit -> UnitDatabase unit -> IO ()
|
|
| 90 | -cacheExternalUnitDatabase eudc db =
|
|
| 91 | - modifyExternalUnitDatabaseCache eudc (insertExternalUnitDatabases db)
|
|
| 92 | - |
|
| 93 | -clearExternalUnitDatabaseCache :: ExternalUnitDatabaseCache unit -> IO ()
|
|
| 94 | -clearExternalUnitDatabaseCache eudc =
|
|
| 95 | - modifyExternalUnitDatabaseCache eudc (const emptyExternalUnitDatabases)
|
|
| 96 | - |
|
| 97 | 52 | -- ----------------------------------------------------------------------------
|
| 98 | 53 | -- ExternalUnitDatabases
|
| 99 | 54 | -- ----------------------------------------------------------------------------
|
| ... | ... | @@ -230,12 +185,6 @@ data UnitDbConfig = UnitDbConfig |
| 230 | 185 | , unitDbConfigGHCDir :: FilePath
|
| 231 | 186 | }
|
| 232 | 187 | |
| 233 | -readUnitDatabases :: Logger -> ExternalUnitDatabaseCache UnitId -> UnitDbConfig -> IO [UnitDatabase UnitId]
|
|
| 234 | -readUnitDatabases logger db_cache cfg = do
|
|
| 235 | - conf_refs <- getUnitDbRefs cfg
|
|
| 236 | - confs <- liftM catMaybes $ mapM (resolveUnitDatabase cfg) conf_refs
|
|
| 237 | - mapM (readOrGetUnitDatabase logger db_cache cfg) confs
|
|
| 238 | - |
|
| 239 | 188 | getUnitDbRefs :: UnitDbConfig -> IO [PkgDbRef]
|
| 240 | 189 | getUnitDbRefs cfg = do
|
| 241 | 190 | let system_conf_refs = [UserPkgDb, GlobalPkgDb]
|
| ... | ... | @@ -285,17 +234,6 @@ resolveUnitDatabase cfg UserPkgDb = runMaybeT $ do |
| 285 | 234 | if exist then return (OsPath.unsafeEncodeUtf pkgconf) else mzero
|
| 286 | 235 | resolveUnitDatabase _ (PkgDbPath name) = return $ Just name
|
| 287 | 236 | |
| 288 | --- | Get the cached 'UnitDatabase' or read the 'UnitDatabase' at the given location.
|
|
| 289 | -readOrGetUnitDatabase :: Logger -> ExternalUnitDatabaseCache UnitId -> UnitDbConfig -> OsPath -> IO (UnitDatabase UnitId)
|
|
| 290 | -readOrGetUnitDatabase logger db_cache cfg conf_file =
|
|
| 291 | - readExternalUnitDatabase db_cache conf_file >>= \ case
|
|
| 292 | - Nothing -> do
|
|
| 293 | - new_db <- readUnitDatabase logger cfg conf_file
|
|
| 294 | - cacheExternalUnitDatabase db_cache new_db
|
|
| 295 | - pure new_db
|
|
| 296 | - Just db ->
|
|
| 297 | - pure db
|
|
| 298 | - |
|
| 299 | 237 | -- | Read the 'UnitDatabase' at the given location.
|
| 300 | 238 | readUnitDatabase :: Logger -> UnitDbConfig -> OsPath -> IO (UnitDatabase UnitId)
|
| 301 | 239 | readUnitDatabase logger cfg conf_file = do
|
| 1 | +-- | The 'UnitIndex' is a 'UnitEnv' wide data structure that shares
|
|
| 2 | +-- external unit information across the 'UnitState' of all home units
|
|
| 3 | +-- (e.g., 'HomeUnitEnv') in a particular 'UnitEnv'.
|
|
| 4 | +--
|
|
| 5 | +-- It caches already read unit databases, all processed 'UnitInfo's and
|
|
| 6 | +-- the 'WireMap'.
|
|
| 7 | +--
|
|
| 8 | +-- This module is meant to be imported as @Index@.
|
|
| 9 | +--
|
|
| 10 | +-- A short overview of how the different types here related to 'UnitState', 'UnitEnv'
|
|
| 11 | +-- and the 'HomeUnitEnv'.
|
|
| 12 | +--
|
|
| 13 | +-- ┌─────────┐
|
|
| 14 | +-- │ UnitEnv │
|
|
| 15 | +-- └────┬────┘
|
|
| 16 | +-- ├───────────────────────┐
|
|
| 17 | +-- │ │
|
|
| 18 | +-- ┌────▼──────┐ ┌─────▼─────┐
|
|
| 19 | +-- │HomeUnitEnv│ │ UnitIndex ├────────────────┐
|
|
| 20 | +-- └────┬──────┘ └───────────┘ │
|
|
| 21 | +-- │ │
|
|
| 22 | +-- │ Reads cached unit DBs │
|
|
| 23 | +-- ┌────▼──────┐ ┌─────────────────────┐ │
|
|
| 24 | +-- │ UnitState ├──────────>ExternalUnitDatabases◄──────┤
|
|
| 25 | +-- └────┬──┬───┘ └─────────────────────┘ │
|
|
| 26 | +-- │ └───────────────────────┐ │
|
|
| 27 | +-- │ Writes new UnitInfos │ │
|
|
| 28 | +-- │ during initialisation │ │
|
|
| 29 | +-- ┌────▼────────┐ ┌────────v──────────┐ │
|
|
| 30 | +-- │ UnitInfoMap │ │ GlobalUnitInfoMap ◄────────┘
|
|
| 31 | +-- └────┬────────┘ └────────^──────────┘
|
|
| 32 | +-- │ │
|
|
| 33 | +-- └──────────────────────────┘
|
|
| 34 | +-- UnitInfoMap references
|
|
| 35 | +-- GlobalUnitInfoMap values
|
|
| 36 | +-- (All UnitInfos are shared)
|
|
| 37 | +--
|
|
| 38 | +-- Open arrow @A ───> B@: A uses B.
|
|
| 39 | +-- Closed arrow @A ◄─── B@: A is a field of B.
|
|
| 40 | +--
|
|
| 41 | +-- Also, see Note [Sharing 'UnitInfo's across the 'UnitEnv'] for more technical discussion
|
|
| 42 | +-- about sharing 'UnitInfo's.
|
|
| 1 | 43 | module GHC.Unit.External.Index (
|
| 2 | 44 | -- * The 'UnitIndexCache'.
|
| 3 | 45 | -- A mutable wrapper around 'UnitIndex'
|
| ... | ... | @@ -6,12 +48,16 @@ module GHC.Unit.External.Index ( |
| 6 | 48 | readUnitIndex,
|
| 7 | 49 | modifyUnitIndexCache,
|
| 8 | 50 | clearUnitIndexCache,
|
| 51 | + cacheExternalUnitDatabase,
|
|
| 52 | + readExternalUnitDatabases,
|
|
| 53 | + readExternalUnitDatabase,
|
|
| 9 | 54 | -- * 'UnitIndex'
|
| 10 | 55 | UnitIndex,
|
| 11 | 56 | emptyUnitIndex,
|
| 12 | 57 | wiringMap,
|
| 13 | 58 | unwiringMap,
|
| 14 | 59 | globalUnits,
|
| 60 | + externalUnitDatabases,
|
|
| 15 | 61 | setWireMap,
|
| 16 | 62 | wireMapExists,
|
| 17 | 63 | addUnitInfoMap,
|
| ... | ... | @@ -32,10 +78,14 @@ module GHC.Unit.External.Index ( |
| 32 | 78 | updateWiredInUnits,
|
| 33 | 79 | updateWiredInUnitsInUnitInfo,
|
| 34 | 80 | updateWiredInUnitIdInModule,
|
| 81 | + -- * Reading external unit databases into the 'UnitIndexCache'
|
|
| 82 | + readOrGetUnitDatabase,
|
|
| 83 | + readUnitDatabases,
|
|
| 35 | 84 | ) where
|
| 36 | 85 | |
| 37 | 86 | import GHC.Prelude
|
| 38 | 87 | |
| 88 | +import GHC.Data.OsPath
|
|
| 39 | 89 | import GHC.Data.ShortText qualified as ST
|
| 40 | 90 | import GHC.Types.Unique.Map
|
| 41 | 91 | import GHC.Unit.Database
|
| ... | ... | @@ -46,17 +96,36 @@ import GHC.Unit.Info |
| 46 | 96 | import GHC.Unit.Types
|
| 47 | 97 | import GHC.Utils.Logger
|
| 48 | 98 | |
| 99 | +import Control.Monad (liftM)
|
|
| 49 | 100 | import Data.Either
|
| 50 | 101 | import Data.IORef (IORef)
|
| 51 | 102 | import Data.IORef qualified as IORef
|
| 52 | 103 | import Data.Map.Strict (Map)
|
| 53 | 104 | import Data.Map.Strict qualified as Map
|
| 105 | +import Data.Maybe (catMaybes)
|
|
| 54 | 106 | |
| 55 | 107 | -- ----------------------------------------------------------------------------
|
| 56 | 108 | -- UnitIndex
|
| 57 | 109 | -- ----------------------------------------------------------------------------
|
| 58 | 110 | |
| 59 | 111 | -- | Mutable version of 'UnitIndex'.
|
| 112 | +--
|
|
| 113 | +-- The 'UnitIndexCache' ensures that all calls to 'initUnits' will
|
|
| 114 | +-- share the 'UnitInfo' if it is possible.
|
|
| 115 | +--
|
|
| 116 | +-- To share the 'UnitInfo', the 'UnitInfo' needs to be fully-resolved, i.e., its wired-in
|
|
| 117 | +-- dependencies and modules need to be replaced with the 'UnitId' of the wired-in unit.
|
|
| 118 | +-- Thus, the 'UnitIndexCache' caches both the global 'WireMap' and the 'UnitInfoMap'.
|
|
| 119 | +--
|
|
| 120 | +-- The 'WireMap' is globally valid, as other parts of the compiler rely on the fact
|
|
| 121 | +-- that only one instance of wired-in units is used.
|
|
| 122 | +--
|
|
| 123 | +-- Memory Invariant: The 'UnitIndexCache' is the root object for retaining fully-resolved
|
|
| 124 | +-- 'UnitInfo'. 'UnitState' is expected to reference only 'UnitInfo's from the 'UnitIndexCache'.
|
|
| 125 | +-- There is exactly one fully-resolved 'UnitInfo' alive for each external unit per unit database.
|
|
| 126 | +--
|
|
| 127 | +-- A second instance may or may not be stored in the 'externalUnitDatabases', which represent the
|
|
| 128 | +-- in-memory cache of the on-disk unit databases.
|
|
| 60 | 129 | newtype UnitIndexCache = UnitIndexCache
|
| 61 | 130 | { uic_index :: IORef UnitIndex
|
| 62 | 131 | }
|
| ... | ... | @@ -66,21 +135,41 @@ initUnitIndexCache = |
| 66 | 135 | UnitIndexCache <$> IORef.newIORef emptyUnitIndex
|
| 67 | 136 | |
| 68 | 137 | readUnitIndex :: UnitIndexCache -> IO UnitIndex
|
| 69 | -readUnitIndex eudc =
|
|
| 70 | - IORef.readIORef (uic_index eudc)
|
|
| 138 | +readUnitIndex uic =
|
|
| 139 | + IORef.readIORef (uic_index uic)
|
|
| 71 | 140 | |
| 72 | 141 | modifyUnitIndexCache :: UnitIndexCache -> (UnitIndex -> UnitIndex) -> IO ()
|
| 73 | -modifyUnitIndexCache eudc f =
|
|
| 74 | - IORef.modifyIORef' (uic_index eudc) f
|
|
| 142 | +modifyUnitIndexCache uic f =
|
|
| 143 | + IORef.modifyIORef' (uic_index uic) f
|
|
| 75 | 144 | |
| 76 | 145 | clearUnitIndexCache :: UnitIndexCache -> IO ()
|
| 77 | -clearUnitIndexCache eudc =
|
|
| 78 | - modifyUnitIndexCache eudc (const emptyUnitIndex)
|
|
| 146 | +clearUnitIndexCache uic =
|
|
| 147 | + modifyUnitIndexCache uic (const emptyUnitIndex)
|
|
| 148 | + |
|
| 149 | +cacheExternalUnitDatabase :: UnitIndexCache -> UnitDatabase UnitId -> IO ()
|
|
| 150 | +cacheExternalUnitDatabase uic db =
|
|
| 151 | + modifyUnitIndexCache uic
|
|
| 152 | + (\ ui ->
|
|
| 153 | + ui
|
|
| 154 | + { ui_externalUnitDatabases = insertExternalUnitDatabases db (ui_externalUnitDatabases ui)
|
|
| 155 | + }
|
|
| 156 | + )
|
|
| 157 | + |
|
| 158 | +readExternalUnitDatabases :: UnitIndexCache -> IO (ExternalUnitDatabases UnitId)
|
|
| 159 | +readExternalUnitDatabases uic =
|
|
| 160 | + externalUnitDatabases <$> readUnitIndex uic
|
|
| 161 | + |
|
| 162 | +readExternalUnitDatabase :: UnitIndexCache -> OsPath -> IO (Maybe (UnitDatabase UnitId))
|
|
| 163 | +readExternalUnitDatabase uic path = do
|
|
| 164 | + dbs <- readExternalUnitDatabases uic
|
|
| 165 | + pure $ lookupExternalUnitDatabases path dbs
|
|
| 79 | 166 | |
| 80 | 167 | -- | Global index for external units that can be shared across multiple 'HomeUnitEnv's.
|
| 81 | 168 | --
|
| 82 | 169 | -- Allows sharing of the 'WireMap' and 'UnitInfo's that are stored in the 'UnitState'
|
| 83 | 170 | -- of each 'HomeUnitEnv'.
|
| 171 | +--
|
|
| 172 | +-- See Note [Sharing 'UnitInfo's across the 'UnitEnv'] for details about memory usage.
|
|
| 84 | 173 | data UnitIndex = UnitIndex
|
| 85 | 174 | { ui_wireMap :: !WireMap
|
| 86 | 175 | -- ^ A mapping from database unit keys to wired in unit ids.
|
| ... | ... | @@ -95,10 +184,13 @@ data UnitIndex = UnitIndex |
| 95 | 184 | , ui_unitInfoMap :: !GlobalUnitInfoMap
|
| 96 | 185 | -- ^ A global map for all fully-resolved 'UnitInfo's.
|
| 97 | 186 | --
|
| 98 | - -- A 'UnitInfo' is fully-resolved, if its dependencies were updated to reference the
|
|
| 99 | - -- wired-in packages (e.g., 'wiringMap') and the wired-in packages are updated.
|
|
| 100 | - -- Further, the 'UnitInfo' is based on the 'ExternalUnitDatabases' results, resolving
|
|
| 101 | - -- variables such as @${pkgroot}@ in paths.
|
|
| 187 | + -- See Note [Sharing 'UnitInfo's across the 'UnitEnv'] for more details
|
|
| 188 | + -- what we use this for and what a fully-resolved 'UnitInfo' is.
|
|
| 189 | + , ui_externalUnitDatabases :: !(ExternalUnitDatabases UnitId)
|
|
| 190 | + -- ^ Cache the already processed unit databases in-memory.
|
|
| 191 | + --
|
|
| 192 | + -- These 'GenericUnitInfo's have their paths resolved, e.g., no @${pkgroot}@ is
|
|
| 193 | + -- present any more.
|
|
| 102 | 194 | }
|
| 103 | 195 | |
| 104 | 196 | -- | Get the 'WireMap'.
|
| ... | ... | @@ -115,7 +207,18 @@ wiringMap = ui_wireMap |
| 115 | 207 | unwiringMap :: UnitIndex -> UnwireMap
|
| 116 | 208 | unwiringMap = ui_unwireMap
|
| 117 | 209 | |
| 210 | +-- | Access the already processed unit databases.
|
|
| 211 | +externalUnitDatabases :: UnitIndex -> ExternalUnitDatabases UnitId
|
|
| 212 | +externalUnitDatabases = ui_externalUnitDatabases
|
|
| 213 | + |
|
| 118 | 214 | -- | Access the global map of fully-resolved 'UnitInfo's.
|
| 215 | +--
|
|
| 216 | +-- A 'UnitInfo' is fully-resolved, if its dependencies were updated to reference the
|
|
| 217 | +-- wired-in packages (e.g., 'wiringMap') and the wired-in packages are updated.
|
|
| 218 | +-- Further, the 'UnitInfo' is based on the 'ExternalUnitDatabases' results, resolving
|
|
| 219 | +-- variables such as @${pkgroot}@ in paths.
|
|
| 220 | +--
|
|
| 221 | +-- See Note [Sharing 'UnitInfo's across the 'UnitEnv'] for why this is helpful.
|
|
| 119 | 222 | globalUnits :: UnitIndex -> GlobalUnitInfoMap
|
| 120 | 223 | globalUnits = ui_unitInfoMap
|
| 121 | 224 | |
| ... | ... | @@ -124,6 +227,7 @@ emptyUnitIndex = UnitIndex |
| 124 | 227 | { ui_wireMap = emptyWireMap
|
| 125 | 228 | , ui_unwireMap = emptyUnwireMap
|
| 126 | 229 | , ui_unitInfoMap = emptyGlobalUnitInfoMap
|
| 230 | + , ui_externalUnitDatabases = emptyExternalUnitDatabases
|
|
| 127 | 231 | }
|
| 128 | 232 | |
| 129 | 233 | -- | Set the 'WireMap' of 'UnitIndex'.
|
| ... | ... | @@ -167,7 +271,8 @@ type UnitAbiHash = ST.ShortText |
| 167 | 271 | --
|
| 168 | 272 | -- However, a user can choose a conflicting 'UnitId', causing a conflict after all.
|
| 169 | 273 | -- We use the 'UnitAbiHash' for disambiguation. If both 'UnitId' and 'UnitAbiHash' are
|
| 170 | --- identical in separate unit databases, we can assume they are the same unit.
|
|
| 274 | +-- identical in separate unit databases, we can assume they are the same unit, according
|
|
| 275 | +-- to the documentation of GHC.
|
|
| 171 | 276 | newtype GlobalUnitInfoMap = GlobalUnitInfoMap (UniqMap UnitId (Map UnitAbiHash UnitInfo))
|
| 172 | 277 | |
| 173 | 278 | -- | Lookup the 'UnitInfo' in the 'GlobalUnitInfoMap'.
|
| ... | ... | @@ -238,7 +343,10 @@ updateWiredInUnitIndex wired_map pkgs unit_index = do |
| 238 | 343 | ui <- readUnitIndex unit_index
|
| 239 | 344 | let
|
| 240 | 345 | all_pkgs = updateWiredInUnits wired_map (ui_unitInfoMap ui) pkgs
|
| 241 | - (new_pkgs, _pkgs_set) = partitionEithers all_pkgs
|
|
| 346 | + (new_pkgs', _pkgs_set) = partitionEithers all_pkgs
|
|
| 347 | + -- Make sure we force the 'UnitInfo' here.
|
|
| 348 | + -- Otherwise, we will retain a reference to the old 'UnitInfo'
|
|
| 349 | + new_pkgs <- traverse evaluateUnitInfoLists new_pkgs'
|
|
| 242 | 350 | modifyUnitIndexCache unit_index (addUnitInfoMap $ mkUnitInfoMap new_pkgs)
|
| 243 | 351 | pure (map (either id id) all_pkgs)
|
| 244 | 352 | |
| ... | ... | @@ -310,3 +418,23 @@ upd_wired_in :: WireMap -> UnitId -> UnitId |
| 310 | 418 | upd_wired_in wiredInMap key
|
| 311 | 419 | | Just key' <- lookupWireMap key wiredInMap = key'
|
| 312 | 420 | | otherwise = key
|
| 421 | + |
|
| 422 | +-- -----------------------------------------------------------------------------
|
|
| 423 | +-- Reading the unit database(s) into the 'UnitIndexCache'
|
|
| 424 | + |
|
| 425 | +readUnitDatabases :: Logger -> UnitIndexCache -> UnitDbConfig -> IO [UnitDatabase UnitId]
|
|
| 426 | +readUnitDatabases logger db_cache cfg = do
|
|
| 427 | + conf_refs <- getUnitDbRefs cfg
|
|
| 428 | + confs <- liftM catMaybes $ mapM (resolveUnitDatabase cfg) conf_refs
|
|
| 429 | + mapM (readOrGetUnitDatabase logger db_cache cfg) confs
|
|
| 430 | + |
|
| 431 | +-- | Get the cached 'UnitDatabase' or read the 'UnitDatabase' at the given location.
|
|
| 432 | +readOrGetUnitDatabase :: Logger -> UnitIndexCache -> UnitDbConfig -> OsPath -> IO (UnitDatabase UnitId)
|
|
| 433 | +readOrGetUnitDatabase logger db_cache cfg conf_file =
|
|
| 434 | + readExternalUnitDatabase db_cache conf_file >>= \ case
|
|
| 435 | + Nothing -> do
|
|
| 436 | + new_db <- readUnitDatabase logger cfg conf_file
|
|
| 437 | + cacheExternalUnitDatabase db_cache new_db
|
|
| 438 | + pure new_db
|
|
| 439 | + Just db ->
|
|
| 440 | + pure db |
| ... | ... | @@ -7,12 +7,12 @@ module GHC.Unit.External.ModuleOrigin ( |
| 7 | 7 | originEmpty,
|
| 8 | 8 | ) where
|
| 9 | 9 | |
| 10 | +import Data.Semigroup qualified as Semigroup
|
|
| 10 | 11 | import GHC.Prelude
|
| 11 | 12 | import GHC.Unit.External.Validate
|
| 12 | 13 | import GHC.Unit.Info
|
| 13 | 14 | import GHC.Utils.Outputable
|
| 14 | 15 | import GHC.Utils.Panic
|
| 15 | -import qualified Data.Semigroup as Semigroup
|
|
| 16 | 16 | |
| 17 | 17 | -- | Given a module name, there may be multiple ways it came into scope,
|
| 18 | 18 | -- possibly simultaneously. This data type tracks all the possible ways
|
| ... | ... | @@ -6,7 +6,6 @@ module GHC.Unit.State ( |
| 6 | 6 | module GHC.Unit.Info,
|
| 7 | 7 | -- * Reading the package config, and processing cmdline args
|
| 8 | 8 | UnitState(..),
|
| 9 | - TrustOverlay(..),
|
|
| 10 | 9 | UnitDatabase (..),
|
| 11 | 10 | UnitErr (..),
|
| 12 | 11 | emptyUnitState,
|
| ... | ... | @@ -16,6 +15,13 @@ module GHC.Unit.State ( |
| 16 | 15 | getUnitDbRefs,
|
| 17 | 16 | resolveUnitDatabase,
|
| 18 | 17 | listUnitInfo,
|
| 18 | + -- * Overlays over the unit set
|
|
| 19 | + TrustOverlay,
|
|
| 20 | + IsTrusted(..),
|
|
| 21 | + lookupTrustOverlay,
|
|
| 22 | + distrustUnits,
|
|
| 23 | + trustUnits,
|
|
| 24 | + emptyTrustOverlay,
|
|
| 19 | 25 | -- * Querying the package config
|
| 20 | 26 | lookupUnit,
|
| 21 | 27 | lookupUnit',
|
| ... | ... | @@ -252,12 +258,19 @@ data IsTrusted |
| 252 | 258 | | Distrusted
|
| 253 | 259 | deriving ( Eq, Ord )
|
| 254 | 260 | |
| 261 | +-- | The 'TrustOverlay' stores user overwrites of the on-disk 'unitIsTrusted' status.
|
|
| 262 | +--
|
|
| 263 | +-- The user can overwrite this value via flags such as @-distrust-all-packages@.
|
|
| 264 | +-- We do not modify the 'UnitInfo' directory, but rather store this user selection
|
|
| 265 | +-- in the 'TrustOverlay'.
|
|
| 266 | +--
|
|
| 267 | +-- This allows us to share the 'UnitInfo' completely and saves us memory.
|
|
| 255 | 268 | newtype TrustOverlay = TrustOverlay
|
| 256 | 269 | { trustOverlay :: UniqMap UnitId IsTrusted
|
| 257 | 270 | }
|
| 258 | 271 | |
| 259 | 272 | lookupTrustOverlay :: TrustOverlay -> UnitId -> Maybe IsTrusted
|
| 260 | -lookupTrustOverlay (TrustOverlay to) = lookupUniqMap to
|
|
| 273 | +lookupTrustOverlay to = lookupUniqMap (trustOverlay to)
|
|
| 261 | 274 | |
| 262 | 275 | distrustUnits :: [UnitId] -> TrustOverlay -> TrustOverlay
|
| 263 | 276 | distrustUnits elements (TrustOverlay to) = TrustOverlay $ foldl' (\ acc uid -> addToUniqMap acc uid Distrusted) to elements
|
| ... | ... | @@ -268,6 +281,57 @@ trustUnits elements (TrustOverlay to) = TrustOverlay $ foldl' (\ acc uid -> addT |
| 268 | 281 | emptyTrustOverlay :: TrustOverlay
|
| 269 | 282 | emptyTrustOverlay = TrustOverlay emptyUniqMap
|
| 270 | 283 | |
| 284 | +{-
|
|
| 285 | +Note [Sharing 'UnitInfo's across the 'UnitEnv']
|
|
| 286 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 287 | +The 'UnitState' and 'UnitIndex' are closely related.
|
|
| 288 | + |
|
| 289 | +The 'UnitState' stores all information about the external units referenced by
|
|
| 290 | +a single 'HomeUnitEnv'.
|
|
| 291 | +As a reminder, the 'HomeUnitEnv' stores all information specific to a single home unit,
|
|
| 292 | +such as the 'HomePackageTable', 'DynFlags' and the 'UnitState'.
|
|
| 293 | +The 'UnitState' retains the 'unitInfoMap', an in-memory representation of
|
|
| 294 | +the unit databases that a 'HomeUnitEnv' depends on.
|
|
| 295 | +Multiple home units can depend on the same unit database, and reference the same
|
|
| 296 | +'UnitInfo's across the GHC session.
|
|
| 297 | +We share all 'UnitInfo's across multiple 'HomeUnitEnv's, saving a lot of
|
|
| 298 | +duplication of the same 'UnitInfo'.
|
|
| 299 | +This what the 'UnitIndex' takes care of.
|
|
| 300 | + |
|
| 301 | +The 'UnitIndex' stores all fully-resolved 'UnitInfo's that can be referenced
|
|
| 302 | +by the 'UnitState'.'unitInfoMap'.
|
|
| 303 | +We consider a 'UnitInfo' as fully-resolved, if its dependencies were updated to reference the
|
|
| 304 | +wired-in units (e.g., 'wiringMap') and the wired-in units are updated as well.
|
|
| 305 | +See Note [Wired-in units] for more details on wired-in units.
|
|
| 306 | +Further, the 'UnitInfo' is based on the 'ExternalUnitDatabases' results, resolving
|
|
| 307 | +variables such as @${pkgroot}@ in paths.
|
|
| 308 | + |
|
| 309 | +As such, we can consider the 'UnitIndex' to be global data that is referenced by
|
|
| 310 | +the 'UnitState' for better sharing of 'UnitInfo's.
|
|
| 311 | + |
|
| 312 | +In fact, using the 'UnitIndex', we can impose a hard upper bound on the number
|
|
| 313 | +of live 'UnitInfo's in a GHC session:
|
|
| 314 | + |
|
| 315 | +> For each on-disk 'GenericUnitInfo', there are at most two objects alive.
|
|
| 316 | + |
|
| 317 | +One instance is stored in 'ExternalUnitDatabases' where variables are resolved,
|
|
| 318 | +but the wired-in units haven't been resolved.
|
|
| 319 | + |
|
| 320 | +The second instance is the fully-resolved 'UnitInfo' stored in the 'UnitIndex'.
|
|
| 321 | + |
|
| 322 | +See the module documentation for 'GHC.Unit.External.Index' for an overview
|
|
| 323 | +of how the types relate to each other.
|
|
| 324 | +-}
|
|
| 325 | + |
|
| 326 | +-- | The 'UnitState' contains a plethora of information local to a single 'HomeUnitEnv'.
|
|
| 327 | +--
|
|
| 328 | +-- It stores module visibilities, unit trust for @SafeHaskell@, available units for error messages,
|
|
| 329 | +-- explicit unit dependencies and knows how to instantiate backpack signature and holes
|
|
| 330 | +-- on demand.
|
|
| 331 | +--
|
|
| 332 | +-- A 'HomeUnitEnv' should rarely/never have to look into the 'UnitIndex', all external
|
|
| 333 | +-- unit related information is stored in the 'UnitState'.
|
|
| 334 | +--
|
|
| 271 | 335 | data UnitState = UnitState {
|
| 272 | 336 | -- | A mapping of 'Unit' to 'UnitInfo'. This list is adjusted
|
| 273 | 337 | -- so that only valid units are here. 'UnitInfo' reflects
|
| ... | ... | @@ -276,6 +340,7 @@ data UnitState = UnitState { |
| 276 | 340 | -- may have the 'exposed' flag be 'False'.)
|
| 277 | 341 | --
|
| 278 | 342 | -- All values are shared with 'UnitIndex'.'globalUnits'.
|
| 343 | + -- See Note [Sharing 'UnitInfo's across the 'UnitEnv'] for details.
|
|
| 279 | 344 | unitInfoMap :: UnitInfoMap,
|
| 280 | 345 | |
| 281 | 346 | -- | Set of units that we trust.
|
| ... | ... | @@ -283,7 +348,10 @@ data UnitState = UnitState { |
| 283 | 348 | -- Local overlay of 'UnitInfo'.
|
| 284 | 349 | -- This avoids modifying the 'UnitInfo' directly, potentially saving
|
| 285 | 350 | -- a lot of duplication.
|
| 286 | - trustedUnits :: TrustOverlay,
|
|
| 351 | + --
|
|
| 352 | + -- We keep this in WHNF as it is relatively cheap but could easily retain
|
|
| 353 | + -- references to bigger structures.
|
|
| 354 | + trustedUnits :: !TrustOverlay,
|
|
| 287 | 355 | |
| 288 | 356 | -- | A mapping of 'PackageName' to 'UnitId'. If several units have the same
|
| 289 | 357 | -- package name (e.g. different instantiations), then we return one of them...
|
| ... | ... | @@ -333,8 +401,6 @@ emptyUnitState = UnitState { |
| 333 | 401 | unitInfoMap = emptyUniqMap,
|
| 334 | 402 | trustedUnits = emptyTrustOverlay,
|
| 335 | 403 | packageNameMap = emptyUFM,
|
| 336 | - -- wireMap = emptyUniqMap,
|
|
| 337 | - -- unwireMap = emptyUniqMap,
|
|
| 338 | 404 | preloadUnits = [],
|
| 339 | 405 | explicitUnits = [],
|
| 340 | 406 | homeUnitDepends = Set.empty,
|
| ... | ... | @@ -439,14 +505,16 @@ isUnitInfoTrusted ue u = |
| 439 | 505 | -- 'initUnits' can be called again subsequently after updating the
|
| 440 | 506 | -- 'packageFlags' and 'packageDBFlags' fields of the 'DynFlags', and it will
|
| 441 | 507 | -- update the 'unitState' in 'DynFlags'.
|
| 442 | -initUnits :: Logger -> DynFlags -> UnitIndexCache -> ExternalUnitDatabaseCache UnitId -> Set.Set UnitId -> IO (UnitState, HomeUnit, Maybe PlatformConstants)
|
|
| 443 | -initUnits logger dflags unit_index cached_dbs home_units = do
|
|
| 508 | +--
|
|
| 509 | +-- Also, see Note [Sharing 'UnitInfo's across the 'UnitEnv'] for implementation details.
|
|
| 510 | +initUnits :: Logger -> DynFlags -> UnitIndexCache -> Set.Set UnitId -> IO (UnitState, HomeUnit, Maybe PlatformConstants)
|
|
| 511 | +initUnits logger dflags unit_index home_units = do
|
|
| 444 | 512 | |
| 445 | 513 | let forceUnitInfoMap state = unitInfoMap state `seq` ()
|
| 446 | 514 | |
| 447 | 515 | unit_state <- withTiming logger (text "initializing unit database")
|
| 448 | 516 | forceUnitInfoMap
|
| 449 | - $ mkUnitState logger unit_index cached_dbs (initUnitConfig dflags home_units)
|
|
| 517 | + $ mkUnitState logger unit_index (initUnitConfig dflags home_units)
|
|
| 450 | 518 | |
| 451 | 519 | putDumpFileMaybe logger Opt_D_dump_mod_map "Module Map"
|
| 452 | 520 | FormatText (updSDocContext (\ctx -> ctx {sdocLineLength = 200})
|
| ... | ... | @@ -638,10 +706,9 @@ reportCycles logger sccs = when (logVerbAtLeast logger 2) $ mapM_ report sccs |
| 638 | 706 | mkUnitState
|
| 639 | 707 | :: Logger
|
| 640 | 708 | -> UnitIndexCache
|
| 641 | - -> ExternalUnitDatabaseCache UnitId
|
|
| 642 | 709 | -> UnitConfig
|
| 643 | 710 | -> IO UnitState
|
| 644 | -mkUnitState logger unit_index db_cache cfg = do
|
|
| 711 | +mkUnitState logger unit_index cfg = do
|
|
| 645 | 712 | {-
|
| 646 | 713 | Plan.
|
| 647 | 714 | |
| ... | ... | @@ -695,7 +762,7 @@ mkUnitState logger unit_index db_cache cfg = do |
| 695 | 762 | we build a mapping saying what every in scope module name points to.
|
| 696 | 763 | -}
|
| 697 | 764 | |
| 698 | - dbs <- readUnitDatabases logger db_cache (initUnitDbConfig cfg)
|
|
| 765 | + dbs <- readUnitDatabases logger unit_index (initUnitDbConfig cfg)
|
|
| 699 | 766 | |
| 700 | 767 | -- distrust all units if the flag is set
|
| 701 | 768 | let distrustUnitsOfDb overlay db = foldl' (\ acc ui -> distrustUnits [unitId ui] acc) overlay (unitDatabaseUnits db)
|
| ... | ... | @@ -713,7 +780,7 @@ mkUnitState logger unit_index db_cache cfg = do |
| 713 | 780 | debugTraceMsg logger 2 $
|
| 714 | 781 | text "package flags" <+> ppr other_flags
|
| 715 | 782 | |
| 716 | - let !home_unit_deps = selectHomeUnits (unitConfigHomeUnits cfg) hpt_flags
|
|
| 783 | + let home_unit_deps = selectHomeUnits (unitConfigHomeUnits cfg) hpt_flags
|
|
| 717 | 784 | |
| 718 | 785 | -- Merge databases together, without checking validity
|
| 719 | 786 | (pkg_map1, prec_map) <- mergeDatabases logger dbs
|
| ... | ... | @@ -727,7 +794,7 @@ mkUnitState logger unit_index db_cache cfg = do |
| 727 | 794 | |
| 728 | 795 | -- Compute trust flags (these flags apply regardless of whether
|
| 729 | 796 | -- or not packages are visible or not)
|
| 730 | - !trustUnitsOverlay <- mayThrowUnitErr
|
|
| 797 | + trustUnitsOverlay <- mayThrowUnitErr
|
|
| 731 | 798 | $ foldM (applyTrustFlag prec_map unusable (nonDetEltsUniqMap pkg_map2))
|
| 732 | 799 | distrustedUnitsOverlay (reverse (unitConfigFlagsTrusted cfg))
|
| 733 | 800 | let pkgs1 = nonDetEltsUniqMap pkg_map2
|
| ... | ... | @@ -769,12 +836,12 @@ mkUnitState logger unit_index db_cache cfg = do |
| 769 | 836 | -- Note: we NEVER expose indefinite packages by
|
| 770 | 837 | -- default, because it's almost assuredly not
|
| 771 | 838 | -- what you want (no mix-in linking has occurred).
|
| 772 | - let !x = fsPackageName p in if unitIsExposed p && unitIsDefinite (mkUnit p) && mostPreferable p
|
|
| 839 | + if unitIsExposed p && unitIsDefinite (mkUnit p) && mostPreferable p
|
|
| 773 | 840 | then addToUniqMap vm (mkUnit p)
|
| 774 | 841 | UnitVisibility {
|
| 775 | 842 | uv_expose_all = True,
|
| 776 | 843 | uv_renamings = [],
|
| 777 | - uv_package_name = First (Just x),
|
|
| 844 | + uv_package_name = First (Just $ fsPackageName p),
|
|
| 778 | 845 | uv_requirements = emptyUniqMap,
|
| 779 | 846 | uv_explicit = Nothing
|
| 780 | 847 | }
|
| ... | ... | @@ -857,7 +857,7 @@ installInteractiveHomeUnits dflags = do |
| 857 | 857 | env <- GHC.getSession
|
| 858 | 858 | let unit_index = hscUIC env
|
| 859 | 859 | (unit_state,home_unit,_mconstants) <-
|
| 860 | - liftIO $ initUnits logger dflags unit_index (hscEUDC env) all_home_units
|
|
| 860 | + liftIO $ initUnits logger dflags unit_index all_home_units
|
|
| 861 | 861 | hpt <- liftIO emptyHomePackageTable
|
| 862 | 862 | pure (HUG.mkHomeUnitEnv unit_state dflags hpt (Just home_unit))
|
| 863 | 863 |
| ... | ... | @@ -2,17 +2,38 @@ TOP=../../.. |
| 2 | 2 | include $(TOP)/mk/boilerplate.mk
|
| 3 | 3 | include $(TOP)/mk/test.mk
|
| 4 | 4 | |
| 5 | -.PHONY: generic-unit-info-space_Prep
|
|
| 6 | -generic-unit-info-space_Prep:
|
|
| 5 | +.PHONY: generic-unit-info-space-single_Prep
|
|
| 6 | +generic-unit-info-space-single_Prep:
|
|
| 7 | 7 | |
| 8 | 8 | |
| 9 | -.PHONY: generic-unit-info-space
|
|
| 10 | -generic-unit-info-space:
|
|
| 9 | +.PHONY: generic-unit-info-space-single
|
|
| 10 | +generic-unit-info-space-single:
|
|
| 11 | 11 | "$(TEST_HC)" $(TEST_HC_OPTS) -Wall -v0 generic-unit-info-space.hs -rtsopts \
|
| 12 | 12 | -package ghc -package base -package ghc-internal -threaded
|
| 13 | 13 | |
| 14 | 14 | ./generic-unit-info-space$(exeext) \
|
| 15 | 15 | "`'$(TEST_HC)' --print-libdir | tr -d '\r'`" \
|
| 16 | 16 | "`'$(GHC_PKG)' --no-user-package-db list --simple-output | wc -w`" \
|
| 17 | + -clear-package-db -global-package-db -no-user-package-db \
|
|
| 18 | + +RTS -hT --no-automatic-heap-samples -RTS
|
|
| 19 | + |
|
| 20 | +# Generate 50 trivial home units. The number of 'UnitInfo's must not increase
|
|
| 21 | +# linearly in the number of home units
|
|
| 22 | +HOME_UNITS_01 := 50
|
|
| 23 | +UNITS := $(foreach p,$(shell seq 0 $$(( $(HOME_UNITS_01) - 1 ))),-unit @unitp$(p))
|
|
| 24 | +.PHONY: generic-unit-info-space-mhu_Prep
|
|
| 25 | +generic-unit-info-space-mhu_Prep:
|
|
| 26 | + ./genMhu.sh $(HOME_UNITS_01)
|
|
| 27 | + |
|
| 28 | + |
|
| 29 | +.PHONY: generic-unit-info-space-mhu
|
|
| 30 | +generic-unit-info-space-mhu:
|
|
| 31 | + "$(TEST_HC)" $(TEST_HC_OPTS) -Wall -v0 generic-unit-info-space.hs -rtsopts \
|
|
| 32 | + -package ghc -package base -package ghc-internal -threaded
|
|
| 33 | + |
|
| 34 | + ./generic-unit-info-space$(exeext) \
|
|
| 35 | + "`'$(TEST_HC)' --print-libdir | tr -d '\r'`" \
|
|
| 36 | + "`'$(GHC_PKG)' --global list --simple-output | wc -w`" \
|
|
| 37 | + $(UNITS) \
|
|
| 17 | 38 | +RTS -hT --no-automatic-heap-samples -RTS
|
| 18 | 39 |
| 1 | 1 | # A test for checking the number of live GenericUnitInfo objects
|
| 2 | -test('generic-unit-info-space'
|
|
| 3 | - , [ extra_files(['Foo.hs'])
|
|
| 4 | - , pre_cmd('$MAKE -s --no-print-directory generic-unit-info-space_Prep')
|
|
| 2 | +test('generic-unit-info-space-single'
|
|
| 3 | + , [ extra_files(['Foo.hs', 'generic-unit-info-space.hs'])
|
|
| 4 | + , pre_cmd('$MAKE -s --no-print-directory generic-unit-info-space-single_Prep')
|
|
| 5 | 5 | ]
|
| 6 | 6 | , makefile_test
|
| 7 | - , ['generic-unit-info-space']) |
|
| 7 | + , ['generic-unit-info-space-single'])
|
|
| 8 | + |
|
| 9 | +# A test for checking the number of live GenericUnitInfo objects
|
|
| 10 | +test('generic-unit-info-space-mhu'
|
|
| 11 | + , [ extra_files(['genMhu.sh', 'generic-unit-info-space.hs'])
|
|
| 12 | + , pre_cmd('$MAKE -s --no-print-directory generic-unit-info-space-mhu_Prep')
|
|
| 13 | + ]
|
|
| 14 | + , makefile_test
|
|
| 15 | + , ['generic-unit-info-space-mhu']) |
| 1 | +#! /usr/bin/env bash
|
|
| 2 | + |
|
| 3 | +set -euo pipefail
|
|
| 4 | + |
|
| 5 | +HOME_UNITS=${1:-50}
|
|
| 6 | + |
|
| 7 | +unit_dir() { echo "p$1"; }
|
|
| 8 | +unit_fname(){ echo "unitp$1"; }
|
|
| 9 | +mod_name() { echo "Mod$1"; }
|
|
| 10 | + |
|
| 11 | +mk_unit_file() {
|
|
| 12 | + local p=$1
|
|
| 13 | + echo "-clear-package-db -global-package-db -no-user-package-db -working-dir $(unit_dir "$p") -this-unit-id $(unit_dir "$p") $(mod_name "$p") ${deps[*]}" \
|
|
| 14 | + > "$(unit_fname "$p")"
|
|
| 15 | +}
|
|
| 16 | + |
|
| 17 | +mk_top_mod() {
|
|
| 18 | + local p=$1
|
|
| 19 | + echo "module $(mod_name "$p") where" > "$(unit_dir "$p")/$(mod_name "$p").hs"
|
|
| 20 | +}
|
|
| 21 | + |
|
| 22 | +for ((p = 0; p < HOME_UNITS; p++)); do
|
|
| 23 | + mkdir "$(unit_dir "$p")"
|
|
| 24 | + mk_unit_file "$p"
|
|
| 25 | + mk_top_mod "$p"
|
|
| 26 | +done |
| 1 | +### Heap Census
|
|
| 2 | +There are exactly two GenericUnitInfo closures alive per on-disk package |
| 1 | +### Heap Census
|
|
| 2 | +There are exactly two GenericUnitInfo closures alive per on-disk package |
| ... | ... | @@ -6,6 +6,7 @@ import GHC.Data.FastString (mkFastStringByteString) |
| 6 | 6 | import qualified GHC.Data.ShortText as ST
|
| 7 | 7 | import GHC.Driver.Monad
|
| 8 | 8 | import GHC.Driver.Env.Types
|
| 9 | +import GHC.Driver.Session.Units
|
|
| 9 | 10 | import GHC.Internal.Heap.Closures (closureSize, asBox)
|
| 10 | 11 | import GHC.Platform
|
| 11 | 12 | import GHC.Profiling
|
| ... | ... | @@ -14,6 +15,7 @@ import GHC.Unit.Types (stringToUnitId) |
| 14 | 15 | |
| 15 | 16 | import Control.Monad (guard, when)
|
| 16 | 17 | import Data.Version (makeVersion)
|
| 18 | +import qualified Data.List.NonEmpty as NE
|
|
| 17 | 19 | import qualified Data.Maybe as Maybe
|
| 18 | 20 | import qualified Data.List as List
|
| 19 | 21 | import System.Environment
|
| ... | ... | @@ -24,19 +26,32 @@ main :: IO () |
| 24 | 26 | main = do
|
| 25 | 27 | libdir:numberOfGlobalPkgsStr:restArgs <- getArgs
|
| 26 | 28 | runGhc (Just libdir) $ do
|
| 27 | - initGhcM (read numberOfGlobalPkgsStr) $ ["-clear-package-db", "-global-package-db", "-no-user-package-db"] ++ restArgs
|
|
| 28 | - |
|
| 29 | + initGhcM (read numberOfGlobalPkgsStr) restArgs
|
|
| 29 | 30 | |
| 30 | 31 | initGhcM :: Int -> [String] -> Ghc ()
|
| 31 | 32 | initGhcM numOfPkgs xs = do
|
| 32 | 33 | session <- getSession
|
| 33 | 34 | df1 <- getSessionDynFlags
|
| 34 | - let cmdOpts = ["-fforce-recomp"] ++ xs
|
|
| 35 | + let (units, args) = extractUnits xs
|
|
| 36 | + let cmdOpts = ["-fforce-recomp"] ++ args
|
|
| 37 | + |
|
| 35 | 38 | (df2, leftovers, _) <- parseDynamicFlags (hsc_logger session) df1 (map noLoc cmdOpts)
|
| 36 | 39 | setSessionDynFlags df2
|
| 37 | - ts <- mapM (\s -> guessTarget s Nothing Nothing) $ map unLoc leftovers
|
|
| 38 | - setTargets ts
|
|
| 39 | - _ <- load LoadAllTargets
|
|
| 40 | + |
|
| 41 | + targets <- case NE.nonEmpty units of
|
|
| 42 | + Nothing ->
|
|
| 43 | + traverse (\s -> guessTarget s Nothing Nothing) $ map unLoc leftovers
|
|
| 44 | + Just mhu -> do
|
|
| 45 | + ts <- initMulti mhu (\ _ _ _ _ -> pure ())
|
|
| 46 | + traverse (\(n, mu, mp) -> guessTarget n mu mp) ts
|
|
| 47 | + |
|
| 48 | + setTargets targets
|
|
| 49 | + success <- load LoadAllTargets
|
|
| 50 | + |
|
| 51 | + liftIO $ when (failed success) $ do
|
|
| 52 | + putStrLn "Failed to load targets"
|
|
| 53 | + Exit.exitFailure
|
|
| 54 | + |
|
| 40 | 55 | liftIO $ do
|
| 41 | 56 | requestHeapCensus
|
| 42 | 57 | performGC
|
| ... | ... | @@ -64,15 +79,17 @@ initGhcM numOfPkgs xs = do |
| 64 | 79 | -- The output should be:
|
| 65 | 80 | --
|
| 66 | 81 | -- @
|
| 67 | - -- 2 * number of packages in global unit db * 8 * word_size * sizeof(struct GenericUnitInfo)
|
|
| 82 | + -- 2 * number of packages in global unit db * word_size * sizeof(struct GenericUnitInfo)
|
|
| 68 | 83 | -- @
|
| 69 | 84 | --
|
| 70 | - -- This test simply assures, that in a single home unit case, we don't leak 'UnitInfo's
|
|
| 85 | + -- This test simply assures, that when we load a session, we don't leak 'UnitInfo's
|
|
| 71 | 86 | --
|
| 72 | - -- If this number changes for any reason, DO NOT ACCEPT THE CHANGES, you have introduced a space leak.
|
|
| 73 | - when (num /= expectedSizeInBytes) $ do
|
|
| 87 | + -- If this number changes without a good reason, DO NOT ACCEPT THE CHANGES, you have introduced a space leak.
|
|
| 88 | + -- We say less than the expected size is accepted, because in the multiple-home-units case, we don't force the second
|
|
| 89 | + -- UnitInfo closure enough after initial processing.
|
|
| 90 | + when (num > expectedSizeInBytes) $ do
|
|
| 74 | 91 | putStrLn "Space leak detected by generic-unit-info-space test:"
|
| 75 | - putStrLn $ (show (num `div` genericUnitInfoSizeInBytes)) ++ " live GenericUnitInfo when == (" ++ show expectedNumberOfUnitInfos ++ ") are expected"
|
|
| 92 | + putStrLn $ (show (num `div` genericUnitInfoSizeInBytes)) ++ " live GenericUnitInfo when <= (" ++ show expectedNumberOfUnitInfos ++ ") are expected"
|
|
| 76 | 93 | readFile hpFile >>= putStrLn
|
| 77 | 94 | Exit.exitFailure
|
| 78 | 95 | return ()
|
| ... | ... | @@ -135,3 +152,11 @@ minimalUnitInfo = GenericUnitInfo |
| 135 | 152 | , unitIsExposed = True
|
| 136 | 153 | , unitIsTrusted = False
|
| 137 | 154 | }
|
| 155 | + |
|
| 156 | +extractUnits :: [String] -> ([String], [String])
|
|
| 157 | +extractUnits = go [] []
|
|
| 158 | + where
|
|
| 159 | + -- TODO: we should likely use the 'processCmdLineP' instead
|
|
| 160 | + go units rest ("-unit" : x : xs) = go (x : units) rest xs
|
|
| 161 | + go units rest (x : xs) = go units (x : rest) xs
|
|
| 162 | + go units rest [] = (reverse units, reverse rest) |