David Eichmann pushed to branch wip/davide/ghc-toolchain-llvm-versions at Glasgow Haskell Compiler / GHC

Commits:

3 changed files:

Changes:

  • m4/ghc_toolchain.m4
    ... ... @@ -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
    

  • utils/ghc-toolchain/exe/Main.hs
    ... ... @@ -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) <-
    

  • utils/ghc-toolchain/src/GHC/Toolchain/Program.hs
    ... ... @@ -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