Zubin pushed to branch wip/unload-strategy at Glasgow Haskell Compiler / GHC

Commits:

13 changed files:

Changes:

  • changelog.d/unload-strategy
    1
    +section: linker
    
    2
    +synopsis: Add -funload-strategy to choose between purging and unloading object code
    
    3
    +issues: #27741
    
    4
    +mrs: !16583
    
    5
    +
    
    6
    +description: {
    
    7
    +    When the interpreter drops object code it has loaded it can either
    
    8
    +    purge it, clearing its symbol table entries but leaving it in memory,
    
    9
    +    or unload it, additionally allowing a later garbage collection to
    
    10
    +    reclaim its memory. Unloading is preferable, but its implementation is
    
    11
    +    fragile on a number of platforms, so GHC purges on those instead: it
    
    12
    +    unloads on Linux other than ARM, and purges everywhere else.
    
    13
    +    ``-funload-strategy=unload`` and ``-funload-strategy=purge`` opt into
    
    14
    +    the other choice. The flag has no effect when the interpreter is
    
    15
    +    dynamically linked, as neither happens then.
    
    16
    +}

  • compiler/GHC.hs
    ... ... @@ -29,7 +29,7 @@ module GHC (
    29 29
             -- * Flags and settings
    
    30 30
             DynFlags(..), GeneralFlag(..), Severity(..), Backend, gopt,
    
    31 31
             ncgBackend, llvmBackend, viaCBackend, bytecodeBackend, interpreterBackend, noBackend,
    
    32
    -        GhcMode(..), GhcLink(..),
    
    32
    +        GhcMode(..), GhcLink(..), UnloadStrategy(..),
    
    33 33
             parseDynamicFlags, parseTargetFiles,
    
    34 34
             getSessionDynFlags,
    
    35 35
             setTopSessionDynFlags,
    
    ... ... @@ -728,6 +728,12 @@ setTopSessionDynFlags dflags = do
    728 728
     
    
    729 729
       interp <- liftIO $ initInterpreter dflags tmpfs logger platform finder_cache unit_env interp_opts
    
    730 730
     
    
    731
    +  case (hsc_interp hsc_env, unloadStrategy dflags, interp) of
    
    732
    +    (Nothing, Just _, Just i) | interpreterDynamic i ->
    
    733
    +      liftIO $ logInfo logger $ withPprStyle defaultUserStyle $
    
    734
    +        text "warning: -funload-strategy is ignored with a dynamic interpreter"
    
    735
    +    _ -> return ()
    
    736
    +
    
    731 737
       modifySession $ \h -> hscSetFlags dflags
    
    732 738
                             h{ hsc_IC = (hsc_IC h){ ic_dflags = dflags }
    
    733 739
                              , hsc_interp = hsc_interp h <|> interp
    

  • compiler/GHC/Driver/Config/Interpreter.hs
    ... ... @@ -43,4 +43,5 @@ initInterpOpts dflags = do
    43 43
         , interpLdConfig = configureLd dflags
    
    44 44
         , interpCcConfig = configureCc dflags
    
    45 45
         , interpExecutableLinkOpts = initExecutableLinkOpts dflags Dynamic
    
    46
    +    , interpUnloadStrategyFlag = unloadStrategy dflags
    
    46 47
         }

  • compiler/GHC/Driver/DynFlags.hs
    ... ... @@ -40,6 +40,7 @@ module GHC.Driver.DynFlags (
    40 40
             isPackageDbRef,
    
    41 41
             Option(..), showOpt,
    
    42 42
             DynLibLoader(..),
    
    43
    +        UnloadStrategy(..),
    
    43 44
             positionIndependent,
    
    44 45
             optimisationFlags,
    
    45 46
     
    
    ... ... @@ -308,6 +309,7 @@ data DynFlags = DynFlags {
    308 309
       outputHi              :: Maybe String,
    
    309 310
       dynOutputHi           :: Maybe String,
    
    310 311
       dynLibLoader          :: DynLibLoader,
    
    312
    +  unloadStrategy        :: Maybe UnloadStrategy,
    
    311 313
     
    
    312 314
       dynamicNow            :: !Bool, -- ^ Indicate if we are now generating dynamic output
    
    313 315
                                       -- because of -dynamic-too. This predicate is
    
    ... ... @@ -657,6 +659,7 @@ defaultDynFlags mySettings =
    657 659
             outputHi                = Nothing,
    
    658 660
             dynOutputHi             = Nothing,
    
    659 661
             dynLibLoader            = SystemDependent,
    
    662
    +        unloadStrategy          = Nothing,
    
    660 663
             dumpPrefix              = "non-module.",
    
    661 664
             dumpPrefixForce         = Nothing,
    
    662 665
             ldInputs                = [],
    
    ... ... @@ -965,6 +968,11 @@ data DynLibLoader
    965 968
       | SystemDependent
    
    966 969
       deriving Eq
    
    967 970
     
    
    971
    +data UnloadStrategy
    
    972
    +  = UnloadStrategyUnload
    
    973
    +  | UnloadStrategyPurge
    
    974
    +  deriving Eq
    
    975
    +
    
    968 976
     data RtsOptsEnabled
    
    969 977
       = RtsOptsNone | RtsOptsIgnore | RtsOptsIgnoreAll | RtsOptsSafeOnly
    
    970 978
       | RtsOptsAll
    

  • compiler/GHC/Driver/Session.hs
    ... ... @@ -55,6 +55,7 @@ module GHC.Driver.Session (
    55 55
             PackageDBFlag(..), PkgDbRef(..),
    
    56 56
             Option(..), showOpt,
    
    57 57
             DynLibLoader(..),
    
    58
    +        UnloadStrategy(..),
    
    58 59
             fFlags, fLangFlags, xFlags,
    
    59 60
             wWarningFlags,
    
    60 61
             makeDynFlagsConsistent,
    
    ... ... @@ -725,6 +726,12 @@ parseDynLibLoaderMode f d =
    725 726
        ("sysdep", "")       -> d { dynLibLoader = SystemDependent }
    
    726 727
        _                    -> throwGhcException (CmdLineError ("Unknown dynlib loader: " ++ f))
    
    727 728
     
    
    729
    +parseUnloadStrategy :: String -> DynFlags -> DynFlags
    
    730
    +parseUnloadStrategy f d = case f of
    
    731
    +  "unload" -> d { unloadStrategy = Just UnloadStrategyUnload }
    
    732
    +  "purge"  -> d { unloadStrategy = Just UnloadStrategyPurge }
    
    733
    +  _        -> throwGhcException (CmdLineError ("Unknown unload strategy: " ++ f))
    
    734
    +
    
    728 735
     setDumpPrefixForce f d = d { dumpPrefixForce = f}
    
    729 736
     
    
    730 737
     -- XXX HACK: Prelude> words "'does not' work" ===> ["'does","not'","work"]
    
    ... ... @@ -1892,6 +1899,8 @@ dynamic_flags_deps = [
    1892 1899
           (intSuffix (\n d -> d {maxForcedSpecArgs = n}))
    
    1893 1900
       , make_ord_flag defGhciFlag "fghci-hist-size"
    
    1894 1901
           (intSuffix (\n d -> d {ghciHistSize = n}))
    
    1902
    +  , make_ord_flag defFlag "funload-strategy"
    
    1903
    +      (hasArg parseUnloadStrategy)
    
    1895 1904
     
    
    1896 1905
       -- wasm ghci browser mode
    
    1897 1906
       , make_ord_flag defGhciFlag "fghci-browser-host"
    

  • compiler/GHC/Linker/Loader.hs
    ... ... @@ -111,6 +111,7 @@ import GHC.Linker.Types
    111 111
     import Control.Monad
    
    112 112
     
    
    113 113
     import Data.Array
    
    114
    +import Data.Containers.ListUtils (nubOrd)
    
    114 115
     import Data.ByteString (ByteString)
    
    115 116
     import qualified Data.Set as Set
    
    116 117
     import Data.Char (isSpace)
    
    ... ... @@ -898,8 +899,9 @@ dropModules interp mods pls = do
    898 899
                 }
    
    899 900
             }
    
    900 901
     
    
    901
    -  mapM_ (purgeLinkableObjs interp) victim_usages
    
    902
    -  when (any (not . null . linkableUsageObjs) victim_usages) $
    
    902
    +  let victim_objs = nubOrd (concatMap linkableUsageObjs victim_usages)
    
    903
    +  dropLinkableObjs interp victim_objs
    
    904
    +  when (not (null victim_objs)) $
    
    903 905
         purgeLookupSymbolCache interp
    
    904 906
     
    
    905 907
       mapM_ (removeSptEntry interp)
    
    ... ... @@ -914,31 +916,14 @@ dropModules interp mods pls = do
    914 916
             modifyHomePackageBytecodeState (bco_loader_state pls) drop_bytecode_state
    
    915 917
         }
    
    916 918
     
    
    917
    --- | Purge the symbols of a dropped module's objects. We don't unload
    
    918
    --- them, because unloading is not well supported.
    
    919
    --- See Note [Automatically reloading stale linkables]
    
    920
    --- See Note [Unloading vs purging objects] in GHC.Runtime.Interpreter
    
    921
    -purgeLinkableObjs :: Interp -> LinkableUsage -> IO ()
    
    922
    -purgeLinkableObjs interp lnk
    
    923
    -  | interpreterDynamic interp = return ()
    
    924
    -  | otherwise
    
    925
    -  = mapM_ (purgeObj interp) (linkableUsageObjs lnk)
    
    926
    -
    
    927 919
     -- See Note [Unloading vs purging objects] in GHC.Runtime.Interpreter
    
    928
    -unloadLinkableObjs :: Interp -> LinkableUsage -> IO ()
    
    929
    -unloadLinkableObjs interp lnk
    
    920
    +dropLinkableObjs :: Interp -> [FilePath] -> IO ()
    
    921
    +dropLinkableObjs interp objs
    
    930 922
       | interpreterDynamic interp = return ()
    
    931
    -    -- We don't do any cleanup when linking objects with the
    
    932
    -    -- dynamic linker.  Doing so introduces extra complexity for
    
    933
    -    -- not much benefit.
    
    934 923
       | otherwise
    
    935
    -  = mapM_ (unloadObj interp) (linkableUsageObjs lnk)
    
    936
    -      -- The components of a BCO linkable may contain
    
    937
    -      -- dot-o files (generated from C stubs).
    
    938
    -      --
    
    939
    -      -- But the BCO parts can be unlinked just by
    
    940
    -      -- letting go of them (plus of course depopulating
    
    941
    -      -- the symbol table which is done in the main body)
    
    924
    +  = case interpUnloadStrategy interp of
    
    925
    +      UnloadStrategyUnload -> mapM_ (unloadObj interp) objs
    
    926
    +      UnloadStrategyPurge  -> mapM_ (purgeObj interp) objs
    
    942 927
     
    
    943 928
     -- | Load a linkable from a module, and add all the names from the linkable into the
    
    944 929
     -- closure environment.
    
    ... ... @@ -1354,12 +1339,13 @@ unload_wkr interp pls@LoaderState{..} = do
    1354 1339
       -- testsuite/ghci can detect space leaks here.
    
    1355 1340
     
    
    1356 1341
       let linkables_to_unload = moduleEnvElts objs_loaded ++ moduleEnvElts bcos_loaded
    
    1342
    +      objs_to_unload = nubOrd (concatMap linkableUsageObjs linkables_to_unload)
    
    1357 1343
     
    
    1358
    -  mapM_ (unloadLinkableObjs interp) linkables_to_unload
    
    1344
    +  dropLinkableObjs interp objs_to_unload
    
    1359 1345
     
    
    1360 1346
       -- If we unloaded any object files at all, we need to purge the cache
    
    1361 1347
       -- of lookupSymbol results.
    
    1362
    -  when (not (null (filter (not . null . linkableUsageObjs) linkables_to_unload))) $
    
    1348
    +  when (not (null objs_to_unload)) $
    
    1363 1349
         purgeLookupSymbolCache interp
    
    1364 1350
     
    
    1365 1351
       mapM_ (removeSptEntry interp) (concat (moduleEnvElts loaded_spt_keys))
    

  • compiler/GHC/Runtime/Interpreter.hs
    ... ... @@ -601,24 +601,33 @@ unloadObj interp path = do
    601 601
     
    
    602 602
     {- Note [Unloading vs purging objects]
    
    603 603
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    604
    -unloadObj removes the object's symbols and frees its memory. The memory
    
    605
    -is only freed at a major GC, once nothing references the object.
    
    606
    -purgeObj removes the symbols and never frees the memory.
    
    607
    -
    
    608
    -We only unloadObj in unload, which the driver calls before a
    
    609
    -compilation sweep, when everything is unloaded together. We purgeObj
    
    610
    -when dropModules replaces or removes single modules, because unloading
    
    611
    -is not well supported on many platforms/configurations. Purging is
    
    612
    -enough for correctness: new lookups find the replacement's symbols,
    
    613
    -and values built by the old code and computations still using it keep
    
    614
    -working.
    
    615
    -
    
    616
    -With a dynamic interpreter there is nothing to purge. Objects are
    
    617
    -linked into temporary shared libraries and their symbols are found by
    
    618
    -searching the loaded libraries, not in the linker's symbol table.
    
    619
    -Dropping a module flushes the symbol cache, and the replacement is
    
    620
    -loaded as a new library, so lookups find the replacement first and the
    
    621
    -old library stays loaded. This behaves like purging.
    
    604
    +There are two ways to drop objects:
    
    605
    +
    
    606
    +- unloadObj removes the object's symbols and eventually the RTS may decide to free its memory.
    
    607
    +- purgeObj removes the symbols and never frees the memory.
    
    608
    +
    
    609
    +purgeObj is enough for correctness, but leaks memory. unloadObj can be finnicky on certain
    
    610
    +platforms and/or may not be implemented correctly.
    
    611
    +
    
    612
    +We use the unload strategy given by -funload-strategy.
    
    613
    +The strategy we use by default depends on platform calculus, given the
    
    614
    +bugs that apply to each platform. We try to unload where we don't know of
    
    615
    +any bugs affecting correctness:
    
    616
    +
    
    617
    +- Linux: mostly unload.
    
    618
    +  - i386/x86_64: unload
    
    619
    +  - AArch64: We unload, but perhaps we should purge instead because of #24170.
    
    620
    +  - 32-bit ARM: purge. Unloading is broken (#21991).
    
    621
    +- FreeBSD: purge. Unloading is not implemented (#25491).
    
    622
    +- Darwin: purge. The process crashes at exit if an unloaded object
    
    623
    +  had C finalizers (#27616).
    
    624
    +- Windows: purge. Unloading code that is still in use is fragile
    
    625
    +  (#20852).
    
    626
    +- Purge as a fallback for everything else
    
    627
    +
    
    628
    +With a dynamic interpreter we never purge, or unload:
    
    629
    +Dynamic objects are linked into temporary shared libraries, and if a library is
    
    630
    +reloaded, then it shadows over the old one.
    
    622 631
     -}
    
    623 632
     
    
    624 633
     -- | Purge an object's symbols.
    

  • compiler/GHC/Runtime/Interpreter/Init.hs
    ... ... @@ -57,6 +57,7 @@ data InterpOpts = InterpOpts
    57 57
       , interpBrowserPlaywrightBrowserType :: Maybe String
    
    58 58
       , interpBrowserPlaywrightLaunchOpts :: Maybe String
    
    59 59
       , interpExecutableLinkOpts :: ExecutableLinkOpts
    
    60
    +  , interpUnloadStrategyFlag :: Maybe UnloadStrategy
    
    60 61
       }
    
    61 62
     
    
    62 63
     -- | Initialize code interpreter
    
    ... ... @@ -87,6 +88,14 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do
    87 88
             | otherwise =
    
    88 89
                 Just (objectSuf dflags, waysTag (WayDyn `addWay` target_full_ways) ++ "_o")
    
    89 90
     
    
    91
    +  -- See Note [Unloading vs purging objects] in GHC.Runtime.Interpreter
    
    92
    +  let unload_strategy = case interpUnloadStrategyFlag opts of
    
    93
    +        Just s -> s
    
    94
    +        Nothing -> case (platformOS platform, platformArch platform) of
    
    95
    +          (OSLinux, ArchARM {}) -> UnloadStrategyPurge
    
    96
    +          (OSLinux, _)          -> UnloadStrategyUnload
    
    97
    +          _                     -> UnloadStrategyPurge
    
    98
    +
    
    90 99
       -- see Note [Target code interpreter]
    
    91 100
       if
    
    92 101
     #if !defined(wasm32_HOST_ARCH)
    
    ... ... @@ -116,7 +125,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do
    116 125
                     , wasmInterpHsSoSuffix = way_tag ++ dynLibSuffix (interpNameVer opts)
    
    117 126
                     , wasmInterpUnitState = ue_homeUnitState unit_env
    
    118 127
                     }
    
    119
    -        pure $ Just $ Interp (ExternalInterp $ ExtWasm $ ExtInterpState cfg s) loader lookup_cache fs_cache wasm_obj_suffix
    
    128
    +        pure $ Just $ Interp (ExternalInterp $ ExtWasm $ ExtInterpState cfg s) loader lookup_cache fs_cache wasm_obj_suffix unload_strategy
    
    120 129
     #endif
    
    121 130
     
    
    122 131
         -- JavaScript interpreter
    
    ... ... @@ -135,7 +144,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do
    135 144
                   , jsInterpFinderOpts  = interpFinderOpts opts
    
    136 145
                   , jsInterpFinderCache = finder_cache
    
    137 146
                   }
    
    138
    -         return (Just (Interp (ExternalInterp (ExtJS (ExtInterpState cfg s))) loader lookup_cache fs_cache Nothing))
    
    147
    +         return (Just (Interp (ExternalInterp (ExtJS (ExtInterpState cfg s))) loader lookup_cache fs_cache Nothing unload_strategy))
    
    139 148
     
    
    140 149
         -- external interpreter
    
    141 150
         | interpExternal opts
    
    ... ... @@ -162,7 +171,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do
    162 171
                }
    
    163 172
             s <- liftIO $ newMVar InterpPending
    
    164 173
             loader <- liftIO Loader.uninitializedLoader
    
    165
    -        return (Just (Interp (ExternalInterp (ExtIServ (ExtInterpState conf s))) loader lookup_cache fs_cache Nothing))
    
    174
    +        return (Just (Interp (ExternalInterp (ExtIServ (ExtInterpState conf s))) loader lookup_cache fs_cache Nothing unload_strategy))
    
    166 175
     
    
    167 176
         -- Internal interpreter
    
    168 177
         | otherwise
    
    ... ... @@ -170,7 +179,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do
    170 179
     #if defined(HAVE_INTERNAL_INTERPRETER)
    
    171 180
          do
    
    172 181
           loader <- liftIO Loader.uninitializedLoader
    
    173
    -      return (Just (Interp InternalInterp loader lookup_cache fs_cache internal_obj_suffix))
    
    182
    +      return (Just (Interp InternalInterp loader lookup_cache fs_cache internal_obj_suffix unload_strategy))
    
    174 183
     #else
    
    175 184
           return Nothing
    
    176 185
     #endif

  • compiler/GHC/Runtime/Interpreter/Types.hs
    ... ... @@ -52,6 +52,7 @@ import GHC.Platform
    52 52
     #if defined(HAVE_INTERNAL_INTERPRETER)
    
    53 53
     import GHC.Platform.Ways
    
    54 54
     #endif
    
    55
    +import GHC.Driver.DynFlags (UnloadStrategy)
    
    55 56
     import GHC.Utils.TmpFs
    
    56 57
     import GHC.Utils.Logger
    
    57 58
     import GHC.Unit.Env
    
    ... ... @@ -83,6 +84,8 @@ data Interp = Interp
    83 84
       , interpObjSuffix :: !(Maybe (String, String))
    
    84 85
           -- ^ @(from, to)@ object suffixes to swap when the interpreter cannot
    
    85 86
           -- load objects built the target's way
    
    87
    +
    
    88
    +  , interpUnloadStrategy :: !UnloadStrategy
    
    86 89
       }
    
    87 90
     
    
    88 91
     data InterpInstance
    

  • docs/users_guide/ghci.rst
    ... ... @@ -3554,6 +3554,32 @@ breakpoints in object-code modules, for example. Only the exports of an
    3554 3554
     object-code module will be visible in GHCi, rather than all top-level
    
    3555 3555
     bindings as in interpreted modules.
    
    3556 3556
     
    
    3557
    +.. ghc-flag:: -funload-strategy=⟨strategy⟩
    
    3558
    +    :shortdesc: Whether to ``purge`` or ``unload`` object code that the
    
    3559
    +        interpreter drops.
    
    3560
    +    :type: dynamic
    
    3561
    +    :category: linking
    
    3562
    +
    
    3563
    +    :since: 10.2.1
    
    3564
    +
    
    3565
    +    When the interpreter drops object code it has loaded, it can either
    
    3566
    +    *purge* it or *unload* it.
    
    3567
    +
    
    3568
    +    Purging clears the symbol table entries the object contributed, so
    
    3569
    +    that nothing linked afterwards can refer to it, but the object stays
    
    3570
    +    in memory. Unloading does the same, and additionally marks the object
    
    3571
    +    as no longer needed, so that a later garbage collection may notice
    
    3572
    +    that nothing refers to it and reclaim its memory.
    
    3573
    +
    
    3574
    +    Unloading is the better choice where it works, but its implementation
    
    3575
    +    is fragile on a number of platforms, so GHC purges on those instead.
    
    3576
    +    By default GHC unloads on Linux other than ARM, and purges everywhere
    
    3577
    +    else. This flag opts into the other choice.
    
    3578
    +
    
    3579
    +    The distinction is moot when the interpreter is dynamically linked,
    
    3580
    +    as neither purging nor unloading happens then. GHC warns that the
    
    3581
    +    flag is ignored in that case.
    
    3582
    +
    
    3557 3583
     .. _external-interpreter:
    
    3558 3584
     
    
    3559 3585
     Running the interpreter in a separate process
    

  • testsuite/tests/ghc-api/T27606/T27606c.hs
    ... ... @@ -24,11 +24,16 @@ import Unsafe.Coerce (unsafeCoerce)
    24 24
     
    
    25 25
     main :: IO ()
    
    26 26
     main = do
    
    27
    -  [libdir] <- getArgs
    
    27
    +  libdir:rest <- getArgs
    
    28
    +  let strat = case rest of
    
    29
    +        []         -> Nothing
    
    30
    +        ["purge"]  -> Just UnloadStrategyPurge
    
    31
    +        ["unload"] -> Just UnloadStrategyUnload
    
    32
    +        _          -> error "usage: T27606c <libdir> [purge|unload]"
    
    28 33
       writeA 1
    
    29 34
       writeC
    
    30 35
       runGhc (Just libdir) $ do
    
    31
    -    setupSession ["B.hs", "C.hs"]
    
    36
    +    setupSession strat ["B.hs", "C.hs"]
    
    32 37
         _ <- load LoadAllTargets
    
    33 38
         setContext [ IIDecl (simpleImportDecl (mkModuleName "Prelude"))
    
    34 39
                    , IIDecl (simpleImportDecl (mkModuleName "A"))
    
    ... ... @@ -61,10 +66,10 @@ writeC = writeFile "C.hs" $ unlines
    61 66
       , "c = unsafePerformIO (appendFile \"c.log\" \"x\" >> pure 100)"
    
    62 67
       ]
    
    63 68
     
    
    64
    -setupSession :: [String] -> Ghc ()
    
    65
    -setupSession targets = do
    
    69
    +setupSession :: Maybe UnloadStrategy -> [String] -> Ghc ()
    
    70
    +setupSession strat targets = do
    
    66 71
       df <- getSessionDynFlags
    
    67
    -  _ <- setSessionDynFlags df { ghcLink = LinkInMemory }
    
    72
    +  _ <- setSessionDynFlags df { ghcLink = LinkInMemory, unloadStrategy = strat }
    
    68 73
       ts <- mapM (\t -> guessTarget t Nothing Nothing) targets
    
    69 74
       setTargets ts
    
    70 75
     
    
    ... ... @@ -74,7 +79,7 @@ setupSession targets = do
    74 79
     compileA :: String -> NameCache -> IO HomeModInfo
    
    75 80
     compileA libdir nc = runGhc (Just libdir) $ do
    
    76 81
       getSession >>= \h -> setSession h { hsc_NC = nc }
    
    77
    -  setupSession ["A.hs"]
    
    82
    +  setupSession Nothing ["A.hs"]
    
    78 83
       ok <- load LoadAllTargets
    
    79 84
       when (failed ok) $ error "compileA: load failed"
    
    80 85
       hsc <- getSession
    

  • testsuite/tests/ghc-api/T27606/T27606c_purge.stdout
    1
    +102
    
    2
    +102
    
    3
    +104
    
    4
    +104
    
    5
    +1

  • testsuite/tests/ghc-api/T27606/all.T
    ... ... @@ -25,6 +25,15 @@ test('T27606c',
    25 25
          compile_and_run,
    
    26 26
          ['-package ghc'])
    
    27 27
     
    
    28
    +test('T27606c_purge',
    
    29
    +     [extra_run_opts(f'"{config.libdir}" purge'),
    
    30
    +      extra_files(['T27606c.hs', 'B.hs']),
    
    31
    +      req_rts_linker,
    
    32
    +      when(config.ghc_dynamic, skip),
    
    33
    +      when(arch('wasm32'), skip)],
    
    34
    +     multimod_compile_and_run,
    
    35
    +     ['T27606c', '-package ghc'])
    
    36
    +
    
    28 37
     test('T27606d',
    
    29 38
          [extra_run_opts(f'"{config.libdir}"'),
    
    30 39
           req_rts_linker,