David Eichmann pushed to branch wip/davide/ghc-toolchain-llvm-versions at Glasgow Haskell Compiler / GHC Commits: 1c662025 by David Eichmann at 2026-05-18T10:23:05+01:00 ghc-toolchain: implement llvm program versioning logic - - - - - 3 changed files: - m4/ghc_toolchain.m4 - utils/ghc-toolchain/exe/Main.hs - utils/ghc-toolchain/src/GHC/Toolchain/Program.hs Changes: ===================================== m4/ghc_toolchain.m4 ===================================== @@ -143,7 +143,17 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN_BIN],[ GHC_TOOLCHAIN_BIN="bin/${CrossCompilePrefix}ghc-toolchain-bin" ;; NO) - # We're in the source tree, so compile ghc-toolchain + # We're in the source tree + + # Check for consistency of LLVM versions + hs_min_llvm=$(sed -n 's/^minLlvmVersion = //p' utils/ghc-toolchain/src/GHC/Toolchain/Program.hs) + hs_max_llvm=$(sed -n 's/^maxLlvmVersionExcl = //p' utils/ghc-toolchain/src/GHC/Toolchain/Program.hs) + test "$hs_min_llvm" = "$LlvmMinVersion" || \ + AC_MSG_ERROR([minLlvmVersion ($hs_min_llvm) in utils/ghc-toolchain/src/GHC/Toolchain/Program.hs must equal LlvmMinVersion ($LlvmMinVersion) in configure.ac]) + test "$hs_max_llvm" = "$LlvmMaxVersion" || \ + AC_MSG_ERROR([maxLlvmVersionExcl ($hs_max_llvm) in utils/ghc-toolchain/src/GHC/Toolchain/Program.hs must equal LlvmMaxVersion ($LlvmMaxVersion) in configure.ac]) + + # Compile ghc-toolchain "$GHC" -v0 \ -ilibraries/ghc-platform/src -iutils/ghc-toolchain/src \ -XNoImplicitPrelude \ ===================================== utils/ghc-toolchain/exe/Main.hs ===================================== @@ -289,13 +289,9 @@ formatOpts = [ validateOpts :: Opts -> [String] validateOpts opts = mconcat - [ assertJust _optTriple "missing --triple flag" - , assertJust _optOutput "missing --output flag" + [ ["missing --triple flag" | isNothing (optTriple opts)] + , ["missing --output flag" | isNothing (optOutput opts)] ] - where - assertJust :: Lens Opts (Maybe a) -> String -> [String] - assertJust lens msg = - [ msg | Nothing <- pure $ view lens opts ] main :: IO () main = do @@ -480,13 +476,13 @@ mkTarget opts = do throwE "Neither a object-merging tool (e.g. ld -r) nor an ar that supports -L is available" -- LLVM toolchain - llc <- optional $ findProgram "llc" (optLlc opts) ["llc"] - opt <- optional $ findProgram "opt" (optOpt opts) ["opt"] - llvmAs <- optional $ findProgram "llvm assembler" (optLlvmAs opts) ["clang"] + llc <- optional $ findLlvmProgram "llc" (optLlc opts) "llc" True + opt <- optional $ findLlvmProgram "opt" (optOpt opts) "opt" True + llvmAs <- optional $ findLlvmProgram "llvm assembler" (optLlvmAs opts) "clang" True -- for windows, also used for cross compiling windres <- optional $ findProgram "windres" (optWindres opts) ["windres"] - dlltool <- optional $ findProgram "dlltool" (optDlltool opts) ["llvm-dlltool"] + dlltool <- optional $ findLlvmProgram "dlltool" (optDlltool opts) "llvm-dlltool" False -- Darwin-specific utilities (otool, installNameTool) <- ===================================== utils/ghc-toolchain/src/GHC/Toolchain/Program.hs ===================================== @@ -16,6 +16,7 @@ module GHC.Toolchain.Program , _poPath , _poFlags , findProgram + , findLlvmProgram -- * Compiler programs , compile , supportsTarget @@ -23,7 +24,8 @@ module GHC.Toolchain.Program import Control.Monad import Control.Monad.IO.Class -import Data.List (intercalate, isPrefixOf) +import Data.Char (isDigit) +import Data.List (find, intercalate, isPrefixOf, tails) import Data.Maybe import System.FilePath import System.Directory @@ -131,17 +133,13 @@ programFromOpt userSpec path flags = Program { prgPath = fromMaybe path (poPath -- in the given list of candidates. -- -- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags. -findProgram :: String +findProgram :: String -- ^ The program description -> ProgOpt -- ^ path provided by user -> [FilePath] -- ^ candidate names -> M Program findProgram description userSpec candidates - | Just path <- poPath userSpec = do - let err = - [ "Failed to find " ++ description ++ "." - , "Looked for user-specified program '" ++ path ++ "' in the system search path." - ] - toProgram <$> find_it path <|> throwEs err + | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec + = findProgramFromProgOpts | otherwise = do env <- getEnv @@ -154,17 +152,91 @@ findProgram description userSpec candidates [ "Failed to find " ++ description ++ "." , "Looked for one of " ++ show candidates' ++ " in the system search path." ] - toProgram <$> oneOf' err (map find_it candidates') <|> throwEs err + path <- oneOf' err (map findExecutableErr candidates') + return Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) } + +-- Note that @configure.ac@ checks these llvm version constants (using @sed@) to +-- ensure they are the same as the @$LlvmMinVersion@ and @$LlvmMaxVersion@ +-- defined in @configure.ac@. + +-- Min llvm version (inclusive) +minLlvmVersion :: Int +minLlvmVersion = 13 + +-- Max llvm version (exclusive) +maxLlvmVersionExcl :: Int +maxLlvmVersionExcl = 23 + +-- Max llvm version (inclusive) +maxLlvmVersion :: Int +maxLlvmVersion = maxLlvmVersionExcl - 1 + +-- | Tries to find an llvm program with the highest supported llvm versions. +-- This searches for an explicitly versioned executable (postfixed with the llvm version). +-- If an explicitly versioned executable is not found, then this searches for a non-explicitly +-- versioned executable. If supported, the llvm version is checked by passing @--version@ to +-- the executable. +-- +-- If the 'ProgOpt' program flags are unspecified the program will have an empty list of flags. +findLlvmProgram :: String -- ^ The llvm program description + -> ProgOpt -- ^ path provided by user + -> FilePath -- ^ Candidate name + -> Bool -- ^ True if the program supports the @--version@ flag and the output + -- contains the llvm version number in the form @version <LLVM_VERSION>@ + -> M Program +findLlvmProgram description userSpec candidate checkVersion + | Just findProgramFromProgOpts <- maybeFindProgramFromProgOpts description userSpec + = findProgramFromProgOpts + + | otherwise = do + program <- findProgram description userSpec (versionedCandidates ++ [candidate]) + when checkVersion $ do + -- Extract the version from the `--version` output + versionOutput <- readProgramStdout program ["--version"] + let versionStrPrefix = "version " + + versionMay :: Maybe Int + versionMay = fmap (read . takeWhile isDigit . drop (length versionStrPrefix)) + . find (versionStrPrefix `isPrefixOf`) + $ tails versionOutput + + errSupportedVersions = prgPath program <> ": We only support llvm " <> show minLlvmVersion <> " upto " <> show maxLlvmVersion <> " (non-inclusive)" + case versionMay of + Nothing -> throwE (errSupportedVersions <> " (no version found).") + Just version -> when + (version < minLlvmVersion || version > maxLlvmVersion) + (throwE $ errSupportedVersions <> " (found " <> show version <> ").") + return program where - toProgram path = Program { prgPath = path, prgFlags = fromMaybe [] (poFlags userSpec) } - - find_it name = do - r <- liftIO $ findExecutable name - case r of - Nothing -> throwE $ name ++ " not found in search path" - -- Use the given `prgPath` or candidate name rather than the - -- absolute path returned by `findExecutable`. - Just _x -> return name + versionedCandidates = + [ candidate <> postfix + | llvmVersion <- show <$> [maxLlvmVersion, maxLlvmVersion-1 .. minLlvmVersion] + , postfix <- + [ "-" <> llvmVersion + , "-" <> llvmVersion <> ".0" + , llvmVersion + ] + ] + +maybeFindProgramFromProgOpts :: String -> ProgOpt -> Maybe (M Program) +maybeFindProgramFromProgOpts description userSpec = case poPath userSpec of + Nothing -> Nothing + Just path -> Just $ do + let err = + [ "Failed to find " ++ description ++ "." + , "Looked for user-specified program '" ++ path ++ "' in the system search path." + ] + path' <- findExecutableErr path <|> throwEs err + return Program { prgPath = path', prgFlags = fromMaybe [] (poFlags userSpec) } + +findExecutableErr :: String -> M FilePath +findExecutableErr name = do + r <- liftIO $ findExecutable name + case r of + Nothing -> throwE $ name ++ " not found in search path" + -- Use the given `prgPath` or candidate name rather than the + -- absolute path returned by `findExecutable`. + Just _x -> return name -------------------- Compiling utilities -------------------- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1c662025970f29945436f7e33047a219... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/1c662025970f29945436f7e33047a219... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
David Eichmann (@DavidEichmann)