Zubin pushed to branch wip/linkable-hashes at Glasgow Haskell Compiler / GHC
Commits:
-
8eea219f
by Zubin Duggal at 2026-08-25T14:52:54+05:30
-
e1c8030b
by Zubin Duggal at 2026-08-25T14:52:54+05:30
-
afdb51c2
by Zubin Duggal at 2026-08-25T14:52:54+05:30
-
c7f64e7d
by Zubin Duggal at 2026-08-25T14:52:54+05:30
15 changed files:
- compiler/GHC/ByteCode/Serialize.hs
- compiler/GHC/Driver/Main/Compile.hs
- compiler/GHC/Driver/Main/Passes.hs
- compiler/GHC/Driver/Pipeline.hs
- compiler/GHC/Linker/ByteCode.hs
- compiler/GHC/Linker/Deps.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Linker/Types.hs
- compiler/GHC/Runtime/Interpreter/Init.hs
- compiler/GHC/Runtime/Interpreter/Types.hs
- compiler/GHC/Unit/Finder.hs
- + testsuite/tests/driver/recomp023/M.hs
- + testsuite/tests/driver/recomp023/Makefile
- + testsuite/tests/driver/recomp023/all.T
- + testsuite/tests/driver/recomp023/recomp023.stdout
Changes:
| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | {- | This module implements the serialization of bytecode objects to and from disk.
|
| 7 | 7 | -}
|
| 8 | 8 | module GHC.ByteCode.Serialize
|
| 9 | - ( writeBinByteCode, readBinByteCode, readOnDiskModuleByteCode
|
|
| 9 | + ( writeBinByteCode, readBinByteCode, readBinByteCodeHash, readOnDiskModuleByteCode
|
|
| 10 | 10 | , ModuleByteCode(..)
|
| 11 | 11 | , BytecodeLibX(..)
|
| 12 | 12 | , BytecodeLib
|
| ... | ... | @@ -15,6 +15,7 @@ module GHC.ByteCode.Serialize |
| 15 | 15 | , InterpreterLibraryContents(..)
|
| 16 | 16 | , writeBytecodeLib
|
| 17 | 17 | , readBytecodeLib
|
| 18 | + , readBytecodeLibInputsHash
|
|
| 18 | 19 | , mkModuleByteCode
|
| 19 | 20 | , fingerprintModuleByteCodeContents
|
| 20 | 21 | , decodeOnDiskModuleByteCode
|
| ... | ... | @@ -84,6 +85,7 @@ The ticket where bytecode objects were dicussed is #26298 |
| 84 | 85 | See Note [-fwrite-byte-code is not the default]
|
| 85 | 86 | See Note [Recompilation avoidance with bytecode objects]
|
| 86 | 87 | See Note [Persistent bytecode file headers]
|
| 88 | +See Note [Hash of bytecode libs]
|
|
| 87 | 89 | |
| 88 | 90 | Note [Persistent bytecode file headers]
|
| 89 | 91 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -96,15 +98,28 @@ To make these failures explicit, we write a file-kind-specific magic word and |
| 96 | 98 | the current `hiVersion` ahead of the binary payload. Readers validate this
|
| 97 | 99 | header before setting up the normal `Name`/`FastString` deserialisation
|
| 98 | 100 | machinery. This follows the same approach as normal interface files.
|
| 101 | + |
|
| 102 | +Note [Hash of bytecode libs]
|
|
| 103 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 104 | +A bytecode library records the combined hash of its constituents, so
|
|
| 105 | +that relinking can be skipped when they haven't changed.
|
|
| 106 | + |
|
| 107 | +We can't hash the library file itself: we would have to build the
|
|
| 108 | +library to know which hash to expect. So we compare the inputs.
|
|
| 109 | + |
|
| 110 | +The hash is written right after the file header. The constituent hashes
|
|
| 111 | +are sorted before they are combined, so the order of the modules
|
|
| 112 | +doesn't matter.
|
|
| 99 | 113 | -}
|
| 100 | 114 | |
| 101 | -writeBytecodeLib :: BytecodeLib -> FilePath -> IO ()
|
|
| 102 | -writeBytecodeLib lib path = do
|
|
| 115 | +writeBytecodeLib :: Fingerprint -> BytecodeLib -> FilePath -> IO ()
|
|
| 116 | +writeBytecodeLib inputs_hash lib path = do
|
|
| 103 | 117 | odbco <- encodeBytecodeLib lib
|
| 104 | 118 | createDirectoryIfMissing True (takeDirectory path)
|
| 105 | 119 | bh' <- openBinMem initBinMemSize
|
| 106 | 120 | bh <- addBinNameWriter bh'
|
| 107 | 121 | writePersistentBytecodeHeader BytecodeLibraryFile bh
|
| 122 | + put_ bh inputs_hash
|
|
| 108 | 123 | putWithUserData QuietBinIFace NormalCompression bh odbco
|
| 109 | 124 | writeBinMem bh path
|
| 110 | 125 | |
| ... | ... | @@ -112,10 +127,18 @@ readBytecodeLib :: HscEnv -> FilePath -> IO OnDiskBytecodeLib |
| 112 | 127 | readBytecodeLib hsc_env path = do
|
| 113 | 128 | bh' <- readBinMem path
|
| 114 | 129 | readPersistentBytecodeHeader BytecodeLibraryFile path bh'
|
| 130 | + _inputs_hash <- get bh' :: IO Fingerprint
|
|
| 115 | 131 | bh <- addBinNameReader (hsc_NC hsc_env) bh'
|
| 116 | 132 | res <- getWithUserData (hsc_NC hsc_env) bh
|
| 117 | 133 | pure res
|
| 118 | 134 | |
| 135 | +-- See Note [Hash of bytecode libs]
|
|
| 136 | +readBytecodeLibInputsHash :: FilePath -> IO Fingerprint
|
|
| 137 | +readBytecodeLibInputsHash path = do
|
|
| 138 | + bh <- readBinMem path
|
|
| 139 | + readPersistentBytecodeHeader BytecodeLibraryFile path bh
|
|
| 140 | + get bh
|
|
| 141 | + |
|
| 119 | 142 | -- | Convert an 'OnDiskModuleByteCode' to an 'ModuleByteCode'.
|
| 120 | 143 | -- 'OnDiskModuleByteCode' is the representation which we read from a file,
|
| 121 | 144 | -- the 'ModuleByteCode' is the representation which is manipulated by program logic.
|
| ... | ... | @@ -200,6 +223,10 @@ readBinByteCode hsc_env f = do |
| 200 | 223 | odbco <- readOnDiskModuleByteCode hsc_env f
|
| 201 | 224 | decodeOnDiskModuleByteCode hsc_env odbco
|
| 202 | 225 | |
| 226 | +-- | Read only the hash from the start of a bytecode file
|
|
| 227 | +readBinByteCodeHash :: HscEnv -> FilePath -> IO Fingerprint
|
|
| 228 | +readBinByteCodeHash hsc_env f = odgbc_hash <$> readOnDiskModuleByteCode hsc_env f
|
|
| 229 | + |
|
| 203 | 230 | readOnDiskModuleByteCode :: HscEnv -> FilePath -> IO OnDiskModuleByteCode
|
| 204 | 231 | readOnDiskModuleByteCode hsc_env f = do
|
| 205 | 232 | bh' <- readBinMem f
|
| ... | ... | @@ -132,7 +132,6 @@ import GHC.Data.OsPath (unsafeEncodeUtf) |
| 132 | 132 | import qualified GHC.Data.Stream as Stream
|
| 133 | 133 | |
| 134 | 134 | |
| 135 | -import Data.Traversable (for)
|
|
| 136 | 135 | import Control.Monad
|
| 137 | 136 | import Data.IORef
|
| 138 | 137 | import System.Directory
|
| ... | ... | @@ -141,7 +140,6 @@ import Data.Map (Map) |
| 141 | 140 | import qualified Data.Set as S
|
| 142 | 141 | import GHC.Unit.Module.WholeCoreBindings
|
| 143 | 142 | import GHC.Types.TypeEnv
|
| 144 | -import Data.Time
|
|
| 145 | 143 | |
| 146 | 144 | import System.IO.Unsafe ( unsafeInterleaveIO )
|
| 147 | 145 | import GHC.Iface.Env ( trace_if )
|
| ... | ... | @@ -218,12 +216,7 @@ loadIfaceByteCode hsc_env iface location type_env = |
| 218 | 216 | where
|
| 219 | 217 | compile decls = do
|
| 220 | 218 | bco <- compileWholeCoreBindings hsc_env type_env decls
|
| 221 | - linkable $ pure $ DotGBC bco
|
|
| 222 | - |
|
| 223 | - linkable parts = do
|
|
| 224 | - if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
|
|
| 225 | - time <- maybe getCurrentTime pure if_time
|
|
| 226 | - return $! Linkable time (mi_module iface) parts
|
|
| 219 | + return $! Linkable (gbc_hash bco) (mi_module iface) (pure (DotGBC bco))
|
|
| 227 | 220 | |
| 228 | 221 | loadIfaceByteCodeLazy ::
|
| 229 | 222 | HscEnv ->
|
| ... | ... | @@ -240,12 +233,7 @@ loadIfaceByteCodeLazy hsc_env iface location type_env = |
| 240 | 233 | compile decls = do
|
| 241 | 234 | bco <- unsafeInterleaveIO $ do
|
| 242 | 235 | compileWholeCoreBindings hsc_env type_env decls
|
| 243 | - linkable bco
|
|
| 244 | - |
|
| 245 | - linkable parts = do
|
|
| 246 | - if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
|
|
| 247 | - time <- maybe getCurrentTime pure if_time
|
|
| 248 | - return $!Linkable time (mi_module iface) parts
|
|
| 236 | + return $! Linkable (gbc_hash bco) (mi_module iface) bco
|
|
| 249 | 237 | |
| 250 | 238 | -- | If the 'Linkable' contains Core bindings loaded from an interface, replace
|
| 251 | 239 | -- them with a lazy IO thunk that compiles them to bytecode and foreign objects,
|
| ... | ... | @@ -283,12 +271,13 @@ initWholeCoreBindings hsc_env iface details (RecompLinkables bc o) = do |
| 283 | 271 | |
| 284 | 272 | go :: RecompBytecodeLinkable -> IO (Maybe (LinkableWith ModuleByteCode))
|
| 285 | 273 | go (NormalLinkable l) = pure l
|
| 286 | - go (WholeCoreBindingsLinkable wcbl) =
|
|
| 287 | - fmap Just $ for wcbl $ \wcb -> do
|
|
| 288 | - add_iface_to_hpt iface details hsc_env
|
|
| 289 | - bco <- unsafeInterleaveIO $ do
|
|
| 290 | - compileWholeCoreBindings hsc_env type_env wcb
|
|
| 291 | - pure bco
|
|
| 274 | + go (WholeCoreBindingsLinkable wcbl) = do
|
|
| 275 | + add_iface_to_hpt iface details hsc_env
|
|
| 276 | + bco <- unsafeInterleaveIO $ do
|
|
| 277 | + compileWholeCoreBindings hsc_env type_env (linkableParts wcbl)
|
|
| 278 | + -- We need to fill in the hash over here, replacing the panic
|
|
| 279 | + -- because WholeCoreBindingsLinkable doesn't have a hash.
|
|
| 280 | + pure $ Just $ Linkable (gbc_hash bco) (linkableModule wcbl) bco
|
|
| 292 | 281 | |
| 293 | 282 | -- | Hydrate interface Core bindings and compile them to bytecode.
|
| 294 | 283 | --
|
| ... | ... | @@ -842,11 +831,7 @@ make user's opt into writing the files. |
| 842 | 831 | generateAndWriteByteCodeLinkable :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO (LinkableWith ModuleByteCode)
|
| 843 | 832 | generateAndWriteByteCodeLinkable hsc_env cgguts mod_location = do
|
| 844 | 833 | bco_object <- generateAndWriteByteCode hsc_env cgguts mod_location
|
| 845 | - -- Either, get the same time as the .gbc file if it exists, or just the current time.
|
|
| 846 | - -- It's important the time of the linkable matches the time of the .gbc file for recompilation
|
|
| 847 | - -- checking.
|
|
| 848 | - bco_time <- maybe getCurrentTime pure =<< modificationTimeIfExists (ml_bytecode_file_ospath mod_location)
|
|
| 849 | - return $ mkOnlyModuleByteCodeLinkable bco_time bco_object
|
|
| 834 | + return $ mkOnlyModuleByteCodeLinkable bco_object
|
|
| 850 | 835 | |
| 851 | 836 | mkModuleByteCode :: HscEnv -> Module -> ModLocation -> CgInteractiveGuts -> IO ModuleByteCode
|
| 852 | 837 | mkModuleByteCode hsc_env mod mod_location cgguts = do
|
| ... | ... | @@ -861,9 +846,8 @@ generateFreshByteCodeLinkable :: HscEnv |
| 861 | 846 | -> ModLocation
|
| 862 | 847 | -> IO Linkable
|
| 863 | 848 | generateFreshByteCodeLinkable hsc_env mod_name cgguts mod_location = do
|
| 864 | - bco_time <- getCurrentTime
|
|
| 865 | 849 | bco_object <- mkModuleByteCode hsc_env (mkHomeModule (hsc_home_unit hsc_env) mod_name) mod_location cgguts
|
| 866 | - return $ mkModuleByteCodeLinkable bco_time bco_object
|
|
| 850 | + return $ mkModuleByteCodeLinkable bco_object
|
|
| 867 | 851 | ------------------------------
|
| 868 | 852 | |
| 869 | 853 | hscCompileCmmFile :: HscEnv -> FilePath -> FilePath -> FilePath -> IO (Maybe FilePath)
|
| ... | ... | @@ -153,7 +153,7 @@ import GHC.Types.Unique.Set |
| 153 | 153 | import GHC.Types.Var.Env ( mkEmptyTidyEnv )
|
| 154 | 154 | import GHC.Types.Var.Set
|
| 155 | 155 | |
| 156 | -import GHC.Utils.Fingerprint ( Fingerprint )
|
|
| 156 | +import GHC.Utils.Fingerprint ( Fingerprint, getFileHash )
|
|
| 157 | 157 | import GHC.Utils.Panic
|
| 158 | 158 | import GHC.Utils.Error
|
| 159 | 159 | import GHC.Utils.Outputable
|
| ... | ... | @@ -168,8 +168,8 @@ import GHC.Data.Maybe |
| 168 | 168 | import qualified GHC.Data.Strict as Strict
|
| 169 | 169 | |
| 170 | 170 | import qualified Data.Array as A
|
| 171 | -import Data.List ( nub, isPrefixOf, partition )
|
|
| 172 | 171 | import qualified Data.List.NonEmpty as NE
|
| 172 | +import Data.List ( nub, isPrefixOf, partition )
|
|
| 173 | 173 | import Control.Monad
|
| 174 | 174 | import Data.IORef
|
| 175 | 175 | import System.FilePath as FilePath
|
| ... | ... | @@ -180,7 +180,6 @@ import Data.Set (Set) |
| 180 | 180 | import Control.DeepSeq (force)
|
| 181 | 181 | import Control.Exception as E (mask_, finally)
|
| 182 | 182 | import Data.List.NonEmpty (NonEmpty ((:|)))
|
| 183 | -import Data.Time
|
|
| 184 | 183 | |
| 185 | 184 | import System.IO.Unsafe ( unsafeInterleaveIO )
|
| 186 | 185 | import GHC.Iface.Env ( trace_if )
|
| ... | ... | @@ -707,15 +706,24 @@ checkObjects dflags mb_old_linkable summary = do |
| 707 | 706 | -- Not in dynamic-too mode
|
| 708 | 707 | else k
|
| 709 | 708 | |
| 709 | + -- We check by date first, even though we have the hash
|
|
| 710 | + -- If a compilation is interupted after writing the .hi
|
|
| 711 | + -- but before writing the .o, then we catch this through
|
|
| 712 | + -- modtimes.
|
|
| 713 | + -- If the object file is newer than the .hi file, and the
|
|
| 714 | + -- .hi file is up to date, we also assume the object file
|
|
| 715 | + -- is up to date.
|
|
| 710 | 716 | checkDynamicObj $
|
| 711 | 717 | case (,) <$> mb_obj_date <*> mb_if_date of
|
| 712 | 718 | Just (obj_date, if_date)
|
| 713 | - | obj_date >= if_date ->
|
|
| 719 | + | obj_date >= if_date -> do
|
|
| 720 | + disk_hash <- getFileHash obj_fn
|
|
| 714 | 721 | case mb_old_linkable of
|
| 715 | 722 | Just old_linkable
|
| 716 | - | linkableIsNativeCodeOnly old_linkable, linkableTime old_linkable == obj_date
|
|
| 723 | + | linkableIsNativeCodeOnly old_linkable
|
|
| 724 | + , linkableHash old_linkable == disk_hash
|
|
| 717 | 725 | -> return $ UpToDateItem old_linkable
|
| 718 | - _ -> UpToDateItem <$> findObjectLinkable this_mod obj_fn obj_date
|
|
| 726 | + _ -> return $ UpToDateItem (findObjectLinkable this_mod obj_fn disk_hash)
|
|
| 719 | 727 | _ -> return $ outOfDateItemBecause MissingObjectFile Nothing
|
| 720 | 728 | |
| 721 | 729 | -- | Check to see if we can reuse the old linkable, by this point we will
|
| ... | ... | @@ -724,15 +732,22 @@ checkObjects dflags mb_old_linkable summary = do |
| 724 | 732 | checkByteCodeInMemory :: HscEnv -> ModSummary -> Maybe (LinkableWith ModuleByteCode) -> IO (MaybeValidated (LinkableWith ModuleByteCode))
|
| 725 | 733 | checkByteCodeInMemory hsc_env mod_sum mb_old_linkable =
|
| 726 | 734 | case mb_old_linkable of
|
| 727 | - Just old_linkable
|
|
| 735 | + Just old_linkable -> do
|
|
| 728 | 736 | -- If `-fwrite-byte-code` is enabled, then check that the .gbc file is
|
| 729 | 737 | -- up-to-date with the linkable we have in our hand.
|
| 730 | 738 | -- If ms_bytecode_date is Nothing, then the .gbc file does not exist yet.
|
| 731 | - -- Otherwise, check that the date matches the linkable date exactly.
|
|
| 732 | - | if gopt Opt_WriteByteCode (hsc_dflags hsc_env)
|
|
| 733 | - then maybe False (linkableTime old_linkable ==) (ms_bytecode_date mod_sum)
|
|
| 734 | - else True
|
|
| 735 | - -> return $ (UpToDateItem old_linkable)
|
|
| 739 | + -- Otherwise, check that the hash matches the disk.
|
|
| 740 | + ok <- if gopt Opt_WriteByteCode (hsc_dflags hsc_env)
|
|
| 741 | + then case ms_bytecode_date mod_sum of
|
|
| 742 | + Nothing -> pure False
|
|
| 743 | + Just _ -> do
|
|
| 744 | + disk_hash <- ByteCode.readBinByteCodeHash hsc_env
|
|
| 745 | + (ml_bytecode_file (ms_location mod_sum))
|
|
| 746 | + pure (disk_hash == linkableHash old_linkable)
|
|
| 747 | + else pure True
|
|
| 748 | + if ok
|
|
| 749 | + then return (UpToDateItem old_linkable)
|
|
| 750 | + else return $ outOfDateItemBecause MissingBytecode Nothing
|
|
| 736 | 751 | _ -> return $ outOfDateItemBecause MissingBytecode Nothing
|
| 737 | 752 | |
| 738 | 753 | -- | Load bytecode from a ".gbc" object file if it exists and is up-to-date
|
| ... | ... | @@ -749,7 +764,7 @@ checkByteCodeFromObject hsc_env mod_sum = do |
| 749 | 764 | -- that the one we have on disk would be suitable as well.
|
| 750 | 765 | linkable <- unsafeInterleaveIO $ do
|
| 751 | 766 | bco <- ByteCode.readBinByteCode hsc_env obj_fn
|
| 752 | - return $ mkOnlyModuleByteCodeLinkable obj_date bco
|
|
| 767 | + return $ mkOnlyModuleByteCodeLinkable bco
|
|
| 753 | 768 | return $ UpToDateItem linkable
|
| 754 | 769 | _ -> return $ outOfDateItemBecause MissingBytecode Nothing
|
| 755 | 770 | |
| ... | ... | @@ -759,9 +774,11 @@ checkByteCodeFromIfaceCoreBindings :: HscEnv -> ModIface -> ModSummary -> IO (Ma |
| 759 | 774 | checkByteCodeFromIfaceCoreBindings _hsc_env iface mod_sum = do
|
| 760 | 775 | let
|
| 761 | 776 | this_mod = ms_mod mod_sum
|
| 762 | - if_date = fromJust $ ms_iface_date mod_sum
|
|
| 777 | + -- This hash isn't used, initWholeCoreBindings will replace it
|
|
| 778 | + -- with a real linkable with bytecode that has a hash.
|
|
| 779 | + wcb_hash = panic "linkableHash: WholeCoreBindingsLinkable"
|
|
| 763 | 780 | case iface_core_bindings iface (ms_location mod_sum) of
|
| 764 | - Just fi -> return $ UpToDateItem (Linkable if_date this_mod fi)
|
|
| 781 | + Just fi -> return $ UpToDateItem (Linkable wcb_hash this_mod fi)
|
|
| 765 | 782 | _ -> return $ outOfDateItemBecause MissingBytecode Nothing
|
| 766 | 783 | |
| 767 | 784 | |
| ... | ... | @@ -1621,10 +1638,9 @@ hscCompileCoreExpr' hsc_env srcspan ds_expr = do |
| 1621 | 1638 | Strict.Nothing -- no hpc info
|
| 1622 | 1639 | |
| 1623 | 1640 | {- load it -}
|
| 1624 | - bco_time <- getCurrentTime
|
|
| 1625 | 1641 | mbc <- ByteCode.mkModuleByteCode this_mod bcos []
|
| 1626 | 1642 | (mods_needed, units_needed) <- loadDecls interp hsc_env srcspan $
|
| 1627 | - Linkable bco_time this_mod $ NE.singleton (DotGBC mbc)
|
|
| 1643 | + mkModuleByteCodeLinkable mbc
|
|
| 1628 | 1644 | -- Get the foreign reference to the name we should have just loaded.
|
| 1629 | 1645 | mhvs <- lookupFromLoadedEnv interp (idName binding_id)
|
| 1630 | 1646 | {- Get the HValue for the root -}
|
| ... | ... | @@ -1683,7 +1699,7 @@ jsCodeGen hsc_env srcspan i this_mod stg_binds_with_deps binding_id = do |
| 1683 | 1699 | -- state independently to load new objects here.
|
| 1684 | 1700 | |
| 1685 | 1701 | let objs = mapMaybe linkableFilterNative (ldNeededLinkables deps)
|
| 1686 | - (objs_loaded', _new_objs) = rmDupLinkables (objs_loaded pls) objs
|
|
| 1702 | + (objs_loaded', _new_objs) = rmDupLinkables Nothing (objs_loaded pls) objs
|
|
| 1687 | 1703 | |
| 1688 | 1704 | -- Compute LoadedPkgInfo metadata for recompilation avoidance.
|
| 1689 | 1705 | -- We don't call loadPackages' because the JS interpreter doesn't load
|
| ... | ... | @@ -126,9 +126,11 @@ import qualified Control.Monad.Catch as MC (handle, mask, onException) |
| 126 | 126 | import Data.Maybe
|
| 127 | 127 | import qualified Data.Set as Set
|
| 128 | 128 | import qualified Data.List.NonEmpty as NE
|
| 129 | +import Data.List (sort)
|
|
| 129 | 130 | import Data.List.NonEmpty (NonEmpty(..))
|
| 130 | 131 | |
| 131 | -import Data.Time ( getCurrentTime )
|
|
| 132 | +import GHC.Utils.Fingerprint ( fingerprintFingerprints, getFileHash )
|
|
| 133 | +import GHC.ByteCode.Serialize ( readBytecodeLibInputsHash )
|
|
| 132 | 134 | import GHC.Tc.Utils.Monad (shutdownTcMPluginsIO, FrontendResult (..), tcg_plugins)
|
| 133 | 135 | |
| 134 | 136 | |
| ... | ... | @@ -547,11 +549,13 @@ checkBytecodeLibraryLinkingNeeded _logger dflags unit_env linkables _pkg_deps = |
| 547 | 549 | e_bytecode_lib_time <- modificationTimeIfExists exe_file_os
|
| 548 | 550 | case e_bytecode_lib_time of
|
| 549 | 551 | Nothing -> return $ NeedsRecompile MustCompile
|
| 550 | - Just t -> do
|
|
| 551 | - let bytecode_times = map linkableTime linkables
|
|
| 552 | - if any (t <) bytecode_times
|
|
| 553 | - then return $ needsRecompileBecause ObjectsChanged
|
|
| 554 | - else return UpToDate
|
|
| 552 | + Just _ -> do
|
|
| 553 | + -- See Note [Hash of bytecode libs] in GHC.ByteCode.Serialize
|
|
| 554 | + e_recorded <- tryIO (readBytecodeLibInputsHash exe_file)
|
|
| 555 | + let current = fingerprintFingerprints (sort (map linkableHash linkables))
|
|
| 556 | + case e_recorded of
|
|
| 557 | + Right recorded | recorded == current -> return UpToDate
|
|
| 558 | + _ -> return $ needsRecompileBecause ObjectsChanged
|
|
| 555 | 559 | |
| 556 | 560 | checkNativeLibraryLinkingNeeded :: Bool -> Logger -> DynFlags -> UnitEnv -> [Linkable] -> [UnitId] -> IO RecompileRequired
|
| 557 | 561 | checkNativeLibraryLinkingNeeded staticLink _ dflags unit_env linkables pkg_deps = do
|
| ... | ... | @@ -576,8 +580,9 @@ checkNativeLibraryLinkingNeeded staticLink _ dflags unit_env linkables pkg_deps |
| 576 | 580 | Just t -> do
|
| 577 | 581 | -- first check object files and extra_ld_inputs
|
| 578 | 582 | let extra_ld_inputs = [ f | FileOption _ f <- ldInputs dflags ]
|
| 579 | - (errs,extra_times) <- partitionWithM (tryIO . getModificationUTCTime) extra_ld_inputs
|
|
| 580 | - let obj_times = map linkableTime linkables ++ extra_times
|
|
| 583 | + obj_files = concatMap linkableFiles linkables
|
|
| 584 | + (errs,obj_times) <- partitionWithM (tryIO . getModificationUTCTime)
|
|
| 585 | + (obj_files ++ extra_ld_inputs)
|
|
| 581 | 586 | if not (null errs) || any (t <) obj_times
|
| 582 | 587 | then return $ needsRecompileBecause ObjectsChanged
|
| 583 | 588 | else do
|
| ... | ... | @@ -936,9 +941,9 @@ hscGenBackendPipeline pipe_env hsc_env mod_sum result = do |
| 936 | 941 | -- No object file produced, bytecode or NoBackend
|
| 937 | 942 | Nothing -> return mlinkable
|
| 938 | 943 | Just o_fp -> do
|
| 939 | - part_time <- liftIO getCurrentTime
|
|
| 940 | 944 | final_object <- use (T_MergeForeign pipe_env hsc_env o_fp fos)
|
| 941 | - let !linkable = Linkable part_time (ms_mod mod_sum) (NE.singleton (DotO final_object ModuleObject))
|
|
| 945 | + !obj_hash <- liftIO $ getFileHash final_object
|
|
| 946 | + let !linkable = Linkable obj_hash (ms_mod mod_sum) (NE.singleton (DotO final_object ModuleObject))
|
|
| 942 | 947 | -- Add the object linkable to the potential bytecode linkable which was generated in HscBackend.
|
| 943 | 948 | return (mlinkable { homeMod_object = Just linkable })
|
| 944 | 949 |
| ... | ... | @@ -8,7 +8,8 @@ import GHC.Utils.Error |
| 8 | 8 | import GHC.Driver.Env
|
| 9 | 9 | import GHC.Utils.Outputable
|
| 10 | 10 | import GHC.Linker.Loader
|
| 11 | -import Data.List (partition)
|
|
| 11 | +import Data.List (partition, sort)
|
|
| 12 | +import GHC.Utils.Fingerprint (fingerprintFingerprints)
|
|
| 12 | 13 | import GHC.Driver.Phases (isBytecodeFilename)
|
| 13 | 14 | import GHC.Runtime.Interpreter (interpreterDynamic)
|
| 14 | 15 | import Data.Maybe
|
| ... | ... | @@ -40,8 +41,10 @@ linkBytecodeLib hsc_env gbcs = do |
| 40 | 41 | bytecodeLibFiles = all_cbcs,
|
| 41 | 42 | bytecodeLibForeign = interpreter_foreign_lib
|
| 42 | 43 | }
|
| 44 | + let inputs_hash = fingerprintFingerprints
|
|
| 45 | + (sort [ gbc_hash m | m <- on_disk_bcos ++ gbcs ])
|
|
| 43 | 46 | let output_fn = fromMaybe "a.out" (outputFile dflags)
|
| 44 | - writeBytecodeLib bytecodeLib' output_fn
|
|
| 47 | + writeBytecodeLib inputs_hash bytecodeLib' output_fn
|
|
| 45 | 48 | return ()
|
| 46 | 49 | |
| 47 | 50 |
| ... | ... | @@ -41,11 +41,10 @@ import qualified GHC.Unit.Home.Graph as HUG |
| 41 | 41 | import GHC.Data.Maybe
|
| 42 | 42 | |
| 43 | 43 | import Control.Applicative
|
| 44 | +import Control.Monad (forM_, unless)
|
|
| 44 | 45 | |
| 45 | -import Data.List (isSuffixOf)
|
|
| 46 | +import System.Directory (doesFileExist)
|
|
| 46 | 47 | |
| 47 | -import System.FilePath
|
|
| 48 | -import System.Directory
|
|
| 49 | 48 | |
| 50 | 49 | data LinkDepsOpts = LinkDepsOpts
|
| 51 | 50 | { ldObjSuffix :: !String -- ^ Suffix of .o files
|
| ... | ... | @@ -87,18 +86,18 @@ getLinkDeps opts interp pls span mods = do |
| 87 | 86 | -- the "normal" way, i.e. no non-std ways like profiling or ticky-ticky.
|
| 88 | 87 | -- So here we check the build tag: if we're building a non-standard way
|
| 89 | 88 | -- then we need to find & link object files built the "normal" way.
|
| 90 | - maybe_normal_osuf <- checkNonStdWay opts interp span
|
|
| 89 | + checkNonStdWay opts interp span
|
|
| 91 | 90 | |
| 92 | - get_link_deps opts pls maybe_normal_osuf span mods
|
|
| 91 | + get_link_deps opts interp pls span mods
|
|
| 93 | 92 | |
| 94 | 93 | get_link_deps
|
| 95 | 94 | :: LinkDepsOpts
|
| 95 | + -> Interp
|
|
| 96 | 96 | -> LoaderState
|
| 97 | - -> Maybe FilePath -- replace object suffixes?
|
|
| 98 | 97 | -> SrcSpan
|
| 99 | 98 | -> [Module]
|
| 100 | 99 | -> IO LinkDeps
|
| 101 | -get_link_deps opts pls maybe_normal_osuf span mods = do
|
|
| 100 | +get_link_deps opts interp pls span mods = do
|
|
| 102 | 101 | |
| 103 | 102 | -- Three step process:
|
| 104 | 103 | |
| ... | ... | @@ -122,9 +121,9 @@ get_link_deps opts pls maybe_normal_osuf span mods = do |
| 122 | 121 | -- 3. For each dependent module, find its linkable
|
| 123 | 122 | -- This will either be in the HPT or (in the case of one-shot
|
| 124 | 123 | -- compilation) we may need to use maybe_getFileLinkable
|
| 125 | - lnks_needed <- mapM (get_linkable (ldObjSuffix opts)) mods_needed
|
|
| 124 | + lnks_needed <- mapM get_linkable mods_needed
|
|
| 126 | 125 | let
|
| 127 | - lnks_needed_usages = mkLinkablesUsage lnks_needed
|
|
| 126 | + lnks_needed_usages = mkLinkablesUsage (interpObjSuffix interp) lnks_needed
|
|
| 128 | 127 | new_link_deps lnks = LinkDeps
|
| 129 | 128 | { ldNeededLinkables = lnks_needed
|
| 130 | 129 | , ldAllLinkables = lnks
|
| ... | ... | @@ -156,9 +155,12 @@ get_link_deps opts pls maybe_normal_osuf span mods = do |
| 156 | 155 | then homeModInfoByteCode hmi <|> homeModInfoObject hmi
|
| 157 | 156 | else homeModInfoObject hmi <|> homeModInfoByteCode hmi
|
| 158 | 157 | |
| 159 | - get_linkable osuf mod -- A home-package module
|
|
| 158 | + get_linkable mod -- A home-package module
|
|
| 160 | 159 | = HUG.lookupHugByModule mod (ue_home_unit_graph unit_env) >>= \case
|
| 161 | - Just mod_info -> adjust_linkable (expectJust (homeModLinkable mod_info))
|
|
| 160 | + Just mod_info -> do
|
|
| 161 | + let lnk = expectJust (homeModLinkable mod_info)
|
|
| 162 | + validate_objects lnk
|
|
| 163 | + pure lnk
|
|
| 162 | 164 | Nothing -> do
|
| 163 | 165 | -- It's not in the HPT because we are in one shot mode,
|
| 164 | 166 | -- so use the Finder to get a ModLocation...
|
| ... | ... | @@ -185,30 +187,23 @@ get_link_deps opts pls maybe_normal_osuf span mods = do |
| 185 | 187 | mb_lnk <- findObjectLinkableMaybe mod loc
|
| 186 | 188 | case mb_lnk of
|
| 187 | 189 | Nothing -> no_obj mod
|
| 188 | - Just lnk -> adjust_linkable lnk
|
|
| 190 | + Just lnk -> validate_objects lnk >> pure lnk
|
|
| 189 | 191 | _ -> no_obj (moduleName mod)
|
| 190 | 192 | |
| 191 | - adjust_linkable lnk
|
|
| 192 | - | Just new_osuf <- maybe_normal_osuf = do
|
|
| 193 | - new_parts <- mapM (adjust_part new_osuf)
|
|
| 194 | - (linkableParts lnk)
|
|
| 195 | - return lnk{ linkableParts=new_parts }
|
|
| 196 | - | otherwise =
|
|
| 197 | - return lnk
|
|
| 198 | - |
|
| 199 | - adjust_part new_osuf part = case part of
|
|
| 200 | - DotO file ModuleObject -> do
|
|
| 201 | - massert (osuf `isSuffixOf` file)
|
|
| 202 | - let file_base = fromJust (stripExtension osuf file)
|
|
| 203 | - new_file = file_base <.> new_osuf
|
|
| 204 | - ok <- doesFileExist new_file
|
|
| 205 | - if (not ok)
|
|
| 206 | - then dieWith opts span $
|
|
| 207 | - text "cannot find object file "
|
|
| 208 | - <> quotes (text new_file) $$ while_linking_expr
|
|
| 209 | - else return (DotO new_file ModuleObject)
|
|
| 210 | - DotO file ForeignObject -> pure (DotO file ForeignObject)
|
|
| 211 | - DotGBC {} -> pure part
|
|
| 193 | + -- The loader loads the interpreter's object variant of each
|
|
| 194 | + -- module object. Check it exists here, where we can say which
|
|
| 195 | + -- module and expression needed it.
|
|
| 196 | + validate_objects lnk
|
|
| 197 | + | Just suffixes <- interpObjSuffix interp =
|
|
| 198 | + forM_ (linkableParts lnk) $ \part -> case part of
|
|
| 199 | + DotO f ModuleObject -> do
|
|
| 200 | + let f' = swapObjSuffix suffixes f
|
|
| 201 | + ok <- doesFileExist f'
|
|
| 202 | + unless ok $ dieWith opts span $
|
|
| 203 | + text "cannot find object file" <+> quotes (text f') $$
|
|
| 204 | + while_linking_expr
|
|
| 205 | + _ -> pure ()
|
|
| 206 | + | otherwise = pure ()
|
|
| 212 | 207 | |
| 213 | 208 | |
| 214 | 209 | {-
|
| ... | ... | @@ -241,19 +236,11 @@ dieWith opts span msg = throwProgramError opts (mkLocMessage MCFatal span msg) |
| 241 | 236 | throwProgramError :: LinkDepsOpts -> SDoc -> IO a
|
| 242 | 237 | throwProgramError opts doc = throwGhcExceptionIO (ProgramError (renderWithContext (ldPprOpts opts) doc))
|
| 243 | 238 | |
| 244 | -checkNonStdWay :: LinkDepsOpts -> Interp -> SrcSpan -> IO (Maybe FilePath)
|
|
| 239 | +checkNonStdWay :: LinkDepsOpts -> Interp -> SrcSpan -> IO ()
|
|
| 245 | 240 | checkNonStdWay _opts interp _srcspan
|
| 246 | - -- On some targets (e.g. wasm) the RTS linker only supports loading
|
|
| 247 | - -- dynamic code, in which case we need to ensure the .dyn_o object
|
|
| 248 | - -- is picked (instead of .o which is also present because of
|
|
| 249 | - -- -dynamic-too)
|
|
| 250 | - | ldForceDyn _opts = do
|
|
| 251 | - let target_ways = fullWays $ ldWays _opts
|
|
| 252 | - pure $ if target_ways `hasWay` WayDyn
|
|
| 253 | - then Nothing
|
|
| 254 | - else Just $ waysTag (WayDyn `addWay` target_ways) ++ "_o"
|
|
| 255 | - |
|
| 256 | - | ExternalInterp {} <- interpInstance interp = return Nothing
|
|
| 241 | + | ldForceDyn _opts = return ()
|
|
| 242 | + |
|
| 243 | + | ExternalInterp {} <- interpInstance interp = return ()
|
|
| 257 | 244 | -- with -fexternal-interpreter we load the .o files, whatever way
|
| 258 | 245 | -- they were built. If they were built for a non-std way, then
|
| 259 | 246 | -- we will use the appropriate variant of the iserv binary to load them.
|
| ... | ... | @@ -262,26 +249,23 @@ checkNonStdWay _opts interp _srcspan |
| 262 | 249 | -- complain that they are redundant.
|
| 263 | 250 | #if defined(HAVE_INTERNAL_INTERPRETER)
|
| 264 | 251 | checkNonStdWay opts _interp srcspan
|
| 265 | - | hostFullWays == targetFullWays = return Nothing
|
|
| 252 | + | hostFullWays == targetFullWays = return ()
|
|
| 266 | 253 | -- Only if we are compiling with the same ways as GHC is built
|
| 267 | 254 | -- with, can we dynamically load those object files. (see #3604)
|
| 268 | 255 | |
| 269 | 256 | | ldObjSuffix opts == normalObjectSuffix && not (null targetFullWays)
|
| 270 | 257 | = failNonStd opts srcspan
|
| 271 | 258 | |
| 272 | - | otherwise = return (Just (hostWayTag ++ "o"))
|
|
| 259 | + | otherwise = return ()
|
|
| 273 | 260 | where
|
| 274 | 261 | targetFullWays = fullWays (ldWays opts)
|
| 275 | - hostWayTag = case waysTag hostFullWays of
|
|
| 276 | - "" -> ""
|
|
| 277 | - tag -> tag ++ "_"
|
|
| 278 | 262 | |
| 279 | 263 | normalObjectSuffix :: String
|
| 280 | 264 | normalObjectSuffix = "o"
|
| 281 | 265 | |
| 282 | 266 | data Way' = Normal | Prof | Dyn | ProfDyn
|
| 283 | 267 | |
| 284 | -failNonStd :: LinkDepsOpts -> SrcSpan -> IO (Maybe FilePath)
|
|
| 268 | +failNonStd :: LinkDepsOpts -> SrcSpan -> IO ()
|
|
| 285 | 269 | failNonStd opts srcspan = dieWith opts srcspan $
|
| 286 | 270 | text "Cannot load" <+> pprWay' compWay <+>
|
| 287 | 271 | text "objects when GHC is built" <+> pprWay' ghciWay $$
|
| ... | ... | @@ -44,7 +44,6 @@ where |
| 44 | 44 | import GHC.Prelude
|
| 45 | 45 | |
| 46 | 46 | import GHC.Settings
|
| 47 | -import GHC.Utils.Misc
|
|
| 48 | 47 | |
| 49 | 48 | import GHC.Platform
|
| 50 | 49 | import GHC.Platform.Ways
|
| ... | ... | @@ -673,14 +672,13 @@ findWholeCoreBindings hsc_env mod = do |
| 673 | 672 | |
| 674 | 673 | findBytecodeLinkableMaybe :: HscEnv -> ModLocation -> IO (Maybe Linkable)
|
| 675 | 674 | findBytecodeLinkableMaybe hsc_env locn = do
|
| 676 | - let bytecode_fn = ml_bytecode_file locn
|
|
| 677 | - bytecode_fn_os = ml_bytecode_file_ospath locn
|
|
| 678 | - maybe_bytecode_time <- modificationTimeIfExists bytecode_fn_os
|
|
| 679 | - case maybe_bytecode_time of
|
|
| 680 | - Nothing -> return Nothing
|
|
| 681 | - Just bytecode_time -> do
|
|
| 675 | + let bytecode_fn = ml_bytecode_file locn
|
|
| 676 | + exists <- doesFileExist bytecode_fn
|
|
| 677 | + if not exists
|
|
| 678 | + then return Nothing
|
|
| 679 | + else do
|
|
| 682 | 680 | bco <- readBinByteCode hsc_env bytecode_fn
|
| 683 | - return $ Just $ mkModuleByteCodeLinkable bytecode_time bco
|
|
| 681 | + return $ Just $ mkModuleByteCodeLinkable bco
|
|
| 684 | 682 | |
| 685 | 683 | get_reachable_nodes :: HscEnv -> [Module] -> IO ([Module], UniqDSet UnitId)
|
| 686 | 684 | get_reachable_nodes hsc_env mods
|
| ... | ... | @@ -834,7 +832,7 @@ linkableInSet :: Linkable -> LinkableSet LinkableUsage -> Bool |
| 834 | 832 | linkableInSet l objs_loaded =
|
| 835 | 833 | case lookupModuleEnv objs_loaded (linkableModule l) of
|
| 836 | 834 | Nothing -> False
|
| 837 | - Just m -> linkableTime l == linkableTime m
|
|
| 835 | + Just m -> linkableHash l == linkableHash m
|
|
| 838 | 836 | |
| 839 | 837 | |
| 840 | 838 | {- **********************************************************************
|
| ... | ... | @@ -854,9 +852,9 @@ loadObjects |
| 854 | 852 | -> [Linkable]
|
| 855 | 853 | -> IO (LoaderState, SuccessFlag)
|
| 856 | 854 | loadObjects interp hsc_env pls objs = do
|
| 857 | - let (objs_loaded', new_objs) = rmDupLinkables (objs_loaded pls) objs
|
|
| 855 | + let (objs_loaded', new_objs) = rmDupLinkables (interpObjSuffix interp) (objs_loaded pls) objs
|
|
| 858 | 856 | pls1 = pls { objs_loaded = objs_loaded' }
|
| 859 | - wanted_objs = concatMap linkableObjs new_objs
|
|
| 857 | + wanted_objs <- concat <$> mapM (loadableObjs interp) new_objs
|
|
| 860 | 858 | |
| 861 | 859 | if interpreterDynamic interp
|
| 862 | 860 | then do pls2 <- dynLoadObjs interp hsc_env pls1 wanted_objs
|
| ... | ... | @@ -875,6 +873,18 @@ loadObjects interp hsc_env pls objs = do |
| 875 | 873 | return (pls2, Failed)
|
| 876 | 874 | |
| 877 | 875 | |
| 876 | +loadableObjs :: Interp -> Linkable -> IO [FilePath]
|
|
| 877 | +loadableObjs interp l = concat <$> mapM go (Foldable.toList (linkableParts l))
|
|
| 878 | + where
|
|
| 879 | + go (DotO fn ModuleObject)
|
|
| 880 | + | Just suffixes <- interpObjSuffix interp = do
|
|
| 881 | + let fn' = swapObjSuffix suffixes fn
|
|
| 882 | + ok <- doesFileExist fn'
|
|
| 883 | + if ok
|
|
| 884 | + then return [fn']
|
|
| 885 | + else throwGhcExceptionIO (ProgramError ("cannot find object file " ++ fn'))
|
|
| 886 | + go part = return (linkablePartObjectPaths part)
|
|
| 887 | + |
|
| 878 | 888 | -- | Create a shared library containing the given object files
|
| 879 | 889 | mkDynLoadLib :: HscEnv -> (Ways -> Ways) -> [(FilePath, String)] ->[UnitId] -> [FilePath] -> IO (Maybe (FilePath, FilePath, String))
|
| 880 | 890 | mkDynLoadLib _ _ _ _ [] = return Nothing
|
| ... | ... | @@ -959,17 +969,18 @@ dynLoadObjs interp hsc_env pls objs = do |
| 959 | 969 | then addWay WayProf
|
| 960 | 970 | else id
|
| 961 | 971 | |
| 962 | -rmDupLinkables :: LinkableSet LinkableUsage -- ^ Already loaded
|
|
| 972 | +rmDupLinkables :: Maybe (String, String)
|
|
| 973 | + -> LinkableSet LinkableUsage -- ^ Already loaded
|
|
| 963 | 974 | -> [Linkable] -- ^ New linkables
|
| 964 | 975 | -> (LinkableSet LinkableUsage, -- New loaded set (including new ones)
|
| 965 | 976 | [Linkable]) -- New linkables (excluding dups)
|
| 966 | -rmDupLinkables already ls
|
|
| 977 | +rmDupLinkables mb_osuf already ls
|
|
| 967 | 978 | = go already [] ls
|
| 968 | 979 | where
|
| 969 | 980 | go !already extras [] = (already, extras)
|
| 970 | 981 | go !already extras (l:ls)
|
| 971 | 982 | | linkableInSet l already = go already extras ls
|
| 972 | - | otherwise = go (extendModuleEnv already (linkableModule l) $! mkLinkableUsage l) (l:extras) ls
|
|
| 983 | + | otherwise = go (extendModuleEnv already (linkableModule l) $! mkLinkableUsage mb_osuf l) (l:extras) ls
|
|
| 973 | 984 | |
| 974 | 985 | {- **********************************************************************
|
| 975 | 986 | |
| ... | ... | @@ -981,7 +992,7 @@ rmDupLinkables already ls |
| 981 | 992 | dynLinkBCOs :: Interp -> LoaderState -> KeepModuleLinkableDefinitions -> [Linkable] -> IO LoaderState
|
| 982 | 993 | dynLinkBCOs interp pls keep_spec bcos =
|
| 983 | 994 | |
| 984 | - let (bcos_loaded', new_bcos) = rmDupLinkables (bcos_loaded pls) bcos
|
|
| 995 | + let (bcos_loaded', new_bcos) = rmDupLinkables Nothing (bcos_loaded pls) bcos
|
|
| 985 | 996 | pls1 = pls { bcos_loaded = bcos_loaded' }
|
| 986 | 997 | |
| 987 | 998 | cbcs :: [CompiledByteCode]
|
| ... | ... | @@ -36,8 +36,6 @@ module GHC.Linker.Types |
| 36 | 36 | , LinkedBreaks(..)
|
| 37 | 37 | , emptyLinkedBreaks
|
| 38 | 38 | , LinkableSet
|
| 39 | - , mkLinkableSet
|
|
| 40 | - , unionLinkableSet
|
|
| 41 | 39 | , ObjFile
|
| 42 | 40 | , SptEntry(..)
|
| 43 | 41 | , LibrarySpec(..)
|
| ... | ... | @@ -58,9 +56,8 @@ module GHC.Linker.Types |
| 58 | 56 | , linkableBCOs
|
| 59 | 57 | , linkablePartBCOs
|
| 60 | 58 | , linkableModuleByteCodes
|
| 61 | - , linkableNativeParts
|
|
| 62 | - , linkablePartitionParts
|
|
| 63 | 59 | , linkablePartPath
|
| 60 | + , linkablePartObjectPaths
|
|
| 64 | 61 | , isNativeCode
|
| 65 | 62 | , linkableFilterByteCode
|
| 66 | 63 | , linkableFilterNative
|
| ... | ... | @@ -70,6 +67,7 @@ module GHC.Linker.Types |
| 70 | 67 | , linkableUsageObjs
|
| 71 | 68 | , mkLinkablesUsage
|
| 72 | 69 | , mkLinkableUsage
|
| 70 | + , swapObjSuffix
|
|
| 73 | 71 | |
| 74 | 72 | , ModuleByteCode(..)
|
| 75 | 73 | )
|
| ... | ... | @@ -95,6 +93,7 @@ import GHC.Unit.Module.Deps (LinkablePartUsage (..), linkablePartUsageObjectPath |
| 95 | 93 | import GHC.Unit.Module.Env
|
| 96 | 94 | import GHC.Unit.Module.WholeCoreBindings
|
| 97 | 95 | import GHC.Utils.Misc (seqNonEmpty)
|
| 96 | +import GHC.Utils.Panic (pprPanic)
|
|
| 98 | 97 | |
| 99 | 98 | import GHC.Utils.Outputable
|
| 100 | 99 | |
| ... | ... | @@ -102,10 +101,10 @@ import Control.Applicative ((<|>)) |
| 102 | 101 | import Control.Concurrent.MVar
|
| 103 | 102 | import Data.Array
|
| 104 | 103 | import Data.Functor.Identity
|
| 105 | -import Data.Time ( UTCTime )
|
|
| 106 | 104 | import Data.Maybe (mapMaybe)
|
| 107 | 105 | import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
| 108 | 106 | import qualified Data.List.NonEmpty as NE
|
| 107 | +import System.FilePath (stripExtension, (<.>))
|
|
| 109 | 108 | |
| 110 | 109 | {- **********************************************************************
|
| 111 | 110 | |
| ... | ... | @@ -376,10 +375,10 @@ instance Outputable LoadedPkgInfo where |
| 376 | 375 | |
| 377 | 376 | -- | Information we can use to dynamically link modules into the compiler
|
| 378 | 377 | data LinkableWith parts = Linkable
|
| 379 | - { linkableTime :: !UTCTime
|
|
| 380 | - -- ^ Time at which this linkable was built
|
|
| 381 | - -- (i.e. when the bytecodes were produced,
|
|
| 382 | - -- or the mod date on the files)
|
|
| 378 | + { linkableHash :: Fingerprint
|
|
| 379 | + -- ^ The identity of the linkable, derived from the hash of its contents
|
|
| 380 | + -- Lazy because bytecode is compiled lazily (see loadIfaceByteCodeLazy),
|
|
| 381 | + -- and inspecting the hash can force compiling the bytecode itself.
|
|
| 383 | 382 | |
| 384 | 383 | , linkableModule :: !Module
|
| 385 | 384 | -- ^ The linkable module itself
|
| ... | ... | @@ -396,22 +395,12 @@ type LinkableUsage = LinkableWith (NonEmpty LinkablePartUsage) |
| 396 | 395 | |
| 397 | 396 | type LinkableSet = ModuleEnv
|
| 398 | 397 | |
| 399 | -mkLinkableSet :: [Linkable] -> LinkableSet Linkable
|
|
| 400 | -mkLinkableSet ls = mkModuleEnv [(linkableModule l, l) | l <- ls]
|
|
| 401 | - |
|
| 402 | --- | Union of LinkableSets.
|
|
| 403 | ---
|
|
| 404 | --- In case of conflict, keep the most recent Linkable (as per linkableTime)
|
|
| 405 | -unionLinkableSet :: LinkableSet (LinkableWith a) -> LinkableSet (LinkableWith a) -> LinkableSet (LinkableWith a)
|
|
| 406 | -unionLinkableSet = plusModuleEnv_C go
|
|
| 407 | - where
|
|
| 408 | - go l1 l2
|
|
| 409 | - | linkableTime l1 > linkableTime l2 = l1
|
|
| 410 | - | otherwise = l2
|
|
| 411 | 398 | |
| 412 | 399 | instance Outputable a => Outputable (LinkableWith a) where
|
| 413 | - ppr (Linkable when_made mod parts)
|
|
| 414 | - = (text "Linkable" <+> parens (text (show when_made)) <+> ppr mod)
|
|
| 400 | + -- Don't print the hash, forcing it can trigger compilation
|
|
| 401 | + -- See the comment on 'linkableHash'
|
|
| 402 | + ppr (Linkable _ mod parts)
|
|
| 403 | + = (text "Linkable" <+> ppr mod)
|
|
| 415 | 404 | $$ nest 3 (ppr parts)
|
| 416 | 405 | |
| 417 | 406 | type ObjFile = FilePath
|
| ... | ... | @@ -452,13 +441,13 @@ data ModuleByteCode = ModuleByteCode { gbc_module :: Module |
| 452 | 441 | , gbc_hash :: !Fingerprint
|
| 453 | 442 | }
|
| 454 | 443 | |
| 455 | -mkModuleByteCodeLinkable :: UTCTime -> ModuleByteCode -> Linkable
|
|
| 456 | -mkModuleByteCodeLinkable linkable_time bco = do
|
|
| 457 | - Linkable linkable_time (gbc_module bco) (pure (DotGBC bco))
|
|
| 444 | +mkModuleByteCodeLinkable :: ModuleByteCode -> Linkable
|
|
| 445 | +mkModuleByteCodeLinkable bco =
|
|
| 446 | + Linkable (gbc_hash bco) (gbc_module bco) (pure (DotGBC bco))
|
|
| 458 | 447 | |
| 459 | -mkOnlyModuleByteCodeLinkable :: UTCTime -> ModuleByteCode -> LinkableWith ModuleByteCode
|
|
| 460 | -mkOnlyModuleByteCodeLinkable linkable_time bco = do
|
|
| 461 | - Linkable linkable_time (gbc_module bco) bco
|
|
| 448 | +mkOnlyModuleByteCodeLinkable :: ModuleByteCode -> LinkableWith ModuleByteCode
|
|
| 449 | +mkOnlyModuleByteCodeLinkable bco =
|
|
| 450 | + Linkable (gbc_hash bco) (gbc_module bco) bco
|
|
| 462 | 451 | |
| 463 | 452 | instance Outputable ModuleByteCode where
|
| 464 | 453 | ppr (ModuleByteCode mod _cbc _fos _) = text "ModuleByteCode" <+> ppr mod
|
| ... | ... | @@ -484,21 +473,13 @@ linkableBCOs l = [ gbc_compiled_byte_code gbc | DotGBC gbc <- NE.toList (linkabl |
| 484 | 473 | linkableModuleByteCodes :: Linkable -> [ModuleByteCode]
|
| 485 | 474 | linkableModuleByteCodes l = [ mbc | DotGBC mbc <- NE.toList (linkableParts l) ]
|
| 486 | 475 | |
| 487 | --- | List the native linkable parts (.o) of a linkable
|
|
| 488 | -linkableNativeParts :: Linkable -> [LinkablePart]
|
|
| 489 | -linkableNativeParts l = NE.filter isNativeCode (linkableParts l)
|
|
| 490 | - |
|
| 491 | --- | Split linkable parts into (native code parts, BCOs parts)
|
|
| 492 | -linkablePartitionParts :: Linkable -> ([LinkablePart],[LinkablePart])
|
|
| 493 | -linkablePartitionParts l = NE.partition isNativeCode (linkableParts l)
|
|
| 494 | - |
|
| 495 | 476 | -- | List the native objects (.o) of a linkable
|
| 496 | 477 | linkableObjs :: Linkable -> [FilePath]
|
| 497 | 478 | linkableObjs l = concatMap linkablePartObjectPaths (linkableParts l)
|
| 498 | 479 | |
| 499 | 480 | -- | List the paths of the native objects (.o)
|
| 500 | 481 | linkableFiles :: Linkable -> [FilePath]
|
| 501 | -linkableFiles l = concatMap linkablePartNativePaths (NE.toList (linkableParts l))
|
|
| 482 | +linkableFiles l = mapMaybe linkablePartPath (NE.toList (linkableParts l))
|
|
| 502 | 483 | |
| 503 | 484 | -------------------------------------------
|
| 504 | 485 | |
| ... | ... | @@ -514,13 +495,6 @@ linkablePartPath = \case |
| 514 | 495 | DotO fn _ -> Just fn
|
| 515 | 496 | DotGBC {} -> Nothing
|
| 516 | 497 | |
| 517 | --- | Return the paths of all object code files (.o) contained in this
|
|
| 518 | --- 'LinkablePart'.
|
|
| 519 | -linkablePartNativePaths :: LinkablePart -> [FilePath]
|
|
| 520 | -linkablePartNativePaths = \case
|
|
| 521 | - DotO fn _ -> [fn]
|
|
| 522 | - DotGBC {} -> []
|
|
| 523 | - |
|
| 524 | 498 | -- | Return the paths of all object files (.o) contained in this 'LinkablePart'.
|
| 525 | 499 | linkablePartObjectPaths :: LinkablePart -> [FilePath]
|
| 526 | 500 | linkablePartObjectPaths = \case
|
| ... | ... | @@ -578,8 +552,13 @@ partitionLinkables linkables = |
| 578 | 552 | --
|
| 579 | 553 | -- Each 'LinkablePartUsage' is fully evaluated to avoid retaining any reference
|
| 580 | 554 | -- to the original 'LinkablePart'.
|
| 581 | -mkLinkableUsage :: Linkable -> LinkableUsage
|
|
| 582 | -mkLinkableUsage lnk =
|
|
| 555 | +swapObjSuffix :: (String, String) -> FilePath -> FilePath
|
|
| 556 | +swapObjSuffix (from, to) file = case stripExtension from file of
|
|
| 557 | + Just base -> base <.> to
|
|
| 558 | + Nothing -> pprPanic "swapObjSuffix" (text file <+> text from)
|
|
| 559 | + |
|
| 560 | +mkLinkableUsage :: Maybe (String, String) -> Linkable -> LinkableUsage
|
|
| 561 | +mkLinkableUsage mb_osuf lnk =
|
|
| 583 | 562 | let
|
| 584 | 563 | linkablesWithUsage = NE.map (go (linkableModule lnk)) (linkableParts lnk)
|
| 585 | 564 | lnkUsage = lnk
|
| ... | ... | @@ -589,7 +568,9 @@ mkLinkableUsage lnk = |
| 589 | 568 | seqNonEmpty linkablesWithUsage linkablesWithUsage
|
| 590 | 569 | }
|
| 591 | 570 | in
|
| 592 | - linkableParts lnkUsage `seq` lnkUsage
|
|
| 571 | + -- Also force the hash so that we don't retain the actual bytecode
|
|
| 572 | + -- from a LinkableUsage
|
|
| 573 | + linkableHash lnkUsage `seq` linkableParts lnkUsage `seq` lnkUsage
|
|
| 593 | 574 | where
|
| 594 | 575 | mkFileLinkablePartUsage m fp objs =
|
| 595 | 576 | FileLinkablePartUsage
|
| ... | ... | @@ -609,11 +590,15 @@ mkLinkableUsage lnk = |
| 609 | 590 | |
| 610 | 591 | go :: Module -> LinkablePart -> LinkablePartUsage
|
| 611 | 592 | go m lnkPart = case lnkPart of
|
| 593 | + DotO fn ModuleObject
|
|
| 594 | + | Just suffixes <- mb_osuf
|
|
| 595 | + , let fn' = swapObjSuffix suffixes fn
|
|
| 596 | + -> mkFileLinkablePartUsage m fn' [fn']
|
|
| 612 | 597 | DotO fn _ -> mkFileLinkablePartUsage m fn (linkablePartObjectPaths lnkPart)
|
| 613 | 598 | DotGBC mbc -> mkByteCodeLinkablePartUsage m (gbc_hash mbc) (linkablePartObjectPaths lnkPart)
|
| 614 | 599 | |
| 615 | -mkLinkablesUsage :: [Linkable] -> [LinkableUsage]
|
|
| 616 | -mkLinkablesUsage linkables = map mkLinkableUsage linkables
|
|
| 600 | +mkLinkablesUsage :: Maybe (String, String) -> [Linkable] -> [LinkableUsage]
|
|
| 601 | +mkLinkablesUsage mb_osuf linkables = map (mkLinkableUsage mb_osuf) linkables
|
|
| 617 | 602 | |
| 618 | 603 | linkableUsageObjs :: LinkableUsage -> [FilePath]
|
| 619 | 604 | linkableUsageObjs lnkWithUsage = concatMap linkablePartUsageObjectPaths (linkableParts lnkWithUsage)
|
| ... | ... | @@ -11,6 +11,7 @@ where |
| 11 | 11 | import GHC.Prelude
|
| 12 | 12 | import GHC.Data.FastString.Env
|
| 13 | 13 | import GHC.Driver.DynFlags
|
| 14 | +import GHC.Driver.Session (objectSuf)
|
|
| 14 | 15 | import GHC.Platform
|
| 15 | 16 | import GHC.Platform.Ways
|
| 16 | 17 | import GHC.Settings
|
| ... | ... | @@ -74,6 +75,23 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do |
| 74 | 75 | |
| 75 | 76 | fs_cache <- liftIO $ newMVar emptyFsEnv
|
| 76 | 77 | |
| 78 | +#if defined(HAVE_INTERNAL_INTERPRETER)
|
|
| 79 | + let host_way_tag = case waysTag hostFullWays of
|
|
| 80 | + "" -> ""
|
|
| 81 | + tag -> tag ++ "_"
|
|
| 82 | + internal_obj_suffix
|
|
| 83 | + | hostFullWays == fullWays (interpWays opts) = Nothing
|
|
| 84 | + | otherwise = Just (objectSuf dflags, host_way_tag ++ "o")
|
|
| 85 | +#endif
|
|
| 86 | + |
|
| 87 | +#if !defined(wasm32_HOST_ARCH)
|
|
| 88 | + let target_full_ways = fullWays (interpWays opts)
|
|
| 89 | + wasm_obj_suffix
|
|
| 90 | + | target_full_ways `hasWay` WayDyn = Nothing
|
|
| 91 | + | otherwise =
|
|
| 92 | + Just (objectSuf dflags, waysTag (WayDyn `addWay` target_full_ways) ++ "_o")
|
|
| 93 | +#endif
|
|
| 94 | + |
|
| 77 | 95 | -- see Note [Target code interpreter]
|
| 78 | 96 | if
|
| 79 | 97 | #if !defined(wasm32_HOST_ARCH)
|
| ... | ... | @@ -103,7 +121,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do |
| 103 | 121 | , wasmInterpHsSoSuffix = way_tag ++ dynLibSuffix (interpNameVer opts)
|
| 104 | 122 | , wasmInterpUnitState = ue_homeUnitState unit_env
|
| 105 | 123 | }
|
| 106 | - pure $ Just $ Interp (ExternalInterp $ ExtWasm $ ExtInterpState cfg s) loader lookup_cache fs_cache
|
|
| 124 | + pure $ Just $ Interp (ExternalInterp $ ExtWasm $ ExtInterpState cfg s) loader lookup_cache fs_cache wasm_obj_suffix
|
|
| 107 | 125 | #endif
|
| 108 | 126 | |
| 109 | 127 | -- JavaScript interpreter
|
| ... | ... | @@ -122,7 +140,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do |
| 122 | 140 | , jsInterpFinderOpts = interpFinderOpts opts
|
| 123 | 141 | , jsInterpFinderCache = finder_cache
|
| 124 | 142 | }
|
| 125 | - return (Just (Interp (ExternalInterp (ExtJS (ExtInterpState cfg s))) loader lookup_cache fs_cache))
|
|
| 143 | + return (Just (Interp (ExternalInterp (ExtJS (ExtInterpState cfg s))) loader lookup_cache fs_cache Nothing))
|
|
| 126 | 144 | |
| 127 | 145 | -- external interpreter
|
| 128 | 146 | | interpExternal opts
|
| ... | ... | @@ -149,7 +167,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do |
| 149 | 167 | }
|
| 150 | 168 | s <- liftIO $ newMVar InterpPending
|
| 151 | 169 | loader <- liftIO Loader.uninitializedLoader
|
| 152 | - return (Just (Interp (ExternalInterp (ExtIServ (ExtInterpState conf s))) loader lookup_cache fs_cache))
|
|
| 170 | + return (Just (Interp (ExternalInterp (ExtIServ (ExtInterpState conf s))) loader lookup_cache fs_cache Nothing))
|
|
| 153 | 171 | |
| 154 | 172 | -- Internal interpreter
|
| 155 | 173 | | otherwise
|
| ... | ... | @@ -157,7 +175,7 @@ initInterpreter dflags tmpfs logger platform finder_cache unit_env opts = do |
| 157 | 175 | #if defined(HAVE_INTERNAL_INTERPRETER)
|
| 158 | 176 | do
|
| 159 | 177 | loader <- liftIO Loader.uninitializedLoader
|
| 160 | - return (Just (Interp InternalInterp loader lookup_cache fs_cache))
|
|
| 178 | + return (Just (Interp InternalInterp loader lookup_cache fs_cache internal_obj_suffix))
|
|
| 161 | 179 | #else
|
| 162 | 180 | return Nothing
|
| 163 | 181 | #endif |
| ... | ... | @@ -79,6 +79,10 @@ data Interp = Interp |
| 79 | 79 | |
| 80 | 80 | , interpStringCache :: !(MVar (FastStringEnv (RemotePtr ())))
|
| 81 | 81 | -- ^ MallocStrings cache
|
| 82 | + |
|
| 83 | + , interpObjSuffix :: !(Maybe (String, String))
|
|
| 84 | + -- ^ @(from, to)@ object suffixes to swap when the interpreter cannot
|
|
| 85 | + -- load objects built the target's way
|
|
| 82 | 86 | }
|
| 83 | 87 | |
| 84 | 88 | data InterpInstance
|
| ... | ... | @@ -58,7 +58,6 @@ import GHC.Unit.Finder.Types |
| 58 | 58 | |
| 59 | 59 | import qualified GHC.Data.ShortText as ST
|
| 60 | 60 | |
| 61 | -import GHC.Utils.Misc
|
|
| 62 | 61 | import GHC.Utils.Outputable as Outputable
|
| 63 | 62 | import GHC.Utils.Panic
|
| 64 | 63 | |
| ... | ... | @@ -72,7 +71,6 @@ import GHC.Fingerprint |
| 72 | 71 | import Data.IORef
|
| 73 | 72 | import Control.Applicative ((<|>))
|
| 74 | 73 | import Control.Monad
|
| 75 | -import Data.Time
|
|
| 76 | 74 | import qualified Data.Map as M
|
| 77 | 75 | import GHC.Types.Unique.Map
|
| 78 | 76 | import GHC.Driver.Env
|
| ... | ... | @@ -1004,15 +1002,15 @@ mkStubPaths fopts mod location = do |
| 1004 | 1002 | findObjectLinkableMaybe :: Module -> ModLocation -> IO (Maybe Linkable)
|
| 1005 | 1003 | findObjectLinkableMaybe mod locn
|
| 1006 | 1004 | = do let obj_fn = ml_obj_file locn
|
| 1007 | - maybe_obj_time <- modificationTimeIfExists (ml_obj_file_ospath locn)
|
|
| 1008 | - case maybe_obj_time of
|
|
| 1009 | - Nothing -> return Nothing
|
|
| 1010 | - Just obj_time -> liftM Just (findObjectLinkable mod obj_fn obj_time)
|
|
| 1011 | - |
|
| 1012 | --- Make an object linkable when we know the object file exists, and we know
|
|
| 1013 | --- its modification time.
|
|
| 1014 | -findObjectLinkable :: Module -> FilePath -> UTCTime -> IO Linkable
|
|
| 1015 | -findObjectLinkable mod obj_fn obj_time =
|
|
| 1016 | - pure (Linkable obj_time mod (NE.singleton (DotO obj_fn ModuleObject)))
|
|
| 1005 | + exists <- doesFileExist (ml_obj_file_ospath locn)
|
|
| 1006 | + if not exists
|
|
| 1007 | + then return Nothing
|
|
| 1008 | + else do
|
|
| 1009 | + obj_hash <- getFileHash obj_fn
|
|
| 1010 | + return (Just (findObjectLinkable mod obj_fn obj_hash))
|
|
| 1011 | + |
|
| 1012 | +findObjectLinkable :: Module -> FilePath -> Fingerprint -> Linkable
|
|
| 1013 | +findObjectLinkable mod obj_fn obj_hash =
|
|
| 1014 | + Linkable obj_hash mod (NE.singleton (DotO obj_fn ModuleObject))
|
|
| 1017 | 1015 | -- We used to look for _stub.o files here, but that was a bug (#706)
|
| 1018 | 1016 | -- Now GHC merges the stub.o into the main .o (#3687) |
| 1 | +module M where
|
|
| 2 | + |
|
| 3 | +m :: Int
|
|
| 4 | +m = 5 |
| 1 | +TOP=../../..
|
|
| 2 | +include $(TOP)/mk/boilerplate.mk
|
|
| 3 | +include $(TOP)/mk/test.mk
|
|
| 4 | + |
|
| 5 | +clean:
|
|
| 6 | + |
|
| 7 | +recomp023: clean
|
|
| 8 | + '$(TEST_HC)' $(TEST_HC_OPTS) --make -bytecodelib -fbyte-code \
|
|
| 9 | + -fwrite-interface -fwrite-byte-code -this-unit-id=recomp023 \
|
|
| 10 | + -o recomp023.bytecodelib M.hs
|
|
| 11 | + '$(TEST_HC)' $(TEST_HC_OPTS) --make -bytecodelib -fbyte-code \
|
|
| 12 | + -fwrite-interface -fwrite-byte-code -this-unit-id=recomp023 \
|
|
| 13 | + -o recomp023.bytecodelib M.hs |
| 1 | +test('recomp023', [extra_files(['M.hs']), req_bco, normalise_slashes],
|
|
| 2 | + makefile_test, []) |
| 1 | +[1 of 2] Compiling M ( M.hs, M.gbc )
|
|
| 2 | +[2 of 2] Linking recomp023.bytecodelib |