David Eichmann pushed to branch wip/davide/ghc-toolchain-llvm-versions at Glasgow Haskell Compiler / GHC
Commits:
-
6278067c
by David Eichmann at 2026-05-13T11:23:03+01:00
3 changed files:
- m4/ghc_toolchain.m4
- utils/ghc-toolchain/exe/Main.hs
- utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
Changes:
| ... | ... | @@ -55,6 +55,8 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN], |
| 55 | 55 | rm -f acargs
|
| 56 | 56 | echo "--triple=$HostPlatform" >> acargs
|
| 57 | 57 | echo "--output=$1/default.host.target.ghc-toolchain" >> acargs
|
| 58 | + echo "--llvm-min-version=$LlvmMinVersion" >> acargs
|
|
| 59 | + echo "--llvm-max-version-excl=$LlvmMaxVersion" >> acargs
|
|
| 58 | 60 | echo "--cc=$CC_STAGE0" >> acargs
|
| 59 | 61 | echo "--cc-link=$CC_STAGE0" >> acargs
|
| 60 | 62 | echo "--ar=$AR_STAGE0" >> acargs
|
| ... | ... | @@ -82,6 +84,8 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN], |
| 82 | 84 | echo "--triple=$target" >> acargs
|
| 83 | 85 | echo "--output=$1/default.target.ghc-toolchain" >> acargs
|
| 84 | 86 | echo "--llvm-triple=$LlvmTarget" >> acargs
|
| 87 | + echo "--llvm-min-version=$LlvmMinVersion" >> acargs
|
|
| 88 | + echo "--llvm-max-version-excl=$LlvmMaxVersion" >> acargs
|
|
| 85 | 89 | echo "--cc=$CC" >> acargs
|
| 86 | 90 | echo "--cxx=$CXX" >> acargs
|
| 87 | 91 | echo "--cpp=$CPPCmd" >> acargs
|
| ... | ... | @@ -39,6 +39,8 @@ data Opts = Opts |
| 39 | 39 | , optTargetPrefix :: Maybe String
|
| 40 | 40 | , optLocallyExecutable :: Maybe Bool
|
| 41 | 41 | , optLlvmTriple :: Maybe String
|
| 42 | + , optLlvmMinVersion :: Maybe Int -- ^ Minimum supported LLVM version (inclusive)
|
|
| 43 | + , optLlvmMaxVersion :: Maybe Int -- ^ Maximum supported LLVM version (inclusive)
|
|
| 42 | 44 | , optOutput :: Maybe String
|
| 43 | 45 | , optCc :: ProgOpt
|
| 44 | 46 | , optCxx :: ProgOpt
|
| ... | ... | @@ -98,6 +100,8 @@ emptyOpts = Opts |
| 98 | 100 | , optTargetPrefix = Nothing
|
| 99 | 101 | , optLocallyExecutable = Nothing
|
| 100 | 102 | , optLlvmTriple = Nothing
|
| 103 | + , optLlvmMinVersion = Nothing
|
|
| 104 | + , optLlvmMaxVersion = Nothing
|
|
| 101 | 105 | , optOutput = Nothing
|
| 102 | 106 | , optCc = po0
|
| 103 | 107 | , optCxx = po0
|
| ... | ... | @@ -163,6 +167,10 @@ _optTriple = Lens optTriple (\x o -> o {optTriple=x}) |
| 163 | 167 | _optLlvmTriple :: Lens Opts (Maybe String)
|
| 164 | 168 | _optLlvmTriple = Lens optLlvmTriple (\x o -> o {optLlvmTriple=x})
|
| 165 | 169 | |
| 170 | +_optLlvmMinVersion, _optLlvmMaxVersion :: Lens Opts (Maybe Int)
|
|
| 171 | +_optLlvmMinVersion = Lens optLlvmMinVersion (\x o -> o {optLlvmMinVersion=x})
|
|
| 172 | +_optLlvmMaxVersion = Lens optLlvmMaxVersion (\x o -> o {optLlvmMaxVersion=x})
|
|
| 173 | + |
|
| 166 | 174 | _optOutput :: Lens Opts (Maybe String)
|
| 167 | 175 | _optOutput = Lens optOutput (\x o -> o {optOutput=x})
|
| 168 | 176 | |
| ... | ... | @@ -192,6 +200,8 @@ options = |
| 192 | 200 | [ tripleOpt
|
| 193 | 201 | , targetPrefixOpt
|
| 194 | 202 | , llvmTripleOpt
|
| 203 | + , llvmMinVersionOpt
|
|
| 204 | + , llvmMaxVersionOpt
|
|
| 195 | 205 | , verbosityOpt
|
| 196 | 206 | , keepTempOpt
|
| 197 | 207 | , outputOpt
|
| ... | ... | @@ -259,6 +269,9 @@ options = |
| 259 | 269 | tripleOpt = Option ['t'] ["triple"] (ReqArg (set _optTriple . Just) "TRIPLE") "Target triple"
|
| 260 | 270 | llvmTripleOpt = Option [] ["llvm-triple"] (ReqArg (set _optLlvmTriple . Just) "LLVM-TRIPLE") "LLVM Target triple"
|
| 261 | 271 | |
| 272 | + llvmMinVersionOpt = Option [] ["llvm-min-version"] (ReqArg (set _optLlvmMinVersion . Just . read) "LLVM-MIN-VERSION") "LLVM min version (inclusive)"
|
|
| 273 | + llvmMaxVersionOpt = Option [] ["llvm-max-version-excl"] (ReqArg (set _optLlvmMaxVersion . Just . subtract 1 . read) "LLVM-MAX-VERSION") "LLVM max version (exclusive)"
|
|
| 274 | + |
|
| 262 | 275 | targetPrefixOpt = Option ['T'] ["target-prefix"] (ReqArg (set _optTargetPrefix . Just) "PREFIX")
|
| 263 | 276 | "A target prefix which will be added to all tool names when searching for toolchain components"
|
| 264 | 277 | |
| ... | ... | @@ -289,13 +302,11 @@ formatOpts = [ |
| 289 | 302 | |
| 290 | 303 | validateOpts :: Opts -> [String]
|
| 291 | 304 | validateOpts opts = mconcat
|
| 292 | - [ assertJust _optTriple "missing --triple flag"
|
|
| 293 | - , assertJust _optOutput "missing --output flag"
|
|
| 305 | + [ ["missing --triple flag" | isNothing (optTriple opts)]
|
|
| 306 | + , ["missing --output flag" | isNothing (optOutput opts)]
|
|
| 307 | + , ["missing --llvm-min-version flag" | isNothing (optLlvmMinVersion opts) ]
|
|
| 308 | + , ["missing --llvm-max-version-excl flag" | isNothing (optLlvmMinVersion opts) ]
|
|
| 294 | 309 | ]
|
| 295 | - where
|
|
| 296 | - assertJust :: Lens Opts (Maybe a) -> String -> [String]
|
|
| 297 | - assertJust lens msg =
|
|
| 298 | - [ msg | Nothing <- pure $ view lens opts ]
|
|
| 299 | 310 | |
| 300 | 311 | main :: IO ()
|
| 301 | 312 | main = do
|
| ... | ... | @@ -448,6 +459,8 @@ mkTarget opts = do |
| 448 | 459 | normalised_triple <- normaliseTriple (fromMaybe (error "missing --triple") (optTriple opts))
|
| 449 | 460 | -- Use Llvm target if specified, otherwise use triple as llvm target
|
| 450 | 461 | let tgtLlvmTarget = fromMaybe normalised_triple (optLlvmTriple opts)
|
| 462 | + let llvmMinVersion = fromMaybe (error "missing --llvm-min-version") (optLlvmMinVersion opts)
|
|
| 463 | + let llvmMaxVersion = fromMaybe (error "missing --llvm-max-version-excl") (optLlvmMaxVersion opts)
|
|
| 451 | 464 | |
| 452 | 465 | (archOs, tgtVendor) <- do
|
| 453 | 466 | cc0 <- findBasicCc (optCc opts)
|
| ... | ... | @@ -480,13 +493,14 @@ mkTarget opts = do |
| 480 | 493 | throwE "Neither a object-merging tool (e.g. ld -r) nor an ar that supports -L is available"
|
| 481 | 494 | |
| 482 | 495 | -- LLVM toolchain
|
| 483 | - llc <- optional $ findProgram "llc" (optLlc opts) ["llc"]
|
|
| 484 | - opt <- optional $ findProgram "opt" (optOpt opts) ["opt"]
|
|
| 485 | - llvmAs <- optional $ findProgram "llvm assembler" (optLlvmAs opts) ["clang"]
|
|
| 496 | + let findLlvmProgram' = findLlvmProgram llvmMinVersion llvmMaxVersion
|
|
| 497 | + llc <- optional $ findLlvmProgram' "llc" (optLlc opts) "llc" True
|
|
| 498 | + opt <- optional $ findLlvmProgram' "opt" (optOpt opts) "opt" True
|
|
| 499 | + llvmAs <- optional $ findLlvmProgram' "llvm assembler" (optLlvmAs opts) "clang" True
|
|
| 486 | 500 | |
| 487 | 501 | -- for windows, also used for cross compiling
|
| 488 | 502 | windres <- optional $ findProgram "windres" (optWindres opts) ["windres"]
|
| 489 | - dlltool <- optional $ findProgram "dlltool" (optDlltool opts) ["llvm-dlltool"]
|
|
| 503 | + dlltool <- optional $ findLlvmProgram' "dlltool" (optDlltool opts) "llvm-dlltool" False
|
|
| 490 | 504 | |
| 491 | 505 | -- Darwin-specific utilities
|
| 492 | 506 | (otool, installNameTool) <-
|
| ... | ... | @@ -16,6 +16,7 @@ module GHC.Toolchain.Program |
| 16 | 16 | , _poPath
|
| 17 | 17 | , _poFlags
|
| 18 | 18 | , findProgram
|
| 19 | + , findLlvmProgram
|
|
| 19 | 20 | -- * Compiler programs
|
| 20 | 21 | , compile
|
| 21 | 22 | , supportsTarget
|
| ... | ... | @@ -23,7 +24,8 @@ module GHC.Toolchain.Program |
| 23 | 24 | |
| 24 | 25 | import Control.Monad
|
| 25 | 26 | import Control.Monad.IO.Class
|
| 26 | -import Data.List (intercalate, isPrefixOf)
|
|
| 27 | +import Data.Char (isDigit)
|
|
| 28 | +import Data.List (find, intercalate, isPrefixOf, tails)
|
|
| 27 | 29 | import Data.Maybe
|
| 28 | 30 | import System.FilePath
|
| 29 | 31 | import System.Directory
|
| ... | ... | @@ -131,17 +133,13 @@ programFromOpt userSpec path flags = Program { prgPath = fromMaybe path (poPath |
| 131 | 133 | -- in the given list of candidates.
|
| 132 | 134 | --
|
| 133 | 135 | -- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags.
|
| 134 | -findProgram :: String
|
|
| 136 | +findProgram :: String -- ^ The program description
|
|
| 135 | 137 | -> ProgOpt -- ^ path provided by user
|
| 136 | 138 | -> [FilePath] -- ^ candidate names
|
| 137 | 139 | -> M Program
|
| 138 | 140 | findProgram description userSpec candidates
|
| 139 | - | Just path <- poPath userSpec = do
|
|
| 140 | - let err =
|
|
| 141 | - [ "Failed to find " ++ description ++ "."
|
|
| 142 | - , "Looked for user-specified program '" ++ path ++ "' in the system search path."
|
|
| 143 | - ]
|
|
| 144 | - toProgram <$> find_it path <|> throwEs err
|
|
| 141 | + | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec
|
|
| 142 | + = findProgramFromProgOpts
|
|
| 145 | 143 | |
| 146 | 144 | | otherwise = do
|
| 147 | 145 | env <- getEnv
|
| ... | ... | @@ -154,17 +152,78 @@ findProgram description userSpec candidates |
| 154 | 152 | [ "Failed to find " ++ description ++ "."
|
| 155 | 153 | , "Looked for one of " ++ show candidates' ++ " in the system search path."
|
| 156 | 154 | ]
|
| 157 | - toProgram <$> oneOf' err (map find_it candidates') <|> throwEs err
|
|
| 155 | + mkProgram userSpec <$> oneOf' err (map findExecutableErr candidates') <|> throwEs err
|
|
| 156 | + |
|
| 157 | +-- | Tries to find an llvm program with the highest supported llvm versions.
|
|
| 158 | +-- This searches for an explicitly versioned executable (postfixed with the llvm version).
|
|
| 159 | +-- If an explicitly versioned executable is not found, then this searches for a non-explicitly
|
|
| 160 | +-- versioned executable. If supported, the llvm version is checked by passing @--version@ to
|
|
| 161 | +-- the executable.
|
|
| 162 | +--
|
|
| 163 | +-- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags.
|
|
| 164 | +findLlvmProgram :: Int -- ^ Min llvm version (inclusive)
|
|
| 165 | + -> Int -- ^ Max llvm version (inclusive)
|
|
| 166 | + -> String -- ^ The llvm program description
|
|
| 167 | + -> ProgOpt -- ^ path provided by user
|
|
| 168 | + -> FilePath -- ^ Candidate name
|
|
| 169 | + -> Bool -- ^ True if the program supports the @--version@ flag and the output
|
|
| 170 | + -- contains the llvm version number in the form @version <LLVM_VERSION>@
|
|
| 171 | + -> M Program
|
|
| 172 | +findLlvmProgram minLlvmVersion maxLlvmVersion description userSpec candidate checkVersion
|
|
| 173 | + | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec
|
|
| 174 | + = findProgramFromProgOpts
|
|
| 175 | + |
|
| 176 | + | otherwise = do
|
|
| 177 | + program <- findProgram description userSpec (versionedCandidates ++ [candidate])
|
|
| 178 | + when checkVersion $ do
|
|
| 179 | + -- Extract the version from the `--version` output
|
|
| 180 | + versionOutput <- readProgramStdout program ["--version"]
|
|
| 181 | + let versionStrPrefix = "version "
|
|
| 182 | + |
|
| 183 | + versionMay :: Maybe Int
|
|
| 184 | + versionMay = fmap (read . takeWhile isDigit . drop (length versionStrPrefix))
|
|
| 185 | + . find (versionStrPrefix `isPrefixOf`)
|
|
| 186 | + $ tails versionOutput
|
|
| 187 | + |
|
| 188 | + errSupportedVersions = prgPath program <> ": We only support llvm " <> show minLlvmVersion <> " upto " <> show maxLlvmVersion <> " (non-inclusive)"
|
|
| 189 | + case versionMay of
|
|
| 190 | + Nothing -> throwE (errSupportedVersions <> " (no version found).")
|
|
| 191 | + Just version -> when
|
|
| 192 | + (version < minLlvmVersion || version > maxLlvmVersion)
|
|
| 193 | + (throwE $ errSupportedVersions <> " (found " <> show version <> ").")
|
|
| 194 | + return program
|
|
| 158 | 195 | where
|
| 159 | - toProgram path = Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) }
|
|
| 160 | - |
|
| 161 | - find_it name = do
|
|
| 162 | - r <- liftIO $ findExecutable name
|
|
| 163 | - case r of
|
|
| 164 | - Nothing -> throwE $ name ++ " not found in search path"
|
|
| 165 | - -- Use the given `prgPath` or candidate name rather than the
|
|
| 166 | - -- absolute path returned by `findExecutable`.
|
|
| 167 | - Just _x -> return name
|
|
| 196 | + versionedCandidates =
|
|
| 197 | + [ candidate <> postfix
|
|
| 198 | + | llvmVersion <- show <$> [minLlvmVersion, minLlvmVersion-1 .. maxLlvmVersion]
|
|
| 199 | + , postfix <-
|
|
| 200 | + [ "-" <> llvmVersion
|
|
| 201 | + , "-" <> llvmVersion <> ".0"
|
|
| 202 | + , llvmVersion
|
|
| 203 | + ]
|
|
| 204 | + ]
|
|
| 205 | + |
|
| 206 | +maybeFindProgramFromProgOpts :: String -> ProgOpt -> Maybe (M Program)
|
|
| 207 | +maybeFindProgramFromProgOpts description userSpec = case poPath userSpec of
|
|
| 208 | + Nothing -> Nothing
|
|
| 209 | + Just path -> do
|
|
| 210 | + let err =
|
|
| 211 | + [ "Failed to find " ++ description ++ "."
|
|
| 212 | + , "Looked for user-specified program '" ++ path ++ "' in the system search path."
|
|
| 213 | + ]
|
|
| 214 | + Just (mkProgram userSpec <$> findExecutableErr path <|> throwEs err)
|
|
| 215 | + |
|
| 216 | +mkProgram :: ProgOpt -> FilePath -> Program
|
|
| 217 | +mkProgram userSpec path = Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) }
|
|
| 218 | + |
|
| 219 | +findExecutableErr :: String -> M FilePath
|
|
| 220 | +findExecutableErr name = do
|
|
| 221 | + r <- liftIO $ findExecutable name
|
|
| 222 | + case r of
|
|
| 223 | + Nothing -> throwE $ name ++ " not found in search path"
|
|
| 224 | + -- Use the given `prgPath` or candidate name rather than the
|
|
| 225 | + -- absolute path returned by `findExecutable`.
|
|
| 226 | + Just _x -> return name
|
|
| 168 | 227 | |
| 169 | 228 | -------------------- Compiling utilities --------------------
|
| 170 | 229 |