Cheng Shao deleted branch wip/batch-loaddll at Glasgow Haskell Compiler / GHC
--
You're receiving this email because of your account on gitlab.haskell.org.
1
0
Cheng Shao deleted branch wip/wasm-dyld-pie at Glasgow Haskell Compiler / GHC
--
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 3 commits: gitlab-ci: Run ghcup-metadata jobs on OpenCape runners
by Marge Bot (@marge-bot) 29 Nov '25
by Marge Bot (@marge-bot) 29 Nov '25
29 Nov '25
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
8347aad7 by Ben Gamari at 2025-11-29T08:41:12-05:00
gitlab-ci: Run ghcup-metadata jobs on OpenCape runners
This significantly reduces our egress traffic
and makes the jobs significantly faster.
- - - - -
fb6f9099 by Matthew Pickering at 2025-11-29T08:41:13-05:00
Use 'OsPath' in getModificationTimeIfExists
This part of the compiler is quite hot during recompilation checking in
particular since the filepaths will be translated to a string. It is
better to use the 'OsPath' native function, which turns out to be easy
to do.
- - - - -
87667775 by Georgios Karachalias at 2025-11-29T08:41:21-05:00
Use OsPath in PkgDbRef and UnitDatabase, not FilePath
- - - - -
14 changed files:
- .gitlab-ci.yml
- compiler/GHC/Data/OsPath.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Utils/Misc.hs
- compiler/GHC/Utils/Outputable.hs
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -1184,6 +1184,10 @@ project-version:
image: nixos/nix:2.25.2
dependencies: null
tags:
+ # N.B. we use the OpenCape runners here since this job involves a significant
+ # amount of artifact fetching. This is much more efficient on these runners
+ # as they are near the GitLab box.
+ - opencape
- x86_64-linux
variables:
BUILD_FLAVOUR: default
=====================================
compiler/GHC/Data/OsPath.hs
=====================================
@@ -11,6 +11,15 @@ module GHC.Data.OsPath
-- * Common utility functions
, (</>)
, (<.>)
+ , splitSearchPath
+ , isRelative
+ , dropTrailingPathSeparator
+ , takeDirectory
+ , isSuffixOf
+ , doesDirectoryExist
+ , doesFileExist
+ , getDirectoryContents
+ , createDirectoryIfMissing
)
where
@@ -20,6 +29,8 @@ import GHC.Utils.Misc (HasCallStack)
import GHC.Utils.Panic (panic)
import System.OsPath
+import System.OsString (isSuffixOf)
+import System.Directory.OsPath (doesDirectoryExist, doesFileExist, getDirectoryContents, createDirectoryIfMissing)
import System.Directory.Internal (os)
-- | Decode an 'OsPath' to 'FilePath', throwing an 'error' if decoding failed.
=====================================
compiler/GHC/Driver/Backpack.hs
=====================================
@@ -441,7 +441,7 @@ addUnit u = do
Nothing -> panic "addUnit: called too early"
Just dbs ->
let newdb = UnitDatabase
- { unitDatabasePath = "(in memory " ++ showSDoc dflags0 (ppr (unitId u)) ++ ")"
+ { unitDatabasePath = unsafeEncodeUtf $ "(in memory " ++ showSDoc dflags0 (ppr (unitId u)) ++ ")"
, unitDatabaseUnits = [u]
}
in return (dbs ++ [newdb]) -- added at the end because ordering matters
@@ -789,8 +789,8 @@ summariseRequirement pn mod_name = do
env <- getBkpEnv
src_hash <- liftIO $ getFileHash (bkp_filename env)
- hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file_ospath location)
let loc = srcLocSpan (mkSrcLoc (mkFastString (bkp_filename env)) 1 1)
let fc = hsc_FC hsc_env
@@ -875,8 +875,8 @@ hsModuleToModSummary home_keys pn hsc_src modname
HsSrcFile -> os "hs")
hsc_src
-- This duplicates a pile of logic in GHC.Driver.Make
- hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file_ospath location)
-- Also copied from 'getImports'
let (src_idecls, ord_idecls) = partition ((== IsBoot) . ideclSource . unLoc) imps
=====================================
compiler/GHC/Driver/CodeOutput.hs
=====================================
@@ -38,7 +38,7 @@ import GHC.Driver.LlvmConfigCache (LlvmConfigCache)
import GHC.Driver.Ppr
import GHC.Driver.Backend
-import GHC.Data.OsPath
+import GHC.Data.OsPath qualified as OsPath
import qualified GHC.Data.ShortText as ST
import GHC.Data.Stream ( liftIO )
import qualified GHC.Data.Stream as Stream
@@ -61,8 +61,6 @@ import GHC.Types.ForeignStubs
import GHC.Types.Unique.DSM
import GHC.Types.Unique.Supply ( UniqueTag(..) )
-import System.Directory
-import System.FilePath
import System.IO
import Data.Set (Set)
import qualified Data.Set as Set
@@ -321,10 +319,9 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs
stub_h_file_exists <-
case mkStubPaths (initFinderOpts dflags) (moduleName mod) location of
Nothing -> pure False
- Just path -> do
- let stub_h = unsafeDecodeUtf path
- createDirectoryIfMissing True (takeDirectory stub_h)
- outputForeignStubs_help stub_h stub_h_output_w
+ Just stub_h -> do
+ OsPath.createDirectoryIfMissing True (OsPath.takeDirectory stub_h)
+ outputForeignStubs_help (OsPath.unsafeDecodeUtf stub_h) stub_h_output_w
("#include <HsFFI.h>\n" ++ cplusplus_hdr) cplusplus_ftr
putDumpFileMaybe logger Opt_D_dump_foreign
=====================================
compiler/GHC/Driver/Downsweep.hs
=====================================
@@ -1265,7 +1265,7 @@ checkSummaryHash
| ms_hs_hash old_summary == src_hash &&
not (gopt Opt_ForceRecomp (hsc_dflags hsc_env)) = do
-- update the object-file timestamp
- obj_timestamp <- modificationTimeIfExists (ml_obj_file location)
+ obj_timestamp <- modificationTimeIfExists (ml_obj_file_ospath location)
-- We have to repopulate the Finder's cache for file targets
-- because the file might not even be on the regular search path
@@ -1277,8 +1277,8 @@ checkSummaryHash
hsc_src = ms_hsc_src old_summary
addModuleToFinder fc mod location hsc_src
- hi_timestamp <- modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- modificationTimeIfExists (ml_hie_file_ospath location)
return $ Right
( old_summary
@@ -1482,11 +1482,11 @@ data MakeNewModSummary
makeNewModSummary :: HscEnv -> MakeNewModSummary -> IO ModSummary
makeNewModSummary hsc_env MakeNewModSummary{..} = do
let PreprocessedImports{..} = nms_preimps
- obj_timestamp <- modificationTimeIfExists (ml_obj_file nms_location)
- dyn_obj_timestamp <- modificationTimeIfExists (ml_dyn_obj_file nms_location)
- hi_timestamp <- modificationTimeIfExists (ml_hi_file nms_location)
- hie_timestamp <- modificationTimeIfExists (ml_hie_file nms_location)
- bytecode_timestamp <- modificationTimeIfExists (ml_bytecode_file nms_location)
+ obj_timestamp <- modificationTimeIfExists (ml_obj_file_ospath nms_location)
+ dyn_obj_timestamp <- modificationTimeIfExists (ml_dyn_obj_file_ospath nms_location)
+ hi_timestamp <- modificationTimeIfExists (ml_hi_file_ospath nms_location)
+ hie_timestamp <- modificationTimeIfExists (ml_hie_file_ospath nms_location)
+ bytecode_timestamp <- modificationTimeIfExists (ml_bytecode_file_ospath nms_location)
extra_sig_imports <- findExtraSigImports hsc_env nms_hsc_src pi_mod_name
(implicit_sigs, _inst_deps) <- implicitRequirementsShallow (hscSetActiveUnitId (moduleUnitId nms_mod) hsc_env) pi_theimps
=====================================
compiler/GHC/Driver/DynFlags.hs
=====================================
@@ -101,6 +101,7 @@ import GHC.Core.Unfold
import GHC.Data.Bool
import GHC.Data.EnumSet (EnumSet)
import GHC.Data.Maybe
+import GHC.Data.OsPath ( OsPath )
import GHC.Builtin.Names ( mAIN_NAME )
import GHC.Driver.Backend
import GHC.Driver.Flags
@@ -953,7 +954,7 @@ setDynamicNow dflags0 =
data PkgDbRef
= GlobalPkgDb
| UserPkgDb
- | PkgDbPath FilePath
+ | PkgDbPath OsPath
deriving Eq
=====================================
compiler/GHC/Driver/Main.hs
=====================================
@@ -1091,7 +1091,7 @@ loadIfaceByteCode hsc_env iface location type_env =
linkable $ pure $ DotGBC bco
linkable parts = do
- if_time <- modificationTimeIfExists (ml_hi_file location)
+ if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
time <- maybe getCurrentTime pure if_time
return $! Linkable time (mi_module iface) parts
@@ -1112,7 +1112,7 @@ loadIfaceByteCodeLazy hsc_env iface location type_env =
linkable $ NE.singleton (DotGBC bco)
linkable parts = do
- if_time <- modificationTimeIfExists (ml_hi_file location)
+ if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
time <- maybe getCurrentTime pure if_time
return $!Linkable time (mi_module iface) parts
@@ -2240,7 +2240,7 @@ generateAndWriteByteCodeLinkable hsc_env cgguts mod_location = do
-- Either, get the same time as the .gbc file if it exists, or just the current time.
-- It's important the time of the linkable matches the time of the .gbc file for recompilation
-- checking.
- bco_time <- maybe getCurrentTime pure =<< modificationTimeIfExists (ml_bytecode_file mod_location)
+ bco_time <- maybe getCurrentTime pure =<< modificationTimeIfExists (ml_bytecode_file_ospath mod_location)
return $ mkModuleByteCodeLinkable bco_time bco_object
mkModuleByteCode :: HscEnv -> Module -> ModLocation -> CgInteractiveGuts -> IO ModuleByteCode
=====================================
compiler/GHC/Driver/Pipeline/Execute.hs
=====================================
@@ -730,17 +730,17 @@ runHscPhase pipe_env hsc_env0 input_fn src_flavour = do
-- the object file for one module.)
-- Note the nasty duplication with the same computation in compileFile above
location <- mkOneShotModLocation pipe_env dflags src_flavour mod_name
- let o_file = ml_obj_file location -- The real object file
- hi_file = ml_hi_file location
- hie_file = ml_hie_file location
- dyn_o_file = ml_dyn_obj_file location
+ let o_file = ml_obj_file_ospath location -- The real object file
+ hi_file = ml_hi_file_ospath location
+ hie_file = ml_hie_file_ospath location
+ dyn_o_file = ml_dyn_obj_file_ospath location
src_hash <- getFileHash (basename <.> suff)
hi_date <- modificationTimeIfExists hi_file
hie_date <- modificationTimeIfExists hie_file
o_mod <- modificationTimeIfExists o_file
dyn_o_mod <- modificationTimeIfExists dyn_o_file
- bytecode_date <- modificationTimeIfExists (ml_bytecode_file location)
+ bytecode_date <- modificationTimeIfExists (ml_bytecode_file_ospath location)
-- Tell the finder cache about this module
mod <- do
=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -300,6 +300,8 @@ import qualified Data.Set as Set
import GHC.Types.Unique.Set
import Data.Word
import System.FilePath
+import qualified GHC.Data.OsPath as OsPath
+
import Text.ParserCombinators.ReadP hiding (char)
import Text.ParserCombinators.ReadP as R
@@ -2071,7 +2073,7 @@ package_flags_deps :: [(Deprecation, Flag (CmdLineP DynFlags))]
package_flags_deps = [
------- Packages ----------------------------------------------------
make_ord_flag defFlag "package-db"
- (HasArg (addPkgDbRef . PkgDbPath))
+ (HasArg (addPkgDbRef . PkgDbPath . OsPath.unsafeEncodeUtf))
, make_ord_flag defFlag "clear-package-db" (NoArg clearPkgDb)
, make_ord_flag defFlag "no-global-package-db" (NoArg removeGlobalPkgDb)
, make_ord_flag defFlag "no-user-package-db" (NoArg removeUserPkgDb)
@@ -2081,7 +2083,7 @@ package_flags_deps = [
(NoArg (addPkgDbRef UserPkgDb))
-- backwards compat with GHC<=7.4 :
, make_dep_flag defFlag "package-conf"
- (HasArg $ addPkgDbRef . PkgDbPath) "Use -package-db instead"
+ (HasArg $ addPkgDbRef . PkgDbPath . OsPath.unsafeEncodeUtf) "Use -package-db instead"
, make_dep_flag defFlag "no-user-package-conf"
(NoArg removeUserPkgDb) "Use -no-user-package-db instead"
, make_ord_flag defGhcFlag "package-name" (HasArg $ \name ->
@@ -3307,7 +3309,7 @@ parseEnvFile :: FilePath -> String -> DynP ()
parseEnvFile envfile = mapM_ parseEntry . lines
where
parseEntry str = case words str of
- ("package-db": _) -> addPkgDbRef (PkgDbPath (envdir </> db))
+ ("package-db": _) -> addPkgDbRef (PkgDbPath (OsPath.unsafeEncodeUtf (envdir </> db)))
-- relative package dbs are interpreted relative to the env file
where envdir = takeDirectory envfile
db = drop 11 str
=====================================
compiler/GHC/Linker/Loader.hs
=====================================
@@ -658,8 +658,9 @@ findWholeCoreBindings hsc_env mod = do
findBytecodeLinkableMaybe :: HscEnv -> Module -> ModLocation -> IO (Maybe Linkable)
findBytecodeLinkableMaybe hsc_env mod locn = do
- let bytecode_fn = ml_bytecode_file locn
- maybe_bytecode_time <- modificationTimeIfExists bytecode_fn
+ let bytecode_fn = ml_bytecode_file locn
+ bytecode_fn_os = ml_bytecode_file_ospath locn
+ maybe_bytecode_time <- modificationTimeIfExists bytecode_fn_os
case maybe_bytecode_time of
Nothing -> return Nothing
Just bytecode_time -> do
=====================================
compiler/GHC/Unit/Finder.hs
=====================================
@@ -63,7 +63,6 @@ import GHC.Types.SourceFile
import GHC.Fingerprint
import Data.IORef
-import System.Directory.OsPath
import Control.Applicative ((<|>))
import Control.Monad
import Data.Time
@@ -826,7 +825,7 @@ mkStubPaths fopts mod location = do
findObjectLinkableMaybe :: Module -> ModLocation -> IO (Maybe Linkable)
findObjectLinkableMaybe mod locn
= do let obj_fn = ml_obj_file locn
- maybe_obj_time <- modificationTimeIfExists obj_fn
+ maybe_obj_time <- modificationTimeIfExists (ml_obj_file_ospath locn)
case maybe_obj_time of
Nothing -> return Nothing
Just obj_time -> liftM Just (findObjectLinkable mod obj_fn obj_time)
=====================================
compiler/GHC/Unit/State.hs
=====================================
@@ -101,6 +101,8 @@ import GHC.Data.Maybe
import System.Environment ( getEnv )
import GHC.Data.FastString
+import GHC.Data.OsPath ( OsPath )
+import qualified GHC.Data.OsPath as OsPath
import qualified GHC.Data.ShortText as ST
import GHC.Utils.Logger
import GHC.Utils.Error
@@ -111,7 +113,7 @@ import System.FilePath as FilePath
import Control.Monad
import Data.Graph (stronglyConnComp, SCC(..))
import Data.Char ( toUpper )
-import Data.List ( intersperse, partition, sortBy, isSuffixOf, sortOn )
+import Data.List ( intersperse, partition, sortBy, sortOn )
import Data.Set (Set)
import Data.Monoid (First(..))
import qualified Data.Semigroup as Semigroup
@@ -407,7 +409,7 @@ initUnitConfig dflags cached_dbs home_units =
where
offsetPackageDb :: Maybe FilePath -> PackageDBFlag -> PackageDBFlag
- offsetPackageDb (Just offset) (PackageDB (PkgDbPath p)) | isRelative p = PackageDB (PkgDbPath (offset </> p))
+ offsetPackageDb (Just offset) (PackageDB (PkgDbPath p)) | OsPath.isRelative p = PackageDB (PkgDbPath (OsPath.unsafeEncodeUtf offset OsPath.</> p))
offsetPackageDb _ p = p
@@ -502,12 +504,12 @@ emptyUnitState = UnitState {
-- | Unit database
data UnitDatabase unit = UnitDatabase
- { unitDatabasePath :: FilePath
+ { unitDatabasePath :: OsPath
, unitDatabaseUnits :: [GenUnitInfo unit]
}
instance Outputable u => Outputable (UnitDatabase u) where
- ppr (UnitDatabase fp _u) = text "DB:" <+> text fp
+ ppr (UnitDatabase fp _u) = text "DB:" <+> ppr fp
type UnitInfoMap = UniqMap UnitId UnitInfo
@@ -722,9 +724,9 @@ getUnitDbRefs cfg = do
Left _ -> system_conf_refs
Right path
| Just (xs, x) <- snocView path, isSearchPathSeparator x
- -> map PkgDbPath (splitSearchPath xs) ++ system_conf_refs
+ -> map PkgDbPath (OsPath.splitSearchPath (OsPath.unsafeEncodeUtf xs)) ++ system_conf_refs
| otherwise
- -> map PkgDbPath (splitSearchPath path)
+ -> map PkgDbPath (OsPath.splitSearchPath (OsPath.unsafeEncodeUtf path))
-- Apply the package DB-related flags from the command line to get the
-- final list of package DBs.
@@ -753,24 +755,24 @@ getUnitDbRefs cfg = do
-- NB: This logic is reimplemented in Cabal, so if you change it,
-- make sure you update Cabal. (Or, better yet, dump it in the
-- compiler info so Cabal can use the info.)
-resolveUnitDatabase :: UnitConfig -> PkgDbRef -> IO (Maybe FilePath)
-resolveUnitDatabase cfg GlobalPkgDb = return $ Just (unitConfigGlobalDB cfg)
+resolveUnitDatabase :: UnitConfig -> PkgDbRef -> IO (Maybe OsPath)
+resolveUnitDatabase cfg GlobalPkgDb = return $ Just $ OsPath.unsafeEncodeUtf $ unitConfigGlobalDB cfg
resolveUnitDatabase cfg UserPkgDb = runMaybeT $ do
dir <- versionedAppDir (unitConfigProgramName cfg) (unitConfigPlatformArchOS cfg)
let pkgconf = dir </> unitConfigDBName cfg
exist <- tryMaybeT $ doesDirectoryExist pkgconf
- if exist then return pkgconf else mzero
+ if exist then return (OsPath.unsafeEncodeUtf pkgconf) else mzero
resolveUnitDatabase _ (PkgDbPath name) = return $ Just name
-readUnitDatabase :: Logger -> UnitConfig -> FilePath -> IO (UnitDatabase UnitId)
+readUnitDatabase :: Logger -> UnitConfig -> OsPath -> IO (UnitDatabase UnitId)
readUnitDatabase logger cfg conf_file = do
- isdir <- doesDirectoryExist conf_file
+ isdir <- OsPath.doesDirectoryExist conf_file
proto_pkg_configs <-
if isdir
then readDirStyleUnitInfo conf_file
else do
- isfile <- doesFileExist conf_file
+ isfile <- OsPath.doesFileExist conf_file
if isfile
then do
mpkgs <- tryReadOldFileStyleUnitInfo
@@ -778,48 +780,49 @@ readUnitDatabase logger cfg conf_file = do
Just pkgs -> return pkgs
Nothing -> throwGhcExceptionIO $ InstallationError $
"ghc no longer supports single-file style package " ++
- "databases (" ++ conf_file ++
+ "databases (" ++ show conf_file ++
") use 'ghc-pkg init' to create the database with " ++
"the correct format."
else throwGhcExceptionIO $ InstallationError $
- "can't find a package database at " ++ conf_file
+ "can't find a package database at " ++ show conf_file
let
-- Fix #16360: remove trailing slash from conf_file before calculating pkgroot
- conf_file' = dropTrailingPathSeparator conf_file
- top_dir = unitConfigGHCDir cfg
- pkgroot = takeDirectory conf_file'
+ conf_file' = OsPath.dropTrailingPathSeparator conf_file
+ top_dir = OsPath.unsafeEncodeUtf (unitConfigGHCDir cfg)
+ pkgroot = OsPath.takeDirectory conf_file'
pkg_configs1 = map (mungeUnitInfo top_dir pkgroot . mapUnitInfo (\(UnitKey x) -> UnitId x) . mkUnitKeyInfo)
proto_pkg_configs
--
return $ UnitDatabase conf_file' pkg_configs1
where
+ readDirStyleUnitInfo :: OsPath -> IO [DbUnitInfo]
readDirStyleUnitInfo conf_dir = do
- let filename = conf_dir </> "package.cache"
- cache_exists <- doesFileExist filename
+ let filename = conf_dir OsPath.</> (OsPath.unsafeEncodeUtf "package.cache")
+ cache_exists <- OsPath.doesFileExist filename
if cache_exists
then do
- debugTraceMsg logger 2 $ text "Using binary package database:" <+> text filename
- readPackageDbForGhc filename
+ debugTraceMsg logger 2 $ text "Using binary package database:" <+> ppr filename
+ readPackageDbForGhc (OsPath.unsafeDecodeUtf filename)
else do
-- If there is no package.cache file, we check if the database is not
-- empty by inspecting if the directory contains any .conf file. If it
-- does, something is wrong and we fail. Otherwise we assume that the
-- database is empty.
debugTraceMsg logger 2 $ text "There is no package.cache in"
- <+> text conf_dir
+ <+> ppr conf_dir
<> text ", checking if the database is empty"
- db_empty <- all (not . isSuffixOf ".conf")
- <$> getDirectoryContents conf_dir
+ db_empty <- all (not . OsPath.isSuffixOf (OsPath.unsafeEncodeUtf ".conf"))
+ <$> OsPath.getDirectoryContents conf_dir
if db_empty
then do
debugTraceMsg logger 3 $ text "There are no .conf files in"
- <+> text conf_dir <> text ", treating"
+ <+> ppr conf_dir <> text ", treating"
<+> text "package database as empty"
return []
else
throwGhcExceptionIO $ InstallationError $
- "there is no package.cache in " ++ conf_dir ++
+ "there is no package.cache in " ++ show conf_dir ++
" even though package database is not empty"
@@ -832,13 +835,13 @@ readUnitDatabase logger cfg conf_file = do
-- assumes it's a file and tries to overwrite with 'writeFile'.
-- ghc-pkg also cooperates with this workaround.
tryReadOldFileStyleUnitInfo = do
- content <- readFile conf_file `catchIO` \_ -> return ""
+ content <- readFile (OsPath.unsafeDecodeUtf conf_file) `catchIO` \_ -> return ""
if take 2 content == "[]"
then do
- let conf_dir = conf_file <.> "d"
- direxists <- doesDirectoryExist conf_dir
+ let conf_dir = conf_file OsPath.<.> OsPath.unsafeEncodeUtf "d"
+ direxists <- OsPath.doesDirectoryExist conf_dir
if direxists
- then do debugTraceMsg logger 2 (text "Ignoring old file-style db and trying:" <+> text conf_dir)
+ then do debugTraceMsg logger 2 (text "Ignoring old file-style db and trying:" <+> ppr conf_dir)
liftM Just (readDirStyleUnitInfo conf_dir)
else return (Just []) -- ghc-pkg will create it when it's updated
else return Nothing
@@ -848,11 +851,11 @@ distrustAllUnits pkgs = map distrust pkgs
where
distrust pkg = pkg{ unitIsTrusted = False }
-mungeUnitInfo :: FilePath -> FilePath
+mungeUnitInfo :: OsPath -> OsPath
-> UnitInfo -> UnitInfo
mungeUnitInfo top_dir pkgroot =
mungeDynLibFields
- . mungeUnitInfoPaths (ST.pack top_dir) (ST.pack pkgroot)
+ . mungeUnitInfoPaths (ST.pack (OsPath.unsafeDecodeUtf top_dir)) (ST.pack (OsPath.unsafeDecodeUtf pkgroot))
mungeDynLibFields :: UnitInfo -> UnitInfo
mungeDynLibFields pkg =
@@ -1373,7 +1376,7 @@ mergeDatabases logger = foldM merge (emptyUniqMap, emptyUniqMap) . zip [1..]
where
merge (pkg_map, prec_map) (i, UnitDatabase db_path db) = do
debugTraceMsg logger 2 $
- text "loading package database" <+> text db_path
+ text "loading package database" <+> ppr db_path
forM_ (Set.toList override_set) $ \pkg ->
debugTraceMsg logger 2 $
text "package" <+> ppr pkg <+>
=====================================
compiler/GHC/Utils/Misc.hs
=====================================
@@ -137,6 +137,8 @@ import Control.Monad ( guard )
import Control.Monad.IO.Class ( MonadIO, liftIO )
import System.IO.Error as IO ( isDoesNotExistError )
import System.Directory ( doesDirectoryExist, getModificationTime, renameFile )
+import qualified System.Directory.OsPath as OsPath
+import System.OsPath (OsPath)
import System.FilePath
import Data.Bifunctor ( first, second )
@@ -1248,9 +1250,9 @@ getModificationUTCTime = getModificationTime
-- --------------------------------------------------------------
-- check existence & modification time at the same time
-modificationTimeIfExists :: FilePath -> IO (Maybe UTCTime)
+modificationTimeIfExists :: OsPath -> IO (Maybe UTCTime)
modificationTimeIfExists f =
- (do t <- getModificationUTCTime f; return (Just t))
+ (do t <- OsPath.getModificationTime f; return (Just t))
`catchIO` \e -> if isDoesNotExistError e
then return Nothing
else ioError e
=====================================
compiler/GHC/Utils/Outputable.hs
=====================================
@@ -149,6 +149,7 @@ import Data.String
import Data.Word
import System.IO ( Handle )
import System.FilePath
+import System.OsPath (OsPath, decodeUtf)
import Text.Printf
import Numeric (showFFloat)
import Numeric.Natural (Natural)
@@ -1101,6 +1102,8 @@ instance Outputable Extension where
instance Outputable ModuleName where
ppr = pprModuleName
+instance Outputable OsPath where
+ ppr p = text $ either show id (decodeUtf p)
pprModuleName :: IsLine doc => ModuleName -> doc
pprModuleName (ModuleName nm) =
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/80858c56b5a61321ff4ca4ffb397fa…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/80858c56b5a61321ff4ca4ffb397fa…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/D4759] rts: Make LOOKS_LIKE_INFO_PTR a bit more strict
by Cheng Shao (@TerrorJack) 29 Nov '25
by Cheng Shao (@TerrorJack) 29 Nov '25
29 Nov '25
Cheng Shao pushed to branch wip/D4759 at Glasgow Haskell Compiler / GHC
Commits:
90715f57 by Ben Gamari at 2025-11-29T03:13:27+01:00
rts: Make LOOKS_LIKE_INFO_PTR a bit more strict
In particular, we now verify that the info table doesn't reside in the
dynamic heap.
Test Plan: Validate
Previously Differential Revision https://phabricator.haskell.org/D4759
- - - - -
1 changed file:
- rts/include/rts/storage/ClosureMacros.h
Changes:
=====================================
rts/include/rts/storage/ClosureMacros.h
=====================================
@@ -323,7 +323,7 @@ EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR_NOT_NULL (StgWord p);
EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR_NOT_NULL (StgWord p)
{
StgInfoTable *info = INFO_PTR_TO_STRUCT((StgInfoTable *)p);
- return info->type != INVALID_OBJECT && info->type < N_CLOSURE_TYPES;
+ return info->type != INVALID_OBJECT && info->type < N_CLOSURE_TYPES && !HEAP_ALLOCED(p);
}
EXTERN_INLINE bool LOOKS_LIKE_INFO_PTR (StgWord p);
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/90715f579c18e2e38854af52b971a05…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/90715f579c18e2e38854af52b971a05…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 5 commits: Fix PIC jump tables on Windows (#24016)
by Marge Bot (@marge-bot) 28 Nov '25
by Marge Bot (@marge-bot) 28 Nov '25
28 Nov '25
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
84a087d5 by Sylvain Henry at 2025-11-28T17:35:28-05:00
Fix PIC jump tables on Windows (#24016)
Avoid overflows in jump tables by using a base label closer to the jump
targets. See added Note [Jump tables]
- - - - -
82db7042 by Zubin Duggal at 2025-11-28T17:36:10-05:00
rts/linker/PEi386: Copy strings before they are inserted into LoadedDllCache. The original strings are temporary and might be freed at an arbitrary point.
Fixes #26613
- - - - -
b419a523 by Ben Gamari at 2025-11-28T18:08:55-05:00
gitlab-ci: Run ghcup-metadata jobs on OpenCape runners
This significantly reduces our egress traffic
and makes the jobs significantly faster.
- - - - -
5e0e1b15 by Matthew Pickering at 2025-11-28T18:08:56-05:00
Use 'OsPath' in getModificationTimeIfExists
This part of the compiler is quite hot during recompilation checking in
particular since the filepaths will be translated to a string. It is
better to use the 'OsPath' native function, which turns out to be easy
to do.
- - - - -
80858c56 by Georgios Karachalias at 2025-11-28T18:09:03-05:00
Use OsPath in PkgDbRef and UnitDatabase, not FilePath
- - - - -
22 changed files:
- .gitlab-ci.yml
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- compiler/GHC/Data/OsPath.hs
- compiler/GHC/Driver/Backpack.hs
- compiler/GHC/Driver/CodeOutput.hs
- compiler/GHC/Driver/Downsweep.hs
- compiler/GHC/Driver/DynFlags.hs
- compiler/GHC/Driver/Main.hs
- compiler/GHC/Driver/Pipeline/Execute.hs
- compiler/GHC/Driver/Session.hs
- compiler/GHC/Linker/Loader.hs
- compiler/GHC/Unit/Finder.hs
- compiler/GHC/Unit/State.hs
- compiler/GHC/Utils/Misc.hs
- compiler/GHC/Utils/Outputable.hs
- docs/users_guide/phases.rst
- rts/linker/PEi386.c
- + testsuite/tests/codeGen/should_run/T24016.hs
- + testsuite/tests/codeGen/should_run/T24016.stdout
- testsuite/tests/codeGen/should_run/all.T
Changes:
=====================================
.gitlab-ci.yml
=====================================
@@ -1184,6 +1184,10 @@ project-version:
image: nixos/nix:2.25.2
dependencies: null
tags:
+ # N.B. we use the OpenCape runners here since this job involves a significant
+ # amount of artifact fetching. This is much more efficient on these runners
+ # as they are near the GitLab box.
+ - opencape
- x86_64-linux
variables:
BUILD_FLAVOUR: default
=====================================
compiler/GHC/CmmToAsm/X86/CodeGen.hs
=====================================
@@ -376,7 +376,7 @@ stmtToInstrs bid stmt = do
--We try to arrange blocks such that the likely branch is the fallthrough
--in GHC.Cmm.ContFlowOpt. So we can assume the condition is likely false here.
CmmCondBranch arg true false _ -> genCondBranch bid true false arg
- CmmSwitch arg ids -> genSwitch arg ids
+ CmmSwitch arg ids -> genSwitch arg ids bid
CmmCall { cml_target = arg
, cml_args_regs = gregs } -> genJump arg (jumpRegs platform gregs)
_ ->
@@ -489,13 +489,6 @@ is32BitInteger i = i64 <= 0x7fffffff && i64 >= -0x80000000
where i64 = fromIntegral i :: Int64
--- | Convert a BlockId to some CmmStatic data
-jumpTableEntry :: NCGConfig -> Maybe BlockId -> CmmStatic
-jumpTableEntry config Nothing = CmmStaticLit (CmmInt 0 (ncgWordWidth config))
-jumpTableEntry _ (Just blockid) = CmmStaticLit (CmmLabel blockLabel)
- where blockLabel = blockLbl blockid
-
-
-- -----------------------------------------------------------------------------
-- General things for putting together code sequences
@@ -5375,11 +5368,52 @@ index (1),
indexExpr = UU_Conv(indexOffset); // == 1::I64
See #21186.
--}
-genSwitch :: CmmExpr -> SwitchTargets -> NatM InstrBlock
+Note [Jump tables]
+~~~~~~~~~~~~~~~~~~
+The x86 backend has a virtual JMP_TBL instruction which payload can be used to
+generate both the jump instruction and the jump table contents. `genSwitch` is
+responsible for generating these JMP_TBL instructions.
+
+Depending on `-fPIC` flag and on the architecture, we generate the following
+jump table variants:
+
+ | Variant | Arch | Table's contents | Reference to the table |
+ |---------|--------|----------------------------------------|------------------------|
+ | PIC | Both | Relative offset: target_lbl - base_lbl | PIC |
+ | Non-PIC | 64-bit | Absolute: target_lbl | Non-PIC (rip-relative) |
+ | Non-PIC | 32-bit | Absolute: target_lbl | Non-PIC (absolute) |
+
+For the PIC variant, we store relative entries (`target_lbl - base_lbl`) in the
+jump table. Using absolute entries with PIC would require target_lbl symbols to
+be resolved at link time, hence to be global labels (currently they are local
+labels).
+
+We use the block_id of the code containing the jump as `base_lbl`. It ensures
+that target_lbl and base_lbl are close enough to each others, avoiding
+overflows.
+
+Historical note: in the past we used the table label `table_lbl` as base_lbl. It
+allowed the jumping code to only compute one global address (table_lbl) both to
+read the table and to compute the target address. However:
-genSwitch expr targets = do
+ * the table could be too far from the jump and on Windows which only
+ has 32-bit relative relocations (IMAGE_REL_AMD64_REL64 doesn't exist),
+ `dest_lbl - table_lbl` overflowed (see #24016)
+
+ * Mac OS X/x86-64 linker was unable to handle `.quad L1 - L0`
+ relocations if L0 wasn't preceded by a non-anonymous label in its
+ section (which was the case with table_lbl). Hence we used to put the
+ jump table in the .text section in this case.
+
+
+-}
+
+-- | Generate a JMP_TBL instruction
+--
+-- See Note [Jump tables]
+genSwitch :: CmmExpr -> SwitchTargets -> BlockId -> NatM InstrBlock
+genSwitch expr targets bid = do
config <- getConfig
let platform = ncgPlatform config
expr_w = cmmExprWidth platform expr
@@ -5390,79 +5424,76 @@ genSwitch expr targets = do
indexExpr = CmmMachOp
(MO_UU_Conv expr_w (platformWordWidth platform))
[indexExpr0]
- if ncgPIC config
- then do
- (reg,e_code) <- getNonClobberedReg indexExpr
- -- getNonClobberedReg because it needs to survive across t_code
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- os = platformOS platform
- -- Might want to use .rodata.<function we're in> instead, but as
- -- long as it's something unique it'll work out since the
- -- references to the jump table are in the appropriate section.
- rosection = case os of
- -- on Mac OS X/x86_64, put the jump table in the text section to
- -- work around a limitation of the linker.
- -- ld64 is unable to handle the relocations for
- -- .quad L1 - L0
- -- if L0 is not preceded by a non-anonymous label in its section.
- OSDarwin | not is32bit -> Section Text lbl
- _ -> Section ReadOnlyData lbl
- dynRef <- cmmMakeDynamicReference config DataReference lbl
- (tableReg,t_code) <- getSomeReg $ dynRef
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg)
- (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
-
- return $ e_code `appOL` t_code `appOL` toOL [
- ADD (intFormat (platformWordWidth platform)) op (OpReg tableReg),
- JMP_TBL (OpReg tableReg) ids rosection lbl
- ]
- else do
- (reg,e_code) <- getSomeReg indexExpr
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- if is32bit
- then let op = OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl lbl))
- jmp_code = JMP_TBL op ids (Section ReadOnlyData lbl) lbl
- in return $ e_code `appOL` unitOL jmp_code
- else do
+
+ (offset, blockIds) = switchTargetsToTable targets
+ ids = map (fmap DestBlockId) blockIds
+
+ is32bit = target32Bit platform
+ fmt = archWordFormat is32bit
+
+ table_lbl <- getNewLabelNat
+ let bid_lbl = blockLbl bid
+ let table_section = Section ReadOnlyData table_lbl
+
+ -- see Note [Jump tables] for a description of the following 3 variants.
+ if
+ | ncgPIC config -> do
+ -- PIC support: store relative offsets in the jump table to allow the code
+ -- to be relocated without updating the table. The table itself and the
+ -- block label used to make the relative labels absolute are read in a PIC
+ -- way (via cmmMakeDynamicReference).
+ (reg,e_code) <- getNonClobberedReg indexExpr -- getNonClobberedReg because it needs to survive across t_code and j_code
+ (tableReg,t_code) <- getNonClobberedReg =<< cmmMakeDynamicReference config DataReference table_lbl
+ (targetReg,j_code) <- getSomeReg =<< cmmMakeDynamicReference config DataReference bid_lbl
+ pure $ e_code `appOL` t_code `appOL` j_code `appOL` toOL
+ [ ADD fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl (Just bid_lbl)
+ ]
+
+ | not is32bit -> do
+ -- 64-bit non-PIC code
+ (reg,e_code) <- getSomeReg indexExpr
+ tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ pure $ e_code `appOL` toOL
-- See Note [%rip-relative addressing on x86-64].
- tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
- targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
- fmt = archWordFormat is32bit
- code = e_code `appOL` toOL
- [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl lbl))) (OpReg tableReg)
- , MOV fmt op (OpReg targetReg)
- , JMP_TBL (OpReg targetReg) ids (Section ReadOnlyData lbl) lbl
- ]
- return code
- where
- (offset, blockIds) = switchTargetsToTable targets
- ids = map (fmap DestBlockId) blockIds
+ [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl table_lbl))) (OpReg tableReg)
+ , MOV fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl Nothing
+ ]
+
+ | otherwise -> do
+ -- 32-bit non-PIC code is a straightforward jump to &table[entry].
+ (reg,e_code) <- getSomeReg indexExpr
+ pure $ e_code `appOL` unitOL
+ ( JMP_TBL (OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl table_lbl)))
+ ids table_section table_lbl Nothing
+ )
generateJumpTableForInstr :: NCGConfig -> Instr -> Maybe (NatCmmDecl (Alignment, RawCmmStatics) Instr)
-generateJumpTableForInstr config (JMP_TBL _ ids section lbl)
- = let getBlockId (DestBlockId id) = id
- getBlockId _ = panic "Non-Label target in Jump Table"
- blockIds = map (fmap getBlockId) ids
- in Just (createJumpTable config blockIds section lbl)
-generateJumpTableForInstr _ _ = Nothing
-
-createJumpTable :: NCGConfig -> [Maybe BlockId] -> Section -> CLabel
- -> GenCmmDecl (Alignment, RawCmmStatics) h g
-createJumpTable config ids section lbl
- = let jumpTable
- | ncgPIC config =
- let ww = ncgWordWidth config
- jumpTableEntryRel Nothing
- = CmmStaticLit (CmmInt 0 ww)
- jumpTableEntryRel (Just blockid)
- = CmmStaticLit (CmmLabelDiffOff blockLabel lbl 0 ww)
- where blockLabel = blockLbl blockid
- in map jumpTableEntryRel ids
- | otherwise = map (jumpTableEntry config) ids
- in CmmData section (mkAlignment 1, CmmStaticsRaw lbl jumpTable)
+generateJumpTableForInstr config = \case
+ JMP_TBL _ ids section table_lbl mrel_lbl ->
+ let getBlockId (DestBlockId id) = id
+ getBlockId _ = panic "Non-Label target in Jump Table"
+ block_ids = map (fmap getBlockId) ids
+
+ jumpTable = case mrel_lbl of
+ Nothing -> map mk_absolute block_ids -- absolute entries
+ Just rel_lbl -> map (mk_relative rel_lbl) block_ids -- offsets relative to rel_lbl
+
+ mk_absolute = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabel (blockLbl blockid))
+
+ mk_relative rel_lbl = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabelDiffOff (blockLbl blockid) rel_lbl 0 (ncgWordWidth config))
+
+ in Just (CmmData section (mkAlignment 1, CmmStaticsRaw table_lbl jumpTable))
+
+ _ -> Nothing
extractUnwindPoints :: [Instr] -> [UnwindPoint]
extractUnwindPoints instrs =
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -252,6 +252,7 @@ data Instr
[Maybe JumpDest] -- Targets of the jump table
Section -- Data section jump table should be put in
CLabel -- Label of jump table
+ !(Maybe CLabel) -- Label used to compute relative offsets. Otherwise we store absolute addresses.
-- | X86 call instruction
| CALL (Either Imm Reg) -- ^ Jump target
[RegWithFormat] -- ^ Arguments (required for register allocation)
@@ -486,7 +487,7 @@ regUsageOfInstr platform instr
JXX _ _ -> mkRU [] []
JXX_GBL _ _ -> mkRU [] []
JMP op regs -> mkRU (use_R addrFmt op regs) []
- JMP_TBL op _ _ _ -> mkRU (use_R addrFmt op []) []
+ JMP_TBL op _ _ _ _ -> mkRU (use_R addrFmt op []) []
CALL (Left _) params -> mkRU params (map mkFmt $ callClobberedRegs platform)
CALL (Right reg) params -> mkRU (mk addrFmt reg:params) (map mkFmt $ callClobberedRegs platform)
CLTD fmt -> mkRU [mk fmt eax] [mk fmt edx]
@@ -812,7 +813,7 @@ patchRegsOfInstr platform instr env
POP fmt op -> patch1 (POP fmt) op
SETCC cond op -> patch1 (SETCC cond) op
JMP op regs -> JMP (patchOp op) regs
- JMP_TBL op ids s lbl -> JMP_TBL (patchOp op) ids s lbl
+ JMP_TBL op ids s tl jl -> JMP_TBL (patchOp op) ids s tl jl
FMA3 fmt perm var x1 x2 x3 -> patch3 (FMA3 fmt perm var) x1 x2 x3
@@ -1016,9 +1017,9 @@ isJumpishInstr instr
canFallthroughTo :: Instr -> BlockId -> Bool
canFallthroughTo insn bid
= case insn of
- JXX _ target -> bid == target
- JMP_TBL _ targets _ _ -> all isTargetBid targets
- _ -> False
+ JXX _ target -> bid == target
+ JMP_TBL _ targets _ _ _ -> all isTargetBid targets
+ _ -> False
where
isTargetBid target = case target of
Nothing -> True
@@ -1031,9 +1032,9 @@ jumpDestsOfInstr
jumpDestsOfInstr insn
= case insn of
- JXX _ id -> [id]
- JMP_TBL _ ids _ _ -> [id | Just (DestBlockId id) <- ids]
- _ -> []
+ JXX _ id -> [id]
+ JMP_TBL _ ids _ _ _ -> [id | Just (DestBlockId id) <- ids]
+ _ -> []
patchJumpInstr
@@ -1042,8 +1043,8 @@ patchJumpInstr
patchJumpInstr insn patchF
= case insn of
JXX cc id -> JXX cc (patchF id)
- JMP_TBL op ids section lbl
- -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section lbl
+ JMP_TBL op ids section table_lbl rel_lbl
+ -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section table_lbl rel_lbl
_ -> insn
where
patchJumpDest f (DestBlockId id) = DestBlockId (f id)
@@ -1504,14 +1505,14 @@ shortcutJump fn insn = shortcutJump' fn (setEmpty :: LabelSet) insn
Just (DestBlockId id') -> shortcutJump' fn seen' (JXX cc id')
Just (DestImm imm) -> shortcutJump' fn seen' (JXX_GBL cc imm)
where seen' = setInsert id seen
- shortcutJump' fn _ (JMP_TBL addr blocks section tblId) =
+ shortcutJump' fn _ (JMP_TBL addr blocks section table_lbl rel_lbl) =
let updateBlock (Just (DestBlockId bid)) =
case fn bid of
Nothing -> Just (DestBlockId bid )
Just dest -> Just dest
updateBlock dest = dest
blocks' = map updateBlock blocks
- in JMP_TBL addr blocks' section tblId
+ in JMP_TBL addr blocks' section table_lbl rel_lbl
shortcutJump' _ _ other = other
-- Here because it knows about JumpDest
=====================================
compiler/GHC/CmmToAsm/X86/Ppr.hs
=====================================
@@ -895,7 +895,7 @@ pprInstr platform i = case i of
JMP op _
-> line $ text "\tjmp *" <> pprOperand platform (archWordFormat (target32Bit platform)) op
- JMP_TBL op _ _ _
+ JMP_TBL op _ _ _ _
-> pprInstr platform (JMP op [])
CALL (Left imm) _
=====================================
compiler/GHC/Data/OsPath.hs
=====================================
@@ -11,6 +11,15 @@ module GHC.Data.OsPath
-- * Common utility functions
, (</>)
, (<.>)
+ , splitSearchPath
+ , isRelative
+ , dropTrailingPathSeparator
+ , takeDirectory
+ , isSuffixOf
+ , doesDirectoryExist
+ , doesFileExist
+ , getDirectoryContents
+ , createDirectoryIfMissing
)
where
@@ -20,6 +29,8 @@ import GHC.Utils.Misc (HasCallStack)
import GHC.Utils.Panic (panic)
import System.OsPath
+import System.OsString (isSuffixOf)
+import System.Directory.OsPath (doesDirectoryExist, doesFileExist, getDirectoryContents, createDirectoryIfMissing)
import System.Directory.Internal (os)
-- | Decode an 'OsPath' to 'FilePath', throwing an 'error' if decoding failed.
=====================================
compiler/GHC/Driver/Backpack.hs
=====================================
@@ -441,7 +441,7 @@ addUnit u = do
Nothing -> panic "addUnit: called too early"
Just dbs ->
let newdb = UnitDatabase
- { unitDatabasePath = "(in memory " ++ showSDoc dflags0 (ppr (unitId u)) ++ ")"
+ { unitDatabasePath = unsafeEncodeUtf $ "(in memory " ++ showSDoc dflags0 (ppr (unitId u)) ++ ")"
, unitDatabaseUnits = [u]
}
in return (dbs ++ [newdb]) -- added at the end because ordering matters
@@ -789,8 +789,8 @@ summariseRequirement pn mod_name = do
env <- getBkpEnv
src_hash <- liftIO $ getFileHash (bkp_filename env)
- hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file_ospath location)
let loc = srcLocSpan (mkSrcLoc (mkFastString (bkp_filename env)) 1 1)
let fc = hsc_FC hsc_env
@@ -875,8 +875,8 @@ hsModuleToModSummary home_keys pn hsc_src modname
HsSrcFile -> os "hs")
hsc_src
-- This duplicates a pile of logic in GHC.Driver.Make
- hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file_ospath location)
-- Also copied from 'getImports'
let (src_idecls, ord_idecls) = partition ((== IsBoot) . ideclSource . unLoc) imps
=====================================
compiler/GHC/Driver/CodeOutput.hs
=====================================
@@ -38,7 +38,7 @@ import GHC.Driver.LlvmConfigCache (LlvmConfigCache)
import GHC.Driver.Ppr
import GHC.Driver.Backend
-import GHC.Data.OsPath
+import GHC.Data.OsPath qualified as OsPath
import qualified GHC.Data.ShortText as ST
import GHC.Data.Stream ( liftIO )
import qualified GHC.Data.Stream as Stream
@@ -61,8 +61,6 @@ import GHC.Types.ForeignStubs
import GHC.Types.Unique.DSM
import GHC.Types.Unique.Supply ( UniqueTag(..) )
-import System.Directory
-import System.FilePath
import System.IO
import Data.Set (Set)
import qualified Data.Set as Set
@@ -321,10 +319,9 @@ outputForeignStubs logger tmpfs dflags unit_state mod location stubs
stub_h_file_exists <-
case mkStubPaths (initFinderOpts dflags) (moduleName mod) location of
Nothing -> pure False
- Just path -> do
- let stub_h = unsafeDecodeUtf path
- createDirectoryIfMissing True (takeDirectory stub_h)
- outputForeignStubs_help stub_h stub_h_output_w
+ Just stub_h -> do
+ OsPath.createDirectoryIfMissing True (OsPath.takeDirectory stub_h)
+ outputForeignStubs_help (OsPath.unsafeDecodeUtf stub_h) stub_h_output_w
("#include <HsFFI.h>\n" ++ cplusplus_hdr) cplusplus_ftr
putDumpFileMaybe logger Opt_D_dump_foreign
=====================================
compiler/GHC/Driver/Downsweep.hs
=====================================
@@ -1265,7 +1265,7 @@ checkSummaryHash
| ms_hs_hash old_summary == src_hash &&
not (gopt Opt_ForceRecomp (hsc_dflags hsc_env)) = do
-- update the object-file timestamp
- obj_timestamp <- modificationTimeIfExists (ml_obj_file location)
+ obj_timestamp <- modificationTimeIfExists (ml_obj_file_ospath location)
-- We have to repopulate the Finder's cache for file targets
-- because the file might not even be on the regular search path
@@ -1277,8 +1277,8 @@ checkSummaryHash
hsc_src = ms_hsc_src old_summary
addModuleToFinder fc mod location hsc_src
- hi_timestamp <- modificationTimeIfExists (ml_hi_file location)
- hie_timestamp <- modificationTimeIfExists (ml_hie_file location)
+ hi_timestamp <- modificationTimeIfExists (ml_hi_file_ospath location)
+ hie_timestamp <- modificationTimeIfExists (ml_hie_file_ospath location)
return $ Right
( old_summary
@@ -1482,11 +1482,11 @@ data MakeNewModSummary
makeNewModSummary :: HscEnv -> MakeNewModSummary -> IO ModSummary
makeNewModSummary hsc_env MakeNewModSummary{..} = do
let PreprocessedImports{..} = nms_preimps
- obj_timestamp <- modificationTimeIfExists (ml_obj_file nms_location)
- dyn_obj_timestamp <- modificationTimeIfExists (ml_dyn_obj_file nms_location)
- hi_timestamp <- modificationTimeIfExists (ml_hi_file nms_location)
- hie_timestamp <- modificationTimeIfExists (ml_hie_file nms_location)
- bytecode_timestamp <- modificationTimeIfExists (ml_bytecode_file nms_location)
+ obj_timestamp <- modificationTimeIfExists (ml_obj_file_ospath nms_location)
+ dyn_obj_timestamp <- modificationTimeIfExists (ml_dyn_obj_file_ospath nms_location)
+ hi_timestamp <- modificationTimeIfExists (ml_hi_file_ospath nms_location)
+ hie_timestamp <- modificationTimeIfExists (ml_hie_file_ospath nms_location)
+ bytecode_timestamp <- modificationTimeIfExists (ml_bytecode_file_ospath nms_location)
extra_sig_imports <- findExtraSigImports hsc_env nms_hsc_src pi_mod_name
(implicit_sigs, _inst_deps) <- implicitRequirementsShallow (hscSetActiveUnitId (moduleUnitId nms_mod) hsc_env) pi_theimps
=====================================
compiler/GHC/Driver/DynFlags.hs
=====================================
@@ -101,6 +101,7 @@ import GHC.Core.Unfold
import GHC.Data.Bool
import GHC.Data.EnumSet (EnumSet)
import GHC.Data.Maybe
+import GHC.Data.OsPath ( OsPath )
import GHC.Builtin.Names ( mAIN_NAME )
import GHC.Driver.Backend
import GHC.Driver.Flags
@@ -953,7 +954,7 @@ setDynamicNow dflags0 =
data PkgDbRef
= GlobalPkgDb
| UserPkgDb
- | PkgDbPath FilePath
+ | PkgDbPath OsPath
deriving Eq
=====================================
compiler/GHC/Driver/Main.hs
=====================================
@@ -1091,7 +1091,7 @@ loadIfaceByteCode hsc_env iface location type_env =
linkable $ pure $ DotGBC bco
linkable parts = do
- if_time <- modificationTimeIfExists (ml_hi_file location)
+ if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
time <- maybe getCurrentTime pure if_time
return $! Linkable time (mi_module iface) parts
@@ -1112,7 +1112,7 @@ loadIfaceByteCodeLazy hsc_env iface location type_env =
linkable $ NE.singleton (DotGBC bco)
linkable parts = do
- if_time <- modificationTimeIfExists (ml_hi_file location)
+ if_time <- modificationTimeIfExists (ml_hi_file_ospath location)
time <- maybe getCurrentTime pure if_time
return $!Linkable time (mi_module iface) parts
@@ -2240,7 +2240,7 @@ generateAndWriteByteCodeLinkable hsc_env cgguts mod_location = do
-- Either, get the same time as the .gbc file if it exists, or just the current time.
-- It's important the time of the linkable matches the time of the .gbc file for recompilation
-- checking.
- bco_time <- maybe getCurrentTime pure =<< modificationTimeIfExists (ml_bytecode_file mod_location)
+ bco_time <- maybe getCurrentTime pure =<< modificationTimeIfExists (ml_bytecode_file_ospath mod_location)
return $ mkModuleByteCodeLinkable bco_time bco_object
mkModuleByteCode :: HscEnv -> Module -> ModLocation -> CgInteractiveGuts -> IO ModuleByteCode
=====================================
compiler/GHC/Driver/Pipeline/Execute.hs
=====================================
@@ -730,17 +730,17 @@ runHscPhase pipe_env hsc_env0 input_fn src_flavour = do
-- the object file for one module.)
-- Note the nasty duplication with the same computation in compileFile above
location <- mkOneShotModLocation pipe_env dflags src_flavour mod_name
- let o_file = ml_obj_file location -- The real object file
- hi_file = ml_hi_file location
- hie_file = ml_hie_file location
- dyn_o_file = ml_dyn_obj_file location
+ let o_file = ml_obj_file_ospath location -- The real object file
+ hi_file = ml_hi_file_ospath location
+ hie_file = ml_hie_file_ospath location
+ dyn_o_file = ml_dyn_obj_file_ospath location
src_hash <- getFileHash (basename <.> suff)
hi_date <- modificationTimeIfExists hi_file
hie_date <- modificationTimeIfExists hie_file
o_mod <- modificationTimeIfExists o_file
dyn_o_mod <- modificationTimeIfExists dyn_o_file
- bytecode_date <- modificationTimeIfExists (ml_bytecode_file location)
+ bytecode_date <- modificationTimeIfExists (ml_bytecode_file_ospath location)
-- Tell the finder cache about this module
mod <- do
=====================================
compiler/GHC/Driver/Session.hs
=====================================
@@ -300,6 +300,8 @@ import qualified Data.Set as Set
import GHC.Types.Unique.Set
import Data.Word
import System.FilePath
+import qualified GHC.Data.OsPath as OsPath
+
import Text.ParserCombinators.ReadP hiding (char)
import Text.ParserCombinators.ReadP as R
@@ -2071,7 +2073,7 @@ package_flags_deps :: [(Deprecation, Flag (CmdLineP DynFlags))]
package_flags_deps = [
------- Packages ----------------------------------------------------
make_ord_flag defFlag "package-db"
- (HasArg (addPkgDbRef . PkgDbPath))
+ (HasArg (addPkgDbRef . PkgDbPath . OsPath.unsafeEncodeUtf))
, make_ord_flag defFlag "clear-package-db" (NoArg clearPkgDb)
, make_ord_flag defFlag "no-global-package-db" (NoArg removeGlobalPkgDb)
, make_ord_flag defFlag "no-user-package-db" (NoArg removeUserPkgDb)
@@ -2081,7 +2083,7 @@ package_flags_deps = [
(NoArg (addPkgDbRef UserPkgDb))
-- backwards compat with GHC<=7.4 :
, make_dep_flag defFlag "package-conf"
- (HasArg $ addPkgDbRef . PkgDbPath) "Use -package-db instead"
+ (HasArg $ addPkgDbRef . PkgDbPath . OsPath.unsafeEncodeUtf) "Use -package-db instead"
, make_dep_flag defFlag "no-user-package-conf"
(NoArg removeUserPkgDb) "Use -no-user-package-db instead"
, make_ord_flag defGhcFlag "package-name" (HasArg $ \name ->
@@ -3307,7 +3309,7 @@ parseEnvFile :: FilePath -> String -> DynP ()
parseEnvFile envfile = mapM_ parseEntry . lines
where
parseEntry str = case words str of
- ("package-db": _) -> addPkgDbRef (PkgDbPath (envdir </> db))
+ ("package-db": _) -> addPkgDbRef (PkgDbPath (OsPath.unsafeEncodeUtf (envdir </> db)))
-- relative package dbs are interpreted relative to the env file
where envdir = takeDirectory envfile
db = drop 11 str
=====================================
compiler/GHC/Linker/Loader.hs
=====================================
@@ -658,8 +658,9 @@ findWholeCoreBindings hsc_env mod = do
findBytecodeLinkableMaybe :: HscEnv -> Module -> ModLocation -> IO (Maybe Linkable)
findBytecodeLinkableMaybe hsc_env mod locn = do
- let bytecode_fn = ml_bytecode_file locn
- maybe_bytecode_time <- modificationTimeIfExists bytecode_fn
+ let bytecode_fn = ml_bytecode_file locn
+ bytecode_fn_os = ml_bytecode_file_ospath locn
+ maybe_bytecode_time <- modificationTimeIfExists bytecode_fn_os
case maybe_bytecode_time of
Nothing -> return Nothing
Just bytecode_time -> do
=====================================
compiler/GHC/Unit/Finder.hs
=====================================
@@ -63,7 +63,6 @@ import GHC.Types.SourceFile
import GHC.Fingerprint
import Data.IORef
-import System.Directory.OsPath
import Control.Applicative ((<|>))
import Control.Monad
import Data.Time
@@ -826,7 +825,7 @@ mkStubPaths fopts mod location = do
findObjectLinkableMaybe :: Module -> ModLocation -> IO (Maybe Linkable)
findObjectLinkableMaybe mod locn
= do let obj_fn = ml_obj_file locn
- maybe_obj_time <- modificationTimeIfExists obj_fn
+ maybe_obj_time <- modificationTimeIfExists (ml_obj_file_ospath locn)
case maybe_obj_time of
Nothing -> return Nothing
Just obj_time -> liftM Just (findObjectLinkable mod obj_fn obj_time)
=====================================
compiler/GHC/Unit/State.hs
=====================================
@@ -101,6 +101,8 @@ import GHC.Data.Maybe
import System.Environment ( getEnv )
import GHC.Data.FastString
+import GHC.Data.OsPath ( OsPath )
+import qualified GHC.Data.OsPath as OsPath
import qualified GHC.Data.ShortText as ST
import GHC.Utils.Logger
import GHC.Utils.Error
@@ -111,7 +113,7 @@ import System.FilePath as FilePath
import Control.Monad
import Data.Graph (stronglyConnComp, SCC(..))
import Data.Char ( toUpper )
-import Data.List ( intersperse, partition, sortBy, isSuffixOf, sortOn )
+import Data.List ( intersperse, partition, sortBy, sortOn )
import Data.Set (Set)
import Data.Monoid (First(..))
import qualified Data.Semigroup as Semigroup
@@ -407,7 +409,7 @@ initUnitConfig dflags cached_dbs home_units =
where
offsetPackageDb :: Maybe FilePath -> PackageDBFlag -> PackageDBFlag
- offsetPackageDb (Just offset) (PackageDB (PkgDbPath p)) | isRelative p = PackageDB (PkgDbPath (offset </> p))
+ offsetPackageDb (Just offset) (PackageDB (PkgDbPath p)) | OsPath.isRelative p = PackageDB (PkgDbPath (OsPath.unsafeEncodeUtf offset OsPath.</> p))
offsetPackageDb _ p = p
@@ -502,12 +504,12 @@ emptyUnitState = UnitState {
-- | Unit database
data UnitDatabase unit = UnitDatabase
- { unitDatabasePath :: FilePath
+ { unitDatabasePath :: OsPath
, unitDatabaseUnits :: [GenUnitInfo unit]
}
instance Outputable u => Outputable (UnitDatabase u) where
- ppr (UnitDatabase fp _u) = text "DB:" <+> text fp
+ ppr (UnitDatabase fp _u) = text "DB:" <+> ppr fp
type UnitInfoMap = UniqMap UnitId UnitInfo
@@ -722,9 +724,9 @@ getUnitDbRefs cfg = do
Left _ -> system_conf_refs
Right path
| Just (xs, x) <- snocView path, isSearchPathSeparator x
- -> map PkgDbPath (splitSearchPath xs) ++ system_conf_refs
+ -> map PkgDbPath (OsPath.splitSearchPath (OsPath.unsafeEncodeUtf xs)) ++ system_conf_refs
| otherwise
- -> map PkgDbPath (splitSearchPath path)
+ -> map PkgDbPath (OsPath.splitSearchPath (OsPath.unsafeEncodeUtf path))
-- Apply the package DB-related flags from the command line to get the
-- final list of package DBs.
@@ -753,24 +755,24 @@ getUnitDbRefs cfg = do
-- NB: This logic is reimplemented in Cabal, so if you change it,
-- make sure you update Cabal. (Or, better yet, dump it in the
-- compiler info so Cabal can use the info.)
-resolveUnitDatabase :: UnitConfig -> PkgDbRef -> IO (Maybe FilePath)
-resolveUnitDatabase cfg GlobalPkgDb = return $ Just (unitConfigGlobalDB cfg)
+resolveUnitDatabase :: UnitConfig -> PkgDbRef -> IO (Maybe OsPath)
+resolveUnitDatabase cfg GlobalPkgDb = return $ Just $ OsPath.unsafeEncodeUtf $ unitConfigGlobalDB cfg
resolveUnitDatabase cfg UserPkgDb = runMaybeT $ do
dir <- versionedAppDir (unitConfigProgramName cfg) (unitConfigPlatformArchOS cfg)
let pkgconf = dir </> unitConfigDBName cfg
exist <- tryMaybeT $ doesDirectoryExist pkgconf
- if exist then return pkgconf else mzero
+ if exist then return (OsPath.unsafeEncodeUtf pkgconf) else mzero
resolveUnitDatabase _ (PkgDbPath name) = return $ Just name
-readUnitDatabase :: Logger -> UnitConfig -> FilePath -> IO (UnitDatabase UnitId)
+readUnitDatabase :: Logger -> UnitConfig -> OsPath -> IO (UnitDatabase UnitId)
readUnitDatabase logger cfg conf_file = do
- isdir <- doesDirectoryExist conf_file
+ isdir <- OsPath.doesDirectoryExist conf_file
proto_pkg_configs <-
if isdir
then readDirStyleUnitInfo conf_file
else do
- isfile <- doesFileExist conf_file
+ isfile <- OsPath.doesFileExist conf_file
if isfile
then do
mpkgs <- tryReadOldFileStyleUnitInfo
@@ -778,48 +780,49 @@ readUnitDatabase logger cfg conf_file = do
Just pkgs -> return pkgs
Nothing -> throwGhcExceptionIO $ InstallationError $
"ghc no longer supports single-file style package " ++
- "databases (" ++ conf_file ++
+ "databases (" ++ show conf_file ++
") use 'ghc-pkg init' to create the database with " ++
"the correct format."
else throwGhcExceptionIO $ InstallationError $
- "can't find a package database at " ++ conf_file
+ "can't find a package database at " ++ show conf_file
let
-- Fix #16360: remove trailing slash from conf_file before calculating pkgroot
- conf_file' = dropTrailingPathSeparator conf_file
- top_dir = unitConfigGHCDir cfg
- pkgroot = takeDirectory conf_file'
+ conf_file' = OsPath.dropTrailingPathSeparator conf_file
+ top_dir = OsPath.unsafeEncodeUtf (unitConfigGHCDir cfg)
+ pkgroot = OsPath.takeDirectory conf_file'
pkg_configs1 = map (mungeUnitInfo top_dir pkgroot . mapUnitInfo (\(UnitKey x) -> UnitId x) . mkUnitKeyInfo)
proto_pkg_configs
--
return $ UnitDatabase conf_file' pkg_configs1
where
+ readDirStyleUnitInfo :: OsPath -> IO [DbUnitInfo]
readDirStyleUnitInfo conf_dir = do
- let filename = conf_dir </> "package.cache"
- cache_exists <- doesFileExist filename
+ let filename = conf_dir OsPath.</> (OsPath.unsafeEncodeUtf "package.cache")
+ cache_exists <- OsPath.doesFileExist filename
if cache_exists
then do
- debugTraceMsg logger 2 $ text "Using binary package database:" <+> text filename
- readPackageDbForGhc filename
+ debugTraceMsg logger 2 $ text "Using binary package database:" <+> ppr filename
+ readPackageDbForGhc (OsPath.unsafeDecodeUtf filename)
else do
-- If there is no package.cache file, we check if the database is not
-- empty by inspecting if the directory contains any .conf file. If it
-- does, something is wrong and we fail. Otherwise we assume that the
-- database is empty.
debugTraceMsg logger 2 $ text "There is no package.cache in"
- <+> text conf_dir
+ <+> ppr conf_dir
<> text ", checking if the database is empty"
- db_empty <- all (not . isSuffixOf ".conf")
- <$> getDirectoryContents conf_dir
+ db_empty <- all (not . OsPath.isSuffixOf (OsPath.unsafeEncodeUtf ".conf"))
+ <$> OsPath.getDirectoryContents conf_dir
if db_empty
then do
debugTraceMsg logger 3 $ text "There are no .conf files in"
- <+> text conf_dir <> text ", treating"
+ <+> ppr conf_dir <> text ", treating"
<+> text "package database as empty"
return []
else
throwGhcExceptionIO $ InstallationError $
- "there is no package.cache in " ++ conf_dir ++
+ "there is no package.cache in " ++ show conf_dir ++
" even though package database is not empty"
@@ -832,13 +835,13 @@ readUnitDatabase logger cfg conf_file = do
-- assumes it's a file and tries to overwrite with 'writeFile'.
-- ghc-pkg also cooperates with this workaround.
tryReadOldFileStyleUnitInfo = do
- content <- readFile conf_file `catchIO` \_ -> return ""
+ content <- readFile (OsPath.unsafeDecodeUtf conf_file) `catchIO` \_ -> return ""
if take 2 content == "[]"
then do
- let conf_dir = conf_file <.> "d"
- direxists <- doesDirectoryExist conf_dir
+ let conf_dir = conf_file OsPath.<.> OsPath.unsafeEncodeUtf "d"
+ direxists <- OsPath.doesDirectoryExist conf_dir
if direxists
- then do debugTraceMsg logger 2 (text "Ignoring old file-style db and trying:" <+> text conf_dir)
+ then do debugTraceMsg logger 2 (text "Ignoring old file-style db and trying:" <+> ppr conf_dir)
liftM Just (readDirStyleUnitInfo conf_dir)
else return (Just []) -- ghc-pkg will create it when it's updated
else return Nothing
@@ -848,11 +851,11 @@ distrustAllUnits pkgs = map distrust pkgs
where
distrust pkg = pkg{ unitIsTrusted = False }
-mungeUnitInfo :: FilePath -> FilePath
+mungeUnitInfo :: OsPath -> OsPath
-> UnitInfo -> UnitInfo
mungeUnitInfo top_dir pkgroot =
mungeDynLibFields
- . mungeUnitInfoPaths (ST.pack top_dir) (ST.pack pkgroot)
+ . mungeUnitInfoPaths (ST.pack (OsPath.unsafeDecodeUtf top_dir)) (ST.pack (OsPath.unsafeDecodeUtf pkgroot))
mungeDynLibFields :: UnitInfo -> UnitInfo
mungeDynLibFields pkg =
@@ -1373,7 +1376,7 @@ mergeDatabases logger = foldM merge (emptyUniqMap, emptyUniqMap) . zip [1..]
where
merge (pkg_map, prec_map) (i, UnitDatabase db_path db) = do
debugTraceMsg logger 2 $
- text "loading package database" <+> text db_path
+ text "loading package database" <+> ppr db_path
forM_ (Set.toList override_set) $ \pkg ->
debugTraceMsg logger 2 $
text "package" <+> ppr pkg <+>
=====================================
compiler/GHC/Utils/Misc.hs
=====================================
@@ -137,6 +137,8 @@ import Control.Monad ( guard )
import Control.Monad.IO.Class ( MonadIO, liftIO )
import System.IO.Error as IO ( isDoesNotExistError )
import System.Directory ( doesDirectoryExist, getModificationTime, renameFile )
+import qualified System.Directory.OsPath as OsPath
+import System.OsPath (OsPath)
import System.FilePath
import Data.Bifunctor ( first, second )
@@ -1248,9 +1250,9 @@ getModificationUTCTime = getModificationTime
-- --------------------------------------------------------------
-- check existence & modification time at the same time
-modificationTimeIfExists :: FilePath -> IO (Maybe UTCTime)
+modificationTimeIfExists :: OsPath -> IO (Maybe UTCTime)
modificationTimeIfExists f =
- (do t <- getModificationUTCTime f; return (Just t))
+ (do t <- OsPath.getModificationTime f; return (Just t))
`catchIO` \e -> if isDoesNotExistError e
then return Nothing
else ioError e
=====================================
compiler/GHC/Utils/Outputable.hs
=====================================
@@ -149,6 +149,7 @@ import Data.String
import Data.Word
import System.IO ( Handle )
import System.FilePath
+import System.OsPath (OsPath, decodeUtf)
import Text.Printf
import Numeric (showFFloat)
import Numeric.Natural (Natural)
@@ -1101,6 +1102,8 @@ instance Outputable Extension where
instance Outputable ModuleName where
ppr = pprModuleName
+instance Outputable OsPath where
+ ppr p = text $ either show id (decodeUtf p)
pprModuleName :: IsLine doc => ModuleName -> doc
pprModuleName (ModuleName nm) =
=====================================
docs/users_guide/phases.rst
=====================================
@@ -770,10 +770,9 @@ Options affecting code generation
:type: dynamic
:category: codegen
- Generate position-independent code (code that can be put into shared
- libraries). This currently works on Linux x86 and x86-64. On
- Windows, position-independent code is never used so the flag is a
- no-op on that platform.
+ Generate position-independent code (PIC). This code can be put into shared
+ libraries and is sometimes required by operating systems, e.g. systems using
+ Address Space Layout Randomization (ASLR).
.. ghc-flag:: -fexternal-dynamic-refs
:shortdesc: Generate code for linking against dynamic libraries
@@ -790,9 +789,7 @@ Options affecting code generation
:category: codegen
Generate code in such a way to be linkable into a position-independent
- executable This currently works on Linux x86 and x86-64. On Windows,
- position-independent code is never used so the flag is a no-op on that
- platform. To link the final executable use :ghc-flag:`-pie`.
+ executable. To link the final executable use :ghc-flag:`-pie`.
.. ghc-flag:: -dynamic
:shortdesc: Build dynamically-linked object files and executables
=====================================
rts/linker/PEi386.c
=====================================
@@ -552,7 +552,12 @@ static int compare_path(StgWord key1, StgWord key2)
static void addLoadedDll(LoadedDllCache *cache, const pathchar *dll_name, HINSTANCE instance)
{
- insertHashTable_(cache->hash, (StgWord) dll_name, instance, hash_path);
+ // dll_name might be deallocated, we need to copy it to have a stable reference to the contents
+ // See #26613
+ size_t size = wcslen(dll_name) + 1;
+ pathchar* dll_name_copy = stgMallocBytes(size * sizeof(pathchar), "addLoadedDll");
+ wcsncpy(dll_name_copy, dll_name, size);
+ insertHashTable_(cache->hash, (StgWord) dll_name_copy, instance, hash_path);
}
static HINSTANCE isDllLoaded(const LoadedDllCache *cache, const pathchar *dll_name)
=====================================
testsuite/tests/codeGen/should_run/T24016.hs
=====================================
@@ -0,0 +1,24 @@
+module Main (main) where
+
+data Command
+ = Command1
+ | Command2
+ | Command3
+ | Command4
+ | Command5
+ | Command6 -- Commenting this line works with -fPIC, uncommenting leads to a crash.
+
+main :: IO ()
+main = do
+ let x = case cmd of
+ Command1 -> 1 :: Int
+ Command2 -> 2
+ Command3 -> 3
+ Command4 -> 4
+ Command5 -> 5
+ Command6 -> 6
+ putStrLn (show x)
+
+{-# NOINLINE cmd #-}
+cmd :: Command
+cmd = Command6
=====================================
testsuite/tests/codeGen/should_run/T24016.stdout
=====================================
@@ -0,0 +1 @@
+6
=====================================
testsuite/tests/codeGen/should_run/all.T
=====================================
@@ -257,3 +257,4 @@ test('CCallConv', [req_c], compile_and_run, ['CCallConv_c.c'])
test('T25364', normal, compile_and_run, [''])
test('T26061', normal, compile_and_run, [''])
test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
+test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f3ccdc29eb988b3be39937051d12dc…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/f3ccdc29eb988b3be39937051d12dc…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/T25821] 3 commits: Fix PIC jump tables on Windows (#24016)
by Cheng Shao (@TerrorJack) 28 Nov '25
by Cheng Shao (@TerrorJack) 28 Nov '25
28 Nov '25
Cheng Shao pushed to branch wip/T25821 at Glasgow Haskell Compiler / GHC
Commits:
84a087d5 by Sylvain Henry at 2025-11-28T17:35:28-05:00
Fix PIC jump tables on Windows (#24016)
Avoid overflows in jump tables by using a base label closer to the jump
targets. See added Note [Jump tables]
- - - - -
82db7042 by Zubin Duggal at 2025-11-28T17:36:10-05:00
rts/linker/PEi386: Copy strings before they are inserted into LoadedDllCache. The original strings are temporary and might be freed at an arbitrary point.
Fixes #26613
- - - - -
ed8a0669 by Ben Gamari at 2025-11-29T00:06:50+01:00
hadrian: Place user options after package arguments
This makes it easier for the user to override the default package
arguments with `UserSettings.hs`.
Fixes #25821.
-------------------------
Metric Decrease:
T14697
-------------------------
- - - - -
9 changed files:
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- docs/users_guide/phases.rst
- hadrian/src/Settings.hs
- rts/linker/PEi386.c
- + testsuite/tests/codeGen/should_run/T24016.hs
- + testsuite/tests/codeGen/should_run/T24016.stdout
- testsuite/tests/codeGen/should_run/all.T
Changes:
=====================================
compiler/GHC/CmmToAsm/X86/CodeGen.hs
=====================================
@@ -376,7 +376,7 @@ stmtToInstrs bid stmt = do
--We try to arrange blocks such that the likely branch is the fallthrough
--in GHC.Cmm.ContFlowOpt. So we can assume the condition is likely false here.
CmmCondBranch arg true false _ -> genCondBranch bid true false arg
- CmmSwitch arg ids -> genSwitch arg ids
+ CmmSwitch arg ids -> genSwitch arg ids bid
CmmCall { cml_target = arg
, cml_args_regs = gregs } -> genJump arg (jumpRegs platform gregs)
_ ->
@@ -489,13 +489,6 @@ is32BitInteger i = i64 <= 0x7fffffff && i64 >= -0x80000000
where i64 = fromIntegral i :: Int64
--- | Convert a BlockId to some CmmStatic data
-jumpTableEntry :: NCGConfig -> Maybe BlockId -> CmmStatic
-jumpTableEntry config Nothing = CmmStaticLit (CmmInt 0 (ncgWordWidth config))
-jumpTableEntry _ (Just blockid) = CmmStaticLit (CmmLabel blockLabel)
- where blockLabel = blockLbl blockid
-
-
-- -----------------------------------------------------------------------------
-- General things for putting together code sequences
@@ -5375,11 +5368,52 @@ index (1),
indexExpr = UU_Conv(indexOffset); // == 1::I64
See #21186.
--}
-genSwitch :: CmmExpr -> SwitchTargets -> NatM InstrBlock
+Note [Jump tables]
+~~~~~~~~~~~~~~~~~~
+The x86 backend has a virtual JMP_TBL instruction which payload can be used to
+generate both the jump instruction and the jump table contents. `genSwitch` is
+responsible for generating these JMP_TBL instructions.
+
+Depending on `-fPIC` flag and on the architecture, we generate the following
+jump table variants:
+
+ | Variant | Arch | Table's contents | Reference to the table |
+ |---------|--------|----------------------------------------|------------------------|
+ | PIC | Both | Relative offset: target_lbl - base_lbl | PIC |
+ | Non-PIC | 64-bit | Absolute: target_lbl | Non-PIC (rip-relative) |
+ | Non-PIC | 32-bit | Absolute: target_lbl | Non-PIC (absolute) |
+
+For the PIC variant, we store relative entries (`target_lbl - base_lbl`) in the
+jump table. Using absolute entries with PIC would require target_lbl symbols to
+be resolved at link time, hence to be global labels (currently they are local
+labels).
+
+We use the block_id of the code containing the jump as `base_lbl`. It ensures
+that target_lbl and base_lbl are close enough to each others, avoiding
+overflows.
+
+Historical note: in the past we used the table label `table_lbl` as base_lbl. It
+allowed the jumping code to only compute one global address (table_lbl) both to
+read the table and to compute the target address. However:
-genSwitch expr targets = do
+ * the table could be too far from the jump and on Windows which only
+ has 32-bit relative relocations (IMAGE_REL_AMD64_REL64 doesn't exist),
+ `dest_lbl - table_lbl` overflowed (see #24016)
+
+ * Mac OS X/x86-64 linker was unable to handle `.quad L1 - L0`
+ relocations if L0 wasn't preceded by a non-anonymous label in its
+ section (which was the case with table_lbl). Hence we used to put the
+ jump table in the .text section in this case.
+
+
+-}
+
+-- | Generate a JMP_TBL instruction
+--
+-- See Note [Jump tables]
+genSwitch :: CmmExpr -> SwitchTargets -> BlockId -> NatM InstrBlock
+genSwitch expr targets bid = do
config <- getConfig
let platform = ncgPlatform config
expr_w = cmmExprWidth platform expr
@@ -5390,79 +5424,76 @@ genSwitch expr targets = do
indexExpr = CmmMachOp
(MO_UU_Conv expr_w (platformWordWidth platform))
[indexExpr0]
- if ncgPIC config
- then do
- (reg,e_code) <- getNonClobberedReg indexExpr
- -- getNonClobberedReg because it needs to survive across t_code
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- os = platformOS platform
- -- Might want to use .rodata.<function we're in> instead, but as
- -- long as it's something unique it'll work out since the
- -- references to the jump table are in the appropriate section.
- rosection = case os of
- -- on Mac OS X/x86_64, put the jump table in the text section to
- -- work around a limitation of the linker.
- -- ld64 is unable to handle the relocations for
- -- .quad L1 - L0
- -- if L0 is not preceded by a non-anonymous label in its section.
- OSDarwin | not is32bit -> Section Text lbl
- _ -> Section ReadOnlyData lbl
- dynRef <- cmmMakeDynamicReference config DataReference lbl
- (tableReg,t_code) <- getSomeReg $ dynRef
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg)
- (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
-
- return $ e_code `appOL` t_code `appOL` toOL [
- ADD (intFormat (platformWordWidth platform)) op (OpReg tableReg),
- JMP_TBL (OpReg tableReg) ids rosection lbl
- ]
- else do
- (reg,e_code) <- getSomeReg indexExpr
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- if is32bit
- then let op = OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl lbl))
- jmp_code = JMP_TBL op ids (Section ReadOnlyData lbl) lbl
- in return $ e_code `appOL` unitOL jmp_code
- else do
+
+ (offset, blockIds) = switchTargetsToTable targets
+ ids = map (fmap DestBlockId) blockIds
+
+ is32bit = target32Bit platform
+ fmt = archWordFormat is32bit
+
+ table_lbl <- getNewLabelNat
+ let bid_lbl = blockLbl bid
+ let table_section = Section ReadOnlyData table_lbl
+
+ -- see Note [Jump tables] for a description of the following 3 variants.
+ if
+ | ncgPIC config -> do
+ -- PIC support: store relative offsets in the jump table to allow the code
+ -- to be relocated without updating the table. The table itself and the
+ -- block label used to make the relative labels absolute are read in a PIC
+ -- way (via cmmMakeDynamicReference).
+ (reg,e_code) <- getNonClobberedReg indexExpr -- getNonClobberedReg because it needs to survive across t_code and j_code
+ (tableReg,t_code) <- getNonClobberedReg =<< cmmMakeDynamicReference config DataReference table_lbl
+ (targetReg,j_code) <- getSomeReg =<< cmmMakeDynamicReference config DataReference bid_lbl
+ pure $ e_code `appOL` t_code `appOL` j_code `appOL` toOL
+ [ ADD fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl (Just bid_lbl)
+ ]
+
+ | not is32bit -> do
+ -- 64-bit non-PIC code
+ (reg,e_code) <- getSomeReg indexExpr
+ tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ pure $ e_code `appOL` toOL
-- See Note [%rip-relative addressing on x86-64].
- tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
- targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
- fmt = archWordFormat is32bit
- code = e_code `appOL` toOL
- [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl lbl))) (OpReg tableReg)
- , MOV fmt op (OpReg targetReg)
- , JMP_TBL (OpReg targetReg) ids (Section ReadOnlyData lbl) lbl
- ]
- return code
- where
- (offset, blockIds) = switchTargetsToTable targets
- ids = map (fmap DestBlockId) blockIds
+ [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl table_lbl))) (OpReg tableReg)
+ , MOV fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl Nothing
+ ]
+
+ | otherwise -> do
+ -- 32-bit non-PIC code is a straightforward jump to &table[entry].
+ (reg,e_code) <- getSomeReg indexExpr
+ pure $ e_code `appOL` unitOL
+ ( JMP_TBL (OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl table_lbl)))
+ ids table_section table_lbl Nothing
+ )
generateJumpTableForInstr :: NCGConfig -> Instr -> Maybe (NatCmmDecl (Alignment, RawCmmStatics) Instr)
-generateJumpTableForInstr config (JMP_TBL _ ids section lbl)
- = let getBlockId (DestBlockId id) = id
- getBlockId _ = panic "Non-Label target in Jump Table"
- blockIds = map (fmap getBlockId) ids
- in Just (createJumpTable config blockIds section lbl)
-generateJumpTableForInstr _ _ = Nothing
-
-createJumpTable :: NCGConfig -> [Maybe BlockId] -> Section -> CLabel
- -> GenCmmDecl (Alignment, RawCmmStatics) h g
-createJumpTable config ids section lbl
- = let jumpTable
- | ncgPIC config =
- let ww = ncgWordWidth config
- jumpTableEntryRel Nothing
- = CmmStaticLit (CmmInt 0 ww)
- jumpTableEntryRel (Just blockid)
- = CmmStaticLit (CmmLabelDiffOff blockLabel lbl 0 ww)
- where blockLabel = blockLbl blockid
- in map jumpTableEntryRel ids
- | otherwise = map (jumpTableEntry config) ids
- in CmmData section (mkAlignment 1, CmmStaticsRaw lbl jumpTable)
+generateJumpTableForInstr config = \case
+ JMP_TBL _ ids section table_lbl mrel_lbl ->
+ let getBlockId (DestBlockId id) = id
+ getBlockId _ = panic "Non-Label target in Jump Table"
+ block_ids = map (fmap getBlockId) ids
+
+ jumpTable = case mrel_lbl of
+ Nothing -> map mk_absolute block_ids -- absolute entries
+ Just rel_lbl -> map (mk_relative rel_lbl) block_ids -- offsets relative to rel_lbl
+
+ mk_absolute = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabel (blockLbl blockid))
+
+ mk_relative rel_lbl = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabelDiffOff (blockLbl blockid) rel_lbl 0 (ncgWordWidth config))
+
+ in Just (CmmData section (mkAlignment 1, CmmStaticsRaw table_lbl jumpTable))
+
+ _ -> Nothing
extractUnwindPoints :: [Instr] -> [UnwindPoint]
extractUnwindPoints instrs =
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -252,6 +252,7 @@ data Instr
[Maybe JumpDest] -- Targets of the jump table
Section -- Data section jump table should be put in
CLabel -- Label of jump table
+ !(Maybe CLabel) -- Label used to compute relative offsets. Otherwise we store absolute addresses.
-- | X86 call instruction
| CALL (Either Imm Reg) -- ^ Jump target
[RegWithFormat] -- ^ Arguments (required for register allocation)
@@ -486,7 +487,7 @@ regUsageOfInstr platform instr
JXX _ _ -> mkRU [] []
JXX_GBL _ _ -> mkRU [] []
JMP op regs -> mkRU (use_R addrFmt op regs) []
- JMP_TBL op _ _ _ -> mkRU (use_R addrFmt op []) []
+ JMP_TBL op _ _ _ _ -> mkRU (use_R addrFmt op []) []
CALL (Left _) params -> mkRU params (map mkFmt $ callClobberedRegs platform)
CALL (Right reg) params -> mkRU (mk addrFmt reg:params) (map mkFmt $ callClobberedRegs platform)
CLTD fmt -> mkRU [mk fmt eax] [mk fmt edx]
@@ -812,7 +813,7 @@ patchRegsOfInstr platform instr env
POP fmt op -> patch1 (POP fmt) op
SETCC cond op -> patch1 (SETCC cond) op
JMP op regs -> JMP (patchOp op) regs
- JMP_TBL op ids s lbl -> JMP_TBL (patchOp op) ids s lbl
+ JMP_TBL op ids s tl jl -> JMP_TBL (patchOp op) ids s tl jl
FMA3 fmt perm var x1 x2 x3 -> patch3 (FMA3 fmt perm var) x1 x2 x3
@@ -1016,9 +1017,9 @@ isJumpishInstr instr
canFallthroughTo :: Instr -> BlockId -> Bool
canFallthroughTo insn bid
= case insn of
- JXX _ target -> bid == target
- JMP_TBL _ targets _ _ -> all isTargetBid targets
- _ -> False
+ JXX _ target -> bid == target
+ JMP_TBL _ targets _ _ _ -> all isTargetBid targets
+ _ -> False
where
isTargetBid target = case target of
Nothing -> True
@@ -1031,9 +1032,9 @@ jumpDestsOfInstr
jumpDestsOfInstr insn
= case insn of
- JXX _ id -> [id]
- JMP_TBL _ ids _ _ -> [id | Just (DestBlockId id) <- ids]
- _ -> []
+ JXX _ id -> [id]
+ JMP_TBL _ ids _ _ _ -> [id | Just (DestBlockId id) <- ids]
+ _ -> []
patchJumpInstr
@@ -1042,8 +1043,8 @@ patchJumpInstr
patchJumpInstr insn patchF
= case insn of
JXX cc id -> JXX cc (patchF id)
- JMP_TBL op ids section lbl
- -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section lbl
+ JMP_TBL op ids section table_lbl rel_lbl
+ -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section table_lbl rel_lbl
_ -> insn
where
patchJumpDest f (DestBlockId id) = DestBlockId (f id)
@@ -1504,14 +1505,14 @@ shortcutJump fn insn = shortcutJump' fn (setEmpty :: LabelSet) insn
Just (DestBlockId id') -> shortcutJump' fn seen' (JXX cc id')
Just (DestImm imm) -> shortcutJump' fn seen' (JXX_GBL cc imm)
where seen' = setInsert id seen
- shortcutJump' fn _ (JMP_TBL addr blocks section tblId) =
+ shortcutJump' fn _ (JMP_TBL addr blocks section table_lbl rel_lbl) =
let updateBlock (Just (DestBlockId bid)) =
case fn bid of
Nothing -> Just (DestBlockId bid )
Just dest -> Just dest
updateBlock dest = dest
blocks' = map updateBlock blocks
- in JMP_TBL addr blocks' section tblId
+ in JMP_TBL addr blocks' section table_lbl rel_lbl
shortcutJump' _ _ other = other
-- Here because it knows about JumpDest
=====================================
compiler/GHC/CmmToAsm/X86/Ppr.hs
=====================================
@@ -895,7 +895,7 @@ pprInstr platform i = case i of
JMP op _
-> line $ text "\tjmp *" <> pprOperand platform (archWordFormat (target32Bit platform)) op
- JMP_TBL op _ _ _
+ JMP_TBL op _ _ _ _
-> pprInstr platform (JMP op [])
CALL (Left imm) _
=====================================
docs/users_guide/phases.rst
=====================================
@@ -770,10 +770,9 @@ Options affecting code generation
:type: dynamic
:category: codegen
- Generate position-independent code (code that can be put into shared
- libraries). This currently works on Linux x86 and x86-64. On
- Windows, position-independent code is never used so the flag is a
- no-op on that platform.
+ Generate position-independent code (PIC). This code can be put into shared
+ libraries and is sometimes required by operating systems, e.g. systems using
+ Address Space Layout Randomization (ASLR).
.. ghc-flag:: -fexternal-dynamic-refs
:shortdesc: Generate code for linking against dynamic libraries
@@ -790,9 +789,7 @@ Options affecting code generation
:category: codegen
Generate code in such a way to be linkable into a position-independent
- executable This currently works on Linux x86 and x86-64. On Windows,
- position-independent code is never used so the flag is a no-op on that
- platform. To link the final executable use :ghc-flag:`-pie`.
+ executable. To link the final executable use :ghc-flag:`-pie`.
.. ghc-flag:: -dynamic
:shortdesc: Build dynamically-linked object files and executables
=====================================
hadrian/src/Settings.hs
=====================================
@@ -35,7 +35,7 @@ getExtraArgs :: Args
getExtraArgs = expr flavour >>= extraArgs
getArgs :: Args
-getArgs = mconcat [ defaultBuilderArgs, getExtraArgs, defaultPackageArgs ]
+getArgs = mconcat [ defaultBuilderArgs, defaultPackageArgs, getExtraArgs ]
getLibraryWays :: Ways
getLibraryWays = expr flavour >>= libraryWays
=====================================
rts/linker/PEi386.c
=====================================
@@ -552,7 +552,12 @@ static int compare_path(StgWord key1, StgWord key2)
static void addLoadedDll(LoadedDllCache *cache, const pathchar *dll_name, HINSTANCE instance)
{
- insertHashTable_(cache->hash, (StgWord) dll_name, instance, hash_path);
+ // dll_name might be deallocated, we need to copy it to have a stable reference to the contents
+ // See #26613
+ size_t size = wcslen(dll_name) + 1;
+ pathchar* dll_name_copy = stgMallocBytes(size * sizeof(pathchar), "addLoadedDll");
+ wcsncpy(dll_name_copy, dll_name, size);
+ insertHashTable_(cache->hash, (StgWord) dll_name_copy, instance, hash_path);
}
static HINSTANCE isDllLoaded(const LoadedDllCache *cache, const pathchar *dll_name)
=====================================
testsuite/tests/codeGen/should_run/T24016.hs
=====================================
@@ -0,0 +1,24 @@
+module Main (main) where
+
+data Command
+ = Command1
+ | Command2
+ | Command3
+ | Command4
+ | Command5
+ | Command6 -- Commenting this line works with -fPIC, uncommenting leads to a crash.
+
+main :: IO ()
+main = do
+ let x = case cmd of
+ Command1 -> 1 :: Int
+ Command2 -> 2
+ Command3 -> 3
+ Command4 -> 4
+ Command5 -> 5
+ Command6 -> 6
+ putStrLn (show x)
+
+{-# NOINLINE cmd #-}
+cmd :: Command
+cmd = Command6
=====================================
testsuite/tests/codeGen/should_run/T24016.stdout
=====================================
@@ -0,0 +1 @@
+6
=====================================
testsuite/tests/codeGen/should_run/all.T
=====================================
@@ -257,3 +257,4 @@ test('CCallConv', [req_c], compile_and_run, ['CCallConv_c.c'])
test('T25364', normal, compile_and_run, [''])
test('T26061', normal, compile_and_run, [''])
test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
+test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b650f96252e2fc18248def9b849043…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/b650f96252e2fc18248def9b849043…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][wip/T25821] hadrian: Place user options after package arguments
by Cheng Shao (@TerrorJack) 28 Nov '25
by Cheng Shao (@TerrorJack) 28 Nov '25
28 Nov '25
Cheng Shao pushed to branch wip/T25821 at Glasgow Haskell Compiler / GHC
Commits:
b650f962 by Ben Gamari at 2025-11-29T00:05:20+01:00
hadrian: Place user options after package arguments
This makes it easier for the user to override the default package
arguments with `UserSettings.hs`.
Fixes #25821.
-------------------------
Metric Decrease:
T14697
-------------------------
- - - - -
1 changed file:
- hadrian/src/Settings.hs
Changes:
=====================================
hadrian/src/Settings.hs
=====================================
@@ -35,7 +35,7 @@ getExtraArgs :: Args
getExtraArgs = expr flavour >>= extraArgs
getArgs :: Args
-getArgs = mconcat [ defaultBuilderArgs, getExtraArgs, defaultPackageArgs ]
+getArgs = mconcat [ defaultBuilderArgs, defaultPackageArgs, getExtraArgs ]
getLibraryWays :: Ways
getLibraryWays = expr flavour >>= libraryWays
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b650f96252e2fc18248def9b8490439…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b650f96252e2fc18248def9b8490439…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
[Git][ghc/ghc][master] rts/linker/PEi386: Copy strings before they are inserted into LoadedDllCache....
by Marge Bot (@marge-bot) 28 Nov '25
by Marge Bot (@marge-bot) 28 Nov '25
28 Nov '25
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
82db7042 by Zubin Duggal at 2025-11-28T17:36:10-05:00
rts/linker/PEi386: Copy strings before they are inserted into LoadedDllCache. The original strings are temporary and might be freed at an arbitrary point.
Fixes #26613
- - - - -
1 changed file:
- rts/linker/PEi386.c
Changes:
=====================================
rts/linker/PEi386.c
=====================================
@@ -552,7 +552,12 @@ static int compare_path(StgWord key1, StgWord key2)
static void addLoadedDll(LoadedDllCache *cache, const pathchar *dll_name, HINSTANCE instance)
{
- insertHashTable_(cache->hash, (StgWord) dll_name, instance, hash_path);
+ // dll_name might be deallocated, we need to copy it to have a stable reference to the contents
+ // See #26613
+ size_t size = wcslen(dll_name) + 1;
+ pathchar* dll_name_copy = stgMallocBytes(size * sizeof(pathchar), "addLoadedDll");
+ wcsncpy(dll_name_copy, dll_name, size);
+ insertHashTable_(cache->hash, (StgWord) dll_name_copy, instance, hash_path);
}
static HINSTANCE isDllLoaded(const LoadedDllCache *cache, const pathchar *dll_name)
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/82db70420e546fba189ddc26cc69e57…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/82db70420e546fba189ddc26cc69e57…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
28 Nov '25
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
84a087d5 by Sylvain Henry at 2025-11-28T17:35:28-05:00
Fix PIC jump tables on Windows (#24016)
Avoid overflows in jump tables by using a base label closer to the jump
targets. See added Note [Jump tables]
- - - - -
7 changed files:
- compiler/GHC/CmmToAsm/X86/CodeGen.hs
- compiler/GHC/CmmToAsm/X86/Instr.hs
- compiler/GHC/CmmToAsm/X86/Ppr.hs
- docs/users_guide/phases.rst
- + testsuite/tests/codeGen/should_run/T24016.hs
- + testsuite/tests/codeGen/should_run/T24016.stdout
- testsuite/tests/codeGen/should_run/all.T
Changes:
=====================================
compiler/GHC/CmmToAsm/X86/CodeGen.hs
=====================================
@@ -376,7 +376,7 @@ stmtToInstrs bid stmt = do
--We try to arrange blocks such that the likely branch is the fallthrough
--in GHC.Cmm.ContFlowOpt. So we can assume the condition is likely false here.
CmmCondBranch arg true false _ -> genCondBranch bid true false arg
- CmmSwitch arg ids -> genSwitch arg ids
+ CmmSwitch arg ids -> genSwitch arg ids bid
CmmCall { cml_target = arg
, cml_args_regs = gregs } -> genJump arg (jumpRegs platform gregs)
_ ->
@@ -489,13 +489,6 @@ is32BitInteger i = i64 <= 0x7fffffff && i64 >= -0x80000000
where i64 = fromIntegral i :: Int64
--- | Convert a BlockId to some CmmStatic data
-jumpTableEntry :: NCGConfig -> Maybe BlockId -> CmmStatic
-jumpTableEntry config Nothing = CmmStaticLit (CmmInt 0 (ncgWordWidth config))
-jumpTableEntry _ (Just blockid) = CmmStaticLit (CmmLabel blockLabel)
- where blockLabel = blockLbl blockid
-
-
-- -----------------------------------------------------------------------------
-- General things for putting together code sequences
@@ -5375,11 +5368,52 @@ index (1),
indexExpr = UU_Conv(indexOffset); // == 1::I64
See #21186.
--}
-genSwitch :: CmmExpr -> SwitchTargets -> NatM InstrBlock
+Note [Jump tables]
+~~~~~~~~~~~~~~~~~~
+The x86 backend has a virtual JMP_TBL instruction which payload can be used to
+generate both the jump instruction and the jump table contents. `genSwitch` is
+responsible for generating these JMP_TBL instructions.
+
+Depending on `-fPIC` flag and on the architecture, we generate the following
+jump table variants:
+
+ | Variant | Arch | Table's contents | Reference to the table |
+ |---------|--------|----------------------------------------|------------------------|
+ | PIC | Both | Relative offset: target_lbl - base_lbl | PIC |
+ | Non-PIC | 64-bit | Absolute: target_lbl | Non-PIC (rip-relative) |
+ | Non-PIC | 32-bit | Absolute: target_lbl | Non-PIC (absolute) |
+
+For the PIC variant, we store relative entries (`target_lbl - base_lbl`) in the
+jump table. Using absolute entries with PIC would require target_lbl symbols to
+be resolved at link time, hence to be global labels (currently they are local
+labels).
+
+We use the block_id of the code containing the jump as `base_lbl`. It ensures
+that target_lbl and base_lbl are close enough to each others, avoiding
+overflows.
+
+Historical note: in the past we used the table label `table_lbl` as base_lbl. It
+allowed the jumping code to only compute one global address (table_lbl) both to
+read the table and to compute the target address. However:
-genSwitch expr targets = do
+ * the table could be too far from the jump and on Windows which only
+ has 32-bit relative relocations (IMAGE_REL_AMD64_REL64 doesn't exist),
+ `dest_lbl - table_lbl` overflowed (see #24016)
+
+ * Mac OS X/x86-64 linker was unable to handle `.quad L1 - L0`
+ relocations if L0 wasn't preceded by a non-anonymous label in its
+ section (which was the case with table_lbl). Hence we used to put the
+ jump table in the .text section in this case.
+
+
+-}
+
+-- | Generate a JMP_TBL instruction
+--
+-- See Note [Jump tables]
+genSwitch :: CmmExpr -> SwitchTargets -> BlockId -> NatM InstrBlock
+genSwitch expr targets bid = do
config <- getConfig
let platform = ncgPlatform config
expr_w = cmmExprWidth platform expr
@@ -5390,79 +5424,76 @@ genSwitch expr targets = do
indexExpr = CmmMachOp
(MO_UU_Conv expr_w (platformWordWidth platform))
[indexExpr0]
- if ncgPIC config
- then do
- (reg,e_code) <- getNonClobberedReg indexExpr
- -- getNonClobberedReg because it needs to survive across t_code
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- os = platformOS platform
- -- Might want to use .rodata.<function we're in> instead, but as
- -- long as it's something unique it'll work out since the
- -- references to the jump table are in the appropriate section.
- rosection = case os of
- -- on Mac OS X/x86_64, put the jump table in the text section to
- -- work around a limitation of the linker.
- -- ld64 is unable to handle the relocations for
- -- .quad L1 - L0
- -- if L0 is not preceded by a non-anonymous label in its section.
- OSDarwin | not is32bit -> Section Text lbl
- _ -> Section ReadOnlyData lbl
- dynRef <- cmmMakeDynamicReference config DataReference lbl
- (tableReg,t_code) <- getSomeReg $ dynRef
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg)
- (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
-
- return $ e_code `appOL` t_code `appOL` toOL [
- ADD (intFormat (platformWordWidth platform)) op (OpReg tableReg),
- JMP_TBL (OpReg tableReg) ids rosection lbl
- ]
- else do
- (reg,e_code) <- getSomeReg indexExpr
- lbl <- getNewLabelNat
- let is32bit = target32Bit platform
- if is32bit
- then let op = OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl lbl))
- jmp_code = JMP_TBL op ids (Section ReadOnlyData lbl) lbl
- in return $ e_code `appOL` unitOL jmp_code
- else do
+
+ (offset, blockIds) = switchTargetsToTable targets
+ ids = map (fmap DestBlockId) blockIds
+
+ is32bit = target32Bit platform
+ fmt = archWordFormat is32bit
+
+ table_lbl <- getNewLabelNat
+ let bid_lbl = blockLbl bid
+ let table_section = Section ReadOnlyData table_lbl
+
+ -- see Note [Jump tables] for a description of the following 3 variants.
+ if
+ | ncgPIC config -> do
+ -- PIC support: store relative offsets in the jump table to allow the code
+ -- to be relocated without updating the table. The table itself and the
+ -- block label used to make the relative labels absolute are read in a PIC
+ -- way (via cmmMakeDynamicReference).
+ (reg,e_code) <- getNonClobberedReg indexExpr -- getNonClobberedReg because it needs to survive across t_code and j_code
+ (tableReg,t_code) <- getNonClobberedReg =<< cmmMakeDynamicReference config DataReference table_lbl
+ (targetReg,j_code) <- getSomeReg =<< cmmMakeDynamicReference config DataReference bid_lbl
+ pure $ e_code `appOL` t_code `appOL` j_code `appOL` toOL
+ [ ADD fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl (Just bid_lbl)
+ ]
+
+ | not is32bit -> do
+ -- 64-bit non-PIC code
+ (reg,e_code) <- getSomeReg indexExpr
+ tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
+ pure $ e_code `appOL` toOL
-- See Note [%rip-relative addressing on x86-64].
- tableReg <- getNewRegNat (intFormat (platformWordWidth platform))
- targetReg <- getNewRegNat (intFormat (platformWordWidth platform))
- let op = OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0))
- fmt = archWordFormat is32bit
- code = e_code `appOL` toOL
- [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl lbl))) (OpReg tableReg)
- , MOV fmt op (OpReg targetReg)
- , JMP_TBL (OpReg targetReg) ids (Section ReadOnlyData lbl) lbl
- ]
- return code
- where
- (offset, blockIds) = switchTargetsToTable targets
- ids = map (fmap DestBlockId) blockIds
+ [ LEA fmt (OpAddr (AddrBaseIndex EABaseRip EAIndexNone (ImmCLbl table_lbl))) (OpReg tableReg)
+ , MOV fmt (OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)))
+ (OpReg targetReg)
+ , JMP_TBL (OpReg targetReg) ids table_section table_lbl Nothing
+ ]
+
+ | otherwise -> do
+ -- 32-bit non-PIC code is a straightforward jump to &table[entry].
+ (reg,e_code) <- getSomeReg indexExpr
+ pure $ e_code `appOL` unitOL
+ ( JMP_TBL (OpAddr (AddrBaseIndex EABaseNone (EAIndex reg (platformWordSizeInBytes platform)) (ImmCLbl table_lbl)))
+ ids table_section table_lbl Nothing
+ )
generateJumpTableForInstr :: NCGConfig -> Instr -> Maybe (NatCmmDecl (Alignment, RawCmmStatics) Instr)
-generateJumpTableForInstr config (JMP_TBL _ ids section lbl)
- = let getBlockId (DestBlockId id) = id
- getBlockId _ = panic "Non-Label target in Jump Table"
- blockIds = map (fmap getBlockId) ids
- in Just (createJumpTable config blockIds section lbl)
-generateJumpTableForInstr _ _ = Nothing
-
-createJumpTable :: NCGConfig -> [Maybe BlockId] -> Section -> CLabel
- -> GenCmmDecl (Alignment, RawCmmStatics) h g
-createJumpTable config ids section lbl
- = let jumpTable
- | ncgPIC config =
- let ww = ncgWordWidth config
- jumpTableEntryRel Nothing
- = CmmStaticLit (CmmInt 0 ww)
- jumpTableEntryRel (Just blockid)
- = CmmStaticLit (CmmLabelDiffOff blockLabel lbl 0 ww)
- where blockLabel = blockLbl blockid
- in map jumpTableEntryRel ids
- | otherwise = map (jumpTableEntry config) ids
- in CmmData section (mkAlignment 1, CmmStaticsRaw lbl jumpTable)
+generateJumpTableForInstr config = \case
+ JMP_TBL _ ids section table_lbl mrel_lbl ->
+ let getBlockId (DestBlockId id) = id
+ getBlockId _ = panic "Non-Label target in Jump Table"
+ block_ids = map (fmap getBlockId) ids
+
+ jumpTable = case mrel_lbl of
+ Nothing -> map mk_absolute block_ids -- absolute entries
+ Just rel_lbl -> map (mk_relative rel_lbl) block_ids -- offsets relative to rel_lbl
+
+ mk_absolute = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabel (blockLbl blockid))
+
+ mk_relative rel_lbl = \case
+ Nothing -> CmmStaticLit (CmmInt 0 (ncgWordWidth config))
+ Just blockid -> CmmStaticLit (CmmLabelDiffOff (blockLbl blockid) rel_lbl 0 (ncgWordWidth config))
+
+ in Just (CmmData section (mkAlignment 1, CmmStaticsRaw table_lbl jumpTable))
+
+ _ -> Nothing
extractUnwindPoints :: [Instr] -> [UnwindPoint]
extractUnwindPoints instrs =
=====================================
compiler/GHC/CmmToAsm/X86/Instr.hs
=====================================
@@ -252,6 +252,7 @@ data Instr
[Maybe JumpDest] -- Targets of the jump table
Section -- Data section jump table should be put in
CLabel -- Label of jump table
+ !(Maybe CLabel) -- Label used to compute relative offsets. Otherwise we store absolute addresses.
-- | X86 call instruction
| CALL (Either Imm Reg) -- ^ Jump target
[RegWithFormat] -- ^ Arguments (required for register allocation)
@@ -486,7 +487,7 @@ regUsageOfInstr platform instr
JXX _ _ -> mkRU [] []
JXX_GBL _ _ -> mkRU [] []
JMP op regs -> mkRU (use_R addrFmt op regs) []
- JMP_TBL op _ _ _ -> mkRU (use_R addrFmt op []) []
+ JMP_TBL op _ _ _ _ -> mkRU (use_R addrFmt op []) []
CALL (Left _) params -> mkRU params (map mkFmt $ callClobberedRegs platform)
CALL (Right reg) params -> mkRU (mk addrFmt reg:params) (map mkFmt $ callClobberedRegs platform)
CLTD fmt -> mkRU [mk fmt eax] [mk fmt edx]
@@ -812,7 +813,7 @@ patchRegsOfInstr platform instr env
POP fmt op -> patch1 (POP fmt) op
SETCC cond op -> patch1 (SETCC cond) op
JMP op regs -> JMP (patchOp op) regs
- JMP_TBL op ids s lbl -> JMP_TBL (patchOp op) ids s lbl
+ JMP_TBL op ids s tl jl -> JMP_TBL (patchOp op) ids s tl jl
FMA3 fmt perm var x1 x2 x3 -> patch3 (FMA3 fmt perm var) x1 x2 x3
@@ -1016,9 +1017,9 @@ isJumpishInstr instr
canFallthroughTo :: Instr -> BlockId -> Bool
canFallthroughTo insn bid
= case insn of
- JXX _ target -> bid == target
- JMP_TBL _ targets _ _ -> all isTargetBid targets
- _ -> False
+ JXX _ target -> bid == target
+ JMP_TBL _ targets _ _ _ -> all isTargetBid targets
+ _ -> False
where
isTargetBid target = case target of
Nothing -> True
@@ -1031,9 +1032,9 @@ jumpDestsOfInstr
jumpDestsOfInstr insn
= case insn of
- JXX _ id -> [id]
- JMP_TBL _ ids _ _ -> [id | Just (DestBlockId id) <- ids]
- _ -> []
+ JXX _ id -> [id]
+ JMP_TBL _ ids _ _ _ -> [id | Just (DestBlockId id) <- ids]
+ _ -> []
patchJumpInstr
@@ -1042,8 +1043,8 @@ patchJumpInstr
patchJumpInstr insn patchF
= case insn of
JXX cc id -> JXX cc (patchF id)
- JMP_TBL op ids section lbl
- -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section lbl
+ JMP_TBL op ids section table_lbl rel_lbl
+ -> JMP_TBL op (map (fmap (patchJumpDest patchF)) ids) section table_lbl rel_lbl
_ -> insn
where
patchJumpDest f (DestBlockId id) = DestBlockId (f id)
@@ -1504,14 +1505,14 @@ shortcutJump fn insn = shortcutJump' fn (setEmpty :: LabelSet) insn
Just (DestBlockId id') -> shortcutJump' fn seen' (JXX cc id')
Just (DestImm imm) -> shortcutJump' fn seen' (JXX_GBL cc imm)
where seen' = setInsert id seen
- shortcutJump' fn _ (JMP_TBL addr blocks section tblId) =
+ shortcutJump' fn _ (JMP_TBL addr blocks section table_lbl rel_lbl) =
let updateBlock (Just (DestBlockId bid)) =
case fn bid of
Nothing -> Just (DestBlockId bid )
Just dest -> Just dest
updateBlock dest = dest
blocks' = map updateBlock blocks
- in JMP_TBL addr blocks' section tblId
+ in JMP_TBL addr blocks' section table_lbl rel_lbl
shortcutJump' _ _ other = other
-- Here because it knows about JumpDest
=====================================
compiler/GHC/CmmToAsm/X86/Ppr.hs
=====================================
@@ -895,7 +895,7 @@ pprInstr platform i = case i of
JMP op _
-> line $ text "\tjmp *" <> pprOperand platform (archWordFormat (target32Bit platform)) op
- JMP_TBL op _ _ _
+ JMP_TBL op _ _ _ _
-> pprInstr platform (JMP op [])
CALL (Left imm) _
=====================================
docs/users_guide/phases.rst
=====================================
@@ -770,10 +770,9 @@ Options affecting code generation
:type: dynamic
:category: codegen
- Generate position-independent code (code that can be put into shared
- libraries). This currently works on Linux x86 and x86-64. On
- Windows, position-independent code is never used so the flag is a
- no-op on that platform.
+ Generate position-independent code (PIC). This code can be put into shared
+ libraries and is sometimes required by operating systems, e.g. systems using
+ Address Space Layout Randomization (ASLR).
.. ghc-flag:: -fexternal-dynamic-refs
:shortdesc: Generate code for linking against dynamic libraries
@@ -790,9 +789,7 @@ Options affecting code generation
:category: codegen
Generate code in such a way to be linkable into a position-independent
- executable This currently works on Linux x86 and x86-64. On Windows,
- position-independent code is never used so the flag is a no-op on that
- platform. To link the final executable use :ghc-flag:`-pie`.
+ executable. To link the final executable use :ghc-flag:`-pie`.
.. ghc-flag:: -dynamic
:shortdesc: Build dynamically-linked object files and executables
=====================================
testsuite/tests/codeGen/should_run/T24016.hs
=====================================
@@ -0,0 +1,24 @@
+module Main (main) where
+
+data Command
+ = Command1
+ | Command2
+ | Command3
+ | Command4
+ | Command5
+ | Command6 -- Commenting this line works with -fPIC, uncommenting leads to a crash.
+
+main :: IO ()
+main = do
+ let x = case cmd of
+ Command1 -> 1 :: Int
+ Command2 -> 2
+ Command3 -> 3
+ Command4 -> 4
+ Command5 -> 5
+ Command6 -> 6
+ putStrLn (show x)
+
+{-# NOINLINE cmd #-}
+cmd :: Command
+cmd = Command6
=====================================
testsuite/tests/codeGen/should_run/T24016.stdout
=====================================
@@ -0,0 +1 @@
+6
=====================================
testsuite/tests/codeGen/should_run/all.T
=====================================
@@ -257,3 +257,4 @@ test('CCallConv', [req_c], compile_and_run, ['CCallConv_c.c'])
test('T25364', normal, compile_and_run, [''])
test('T26061', normal, compile_and_run, [''])
test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
+test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/84a087d5d5054f25f664c72f42cc528…
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/84a087d5d5054f25f664c72f42cc528…
You're receiving this email because of your account on gitlab.haskell.org.
1
0
Cheng Shao pushed new branch wip/fix-clangd at Glasgow Haskell Compiler / GHC
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/tree/wip/fix-clangd
You're receiving this email because of your account on gitlab.haskell.org.
1
0