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
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
=====================================
@@ -55,6 +55,8 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN],
rm -f acargs
echo "--triple=$HostPlatform" >> acargs
echo "--output=$1/default.host.target.ghc-toolchain" >> acargs
+ echo "--llvm-min-version=$LlvmMinVersion" >> acargs
+ echo "--llvm-max-version-excl=$LlvmMaxVersion" >> acargs
echo "--cc=$CC_STAGE0" >> acargs
echo "--cc-link=$CC_STAGE0" >> acargs
echo "--ar=$AR_STAGE0" >> acargs
@@ -82,6 +84,8 @@ AC_DEFUN([FIND_GHC_TOOLCHAIN],
echo "--triple=$target" >> acargs
echo "--output=$1/default.target.ghc-toolchain" >> acargs
echo "--llvm-triple=$LlvmTarget" >> acargs
+ echo "--llvm-min-version=$LlvmMinVersion" >> acargs
+ echo "--llvm-max-version-excl=$LlvmMaxVersion" >> acargs
echo "--cc=$CC" >> acargs
echo "--cxx=$CXX" >> acargs
echo "--cpp=$CPPCmd" >> acargs
=====================================
utils/ghc-toolchain/exe/Main.hs
=====================================
@@ -39,6 +39,8 @@ data Opts = Opts
, optTargetPrefix :: Maybe String
, optLocallyExecutable :: Maybe Bool
, optLlvmTriple :: Maybe String
+ , optLlvmMinVersion :: Maybe Int -- ^ Minimum supported LLVM version (inclusive)
+ , optLlvmMaxVersion :: Maybe Int -- ^ Maximum supported LLVM version (inclusive)
, optOutput :: Maybe String
, optCc :: ProgOpt
, optCxx :: ProgOpt
@@ -98,6 +100,8 @@ emptyOpts = Opts
, optTargetPrefix = Nothing
, optLocallyExecutable = Nothing
, optLlvmTriple = Nothing
+ , optLlvmMinVersion = Nothing
+ , optLlvmMaxVersion = Nothing
, optOutput = Nothing
, optCc = po0
, optCxx = po0
@@ -163,6 +167,10 @@ _optTriple = Lens optTriple (\x o -> o {optTriple=x})
_optLlvmTriple :: Lens Opts (Maybe String)
_optLlvmTriple = Lens optLlvmTriple (\x o -> o {optLlvmTriple=x})
+_optLlvmMinVersion, _optLlvmMaxVersion :: Lens Opts (Maybe Int)
+_optLlvmMinVersion = Lens optLlvmMinVersion (\x o -> o {optLlvmMinVersion=x})
+_optLlvmMaxVersion = Lens optLlvmMaxVersion (\x o -> o {optLlvmMaxVersion=x})
+
_optOutput :: Lens Opts (Maybe String)
_optOutput = Lens optOutput (\x o -> o {optOutput=x})
@@ -192,6 +200,8 @@ options =
[ tripleOpt
, targetPrefixOpt
, llvmTripleOpt
+ , llvmMinVersionOpt
+ , llvmMaxVersionOpt
, verbosityOpt
, keepTempOpt
, outputOpt
@@ -259,6 +269,9 @@ options =
tripleOpt = Option ['t'] ["triple"] (ReqArg (set _optTriple . Just) "TRIPLE") "Target triple"
llvmTripleOpt = Option [] ["llvm-triple"] (ReqArg (set _optLlvmTriple . Just) "LLVM-TRIPLE") "LLVM Target triple"
+ llvmMinVersionOpt = Option [] ["llvm-min-version"] (ReqArg (set _optLlvmMinVersion . Just . read) "LLVM-MIN-VERSION") "LLVM min version (inclusive)"
+ llvmMaxVersionOpt = Option [] ["llvm-max-version-excl"] (ReqArg (set _optLlvmMaxVersion . Just . subtract 1 . read) "LLVM-MAX-VERSION") "LLVM max version (exclusive)"
+
targetPrefixOpt = Option ['T'] ["target-prefix"] (ReqArg (set _optTargetPrefix . Just) "PREFIX")
"A target prefix which will be added to all tool names when searching for toolchain components"
@@ -289,13 +302,11 @@ 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)]
+ , ["missing --llvm-min-version flag" | isNothing (optLlvmMinVersion opts) ]
+ , ["missing --llvm-max-version-excl flag" | isNothing (optLlvmMinVersion opts) ]
]
- where
- assertJust :: Lens Opts (Maybe a) -> String -> [String]
- assertJust lens msg =
- [ msg | Nothing <- pure $ view lens opts ]
main :: IO ()
main = do
@@ -448,6 +459,8 @@ mkTarget opts = do
normalised_triple <- normaliseTriple (fromMaybe (error "missing --triple") (optTriple opts))
-- Use Llvm target if specified, otherwise use triple as llvm target
let tgtLlvmTarget = fromMaybe normalised_triple (optLlvmTriple opts)
+ let llvmMinVersion = fromMaybe (error "missing --llvm-min-version") (optLlvmMinVersion opts)
+ let llvmMaxVersion = fromMaybe (error "missing --llvm-max-version-excl") (optLlvmMaxVersion opts)
(archOs, tgtVendor) <- do
cc0 <- findBasicCc (optCc opts)
@@ -480,13 +493,14 @@ 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"]
+ let findLlvmProgram' = findLlvmProgram llvmMinVersion llvmMaxVersion
+ 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,78 @@ 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
+ mkProgram userSpec <$> oneOf' err (map findExecutableErr candidates') <|> throwEs err
+
+-- | 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 :: Int -- ^ Min llvm version (inclusive)
+ -> Int -- ^ Max llvm version (inclusive)
+ -> 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
participants (1)
-
David Eichmann (@DavidEichmann)