diff options
Diffstat (limited to 'compiler/GHC')
-rw-r--r-- | compiler/GHC/Driver/Hooks.hs | 4 | ||||
-rw-r--r-- | compiler/GHC/Driver/Main.hs | 2 | ||||
-rw-r--r-- | compiler/GHC/Driver/Make.hs | 3 | ||||
-rw-r--r-- | compiler/GHC/Driver/Phases.hs | 41 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline.hs | 2254 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline.hs-boot | 13 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline/Execute.hs | 1263 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline/Monad.hs | 189 | ||||
-rw-r--r-- | compiler/GHC/Driver/Pipeline/Phases.hs | 52 | ||||
-rw-r--r-- | compiler/GHC/Driver/Session.hs | 4 |
10 files changed, 1896 insertions, 1929 deletions
diff --git a/compiler/GHC/Driver/Hooks.hs b/compiler/GHC/Driver/Hooks.hs index 6730daca5c..e4f4262d5e 100644 --- a/compiler/GHC/Driver/Hooks.hs +++ b/compiler/GHC/Driver/Hooks.hs @@ -33,7 +33,7 @@ import GHC.Prelude import GHC.Driver.Env import GHC.Driver.Session -import GHC.Driver.Pipeline.Monad +import GHC.Driver.Pipeline.Phases import GHC.Hs.Decls import GHC.Hs.Binds @@ -137,7 +137,7 @@ data Hooks = Hooks , hscCompileCoreExprHook :: !(Maybe (HscEnv -> (SrcSpan, Maybe ModuleNameWithIsBoot) -> CoreExpr -> IO ForeignHValue)) , ghcPrimIfaceHook :: !(Maybe ModIface) - , runPhaseHook :: !(Maybe (PhasePlus -> FilePath -> CompPipeline (PhasePlus, FilePath))) + , runPhaseHook :: !(Maybe PhaseHook) , runMetaHook :: !(Maybe (MetaHook TcM)) , linkHook :: !(Maybe (GhcLink -> DynFlags -> Bool -> HomePackageTable -> IO SuccessFlag)) diff --git a/compiler/GHC/Driver/Main.hs b/compiler/GHC/Driver/Main.hs index c3c7cd9e31..3d55e77191 100644 --- a/compiler/GHC/Driver/Main.hs +++ b/compiler/GHC/Driver/Main.hs @@ -1564,7 +1564,7 @@ hscSimpleIface' tc_result summary mb_old_iface = do -- | Compile to hard-code. hscGenHardCode :: HscEnv -> CgGuts -> ModLocation -> FilePath -> IO (FilePath, Maybe FilePath, [(ForeignSrcLang, FilePath)], CgInfos) - -- ^ @Just f@ <=> _stub.c is f + -- ^ @Just f@ <=> _stub.c is f hscGenHardCode hsc_env cgguts location output_filename = do let CgGuts{ -- This is the last use of the ModGuts in a compilation. -- From now on, we just use the bits we need. diff --git a/compiler/GHC/Driver/Make.hs b/compiler/GHC/Driver/Make.hs index ae12059726..46bb160cfc 100644 --- a/compiler/GHC/Driver/Make.hs +++ b/compiler/GHC/Driver/Make.hs @@ -7,6 +7,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} +{-# LANGUAGE FlexibleContexts #-} -- ----------------------------------------------------------------------------- -- @@ -1558,7 +1559,7 @@ upsweep_mod hsc_env mHscMessage old_hpt summary mod_index nmods compile_it :: Maybe Linkable -> IO HomeModInfo compile_it mb_linkable = - compileOne' Nothing mHscMessage hsc_env summary mod_index nmods + compileOne' mHscMessage hsc_env summary mod_index nmods mb_old_iface mb_linkable in diff --git a/compiler/GHC/Driver/Phases.hs b/compiler/GHC/Driver/Phases.hs index 07ec166ca3..2b4e234d12 100644 --- a/compiler/GHC/Driver/Phases.hs +++ b/compiler/GHC/Driver/Phases.hs @@ -1,5 +1,3 @@ - - ----------------------------------------------------------------------------- -- -- GHC Driver @@ -10,10 +8,13 @@ module GHC.Driver.Phases ( Phase(..), - happensBefore, eqPhase, anyHsc, isStopLn, + happensBefore, eqPhase, isStopLn, startPhase, phaseInputExt, + StopPhase(..), + stopPhaseToPhase, + isHaskellishSuffix, isHaskellSrcSuffix, isBackpackishSuffix, @@ -67,6 +68,19 @@ import System.FilePath linker | other | - | a.out -} +-- Phases we can actually stop after +data StopPhase = StopPreprocess -- -E + | StopC -- -C + | StopAs -- -S + | NoStop -- -c + +stopPhaseToPhase :: StopPhase -> Phase +stopPhaseToPhase StopPreprocess = anyHsc +stopPhaseToPhase StopC = HCc +stopPhaseToPhase StopAs = As False +stopPhaseToPhase NoStop = StopLn + +-- | Untyped Phase description data Phase = Unlit HscSource | Cpp HscSource @@ -86,7 +100,6 @@ data Phase | MergeForeign -- merge in the foreign object files -- The final phase is a pseudo-phase that tells the pipeline to stop. - -- There is no runPhase case for it. | StopLn -- Stop, but linking will follow, so generate .o file deriving (Eq, Show) @@ -122,22 +135,8 @@ eqPhase Ccxx Ccxx = True eqPhase Cobjcxx Cobjcxx = True eqPhase _ _ = False -{- Note [Partial ordering on phases] - -We want to know which phases will occur before which others. This is used for -sanity checking, to ensure that the pipeline will stop at some point (see -GHC.Driver.Pipeline.runPipeline). - -A < B iff A occurs before B in a normal compilation pipeline. - -There is explicitly not a total ordering on phases, because in registerised -builds, the phase `HsC` doesn't happen before nor after any other phase. - -Although we check that a normal user doesn't set the stop_phase to HsC through -use of -C with registerised builds (in Main.checkOptions), it is still -possible for a ghc-api user to do so. So be careful when using the function -happensBefore, and don't think that `not (a <= b)` implies `b < a`. --} +-- MP: happensBefore is only used in preprocessPipeline, that usage should +-- be refactored and this usage removed. happensBefore :: Platform -> Phase -> Phase -> Bool happensBefore platform p1 p2 = p1 `happensBefore'` p2 where StopLn `happensBefore'` _ = False @@ -211,7 +210,7 @@ phaseInputExt (Cpp _) = "lpp" -- intermediate only phaseInputExt (HsPp _) = "hscpp" -- intermediate only phaseInputExt (Hsc _) = "hspp" -- intermediate only -- NB: as things stand, phaseInputExt (Hsc x) must not evaluate x - -- because runPipeline uses the StopBefore phase to pick the + -- because runPhase uses the StopBefore phase to pick the -- output filename. That could be fixed, but watch out. phaseInputExt HCc = "hc" phaseInputExt Ccxx = "cpp" diff --git a/compiler/GHC/Driver/Pipeline.hs b/compiler/GHC/Driver/Pipeline.hs index 3825019d8b..cdd22b1388 100644 --- a/compiler/GHC/Driver/Pipeline.hs +++ b/compiler/GHC/Driver/Pipeline.hs @@ -1,13 +1,12 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE LambdaCase #-} - -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE FlexibleContexts #-} ----------------------------------------------------------------------------- -- @@ -18,82 +17,85 @@ ----------------------------------------------------------------------------- module GHC.Driver.Pipeline ( - -- Run a series of compilation steps in a pipeline, for a - -- collection of source files. + -- * Run a series of compilation steps in a pipeline, for a + -- collection of source files. oneShot, compileFile, - -- Interfaces for the compilation manager (interpreted/batch-mode) + -- * Interfaces for the compilation manager (interpreted/batch-mode) preprocess, compileOne, compileOne', - link, + compileForeign, compileEmptyStub, + + -- * Linking + link, linkingNeeded, checkLinkInfo, + + -- * PipeEnv + PipeEnv(..), mkPipeEnv, phaseOutputFilenameNew, + + -- * Running individual phases + TPhase(..), runPhase, + hscPostBackendPhase, + + -- * Constructing Pipelines + TPipelineClass, MonadUse(..), + + preprocessPipeline, fullPipeline, hscPipeline, hscBackendPipeline, hscPostBackendPipeline, + hscGenBackendPipeline, asPipeline, viaCPipeline, cmmCppPipeline, cmmPipeline, + llvmPipeline, llvmLlcPipeline, llvmManglePipeline, pipelineStart, + + -- * Default method of running a pipeline + runPipeline +) where - -- Exports for hooks to override runPhase and link - PhasePlus(..), CompPipeline(..), PipeEnv(..), PipeState(..), - phaseOutputFilename, getOutputFilename, getPipeState, getPipeEnv, - hscPostBackendPhase, getLocation, setModLocation, setDynFlags, - runPhase, - doCpp, - linkingNeeded, checkLinkInfo, writeInterfaceOnlyMode - ) where #include "ghcplatform.h" import GHC.Prelude import GHC.Platform -import GHC.Tc.Types -import GHC.Tc.Utils.Monad hiding ( getImports ) +import GHC.Utils.Monad ( MonadIO(liftIO), mapMaybeM ) import GHC.Driver.Main import GHC.Driver.Env hiding ( Hsc ) import GHC.Driver.Errors import GHC.Driver.Errors.Types import GHC.Driver.Pipeline.Monad -import GHC.Driver.Config.Parser (initParserOpts) import GHC.Driver.Config.Diagnostic import GHC.Driver.Phases +import GHC.Driver.Pipeline.Phases import GHC.Driver.Session import GHC.Driver.Backend import GHC.Driver.Ppr import GHC.Driver.Hooks import GHC.Platform.Ways -import GHC.Platform.ArchOS - -import GHC.Parser.Header import GHC.SysTools import GHC.Utils.TmpFs import GHC.Linker.ExtraObj -import GHC.Linker.Dynamic import GHC.Linker.Static import GHC.Linker.Types import GHC.Utils.Outputable import GHC.Utils.Error -import GHC.Utils.Fingerprint import GHC.Utils.Panic -import GHC.Utils.Panic.Plain import GHC.Utils.Misc import GHC.Utils.Exception as Exception import GHC.Utils.Logger -import GHC.CmmToLlvm ( llvmFixupAsm, llvmVersionList ) import qualified GHC.LanguageExtensions as LangExt -import GHC.Settings import GHC.Data.FastString ( mkFastString ) -import GHC.Data.StringBuffer ( hGetStringBuffer, hPutStringBuffer ) +import GHC.Data.StringBuffer ( hPutStringBuffer ) import GHC.Data.Maybe ( expectJust ) import GHC.Iface.Make ( mkFullIface ) import GHC.Runtime.Loader ( initializePlugins ) -import GHC.Types.Basic ( SuccessFlag(..) ) +import GHC.Types.Basic ( SuccessFlag(..), ForeignSrcLang(..) ) import GHC.Types.Error ( singleMessage, getMessages ) -import GHC.Types.Name.Env import GHC.Types.Target import GHC.Types.SrcLoc import GHC.Types.SourceFile @@ -101,7 +103,8 @@ import GHC.Types.SourceError import GHC.Unit import GHC.Unit.Env -import GHC.Unit.Finder +--import GHC.Unit.Finder +--import GHC.Unit.State import GHC.Unit.Module.ModSummary import GHC.Unit.Module.ModIface import GHC.Unit.Module.Graph (needsTemplateHaskellOrQQ) @@ -113,13 +116,14 @@ import System.FilePath import System.IO import Control.Monad import qualified Control.Monad.Catch as MC (handle) -import Data.IORef -import Data.List ( isInfixOf, intercalate ) import Data.Maybe -import Data.Version import Data.Either ( partitionEithers ) import Data.Time ( getCurrentTime ) +import GHC.Driver.Pipeline.Execute + +-- Simpler type synonym for actions in the pipeline monad +type P m = TPipelineClass TPhase m -- --------------------------------------------------------------------------- -- Pre-process @@ -141,17 +145,10 @@ preprocess hsc_env input_fn mb_input_buf mb_phase = MC.handle handler $ fmap Right $ do massertPpr (isJust mb_phase || isHaskellSrcFilename input_fn) (text input_fn) - (dflags, fp, mb_iface, mb_linkable) <- runPipeline anyHsc hsc_env (input_fn, mb_input_buf, fmap RealPhase mb_phase) - Nothing - -- We keep the processed file for the whole session to save on - -- duplicated work in ghci. - (Temporary TFL_GhcSession) - Nothing{-no ModLocation-} - []{-no foreign objects-} - -- We stop before Hsc phase so we shouldn't generate an interface - massert (isNothing mb_iface) - massert (isNothing mb_linkable) - return (dflags, fp) + input_fn_final <- mkInputFn + let preprocess_pipeline = preprocessPipeline pipe_env (setDumpPrefix pipe_env hsc_env) input_fn_final + runPipeline (hsc_hooks hsc_env) preprocess_pipeline + where srcspan = srcLocSpan $ mkSrcLoc (mkFastString input_fn) 1 1 handler (ProgramError msg) = @@ -173,6 +170,24 @@ preprocess hsc_env input_fn mb_input_buf mb_phase = -> Just (DriverPsHeaderMessage (PsHeaderMessage msg)) _ -> Nothing + pipe_env = mkPipeEnv StopPreprocess input_fn (Temporary TFL_GhcSession) + mkInputFn = + case mb_input_buf of + Just input_buf -> do + fn <- newTempName (hsc_logger hsc_env) + (hsc_tmpfs hsc_env) + (hsc_dflags hsc_env) + TFL_CurrentModule + ("buf_" ++ src_suffix pipe_env) + hdl <- openBinaryFile fn WriteMode + -- Add a LINE pragma so reported source locations will + -- mention the real input file, not this temp file. + hPutStrLn hdl $ "{-# LINE 1 \""++ input_fn ++ "\"#-}" + hPutStringBuffer hdl input_buf + hClose hdl + return fn + Nothing -> return input_fn + -- --------------------------------------------------------------------------- -- | Compile @@ -187,6 +202,7 @@ preprocess hsc_env input_fn mb_input_buf mb_phase = -- -- NB. No old interface can also mean that the source has changed. + compileOne :: HscEnv -> ModSummary -- ^ summary for module being compiled -> Int -- ^ module N ... @@ -195,10 +211,9 @@ compileOne :: HscEnv -> Maybe Linkable -- ^ old linkable, if we have one -> IO HomeModInfo -- ^ the complete HomeModInfo, if successful -compileOne = compileOne' Nothing (Just batchMsg) +compileOne = compileOne' (Just batchMsg) -compileOne' :: Maybe TcGblEnv - -> Maybe Messager +compileOne' :: Maybe Messager -> HscEnv -> ModSummary -- ^ summary for module being compiled -> Int -- ^ module N ... @@ -207,7 +222,7 @@ compileOne' :: Maybe TcGblEnv -> Maybe Linkable -- ^ old linkable, if we have one -> IO HomeModInfo -- ^ the complete HomeModInfo, if successful -compileOne' m_tc_result mHscMessage +compileOne' mHscMessage hsc_env0 summary mod_index nmods mb_old_iface mb_old_linkable = do @@ -222,25 +237,14 @@ compileOne' m_tc_result mHscMessage [ml_obj_file $ ms_location summary] plugin_hsc_env <- initializePlugins hsc_env (Just (ms_mnwib summary)) - let runPostTc = compileOnePostTc plugin_hsc_env summary - - case m_tc_result of - Just tc_result - | not always_do_basic_recompilation_check -> do - runPostTc (FrontendTypecheck tc_result) emptyMessages Nothing - _ -> do - status <- hscRecompStatus mHscMessage plugin_hsc_env summary - mb_old_iface mb_old_linkable (mod_index, nmods) - - case status of - HscUpToDate iface old_linkable -> do - massert ( isJust old_linkable || isNoLink (ghcLink dflags) ) - -- See Note [ModDetails and --make mode] - details <- initModDetails plugin_hsc_env summary iface - return $! HomeModInfo iface details old_linkable - HscRecompNeeded mb_old_hash -> do - (tc_result, warnings) <- hscTypecheckAndGetWarnings plugin_hsc_env summary - runPostTc tc_result warnings mb_old_hash + let pipe_env = mkPipeEnv NoStop input_fn pipelineOutput + status <- hscRecompStatus mHscMessage plugin_hsc_env summary + mb_old_iface mb_old_linkable (mod_index, nmods) + let pipeline = hscPipeline pipe_env (setDumpPrefix pipe_env plugin_hsc_env, summary, status) + (iface, old_linkable) <- runPipeline (hsc_hooks hsc_env) pipeline + -- See Note [ModDetails and --make mode] + details <- initModDetails plugin_hsc_env summary iface + return $! HomeModInfo iface details old_linkable where lcl_dflags = ms_hspp_opts summary location = ms_location summary @@ -252,6 +256,11 @@ compileOne' m_tc_result mHscMessage isProfWay = any (== WayProf) (ways lcl_dflags) internalInterpreter = not (gopt Opt_ExternalInterpreter lcl_dflags) + pipelineOutput = case bcknd of + Interpreter -> NoOutputFile + NoBackend -> NoOutputFile + _ -> Persistent + logger = hsc_logger hsc_env0 tmpfs = hsc_tmpfs hsc_env0 @@ -295,122 +304,6 @@ compileOne' m_tc_result mHscMessage dflags = dflags3 { includePaths = addImplicitQuoteInclude old_paths [current_dir] } hsc_env = hscSetFlags dflags hsc_env0 - always_do_basic_recompilation_check = case bcknd of - Interpreter -> True - _ -> False - --- | Do the post typechecking compilation of a module in the --make mode -compileOnePostTc - :: HscEnv - -> ModSummary - -> FrontendResult - -> WarningMessages - -> Maybe Fingerprint - -> IO HomeModInfo -compileOnePostTc hsc_env summary tc_result warnings mb_old_hash = do - output_fn <- getOutputFilename logger tmpfs next_phase - (Temporary TFL_CurrentModule) - basename dflags next_phase (Just location) - (_, _, Just iface, mb_linkable) <- runPipeline StopLn hsc_env - (output_fn, - Nothing, - Just (HscPostTc summary tc_result warnings mb_old_hash)) - (Just basename) - pipelineOutput - (Just location) - [] - -- TODO: figure out a way to set this in runPipeline for HsSrcFile - mLinkable <- case () of - _ | Just l <- mb_linkable -> return $ Just l - | bcknd == NoBackend -> return Nothing - | src_flavour == HsSrcFile -> do - -- The object filename comes from the ModLocation - o_time <- getModificationUTCTime object_filename - let !linkable = LM o_time this_mod [DotO object_filename] - return $ Just linkable - | otherwise -> return Nothing - -- See Note [ModDetails and --make mode] - details <- initModDetails hsc_env summary iface - return $! HomeModInfo iface details mLinkable - - where dflags = hsc_dflags hsc_env - this_mod = ms_mod summary - location = ms_location summary - input_fn = expectJust "compile:hs" (ml_hs_file location) - - logger = hsc_logger hsc_env - tmpfs = hsc_tmpfs hsc_env - src_flavour = ms_hsc_src summary - next_phase = hscPostBackendPhase src_flavour bcknd - bcknd = backend dflags - object_filename = ml_obj_file location - - basename = dropExtension input_fn - - pipelineOutput = case bcknd of - Interpreter -> NoOutputFile - NoBackend -> NoOutputFile - _ -> Persistent - ------------------------------------------------------------------------------ --- stub .h and .c files (for foreign export support), and cc files. - --- The _stub.c file is derived from the haskell source file, possibly taking --- into account the -stubdir option. --- --- The object file created by compiling the _stub.c file is put into a --- temporary file, which will be later combined with the main .o file --- (see the MergeForeigns phase). --- --- Moreover, we also let the user emit arbitrary C/C++/ObjC/ObjC++ files --- from TH, that are then compiled and linked to the module. This is --- useful to implement facilities such as inline-c. - -compileForeign :: HscEnv -> ForeignSrcLang -> FilePath -> IO FilePath -compileForeign _ RawObject object_file = return object_file -compileForeign hsc_env lang stub_c = do - let phase = case lang of - LangC -> Cc - LangCxx -> Ccxx - LangObjc -> Cobjc - LangObjcxx -> Cobjcxx - LangAsm -> As True -- allow CPP -#if __GLASGOW_HASKELL__ < 811 - RawObject -> panic "compileForeign: should be unreachable" -#endif - (_, stub_o, _, _) <- runPipeline StopLn hsc_env - (stub_c, Nothing, Just (RealPhase phase)) - Nothing (Temporary TFL_GhcSession) - Nothing{-no ModLocation-} - [] - return stub_o - -compileStub :: HscEnv -> FilePath -> IO FilePath -compileStub hsc_env stub_c = compileForeign hsc_env LangC stub_c - -compileEmptyStub :: DynFlags -> HscEnv -> FilePath -> ModLocation -> ModuleName -> IO () -compileEmptyStub dflags hsc_env basename location mod_name = do - -- To maintain the invariant that every Haskell file - -- compiles to object code, we make an empty (but - -- valid) stub object file for signatures. However, - -- we make sure this object file has a unique symbol, - -- so that ranlib on OS X doesn't complain, see - -- https://gitlab.haskell.org/ghc/ghc/issues/12673 - -- and https://github.com/haskell/cabal/issues/2257 - let logger = hsc_logger hsc_env - let tmpfs = hsc_tmpfs hsc_env - empty_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "c" - let home_unit = hsc_home_unit hsc_env - src = text "int" <+> ppr (mkHomeModule home_unit mod_name) <+> text "= 0;" - writeFile empty_stub (showSDoc dflags (pprCode CStyle src)) - _ <- runPipeline StopLn hsc_env - (empty_stub, Nothing, Nothing) - (Just basename) - Persistent - (Just location) - [] - return () - -- --------------------------------------------------------------------------- -- Link -- @@ -614,13 +507,17 @@ findHSLib platform ws dirs lib = do -- ----------------------------------------------------------------------------- -- Compile files in one-shot mode. -oneShot :: HscEnv -> Phase -> [(String, Maybe Phase)] -> IO () +oneShot :: HscEnv -> StopPhase -> [(String, Maybe Phase)] -> IO () oneShot hsc_env stop_phase srcs = do - o_files <- mapM (compileFile hsc_env stop_phase) srcs - doLink hsc_env stop_phase o_files - -compileFile :: HscEnv -> Phase -> (FilePath, Maybe Phase) -> IO FilePath -compileFile hsc_env stop_phase (src, mb_phase) = do + o_files <- mapMaybeM (compileFile hsc_env stop_phase) srcs + case stop_phase of + StopPreprocess -> return () + StopC -> return () + StopAs -> return () + NoStop -> doLink hsc_env o_files + +compileFile :: HscEnv -> StopPhase -> (FilePath, Maybe Phase) -> IO (Maybe FilePath) +compileFile hsc_env stop_phase (src, _mb_phase) = do exists <- doesFileExist src when (not exists) $ throwGhcExceptionIO (CmdLineError ("does not exist: " ++ src)) @@ -634,27 +531,19 @@ compileFile hsc_env stop_phase (src, mb_phase) = do -- otherwise, we use it as the name for the pipeline's output. output | NoBackend <- backend dflags = NoOutputFile - | StopLn <- stop_phase, not (isNoLink ghc_link) = Persistent + | NoStop <- stop_phase, not (isNoLink ghc_link) = Persistent -- -o foo applies to linker | isJust mb_o_file = SpecificFile -- -o foo applies to the file we are compiling now | otherwise = Persistent - - ( _, out_file, _, _) <- runPipeline stop_phase hsc_env - (src, Nothing, fmap RealPhase mb_phase) - Nothing - output - Nothing{-no ModLocation-} [] - return out_file + pipe_env = mkPipeEnv stop_phase src output + pipeline = pipelineStart pipe_env (setDumpPrefix pipe_env hsc_env) src + runPipeline (hsc_hooks hsc_env) pipeline -doLink :: HscEnv -> Phase -> [FilePath] -> IO () -doLink hsc_env stop_phase o_files - | not (isStopLn stop_phase) - = return () -- We stopped before the linking phase - - | otherwise - = let +doLink :: HscEnv -> [FilePath] -> IO () +doLink hsc_env o_files = + let dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env unit_env = hsc_unit_env hsc_env @@ -666,1586 +555,415 @@ doLink hsc_env stop_phase o_files LinkDynLib -> linkDynLibCheck logger tmpfs dflags unit_env o_files [] other -> panicBadLink other +----------------------------------------------------------------------------- +-- stub .h and .c files (for foreign export support), and cc files. --- --------------------------------------------------------------------------- - --- | Run a compilation pipeline, consisting of multiple phases. --- --- This is the interface to the compilation pipeline, which runs --- a series of compilation steps on a single source file, specifying --- at which stage to stop. --- --- The DynFlags can be modified by phases in the pipeline (eg. by --- OPTIONS_GHC pragmas), and the changes affect later phases in the --- pipeline. -runPipeline - :: Phase -- ^ When to stop - -> HscEnv -- ^ Compilation environment - -> (FilePath, Maybe InputFileBuffer, Maybe PhasePlus) - -- ^ Pipeline input file name, optional - -- buffer and maybe -x suffix - -> Maybe FilePath -- ^ original basename (if different from ^^^) - -> PipelineOutput -- ^ Output filename - -> Maybe ModLocation -- ^ A ModLocation, if this is a Haskell module - -> [FilePath] -- ^ foreign objects - -> IO (DynFlags, FilePath, Maybe ModIface, Maybe Linkable) - -- ^ (final flags, output filename, interface, linkable) -runPipeline stop_phase hsc_env0 (input_fn, mb_input_buf, mb_phase) - mb_basename output maybe_loc foreign_os - - = do let - -- Decide where dump files should go based on the pipeline output - hsc_env = hscUpdateFlags (\dflags -> dflags { dumpPrefix = Just (basename ++ ".")}) hsc_env0 - logger = hsc_logger hsc_env - tmpfs = hsc_tmpfs hsc_env - dflags = hsc_dflags hsc_env - - (input_basename, suffix) = splitExtension input_fn - suffix' = drop 1 suffix -- strip off the . - basename | Just b <- mb_basename = b - | otherwise = input_basename - - -- If we were given a -x flag, then use that phase to start from - start_phase = fromMaybe (RealPhase (startPhase suffix')) mb_phase - - isHaskell (RealPhase (Unlit _)) = True - isHaskell (RealPhase (Cpp _)) = True - isHaskell (RealPhase (HsPp _)) = True - isHaskell (RealPhase (Hsc _)) = True - isHaskell (HscPostTc {}) = True - isHaskell (HscBackend {}) = True - isHaskell _ = False - - isHaskellishFile = isHaskell start_phase - - env = PipeEnv{ stop_phase, - src_filename = input_fn, - src_basename = basename, - src_suffix = suffix', - output_spec = output } - - when (isBackpackishSuffix suffix') $ - throwGhcExceptionIO (UsageError - ("use --backpack to process " ++ input_fn)) - - -- We want to catch cases of "you can't get there from here" before - -- we start the pipeline, because otherwise it will just run off the - -- end. - let happensBefore' = happensBefore (targetPlatform dflags) - case start_phase of - RealPhase start_phase' -> - -- See Note [Partial ordering on phases] - -- Not the same as: (stop_phase `happensBefore` start_phase') - when (not (start_phase' `happensBefore'` stop_phase || - start_phase' `eqPhase` stop_phase)) $ - throwGhcExceptionIO (UsageError - ("cannot compile this file to desired target: " - ++ input_fn)) - HscPostTc {} -> return () - HscBackend {} -> return () - - -- Write input buffer to temp file if requested - input_fn' <- case (start_phase, mb_input_buf) of - (RealPhase real_start_phase, Just input_buf) -> do - let suffix = phaseInputExt real_start_phase - fn <- newTempName logger tmpfs dflags TFL_CurrentModule suffix - hdl <- openBinaryFile fn WriteMode - -- Add a LINE pragma so reported source locations will - -- mention the real input file, not this temp file. - hPutStrLn hdl $ "{-# LINE 1 \""++ input_fn ++ "\"#-}" - hPutStringBuffer hdl input_buf - hClose hdl - return fn - (_, _) -> return input_fn - - debugTraceMsg logger 4 (text "Running the pipeline") - r <- runPipeline' start_phase hsc_env env input_fn' - maybe_loc foreign_os - - when isHaskellishFile $ - dynamicTooState dflags >>= \case - DT_Dont -> return () - DT_Dyn -> return () - DT_OK -> return () - -- If we are compiling a Haskell module with -dynamic-too, we - -- first try the "fast path": that is we compile the non-dynamic - -- version and at the same time we check that interfaces depended - -- on exist both for the non-dynamic AND the dynamic way. We also - -- check that they have the same hash. - -- If they don't, dynamicTooState is set to DT_Failed. - -- See GHC.Iface.Load.checkBuildDynamicToo - -- If they do, in the end we produce both the non-dynamic and - -- dynamic outputs. - -- - -- If this "fast path" failed, we execute the whole pipeline - -- again, this time for the dynamic way *only*. To do that we - -- just set the dynamicNow bit from the start to ensure that the - -- dynamic DynFlags fields are used and we disable -dynamic-too - -- (its state is already set to DT_Failed so it wouldn't do much - -- anyway). - DT_Failed - -- NB: Currently disabled on Windows (ref #7134, #8228, and #5987) - | OSMinGW32 <- platformOS (targetPlatform dflags) -> return () - | otherwise -> do - debugTraceMsg logger 4 - (text "Running the full pipeline again for -dynamic-too") - let dflags0 = flip gopt_unset Opt_BuildDynamicToo - $ setDynamicNow - $ dflags - hsc_env' <- newHscEnv dflags0 - (dbs,unit_state,home_unit,mconstants) <- initUnits logger dflags0 Nothing - dflags1 <- updatePlatformConstants dflags0 mconstants - unit_env0 <- initUnitEnv (ghcNameVersion dflags1) (targetPlatform dflags1) - let unit_env = unit_env0 - { ue_home_unit = Just home_unit - , ue_units = unit_state - , ue_unit_dbs = Just dbs - } - let hsc_env'' = hscSetFlags dflags1 - $ hsc_env' { hsc_unit_env = unit_env } - _ <- runPipeline' start_phase hsc_env'' env input_fn' - maybe_loc foreign_os - return () - return r - -runPipeline' - :: PhasePlus -- ^ When to start - -> HscEnv -- ^ Compilation environment - -> PipeEnv - -> FilePath -- ^ Input filename - -> Maybe ModLocation -- ^ A ModLocation, if this is a Haskell module - -> [FilePath] -- ^ foreign objects, if we have one - -> IO (DynFlags, FilePath, Maybe ModIface, Maybe Linkable) - -- ^ (final flags, output filename, interface, linkable) -runPipeline' start_phase hsc_env env input_fn - maybe_loc foreign_os - = do - -- Execute the pipeline... - let state = PipeState{ hsc_env, maybe_loc, foreign_os = foreign_os, iface = Nothing - , maybe_linkable = Nothing } - (pipe_state, fp) <- evalP (pipeLoop start_phase input_fn) env state - return (pipeStateDynFlags pipe_state, fp, pipeStateModIface pipe_state - , pipeStateLinkable pipe_state ) - --- --------------------------------------------------------------------------- --- outer pipeline loop - --- | pipeLoop runs phases until we reach the stop phase -pipeLoop :: PhasePlus -> FilePath -> CompPipeline FilePath -pipeLoop phase input_fn = do - env <- getPipeEnv - dflags <- getDynFlags - logger <- getLogger - -- See Note [Partial ordering on phases] - let happensBefore' = happensBefore (targetPlatform dflags) - stopPhase = stop_phase env - case phase of - RealPhase realPhase | realPhase `eqPhase` stopPhase -- All done - -> -- Sometimes, a compilation phase doesn't actually generate any output - -- (eg. the CPP phase when -fcpp is not turned on). If we end on this - -- stage, but we wanted to keep the output, then we have to explicitly - -- copy the file, remembering to prepend a {-# LINE #-} pragma so that - -- further compilation stages can tell what the original filename was. - case output_spec env of - Temporary _ -> - return input_fn - NoOutputFile -> return input_fn - output -> - do pst <- getPipeState - tmpfs <- hsc_tmpfs <$> getPipeSession - final_fn <- liftIO $ getOutputFilename logger tmpfs - stopPhase output (src_basename env) - dflags stopPhase (maybe_loc pst) - when (final_fn /= input_fn) $ do - let msg = "Copying `" ++ input_fn ++"' to `" ++ final_fn ++ "'" - line_prag = "{-# LINE 1 \"" ++ src_filename env ++ "\" #-}\n" - liftIO $ showPass logger msg - liftIO $ copyWithHeader line_prag input_fn final_fn - return final_fn - - - | not (realPhase `happensBefore'` stopPhase) - -- Something has gone wrong. We'll try to cover all the cases when - -- this could happen, so if we reach here it is a panic. - -- eg. it might happen if the -C flag is used on a source file that - -- has {-# OPTIONS -fasm #-}. - -> panic ("pipeLoop: at phase " ++ show realPhase ++ - " but I wanted to stop at phase " ++ show stopPhase) - - _ - -> do liftIO $ debugTraceMsg logger 4 - (text "Running phase" <+> ppr phase) - - case phase of - HscBackend {} -> do - -- Depending on the dynamic-too state, we first run the - -- backend to generate the non-dynamic objects and then - -- re-run it to generate the dynamic ones. - let noDynToo = do - (next_phase, output_fn) <- runHookedPhase phase input_fn - pipeLoop next_phase output_fn - let dynToo = do - -- we must run the non-dynamic way before the dynamic - -- one because there may be interfaces loaded only in - -- the backend (e.g., in CorePrep). See #19264 - r <- noDynToo - - -- we must check the dynamic-too state again, because - -- we may have failed to load a dynamic interface in - -- the backend. - dynamicTooState dflags >>= \case - DT_OK -> do - let dflags' = setDynamicNow dflags -- set "dynamicNow" - setDynFlags dflags' - (next_phase, output_fn) <- runHookedPhase phase input_fn - _ <- pipeLoop next_phase output_fn - -- TODO: we probably shouldn't ignore the result of - -- the dynamic compilation - setDynFlags dflags -- restore flags without "dynamicNow" set - return r - _ -> return r - - dynamicTooState dflags >>= \case - DT_Dont -> noDynToo - DT_Failed -> noDynToo - DT_OK -> dynToo - DT_Dyn -> noDynToo - -- it shouldn't be possible to be in this last case - -- here. It would mean that we executed the whole - -- pipeline with DynamicNow and Opt_BuildDynamicToo set. - -- - -- When we restart the whole pipeline for -dynamic-too - -- we set DynamicNow but we unset Opt_BuildDynamicToo so - -- it's weird. - _ -> do - (next_phase, output_fn) <- runHookedPhase phase input_fn - pipeLoop next_phase output_fn - -runHookedPhase :: PhasePlus -> FilePath -> CompPipeline (PhasePlus, FilePath) -runHookedPhase pp input = do - hooks <- hsc_hooks <$> getPipeSession - case runPhaseHook hooks of - Nothing -> runPhase pp input - Just h -> h pp input - --- ----------------------------------------------------------------------------- --- In each phase, we need to know into what filename to generate the --- output. All the logic about which filenames we generate output --- into is embodied in the following function. - --- | Computes the next output filename after we run @next_phase@. --- Like 'getOutputFilename', but it operates in the 'CompPipeline' monad --- (which specifies all of the ambient information.) -phaseOutputFilename :: Phase{-next phase-} -> CompPipeline FilePath -phaseOutputFilename next_phase = do - PipeEnv{stop_phase, src_basename, output_spec} <- getPipeEnv - PipeState{maybe_loc,hsc_env} <- getPipeState - dflags <- getDynFlags - logger <- getLogger - let tmpfs = hsc_tmpfs hsc_env - liftIO $ getOutputFilename logger tmpfs stop_phase output_spec - src_basename dflags next_phase maybe_loc - --- | Computes the next output filename for something in the compilation --- pipeline. This is controlled by several variables: --- --- 1. 'Phase': the last phase to be run (e.g. 'stopPhase'). This --- is used to tell if we're in the last phase or not, because --- in that case flags like @-o@ may be important. --- 2. 'PipelineOutput': is this intended to be a 'Temporary' or --- 'Persistent' build output? Temporary files just go in --- a fresh temporary name. --- 3. 'String': what was the basename of the original input file? --- 4. 'DynFlags': the obvious thing --- 5. 'Phase': the phase we want to determine the output filename of. --- 6. @Maybe ModLocation@: the 'ModLocation' of the module we're --- compiling; this can be used to override the default output --- of an object file. (TODO: do we actually need this?) -getOutputFilename - :: Logger - -> TmpFs - -> Phase - -> PipelineOutput - -> String - -> DynFlags - -> Phase -- next phase - -> Maybe ModLocation - -> IO FilePath -getOutputFilename logger tmpfs stop_phase output basename dflags next_phase maybe_location - | is_last_phase, Persistent <- output = persistent_fn - | is_last_phase, SpecificFile <- output = case outputFile dflags of - Just f -> return f - Nothing -> - panic "SpecificFile: No filename" - | keep_this_output = persistent_fn - | Temporary lifetime <- output = newTempName logger tmpfs dflags lifetime suffix - | otherwise = newTempName logger tmpfs dflags TFL_CurrentModule - suffix - where - hcsuf = hcSuf dflags - odir = objectDir dflags - osuf = objectSuf dflags - keep_hc = gopt Opt_KeepHcFiles dflags - keep_hscpp = gopt Opt_KeepHscppFiles dflags - keep_s = gopt Opt_KeepSFiles dflags - keep_bc = gopt Opt_KeepLlvmFiles dflags - - myPhaseInputExt HCc = hcsuf - myPhaseInputExt MergeForeign = osuf - myPhaseInputExt StopLn = osuf - myPhaseInputExt other = phaseInputExt other - - is_last_phase = next_phase `eqPhase` stop_phase - - -- sometimes, we keep output from intermediate stages - keep_this_output = - case next_phase of - As _ | keep_s -> True - LlvmOpt | keep_bc -> True - HCc | keep_hc -> True - HsPp _ | keep_hscpp -> True -- See #10869 - _other -> False - - suffix = myPhaseInputExt next_phase - - -- persistent object files get put in odir - persistent_fn - | StopLn <- next_phase = return odir_persistent - | otherwise = return persistent - - persistent = basename <.> suffix - - odir_persistent - | Just loc <- maybe_location = ml_obj_file loc - | Just d <- odir = d </> persistent - | otherwise = persistent - - --- | LLVM Options. These are flags to be passed to opt and llc, to ensure --- consistency we list them in pairs, so that they form groups. -llvmOptions :: DynFlags - -> [(String, String)] -- ^ pairs of (opt, llc) arguments -llvmOptions dflags = - [("-enable-tbaa -tbaa", "-enable-tbaa") | gopt Opt_LlvmTBAA dflags ] - ++ [("-relocation-model=" ++ rmodel - ,"-relocation-model=" ++ rmodel) | not (null rmodel)] - ++ [("-stack-alignment=" ++ (show align) - ,"-stack-alignment=" ++ (show align)) | align > 0 ] - - -- Additional llc flags - ++ [("", "-mcpu=" ++ mcpu) | not (null mcpu) - , not (any (isInfixOf "-mcpu") (getOpts dflags opt_lc)) ] - ++ [("", "-mattr=" ++ attrs) | not (null attrs) ] - ++ [("", "-target-abi=" ++ abi) | not (null abi) ] - - where target = platformMisc_llvmTarget $ platformMisc dflags - Just (LlvmTarget _ mcpu mattr) = lookup target (llvmTargets $ llvmConfig dflags) - - -- Relocation models - rmodel | gopt Opt_PIC dflags = "pic" - | positionIndependent dflags = "pic" - | WayDyn `elem` ways dflags = "dynamic-no-pic" - | otherwise = "static" - - platform = targetPlatform dflags - - align :: Int - align = case platformArch platform of - ArchX86_64 | isAvxEnabled dflags -> 32 - _ -> 0 - - attrs :: String - attrs = intercalate "," $ mattr - ++ ["+sse42" | isSse4_2Enabled dflags ] - ++ ["+sse2" | isSse2Enabled platform ] - ++ ["+sse" | isSseEnabled platform ] - ++ ["+avx512f" | isAvx512fEnabled dflags ] - ++ ["+avx2" | isAvx2Enabled dflags ] - ++ ["+avx" | isAvxEnabled dflags ] - ++ ["+avx512cd"| isAvx512cdEnabled dflags ] - ++ ["+avx512er"| isAvx512erEnabled dflags ] - ++ ["+avx512pf"| isAvx512pfEnabled dflags ] - ++ ["+bmi" | isBmiEnabled dflags ] - ++ ["+bmi2" | isBmi2Enabled dflags ] - - abi :: String - abi = case platformArch (targetPlatform dflags) of - ArchRISCV64 -> "lp64d" - _ -> "" - --- ----------------------------------------------------------------------------- --- | Each phase in the pipeline returns the next phase to execute, and the --- name of the file in which the output was placed. +-- The _stub.c file is derived from the haskell source file, possibly taking +-- into account the -stubdir option. -- --- We must do things dynamically this way, because we often don't know --- what the rest of the phases will be until part-way through the --- compilation: for example, an {-# OPTIONS -fasm #-} at the beginning --- of a source file can change the latter stages of the pipeline from --- taking the LLVM route to using the native code generator. +-- The object file created by compiling the _stub.c file is put into a +-- temporary file, which will be later combined with the main .o file +-- (see the MergeForeigns phase). -- -runPhase :: PhasePlus -- ^ Run this phase - -> FilePath -- ^ name of the input file - -> CompPipeline (PhasePlus, -- next phase to run - FilePath) -- output filename - - -- Invariant: the output filename always contains the output - -- Interesting case: Hsc when there is no recompilation to do - -- Then the output filename is still a .o file - - -------------------------------------------------------------------------------- --- Unlit phase - -runPhase (RealPhase (Unlit sf)) input_fn = do - let - -- escape the characters \, ", and ', but don't try to escape - -- Unicode or anything else (so we don't use Util.charToC - -- here). If we get this wrong, then in - -- GHC.HsToCore.Coverage.isGoodTickSrcSpan where we check that the filename in - -- a SrcLoc is the same as the source filenaame, the two will - -- look bogusly different. See test: - -- libraries/hpc/tests/function/subdir/tough2.hs - escape ('\\':cs) = '\\':'\\': escape cs - escape ('\"':cs) = '\\':'\"': escape cs - escape ('\'':cs) = '\\':'\'': escape cs - escape (c:cs) = c : escape cs - escape [] = [] - - output_fn <- phaseOutputFilename (Cpp sf) - - let flags = [ -- The -h option passes the file name for unlit to - -- put in a #line directive - GHC.SysTools.Option "-h" - -- See Note [Don't normalise input filenames]. - , GHC.SysTools.Option $ escape input_fn - , GHC.SysTools.FileOption "" input_fn - , GHC.SysTools.FileOption "" output_fn - ] - - dflags <- getDynFlags - logger <- getLogger - liftIO $ GHC.SysTools.runUnlit logger dflags flags - - return (RealPhase (Cpp sf), output_fn) - -------------------------------------------------------------------------------- --- Cpp phase : (a) gets OPTIONS out of file --- (b) runs cpp if necessary - -runPhase (RealPhase (Cpp sf)) input_fn - = do - dflags0 <- getDynFlags - let parser_opts0 = initParserOpts dflags0 - src_opts <- liftIO $ getOptionsFromFile parser_opts0 input_fn - (dflags1, unhandled_flags, warns) - <- liftIO $ parseDynamicFilePragma dflags0 src_opts - setDynFlags dflags1 - liftIO $ checkProcessArgsResult unhandled_flags - - - if not (xopt LangExt.Cpp dflags1) then do - -- we have to be careful to emit warnings only once. - unless (gopt Opt_Pp dflags1) $ do - logger <- getLogger - liftIO $ handleFlagWarnings logger (initDiagOpts dflags1) warns - - -- no need to preprocess CPP, just pass input file along - -- to the next phase of the pipeline. - return (RealPhase (HsPp sf), input_fn) - else do - output_fn <- phaseOutputFilename (HsPp sf) - hsc_env <- getPipeSession - logger <- getLogger - liftIO $ doCpp logger - (hsc_tmpfs hsc_env) - (hsc_dflags hsc_env) - (hsc_unit_env hsc_env) - True{-raw-} - input_fn output_fn - -- re-read the pragmas now that we've preprocessed the file - -- See #2464,#3457 - src_opts <- liftIO $ getOptionsFromFile parser_opts0 output_fn - (dflags2, unhandled_flags, warns) - <- liftIO $ parseDynamicFilePragma dflags0 src_opts - setDynFlags dflags2 - liftIO $ checkProcessArgsResult unhandled_flags - unless (gopt Opt_Pp dflags2) $ do - logger <- getLogger - liftIO $ handleFlagWarnings logger (initDiagOpts dflags2) warns - -- the HsPp pass below will emit warnings - - return (RealPhase (HsPp sf), output_fn) - -------------------------------------------------------------------------------- --- HsPp phase - -runPhase (RealPhase (HsPp sf)) input_fn = do - dflags <- getDynFlags - logger <- getLogger - if not (gopt Opt_Pp dflags) then - -- no need to preprocess, just pass input file along - -- to the next phase of the pipeline. - return (RealPhase (Hsc sf), input_fn) - else do - PipeEnv{src_basename, src_suffix} <- getPipeEnv - let orig_fn = src_basename <.> src_suffix - output_fn <- phaseOutputFilename (Hsc sf) - liftIO $ GHC.SysTools.runPp logger dflags - ( [ GHC.SysTools.Option orig_fn - , GHC.SysTools.Option input_fn - , GHC.SysTools.FileOption "" output_fn - ] - ) - - -- re-read pragmas now that we've parsed the file (see #3674) - let parser_opts = initParserOpts dflags - src_opts <- liftIO $ getOptionsFromFile parser_opts output_fn - (dflags1, unhandled_flags, warns) - <- liftIO $ parseDynamicFilePragma dflags src_opts - setDynFlags dflags1 - liftIO $ checkProcessArgsResult unhandled_flags - liftIO $ handleFlagWarnings logger (initDiagOpts dflags1) warns - - return (RealPhase (Hsc sf), output_fn) - ------------------------------------------------------------------------------ --- Hsc phase - --- Compilation of a single module, in "legacy" mode (_not_ under --- the direction of the compilation manager). -runPhase (RealPhase (Hsc src_flavour)) input_fn - = do -- normal Hsc mode, not mkdependHS - dflags0 <- getDynFlags - PipeEnv{ src_basename=basename, - src_suffix=suff } <- getPipeEnv - - -- we add the current directory (i.e. the directory in which - -- the .hs files resides) to the include path, since this is - -- what gcc does, and it's probably what you want. - let current_dir = takeDirectory basename - new_includes = addImplicitQuoteInclude paths [current_dir] - paths = includePaths dflags0 - dflags = dflags0 { includePaths = new_includes } - - setDynFlags dflags - - -- gather the imports and module name - (hspp_buf,mod_name,imps,src_imps) <- liftIO $ do - buf <- hGetStringBuffer input_fn - let imp_prelude = xopt LangExt.ImplicitPrelude dflags - popts = initParserOpts dflags - eimps <- getImports popts imp_prelude buf input_fn (basename <.> suff) - case eimps of - Left errs -> throwErrors (GhcPsMessage <$> errs) - Right (src_imps,imps,L _ mod_name) -> return - (Just buf, mod_name, imps, src_imps) - - -- Take -o into account if present - -- Very like -ohi, but we must *only* do this if we aren't linking - -- (If we're linking then the -o applies to the linked thing, not to - -- the object file for one module.) - -- Note the nasty duplication with the same computation in compileFile above - location <- getLocation 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 = dynamicOutputFile dflags o_file - - src_hash <- liftIO $ getFileHash (basename <.> suff) - hi_date <- liftIO $ modificationTimeIfExists hi_file - hie_date <- liftIO $ modificationTimeIfExists hie_file - o_mod <- liftIO $ modificationTimeIfExists o_file - dyn_o_mod <- liftIO $ modificationTimeIfExists dyn_o_file - - PipeState{hsc_env=hsc_env'} <- getPipeState - - -- Tell the finder cache about this module - mod <- liftIO $ do - let home_unit = hsc_home_unit hsc_env' - let fc = hsc_FC hsc_env' - addHomeModuleToFinder fc home_unit mod_name location - - -- Make the ModSummary to hand to hscMain - let - mod_summary = ModSummary { ms_mod = mod, - ms_hsc_src = src_flavour, - ms_hspp_file = input_fn, - ms_hspp_opts = dflags, - ms_hspp_buf = hspp_buf, - ms_location = location, - ms_hs_hash = src_hash, - ms_obj_date = o_mod, - ms_dyn_obj_date = dyn_o_mod, - ms_parsed_mod = Nothing, - ms_iface_date = hi_date, - ms_hie_date = hie_date, - ms_textual_imps = imps, - ms_srcimps = src_imps } - - - -- run the compiler! - let msg hsc_env _ what _ = oneShotMsg (hsc_logger hsc_env) what - plugin_hsc_env' <- liftIO $ initializePlugins hsc_env' (Just $ ms_mnwib mod_summary) - - -- Need to set the knot-tying mutable variable for interface - -- files. See GHC.Tc.Utils.TcGblEnv.tcg_type_env_var. - -- See also Note [hsc_type_env_var hack] - type_env_var <- liftIO $ newIORef emptyNameEnv - let plugin_hsc_env = plugin_hsc_env' { hsc_type_env_var = Just (mod, type_env_var) } - - status <- liftIO $ hscRecompStatus (Just msg) plugin_hsc_env mod_summary - Nothing Nothing (1, 1) - - logger <- getLogger - case status of - HscUpToDate iface _ -> - do liftIO $ touchObjectFile logger dflags o_file - -- The .o file must have a later modification date - -- than the source file (else we wouldn't get Nothing) - -- but we touch it anyway, to keep 'make' happy (we think). - setIface iface - return (RealPhase StopLn, o_file) - HscRecompNeeded mb_old_hash -> do - (tc_result, warnings) <- liftIO $ - hscTypecheckAndGetWarnings plugin_hsc_env mod_summary - - -- In the rest of the pipeline use the loaded plugins - setPlugins (hsc_plugins plugin_hsc_env) - (hsc_static_plugins plugin_hsc_env) - -- "driver" plugins may have modified the DynFlags so we update them - setDynFlags (hsc_dflags plugin_hsc_env) - - return (HscPostTc mod_summary tc_result warnings mb_old_hash, - panic "HscPostTc doesn't have an input filename") - -runPhase (HscPostTc mod_summary tc_result tc_warnings mb_old_hash) _ = do - PipeState{hsc_env=hsc_env'} <- getPipeState - hscBackendAction <- liftIO $ runHsc hsc_env' $ do - hscDesugarAndSimplify mod_summary tc_result tc_warnings mb_old_hash - - dflags <- getDynFlags - let hscBackendPhase = HscBackend mod_summary hscBackendAction - next_phase <- case hscBackendAction of - HscUpdate iface -> do - setIface iface - case backend dflags of - NoBackend -> return $ RealPhase StopLn - Interpreter -> return $ RealPhase StopLn - _ -> return hscBackendPhase -- Need to create .o, and handle -dynamic-too - _ -> return hscBackendPhase - - return (next_phase, - panic "HscBackend doesn't have an input filename") - -runPhase (HscBackend mod_summary result) _ = do - let mod_name = moduleName (ms_mod mod_summary) - src_flavour = (ms_hsc_src mod_summary) - - dflags <- getDynFlags - logger <- getLogger - location <- getLocation src_flavour mod_name - setModLocation location - - let o_file = ml_obj_file location -- The real object file - next_phase = hscPostBackendPhase src_flavour (backend dflags) - - case result of - HscUpdate iface -> - do - case src_flavour of - HsigFile -> do - -- We need to create a REAL but empty .o file - -- because we are going to attempt to put it in a library - PipeState{hsc_env=hsc_env'} <- getPipeState - let input_fn = expectJust "runPhase" (ml_hs_file location) - basename = dropExtension input_fn - liftIO $ compileEmptyStub dflags hsc_env' basename location mod_name - - -- In the case of hs-boot files, generate a dummy .o-boot - -- stamp file for the benefit of Make - HsBootFile -> liftIO $ touchObjectFile logger dflags o_file - HsSrcFile -> panic "HscUpdate not relevant for HscSrcFile" - - setIface iface - return (RealPhase StopLn, o_file) - HscRecomp { hscs_guts = cgguts, - hscs_mod_location = mod_location, - hscs_partial_iface = partial_iface, - hscs_old_iface_hash = mb_old_iface_hash - } - -> case backend dflags of - NoBackend -> panic "HscRecomp not relevant for NoBackend" - Interpreter -> do - PipeState{hsc_env=hsc_env'} <- getPipeState - -- In interpreted mode the regular codeGen backend is not run so we - -- generate a interface without codeGen info. - final_iface <- liftIO $ mkFullIface hsc_env' partial_iface Nothing - liftIO $ hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash location - - (hasStub, comp_bc, spt_entries) <- liftIO $ hscInteractive hsc_env' cgguts mod_location - - stub_o <- liftIO $ case hasStub of - Nothing -> return [] - Just stub_c -> do - stub_o <- compileStub hsc_env' stub_c - return [DotO stub_o] - - let hs_unlinked = [BCOs comp_bc spt_entries] - unlinked_time <- liftIO getCurrentTime - -- Why do we use the timestamp of the source file here, - -- rather than the current time? This works better in - -- the case where the local clock is out of sync - -- with the filesystem's clock. It's just as accurate: - -- if the source is modified, then the linkable will - -- be out of date. - let !linkable = LM unlinked_time (ms_mod mod_summary) - (hs_unlinked ++ stub_o) - setIface final_iface - setLinkable linkable - return (RealPhase StopLn, - panic "Interpreter backend doesn't have an output file") - _ -> do - output_fn <- phaseOutputFilename next_phase - - PipeState{hsc_env=hsc_env'} <- getPipeState - - (outputFilename, mStub, foreign_files, cg_infos) <- liftIO $ - hscGenHardCode hsc_env' cgguts mod_location output_fn - - let dflags = hsc_dflags hsc_env' - final_iface <- liftIO (mkFullIface hsc_env' partial_iface (Just cg_infos)) - setIface final_iface - - -- See Note [Writing interface files] - liftIO $ hscMaybeWriteIface logger dflags False final_iface mb_old_iface_hash mod_location - - stub_o <- liftIO (mapM (compileStub hsc_env') mStub) - foreign_os <- liftIO $ - mapM (uncurry (compileForeign hsc_env')) foreign_files - setForeignOs (maybe [] return stub_o ++ foreign_os) - - return (RealPhase next_phase, outputFilename) - ------------------------------------------------------------------------------ --- Cmm phase - -runPhase (RealPhase CmmCpp) input_fn = do - hsc_env <- getPipeSession - logger <- getLogger - output_fn <- phaseOutputFilename Cmm - liftIO $ doCpp logger - (hsc_tmpfs hsc_env) - (hsc_dflags hsc_env) - (hsc_unit_env hsc_env) - False{-not raw-} - input_fn output_fn - return (RealPhase Cmm, output_fn) - -runPhase (RealPhase Cmm) input_fn = do - hsc_env <- getPipeSession - let dflags = hsc_dflags hsc_env - let next_phase = hscPostBackendPhase HsSrcFile (backend dflags) - output_fn <- phaseOutputFilename next_phase - PipeState{hsc_env} <- getPipeState - mstub <- liftIO $ hscCompileCmmFile hsc_env input_fn output_fn - stub_o <- liftIO (mapM (compileStub hsc_env) mstub) - setForeignOs (maybeToList stub_o) - return (RealPhase next_phase, output_fn) - ------------------------------------------------------------------------------ --- Cc phase - -runPhase (RealPhase cc_phase) input_fn - | any (cc_phase `eqPhase`) [Cc, Ccxx, HCc, Cobjc, Cobjcxx] - = do - hsc_env <- getPipeSession - let dflags = hsc_dflags hsc_env - let unit_env = hsc_unit_env hsc_env - let home_unit = hsc_home_unit hsc_env - let tmpfs = hsc_tmpfs hsc_env - let platform = ue_platform unit_env - let hcc = cc_phase `eqPhase` HCc - - let cmdline_include_paths = includePaths dflags - - -- HC files have the dependent packages stamped into them - pkgs <- if hcc then liftIO $ getHCFilePackages input_fn else return [] - - -- add package include paths even if we're just compiling .c - -- files; this is the Value Add(TM) that using ghc instead of - -- gcc gives you :) - ps <- liftIO $ mayThrowUnitErr (preloadUnitsInfo' unit_env pkgs) - let pkg_include_dirs = collectIncludeDirs ps - let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) [] - (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs) - let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) [] - (includePathsQuote cmdline_include_paths ++ - includePathsQuoteImplicit cmdline_include_paths) - let include_paths = include_paths_quote ++ include_paths_global - - -- pass -D or -optP to preprocessor when compiling foreign C files - -- (#16737). Doing it in this way is simpler and also enable the C - -- compiler to perform preprocessing and parsing in a single pass, - -- but it may introduce inconsistency if a different pgm_P is specified. - let more_preprocessor_opts = concat - [ ["-Xpreprocessor", i] - | not hcc - , i <- getOpts dflags opt_P - ] - - let gcc_extra_viac_flags = extraGccViaCFlags dflags - let pic_c_flags = picCCOpts dflags - - let verbFlags = getVerbFlags dflags - - -- cc-options are not passed when compiling .hc files. Our - -- hc code doesn't not #include any header files anyway, so these - -- options aren't necessary. - let pkg_extra_cc_opts - | hcc = [] - | otherwise = collectExtraCcOpts ps - - let framework_paths - | platformUsesFrameworks platform - = let pkgFrameworkPaths = collectFrameworksDirs ps - cmdlineFrameworkPaths = frameworkPaths dflags - in map ("-F"++) (cmdlineFrameworkPaths ++ pkgFrameworkPaths) - | otherwise - = [] - - let cc_opt | optLevel dflags >= 2 = [ "-O2" ] - | optLevel dflags >= 1 = [ "-O" ] - | otherwise = [] - - -- Decide next phase - let next_phase = As False - output_fn <- phaseOutputFilename next_phase - - let - more_hcc_opts = - -- on x86 the floating point regs have greater precision - -- than a double, which leads to unpredictable results. - -- By default, we turn this off with -ffloat-store unless - -- the user specified -fexcess-precision. - (if platformArch platform == ArchX86 && - not (gopt Opt_ExcessPrecision dflags) - then [ "-ffloat-store" ] - else []) ++ - - -- gcc's -fstrict-aliasing allows two accesses to memory - -- to be considered non-aliasing if they have different types. - -- This interacts badly with the C code we generate, which is - -- very weakly typed, being derived from C--. - ["-fno-strict-aliasing"] - - ghcVersionH <- liftIO $ getGhcVersionPathName dflags unit_env - - logger <- getLogger - liftIO $ GHC.SysTools.runCc (phaseForeignLanguage cc_phase) logger tmpfs dflags ( - [ GHC.SysTools.FileOption "" input_fn - , GHC.SysTools.Option "-o" - , GHC.SysTools.FileOption "" output_fn - ] - ++ map GHC.SysTools.Option ( - pic_c_flags - - -- Stub files generated for foreign exports references the runIO_closure - -- and runNonIO_closure symbols, which are defined in the base package. - -- These symbols are imported into the stub.c file via RtsAPI.h, and the - -- way we do the import depends on whether we're currently compiling - -- the base package or not. - ++ (if platformOS platform == OSMinGW32 && - isHomeUnitId home_unit baseUnitId - then [ "-DCOMPILING_BASE_PACKAGE" ] - else []) - - -- We only support SparcV9 and better because V8 lacks an atomic CAS - -- instruction. Note that the user can still override this - -- (e.g., -mcpu=ultrasparc) as GCC picks the "best" -mcpu flag - -- regardless of the ordering. - -- - -- This is a temporary hack. See #2872, commit - -- 5bd3072ac30216a505151601884ac88bf404c9f2 - ++ (if platformArch platform == ArchSPARC - then ["-mcpu=v9"] - else []) - - -- GCC 4.6+ doesn't like -Wimplicit when compiling C++. - ++ (if (cc_phase /= Ccxx && cc_phase /= Cobjcxx) - then ["-Wimplicit"] - else []) - - ++ (if hcc - then gcc_extra_viac_flags ++ more_hcc_opts - else []) - ++ verbFlags - ++ [ "-S" ] - ++ cc_opt - ++ [ "-include", ghcVersionH ] - ++ framework_paths - ++ include_paths - ++ more_preprocessor_opts - ++ pkg_extra_cc_opts - )) - - return (RealPhase next_phase, output_fn) - ------------------------------------------------------------------------------ --- As, SpitAs phase : Assembler - --- This is for calling the assembler on a regular assembly file -runPhase (RealPhase (As with_cpp)) input_fn - = do - hsc_env <- getPipeSession - let dflags = hsc_dflags hsc_env - let logger = hsc_logger hsc_env - let unit_env = hsc_unit_env hsc_env - let platform = ue_platform unit_env - - -- LLVM from version 3.0 onwards doesn't support the OS X system - -- assembler, so we use clang as the assembler instead. (#5636) - let (as_prog, get_asm_info) | backend dflags == LLVM - , platformOS platform == OSDarwin - = (GHC.SysTools.runClang, pure Clang) - | otherwise - = (GHC.SysTools.runAs, liftIO $ getAssemblerInfo logger dflags) - - asmInfo <- get_asm_info - - let cmdline_include_paths = includePaths dflags - let pic_c_flags = picCCOpts dflags - - next_phase <- maybeMergeForeign - output_fn <- phaseOutputFilename next_phase - - -- we create directories for the object file, because it - -- might be a hierarchical module. - liftIO $ createDirectoryIfMissing True (takeDirectory output_fn) - - let global_includes = [ GHC.SysTools.Option ("-I" ++ p) - | p <- includePathsGlobal cmdline_include_paths ] - let local_includes = [ GHC.SysTools.Option ("-iquote" ++ p) - | p <- includePathsQuote cmdline_include_paths ++ - includePathsQuoteImplicit cmdline_include_paths] - let runAssembler inputFilename outputFilename - = liftIO $ - withAtomicRename outputFilename $ \temp_outputFilename -> - as_prog - logger dflags - (local_includes ++ global_includes - -- See Note [-fPIC for assembler] - ++ map GHC.SysTools.Option pic_c_flags - -- See Note [Produce big objects on Windows] - ++ [ GHC.SysTools.Option "-Wa,-mbig-obj" - | platformOS (targetPlatform dflags) == OSMinGW32 - , not $ target32Bit (targetPlatform dflags) - ] - - -- We only support SparcV9 and better because V8 lacks an atomic CAS - -- instruction so we have to make sure that the assembler accepts the - -- instruction set. Note that the user can still override this - -- (e.g., -mcpu=ultrasparc). GCC picks the "best" -mcpu flag - -- regardless of the ordering. - -- - -- This is a temporary hack. - ++ (if platformArch (targetPlatform dflags) == ArchSPARC - then [GHC.SysTools.Option "-mcpu=v9"] - else []) - ++ (if any (asmInfo ==) [Clang, AppleClang, AppleClang51] - then [GHC.SysTools.Option "-Qunused-arguments"] - else []) - ++ [ GHC.SysTools.Option "-x" - , if with_cpp - then GHC.SysTools.Option "assembler-with-cpp" - else GHC.SysTools.Option "assembler" - , GHC.SysTools.Option "-c" - , GHC.SysTools.FileOption "" inputFilename - , GHC.SysTools.Option "-o" - , GHC.SysTools.FileOption "" temp_outputFilename - ]) - - liftIO $ debugTraceMsg logger 4 (text "Running the assembler") - runAssembler input_fn output_fn - - return (RealPhase next_phase, output_fn) - - ------------------------------------------------------------------------------ --- LlvmOpt phase -runPhase (RealPhase LlvmOpt) input_fn = do - dflags <- getDynFlags - logger <- getLogger - let -- we always (unless -optlo specified) run Opt since we rely on it to - -- fix up some pretty big deficiencies in the code we generate - optIdx = max 0 $ min 2 $ optLevel dflags -- ensure we're in [0,2] - llvmOpts = case lookup optIdx $ llvmPasses $ llvmConfig dflags of - Just passes -> passes - Nothing -> panic ("runPhase LlvmOpt: llvm-passes file " - ++ "is missing passes for level " - ++ show optIdx) - defaultOptions = map GHC.SysTools.Option . concat . fmap words . fst - $ unzip (llvmOptions dflags) - - -- don't specify anything if user has specified commands. We do this - -- for opt but not llc since opt is very specifically for optimisation - -- passes only, so if the user is passing us extra options we assume - -- they know what they are doing and don't get in the way. - optFlag = if null (getOpts dflags opt_lo) - then map GHC.SysTools.Option $ words llvmOpts - else [] - - output_fn <- phaseOutputFilename LlvmLlc - - liftIO $ GHC.SysTools.runLlvmOpt logger dflags - ( optFlag - ++ defaultOptions ++ - [ GHC.SysTools.FileOption "" input_fn - , GHC.SysTools.Option "-o" - , GHC.SysTools.FileOption "" output_fn] - ) - - return (RealPhase LlvmLlc, output_fn) - - ------------------------------------------------------------------------------ --- LlvmLlc phase - -runPhase (RealPhase LlvmLlc) input_fn = do - -- Note [Clamping of llc optimizations] - -- - -- See #13724 - -- - -- we clamp the llc optimization between [1,2]. This is because passing -O0 - -- to llc 3.9 or llc 4.0, the naive register allocator can fail with - -- - -- Error while trying to spill R1 from class GPR: Cannot scavenge register - -- without an emergency spill slot! - -- - -- Observed at least with target 'arm-unknown-linux-gnueabihf'. - -- - -- - -- With LLVM4, llc -O3 crashes when ghc-stage1 tries to compile - -- rts/HeapStackCheck.cmm - -- - -- llc -O3 '-mtriple=arm-unknown-linux-gnueabihf' -enable-tbaa /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc -o /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_7.lm_s - -- 0 llc 0x0000000102ae63e8 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40 - -- 1 llc 0x0000000102ae69a6 SignalHandler(int) + 358 - -- 2 libsystem_platform.dylib 0x00007fffc23f4b3a _sigtramp + 26 - -- 3 libsystem_c.dylib 0x00007fffc226498b __vfprintf + 17876 - -- 4 llc 0x00000001029d5123 llvm::SelectionDAGISel::LowerArguments(llvm::Function const&) + 5699 - -- 5 llc 0x0000000102a21a35 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) + 3381 - -- 6 llc 0x0000000102a202b1 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) + 1457 - -- 7 llc 0x0000000101bdc474 (anonymous namespace)::ARMDAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&) + 20 - -- 8 llc 0x00000001025573a6 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 134 - -- 9 llc 0x000000010274fb12 llvm::FPPassManager::runOnFunction(llvm::Function&) + 498 - -- 10 llc 0x000000010274fd23 llvm::FPPassManager::runOnModule(llvm::Module&) + 67 - -- 11 llc 0x00000001027501b8 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 920 - -- 12 llc 0x000000010195f075 compileModule(char**, llvm::LLVMContext&) + 12133 - -- 13 llc 0x000000010195bf0b main + 491 - -- 14 libdyld.dylib 0x00007fffc21e5235 start + 1 - -- Stack dump: - -- 0. Program arguments: llc -O3 -mtriple=arm-unknown-linux-gnueabihf -enable-tbaa /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc -o /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_7.lm_s - -- 1. Running pass 'Function Pass Manager' on module '/var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc'. - -- 2. Running pass 'ARM Instruction Selection' on function '@"stg_gc_f1$def"' - -- - -- Observed at least with -mtriple=arm-unknown-linux-gnueabihf -enable-tbaa - -- - dflags <- getDynFlags - logger <- getLogger - let - llvmOpts = case optLevel dflags of - 0 -> "-O1" -- required to get the non-naive reg allocator. Passing -regalloc=greedy is not sufficient. - 1 -> "-O1" - _ -> "-O2" - - defaultOptions = map GHC.SysTools.Option . concatMap words . snd - $ unzip (llvmOptions dflags) - optFlag = if null (getOpts dflags opt_lc) - then map GHC.SysTools.Option $ words llvmOpts - else [] - - next_phase <- if -- hidden debugging flag '-dno-llvm-mangler' to skip mangling - | gopt Opt_NoLlvmMangler dflags -> return (As False) - | otherwise -> return LlvmMangle - - output_fn <- phaseOutputFilename next_phase - - liftIO $ GHC.SysTools.runLlvmLlc logger dflags - ( optFlag - ++ defaultOptions - ++ [ GHC.SysTools.FileOption "" input_fn - , GHC.SysTools.Option "-o" - , GHC.SysTools.FileOption "" output_fn - ] - ) - - return (RealPhase next_phase, output_fn) - - - ------------------------------------------------------------------------------ --- LlvmMangle phase - -runPhase (RealPhase LlvmMangle) input_fn = do - let next_phase = As False - output_fn <- phaseOutputFilename next_phase - platform <- (ue_platform . hsc_unit_env) <$> getPipeSession - logger <- getLogger - liftIO $ withTiming logger (text "LLVM Mangler") id $ - llvmFixupAsm platform input_fn output_fn - return (RealPhase next_phase, output_fn) - ------------------------------------------------------------------------------ --- merge in stub objects - -runPhase (RealPhase MergeForeign) input_fn = do - PipeState{foreign_os,hsc_env} <- getPipeState - output_fn <- phaseOutputFilename StopLn - liftIO $ createDirectoryIfMissing True (takeDirectory output_fn) - if null foreign_os - then panic "runPhase(MergeForeign): no foreign objects" - else do - dflags <- getDynFlags - logger <- getLogger - let tmpfs = hsc_tmpfs hsc_env - liftIO $ joinObjectFiles logger tmpfs dflags (input_fn : foreign_os) output_fn - return (RealPhase StopLn, output_fn) - --- warning suppression -runPhase (RealPhase other) _input_fn = - panic ("runPhase: don't know how to run phase " ++ show other) - -maybeMergeForeign :: CompPipeline Phase -maybeMergeForeign - = do - PipeState{foreign_os} <- getPipeState - if null foreign_os then return StopLn else return MergeForeign - -getLocation :: HscSource -> ModuleName -> CompPipeline ModLocation -getLocation src_flavour mod_name = do - dflags <- getDynFlags - - PipeEnv{ src_basename=basename, - src_suffix=suff } <- getPipeEnv - location1 <- liftIO $ mkHomeModLocation2 dflags mod_name basename suff - - -- Boot-ify it if necessary - let location2 - | HsBootFile <- src_flavour = addBootSuffixLocnOut location1 - | otherwise = location1 - - - -- Take -ohi into account if present - -- This can't be done in mkHomeModuleLocation because - -- it only applies to the module being compiles - let ohi = outputHi dflags - location3 | Just fn <- ohi = location2{ ml_hi_file = fn } - | otherwise = location2 - - -- Take -o into account if present - -- Very like -ohi, but we must *only* do this if we aren't linking - -- (If we're linking then the -o applies to the linked thing, not to - -- the object file for one module.) - -- Note the nasty duplication with the same computation in compileFile - -- above - let expl_o_file = outputFile dflags - location4 | Just ofile <- expl_o_file - , isNoLink (ghcLink dflags) - = location3 { ml_obj_file = ofile } - | otherwise = location3 - return location4 - ------------------------------------------------------------------------------ --- Look for the /* GHC_PACKAGES ... */ comment at the top of a .hc file - -getHCFilePackages :: FilePath -> IO [UnitId] -getHCFilePackages filename = - Exception.bracket (openFile filename ReadMode) hClose $ \h -> do - l <- hGetLine h - case l of - '/':'*':' ':'G':'H':'C':'_':'P':'A':'C':'K':'A':'G':'E':'S':rest -> - return (map stringToUnitId (words rest)) - _other -> - return [] - +-- Moreover, we also let the user emit arbitrary C/C++/ObjC/ObjC++ files +-- from TH, that are then compiled and linked to the module. This is +-- useful to implement facilities such as inline-c. -linkDynLibCheck :: Logger -> TmpFs -> DynFlags -> UnitEnv -> [String] -> [UnitId] -> IO () -linkDynLibCheck logger tmpfs dflags unit_env o_files dep_units = do - when (haveRtsOptsFlags dflags) $ - logMsg logger MCInfo noSrcSpan - $ withPprStyle defaultUserStyle - (text "Warning: -rtsopts and -with-rtsopts have no effect with -shared." $$ - text " Call hs_init_ghc() from your main() function to set these options.") - linkDynLib logger tmpfs dflags unit_env o_files dep_units +compileForeign :: HscEnv -> ForeignSrcLang -> FilePath -> IO FilePath +compileForeign _ RawObject object_file = return object_file +compileForeign hsc_env lang stub_c = do + let pipeline = case lang of + LangC -> viaCPipeline Cc + LangCxx -> viaCPipeline Ccxx + LangObjc -> viaCPipeline Cobjc + LangObjcxx -> viaCPipeline Cobjcxx + LangAsm -> \pe hsc_env ml fp -> Just <$> asPipeline True pe hsc_env ml fp +#if __GLASGOW_HASKELL__ < 811 + RawObject -> panic "compileForeign: should be unreachable" +#endif + pipe_env = mkPipeEnv NoStop stub_c (Temporary TFL_GhcSession) + res <- runPipeline (hsc_hooks hsc_env) (pipeline pipe_env hsc_env Nothing stub_c) + case res of + -- This should never happen as viaCPipeline should only return `Nothing` when the stop phase is `StopC`. + -- Future refactoring to not check StopC for this case + Nothing -> pprPanic "compileForeign" (ppr stub_c) + Just fp -> return fp +compileEmptyStub :: DynFlags -> HscEnv -> FilePath -> ModLocation -> ModuleName -> IO () +compileEmptyStub dflags hsc_env basename location mod_name = do + -- To maintain the invariant that every Haskell file + -- compiles to object code, we make an empty (but + -- valid) stub object file for signatures. However, + -- we make sure this object file has a unique symbol, + -- so that ranlib on OS X doesn't complain, see + -- https://gitlab.haskell.org/ghc/ghc/issues/12673 + -- and https://github.com/haskell/cabal/issues/2257 + let logger = hsc_logger hsc_env + let tmpfs = hsc_tmpfs hsc_env + empty_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "c" + let home_unit = hsc_home_unit hsc_env + src = text "int" <+> ppr (mkHomeModule home_unit mod_name) <+> text "= 0;" + writeFile empty_stub (showSDoc dflags (pprCode CStyle src)) + let pipe_env = (mkPipeEnv NoStop empty_stub Persistent) { src_basename = basename} + pipeline = viaCPipeline HCc pipe_env hsc_env (Just location) empty_stub + _ <- runPipeline (hsc_hooks hsc_env) pipeline + return () --- ----------------------------------------------------------------------------- --- Running CPP --- | Run CPP --- --- UnitEnv is needed to compute MIN_VERSION macros -doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> Bool -> FilePath -> FilePath -> IO () -doCpp logger tmpfs dflags unit_env raw input_fn output_fn = do - let hscpp_opts = picPOpts dflags - let cmdline_include_paths = includePaths dflags - let unit_state = ue_units unit_env - pkg_include_dirs <- mayThrowUnitErr - (collectIncludeDirs <$> preloadUnitsInfo unit_env) - let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) [] - (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs) - let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) [] - (includePathsQuote cmdline_include_paths ++ - includePathsQuoteImplicit cmdline_include_paths) - let include_paths = include_paths_quote ++ include_paths_global - - let verbFlags = getVerbFlags dflags - - let cpp_prog args | raw = GHC.SysTools.runCpp logger dflags args - | otherwise = GHC.SysTools.runCc Nothing logger tmpfs dflags - (GHC.SysTools.Option "-E" : args) - - let platform = targetPlatform dflags - targetArch = stringEncodeArch $ platformArch platform - targetOS = stringEncodeOS $ platformOS platform - isWindows = platformOS platform == OSMinGW32 - let target_defs = - [ "-D" ++ HOST_OS ++ "_BUILD_OS", - "-D" ++ HOST_ARCH ++ "_BUILD_ARCH", - "-D" ++ targetOS ++ "_HOST_OS", - "-D" ++ targetArch ++ "_HOST_ARCH" ] - -- remember, in code we *compile*, the HOST is the same our TARGET, - -- and BUILD is the same as our HOST. - - let io_manager_defs = - [ "-D__IO_MANAGER_WINIO__=1" | isWindows ] ++ - [ "-D__IO_MANAGER_MIO__=1" ] - - let sse_defs = - [ "-D__SSE__" | isSseEnabled platform ] ++ - [ "-D__SSE2__" | isSse2Enabled platform ] ++ - [ "-D__SSE4_2__" | isSse4_2Enabled dflags ] - - let avx_defs = - [ "-D__AVX__" | isAvxEnabled dflags ] ++ - [ "-D__AVX2__" | isAvx2Enabled dflags ] ++ - [ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] ++ - [ "-D__AVX512ER__" | isAvx512erEnabled dflags ] ++ - [ "-D__AVX512F__" | isAvx512fEnabled dflags ] ++ - [ "-D__AVX512PF__" | isAvx512pfEnabled dflags ] - - backend_defs <- getBackendDefs logger dflags - - let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ] - -- Default CPP defines in Haskell source - ghcVersionH <- getGhcVersionPathName dflags unit_env - let hsSourceCppOpts = [ "-include", ghcVersionH ] - - -- MIN_VERSION macros - let uids = explicitUnits unit_state - pkgs = catMaybes (map (lookupUnit unit_state) uids) - mb_macro_include <- - if not (null pkgs) && gopt Opt_VersionMacros dflags - then do macro_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "h" - writeFile macro_stub (generatePackageVersionMacros pkgs) - -- Include version macros for every *exposed* package. - -- Without -hide-all-packages and with a package database - -- size of 1000 packages, it takes cpp an estimated 2 - -- milliseconds to process this file. See #10970 - -- comment 8. - return [GHC.SysTools.FileOption "-include" macro_stub] - else return [] - - cpp_prog ( map GHC.SysTools.Option verbFlags - ++ map GHC.SysTools.Option include_paths - ++ map GHC.SysTools.Option hsSourceCppOpts - ++ map GHC.SysTools.Option target_defs - ++ map GHC.SysTools.Option backend_defs - ++ map GHC.SysTools.Option th_defs - ++ map GHC.SysTools.Option hscpp_opts - ++ map GHC.SysTools.Option sse_defs - ++ map GHC.SysTools.Option avx_defs - ++ map GHC.SysTools.Option io_manager_defs - ++ mb_macro_include - -- Set the language mode to assembler-with-cpp when preprocessing. This - -- alleviates some of the C99 macro rules relating to whitespace and the hash - -- operator, which we tend to abuse. Clang in particular is not very happy - -- about this. - ++ [ GHC.SysTools.Option "-x" - , GHC.SysTools.Option "assembler-with-cpp" - , GHC.SysTools.Option input_fn - -- We hackily use Option instead of FileOption here, so that the file - -- name is not back-slashed on Windows. cpp is capable of - -- dealing with / in filenames, so it works fine. Furthermore - -- if we put in backslashes, cpp outputs #line directives - -- with *double* backslashes. And that in turn means that - -- our error messages get double backslashes in them. - -- In due course we should arrange that the lexer deals - -- with these \\ escapes properly. - , GHC.SysTools.Option "-o" - , GHC.SysTools.FileOption "" output_fn - ]) - -getBackendDefs :: Logger -> DynFlags -> IO [String] -getBackendDefs logger dflags | backend dflags == LLVM = do - llvmVer <- figureLlvmVersion logger dflags - return $ case fmap llvmVersionList llvmVer of - Just [m] -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,0) ] - Just (m:n:_) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ] - _ -> [] +{- Environment Initialisation -} + +mkPipeEnv :: StopPhase -- End phase + -> FilePath -- input fn + -> PipelineOutput -- Output + -> PipeEnv +mkPipeEnv stop_phase input_fn output = + let (basename, suffix) = splitExtension input_fn + suffix' = drop 1 suffix -- strip off the . + env = PipeEnv{ stop_phase, + src_filename = input_fn, + src_basename = basename, + src_suffix = suffix', + output_spec = output } + in env + +setDumpPrefix :: PipeEnv -> HscEnv -> HscEnv +setDumpPrefix pipe_env hsc_env = + hscUpdateFlags (\dflags -> dflags { dumpPrefix = Just (src_basename pipe_env ++ ".")}) hsc_env + +{- The Pipelines -} + +phaseIfFlag :: Monad m + => HscEnv + -> (DynFlags -> Bool) + -> a + -> m a + -> m a +phaseIfFlag hsc_env flag def action = + if flag (hsc_dflags hsc_env) + then action + else return def + +-- | Check if the start is *before* the current phase, otherwise skip with a default +phaseIfAfter :: P m => Platform -> Phase -> Phase -> a -> m a -> m a +phaseIfAfter platform start_phase cur_phase def action = + if start_phase `eqPhase` cur_phase + || happensBefore platform start_phase cur_phase + + then action + else return def + +-- | The preprocessor pipeline +preprocessPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m (DynFlags, FilePath) +preprocessPipeline pipe_env hsc_env input_fn = do + unlit_fn <- + runAfter (Unlit HsSrcFile) input_fn $ do + use (T_Unlit pipe_env hsc_env input_fn) + + + (dflags1, warns1) <- use (T_FileArgs hsc_env unlit_fn) + let hsc_env1 = hscSetFlags dflags1 hsc_env + + (cpp_fn, hsc_env2) + <- runAfterFlag hsc_env1 (Cpp HsSrcFile) (xopt LangExt.Cpp) (unlit_fn, hsc_env1) $ do + cpp_fn <- use (T_Cpp pipe_env hsc_env1 unlit_fn) + (dflags2, _) <- use (T_FileArgs hsc_env1 cpp_fn) + let hsc_env2 = hscSetFlags dflags2 hsc_env1 + return (cpp_fn, hsc_env2) + + + pp_fn <- runAfterFlag hsc_env2 (HsPp HsSrcFile) (gopt Opt_Pp) cpp_fn $ + use (T_HsPp pipe_env hsc_env2 input_fn cpp_fn) + + (dflags3, warns3) + <- if pp_fn == unlit_fn + -- Didn't run any preprocessors so don't need to reparse, would be nicer + -- if `T_FileArgs` recognised this. + then return (dflags1, warns1) + else do + -- Reparse with original hsc_env so that we don't get duplicated options + use (T_FileArgs hsc_env pp_fn) + + liftIO (handleFlagWarnings (hsc_logger hsc_env) (initDiagOpts dflags3) warns3) + return (dflags3, pp_fn) + + + -- This won't change through the compilation pipeline + where platform = targetPlatform (hsc_dflags hsc_env) + runAfter :: P p => Phase + -> a -> p a -> p a + runAfter = phaseIfAfter platform start_phase + start_phase = startPhase (src_suffix pipe_env) + runAfterFlag :: P p + => HscEnv + -> Phase + -> (DynFlags -> Bool) + -> a + -> p a + -> p a + runAfterFlag hsc_env phase flag def action = + runAfter phase def + $ phaseIfFlag hsc_env flag def action + +-- | The complete compilation pipeline, from start to finish +fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, Maybe Linkable) +fullPipeline pipe_env hsc_env pp_fn src_flavour = do + (dflags, input_fn) <- preprocessPipeline pipe_env hsc_env pp_fn + let hsc_env' = hscSetFlags dflags hsc_env + (hsc_env_with_plugins, mod_sum, hsc_recomp_status) + <- use (T_HscRecomp pipe_env hsc_env' input_fn src_flavour) + res <- hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) + checkDynamicToo pipe_env hsc_env pp_fn src_flavour res + -- Once the pipeline has finished, check to see if -dynamic-too failed and + -- rerun again if it failed but just the `--dynamic` way. + +checkDynamicToo :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> (ModIface, Maybe Linkable) -> m (ModIface, Maybe Linkable) +checkDynamicToo pipe_env hsc_env pp_fn src_flavour res = do + liftIO (dynamicTooState (hsc_dflags hsc_env)) >>= \case + DT_Dont -> return res + DT_Dyn -> return res + DT_OK -> return res + -- If we are compiling a Haskell module with -dynamic-too, we + -- first try the "fast path": that is we compile the non-dynamic + -- version and at the same time we check that interfaces depended + -- on exist both for the non-dynamic AND the dynamic way. We also + -- check that they have the same hash. + -- If they don't, dynamicTooState is set to DT_Failed. + -- See GHC.Iface.Load.checkBuildDynamicToo + -- If they do, in the end we produce both the non-dynamic and + -- dynamic outputs. + -- + -- If this "fast path" failed, we execute the whole pipeline + -- again, this time for the dynamic way *only*. To do that we + -- just set the dynamicNow bit from the start to ensure that the + -- dynamic DynFlags fields are used and we disable -dynamic-too + -- (its state is already set to DT_Failed so it wouldn't do much + -- anyway). + DT_Failed + -- NB: Currently disabled on Windows (ref #7134, #8228, and #5987) + | OSMinGW32 <- platformOS (targetPlatform dflags) -> return res + | otherwise -> do + liftIO (debugTraceMsg logger 4 + (text "Running the full pipeline again for -dynamic-too")) + hsc_env' <- liftIO (resetHscEnv hsc_env) + fullPipeline pipe_env hsc_env' pp_fn src_flavour where - format (major, minor) - | minor >= 100 = error "getBackendDefs: Unsupported minor version" - | otherwise = show $ (100 * major + minor :: Int) -- Contract is Int - -getBackendDefs _ _ = - return [] - --- --------------------------------------------------------------------------- --- Macros (cribbed from Cabal) - -generatePackageVersionMacros :: [UnitInfo] -> String -generatePackageVersionMacros pkgs = concat - -- Do not add any C-style comments. See #3389. - [ generateMacros "" pkgname version - | pkg <- pkgs - , let version = unitPackageVersion pkg - pkgname = map fixchar (unitPackageNameString pkg) - ] - -fixchar :: Char -> Char -fixchar '-' = '_' -fixchar c = c - -generateMacros :: String -> String -> Version -> String -generateMacros prefix name version = - concat - ["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n" - ,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n" - ," (major1) < ",major1," || \\\n" - ," (major1) == ",major1," && (major2) < ",major2," || \\\n" - ," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")" - ,"\n\n" - ] + dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + +-- | Enable dynamic-too, reset EPS +resetHscEnv :: HscEnv -> IO HscEnv +resetHscEnv hsc_env = do + let dflags0 = flip gopt_unset Opt_BuildDynamicToo + $ setDynamicNow + $ (hsc_dflags hsc_env) + hsc_env' <- newHscEnv dflags0 + (dbs,unit_state,home_unit,mconstants) <- initUnits (hsc_logger hsc_env) dflags0 Nothing + dflags1 <- updatePlatformConstants dflags0 mconstants + unit_env0 <- initUnitEnv (ghcNameVersion dflags1) (targetPlatform dflags1) + let unit_env = unit_env0 + { ue_home_unit = Just home_unit + , ue_units = unit_state + , ue_unit_dbs = Just dbs + } + let hsc_env'' = hscSetFlags dflags1 $ hsc_env' + { hsc_unit_env = unit_env + } + return hsc_env'' + +-- | Everything after preprocess +hscPipeline :: P m => PipeEnv -> ((HscEnv, ModSummary, HscRecompStatus)) -> m (ModIface, Maybe Linkable) +hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) = do + case hsc_recomp_status of + HscUpToDate iface mb_linkable -> return (iface, mb_linkable) + HscRecompNeeded mb_old_hash -> do + (tc_result, warnings) <- use (T_Hsc hsc_env_with_plugins mod_sum) + hscBackendAction <- use (T_HscPostTc hsc_env_with_plugins mod_sum tc_result warnings mb_old_hash ) + hscBackendPipeline pipe_env hsc_env_with_plugins mod_sum hscBackendAction + +hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, Maybe Linkable) +hscBackendPipeline pipe_env hsc_env mod_sum result = + case backend (hsc_dflags hsc_env) of + NoBackend -> + case result of + HscUpdate iface -> return (iface, Nothing) + HscRecomp {} -> (,) <$> liftIO (mkFullIface hsc_env (hscs_partial_iface result) Nothing) <*> pure Nothing + -- TODO: Why is there not a linkable? + -- Interpreter -> (,) <$> use (T_IO (mkFullIface hsc_env (hscs_partial_iface result) Nothing)) <*> pure Nothing + _ -> do + res <- hscGenBackendPipeline pipe_env hsc_env mod_sum result + liftIO (dynamicTooState (hsc_dflags hsc_env)) >>= \case + DT_OK -> do + let dflags' = setDynamicNow (hsc_dflags hsc_env) -- set "dynamicNow" + () <$ hscGenBackendPipeline pipe_env (hscSetFlags dflags' hsc_env) mod_sum result + _ -> return () + return res + +hscGenBackendPipeline :: P m + => PipeEnv + -> HscEnv + -> ModSummary + -> HscBackendAction + -> m (ModIface, Maybe Linkable) +hscGenBackendPipeline pipe_env hsc_env mod_sum result = do + let mod_name = moduleName (ms_mod mod_sum) + src_flavour = (ms_hsc_src mod_sum) + dflags = hsc_dflags hsc_env + -- MP: The ModLocation is recalculated here to get the right paths when + -- -dynamic-too is enabled. `ModLocation` should be extended with a field for + -- the location of the `dyn_o` file to avoid this recalculation. + location <- liftIO (getLocation pipe_env dflags src_flavour mod_name) + (fos, miface, mlinkable, o_file) <- use (T_HscBackend pipe_env hsc_env mod_name src_flavour location result) + final_fp <- hscPostBackendPipeline pipe_env hsc_env (ms_hsc_src mod_sum) (backend (hsc_dflags hsc_env)) (Just location) o_file + final_linkable <- + case final_fp of + -- No object file produced, bytecode or NoBackend + Nothing -> return mlinkable + Just o_fp -> do + unlinked_time <- liftIO (liftIO getCurrentTime) + final_o <- use (T_MergeForeign pipe_env hsc_env (Just location) o_fp fos) + let !linkable = LM unlinked_time + (ms_mod mod_sum) + [DotO final_o] + return (Just linkable) + return (miface, final_linkable) + +asPipeline :: P m => Bool -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath +asPipeline use_cpp pipe_env hsc_env location input_fn = do + use (T_As use_cpp pipe_env hsc_env location input_fn) + +viaCPipeline :: P m => Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m (Maybe FilePath) +viaCPipeline c_phase pipe_env hsc_env location input_fn = do + out_fn <- use (T_Cc c_phase pipe_env hsc_env input_fn) + case stop_phase pipe_env of + StopC -> return Nothing + _ -> Just <$> asPipeline False pipe_env hsc_env location out_fn + +llvmPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath +llvmPipeline pipe_env hsc_env location fp = do + opt_fn <- use (T_LlvmOpt pipe_env hsc_env fp) + llvmLlcPipeline pipe_env hsc_env location opt_fn + +llvmLlcPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath +llvmLlcPipeline pipe_env hsc_env location opt_fn = do + llc_fn <- use (T_LlvmLlc pipe_env hsc_env opt_fn) + llvmManglePipeline pipe_env hsc_env location llc_fn + +llvmManglePipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath +llvmManglePipeline pipe_env hsc_env location llc_fn = do + mangled_fn <- + if gopt Opt_NoLlvmMangler (hsc_dflags hsc_env) + then use (T_LlvmMangle pipe_env hsc_env llc_fn) + else return llc_fn + asPipeline False pipe_env hsc_env location mangled_fn + +cmmCppPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath +cmmCppPipeline pipe_env hsc_env input_fn = do + output_fn <- use (T_CmmCpp pipe_env hsc_env input_fn) + cmmPipeline pipe_env hsc_env output_fn + +cmmPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath +cmmPipeline pipe_env hsc_env input_fn = do + (fos, output_fn) <- use (T_Cmm pipe_env hsc_env input_fn) + mo_fn <- hscPostBackendPipeline pipe_env hsc_env HsSrcFile (backend (hsc_dflags hsc_env)) Nothing output_fn + case mo_fn of + Nothing -> panic "CMM pipeline - produced no .o file" + Just mo_fn -> use (T_MergeForeign pipe_env hsc_env Nothing mo_fn fos) + +hscPostBackendPipeline :: P m => PipeEnv -> HscEnv -> HscSource -> Backend -> Maybe ModLocation -> FilePath -> m (Maybe FilePath) +hscPostBackendPipeline _ _ HsBootFile _ _ _ = return Nothing +hscPostBackendPipeline _ _ HsigFile _ _ _ = return Nothing +hscPostBackendPipeline pipe_env hsc_env _ bcknd ml input_fn = + case bcknd of + ViaC -> viaCPipeline HCc pipe_env hsc_env ml input_fn + NCG -> Just <$> asPipeline False pipe_env hsc_env ml input_fn + LLVM -> Just <$> llvmPipeline pipe_env hsc_env ml input_fn + NoBackend -> return Nothing + Interpreter -> return Nothing + +-- Pipeline from a given suffix +pipelineStart :: P m => PipeEnv -> HscEnv -> FilePath -> m (Maybe FilePath) +pipelineStart pipe_env hsc_env input_fn = + fromSuffix (src_suffix pipe_env) where - (major1:major2:minor:_) = map show (versionBranch version ++ repeat 0) - --- --------------------------------------------------------------------------- --- join object files into a single relocatable object file, using ld -r + stop_after = stop_phase pipe_env + frontend :: P m => HscSource -> m (Maybe FilePath) + frontend sf = case stop_after of + StopPreprocess -> do + -- The actual output from preprocessing + (_, out_fn) <- preprocessPipeline pipe_env hsc_env input_fn + let logger = hsc_logger hsc_env + -- Sometimes, a compilation phase doesn't actually generate any output + -- (eg. the CPP phase when -fcpp is not turned on). If we end on this + -- stage, but we wanted to keep the output, then we have to explicitly + -- copy the file, remembering to prepend a {-# LINE #-} pragma so that + -- further compilation stages can tell what the original filename was. + -- File name we expected the output to have + final_fn <- liftIO $ phaseOutputFilenameNew (Hsc HsSrcFile) pipe_env hsc_env Nothing + when (final_fn /= out_fn) $ do + let msg = "Copying `" ++ input_fn ++"' to `" ++ final_fn ++ "'" + line_prag = "{-# LINE 1 \"" ++ src_filename pipe_env ++ "\" #-}\n" + liftIO (showPass logger msg) + liftIO (copyWithHeader line_prag input_fn final_fn) + return Nothing + _ -> objFromLinkable <$> fullPipeline pipe_env hsc_env input_fn sf + c :: P m => Phase -> m (Maybe FilePath) + c phase = viaCPipeline phase pipe_env hsc_env Nothing input_fn + as :: P m => Bool -> m (Maybe FilePath) + as use_cpp = Just <$> asPipeline use_cpp pipe_env hsc_env Nothing input_fn + + objFromLinkable (_, Just (LM _ _ [DotO lnk])) = Just lnk + objFromLinkable _ = Nothing + + + fromSuffix :: P m => String -> m (Maybe FilePath) + fromSuffix "lhs" = frontend HsSrcFile + fromSuffix "lhs-boot" = frontend HsBootFile + fromSuffix "lhsig" = frontend HsigFile + fromSuffix "hs" = frontend HsSrcFile + fromSuffix "hs-boot" = frontend HsBootFile + fromSuffix "hsig" = frontend HsigFile + fromSuffix "hscpp" = frontend HsSrcFile + fromSuffix "hspp" = frontend HsSrcFile + fromSuffix "hc" = c HCc + fromSuffix "c" = c Cc + fromSuffix "cpp" = c Ccxx + fromSuffix "C" = c Cc + fromSuffix "m" = c Cobjc + fromSuffix "M" = c Cobjcxx + fromSuffix "mm" = c Cobjcxx + fromSuffix "cc" = c Ccxx + fromSuffix "cxx" = c Ccxx + fromSuffix "s" = as False + fromSuffix "S" = as True + fromSuffix "ll" = Just <$> llvmPipeline pipe_env hsc_env Nothing input_fn + fromSuffix "bc" = Just <$> llvmLlcPipeline pipe_env hsc_env Nothing input_fn + fromSuffix "lm_s" = Just <$> llvmManglePipeline pipe_env hsc_env Nothing input_fn + fromSuffix "o" = return (Just input_fn) + fromSuffix "cmm" = Just <$> cmmCppPipeline pipe_env hsc_env input_fn + fromSuffix "cmmcpp" = Just <$> cmmPipeline pipe_env hsc_env input_fn + fromSuffix _ = return (Just input_fn) {- -Note [Produce big objects on Windows] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Windows Portable Executable object format has a limit of 32k sections, which -we tend to blow through pretty easily. Thankfully, there is a "big object" -extension, which raises this limit to 2^32. However, it must be explicitly -enabled in the toolchain: - - * the assembler accepts the -mbig-obj flag, which causes it to produce a - bigobj-enabled COFF object. - - * the linker accepts the --oformat pe-bigobj-x86-64 flag. Despite what the name - suggests, this tells the linker to produce a bigobj-enabled COFF object, no a - PE executable. -We must enable bigobj output in a few places: +Note [The Pipeline Monad] +~~~~~~~~~~~~~~~~~~~~~~~~~ - * When merging object files (GHC.Driver.Pipeline.joinObjectFiles) +The pipeline is represented as a free monad by the `TPipelineClass` type synonym, +which stipulates the general monadic interface for the pipeline and `MonadUse`, instantiated +to `TPhase`, which indicates the actions available in the pipeline. - * When assembling (GHC.Driver.Pipeline.runPhase (RealPhase As ...)) +The `TPhase` actions correspond to different compiled phases, they are executed by +the 'runPhase' function which interprets each action into IO. -Unfortunately the big object format is not supported on 32-bit targets so -none of this can be used in that case. +The idea in the future is that we can now implement different instiations of +`TPipelineClass` to give different behaviours that the default `HookedPhase` implementation: +* Additional logging of different phases +* Automatic parrelism (in the style of shake) +* Easy consumption by external tools such as ghcide +* Easier to create your own pipeline and extend existing pipelines. -Note [Merging object files for GHCi] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -GHCi can usually loads standard linkable object files using GHC's linker -implementation. However, most users build their projects with -split-sections, -meaning that such object files can have an extremely high number of sections. -As the linker must map each of these sections individually, loading such object -files is very inefficient. - -To avoid this inefficiency, we use the linker's `-r` flag and a linker script -to produce a merged relocatable object file. This file will contain a singe -text section section and can consequently be mapped far more efficiently. As -gcc tends to do unpredictable things to our linker command line, we opt to -invoke ld directly in this case, in contrast to our usual strategy of linking -via gcc. - --} - -joinObjectFiles :: Logger -> TmpFs -> DynFlags -> [FilePath] -> FilePath -> IO () -joinObjectFiles logger tmpfs dflags o_files output_fn = do - let toolSettings' = toolSettings dflags - ldIsGnuLd = toolSettings_ldIsGnuLd toolSettings' - osInfo = platformOS (targetPlatform dflags) - ld_r args = GHC.SysTools.runMergeObjects logger tmpfs dflags ( - -- See Note [Produce big objects on Windows] - concat - [ [GHC.SysTools.Option "--oformat", GHC.SysTools.Option "pe-bigobj-x86-64"] - | OSMinGW32 == osInfo - , not $ target32Bit (targetPlatform dflags) - ] - ++ map GHC.SysTools.Option ld_build_id - ++ [ GHC.SysTools.Option "-o", - GHC.SysTools.FileOption "" output_fn ] - ++ args) - - -- suppress the generation of the .note.gnu.build-id section, - -- which we don't need and sometimes causes ld to emit a - -- warning: - ld_build_id | toolSettings_ldSupportsBuildId toolSettings' = ["--build-id=none"] - | otherwise = [] - - if ldIsGnuLd - then do - script <- newTempName logger tmpfs dflags TFL_CurrentModule "ldscript" - cwd <- getCurrentDirectory - let o_files_abs = map (\x -> "\"" ++ (cwd </> x) ++ "\"") o_files - writeFile script $ "INPUT(" ++ unwords o_files_abs ++ ")" - ld_r [GHC.SysTools.FileOption "" script] - else if toolSettings_ldSupportsFilelist toolSettings' - then do - filelist <- newTempName logger tmpfs dflags TFL_CurrentModule "filelist" - writeFile filelist $ unlines o_files - ld_r [GHC.SysTools.Option "-filelist", - GHC.SysTools.FileOption "" filelist] - else - ld_r (map (GHC.SysTools.FileOption "") o_files) - --- ----------------------------------------------------------------------------- --- Misc. +The structure of the code as a free monad also means that the return type of each +phase is a lot more flexible. - --- | What phase to run after one of the backend code generators has run -hscPostBackendPhase :: HscSource -> Backend -> Phase -hscPostBackendPhase HsBootFile _ = StopLn -hscPostBackendPhase HsigFile _ = StopLn -hscPostBackendPhase _ bcknd = - case bcknd of - ViaC -> HCc - NCG -> As False - LLVM -> LlvmOpt - NoBackend -> StopLn - Interpreter -> StopLn - -touchObjectFile :: Logger -> DynFlags -> FilePath -> IO () -touchObjectFile logger dflags path = do - createDirectoryIfMissing True $ takeDirectory path - GHC.SysTools.touch logger dflags "Touching object file" path - --- | Find out path to @ghcversion.h@ file -getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath -getGhcVersionPathName dflags unit_env = do - candidates <- case ghcVersionFile dflags of - Just path -> return [path] - Nothing -> do - ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env [rtsUnitId]) - return ((</> "ghcversion.h") <$> collectIncludeDirs ps) - - found <- filterM doesFileExist candidates - case found of - [] -> throwGhcExceptionIO (InstallationError - ("ghcversion.h missing; tried: " - ++ intercalate ", " candidates)) - (x:_) -> return x - --- Note [-fPIC for assembler] --- When compiling .c source file GHC's driver pipeline basically --- does the following two things: --- 1. ${CC} -S 'PIC_CFLAGS' source.c --- 2. ${CC} -x assembler -c 'PIC_CFLAGS' source.S --- --- Why do we need to pass 'PIC_CFLAGS' both to C compiler and assembler? --- Because on some architectures (at least sparc32) assembler also chooses --- the relocation type! --- Consider the following C module: --- --- /* pic-sample.c */ --- int v; --- void set_v (int n) { v = n; } --- int get_v (void) { return v; } --- --- $ gcc -S -fPIC pic-sample.c --- $ gcc -c pic-sample.s -o pic-sample.no-pic.o # incorrect binary --- $ gcc -c -fPIC pic-sample.s -o pic-sample.pic.o # correct binary --- --- $ objdump -r -d pic-sample.pic.o > pic-sample.pic.o.od --- $ objdump -r -d pic-sample.no-pic.o > pic-sample.no-pic.o.od --- $ diff -u pic-sample.pic.o.od pic-sample.no-pic.o.od --- --- Most of architectures won't show any difference in this test, but on sparc32 --- the following assembly snippet: --- --- sethi %hi(_GLOBAL_OFFSET_TABLE_-8), %l7 --- --- generates two kinds or relocations, only 'R_SPARC_PC22' is correct: --- --- 3c: 2f 00 00 00 sethi %hi(0), %l7 --- - 3c: R_SPARC_PC22 _GLOBAL_OFFSET_TABLE_-0x8 --- + 3c: R_SPARC_HI22 _GLOBAL_OFFSET_TABLE_-0x8 - -{- Note [Don't normalise input filenames] - -Summary - We used to normalise input filenames when starting the unlit phase. This - broke hpc in `--make` mode with imported literate modules (#2991). - -Introduction - 1) --main - When compiling a module with --main, GHC scans its imports to find out which - other modules it needs to compile too. It turns out that there is a small - difference between saying `ghc --make A.hs`, when `A` imports `B`, and - specifying both modules on the command line with `ghc --make A.hs B.hs`. In - the former case, the filename for B is inferred to be './B.hs' instead of - 'B.hs'. - - 2) unlit - When GHC compiles a literate haskell file, the source code first needs to go - through unlit, which turns it into normal Haskell source code. At the start - of the unlit phase, in `Driver.Pipeline.runPhase`, we call unlit with the - option `-h` and the name of the original file. We used to normalise this - filename using System.FilePath.normalise, which among other things removes - an initial './'. unlit then uses that filename in #line directives that it - inserts in the transformed source code. - - 3) SrcSpan - A SrcSpan represents a portion of a source code file. It has fields - linenumber, start column, end column, and also a reference to the file it - originated from. The SrcSpans for a literate haskell file refer to the - filename that was passed to unlit -h. - - 4) -fhpc - At some point during compilation with -fhpc, in the function - `GHC.HsToCore.Coverage.isGoodTickSrcSpan`, we compare the filename that a - `SrcSpan` refers to with the name of the file we are currently compiling. - For some reason I don't yet understand, they can sometimes legitimally be - different, and then hpc ignores that SrcSpan. - -Problem - When running `ghc --make -fhpc A.hs`, where `A.hs` imports the literate - module `B.lhs`, `B` is inferred to be in the file `./B.lhs` (1). At the - start of the unlit phase, the name `./B.lhs` is normalised to `B.lhs` (2). - Therefore the SrcSpans of `B` refer to the file `B.lhs` (3), but we are - still compiling `./B.lhs`. Hpc thinks these two filenames are different (4), - doesn't include ticks for B, and we have unhappy customers (#2991). - -Solution - Do not normalise `input_fn` when starting the unlit phase. - -Alternative solution - Another option would be to not compare the two filenames on equality, but to - use System.FilePath.equalFilePath. That function first normalises its - arguments. The problem is that by the time we need to do the comparison, the - filenames have been turned into FastStrings, probably for performance - reasons, so System.FilePath.equalFilePath can not be used directly. - -Archeology - The call to `normalise` was added in a commit called "Fix slash - direction on Windows with the new filePath code" (c9b6b5e8). The problem - that commit was addressing has since been solved in a different manner, in a - commit called "Fix the filename passed to unlit" (1eedbc6b). So the - `normalise` is no longer necessary. -} diff --git a/compiler/GHC/Driver/Pipeline.hs-boot b/compiler/GHC/Driver/Pipeline.hs-boot new file mode 100644 index 0000000000..3467ff4ced --- /dev/null +++ b/compiler/GHC/Driver/Pipeline.hs-boot @@ -0,0 +1,13 @@ +module GHC.Driver.Pipeline where + + +import GHC.Driver.Env.Types ( HscEnv ) +import GHC.ForeignSrcLang ( ForeignSrcLang ) +import GHC.Prelude (FilePath, IO) +import GHC.Unit.Module.Location (ModLocation) +import GHC.Unit.Module.Name (ModuleName) +import GHC.Driver.Session (DynFlags) + +-- These are used in GHC.Driver.Pipeline.Execute, but defined in terms of runPipeline +compileForeign :: HscEnv -> ForeignSrcLang -> FilePath -> IO FilePath +compileEmptyStub :: DynFlags -> HscEnv -> FilePath -> ModLocation -> ModuleName -> IO () diff --git a/compiler/GHC/Driver/Pipeline/Execute.hs b/compiler/GHC/Driver/Pipeline/Execute.hs new file mode 100644 index 0000000000..d843f29056 --- /dev/null +++ b/compiler/GHC/Driver/Pipeline/Execute.hs @@ -0,0 +1,1263 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE GADTs #-} +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} +#include "ghcplatform.h" + +{- Functions for providing the default interpretation of the 'TPhase' actions +-} +module GHC.Driver.Pipeline.Execute where + +import GHC.Prelude +import Control.Monad +import Control.Monad.IO.Class +import Control.Monad.Catch +import GHC.Driver.Hooks +import Control.Monad.Trans.Reader +import GHC.Driver.Pipeline.Monad +import GHC.Driver.Pipeline.Phases +import GHC.Driver.Env hiding (Hsc) +import GHC.Unit.Module.Location +import GHC.Driver.Phases +import GHC.Unit.Module.Name ( ModuleName ) +import GHC.Unit.Types +import GHC.Types.SourceFile +import GHC.Unit.Module.Status +import GHC.Unit.Module.ModIface +import GHC.Linker.Types +import GHC.Driver.Backend +import GHC.Driver.Session +import GHC.Driver.CmdLine +import GHC.Unit.Module.ModSummary +import qualified GHC.LanguageExtensions as LangExt +import GHC.Types.SrcLoc +import GHC.Driver.Main +import GHC.Tc.Types +import GHC.Types.Error +import GHC.Driver.Errors.Types +import GHC.Fingerprint +import GHC.Utils.Logger +import GHC.Utils.TmpFs +import GHC.Platform +import Data.List (intercalate, isInfixOf) +import GHC.Unit.Env +import GHC.SysTools.Info +import GHC.Utils.Error +import Data.Maybe +import GHC.CmmToLlvm.Mangler +import GHC.SysTools +import GHC.Utils.Panic.Plain +import System.Directory +import System.FilePath +import GHC.Utils.Misc +import GHC.Utils.Outputable +import qualified Control.Exception as Exception +import GHC.Unit.Info +import GHC.Unit.State +import GHC.Unit.Home +import GHC.Data.Maybe +import GHC.Iface.Make +import Data.Time +import GHC.Driver.Config.Parser +import GHC.Parser.Header +import GHC.Data.StringBuffer +import GHC.Types.SourceError +import GHC.Unit.Finder +import GHC.Runtime.Loader +import Data.IORef +import GHC.Types.Name.Env +import GHC.Platform.Ways +import GHC.Platform.ArchOS +import GHC.CmmToLlvm.Base ( llvmVersionList ) +import {-# SOURCE #-} GHC.Driver.Pipeline (compileForeign, compileEmptyStub) +import GHC.Settings +import System.IO +import GHC.Linker.ExtraObj +import GHC.Linker.Dynamic +import Data.Version +import GHC.Utils.Panic + +newtype HookedUse a = HookedUse { runHookedUse :: (Hooks, PhaseHook) -> IO a } + deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch) via (ReaderT (Hooks, PhaseHook) IO) + +instance MonadUse TPhase HookedUse where + use fa = HookedUse $ \(hooks, (PhaseHook k)) -> + case runPhaseHook hooks of + Nothing -> k fa + Just (PhaseHook h) -> h fa + +-- | The default mechanism to run a pipeline, see Note [The Pipeline Monad] +runPipeline :: Hooks -> HookedUse a -> IO a +runPipeline hooks pipeline = runHookedUse pipeline (hooks, PhaseHook runPhase) + +-- | Default interpretation of each phase, in terms of IO. +runPhase :: TPhase out -> IO out +runPhase (T_Unlit pipe_env hsc_env inp_path) = do + out_path <- phaseOutputFilenameNew (Cpp HsSrcFile) pipe_env hsc_env Nothing + runUnlitPhase hsc_env inp_path out_path +runPhase (T_FileArgs hsc_env inp_path) = getFileArgs hsc_env inp_path +runPhase (T_Cpp pipe_env hsc_env inp_path) = do + out_path <- phaseOutputFilenameNew (HsPp HsSrcFile) pipe_env hsc_env Nothing + runCppPhase hsc_env inp_path out_path +runPhase (T_HsPp pipe_env hsc_env origin_path inp_path) = do + out_path <- phaseOutputFilenameNew (Hsc HsSrcFile) pipe_env hsc_env Nothing + runHsPpPhase hsc_env origin_path inp_path out_path +runPhase (T_HscRecomp pipe_env hsc_env fp hsc_src) = do + runHscPhase pipe_env hsc_env fp hsc_src +runPhase (T_Hsc hsc_env mod_sum) = runHscTcPhase hsc_env mod_sum +runPhase (T_HscPostTc hsc_env ms fer m mfi) = + runHscPostTcPhase hsc_env ms fer m mfi +runPhase (T_HscBackend pipe_env hsc_env mod_name hsc_src location x) = do + runHscBackendPhase pipe_env hsc_env mod_name hsc_src location x +runPhase (T_CmmCpp pipe_env hsc_env input_fn) = do + output_fn <- phaseOutputFilenameNew Cmm pipe_env hsc_env Nothing + doCpp (hsc_logger hsc_env) + (hsc_tmpfs hsc_env) + (hsc_dflags hsc_env) + (hsc_unit_env hsc_env) + False{-not raw-} + input_fn output_fn + return output_fn +runPhase (T_Cmm pipe_env hsc_env input_fn) = do + let dflags = hsc_dflags hsc_env + let next_phase = hscPostBackendPhase HsSrcFile (backend dflags) + output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env Nothing + mstub <- hscCompileCmmFile hsc_env input_fn output_fn + stub_o <- mapM (compileStub hsc_env) mstub + let foreign_os = (maybeToList stub_o) + return (foreign_os, output_fn) + +runPhase (T_Cc phase pipe_env hsc_env input_fn) = runCcPhase phase pipe_env hsc_env input_fn +runPhase (T_As cpp pipe_env hsc_env location input_fn) = do + runAsPhase cpp pipe_env hsc_env location input_fn +runPhase (T_LlvmOpt pipe_env hsc_env input_fn) = + runLlvmOptPhase pipe_env hsc_env input_fn +runPhase (T_LlvmLlc pipe_env hsc_env input_fn) = + runLlvmLlcPhase pipe_env hsc_env input_fn +runPhase (T_LlvmMangle pipe_env hsc_env input_fn) = + runLlvmManglePhase pipe_env hsc_env input_fn +runPhase (T_MergeForeign pipe_env hsc_env location input_fn fos) = + runMergeForeign pipe_env hsc_env location input_fn fos + +runLlvmManglePhase :: PipeEnv -> HscEnv -> FilePath -> IO [Char] +runLlvmManglePhase pipe_env hsc_env input_fn = do + let next_phase = As False + output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env Nothing + let dflags = hsc_dflags hsc_env + llvmFixupAsm (targetPlatform dflags) input_fn output_fn + return output_fn + +runMergeForeign :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> [FilePath] -> IO FilePath +runMergeForeign _pipe_env hsc_env _location input_fn foreign_os = do + if null foreign_os + then return input_fn + else do + -- Work around a binutil < 2.31 bug where you can't merge objects if the output file + -- is one of the inputs + new_o <- newTempName (hsc_logger hsc_env) (hsc_tmpfs hsc_env) (hsc_dflags hsc_env) TFL_CurrentModule "o" + copyFile input_fn new_o + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + let tmpfs = hsc_tmpfs hsc_env + joinObjectFiles logger tmpfs dflags (new_o : foreign_os) input_fn + return input_fn + +runLlvmLlcPhase :: PipeEnv -> HscEnv -> FilePath -> IO FilePath +runLlvmLlcPhase pipe_env hsc_env input_fn = do + -- Note [Clamping of llc optimizations] + -- + -- See #13724 + -- + -- we clamp the llc optimization between [1,2]. This is because passing -O0 + -- to llc 3.9 or llc 4.0, the naive register allocator can fail with + -- + -- Error while trying to spill R1 from class GPR: Cannot scavenge register + -- without an emergency spill slot! + -- + -- Observed at least with target 'arm-unknown-linux-gnueabihf'. + -- + -- + -- With LLVM4, llc -O3 crashes when ghc-stage1 tries to compile + -- rts/HeapStackCheck.cmm + -- + -- llc -O3 '-mtriple=arm-unknown-linux-gnueabihf' -enable-tbaa /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc -o /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_7.lm_s + -- 0 llc 0x0000000102ae63e8 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40 + -- 1 llc 0x0000000102ae69a6 SignalHandler(int) + 358 + -- 2 libsystem_platform.dylib 0x00007fffc23f4b3a _sigtramp + 26 + -- 3 libsystem_c.dylib 0x00007fffc226498b __vfprintf + 17876 + -- 4 llc 0x00000001029d5123 llvm::SelectionDAGISel::LowerArguments(llvm::Function const&) + 5699 + -- 5 llc 0x0000000102a21a35 llvm::SelectionDAGISel::SelectAllBasicBlocks(llvm::Function const&) + 3381 + -- 6 llc 0x0000000102a202b1 llvm::SelectionDAGISel::runOnMachineFunction(llvm::MachineFunction&) + 1457 + -- 7 llc 0x0000000101bdc474 (anonymous namespace)::ARMDAGToDAGISel::runOnMachineFunction(llvm::MachineFunction&) + 20 + -- 8 llc 0x00000001025573a6 llvm::MachineFunctionPass::runOnFunction(llvm::Function&) + 134 + -- 9 llc 0x000000010274fb12 llvm::FPPassManager::runOnFunction(llvm::Function&) + 498 + -- 10 llc 0x000000010274fd23 llvm::FPPassManager::runOnModule(llvm::Module&) + 67 + -- 11 llc 0x00000001027501b8 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 920 + -- 12 llc 0x000000010195f075 compileModule(char**, llvm::LLVMContext&) + 12133 + -- 13 llc 0x000000010195bf0b main + 491 + -- 14 libdyld.dylib 0x00007fffc21e5235 start + 1 + -- Stack dump: + -- 0. Program arguments: llc -O3 -mtriple=arm-unknown-linux-gnueabihf -enable-tbaa /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc -o /var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_7.lm_s + -- 1. Running pass 'Function Pass Manager' on module '/var/folders/fv/xqjrpfj516n5xq_m_ljpsjx00000gn/T/ghc33674_0/ghc_6.bc'. + -- 2. Running pass 'ARM Instruction Selection' on function '@"stg_gc_f1$def"' + -- + -- Observed at least with -mtriple=arm-unknown-linux-gnueabihf -enable-tbaa + -- + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + llvmOpts = case optLevel dflags of + 0 -> "-O1" -- required to get the non-naive reg allocator. Passing -regalloc=greedy is not sufficient. + 1 -> "-O1" + _ -> "-O2" + + defaultOptions = map GHC.SysTools.Option . concatMap words . snd + $ unzip (llvmOptions dflags) + optFlag = if null (getOpts dflags opt_lc) + then map GHC.SysTools.Option $ words llvmOpts + else [] + + next_phase <- if -- hidden debugging flag '-dno-llvm-mangler' to skip mangling + | gopt Opt_NoLlvmMangler dflags -> return (As False) + | otherwise -> return LlvmMangle + + output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env Nothing + + GHC.SysTools.runLlvmLlc logger dflags + ( optFlag + ++ defaultOptions + ++ [ GHC.SysTools.FileOption "" input_fn + , GHC.SysTools.Option "-o" + , GHC.SysTools.FileOption "" output_fn + ] + ) + + return output_fn + +runLlvmOptPhase :: PipeEnv -> HscEnv -> FilePath -> IO FilePath +runLlvmOptPhase pipe_env hsc_env input_fn = do + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + let -- we always (unless -optlo specified) run Opt since we rely on it to + -- fix up some pretty big deficiencies in the code we generate + optIdx = max 0 $ min 2 $ optLevel dflags -- ensure we're in [0,2] + llvmOpts = case lookup optIdx $ llvmPasses $ llvmConfig dflags of + Just passes -> passes + Nothing -> panic ("runPhase LlvmOpt: llvm-passes file " + ++ "is missing passes for level " + ++ show optIdx) + defaultOptions = map GHC.SysTools.Option . concat . fmap words . fst + $ unzip (llvmOptions dflags) + + -- don't specify anything if user has specified commands. We do this + -- for opt but not llc since opt is very specifically for optimisation + -- passes only, so if the user is passing us extra options we assume + -- they know what they are doing and don't get in the way. + optFlag = if null (getOpts dflags opt_lo) + then map GHC.SysTools.Option $ words llvmOpts + else [] + + output_fn <- phaseOutputFilenameNew LlvmLlc pipe_env hsc_env Nothing + + GHC.SysTools.runLlvmOpt logger dflags + ( optFlag + ++ defaultOptions ++ + [ GHC.SysTools.FileOption "" input_fn + , GHC.SysTools.Option "-o" + , GHC.SysTools.FileOption "" output_fn] + ) + + return output_fn + + +runAsPhase :: Bool -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath +runAsPhase with_cpp pipe_env hsc_env location input_fn = do + let dflags = hsc_dflags hsc_env + let logger = hsc_logger hsc_env + let unit_env = hsc_unit_env hsc_env + let platform = ue_platform unit_env + + -- LLVM from version 3.0 onwards doesn't support the OS X system + -- assembler, so we use clang as the assembler instead. (#5636) + let (as_prog, get_asm_info) | backend dflags == LLVM + , platformOS platform == OSDarwin + = (GHC.SysTools.runClang, pure Clang) + | otherwise + = (GHC.SysTools.runAs, getAssemblerInfo logger dflags) + + asmInfo <- get_asm_info + + let cmdline_include_paths = includePaths dflags + let pic_c_flags = picCCOpts dflags + + output_fn <- phaseOutputFilenameNew StopLn pipe_env hsc_env location + + -- we create directories for the object file, because it + -- might be a hierarchical module. + createDirectoryIfMissing True (takeDirectory output_fn) + + let global_includes = [ GHC.SysTools.Option ("-I" ++ p) + | p <- includePathsGlobal cmdline_include_paths ] + let local_includes = [ GHC.SysTools.Option ("-iquote" ++ p) + | p <- includePathsQuote cmdline_include_paths ++ + includePathsQuoteImplicit cmdline_include_paths] + let runAssembler inputFilename outputFilename + = withAtomicRename outputFilename $ \temp_outputFilename -> + as_prog + logger dflags + (local_includes ++ global_includes + -- See Note [-fPIC for assembler] + ++ map GHC.SysTools.Option pic_c_flags + -- See Note [Produce big objects on Windows] + ++ [ GHC.SysTools.Option "-Wa,-mbig-obj" + | platformOS (targetPlatform dflags) == OSMinGW32 + , not $ target32Bit (targetPlatform dflags) + ] + + -- We only support SparcV9 and better because V8 lacks an atomic CAS + -- instruction so we have to make sure that the assembler accepts the + -- instruction set. Note that the user can still override this + -- (e.g., -mcpu=ultrasparc). GCC picks the "best" -mcpu flag + -- regardless of the ordering. + -- + -- This is a temporary hack. + ++ (if platformArch (targetPlatform dflags) == ArchSPARC + then [GHC.SysTools.Option "-mcpu=v9"] + else []) + ++ (if any (asmInfo ==) [Clang, AppleClang, AppleClang51] + then [GHC.SysTools.Option "-Qunused-arguments"] + else []) + ++ [ GHC.SysTools.Option "-x" + , if with_cpp + then GHC.SysTools.Option "assembler-with-cpp" + else GHC.SysTools.Option "assembler" + , GHC.SysTools.Option "-c" + , GHC.SysTools.FileOption "" inputFilename + , GHC.SysTools.Option "-o" + , GHC.SysTools.FileOption "" temp_outputFilename + ]) + + debugTraceMsg logger 4 (text "Running the assembler") + runAssembler input_fn output_fn + + return output_fn + + +runCcPhase :: Phase -> PipeEnv -> HscEnv -> FilePath -> IO FilePath +runCcPhase cc_phase pipe_env hsc_env input_fn = do + let dflags = hsc_dflags hsc_env + let logger = hsc_logger hsc_env + let unit_env = hsc_unit_env hsc_env + let home_unit = hsc_home_unit hsc_env + let tmpfs = hsc_tmpfs hsc_env + let platform = ue_platform unit_env + let hcc = cc_phase `eqPhase` HCc + + let cmdline_include_paths = includePaths dflags + + -- HC files have the dependent packages stamped into them + pkgs <- if hcc then getHCFilePackages input_fn else return [] + + -- add package include paths even if we're just compiling .c + -- files; this is the Value Add(TM) that using ghc instead of + -- gcc gives you :) + ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env pkgs) + let pkg_include_dirs = collectIncludeDirs ps + let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) [] + (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs) + let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) [] + (includePathsQuote cmdline_include_paths ++ + includePathsQuoteImplicit cmdline_include_paths) + let include_paths = include_paths_quote ++ include_paths_global + + -- pass -D or -optP to preprocessor when compiling foreign C files + -- (#16737). Doing it in this way is simpler and also enable the C + -- compiler to perform preprocessing and parsing in a single pass, + -- but it may introduce inconsistency if a different pgm_P is specified. + let more_preprocessor_opts = concat + [ ["-Xpreprocessor", i] + | not hcc + , i <- getOpts dflags opt_P + ] + + let gcc_extra_viac_flags = extraGccViaCFlags dflags + let pic_c_flags = picCCOpts dflags + + let verbFlags = getVerbFlags dflags + + -- cc-options are not passed when compiling .hc files. Our + -- hc code doesn't not #include any header files anyway, so these + -- options aren't necessary. + let pkg_extra_cc_opts + | hcc = [] + | otherwise = collectExtraCcOpts ps + + let framework_paths + | platformUsesFrameworks platform + = let pkgFrameworkPaths = collectFrameworksDirs ps + cmdlineFrameworkPaths = frameworkPaths dflags + in map ("-F"++) (cmdlineFrameworkPaths ++ pkgFrameworkPaths) + | otherwise + = [] + + let cc_opt | optLevel dflags >= 2 = [ "-O2" ] + | optLevel dflags >= 1 = [ "-O" ] + | otherwise = [] + + -- Decide next phase + let next_phase = As False + output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env Nothing + + let + more_hcc_opts = + -- on x86 the floating point regs have greater precision + -- than a double, which leads to unpredictable results. + -- By default, we turn this off with -ffloat-store unless + -- the user specified -fexcess-precision. + (if platformArch platform == ArchX86 && + not (gopt Opt_ExcessPrecision dflags) + then [ "-ffloat-store" ] + else []) ++ + + -- gcc's -fstrict-aliasing allows two accesses to memory + -- to be considered non-aliasing if they have different types. + -- This interacts badly with the C code we generate, which is + -- very weakly typed, being derived from C--. + ["-fno-strict-aliasing"] + + ghcVersionH <- getGhcVersionPathName dflags unit_env + + GHC.SysTools.runCc (phaseForeignLanguage cc_phase) logger tmpfs dflags ( + [ GHC.SysTools.FileOption "" input_fn + , GHC.SysTools.Option "-o" + , GHC.SysTools.FileOption "" output_fn + ] + ++ map GHC.SysTools.Option ( + pic_c_flags + + -- Stub files generated for foreign exports references the runIO_closure + -- and runNonIO_closure symbols, which are defined in the base package. + -- These symbols are imported into the stub.c file via RtsAPI.h, and the + -- way we do the import depends on whether we're currently compiling + -- the base package or not. + ++ (if platformOS platform == OSMinGW32 && + isHomeUnitId home_unit baseUnitId + then [ "-DCOMPILING_BASE_PACKAGE" ] + else []) + + -- We only support SparcV9 and better because V8 lacks an atomic CAS + -- instruction. Note that the user can still override this + -- (e.g., -mcpu=ultrasparc) as GCC picks the "best" -mcpu flag + -- regardless of the ordering. + -- + -- This is a temporary hack. See #2872, commit + -- 5bd3072ac30216a505151601884ac88bf404c9f2 + ++ (if platformArch platform == ArchSPARC + then ["-mcpu=v9"] + else []) + + -- GCC 4.6+ doesn't like -Wimplicit when compiling C++. + ++ (if (cc_phase /= Ccxx && cc_phase /= Cobjcxx) + then ["-Wimplicit"] + else []) + + ++ (if hcc + then gcc_extra_viac_flags ++ more_hcc_opts + else []) + ++ verbFlags + ++ [ "-S" ] + ++ cc_opt + ++ [ "-include", ghcVersionH ] + ++ framework_paths + ++ include_paths + ++ more_preprocessor_opts + ++ pkg_extra_cc_opts + )) + + return output_fn + +-- This is where all object files get written from, for hs-boot and hsig files as well. +runHscBackendPhase :: PipeEnv + -> HscEnv + -> ModuleName + -> HscSource + -> ModLocation + -> HscBackendAction + -> IO ([FilePath], ModIface, Maybe Linkable, FilePath) +runHscBackendPhase pipe_env hsc_env mod_name src_flavour location result = do + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + o_file = ml_obj_file location -- The real object file + next_phase = hscPostBackendPhase src_flavour (backend dflags) + case result of + HscUpdate iface -> + do + case src_flavour of + HsigFile -> do + -- We need to create a REAL but empty .o file + -- because we are going to attempt to put it in a library + let input_fn = expectJust "runPhase" (ml_hs_file location) + basename = dropExtension input_fn + compileEmptyStub dflags hsc_env basename location mod_name + + -- In the case of hs-boot files, generate a dummy .o-boot + -- stamp file for the benefit of Make + HsBootFile -> touchObjectFile logger dflags o_file + HsSrcFile -> panic "HscUpdate not relevant for HscSrcFile" + + return ([], iface, Nothing, o_file) + HscRecomp { hscs_guts = cgguts, + hscs_mod_location = mod_location, + hscs_partial_iface = partial_iface, + hscs_old_iface_hash = mb_old_iface_hash + } + -> case backend dflags of + NoBackend -> panic "HscRecomp not relevant for NoBackend" + Interpreter -> do + -- In interpreted mode the regular codeGen backend is not run so we + -- generate a interface without codeGen info. + final_iface <- mkFullIface hsc_env partial_iface Nothing + hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash location + + (hasStub, comp_bc, spt_entries) <- hscInteractive hsc_env cgguts mod_location + + stub_o <- case hasStub of + Nothing -> return [] + Just stub_c -> do + stub_o <- compileStub hsc_env stub_c + return [DotO stub_o] + + let hs_unlinked = [BCOs comp_bc spt_entries] + unlinked_time <- getCurrentTime + let !linkable = LM unlinked_time (mkHomeModule (hsc_home_unit hsc_env) mod_name) + (hs_unlinked ++ stub_o) + return ([], final_iface, Just linkable, panic "interpreter") + _ -> do + output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env (Just location) + (outputFilename, mStub, foreign_files, cg_infos) <- + hscGenHardCode hsc_env cgguts mod_location output_fn + final_iface <- mkFullIface hsc_env partial_iface (Just cg_infos) + + -- See Note [Writing interface files] + hscMaybeWriteIface logger dflags False final_iface mb_old_iface_hash mod_location + + stub_o <- mapM (compileStub hsc_env) mStub + foreign_os <- + mapM (uncurry (compileForeign hsc_env)) foreign_files + let fos = (maybe [] return stub_o ++ foreign_os) + + -- This is awkward, no linkable is produced here because we still + -- have some way to do before the object file is produced + -- In future we can split up the driver logic more so that this function + -- is in TPipeline and in this branch we can invoke the rest of the backend phase. + return (fos, final_iface, Nothing, outputFilename) + + +runUnlitPhase :: HscEnv -> FilePath -> FilePath -> IO FilePath +runUnlitPhase hsc_env input_fn output_fn = do + let + -- escape the characters \, ", and ', but don't try to escape + -- Unicode or anything else (so we don't use Util.charToC + -- here). If we get this wrong, then in + -- GHC.HsToCore.Coverage.isGoodTickSrcSpan where we check that the filename in + -- a SrcLoc is the same as the source filenaame, the two will + -- look bogusly different. See test: + -- libraries/hpc/tests/function/subdir/tough2.hs + escape ('\\':cs) = '\\':'\\': escape cs + escape ('\"':cs) = '\\':'\"': escape cs + escape ('\'':cs) = '\\':'\'': escape cs + escape (c:cs) = c : escape cs + escape [] = [] + + let flags = [ -- The -h option passes the file name for unlit to + -- put in a #line directive + GHC.SysTools.Option "-h" + -- See Note [Don't normalise input filenames]. + , GHC.SysTools.Option $ escape input_fn + , GHC.SysTools.FileOption "" input_fn + , GHC.SysTools.FileOption "" output_fn + ] + + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + GHC.SysTools.runUnlit logger dflags flags + + return output_fn + +getFileArgs :: HscEnv -> FilePath -> IO ((DynFlags, [Warn])) +getFileArgs hsc_env input_fn = do + let dflags0 = hsc_dflags hsc_env + parser_opts = initParserOpts dflags0 + src_opts <- getOptionsFromFile parser_opts input_fn + (dflags1, unhandled_flags, warns) + <- parseDynamicFilePragma dflags0 src_opts + checkProcessArgsResult unhandled_flags + return (dflags1, warns) + +runCppPhase :: HscEnv -> FilePath -> FilePath -> IO FilePath +runCppPhase hsc_env input_fn output_fn = do + doCpp (hsc_logger hsc_env) + (hsc_tmpfs hsc_env) + (hsc_dflags hsc_env) + (hsc_unit_env hsc_env) + True{-raw-} + input_fn output_fn + return output_fn + + +runHscPhase :: PipeEnv + -> HscEnv + -> FilePath + -> HscSource + -> IO (HscEnv, ModSummary, HscRecompStatus) +runHscPhase pipe_env hsc_env0 input_fn src_flavour = do + let dflags0 = hsc_dflags hsc_env0 + PipeEnv{ src_basename=basename, + src_suffix=suff } = pipe_env + + -- we add the current directory (i.e. the directory in which + -- the .hs files resides) to the include path, since this is + -- what gcc does, and it's probably what you want. + let current_dir = takeDirectory basename + new_includes = addImplicitQuoteInclude paths [current_dir] + paths = includePaths dflags0 + dflags = dflags0 { includePaths = new_includes } + hsc_env = hscSetFlags dflags hsc_env0 + + + + -- gather the imports and module name + (hspp_buf,mod_name,imps,src_imps) <- do + buf <- hGetStringBuffer input_fn + let imp_prelude = xopt LangExt.ImplicitPrelude dflags + popts = initParserOpts dflags + eimps <- getImports popts imp_prelude buf input_fn (basename <.> suff) + case eimps of + Left errs -> throwErrors (GhcPsMessage <$> errs) + Right (src_imps,imps,L _ mod_name) -> return + (Just buf, mod_name, imps, src_imps) + + -- Take -o into account if present + -- Very like -ohi, but we must *only* do this if we aren't linking + -- (If we're linking then the -o applies to the linked thing, not to + -- the object file for one module.) + -- Note the nasty duplication with the same computation in compileFile above + location <- getLocation 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 = dynamicOutputFile dflags o_file + + 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 + + -- Tell the finder cache about this module + mod <- do + let home_unit = hsc_home_unit hsc_env + let fc = hsc_FC hsc_env + addHomeModuleToFinder fc home_unit mod_name location + + -- Make the ModSummary to hand to hscMain + let + mod_summary = ModSummary { ms_mod = mod, + ms_hsc_src = src_flavour, + ms_hspp_file = input_fn, + ms_hspp_opts = dflags, + ms_hspp_buf = hspp_buf, + ms_location = location, + ms_hs_hash = src_hash, + ms_obj_date = o_mod, + ms_dyn_obj_date = dyn_o_mod, + ms_parsed_mod = Nothing, + ms_iface_date = hi_date, + ms_hie_date = hie_date, + ms_textual_imps = imps, + ms_srcimps = src_imps } + + + -- run the compiler! + let msg :: Messager + msg hsc_env _ what _ = oneShotMsg (hsc_logger hsc_env) what + plugin_hsc_env' <- initializePlugins hsc_env (Just $ ms_mnwib mod_summary) + + -- Need to set the knot-tying mutable variable for interface + -- files. See GHC.Tc.Utils.TcGblEnv.tcg_type_env_var. + -- See also Note [hsc_type_env_var hack] + type_env_var <- newIORef emptyNameEnv + let plugin_hsc_env = plugin_hsc_env' { hsc_type_env_var = Just (mod, type_env_var) } + + status <- hscRecompStatus (Just msg) plugin_hsc_env mod_summary + Nothing Nothing (1, 1) + + return (plugin_hsc_env, mod_summary, status) + +runHscTcPhase :: HscEnv -> ModSummary -> IO (FrontendResult, Messages GhcMessage) +runHscTcPhase = hscTypecheckAndGetWarnings + +runHscPostTcPhase :: + HscEnv + -> ModSummary + -> FrontendResult + -> Messages GhcMessage + -> Maybe Fingerprint + -> IO HscBackendAction +runHscPostTcPhase hsc_env mod_summary tc_result tc_warnings mb_old_hash = do + runHsc hsc_env $ do + hscDesugarAndSimplify mod_summary tc_result tc_warnings mb_old_hash + + +runHsPpPhase :: HscEnv -> FilePath -> FilePath -> FilePath -> IO FilePath +runHsPpPhase hsc_env orig_fn input_fn output_fn = do + let dflags = hsc_dflags hsc_env + let logger = hsc_logger hsc_env + GHC.SysTools.runPp logger dflags + ( [ GHC.SysTools.Option orig_fn + , GHC.SysTools.Option input_fn + , GHC.SysTools.FileOption "" output_fn + ] ) + return output_fn + +phaseOutputFilenameNew :: Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> IO FilePath +phaseOutputFilenameNew next_phase pipe_env hsc_env maybe_loc = do + let PipeEnv{stop_phase, src_basename, output_spec} = pipe_env + let dflags = hsc_dflags hsc_env + logger = hsc_logger hsc_env + tmpfs = hsc_tmpfs hsc_env + getOutputFilename logger tmpfs (stopPhaseToPhase stop_phase) output_spec + src_basename dflags next_phase maybe_loc + + +-- | Computes the next output filename for something in the compilation +-- pipeline. This is controlled by several variables: +-- +-- 1. 'Phase': the last phase to be run (e.g. 'stopPhase'). This +-- is used to tell if we're in the last phase or not, because +-- in that case flags like @-o@ may be important. +-- 2. 'PipelineOutput': is this intended to be a 'Temporary' or +-- 'Persistent' build output? Temporary files just go in +-- a fresh temporary name. +-- 3. 'String': what was the basename of the original input file? +-- 4. 'DynFlags': the obvious thing +-- 5. 'Phase': the phase we want to determine the output filename of. +-- 6. @Maybe ModLocation@: the 'ModLocation' of the module we're +-- compiling; this can be used to override the default output +-- of an object file. (TODO: do we actually need this?) +getOutputFilename + :: Logger + -> TmpFs + -> Phase + -> PipelineOutput + -> String + -> DynFlags + -> Phase -- next phase + -> Maybe ModLocation + -> IO FilePath +getOutputFilename logger tmpfs stop_phase output basename dflags next_phase maybe_location + | is_last_phase, Persistent <- output = persistent_fn + | is_last_phase, SpecificFile <- output = case outputFile dflags of + Just f -> return f + Nothing -> + panic "SpecificFile: No filename" + | keep_this_output = persistent_fn + | Temporary lifetime <- output = newTempName logger tmpfs dflags lifetime suffix + | otherwise = newTempName logger tmpfs dflags TFL_CurrentModule + suffix + where + hcsuf = hcSuf dflags + odir = objectDir dflags + osuf = objectSuf dflags + keep_hc = gopt Opt_KeepHcFiles dflags + keep_hscpp = gopt Opt_KeepHscppFiles dflags + keep_s = gopt Opt_KeepSFiles dflags + keep_bc = gopt Opt_KeepLlvmFiles dflags + + myPhaseInputExt HCc = hcsuf + myPhaseInputExt MergeForeign = osuf + myPhaseInputExt StopLn = osuf + myPhaseInputExt other = phaseInputExt other + + is_last_phase = next_phase `eqPhase` stop_phase + + -- sometimes, we keep output from intermediate stages + keep_this_output = + case next_phase of + As _ | keep_s -> True + LlvmOpt | keep_bc -> True + HCc | keep_hc -> True + HsPp _ | keep_hscpp -> True -- See #10869 + _other -> False + + suffix = myPhaseInputExt next_phase + + -- persistent object files get put in odir + persistent_fn + | StopLn <- next_phase = return odir_persistent + | otherwise = return persistent + + persistent = basename <.> suffix + + odir_persistent + | Just loc <- maybe_location = ml_obj_file loc + | Just d <- odir = (d </> persistent) + | otherwise = persistent + + +-- | LLVM Options. These are flags to be passed to opt and llc, to ensure +-- consistency we list them in pairs, so that they form groups. +llvmOptions :: DynFlags + -> [(String, String)] -- ^ pairs of (opt, llc) arguments +llvmOptions dflags = + [("-enable-tbaa -tbaa", "-enable-tbaa") | gopt Opt_LlvmTBAA dflags ] + ++ [("-relocation-model=" ++ rmodel + ,"-relocation-model=" ++ rmodel) | not (null rmodel)] + ++ [("-stack-alignment=" ++ (show align) + ,"-stack-alignment=" ++ (show align)) | align > 0 ] + + -- Additional llc flags + ++ [("", "-mcpu=" ++ mcpu) | not (null mcpu) + , not (any (isInfixOf "-mcpu") (getOpts dflags opt_lc)) ] + ++ [("", "-mattr=" ++ attrs) | not (null attrs) ] + ++ [("", "-target-abi=" ++ abi) | not (null abi) ] + + where target = platformMisc_llvmTarget $ platformMisc dflags + Just (LlvmTarget _ mcpu mattr) = lookup target (llvmTargets $ llvmConfig dflags) + + -- Relocation models + rmodel | gopt Opt_PIC dflags = "pic" + | positionIndependent dflags = "pic" + | WayDyn `elem` ways dflags = "dynamic-no-pic" + | otherwise = "static" + + platform = targetPlatform dflags + + align :: Int + align = case platformArch platform of + ArchX86_64 | isAvxEnabled dflags -> 32 + _ -> 0 + + attrs :: String + attrs = intercalate "," $ mattr + ++ ["+sse42" | isSse4_2Enabled dflags ] + ++ ["+sse2" | isSse2Enabled platform ] + ++ ["+sse" | isSseEnabled platform ] + ++ ["+avx512f" | isAvx512fEnabled dflags ] + ++ ["+avx2" | isAvx2Enabled dflags ] + ++ ["+avx" | isAvxEnabled dflags ] + ++ ["+avx512cd"| isAvx512cdEnabled dflags ] + ++ ["+avx512er"| isAvx512erEnabled dflags ] + ++ ["+avx512pf"| isAvx512pfEnabled dflags ] + ++ ["+bmi" | isBmiEnabled dflags ] + ++ ["+bmi2" | isBmi2Enabled dflags ] + + abi :: String + abi = case platformArch (targetPlatform dflags) of + ArchRISCV64 -> "lp64d" + _ -> "" + +-- ----------------------------------------------------------------------------- +-- Running CPP + +-- | Run CPP +-- +-- UnitEnv is needed to compute MIN_VERSION macros +doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> Bool -> FilePath -> FilePath -> IO () +doCpp logger tmpfs dflags unit_env raw input_fn output_fn = do + let hscpp_opts = picPOpts dflags + let cmdline_include_paths = includePaths dflags + let unit_state = ue_units unit_env + pkg_include_dirs <- mayThrowUnitErr + (collectIncludeDirs <$> preloadUnitsInfo unit_env) + let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) [] + (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs) + let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) [] + (includePathsQuote cmdline_include_paths ++ + includePathsQuoteImplicit cmdline_include_paths) + let include_paths = include_paths_quote ++ include_paths_global + + let verbFlags = getVerbFlags dflags + + let cpp_prog args | raw = GHC.SysTools.runCpp logger dflags args + | otherwise = GHC.SysTools.runCc Nothing logger tmpfs dflags + (GHC.SysTools.Option "-E" : args) + + let platform = targetPlatform dflags + targetArch = stringEncodeArch $ platformArch platform + targetOS = stringEncodeOS $ platformOS platform + isWindows = platformOS platform == OSMinGW32 + let target_defs = + [ "-D" ++ HOST_OS ++ "_BUILD_OS", + "-D" ++ HOST_ARCH ++ "_BUILD_ARCH", + "-D" ++ targetOS ++ "_HOST_OS", + "-D" ++ targetArch ++ "_HOST_ARCH" ] + -- remember, in code we *compile*, the HOST is the same our TARGET, + -- and BUILD is the same as our HOST. + + let io_manager_defs = + [ "-D__IO_MANAGER_WINIO__=1" | isWindows ] ++ + [ "-D__IO_MANAGER_MIO__=1" ] + + let sse_defs = + [ "-D__SSE__" | isSseEnabled platform ] ++ + [ "-D__SSE2__" | isSse2Enabled platform ] ++ + [ "-D__SSE4_2__" | isSse4_2Enabled dflags ] + + let avx_defs = + [ "-D__AVX__" | isAvxEnabled dflags ] ++ + [ "-D__AVX2__" | isAvx2Enabled dflags ] ++ + [ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] ++ + [ "-D__AVX512ER__" | isAvx512erEnabled dflags ] ++ + [ "-D__AVX512F__" | isAvx512fEnabled dflags ] ++ + [ "-D__AVX512PF__" | isAvx512pfEnabled dflags ] + + backend_defs <- getBackendDefs logger dflags + + let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ] + -- Default CPP defines in Haskell source + ghcVersionH <- getGhcVersionPathName dflags unit_env + let hsSourceCppOpts = [ "-include", ghcVersionH ] + + -- MIN_VERSION macros + let uids = explicitUnits unit_state + pkgs = catMaybes (map (lookupUnit unit_state) uids) + mb_macro_include <- + if not (null pkgs) && gopt Opt_VersionMacros dflags + then do macro_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "h" + writeFile macro_stub (generatePackageVersionMacros pkgs) + -- Include version macros for every *exposed* package. + -- Without -hide-all-packages and with a package database + -- size of 1000 packages, it takes cpp an estimated 2 + -- milliseconds to process this file. See #10970 + -- comment 8. + return [GHC.SysTools.FileOption "-include" macro_stub] + else return [] + + cpp_prog ( map GHC.SysTools.Option verbFlags + ++ map GHC.SysTools.Option include_paths + ++ map GHC.SysTools.Option hsSourceCppOpts + ++ map GHC.SysTools.Option target_defs + ++ map GHC.SysTools.Option backend_defs + ++ map GHC.SysTools.Option th_defs + ++ map GHC.SysTools.Option hscpp_opts + ++ map GHC.SysTools.Option sse_defs + ++ map GHC.SysTools.Option avx_defs + ++ map GHC.SysTools.Option io_manager_defs + ++ mb_macro_include + -- Set the language mode to assembler-with-cpp when preprocessing. This + -- alleviates some of the C99 macro rules relating to whitespace and the hash + -- operator, which we tend to abuse. Clang in particular is not very happy + -- about this. + ++ [ GHC.SysTools.Option "-x" + , GHC.SysTools.Option "assembler-with-cpp" + , GHC.SysTools.Option input_fn + -- We hackily use Option instead of FileOption here, so that the file + -- name is not back-slashed on Windows. cpp is capable of + -- dealing with / in filenames, so it works fine. Furthermore + -- if we put in backslashes, cpp outputs #line directives + -- with *double* backslashes. And that in turn means that + -- our error messages get double backslashes in them. + -- In due course we should arrange that the lexer deals + -- with these \\ escapes properly. + , GHC.SysTools.Option "-o" + , GHC.SysTools.FileOption "" output_fn + ]) + +getBackendDefs :: Logger -> DynFlags -> IO [String] +getBackendDefs logger dflags | backend dflags == LLVM = do + llvmVer <- figureLlvmVersion logger dflags + return $ case fmap llvmVersionList llvmVer of + Just [m] -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,0) ] + Just (m:n:_) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ] + _ -> [] + where + format (major, minor) + | minor >= 100 = error "getBackendDefs: Unsupported minor version" + | otherwise = show $ (100 * major + minor :: Int) -- Contract is Int + +getBackendDefs _ _ = + return [] + +-- | What phase to run after one of the backend code generators has run +hscPostBackendPhase :: HscSource -> Backend -> Phase +hscPostBackendPhase HsBootFile _ = StopLn +hscPostBackendPhase HsigFile _ = StopLn +hscPostBackendPhase _ bcknd = + case bcknd of + ViaC -> HCc + NCG -> As False + LLVM -> LlvmOpt + NoBackend -> StopLn + Interpreter -> StopLn + + +compileStub :: HscEnv -> FilePath -> IO FilePath +compileStub hsc_env stub_c = compileForeign hsc_env LangC stub_c + + +-- --------------------------------------------------------------------------- +-- join object files into a single relocatable object file, using ld -r + +{- +Note [Produce big objects on Windows] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Windows Portable Executable object format has a limit of 32k sections, which +we tend to blow through pretty easily. Thankfully, there is a "big object" +extension, which raises this limit to 2^32. However, it must be explicitly +enabled in the toolchain: + + * the assembler accepts the -mbig-obj flag, which causes it to produce a + bigobj-enabled COFF object. + + * the linker accepts the --oformat pe-bigobj-x86-64 flag. Despite what the name + suggests, this tells the linker to produce a bigobj-enabled COFF object, no a + PE executable. + +We must enable bigobj output in a few places: + + * When merging object files (GHC.Driver.Pipeline.joinObjectFiles) + + * When assembling (GHC.Driver.Pipeline.runPhase (RealPhase As ...)) + +Unfortunately the big object format is not supported on 32-bit targets so +none of this can be used in that case. + + +Note [Merging object files for GHCi] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +GHCi can usually loads standard linkable object files using GHC's linker +implementation. However, most users build their projects with -split-sections, +meaning that such object files can have an extremely high number of sections. +As the linker must map each of these sections individually, loading such object +files is very inefficient. + +To avoid this inefficiency, we use the linker's `-r` flag and a linker script +to produce a merged relocatable object file. This file will contain a singe +text section section and can consequently be mapped far more efficiently. As +gcc tends to do unpredictable things to our linker command line, we opt to +invoke ld directly in this case, in contrast to our usual strategy of linking +via gcc. + +-} + +joinObjectFiles :: Logger -> TmpFs -> DynFlags -> [FilePath] -> FilePath -> IO () +joinObjectFiles logger tmpfs dflags o_files output_fn = do + let toolSettings' = toolSettings dflags + ldIsGnuLd = toolSettings_ldIsGnuLd toolSettings' + osInfo = platformOS (targetPlatform dflags) + ld_r args = GHC.SysTools.runMergeObjects logger tmpfs dflags ( + -- See Note [Produce big objects on Windows] + concat + [ [GHC.SysTools.Option "--oformat", GHC.SysTools.Option "pe-bigobj-x86-64"] + | OSMinGW32 == osInfo + , not $ target32Bit (targetPlatform dflags) + ] + ++ map GHC.SysTools.Option ld_build_id + ++ [ GHC.SysTools.Option "-o", + GHC.SysTools.FileOption "" output_fn ] + ++ args) + + -- suppress the generation of the .note.gnu.build-id section, + -- which we don't need and sometimes causes ld to emit a + -- warning: + ld_build_id | toolSettings_ldSupportsBuildId toolSettings' = ["--build-id=none"] + | otherwise = [] + + if ldIsGnuLd + then do + script <- newTempName logger tmpfs dflags TFL_CurrentModule "ldscript" + cwd <- getCurrentDirectory + let o_files_abs = map (\x -> "\"" ++ (cwd </> x) ++ "\"") o_files + writeFile script $ "INPUT(" ++ unwords o_files_abs ++ ")" + ld_r [GHC.SysTools.FileOption "" script] + else if toolSettings_ldSupportsFilelist toolSettings' + then do + filelist <- newTempName logger tmpfs dflags TFL_CurrentModule "filelist" + writeFile filelist $ unlines o_files + ld_r [GHC.SysTools.Option "-filelist", + GHC.SysTools.FileOption "" filelist] + else + ld_r (map (GHC.SysTools.FileOption "") o_files) + +----------------------------------------------------------------------------- +-- Look for the /* GHC_PACKAGES ... */ comment at the top of a .hc file + +getHCFilePackages :: FilePath -> IO [UnitId] +getHCFilePackages filename = + Exception.bracket (openFile filename ReadMode) hClose $ \h -> do + l <- hGetLine h + case l of + '/':'*':' ':'G':'H':'C':'_':'P':'A':'C':'K':'A':'G':'E':'S':rest -> + return (map stringToUnitId (words rest)) + _other -> + return [] + + +linkDynLibCheck :: Logger -> TmpFs -> DynFlags -> UnitEnv -> [String] -> [UnitId] -> IO () +linkDynLibCheck logger tmpfs dflags unit_env o_files dep_units = do + when (haveRtsOptsFlags dflags) $ + logMsg logger MCInfo noSrcSpan + $ withPprStyle defaultUserStyle + (text "Warning: -rtsopts and -with-rtsopts have no effect with -shared." $$ + text " Call hs_init_ghc() from your main() function to set these options.") + linkDynLib logger tmpfs dflags unit_env o_files dep_units + + + +-- --------------------------------------------------------------------------- +-- Macros (cribbed from Cabal) + +generatePackageVersionMacros :: [UnitInfo] -> String +generatePackageVersionMacros pkgs = concat + -- Do not add any C-style comments. See #3389. + [ generateMacros "" pkgname version + | pkg <- pkgs + , let version = unitPackageVersion pkg + pkgname = map fixchar (unitPackageNameString pkg) + ] + +fixchar :: Char -> Char +fixchar '-' = '_' +fixchar c = c + +generateMacros :: String -> String -> Version -> String +generateMacros prefix name version = + concat + ["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n" + ,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n" + ," (major1) < ",major1," || \\\n" + ," (major1) == ",major1," && (major2) < ",major2," || \\\n" + ," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")" + ,"\n\n" + ] + where + (major1:major2:minor:_) = map show (versionBranch version ++ repeat 0) + + +-- ----------------------------------------------------------------------------- +-- Misc. + + + +touchObjectFile :: Logger -> DynFlags -> FilePath -> IO () +touchObjectFile logger dflags path = do + createDirectoryIfMissing True $ takeDirectory path + GHC.SysTools.touch logger dflags "Touching object file" path + +-- | Find out path to @ghcversion.h@ file +getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath +getGhcVersionPathName dflags unit_env = do + candidates <- case ghcVersionFile dflags of + Just path -> return [path] + Nothing -> do + ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env [rtsUnitId]) + return ((</> "ghcversion.h") <$> collectIncludeDirs ps) + + found <- filterM doesFileExist candidates + case found of + [] -> throwGhcExceptionIO (InstallationError + ("ghcversion.h missing; tried: " + ++ intercalate ", " candidates)) + (x:_) -> return x + +-- Note [-fPIC for assembler] +-- When compiling .c source file GHC's driver pipeline basically +-- does the following two things: +-- 1. ${CC} -S 'PIC_CFLAGS' source.c +-- 2. ${CC} -x assembler -c 'PIC_CFLAGS' source.S +-- +-- Why do we need to pass 'PIC_CFLAGS' both to C compiler and assembler? +-- Because on some architectures (at least sparc32) assembler also chooses +-- the relocation type! +-- Consider the following C module: +-- +-- /* pic-sample.c */ +-- int v; +-- void set_v (int n) { v = n; } +-- int get_v (void) { return v; } +-- +-- $ gcc -S -fPIC pic-sample.c +-- $ gcc -c pic-sample.s -o pic-sample.no-pic.o # incorrect binary +-- $ gcc -c -fPIC pic-sample.s -o pic-sample.pic.o # correct binary +-- +-- $ objdump -r -d pic-sample.pic.o > pic-sample.pic.o.od +-- $ objdump -r -d pic-sample.no-pic.o > pic-sample.no-pic.o.od +-- $ diff -u pic-sample.pic.o.od pic-sample.no-pic.o.od +-- +-- Most of architectures won't show any difference in this test, but on sparc32 +-- the following assembly snippet: +-- +-- sethi %hi(_GLOBAL_OFFSET_TABLE_-8), %l7 +-- +-- generates two kinds or relocations, only 'R_SPARC_PC22' is correct: +-- +-- 3c: 2f 00 00 00 sethi %hi(0), %l7 +-- - 3c: R_SPARC_PC22 _GLOBAL_OFFSET_TABLE_-0x8 +-- + 3c: R_SPARC_HI22 _GLOBAL_OFFSET_TABLE_-0x8 + +{- Note [Don't normalise input filenames] + +Summary + We used to normalise input filenames when starting the unlit phase. This + broke hpc in `--make` mode with imported literate modules (#2991). + +Introduction + 1) --main + When compiling a module with --main, GHC scans its imports to find out which + other modules it needs to compile too. It turns out that there is a small + difference between saying `ghc --make A.hs`, when `A` imports `B`, and + specifying both modules on the command line with `ghc --make A.hs B.hs`. In + the former case, the filename for B is inferred to be './B.hs' instead of + 'B.hs'. + + 2) unlit + When GHC compiles a literate haskell file, the source code first needs to go + through unlit, which turns it into normal Haskell source code. At the start + of the unlit phase, in `Driver.Pipeline.runPhase`, we call unlit with the + option `-h` and the name of the original file. We used to normalise this + filename using System.FilePath.normalise, which among other things removes + an initial './'. unlit then uses that filename in #line directives that it + inserts in the transformed source code. + + 3) SrcSpan + A SrcSpan represents a portion of a source code file. It has fields + linenumber, start column, end column, and also a reference to the file it + originated from. The SrcSpans for a literate haskell file refer to the + filename that was passed to unlit -h. + + 4) -fhpc + At some point during compilation with -fhpc, in the function + `GHC.HsToCore.Coverage.isGoodTickSrcSpan`, we compare the filename that a + `SrcSpan` refers to with the name of the file we are currently compiling. + For some reason I don't yet understand, they can sometimes legitimally be + different, and then hpc ignores that SrcSpan. + +Problem + When running `ghc --make -fhpc A.hs`, where `A.hs` imports the literate + module `B.lhs`, `B` is inferred to be in the file `./B.lhs` (1). At the + start of the unlit phase, the name `./B.lhs` is normalised to `B.lhs` (2). + Therefore the SrcSpans of `B` refer to the file `B.lhs` (3), but we are + still compiling `./B.lhs`. Hpc thinks these two filenames are different (4), + doesn't include ticks for B, and we have unhappy customers (#2991). + +Solution + Do not normalise `input_fn` when starting the unlit phase. + +Alternative solution + Another option would be to not compare the two filenames on equality, but to + use System.FilePath.equalFilePath. That function first normalises its + arguments. The problem is that by the time we need to do the comparison, the + filenames have been turned into FastStrings, probably for performance + reasons, so System.FilePath.equalFilePath can not be used directly. + +Archeology + The call to `normalise` was added in a commit called "Fix slash + direction on Windows with the new filePath code" (c9b6b5e8). The problem + that commit was addressing has since been solved in a different manner, in a + commit called "Fix the filename passed to unlit" (1eedbc6b). So the + `normalise` is no longer necessary. +-} diff --git a/compiler/GHC/Driver/Pipeline/Monad.hs b/compiler/GHC/Driver/Pipeline/Monad.hs index 3f6716a954..a760bb6022 100644 --- a/compiler/GHC/Driver/Pipeline/Monad.hs +++ b/compiler/GHC/Driver/Pipeline/Monad.hs @@ -1,115 +1,74 @@ -{-# LANGUAGE DeriveFunctor #-} --- | The CompPipeline monad and associated ops --- --- Defined in separate module so that it can safely be imported from Hooks +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE MultiParamTypeClasses #-} +-- | The 'TPipelineClass' and 'MonadUse' classes and associated types module GHC.Driver.Pipeline.Monad ( - CompPipeline(..), evalP - , PhasePlus(..), HscBackendAction (..) - , PipeEnv(..), PipeState(..), PipelineOutput(..) - , getPipeEnv, getPipeState, getPipeSession - , setDynFlags, setModLocation, setForeignOs, setIface - , pipeStateDynFlags, pipeStateModIface, pipeStateLinkable, setPlugins, setLinkable + TPipelineClass, MonadUse(..) + + , PipeEnv(..) + , PipelineOutput(..) + , getLocation ) where import GHC.Prelude - -import GHC.Utils.Fingerprint -import GHC.Utils.Monad -import GHC.Utils.Outputable -import GHC.Utils.Logger - -import GHC.Driver.Session +import Control.Monad.IO.Class +import qualified Data.Kind as K import GHC.Driver.Phases -import GHC.Driver.Env -import GHC.Driver.Plugins - -import GHC.Linker.Types - -import GHC.Utils.TmpFs (TempFileLifetime) - -import GHC.Types.Error - +import GHC.Utils.TmpFs +import GHC.Driver.Session +import GHC.Types.SourceFile import GHC.Unit.Module -import GHC.Unit.Module.ModIface -import GHC.Unit.Module.ModSummary -import GHC.Unit.Module.Status - -import GHC.Driver.Errors.Types ( GhcMessage ) - -import GHC.Tc.Types - -import Control.Monad - -newtype CompPipeline a = P { unP :: PipeEnv -> PipeState -> IO (PipeState, a) } - deriving (Functor) - -evalP :: CompPipeline a -> PipeEnv -> PipeState -> IO (PipeState, a) -evalP (P f) env st = f env st - -instance Applicative CompPipeline where - pure a = P $ \_env state -> return (state, a) - (<*>) = ap - -instance Monad CompPipeline where - P m >>= k = P $ \env state -> do (state',a) <- m env state - unP (k a) env state' - -instance MonadIO CompPipeline where - liftIO m = P $ \_env state -> do a <- m; return (state, a) +import GHC.Unit.Finder -data PhasePlus = RealPhase Phase - -- | Runs the pipeline post typechecking, till the end - | HscPostTc ModSummary FrontendResult (Messages GhcMessage) (Maybe Fingerprint) - -- | The backend phase runs the code-gen. This may be run twice in - -- the case of -dynamic-too - | HscBackend ModSummary HscBackendAction +-- The interface that the pipeline monad must implement. +type TPipelineClass (f :: K.Type -> K.Type) (m :: K.Type -> K.Type) + = (Functor m, MonadIO m, Applicative m, Monad m, MonadUse f m) +-- | Lift a `f` action into an `m` action. +class MonadUse f m where + use :: f a -> m a -instance Outputable PhasePlus where - ppr (RealPhase p) = ppr p - ppr (HscPostTc {}) = text "HscPostTc" - ppr (HscBackend {}) = text "HscBackend" - --- ----------------------------------------------------------------------------- --- The pipeline uses a monad to carry around various bits of information - --- PipeEnv: invariant information passed down +-- PipeEnv: invariant information passed down through the pipeline data PipeEnv = PipeEnv { - stop_phase :: Phase, -- ^ Stop just before this phase + stop_phase :: StopPhase, -- ^ Stop just after this phase src_filename :: String, -- ^ basename of original input source src_basename :: String, -- ^ basename of original input source src_suffix :: String, -- ^ its extension output_spec :: PipelineOutput -- ^ says where to put the pipeline output } --- PipeState: information that might change during a pipeline run -data PipeState = PipeState { - hsc_env :: HscEnv, - -- ^ only the DynFlags and the Plugins change in the HscEnv. The - -- DynFlags change at various points, for example when we read the - -- OPTIONS_GHC pragmas in the Cpp phase. - maybe_loc :: Maybe ModLocation, - -- ^ the ModLocation. This is discovered during compilation, - -- in the Hsc phase where we read the module header. - foreign_os :: [FilePath], - -- ^ additional object files resulting from compiling foreign - -- code. They come from two sources: foreign stubs, and - -- add{C,Cxx,Objc,Objcxx}File from template haskell - iface :: Maybe ModIface, - -- ^ Interface generated by HscBackend phase. Only available after the - -- phase runs. - maybe_linkable :: Maybe Linkable - -- ^ Linkable generated by HscBackend phase, for the Interpreter backend. - } - -pipeStateDynFlags :: PipeState -> DynFlags -pipeStateDynFlags = hsc_dflags . hsc_env - -pipeStateModIface :: PipeState -> Maybe ModIface -pipeStateModIface = iface - -pipeStateLinkable :: PipeState -> Maybe Linkable -pipeStateLinkable = maybe_linkable +-- | Calculate the ModLocation from the provided DynFlags +getLocation :: PipeEnv -> DynFlags -> HscSource -> ModuleName -> IO ModLocation +getLocation pipe_env dflags src_flavour mod_name = do + let PipeEnv{ src_basename=basename, + src_suffix=suff } = pipe_env + location1 <- mkHomeModLocation2 dflags mod_name basename suff + + -- Boot-ify it if necessary + let location2 + | HsBootFile <- src_flavour = addBootSuffixLocnOut location1 + | otherwise = location1 + + + -- Take -ohi into account if present + -- This can't be done in mkHomeModuleLocation because + -- it only applies to the module being compiles + let ohi = outputHi dflags + location3 | Just fn <- ohi = location2{ ml_hi_file = fn } + | otherwise = location2 + + -- Take -o into account if present + -- Very like -ohi, but we must *only* do this if we aren't linking + -- (If we're linking then the -o applies to the linked thing, not to + -- the object file for one module.) + -- Note the nasty duplication with the same computation in compileFile + -- above + let expl_o_file = outputFile dflags + location4 | Just ofile <- expl_o_file + , isNoLink (ghcLink dflags) + = location3 { ml_obj_file = ofile } + | otherwise = location3 + return location4 data PipelineOutput = Temporary TempFileLifetime @@ -126,41 +85,3 @@ data PipelineOutput | NoOutputFile -- ^ No output should be created, like in Interpreter or NoBackend. deriving Show - -getPipeEnv :: CompPipeline PipeEnv -getPipeEnv = P $ \env state -> return (state, env) - -getPipeState :: CompPipeline PipeState -getPipeState = P $ \_env state -> return (state, state) - -getPipeSession :: CompPipeline HscEnv -getPipeSession = P $ \_env state -> return (state, hsc_env state) - -instance HasDynFlags CompPipeline where - getDynFlags = P $ \_env state -> return (state, hsc_dflags (hsc_env state)) - -instance HasLogger CompPipeline where - getLogger = P $ \_env state -> return (state, hsc_logger (hsc_env state)) - -setDynFlags :: DynFlags -> CompPipeline () -setDynFlags dflags = P $ \_env state -> - return (state{ hsc_env = hscSetFlags dflags (hsc_env state)}, ()) - -setPlugins :: [LoadedPlugin] -> [StaticPlugin] -> CompPipeline () -setPlugins dyn static = P $ \_env state -> - let hsc_env' = (hsc_env state){ hsc_plugins = dyn, hsc_static_plugins = static } - in return (state{hsc_env = hsc_env'}, ()) - -setModLocation :: ModLocation -> CompPipeline () -setModLocation loc = P $ \_env state -> - return (state{ maybe_loc = Just loc }, ()) - -setForeignOs :: [FilePath] -> CompPipeline () -setForeignOs os = P $ \_env state -> - return (state{ foreign_os = os }, ()) - -setIface :: ModIface -> CompPipeline () -setIface iface = P $ \_env state -> return (state{ iface = Just iface }, ()) - -setLinkable :: Linkable -> CompPipeline () -setLinkable l = P $ \_env state -> return (state{ maybe_linkable = Just l }, ()) diff --git a/compiler/GHC/Driver/Pipeline/Phases.hs b/compiler/GHC/Driver/Pipeline/Phases.hs new file mode 100644 index 0000000000..415d29147d --- /dev/null +++ b/compiler/GHC/Driver/Pipeline/Phases.hs @@ -0,0 +1,52 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} + +module GHC.Driver.Pipeline.Phases (TPhase(..), PhaseHook(..)) where + +import GHC.Prelude +import GHC.Driver.Pipeline.Monad +import GHC.Driver.Env.Types +import GHC.Driver.Session +import GHC.Driver.CmdLine +import GHC.Types.SourceFile +import GHC.Unit.Module.ModSummary +import GHC.Unit.Module.Status +import GHC.Tc.Types ( FrontendResult ) +import GHC.Types.Error +import GHC.Driver.Errors.Types +import GHC.Fingerprint.Type +import GHC.Unit.Module.Location ( ModLocation ) +import GHC.Unit.Module.Name ( ModuleName ) +import GHC.Unit.Module.ModIface +import GHC.Linker.Types +import GHC.Driver.Phases + +-- Typed Pipeline Phases +-- MP: TODO: We need to refine the arguments to each of these phases so recompilation +-- can be smarter. For example, rather than passing a whole HscEnv, just pass the options +-- which each phase depends on, then recompilation checking can decide to only rerun each +-- phase if the inputs have been modified. +data TPhase res where + T_Unlit :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_FileArgs :: HscEnv -> FilePath -> TPhase (DynFlags, [Warn]) + T_Cpp :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_HsPp :: PipeEnv -> HscEnv -> FilePath -> FilePath -> TPhase FilePath + T_HscRecomp :: PipeEnv -> HscEnv -> FilePath -> HscSource -> TPhase (HscEnv, ModSummary, HscRecompStatus) + T_Hsc :: HscEnv -> ModSummary -> TPhase (FrontendResult, Messages GhcMessage) + T_HscPostTc :: HscEnv -> ModSummary + -> FrontendResult + -> Messages GhcMessage + -> Maybe Fingerprint + -> TPhase HscBackendAction + T_HscBackend :: PipeEnv -> HscEnv -> ModuleName -> HscSource -> ModLocation -> HscBackendAction -> TPhase ([FilePath], ModIface, Maybe Linkable, FilePath) + T_CmmCpp :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_Cmm :: PipeEnv -> HscEnv -> FilePath -> TPhase ([FilePath], FilePath) + T_Cc :: Phase -> PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_As :: Bool -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> TPhase FilePath + T_LlvmOpt :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_LlvmLlc :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_LlvmMangle :: PipeEnv -> HscEnv -> FilePath -> TPhase FilePath + T_MergeForeign :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> [FilePath] -> TPhase FilePath + +-- | A wrapper around the interpretation function for phases. +data PhaseHook = PhaseHook (forall a . TPhase a -> IO a) diff --git a/compiler/GHC/Driver/Session.hs b/compiler/GHC/Driver/Session.hs index d35e0d96d4..78add24b2b 100644 --- a/compiler/GHC/Driver/Session.hs +++ b/compiler/GHC/Driver/Session.hs @@ -545,11 +545,11 @@ data DynFlags = DynFlags { -- used to query the appropriate fields -- (outputFile/dynOutputFile, ways, etc.) - -- | This is set by 'GHC.Driver.Pipeline.runPipeline' + -- | This is set by 'GHC.Driver.Pipeline.setDumpPrefix' -- or 'ghc.GHCi.UI.runStmt' based on where its output is going. dumpPrefix :: Maybe FilePath, - -- | Override the 'dumpPrefix' set by 'GHC.Driver.Pipeline.runPipeline' + -- | Override the 'dumpPrefix' set by 'GHC.Driver.Pipeline.setDumpPrefix' -- or 'ghc.GHCi.UI.runStmt'. -- Set by @-ddump-file-prefix@ dumpPrefixForce :: Maybe FilePath, |