Fix some validation errors
[ghc-hetmet.git] / compiler / main / DriverPipeline.hs
index f8073c9..afbd03e 100644 (file)
@@ -1,4 +1,5 @@
 {-# OPTIONS -fno-cse #-}
+{-# LANGUAGE NamedFieldPuns #-}
 -- -fno-cse is needed for GLOBAL_VAR's to behave properly
 
 -----------------------------------------------------------------------------
 -----------------------------------------------------------------------------
 
 module DriverPipeline (
-       -- 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 batch-mode driver
+        -- Interfaces for the batch-mode driver
    linkBinary,
 
-       -- Interfaces for the compilation manager (interpreted/batch-mode)
-   preprocess, 
+        -- Interfaces for the compilation manager (interpreted/batch-mode)
+   preprocess,
    compile, compile',
-   link, 
+   link,
 
   ) where
 
@@ -35,33 +36,33 @@ import Finder
 import HscTypes
 import Outputable
 import Module
-import LazyUniqFM              ( eltsUFM )
+import UniqFM           ( eltsUFM )
 import ErrUtils
 import DynFlags
-import StaticFlags     ( v_Ld_inputs, opt_Static, WayName(..) )
+import StaticFlags      ( v_Ld_inputs, opt_PIC, opt_Static, WayName(..) )
 import Config
 import Panic
 import Util
-import StringBuffer    ( hGetStringBuffer )
-import BasicTypes      ( SuccessFlag(..) )
-import Maybes          ( expectJust )
-import ParserCoreUtils ( getCoreModuleName )
+import StringBuffer     ( hGetStringBuffer )
+import BasicTypes       ( SuccessFlag(..) )
+import Maybes           ( expectJust )
+import ParserCoreUtils  ( getCoreModuleName )
 import SrcLoc
 import FastString
--- import MonadUtils
+import LlvmCodeGen      ( llvmFixupAsm )
+import MonadUtils
+import Platform
 
--- import Data.Either
 import Exception
-import Data.IORef      ( readIORef )
--- import GHC.Exts             ( Int(..) )
+import Data.IORef       ( readIORef )
 import System.Directory
 import System.FilePath
 import System.IO
-import System.IO.Error as IO
 import Control.Monad
-import Data.List       ( isSuffixOf )
+import Data.List        ( isSuffixOf )
 import Data.Maybe
 import System.Environment
+import Data.Char
 
 -- ---------------------------------------------------------------------------
 -- Pre-process
@@ -72,14 +73,13 @@ import System.Environment
 -- We return the augmented DynFlags, because they contain the result
 -- of slurping in the OPTIONS pragmas
 
-preprocess :: GhcMonad m =>
-              HscEnv
+preprocess :: HscEnv
            -> (FilePath, Maybe Phase) -- ^ filename and starting phase
-           -> m (DynFlags, FilePath)
+           -> IO (DynFlags, FilePath)
 preprocess hsc_env (filename, mb_phase) =
-  ASSERT2(isJust mb_phase || isHaskellSrcFilename filename, text filename) 
-  runPipeline anyHsc hsc_env (filename, mb_phase) 
-        Nothing Temporary Nothing{-no ModLocation-}
+  ASSERT2(isJust mb_phase || isHaskellSrcFilename filename, text filename)
+  runPipeline anyHsc hsc_env (filename, mb_phase)
+        Nothing Temporary Nothing{-no ModLocation-} Nothing{-no stub-}
 
 -- ---------------------------------------------------------------------------
 
@@ -89,37 +89,33 @@ preprocess hsc_env (filename, mb_phase) =
 --
 -- This is the interface between the compilation manager and the
 -- compiler proper (hsc), where we deal with tedious details like
--- reading the OPTIONS pragma from the source file, and passing the
--- output of hsc through the C compiler.
+-- reading the OPTIONS pragma from the source file, converting the
+-- C or assembly that GHC produces into an object file, and compiling
+-- FFI stub files.
 --
 -- NB.  No old interface can also mean that the source has changed.
 
-compile :: GhcMonad m =>
-           HscEnv
+compile :: HscEnv
         -> ModSummary      -- ^ summary for module being compiled
         -> Int             -- ^ module N ...
         -> Int             -- ^ ... of M
         -> Maybe ModIface  -- ^ old interface, if we have one
         -> Maybe Linkable  -- ^ old linkable, if we have one
-        -> m HomeModInfo   -- ^ the complete HomeModInfo, if successful
+        -> IO HomeModInfo   -- ^ the complete HomeModInfo, if successful
 
 compile = compile' (hscCompileNothing, hscCompileInteractive, hscCompileBatch)
 
-type Compiler m a = HscEnv -> ModSummary -> Bool
-                  -> Maybe ModIface -> Maybe (Int, Int)
-                  -> m a
-
-compile' :: GhcMonad m =>
-           (Compiler m (HscStatus, ModIface, ModDetails),
-            Compiler m (InteractiveStatus, ModIface, ModDetails),
-            Compiler m (HscStatus, ModIface, ModDetails))
+compile' :: 
+           (Compiler (HscStatus, ModIface, ModDetails),
+            Compiler (InteractiveStatus, ModIface, ModDetails),
+            Compiler (HscStatus, ModIface, ModDetails))
         -> HscEnv
         -> ModSummary      -- ^ summary for module being compiled
         -> Int             -- ^ module N ...
         -> Int             -- ^ ... of M
         -> Maybe ModIface  -- ^ old interface, if we have one
         -> Maybe Linkable  -- ^ old linkable, if we have one
-        -> m HomeModInfo   -- ^ the complete HomeModInfo, if successful
+        -> IO HomeModInfo   -- ^ the complete HomeModInfo, if successful
 
 compile' (nothingCompiler, interactiveCompiler, batchCompiler)
         hsc_env0 summary mod_index nmods mb_old_iface maybe_old_linkable
@@ -127,11 +123,11 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
    let dflags0     = ms_hspp_opts summary
        this_mod    = ms_mod summary
        src_flavour = ms_hsc_src summary
-       location           = ms_location summary
-       input_fn    = expectJust "compile:hs" (ml_hs_file location) 
+       location    = ms_location summary
+       input_fn    = expectJust "compile:hs" (ml_hs_file location)
        input_fnpp  = ms_hspp_file summary
 
-   liftIO $ debugTraceMsg dflags0 2 (text "compile: input file" <+> text input_fnpp)
+   debugTraceMsg dflags0 2 (text "compile: input file" <+> text input_fnpp)
 
    let basename = dropExtension input_fn
 
@@ -146,16 +142,16 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
        hsc_env     = hsc_env0 {hsc_dflags = dflags}
 
    -- Figure out what lang we're generating
-   let hsc_lang = hscMaybeAdjustTarget dflags StopLn src_flavour (hscTarget dflags)
+   let hsc_lang = hscTarget dflags
    -- ... and what the next phase should be
    let next_phase = hscNextPhase dflags src_flavour hsc_lang
    -- ... and what file to generate the output into
-   output_fn <- liftIO $ getOutputFilename next_phase
-                       Temporary basename dflags next_phase (Just location)
+   output_fn <- getOutputFilename next_phase
+                        Temporary basename dflags next_phase (Just location)
 
    let dflags' = dflags { hscTarget = hsc_lang,
-                               hscOutName = output_fn,
-                               extCoreName = basename ++ ".hcr" }
+                                hscOutName = output_fn,
+                                extCoreName = basename ++ ".hcr" }
    let hsc_env' = hsc_env { hsc_dflags = dflags' }
 
    -- -fforce-recomp should also work with --make
@@ -163,12 +159,7 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
        source_unchanged = isJust maybe_old_linkable && not force_recomp
        object_filename = ml_obj_file location
 
-   let getStubLinkable False = return []
-       getStubLinkable True
-           = do stub_o <- compileStub hsc_env' this_mod location
-                return [ DotO stub_o ]
-
-       handleBatch HscNoRecomp
+   let handleBatch HscNoRecomp
            = ASSERT (isJust maybe_old_linkable)
              return maybe_old_linkable
 
@@ -180,22 +171,27 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
                     return maybe_old_linkable
 
            | otherwise
-               = do stub_unlinked <- getStubLinkable hasStub
-                    (hs_unlinked, unlinked_time) <-
+               = do (hs_unlinked, unlinked_time) <-
                         case hsc_lang of
-                          HscNothing
-                            -> return ([], ms_hs_date summary)
+                          HscNothing ->
+                            return ([], ms_hs_date summary)
                           -- We're in --make mode: finish the compilation pipeline.
-                          _other
-                            -> do _ <- runPipeline StopLn hsc_env' (output_fn,Nothing)
+                          _other -> do
+                            maybe_stub_o <- case hasStub of
+                               Nothing -> return Nothing
+                               Just stub_c -> do
+                                 stub_o <- compileStub hsc_env' stub_c
+                                 return (Just stub_o)
+                            _ <- runPipeline StopLn hsc_env' (output_fn,Nothing)
                                               (Just basename)
                                               Persistent
                                               (Just location)
+                                              maybe_stub_o
                                   -- The object filename comes from the ModLocation
-                                  o_time <- liftIO $ getModificationTime object_filename
-                                  return ([DotO object_filename], o_time)
-                    let linkable = LM unlinked_time this_mod
-                                  (hs_unlinked ++ stub_unlinked)
+                            o_time <- getModificationTime object_filename
+                            return ([DotO object_filename], o_time)
+                    
+                    let linkable = LM unlinked_time this_mod hs_unlinked
                     return (Just linkable)
 
        handleInterpreted HscNoRecomp
@@ -205,7 +201,12 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
            = ASSERT (isHsBoot src_flavour)
              return maybe_old_linkable
        handleInterpreted (HscRecomp hasStub (Just (comp_bc, modBreaks)))
-           = do stub_unlinked <- getStubLinkable hasStub
+           = do 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 modBreaks]
                     unlinked_time = ms_hs_date summary
                   -- Why do we use the timestamp of the source file here,
@@ -215,7 +216,7 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
                   -- if the source is modified, then the linkable will
                   -- be out of date.
                 let linkable = LM unlinked_time this_mod
-                               (hs_unlinked ++ stub_unlinked)
+                               (hs_unlinked ++ stub_o)
                 return (Just linkable)
 
    let -- runCompiler :: Compiler result -> (result -> Maybe Linkable)
@@ -230,13 +231,9 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
                                      hm_linkable = linkable })
    -- run the compiler
    case hsc_lang of
-      HscInterpreted ->
-                runCompiler interactiveCompiler handleInterpreted
-      HscNothing -> 
-                runCompiler nothingCompiler handleBatch
-      _other -> 
-                runCompiler batchCompiler handleBatch
-
+      HscInterpreted -> runCompiler interactiveCompiler handleInterpreted
+      HscNothing     -> runCompiler nothingCompiler     handleBatch
+      _other         -> runCompiler batchCompiler       handleBatch
 
 -----------------------------------------------------------------------------
 -- stub .h and .c files (for foreign export support)
@@ -244,31 +241,16 @@ compile' (nothingCompiler, interactiveCompiler, batchCompiler)
 -- The _stub.c file is derived from the haskell source file, possibly taking
 -- into account the -stubdir option.
 --
--- Consequently, we derive the _stub.o filename from the haskell object
--- filename.  
---
--- This isn't necessarily the same as the object filename we
--- would get if we just compiled the _stub.c file using the pipeline.
--- For example:
---
---    ghc src/A.hs -odir obj
--- 
--- results in obj/A.o, and src/A_stub.c.  If we compile src/A_stub.c with
--- -odir obj, we would get obj/src/A_stub.o, which is wrong; we want
--- obj/A_stub.o.
+-- 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 MergeStubs phase).
 
-compileStub :: GhcMonad m => HscEnv -> Module -> ModLocation
-            -> m FilePath
-compileStub hsc_env mod location = do
-       -- compile the _stub.c file w/ gcc
-       let (stub_c,_,stub_o) = mkStubPaths (hsc_dflags hsc_env) 
-                                   (moduleName mod) location
-
-       _ <- runPipeline StopLn hsc_env (stub_c,Nothing)  Nothing
-               (SpecificFile stub_o) Nothing{-no ModLocation-}
-
-       return stub_o
+compileStub :: HscEnv -> FilePath -> IO FilePath
+compileStub hsc_env stub_c = do
+        (_, stub_o) <- runPipeline StopLn hsc_env (stub_c,Nothing)  Nothing
+                                   Temporary Nothing{-no ModLocation-} Nothing
 
+        return stub_o
 
 -- ---------------------------------------------------------------------------
 -- Link
@@ -286,11 +268,11 @@ link :: GhcLink                 -- interactive or batch
 -- exports main, i.e., we have good reason to believe that linking
 -- will succeed.
 
-#ifdef GHCI
 link LinkInMemory _ _ _
-    = do -- Not Linking...(demand linker will do the job)
-         return Succeeded
-#endif
+    = if cGhcWithInterpreter == "YES"
+      then -- Not Linking...(demand linker will do the job)
+           return Succeeded
+      else panicBadLink LinkInMemory
 
 link NoLink _ _ _
    = return Succeeded
@@ -301,11 +283,6 @@ link LinkBinary dflags batch_attempt_linking hpt
 link LinkDynLib dflags batch_attempt_linking hpt
    = link' dflags batch_attempt_linking hpt
 
-#ifndef GHCI
--- warning suppression
-link other _ _ _ = panicBadLink other
-#endif
-
 panicBadLink :: GhcLink -> a
 panicBadLink other = panic ("link: GHC not built to link this way: " ++
                             show other)
@@ -374,17 +351,17 @@ linkingNeeded dflags linkables pkg_deps = do
         -- modification times on all of the objects and libraries, then omit
         -- linking (unless the -fforce-recomp flag was given).
   let exe_file = exeFileName dflags
-  e_exe_time <- IO.try $ getModificationTime exe_file
+  e_exe_time <- tryIO $ getModificationTime exe_file
   case e_exe_time of
     Left _  -> return True
     Right t -> do
         -- first check object files and extra_ld_inputs
         extra_ld_inputs <- readIORef v_Ld_inputs
-        e_extra_times <- mapM (IO.try . getModificationTime) extra_ld_inputs
+        e_extra_times <- mapM (tryIO . getModificationTime) extra_ld_inputs
         let (errs,extra_times) = splitEithers e_extra_times
         let obj_times =  map linkableTime linkables ++ extra_times
         if not (null errs) || any (t <) obj_times
-            then return True 
+            then return True
             else do
 
         -- next, check libraries. XXX this only checks Haskell libraries,
@@ -396,12 +373,35 @@ linkingNeeded dflags linkables pkg_deps = do
 
         pkg_libfiles <- mapM (uncurry findHSLib) pkg_hslibs
         if any isNothing pkg_libfiles then return True else do
-        e_lib_times <- mapM (IO.try . getModificationTime)
+        e_lib_times <- mapM (tryIO . getModificationTime)
                           (catMaybes pkg_libfiles)
         let (lib_errs,lib_times) = splitEithers e_lib_times
         if not (null lib_errs) || any (t <) lib_times
            then return True
-           else return False
+           else checkLinkInfo dflags pkg_deps exe_file
+
+-- Returns 'False' if it was, and we can avoid linking, because the
+-- previous binary was linked with "the same options".
+checkLinkInfo :: DynFlags -> [PackageId] -> FilePath -> IO Bool
+checkLinkInfo dflags pkg_deps exe_file
+ | isWindowsTarget || isDarwinTarget
+ -- ToDo: Windows and OS X do not use the ELF binary format, so
+ -- readelf does not work there.  We need to find another way to do
+ -- this.
+ = return False -- conservatively we should return True, but not
+                -- linking in this case was the behaviour for a long
+                -- time so we leave it as-is.
+ | otherwise
+ = do
+   link_info <- getLinkInfo dflags pkg_deps
+   debugTraceMsg dflags 3 $ text ("Link info: " ++ link_info)
+   m_exe_link_info <- readElfSection dflags ghcLinkInfoSectionName exe_file
+   debugTraceMsg dflags 3 $ text ("Exe link info: " ++ show m_exe_link_info)
+   return (Just link_info /= m_exe_link_info)
+
+ghcLinkInfoSectionName :: String
+ghcLinkInfoSectionName = ".debug-ghc-link-info"
+   -- if we use the ".debug" prefix, then strip will strip it by default
 
 findHSLib :: [String] -> String -> IO (Maybe FilePath)
 findHSLib dirs lib = do
@@ -414,75 +414,67 @@ findHSLib dirs lib = do
 -- -----------------------------------------------------------------------------
 -- Compile files in one-shot mode.
 
-oneShot :: GhcMonad m =>
-           HscEnv -> Phase -> [(String, Maybe Phase)] -> m ()
+oneShot :: HscEnv -> Phase -> [(String, Maybe Phase)] -> IO ()
 oneShot hsc_env stop_phase srcs = do
   o_files <- mapM (compileFile hsc_env stop_phase) srcs
-  liftIO $ doLink (hsc_dflags hsc_env) stop_phase o_files
+  doLink (hsc_dflags hsc_env) stop_phase o_files
 
-compileFile :: GhcMonad m =>
-               HscEnv -> Phase -> (FilePath, Maybe Phase) -> m FilePath
+compileFile :: HscEnv -> Phase -> (FilePath, Maybe Phase) -> IO FilePath
 compileFile hsc_env stop_phase (src, mb_phase) = do
-   exists <- liftIO $ doesFileExist src
-   when (not exists) $ 
-       ghcError (CmdLineError ("does not exist: " ++ src))
-   
+   exists <- doesFileExist src
+   when (not exists) $
+        ghcError (CmdLineError ("does not exist: " ++ src))
+
    let
         dflags = hsc_dflags hsc_env
-       split     = dopt Opt_SplitObjs dflags
-       mb_o_file = outputFile dflags
-       ghc_link  = ghcLink dflags      -- Set by -c or -no-link
+        split     = dopt Opt_SplitObjs dflags
+        mb_o_file = outputFile dflags
+        ghc_link  = ghcLink dflags      -- Set by -c or -no-link
 
-       -- When linking, the -o argument refers to the linker's output. 
-       -- otherwise, we use it as the name for the pipeline's output.
+        -- When linking, the -o argument refers to the linker's output.
+        -- otherwise, we use it as the name for the pipeline's output.
         output
-        | StopLn <- stop_phase, not (isNoLink ghc_link) = Persistent
-               -- -o foo applies to linker
-        | Just o_file <- mb_o_file = SpecificFile o_file
-               -- -o foo applies to the file we are compiling now
-        | otherwise = Persistent
-
-        stop_phase' = case stop_phase of 
-                       As | split -> SplitAs
+         | StopLn <- stop_phase, not (isNoLink ghc_link) = Persistent
+                -- -o foo applies to linker
+         | Just o_file <- mb_o_file = SpecificFile o_file
+                -- -o foo applies to the file we are compiling now
+         | otherwise = Persistent
+
+        stop_phase' = case stop_phase of
+                        As | split -> SplitAs
                         _          -> stop_phase
 
    ( _, out_file) <- runPipeline stop_phase' hsc_env
                             (src, mb_phase) Nothing output
-                            Nothing{-no ModLocation-}
+                            Nothing{-no ModLocation-} Nothing
    return out_file
 
 
 doLink :: DynFlags -> Phase -> [FilePath] -> IO ()
 doLink dflags stop_phase o_files
   | not (isStopLn stop_phase)
-  = return ()          -- We stopped before the linking phase
+  = return ()           -- We stopped before the linking phase
 
   | otherwise
   = case ghcLink dflags of
-       NoLink     -> return ()
-       LinkBinary -> linkBinary dflags o_files link_pkgs
-       LinkDynLib -> linkDynLib dflags o_files []
+        NoLink     -> return ()
+        LinkBinary -> linkBinary dflags o_files []
+        LinkDynLib -> linkDynLib dflags o_files []
         other      -> panicBadLink other
-  where
-   -- Always link in the haskell98 package for static linking.  Other
-   -- packages have to be specified via the -package flag.
-    link_pkgs
-     | dopt Opt_AutoLinkPackages dflags = [haskell98PackageId]
-     | otherwise                        = []
 
 
 -- ---------------------------------------------------------------------------
 
-data PipelineOutput 
+data PipelineOutput
   = Temporary
-       -- ^ Output should be to a temporary file: we're going to
-       -- run more compilation steps on this output later.
+        -- ^ Output should be to a temporary file: we're going to
+        -- run more compilation steps on this output later.
   | Persistent
-       -- ^ We want a persistent file, i.e. a file in the current directory
-       -- derived from the input filename, but with the appropriate extension.
-       -- eg. in "ghc -c Foo.hs" the output goes into ./Foo.o.
+        -- ^ We want a persistent file, i.e. a file in the current directory
+        -- derived from the input filename, but with the appropriate extension.
+        -- eg. in "ghc -c Foo.hs" the output goes into ./Foo.o.
   | SpecificFile FilePath
-       -- ^ The output must go into the specified file.
+        -- ^ The output must go into the specified file.
 
 -- | Run a compilation pipeline, consisting of multiple phases.
 --
@@ -494,16 +486,17 @@ data PipelineOutput
 -- OPTIONS_GHC pragmas), and the changes affect later phases in the
 -- pipeline.
 runPipeline
-  :: GhcMonad m =>
-     Phase                     -- ^ When to stop
+  :: Phase                      -- ^ When to stop
   -> HscEnv                     -- ^ Compilation environment
   -> (FilePath,Maybe Phase)     -- ^ Input filename (and maybe -x suffix)
   -> Maybe FilePath             -- ^ original basename (if different from ^^^)
-  -> PipelineOutput            -- ^ Output filename
+  -> PipelineOutput             -- ^ Output filename
   -> Maybe ModLocation          -- ^ A ModLocation, if this is a Haskell module
-  -> m (DynFlags, FilePath)    -- ^ (final flags, output filename)
+  -> Maybe FilePath             -- ^ stub object, if we have one
+  -> IO (DynFlags, FilePath)     -- ^ (final flags, output filename)
 
-runPipeline stop_phase hsc_env0 (input_fn, mb_phase) mb_basename output maybe_loc
+runPipeline stop_phase hsc_env0 (input_fn, mb_phase)
+            mb_basename output maybe_loc maybe_stub_o
   = do
   let dflags0 = hsc_dflags hsc_env0
       (input_basename, suffix) = splitExtension input_fn
@@ -515,7 +508,7 @@ runPipeline stop_phase hsc_env0 (input_fn, mb_phase) mb_basename output maybe_lo
       dflags = dflags0 { dumpPrefix = Just (basename ++ ".") }
       hsc_env = hsc_env0 {hsc_dflags = dflags}
 
-       -- If we were given a -x flag, then use that phase to start from
+        -- If we were given a -x flag, then use that phase to start from
       start_phase = fromMaybe (startPhase suffix') mb_phase
 
   -- We want to catch cases of "you can't get there from here" before
@@ -526,18 +519,26 @@ runPipeline stop_phase hsc_env0 (input_fn, mb_phase) mb_basename output maybe_lo
   -- before B in a normal compilation pipeline.
 
   when (not (start_phase `happensBefore` stop_phase)) $
-       ghcError (UsageError 
-                   ("cannot compile this file to desired target: "
-                      ++ input_fn))
+        ghcError (UsageError
+                    ("cannot compile this file to desired target: "
+                       ++ input_fn))
 
   -- this is a function which will be used to calculate output file names
   -- as we go along (we partially apply it to some of its inputs here)
   let get_output_fn = getOutputFilename stop_phase output basename
 
   -- Execute the pipeline...
-  (dflags', output_fn, maybe_loc) <- 
-       pipeLoop hsc_env start_phase stop_phase input_fn 
-                basename suffix' get_output_fn maybe_loc
+  let env   = PipeEnv{ stop_phase,
+                       src_basename = basename,
+                       src_suffix = suffix',
+                       output_spec = output }
+
+      state = PipeState{ hsc_env, maybe_loc, maybe_stub_o = maybe_stub_o }
+
+  (state', output_fn) <- unP (pipeLoop start_phase input_fn) env state
+
+  let PipeState{ hsc_env=hsc_env', maybe_loc } = state'
+      dflags' = hsc_dflags 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
@@ -545,47 +546,112 @@ runPipeline stop_phase hsc_env0 (input_fn, mb_phase) mb_basename output maybe_lo
   -- copy the file, remembering to prepend a {-# LINE #-} pragma so that
   -- further compilation stages can tell what the original filename was.
   case output of
-    Temporary -> 
-       return (dflags', output_fn)
-    _other -> liftIO $
-       do final_fn <- get_output_fn dflags' stop_phase maybe_loc
-          when (final_fn /= output_fn) $ do
+    Temporary ->
+        return (dflags', output_fn)
+    _other -> 
+        do final_fn <- get_output_fn dflags' stop_phase maybe_loc
+           when (final_fn /= output_fn) $ do
               let msg = ("Copying `" ++ output_fn ++"' to `" ++ final_fn ++ "'")
                   line_prag = Just ("{-# LINE 1 \"" ++ input_fn ++ "\" #-}\n")
-             copyWithHeader dflags msg line_prag output_fn final_fn
-          return (dflags', final_fn)
-
-
+              copyWithHeader dflags msg line_prag output_fn final_fn
+           return (dflags', final_fn)
 
-pipeLoop :: GhcMonad m =>
-            HscEnv -> Phase -> Phase
-        -> FilePath  -> String -> Suffix
-        -> (DynFlags -> Phase -> Maybe ModLocation -> IO FilePath)
-        -> Maybe ModLocation
-        -> m (DynFlags, FilePath, Maybe ModLocation)
-
-pipeLoop hsc_env phase stop_phase 
-        input_fn orig_basename orig_suff 
-        orig_get_output_fn maybe_loc
-
-  | phase `eqPhase` stop_phase           -- All done
-  = return (hsc_dflags hsc_env, input_fn, maybe_loc)
+-- -----------------------------------------------------------------------------
+-- The pipeline uses a monad to carry around various bits of information
+
+-- PipeEnv: invariant information passed down
+data PipeEnv = PipeEnv {
+       stop_phase   :: Phase,       -- ^ Stop just before this phase
+       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 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.
+       maybe_stub_o :: Maybe FilePath
+          -- ^ the stub object.  This is set by the Hsc phase if a stub
+          -- object was created.  The stub object will be joined with
+          -- the main compilation object using "ld -r" at the end.
+  }
+
+getPipeEnv :: CompPipeline PipeEnv
+getPipeEnv = P $ \env state -> return (state, env)
+
+getPipeState :: CompPipeline PipeState
+getPipeState = P $ \_env state -> return (state, state)
+
+getDynFlags :: CompPipeline DynFlags
+getDynFlags = P $ \_env state -> return (state, hsc_dflags (hsc_env state))
+
+setDynFlags :: DynFlags -> CompPipeline ()
+setDynFlags dflags = P $ \_env state ->
+  return (state{hsc_env= (hsc_env state){ hsc_dflags = dflags }}, ())
+
+setModLocation :: ModLocation -> CompPipeline ()
+setModLocation loc = P $ \_env state ->
+  return (state{ maybe_loc = Just loc }, ())
+
+setStubO :: FilePath -> CompPipeline ()
+setStubO stub_o = P $ \_env state ->
+  return (state{ maybe_stub_o = Just stub_o }, ())
+
+newtype CompPipeline a = P { unP :: PipeEnv -> PipeState -> IO (PipeState, a) }
+
+instance Monad CompPipeline where
+  return a = P $ \_env state -> return (state, a)
+  P m >>= k = P $ \env state -> do (state',a) <- m env state
+                                   unP (k a) env state'
+
+io :: IO a -> CompPipeline a
+io m = P $ \_env state -> do a <- m; return (state, a)
+
+phaseOutputFilename :: Phase{-next phase-} -> CompPipeline FilePath
+phaseOutputFilename next_phase = do
+  PipeEnv{stop_phase, src_basename, output_spec} <- getPipeEnv
+  PipeState{maybe_loc, hsc_env} <- getPipeState
+  let dflags = hsc_dflags hsc_env
+  io $ getOutputFilename stop_phase output_spec
+                         src_basename dflags next_phase maybe_loc
 
-  | not (phase `happensBefore` stop_phase)
-       -- 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 phase ++ 
-          " but I wanted to stop at phase " ++ show stop_phase)
+-- ---------------------------------------------------------------------------
+-- outer pipeline loop
+
+-- | pipeLoop runs phases until we reach the stop phase
+pipeLoop :: Phase -> FilePath -> CompPipeline FilePath
+pipeLoop phase input_fn = do
+  PipeEnv{stop_phase} <- getPipeEnv
+  PipeState{hsc_env}  <- getPipeState
+  case () of
+   _ | phase `eqPhase` stop_phase            -- All done
+     -> return input_fn
+
+     | not (phase `happensBefore` stop_phase)
+        -- 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 phase ++
+           " but I wanted to stop at phase " ++ show stop_phase)
+
+     | otherwise
+     -> do io $ debugTraceMsg (hsc_dflags hsc_env) 4
+                         (ptext (sLit "Running phase") <+> ppr phase)
+           dflags <- getDynFlags
+           (next_phase, output_fn) <- runPhase phase input_fn dflags
+           pipeLoop next_phase output_fn
 
-  | otherwise 
-  = do (next_phase, dflags', maybe_loc, output_fn)
-          <- runPhase phase stop_phase hsc_env orig_basename 
-                      orig_suff input_fn orig_get_output_fn maybe_loc
-       let hsc_env' = hsc_env {hsc_dflags = dflags'}
-       pipeLoop hsc_env' next_phase stop_phase output_fn
-                orig_basename orig_suff orig_get_output_fn maybe_loc
+-- -----------------------------------------------------------------------------
+-- 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.
 
 getOutputFilename
   :: Phase -> PipelineOutput -> String
@@ -593,47 +659,47 @@ getOutputFilename
 getOutputFilename stop_phase output basename
  = func
  where
-       func dflags next_phase maybe_location
-          | is_last_phase, Persistent <- output     = persistent_fn
-          | is_last_phase, SpecificFile f <- output = return f
-          | keep_this_output                        = persistent_fn
-          | otherwise                               = newTempName dflags suffix
-          where
-               hcsuf      = hcSuf dflags
-               odir       = objectDir dflags
-               osuf       = objectSuf dflags
-               keep_hc    = dopt Opt_KeepHcFiles dflags
-               keep_raw_s = dopt Opt_KeepRawSFiles dflags
-               keep_s     = dopt Opt_KeepSFiles dflags
-
-               myPhaseInputExt HCc    = hcsuf
-               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
-                            StopLn              -> True
-                            Mangle | keep_raw_s -> True
-                            As     | keep_s     -> True
-                            HCc    | keep_hc    -> True
-                            _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
+        func dflags next_phase maybe_location
+           | is_last_phase, Persistent <- output     = persistent_fn
+           | is_last_phase, SpecificFile f <- output = return f
+           | keep_this_output                        = persistent_fn
+           | otherwise                               = newTempName dflags suffix
+           where
+                hcsuf      = hcSuf dflags
+                odir       = objectDir dflags
+                osuf       = objectSuf dflags
+                keep_hc    = dopt Opt_KeepHcFiles dflags
+                keep_s     = dopt Opt_KeepSFiles dflags
+                keep_bc    = dopt Opt_KeepLlvmFiles dflags
+
+                myPhaseInputExt HCc       = hcsuf
+                myPhaseInputExt MergeStub = 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
+                             _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
 
 
 -- -----------------------------------------------------------------------------
@@ -646,32 +712,23 @@ getOutputFilename stop_phase output basename
 -- of a source file can change the latter stages of the pipeline from
 -- taking the via-C route to using the native code generator.
 --
-runPhase :: GhcMonad m =>
-            Phase      -- ^ Do this phase first
-        -> Phase       -- ^ Stop just before this phase
-        -> HscEnv
-        -> String      -- ^ basename of original input source
-        -> String      -- ^ its extension
-        -> FilePath    -- ^ name of file which contains the input to this phase.
-        -> (DynFlags -> Phase -> Maybe ModLocation -> IO FilePath)
-                       -- ^ how to calculate the output filename
-        -> Maybe ModLocation           -- ^ the ModLocation, if we have one
-        -> m (Phase,                   -- next phase
-               DynFlags,               -- new dynamic flags
-              Maybe ModLocation,       -- the ModLocation, if we have one
-              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 
+runPhase :: Phase       -- ^ Run this phase
+         -> FilePath    -- ^ name of the input file
+         -> DynFlags    -- ^ for convenience, we pass the current dflags in
+         -> CompPipeline (Phase,               -- 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 
+-- Unlit phase
 
-runPhase (Unlit sf) _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
+runPhase (Unlit sf) input_fn dflags
   = do
-       let dflags = hsc_dflags hsc_env
-       output_fn <- liftIO $ get_output_fn dflags (Cpp sf) maybe_loc
+       output_fn <- phaseOutputFilename (Cpp sf)
 
        let unlit_flags = getOpts dflags opt_L
            flags = map SysTools.Option unlit_flags ++
@@ -685,117 +742,147 @@ runPhase (Unlit sf) _stop hsc_env _basename _suff input_fn get_output_fn maybe_l
                    , SysTools.FileOption "" output_fn
                    ]
 
-       liftIO $ SysTools.runUnlit dflags flags
+       io $ SysTools.runUnlit dflags flags
 
-       return (Cpp sf, dflags, maybe_loc, output_fn)
+       return (Cpp sf, output_fn)
 
 -------------------------------------------------------------------------------
 -- Cpp phase : (a) gets OPTIONS out of file
---            (b) runs cpp if necessary
+--             (b) runs cpp if necessary
 
-runPhase (Cpp sf) _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
-  = do let dflags0 = hsc_dflags hsc_env
-       src_opts <- liftIO $ getOptionsFromFile dflags0 input_fn
-       (dflags, unhandled_flags, warns)
-           <- liftIO $ parseDynamicNoPackageFlags dflags0 src_opts
-       handleFlagWarnings dflags warns
-       checkProcessArgsResult unhandled_flags
+runPhase (Cpp sf) input_fn dflags0
+  = do
+       src_opts <- io $ getOptionsFromFile dflags0 input_fn
+       (dflags1, unhandled_flags, warns)
+           <- io $ parseDynamicNoPackageFlags dflags0 src_opts
+       setDynFlags dflags1
+       io $ checkProcessArgsResult unhandled_flags
+
+       if not (xopt Opt_Cpp dflags1) then do
+           -- we have to be careful to emit warnings only once.
+           unless (dopt Opt_Pp dflags1) $ io $ handleFlagWarnings dflags1 warns
 
-       if not (dopt Opt_Cpp dflags) then
            -- no need to preprocess CPP, just pass input file along
-          -- to the next phase of the pipeline.
-          return (HsPp sf, dflags, maybe_loc, input_fn)
-       else do
-           output_fn <- liftIO $ get_output_fn dflags (HsPp sf) maybe_loc
-           liftIO $ doCpp dflags True{-raw-} False{-no CC opts-} input_fn output_fn
-           return (HsPp sf, dflags, maybe_loc, output_fn)
+           -- to the next phase of the pipeline.
+           return (HsPp sf, input_fn)
+        else do
+            output_fn <- phaseOutputFilename (HsPp sf)
+            io $ doCpp dflags1 True{-raw-} False{-no CC opts-} input_fn output_fn
+            -- re-read the pragmas now that we've preprocessed the file
+            -- See #2464,#3457
+            src_opts <- io $ getOptionsFromFile dflags0 output_fn
+            (dflags2, unhandled_flags, warns)
+                <- io $ parseDynamicNoPackageFlags dflags0 src_opts
+            io $ checkProcessArgsResult unhandled_flags
+            unless (dopt Opt_Pp dflags2) $ io $ handleFlagWarnings dflags2 warns
+            -- the HsPp pass below will emit warnings
+
+            setDynFlags dflags2
+
+            return (HsPp sf, output_fn)
 
 -------------------------------------------------------------------------------
--- HsPp phase 
+-- HsPp phase
 
-runPhase (HsPp sf) _stop hsc_env basename suff input_fn get_output_fn maybe_loc
-  = do let dflags = hsc_dflags hsc_env
+runPhase (HsPp sf) input_fn dflags
+  = do
        if not (dopt Opt_Pp dflags) then
            -- no need to preprocess, just pass input file along
-          -- to the next phase of the pipeline.
-          return (Hsc sf, dflags, maybe_loc, input_fn)
-       else do
-           let hspp_opts = getOpts dflags opt_F
-           let orig_fn = basename <.> suff
-           output_fn <- liftIO $ get_output_fn dflags (Hsc sf) maybe_loc
-           liftIO $ SysTools.runPp dflags
-                          ( [ SysTools.Option     orig_fn
-                            , SysTools.Option     input_fn
-                            , SysTools.FileOption "" output_fn
-                            ] ++
-                            map SysTools.Option hspp_opts
-                          )
-           return (Hsc sf, dflags, maybe_loc, output_fn)
+           -- to the next phase of the pipeline.
+          return (Hsc sf, input_fn)
+        else do
+            let hspp_opts = getOpts dflags opt_F
+            PipeEnv{src_basename, src_suffix} <- getPipeEnv
+            let orig_fn = src_basename <.> src_suffix
+            output_fn <- phaseOutputFilename (Hsc sf)
+            io $ SysTools.runPp dflags
+                           ( [ SysTools.Option     orig_fn
+                             , SysTools.Option     input_fn
+                             , SysTools.FileOption "" output_fn
+                             ] ++
+                             map SysTools.Option hspp_opts
+                           )
+
+            -- re-read pragmas now that we've parsed the file (see #3674)
+            src_opts <- io $ getOptionsFromFile dflags output_fn
+            (dflags1, unhandled_flags, warns)
+                <- io $ parseDynamicNoPackageFlags dflags src_opts
+            setDynFlags dflags1
+            io $ checkProcessArgsResult unhandled_flags
+            io $ handleFlagWarnings dflags1 warns
+
+            return (Hsc sf, output_fn)
 
 -----------------------------------------------------------------------------
 -- Hsc phase
 
 -- Compilation of a single module, in "legacy" mode (_not_ under
 -- the direction of the compilation manager).
-runPhase (Hsc src_flavour) stop hsc_env basename suff input_fn get_output_fn _maybe_loc 
- = do  -- normal Hsc mode, not mkdependHS
-        let dflags0 = hsc_dflags hsc_env
+runPhase (Hsc src_flavour) input_fn dflags0
+ = do   -- normal Hsc mode, not mkdependHS
+
+        PipeEnv{ stop_phase=stop,
+                 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 = case takeDirectory basename of
+        let current_dir = case takeDirectory basename of
                       "" -> "." -- XXX Hack
                       d -> d
-       
-           paths = includePaths dflags0
-           dflags = dflags0 { includePaths = current_dir : paths }
-       
+
+            paths = includePaths dflags0
+            dflags = dflags0 { includePaths = current_dir : paths }
+
+        setDynFlags dflags
+
   -- gather the imports and module name
-        (hspp_buf,mod_name,imps,src_imps) <- 
+        (hspp_buf,mod_name,imps,src_imps) <- io $
             case src_flavour of
                 ExtCoreFile -> do  -- no explicit imports in ExtCore input.
-                    m <- liftIO $ getCoreModuleName input_fn
+                    m <- getCoreModuleName input_fn
                     return (Nothing, mkModuleName m, [], [])
 
                 _           -> do
-                    buf <- liftIO $ hGetStringBuffer input_fn
+                    buf <- hGetStringBuffer input_fn
                     (src_imps,imps,L _ mod_name) <- getImports dflags buf input_fn (basename <.> suff)
                     return (Just buf, mod_name, imps, src_imps)
 
   -- Build a ModLocation to pass to hscMain.
   -- The source filename is rather irrelevant by now, but it's used
-  -- by hscMain for messages.  hscMain also needs 
+  -- by hscMain for messages.  hscMain also needs
   -- the .hi and .o filenames, and this is as good a way
-  -- as any to generate them, and better than most. (e.g. takes 
+  -- as any to generate them, and better than most. (e.g. takes
   -- into accout the -osuf flags)
-       location1 <- liftIO $ mkHomeModLocation2 dflags mod_name basename suff
+        location1 <- io $ mkHomeModLocation2 dflags mod_name basename suff
 
   -- Boot-ify it if necessary
-       let location2 | isHsBoot src_flavour = addBootSuffixLocn location1
-                     | otherwise            = location1 
-                                       
+        let location2 | isHsBoot src_flavour = addBootSuffixLocn 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
+        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
+        let expl_o_file = outputFile dflags
+            location4 | Just ofile <- expl_o_file
+                      , isNoLink (ghcLink dflags)
+                      = location3 { ml_obj_file = ofile }
+                      | otherwise = location3
 
-           o_file = ml_obj_file location4      -- The real object file
+            o_file = ml_obj_file location4      -- The real object file
 
+        setModLocation location4
 
   -- Figure out if the source has changed, for recompilation avoidance.
   --
@@ -804,104 +891,111 @@ runPhase (Hsc src_flavour) stop hsc_env basename suff input_fn get_output_fn _ma
   -- changed (which the compiler itself figures out).
   -- Setting source_unchanged to False tells the compiler that M.o is out of
   -- date wrt M.hs (or M.o doesn't exist) so we must recompile regardless.
-       src_timestamp <- liftIO $ getModificationTime (basename <.> suff)
+        src_timestamp <- io $ getModificationTime (basename <.> suff)
 
-       let force_recomp = dopt Opt_ForceRecomp dflags
-       source_unchanged <-
+        let force_recomp = dopt Opt_ForceRecomp dflags
+            hsc_lang = hscTarget dflags
+        source_unchanged <- io $
           if force_recomp || not (isStopLn stop)
-               -- Set source_unchanged to False unconditionally if
-               --      (a) recompilation checker is off, or
-               --      (b) we aren't going all the way to .o file (e.g. ghc -S)
-            then return False  
-               -- Otherwise look at file modification dates
-            else do o_file_exists <- liftIO $ doesFileExist o_file
-                    if not o_file_exists
-                       then return False       -- Need to recompile
-                       else do t2 <- liftIO $ getModificationTime o_file
-                               if t2 > src_timestamp
-                                 then return True
-                                 else return False
+                -- Set source_unchanged to False unconditionally if
+                --      (a) recompilation checker is off, or
+                --      (b) we aren't going all the way to .o file (e.g. ghc -S)
+             then return False
+                -- Otherwise look at file modification dates
+             else do o_file_exists <- doesFileExist o_file
+                     if not o_file_exists
+                        then return False       -- Need to recompile
+                        else do t2 <- getModificationTime o_file
+                                if t2 > src_timestamp
+                                  then return True
+                                  else return False
 
   -- get the DynFlags
-       let hsc_lang = hscMaybeAdjustTarget dflags stop src_flavour (hscTarget dflags)
-       let next_phase = hscNextPhase dflags src_flavour hsc_lang
-       output_fn  <- liftIO $ get_output_fn dflags next_phase (Just location4)
+        let next_phase = hscNextPhase dflags src_flavour hsc_lang
+        output_fn  <- phaseOutputFilename next_phase
 
         let dflags' = dflags { hscTarget = hsc_lang,
-                              hscOutName = output_fn,
-                              extCoreName = basename ++ ".hcr" }
+                               hscOutName = output_fn,
+                               extCoreName = basename ++ ".hcr" }
 
-        let hsc_env' = hsc_env {hsc_dflags = dflags'}
+        setDynFlags dflags'
+        PipeState{hsc_env=hsc_env'} <- getPipeState
 
   -- Tell the finder cache about this module
-       mod <- liftIO $ addHomeModuleToFinder hsc_env' mod_name location4
+        mod <- io $ addHomeModuleToFinder hsc_env' mod_name location4
 
   -- Make the ModSummary to hand to hscMain
-       let
-           mod_summary = ModSummary {  ms_mod       = mod, 
-                                       ms_hsc_src   = src_flavour,
-                                       ms_hspp_file = input_fn,
+        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  = location4,
-                                       ms_hs_date   = src_timestamp,
-                                       ms_obj_date  = Nothing,
-                                       ms_imps      = imps,
-                                       ms_srcimps   = src_imps }
+                                        ms_hspp_buf  = hspp_buf,
+                                        ms_location  = location4,
+                                        ms_hs_date   = src_timestamp,
+                                        ms_obj_date  = Nothing,
+                                        ms_imps      = imps,
+                                        ms_srcimps   = src_imps }
 
   -- run the compiler!
-       result <- hscCompileOneShot hsc_env'
-                         mod_summary source_unchanged 
-                         Nothing       -- No iface
+        result <- io $ hscCompileOneShot hsc_env'
+                          mod_summary source_unchanged
+                          Nothing       -- No iface
                           Nothing       -- No "module i of n" progress info
 
-       case result of
+        case result of
           HscNoRecomp
-              -> do liftIO $ SysTools.touch dflags' "Touching object file" o_file
+              -> do io $ SysTools.touch dflags' "Touching object file" o_file
                     -- The .o file must have a later modification date
                     -- than the source file (else we wouldn't be in HscNoRecomp)
                     -- but we touch it anyway, to keep 'make' happy (we think).
-                    return (StopLn, dflags', Just location4, o_file)
+                    return (StopLn, o_file)
           (HscRecomp hasStub _)
-              -> do when hasStub $
-                         do stub_o <- compileStub hsc_env' mod location4
-                            liftIO $ consIORef v_Ld_inputs stub_o
-                    -- In the case of hs-boot files, generate a dummy .o-boot 
+              -> do case hasStub of
+                      Nothing -> return ()
+                      Just stub_c ->
+                         do stub_o <- io $ compileStub hsc_env' stub_c
+                            setStubO stub_o
+                    -- In the case of hs-boot files, generate a dummy .o-boot
                     -- stamp file for the benefit of Make
                     when (isHsBoot src_flavour) $
-                      liftIO $ SysTools.touch dflags' "Touching object file" o_file
-                    return (next_phase, dflags', Just location4, output_fn)
+                      io $ SysTools.touch dflags' "Touching object file" o_file
+                    return (next_phase, output_fn)
 
 -----------------------------------------------------------------------------
 -- Cmm phase
 
-runPhase CmmCpp _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
+runPhase CmmCpp input_fn dflags
   = do
-       let dflags = hsc_dflags hsc_env
-       output_fn <- liftIO $ get_output_fn dflags Cmm maybe_loc
-       liftIO $ doCpp dflags False{-not raw-} True{-include CC opts-} input_fn output_fn
-       return (Cmm, dflags, maybe_loc, output_fn)
+       output_fn <- phaseOutputFilename Cmm
+       io $ doCpp dflags False{-not raw-} True{-include CC opts-}
+              input_fn output_fn
+       return (Cmm, output_fn)
 
-runPhase Cmm stop hsc_env basename _ input_fn get_output_fn maybe_loc
+runPhase Cmm input_fn dflags
   = do
-        let dflags = hsc_dflags hsc_env
-       let hsc_lang = hscMaybeAdjustTarget dflags stop HsSrcFile (hscTarget dflags)
-       let next_phase = hscNextPhase dflags HsSrcFile hsc_lang
-       output_fn <- liftIO $ get_output_fn dflags next_phase maybe_loc
+        PipeEnv{src_basename} <- getPipeEnv
+        let hsc_lang = hscTarget dflags
+
+        let next_phase = hscNextPhase dflags HsSrcFile hsc_lang
+
+        output_fn <- phaseOutputFilename next_phase
 
         let dflags' = dflags { hscTarget = hsc_lang,
-                              hscOutName = output_fn,
-                              extCoreName = basename ++ ".hcr" }
-        let hsc_env' = hsc_env {hsc_dflags = dflags'}
+                               hscOutName = output_fn,
+                               extCoreName = src_basename ++ ".hcr" }
 
-       hscCmmFile hsc_env' input_fn
+        setDynFlags dflags'
+        PipeState{hsc_env} <- getPipeState
+
+        io $ hscCompileCmmFile hsc_env input_fn
 
         -- XXX: catch errors above and convert them into ghcError?  Original
         -- code was:
         --
-       --when (not ok) $ ghcError (PhaseFailed "cmm" (ExitFailure 1))
+        --when (not ok) $ ghcError (PhaseFailed "cmm" (ExitFailure 1))
 
-       return (next_phase, dflags, maybe_loc, output_fn)
+        return (next_phase, output_fn)
 
 -----------------------------------------------------------------------------
 -- Cc phase
@@ -909,252 +1003,214 @@ runPhase Cmm stop hsc_env basename _ input_fn get_output_fn maybe_loc
 -- we don't support preprocessing .c files (with -E) now.  Doing so introduces
 -- way too many hacks, and I can't say I've ever used it anyway.
 
-runPhase cc_phase _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
-   | cc_phase `eqPhase` Cc || cc_phase `eqPhase` Ccpp || cc_phase `eqPhase` HCc
-   = do        let dflags = hsc_dflags hsc_env
+runPhase cc_phase input_fn dflags
+   | cc_phase `eqPhase` Cc || cc_phase `eqPhase` Ccpp || cc_phase `eqPhase` HCc || cc_phase `eqPhase` Cobjc
+   = do
         let cc_opts = getOpts dflags opt_c
-           hcc = cc_phase `eqPhase` HCc
+            hcc = cc_phase `eqPhase` HCc
 
-               let cmdline_include_paths = includePaths dflags
+        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 []
+        -- HC files have the dependent packages stamped into them
+        pkgs <- if hcc then io $ 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 :)
-        pkg_include_dirs <- liftIO $ getPackageIncludePath dflags pkgs
+        -- 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 :)
+        pkg_include_dirs <- io $ getPackageIncludePath dflags pkgs
         let include_paths = foldr (\ x xs -> "-I" : x : xs) []
-                             (cmdline_include_paths ++ pkg_include_dirs)
+                              (cmdline_include_paths ++ pkg_include_dirs)
 
-       let (md_c_flags, md_regd_c_flags) = machdepCCOpts dflags
-        gcc_extra_viac_flags <- liftIO $ getExtraViaCOpts dflags
+        let gcc_extra_viac_flags = extraGccViaCFlags dflags
         let pic_c_flags = picCCOpts dflags
 
-        let verb = getVerbFlag 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.
-       pkg_extra_cc_opts <-
+        pkg_extra_cc_opts <- io $
           if cc_phase `eqPhase` HCc
              then return []
-             else liftIO $ getPackageExtraCcOpts dflags pkgs
+             else getPackageExtraCcOpts dflags pkgs
 
 #ifdef darwin_TARGET_OS
-        pkg_framework_paths <- liftIO $ getPackageFrameworkPath dflags pkgs
+        pkg_framework_paths <- io $ getPackageFrameworkPath dflags pkgs
         let cmdline_framework_paths = frameworkPaths dflags
-        let framework_paths = map ("-F"++) 
+        let framework_paths = map ("-F"++)
                         (cmdline_framework_paths ++ pkg_framework_paths)
 #endif
 
-       let split_objs = dopt Opt_SplitObjs dflags
-           split_opt | hcc && split_objs = [ "-DUSE_SPLIT_MARKERS" ]
-                     | otherwise         = [ ]
-
-       let cc_opt | optLevel dflags >= 2 = "-O2"
-                  | otherwise            = "-O"
-
-       -- Decide next phase
-       
-        let mangle = dopt Opt_DoAsmMangling dflags
-            next_phase
-               | hcc && mangle     = Mangle
-               | otherwise         = As
-       output_fn <- liftIO $ get_output_fn dflags next_phase maybe_loc
-
-       let
-         more_hcc_opts =
-#if i386_TARGET_ARCH
-               -- 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 dopt Opt_ExcessPrecision dflags 
-                        then [] 
-                        else [ "-ffloat-store" ]) ++
-#endif
+        let split_objs = dopt Opt_SplitObjs dflags
+            split_opt | hcc && split_objs = [ "-DUSE_SPLIT_MARKERS" ]
+                      | otherwise         = [ ]
+
+        let cc_opt | optLevel dflags >= 2 = "-O2"
+                   | otherwise            = "-O"
+
+        -- Decide next phase
+
+        let next_phase = As
+        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 (targetPlatform dflags) == ArchX86 &&
+                    not (dopt 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"]
+
+        let gcc_lang_opt | cc_phase `eqPhase` Ccpp  = "c++"
+                         | cc_phase `eqPhase` Cobjc = "objective-c"
+                         | otherwise                = "c"
+        io $ SysTools.runCc dflags (
+                -- force the C compiler to interpret this file as C when
+                -- compiling .hc files, by adding the -x c option.
+                -- Also useful for plain .c files, just in case GHC saw a
+                -- -x c option.
+                        [ SysTools.Option "-x", SysTools.Option gcc_lang_opt
+                        , SysTools.FileOption "" input_fn
+                        , SysTools.Option "-o"
+                        , SysTools.FileOption "" output_fn
+                        ]
+                       ++ map 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 (targetPlatform dflags) == OSMinGW32 &&
+                              thisPackage dflags == basePackageId
+                                then [ "-DCOMPILING_BASE_PACKAGE" ]
+                                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"]
-
-       liftIO $ SysTools.runCc dflags (
-               -- force the C compiler to interpret this file as C when
-               -- compiling .hc files, by adding the -x c option.
-               -- Also useful for plain .c files, just in case GHC saw a 
-               -- -x c option.
-                       [ SysTools.Option "-x", if cc_phase `eqPhase` Ccpp
-                                                then SysTools.Option "c++" 
-                                                else SysTools.Option "c"] ++
-                       [ SysTools.FileOption "" input_fn
-                       , SysTools.Option "-o"
-                       , SysTools.FileOption "" output_fn
-                       ]
-                      ++ map SysTools.Option (
-                         md_c_flags
-                       ++ pic_c_flags
-
-#if    defined(mingw32_TARGET_OS)
-               -- 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 thisPackage dflags == basePackageId
-                               then [ "-DCOMPILING_BASE_PACKAGE" ]
-                               else [])
-#endif 
-
-#ifdef sparc_TARGET_ARCH
         -- 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.
+        -- (e.g., -mcpu=ultrasparc) as GCC picks the "best" -mcpu flag
+        -- regardless of the ordering.
         --
         -- This is a temporary hack.
-                       ++ ["-mcpu=v9"]
-#endif
-                      ++ (if hcc && mangle
-                            then md_regd_c_flags
-                            else [])
-                      ++ (if hcc
-                            then if mangle 
-                                     then gcc_extra_viac_flags
-                                     else filter (=="-fwrapv")
-                                                gcc_extra_viac_flags
-                                -- still want -fwrapv even for unreg'd
-                            else [])
-                      ++ (if hcc 
-                            then more_hcc_opts
-                            else [])
-                      ++ [ verb, "-S", "-Wimplicit", cc_opt ]
-                      ++ [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
+                       ++ (if platformArch (targetPlatform dflags) == ArchSPARC
+                           then ["-mcpu=v9"]
+                           else [])
+
+                       ++ (if hcc
+                             then gcc_extra_viac_flags ++ more_hcc_opts
+                             else [])
+                       ++ verbFlags
+                       ++ [ "-S", "-Wimplicit", cc_opt ]
+                       ++ [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
 #ifdef darwin_TARGET_OS
                        ++ framework_paths
 #endif
-                      ++ cc_opts
-                      ++ split_opt
-                      ++ include_paths
-                      ++ pkg_extra_cc_opts
-                      ))
+                       ++ cc_opts
+                       ++ split_opt
+                       ++ include_paths
+                       ++ pkg_extra_cc_opts
+                       ))
 
-       return (next_phase, dflags, maybe_loc, output_fn)
+        return (next_phase, output_fn)
 
-       -- ToDo: postprocess the output from gcc
+        -- ToDo: postprocess the output from gcc
 
 -----------------------------------------------------------------------------
--- Mangle phase
-
-runPhase Mangle _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
-   = do let dflags = hsc_dflags hsc_env
-        let mangler_opts = getOpts dflags opt_m
-
-#if i386_TARGET_ARCH
-        machdep_opts <- return [ show (stolen_x86_regs dflags) ]
-#else
-       machdep_opts <- return []
-#endif
-
-       let split = dopt Opt_SplitObjs dflags
-            next_phase
-               | split = SplitMangle
-               | otherwise = As
-       output_fn <- liftIO $ get_output_fn dflags next_phase maybe_loc
-
-       liftIO $ SysTools.runMangle dflags (map SysTools.Option mangler_opts
-                         ++ [ SysTools.FileOption "" input_fn
-                            , SysTools.FileOption "" output_fn
-                            ]
-                         ++ map SysTools.Option machdep_opts)
+-- Splitting phase
 
-       return (next_phase, dflags, maybe_loc, output_fn)
+runPhase SplitMangle input_fn dflags
+  = do  -- tmp_pfx is the prefix used for the split .s files
 
------------------------------------------------------------------------------
--- Splitting phase
+        split_s_prefix <- io $ SysTools.newTempName dflags "split"
+        let n_files_fn = split_s_prefix
 
-runPhase SplitMangle _stop hsc_env _basename _suff input_fn _get_output_fn maybe_loc
-  = liftIO $
-    do  -- tmp_pfx is the prefix used for the split .s files
-       -- We also use it as the file to contain the no. of split .s files (sigh)
-        let dflags = hsc_dflags hsc_env
-       split_s_prefix <- SysTools.newTempName dflags "split"
-       let n_files_fn = split_s_prefix
+        io $ SysTools.runSplit dflags
+                          [ SysTools.FileOption "" input_fn
+                          , SysTools.FileOption "" split_s_prefix
+                          , SysTools.FileOption "" n_files_fn
+                          ]
 
-       SysTools.runSplit dflags
-                         [ SysTools.FileOption "" input_fn
-                         , SysTools.FileOption "" split_s_prefix
-                         , SysTools.FileOption "" n_files_fn
-                         ]
+        -- Save the number of split files for future references
+        s <- io $ readFile n_files_fn
+        let n_files = read s :: Int
+            dflags' = dflags { splitInfo = Just (split_s_prefix, n_files) }
 
-       -- Save the number of split files for future references
-       s <- readFile n_files_fn
-       let n_files = read s :: Int
-           dflags' = dflags { splitInfo = Just (split_s_prefix, n_files) }
+        setDynFlags dflags'
 
-       -- Remember to delete all these files
-       addFilesToClean dflags' [ split_s_prefix ++ "__" ++ show n ++ ".s"
-                               | n <- [1..n_files]]
+        -- Remember to delete all these files
+        io $ addFilesToClean dflags' [ split_s_prefix ++ "__" ++ show n ++ ".s"
+                                     | n <- [1..n_files]]
 
-       return (SplitAs, dflags', maybe_loc, "**splitmangle**")
-         -- we don't use the filename
+        return (SplitAs, "**splitmangle**")
+          -- we don't use the filename
 
 -----------------------------------------------------------------------------
 -- As phase
 
-runPhase As _stop hsc_env _basename _suff input_fn get_output_fn maybe_loc
-  = liftIO $
-    do let dflags = hsc_dflags hsc_env
+runPhase As input_fn dflags
+  = do
         let as_opts =  getOpts dflags opt_a
         let cmdline_include_paths = includePaths dflags
 
-       output_fn <- get_output_fn dflags StopLn maybe_loc
+        next_phase <- maybeMergeStub
+        output_fn <- phaseOutputFilename next_phase
 
-       -- we create directories for the object file, because it
-       -- might be a hierarchical module.
-       createDirectoryHierarchy (takeDirectory output_fn)
+        -- we create directories for the object file, because it
+        -- might be a hierarchical module.
+        io $ createDirectoryHierarchy (takeDirectory output_fn)
+
+        io $ SysTools.runAs dflags
+                       (map SysTools.Option as_opts
+                       ++ [ SysTools.Option ("-I" ++ p) | p <- cmdline_include_paths ]
 
-       let (md_c_flags, _) = machdepCCOpts dflags
-       SysTools.runAs dflags   
-                      (map SysTools.Option as_opts
-                      ++ [ SysTools.Option ("-I" ++ p) | p <- cmdline_include_paths ]
-#ifdef sparc_TARGET_ARCH
         -- 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 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.
-                      ++ [ SysTools.Option "-mcpu=v9" ]
-#endif
-                      ++ [ SysTools.Option "-c"
-                         , SysTools.FileOption "" input_fn
-                         , SysTools.Option "-o"
-                         , SysTools.FileOption "" output_fn
-                         ]
-                      ++ map SysTools.Option md_c_flags)
+        -- (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 [SysTools.Option "-mcpu=v9"]
+                           else [])
 
-       return (StopLn, dflags, maybe_loc, output_fn)
+                       ++ [ SysTools.Option "-c"
+                          , SysTools.FileOption "" input_fn
+                          , SysTools.Option "-o"
+                          , SysTools.FileOption "" output_fn
+                          ])
 
+        return (next_phase, output_fn)
 
-runPhase SplitAs _stop hsc_env _basename _suff _input_fn get_output_fn maybe_loc
-  = liftIO $ do
-        let dflags = hsc_dflags hsc_env
-        output_fn <- get_output_fn dflags StopLn maybe_loc
+
+runPhase SplitAs _input_fn dflags
+  = do
+        -- we'll handle the stub_o file in this phase, so don't MergeStub,
+        -- just jump straight to StopLn afterwards.
+        let next_phase = StopLn
+        output_fn <- phaseOutputFilename next_phase
 
         let base_o = dropExtension output_fn
             osuf = objectSuf dflags
             split_odir  = base_o ++ "_" ++ osuf ++ "_split"
 
-        createDirectoryHierarchy split_odir
+        io $ createDirectoryHierarchy split_odir
 
         -- remove M_split/ *.o, because we're going to archive M_split/ *.o
         -- later and we don't want to pick up any old objects.
-        fs <- getDirectoryContents split_odir
-        mapM_ removeFile $ map (split_odir </>) $ filter (osuf `isSuffixOf`) fs
+        fs <- io $ getDirectoryContents split_odir
+        io $ mapM_ removeFile $
+                map (split_odir </>) $ filter (osuf `isSuffixOf`) fs
 
         let as_opts = getOpts dflags opt_a
 
@@ -1163,14 +1219,15 @@ runPhase SplitAs _stop hsc_env _basename _suff _input_fn get_output_fn maybe_loc
                                   Just x -> x
 
         let split_s   n = split_s_prefix ++ "__" ++ show n <.> "s"
+
+            split_obj :: Int -> FilePath
             split_obj n = split_odir </>
                           takeFileName base_o ++ "__" ++ show n <.> osuf
 
-        let (md_c_flags, _) = machdepCCOpts dflags
         let assemble_file n
               = SysTools.runAs dflags
                          (map SysTools.Option as_opts ++
-#ifdef sparc_TARGET_ARCH
+
         -- 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
@@ -1178,48 +1235,140 @@ runPhase SplitAs _stop hsc_env _basename _suff _input_fn get_output_fn maybe_loc
         -- regardless of the ordering.
         --
         -- This is a temporary hack.
-                          [ SysTools.Option "-mcpu=v9" ] ++
-#endif
+                          (if platformArch (targetPlatform dflags) == ArchSPARC
+                           then [SysTools.Option "-mcpu=v9"]
+                           else []) ++
+
                           [ SysTools.Option "-c"
                           , SysTools.Option "-o"
                           , SysTools.FileOption "" (split_obj n)
                           , SysTools.FileOption "" (split_s n)
-                          ]
-                       ++ map SysTools.Option md_c_flags)
+                          ])
+
+        io $ mapM_ assemble_file [1..n]
+
+        -- Note [pipeline-split-init]
+        -- If we have a stub file, it may contain constructor
+        -- functions for initialisation of this module.  We can't
+        -- simply leave the stub as a separate object file, because it
+        -- will never be linked in: nothing refers to it.  We need to
+        -- ensure that if we ever refer to the data in this module
+        -- that needs initialisation, then we also pull in the
+        -- initialisation routine.
+        --
+        -- To that end, we make a DANGEROUS ASSUMPTION here: the data
+        -- that needs to be initialised is all in the FIRST split
+        -- object.  See Note [codegen-split-init].
 
-        mapM_ assemble_file [1..n]
+        PipeState{maybe_stub_o} <- getPipeState
+        case maybe_stub_o of
+            Nothing     -> return ()
+            Just stub_o -> io $ do
+                     tmp_split_1 <- newTempName dflags osuf
+                     let split_1 = split_obj 1
+                     copyFile split_1 tmp_split_1
+                     removeFile split_1
+                     joinObjectFiles dflags [tmp_split_1, stub_o] split_1
 
-        -- and join the split objects into a single object file:
-        let ld_r args = SysTools.runLink dflags ([
-                            SysTools.Option "-nostdlib",
-                            SysTools.Option "-nodefaultlibs",
-                            SysTools.Option "-Wl,-r",
-                            SysTools.Option ld_x_flag,
-                            SysTools.Option "-o",
-                            SysTools.FileOption "" output_fn ]
-                         ++ map SysTools.Option md_c_flags
-                         ++ args)
-            ld_x_flag | null cLD_X = ""
-                      | otherwise  = "-Wl,-x"
-
-        if cLdIsGNULd == "YES"
-            then do
-                  let script = split_odir </> "ld.script"
-                  writeFile script $
-                      "INPUT(" ++ unwords (map split_obj [1..n]) ++ ")"
-                  ld_r [SysTools.FileOption "" script]
-            else do
-                  ld_r (map (SysTools.FileOption "" . split_obj) [1..n])
+        -- join them into a single .o file
+        io $ joinObjectFiles dflags (map split_obj [1..n]) output_fn
+
+        return (next_phase, output_fn)
+
+-----------------------------------------------------------------------------
+-- LlvmOpt phase
+
+runPhase LlvmOpt input_fn dflags
+  = do
+    let lo_opts = getOpts dflags opt_lo
+    let opt_lvl = max 0 (min 2 $ optLevel 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.
+    let optFlag = if null lo_opts
+                     then [SysTools.Option (llvmOpts !! opt_lvl)]
+                     else []
+
+    output_fn <- phaseOutputFilename LlvmLlc
+
+    io $ SysTools.runLlvmOpt dflags
+               ([ SysTools.FileOption "" input_fn,
+                    SysTools.Option "-o",
+                    SysTools.FileOption "" output_fn]
+                ++ optFlag
+                ++ map SysTools.Option lo_opts)
+
+    return (LlvmLlc, output_fn)
+  where 
+        -- we always (unless -optlo specified) run Opt since we rely on it to
+        -- fix up some pretty big deficiencies in the code we generate
+        llvmOpts = ["-mem2reg", "-O1", "-O2"]
+
+-----------------------------------------------------------------------------
+-- LlvmLlc phase
+
+runPhase LlvmLlc input_fn dflags
+  = do
+    let lc_opts = getOpts dflags opt_lc
+        opt_lvl = max 0 (min 2 $ optLevel dflags)
+        rmodel | opt_PIC        = "pic"
+               | not opt_Static = "dynamic-no-pic"
+               | otherwise      = "static"
+
+    output_fn <- phaseOutputFilename LlvmMangle
+
+    io $ SysTools.runLlvmLlc dflags
+                ([ SysTools.Option (llvmOpts !! opt_lvl),
+                    SysTools.Option $ "-relocation-model=" ++ rmodel,
+                    SysTools.FileOption "" input_fn,
+                    SysTools.Option "-o", SysTools.FileOption "" output_fn]
+                ++ map SysTools.Option lc_opts)
+
+    return (LlvmMangle, output_fn)
+  where
+        -- Bug in LLVM at O3 on OSX.
+        llvmOpts = if platformOS (targetPlatform dflags) == OSDarwin
+                   then ["-O1", "-O2", "-O2"]
+                   else ["-O1", "-O2", "-O3"]
+
+-----------------------------------------------------------------------------
+-- LlvmMangle phase
+
+runPhase LlvmMangle input_fn _dflags
+  = do
+      output_fn <- phaseOutputFilename As
+      io $ llvmFixupAsm input_fn output_fn
+      return (As, output_fn)
+
+-----------------------------------------------------------------------------
+-- merge in stub objects
 
-        return (StopLn, dflags, maybe_loc, output_fn)
+runPhase MergeStub input_fn dflags
+ = do
+     PipeState{maybe_stub_o} <- getPipeState
+     output_fn <- phaseOutputFilename StopLn
+     case maybe_stub_o of
+       Nothing ->
+         panic "runPhase(MergeStub): no stub"
+       Just stub_o -> do
+         io $ joinObjectFiles dflags [input_fn, stub_o] output_fn
+         return (StopLn, output_fn)
 
 -- warning suppression
-runPhase other _stop _dflags _basename _suff _input_fn _get_output_fn _maybe_loc =
+runPhase other _input_fn _dflags =
    panic ("runPhase: don't know how to run phase " ++ show other)
+
+maybeMergeStub :: CompPipeline Phase
+maybeMergeStub
+ = do
+     PipeState{maybe_stub_o} <- getPipeState
+     if isJust maybe_stub_o then return MergeStub else return StopLn
+
 -----------------------------------------------------------------------------
 -- MoveBinary sort-of-phase
 -- After having produced a binary, move it somewhere else and generate a
--- wrapper script calling the binary. Currently, we need this only in 
+-- wrapper script calling the binary. Currently, we need this only in
 -- a parallel way (i.e. in GUM), because PVM expects the binary in a
 -- central directory.
 -- This is called from linkBinary below, after linking. I haven't made it
@@ -1227,10 +1376,10 @@ runPhase other _stop _dflags _basename _suff _input_fn _get_output_fn _maybe_loc
 -- we don't need the generality of a phase (MoveBinary is always
 -- done after linking and makes only sense in a parallel setup)   -- HWL
 
-runPhase_MoveBinary :: DynFlags -> FilePath -> [PackageId] -> IO Bool
-runPhase_MoveBinary dflags input_fn dep_packages
+runPhase_MoveBinary :: DynFlags -> FilePath -> IO Bool
+runPhase_MoveBinary dflags input_fn
     | WayPar `elem` (wayNames dflags) && not opt_Static =
-       panic ("Don't know how to combine PVM wrapper and dynamic wrapper")
+        panic ("Don't know how to combine PVM wrapper and dynamic wrapper")
     | WayPar `elem` (wayNames dflags) = do
         let sysMan = pgm_sysman dflags
         pvm_root <- getEnv "PVM_ROOT"
@@ -1244,47 +1393,92 @@ runPhase_MoveBinary dflags input_fn dep_packages
         copy dflags "copying PVM executable" input_fn pvm_executable
         -- generate a wrapper script for running a parallel prg under PVM
         writeFile input_fn (mk_pvm_wrapper_script pvm_executable pvm_executable_base sysMan)
-       return True
-    | not opt_Static =
-       case (dynLibLoader dflags) of
-         Wrapped wrapmode ->
-             do
-               let (o_base, o_ext) = splitExtension input_fn
-               let wrapped_executable | o_ext == "exe" = (o_base ++ ".dyn") <.> o_ext
-                                      | otherwise = input_fn ++ ".dyn"
-               behaviour <- wrapper_behaviour dflags wrapmode dep_packages
-
-                -- THINKME isn't this possible to do a bit nicer?
-               let behaviour' = concatMap (\x -> if x=='\\' then "\\\\" else [x]) behaviour
-               renameFile input_fn wrapped_executable
-               let rtsDetails = (getPackageDetails (pkgState dflags) rtsPackageId);
-               SysTools.runCc dflags
-                  ([ SysTools.FileOption "" ((head (libraryDirs rtsDetails)) ++ "/dyn-wrapper.c")
-                  , SysTools.Option ("-DBEHAVIOUR=\"" ++ behaviour' ++ "\"")
-                  , SysTools.Option "-o"
-                  , SysTools.FileOption "" input_fn
-                  ] ++ map (SysTools.FileOption "-I") (includeDirs rtsDetails))
-               return True
-         _ -> return True
+        return True
     | otherwise = return True
 
-wrapper_behaviour :: DynFlags -> Maybe [Char] -> [PackageId] -> IO [Char]
-wrapper_behaviour dflags mode dep_packages =
-    let seperateBySemiColon strs = tail $ concatMap (';':) strs
-    in case mode of
-      Nothing -> do
-               pkg_lib_paths <- getPackageLibraryPath dflags dep_packages
-               return ('H' : (seperateBySemiColon pkg_lib_paths))
-      Just s -> do
-       allpkg <- getPreloadPackagesAnd dflags dep_packages
-       putStrLn (unwords (map (packageIdString . packageConfigId) allpkg))
-       return $ 'F':s ++ ';':(seperateBySemiColon (map (packageIdString . packageConfigId) allpkg))
+mkExtraCObj :: DynFlags -> String -> IO FilePath
+mkExtraCObj dflags xs
+ = do cFile <- newTempName dflags "c"
+      oFile <- newTempName dflags "o"
+      writeFile cFile xs
+      let rtsDetails = getPackageDetails (pkgState dflags) rtsPackageId
+      SysTools.runCc dflags
+                     ([Option        "-c",
+                       FileOption "" cFile,
+                       Option        "-o",
+                       FileOption "" oFile] ++
+                      map (FileOption "-I") (includeDirs rtsDetails))
+      return oFile
+
+mkExtraObjToLinkIntoBinary :: DynFlags -> [PackageId] -> IO FilePath
+mkExtraObjToLinkIntoBinary dflags dep_packages = do
+   link_info <- getLinkInfo dflags dep_packages
+   mkExtraCObj dflags (showSDoc (vcat [rts_opts_enabled,
+                                       extra_rts_opts,
+                                       link_opts link_info]
+                                   <> char '\n')) -- final newline, to
+                                                  -- keep gcc happy
+
+  where
+    mk_rts_opts_enabled val
+         = vcat [text "#include \"Rts.h\"",
+                 text "#include \"RtsOpts.h\"",
+                 text "const RtsOptsEnabledEnum rtsOptsEnabled = " <>
+                       text val <> semi ]
+
+    rts_opts_enabled = case rtsOptsEnabled dflags of
+          RtsOptsNone     -> mk_rts_opts_enabled "RtsOptsNone"
+          RtsOptsSafeOnly -> empty -- The default
+          RtsOptsAll      -> mk_rts_opts_enabled "RtsOptsAll"
+
+    extra_rts_opts = case rtsOpts dflags of
+          Nothing   -> empty
+          Just opts -> text "char *ghc_rts_opts = " <> text (show opts) <> semi
+
+    link_opts info
+      | isDarwinTarget  = empty
+      | isWindowsTarget = empty
+      | otherwise = hcat [
+          text "__asm__(\"\\t.section ", text ghcLinkInfoSectionName,
+                                    text ",\\\"\\\",@note\\n",
+                    text "\\t.ascii \\\"", info', text "\\\"\\n\");" ]
+          where
+            -- we need to escape twice: once because we're inside a C string,
+            -- and again because we're inside an asm string.
+            info' = text $ (escape.escape) info
+
+            escape :: String -> String
+            escape = concatMap (charToC.fromIntegral.ord)
+
+-- The "link info" is a string representing the parameters of the
+-- link.  We save this information in the binary, and the next time we
+-- link, if nothing else has changed, we use the link info stored in
+-- the existing binary to decide whether to re-link or not.
+getLinkInfo :: DynFlags -> [PackageId] -> IO String
+getLinkInfo dflags dep_packages = do
+   package_link_opts <- getPackageLinkOpts dflags dep_packages
+#ifdef darwin_TARGET_OS
+   pkg_frameworks <- getPackageFrameworks dflags dep_packages
+#endif
+   extra_ld_inputs <- readIORef v_Ld_inputs
+   let
+      link_info = (package_link_opts,
+#ifdef darwin_TARGET_OS
+                   pkg_frameworks,
+#endif
+                   rtsOpts dflags,
+                   rtsOptsEnabled dflags,
+                   dopt Opt_NoHsMain dflags,
+                   extra_ld_inputs,
+                   getOpts dflags opt_l)
+   --
+   return (show link_info)
 
 -- generates a Perl skript starting a parallel prg under PVM
 mk_pvm_wrapper_script :: String -> String -> String -> String
 mk_pvm_wrapper_script pvm_executable pvm_executable_base sysMan = unlines $
  [
-  "eval 'exec perl -S $0 ${1+\"$@\"}'", 
+  "eval 'exec perl -S $0 ${1+\"$@\"}'",
   "  if $running_under_some_shell;",
   "# =!=!=!=!=!=!=!=!=!=!=!",
   "# This script is automatically generated: DO NOT EDIT!!!",
@@ -1346,9 +1540,9 @@ getHCFilePackages filename =
     l <- hGetLine h
     case l of
       '/':'*':' ':'G':'H':'C':'_':'P':'A':'C':'K':'A':'G':'E':'S':rest ->
-         return (map stringToPackageId (words rest))
+          return (map stringToPackageId (words rest))
       _other ->
-         return []
+          return []
 
 -----------------------------------------------------------------------------
 -- Static linking, of .o files
@@ -1356,7 +1550,7 @@ getHCFilePackages filename =
 -- The list of packages passed to link is the list of packages on
 -- which this program depends, as discovered by the compilation
 -- manager.  It is combined with the list of packages that the user
--- specifies on the command line with -package flags.  
+-- specifies on the command line with -package flags.
 --
 -- In one-shot linking mode, we can't discover the package
 -- dependencies (because we haven't actually done any compilation or
@@ -1365,7 +1559,7 @@ getHCFilePackages filename =
 
 linkBinary :: DynFlags -> [FilePath] -> [PackageId] -> IO ()
 linkBinary dflags o_files dep_packages = do
-    let verb = getVerbFlag dflags
+    let verbFlags = getVerbFlags dflags
         output_fn = exeFileName dflags
 
     -- get the full list of packages to link with, by combining the
@@ -1374,7 +1568,7 @@ linkBinary dflags o_files dep_packages = do
 
     pkg_lib_paths <- getPackageLibraryPath dflags dep_packages
     let pkg_lib_path_opts = concat (map get_pkg_lib_path_opts pkg_lib_paths)
-#ifdef linux_TARGET_OS
+#ifdef elf_OBJ_FORMAT
         get_pkg_lib_path_opts l | (dynLibLoader dflags)==SystemDependent && not opt_Static = ["-L" ++ l, "-Wl,-rpath", "-Wl," ++ l]
                                 | otherwise = ["-L" ++ l]
 #else
@@ -1391,6 +1585,8 @@ linkBinary dflags o_files dep_packages = do
     let main_lib | no_hs_main = []
                  | otherwise  = [ "-lHSrtsmain" ]
 
+    extraLinkObj <- mkExtraObjToLinkIntoBinary dflags dep_packages
+
     pkg_link_opts <- getPackageLinkOpts dflags dep_packages
 
 #ifdef darwin_TARGET_OS
@@ -1402,15 +1598,15 @@ linkBinary dflags o_files dep_packages = do
 
     pkg_frameworks <- getPackageFrameworks dflags dep_packages
     let pkg_framework_opts = concat [ ["-framework", fw] | fw <- pkg_frameworks ]
-    
+
     let frameworks = cmdlineFrameworks dflags
         framework_opts = concat [ ["-framework", fw] | fw <- reverse frameworks ]
-        -- reverse because they're added in reverse order from the cmd line
+         -- reverse because they're added in reverse order from the cmd line
 #endif
-       -- probably _stub.o files
+        -- probably _stub.o files
     extra_ld_inputs <- readIORef v_Ld_inputs
 
-       -- opts from -optl-<blah> (including -l<blah> options)
+        -- opts from -optl-<blah> (including -l<blah> options)
     let extra_ld_opts = getOpts dflags opt_l
 
     let ways = wayNames dflags
@@ -1419,62 +1615,63 @@ linkBinary dflags o_files dep_packages = do
     -- the command line, because they contain symbols that are referred to
     -- by the RTS.  We can't therefore use the ordinary way opts for these.
     let
-       debug_opts | WayDebug `elem` ways = [ 
+        debug_opts | WayDebug `elem` ways = [
 #if defined(HAVE_LIBBFD)
-                       "-lbfd", "-liberty"
+                        "-lbfd", "-liberty"
 #endif
-                        ]
-                  | otherwise            = []
+                         ]
+                   | otherwise            = []
 
     let
-       thread_opts | WayThreaded `elem` ways = [ 
-#if !defined(mingw32_TARGET_OS) && !defined(freebsd_TARGET_OS) && !defined(haiku_TARGET_OS)
-                       "-lpthread"
+        thread_opts | WayThreaded `elem` ways = [
+#if !defined(mingw32_TARGET_OS) && !defined(freebsd_TARGET_OS) && !defined(openbsd_TARGET_OS) && !defined(haiku_TARGET_OS)
+                        "-lpthread"
 #endif
 #if defined(osf3_TARGET_OS)
-                       , "-lexc"
+                        , "-lexc"
 #endif
-                       ]
-                   | otherwise               = []
+                        ]
+                    | otherwise               = []
 
     rc_objs <- maybeCreateManifest dflags output_fn
 
-    let (md_c_flags, _) = machdepCCOpts dflags
-    SysTools.runLink dflags ( 
-                      [ SysTools.Option verb
-                      , SysTools.Option "-o"
-                      , SysTools.FileOption "" output_fn
-                      ]
-                     ++ map SysTools.Option (
-                        md_c_flags
+    SysTools.runLink dflags (
+                       map SysTools.Option verbFlags
+                      ++ [ SysTools.Option "-o"
+                         , SysTools.FileOption "" output_fn
+                         ]
+                      ++ map SysTools.Option (
+                         []
 
-#ifdef mingw32_TARGET_OS
                       -- Permit the linker to auto link _symbol to _imp_symbol.
-                     -- This lets us link against DLLs without needing an "import library".
-                     ++ ["-Wl,--enable-auto-import"]
-#endif
-                     ++ o_files
-                     ++ extra_ld_inputs
-                     ++ lib_path_opts
-                     ++ extra_ld_opts
+                      -- This lets us link against DLLs without needing an "import library".
+                      ++ (if platformOS (targetPlatform dflags) == OSMinGW32
+                          then ["-Wl,--enable-auto-import"]
+                          else [])
+
+                      ++ o_files
+                      ++ extra_ld_inputs
+                      ++ lib_path_opts
+                      ++ extra_ld_opts
                       ++ rc_objs
 #ifdef darwin_TARGET_OS
-                     ++ framework_path_opts
-                     ++ framework_opts
+                      ++ framework_path_opts
+                      ++ framework_opts
 #endif
-                     ++ pkg_lib_path_opts
+                      ++ pkg_lib_path_opts
                       ++ main_lib
-                     ++ pkg_link_opts
+                      ++ [extraLinkObj]
+                      ++ pkg_link_opts
 #ifdef darwin_TARGET_OS
-                     ++ pkg_framework_path_opts
-                     ++ pkg_framework_opts
+                      ++ pkg_framework_path_opts
+                      ++ pkg_framework_opts
 #endif
-                     ++ debug_opts
-                     ++ thread_opts
-                   ))
+                      ++ debug_opts
+                      ++ thread_opts
+                    ))
 
     -- parallel only: move binary to another dir -- HWL
-    success <- runPhase_MoveBinary dflags output_fn dep_packages
+    success <- runPhase_MoveBinary dflags output_fn
     if success then return ()
                else ghcError (InstallationError ("cannot move binary"))
 
@@ -1482,19 +1679,15 @@ linkBinary dflags o_files dep_packages = do
 exeFileName :: DynFlags -> FilePath
 exeFileName dflags
   | Just s <- outputFile dflags =
-#if defined(mingw32_HOST_OS)
-      if null (takeExtension s)
-        then s <.> "exe"
-        else s
-#else
-      s
-#endif
-  | otherwise = 
-#if defined(mingw32_HOST_OS)
-       "main.exe"
-#else
-       "a.out"
-#endif
+      if platformOS (targetPlatform dflags) == OSMinGW32
+      then if null (takeExtension s)
+           then s <.> "exe"
+           else s
+      else s
+  | otherwise =
+      if platformOS (targetPlatform dflags) == OSMinGW32
+      then "main.exe"
+      else "a.out"
 
 maybeCreateManifest
    :: DynFlags
@@ -1509,7 +1702,7 @@ maybeCreateManifest dflags exe_filename = do
 
   let manifest_filename = exe_filename <.> "manifest"
 
-  writeFile manifest_filename $ 
+  writeFile manifest_filename $
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"++
       "  <assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n"++
       "  <assemblyIdentity version=\"1.0.0.0\"\n"++
@@ -1541,9 +1734,9 @@ maybeCreateManifest dflags exe_filename = do
 
   let wr_opts = getOpts dflags opt_windres
   runWindres dflags $ map SysTools.Option $
-        ["--input="++rc_filename, 
+        ["--input="++rc_filename,
          "--output="++rc_obj_filename,
-         "--output-format=coff"] 
+         "--output-format=coff"]
         ++ wr_opts
         -- no FileOptions here: windres doesn't like seeing
         -- backslashes, apparently
@@ -1556,24 +1749,14 @@ maybeCreateManifest dflags exe_filename = do
 
 linkDynLib :: DynFlags -> [String] -> [PackageId] -> IO ()
 linkDynLib dflags o_files dep_packages = do
-    let verb = getVerbFlag dflags
+    let verbFlags = getVerbFlags dflags
     let o_file = outputFile dflags
 
-    -- We don't want to link our dynamic libs against the RTS package,
-    -- because the RTS lib comes in several flavours and we want to be
-    -- able to pick the flavour when a binary is linked.
     pkgs <- getPreloadPackagesAnd dflags dep_packages
 
-    -- On Windows we need to link the RTS import lib as Windows does
-    -- not allow undefined symbols.
-#if !defined(mingw32_HOST_OS)
-    let pkgs_no_rts = filter ((/= rtsPackageId) . packageConfigId) pkgs
-#else
-    let pkgs_no_rts = pkgs
-#endif
-    let pkg_lib_paths = collectLibraryPaths pkgs_no_rts
+    let pkg_lib_paths = collectLibraryPaths pkgs
     let pkg_lib_path_opts = concatMap get_pkg_lib_path_opts pkg_lib_paths
-#ifdef linux_TARGET_OS
+#ifdef elf_OBJ_FORMAT
         get_pkg_lib_path_opts l | (dynLibLoader dflags)==SystemDependent && not opt_Static = ["-L" ++ l, "-Wl,-rpath", "-Wl," ++ l]
                                 | otherwise = ["-L" ++ l]
 #else
@@ -1583,42 +1766,56 @@ linkDynLib dflags o_files dep_packages = do
     let lib_paths = libraryPaths dflags
     let lib_path_opts = map ("-L"++) lib_paths
 
+    -- We don't want to link our dynamic libs against the RTS package,
+    -- because the RTS lib comes in several flavours and we want to be
+    -- able to pick the flavour when a binary is linked.
+    -- On Windows we need to link the RTS import lib as Windows does
+    -- not allow undefined symbols.
+    -- The RTS library path is still added to the library search path
+    -- above in case the RTS is being explicitly linked in (see #3807).
+#if !defined(mingw32_HOST_OS)
+    let pkgs_no_rts = filter ((/= rtsPackageId) . packageConfigId) pkgs
+#else
+    let pkgs_no_rts = pkgs
+#endif
     let pkg_link_opts = collectLinkOpts dflags pkgs_no_rts
 
-       -- probably _stub.o files
+        -- probably _stub.o files
     extra_ld_inputs <- readIORef v_Ld_inputs
 
-    let (md_c_flags, _) = machdepCCOpts dflags
     let extra_ld_opts = getOpts dflags opt_l
+
+    extraLinkObj <- mkExtraObjToLinkIntoBinary dflags dep_packages
+
 #if defined(mingw32_HOST_OS)
     -----------------------------------------------------------------------------
     -- Making a DLL
     -----------------------------------------------------------------------------
     let output_fn = case o_file of { Just s -> s; Nothing -> "HSdll.dll"; }
 
-    SysTools.runLink dflags
-        ([ SysTools.Option verb
-         , SysTools.Option "-o"
-         , SysTools.FileOption "" output_fn
-         , SysTools.Option "-shared"
-         ] ++
-         [ SysTools.FileOption "-Wl,--out-implib=" (output_fn ++ ".a")
-         | dopt Opt_SharedImplib dflags
-         ]
-        ++ map (SysTools.FileOption "") o_files
-        ++ map SysTools.Option (
-           md_c_flags
-           
+    SysTools.runLink dflags (
+            map SysTools.Option verbFlags
+         ++ [ SysTools.Option "-o"
+            , SysTools.FileOption "" output_fn
+            , SysTools.Option "-shared"
+            ] ++
+            [ SysTools.FileOption "-Wl,--out-implib=" (output_fn ++ ".a")
+            | dopt Opt_SharedImplib dflags
+            ]
+         ++ map (SysTools.FileOption "") o_files
+         ++ map SysTools.Option (
+
          -- Permit the linker to auto link _symbol to _imp_symbol
-        -- This lets us link against DLLs without needing an "import library"
-        ++ ["-Wl,--enable-auto-import"]
-
-        ++ extra_ld_inputs
-        ++ lib_path_opts
-        ++ extra_ld_opts
-        ++ pkg_lib_path_opts
-        ++ pkg_link_opts
-       ))
+         -- This lets us link against DLLs without needing an "import library"
+            ["-Wl,--enable-auto-import"]
+
+         ++ extra_ld_inputs
+         ++ lib_path_opts
+         ++ extra_ld_opts
+         ++ pkg_lib_path_opts
+         ++ [extraLinkObj]
+         ++ pkg_link_opts
+        ))
 #elif defined(darwin_TARGET_OS)
     -----------------------------------------------------------------------------
     -- Making a darwin dylib
@@ -1629,19 +1826,15 @@ linkDynLib dflags o_files dep_packages = do
     -- -undefined dynamic_lookup:
     --   Without these options, we'd have to specify the correct dependencies
     --   for each of the dylibs. Note that we could (and should) do without this
-    --  for all libraries except the RTS; all we need to do is to pass the
-    --  correct HSfoo_dyn.dylib files to the link command.
-    --  This feature requires Mac OS X 10.3 or later; there is a similar feature,
-    --  -flat_namespace -undefined suppress, which works on earlier versions,
-    --  but it has other disadvantages.
+    --   for all libraries except the RTS; all we need to do is to pass the
+    --   correct HSfoo_dyn.dylib files to the link command.
+    --   This feature requires Mac OS X 10.3 or later; there is a similar feature,
+    --   -flat_namespace -undefined suppress, which works on earlier versions,
+    --   but it has other disadvantages.
     -- -single_module
-    --  Build the dynamic library as a single "module", i.e. no dynamic binding
-    --  nonsense when referring to symbols from within the library. The NCG
-    --  assumes that this option is specified (on i386, at least).
-    -- -Wl,-macosx_version_min -Wl,10.3
-    --  Tell the linker its safe to assume that the library will run on 10.3 or
-    --  later, so that it will not complain about the use of the option
-    --  -undefined dynamic_lookup above.
+    --   Build the dynamic library as a single "module", i.e. no dynamic binding
+    --   nonsense when referring to symbols from within the library. The NCG
+    --   assumes that this option is specified (on i386, at least).
     -- -install_name
     --   Mac OS/X stores the path where a dynamic library is (to be) installed
     --   in the library itself.  It's called the "install name" of the library.
@@ -1659,46 +1852,60 @@ linkDynLib dflags o_files dep_packages = do
         Nothing -> do
             pwd <- getCurrentDirectory
             return $ pwd `combine` output_fn
-    SysTools.runLink dflags
-        ([ SysTools.Option verb
-         , SysTools.Option "-dynamiclib"
-         , SysTools.Option "-o"
-         , SysTools.FileOption "" output_fn
-         ]
-        ++ map SysTools.Option (
-           md_c_flags
-        ++ o_files
-        ++ [ "-undefined", "dynamic_lookup", "-single_module", "-Wl,-macosx_version_min","-Wl,10.5",
-              "-Wl,-read_only_relocs,suppress", "-install_name", instName ]
-        ++ extra_ld_inputs
-        ++ lib_path_opts
-        ++ extra_ld_opts
-        ++ pkg_lib_path_opts
-        ++ pkg_link_opts
-       ))
+    SysTools.runLink dflags (
+            map SysTools.Option verbFlags
+         ++ [ SysTools.Option "-dynamiclib"
+            , SysTools.Option "-o"
+            , SysTools.FileOption "" output_fn
+            ]
+         ++ map SysTools.Option (
+            o_files
+         ++ [ "-undefined", "dynamic_lookup", "-single_module",
+#if !defined(x86_64_TARGET_ARCH)
+              "-Wl,-read_only_relocs,suppress",
+#endif
+              "-install_name", instName ]
+         ++ extra_ld_inputs
+         ++ lib_path_opts
+         ++ extra_ld_opts
+         ++ pkg_lib_path_opts
+         ++ [extraLinkObj]
+         ++ pkg_link_opts
+        ))
 #else
     -----------------------------------------------------------------------------
     -- Making a DSO
     -----------------------------------------------------------------------------
 
     let output_fn = case o_file of { Just s -> s; Nothing -> "a.out"; }
-
-    SysTools.runLink dflags
-        ([ SysTools.Option verb
-         , SysTools.Option "-o"
-         , SysTools.FileOption "" output_fn
-         ]
-        ++ map SysTools.Option (
-           md_c_flags
-        ++ o_files
-        ++ [ "-shared", "-Wl,-Bsymbolic" ] -- we need symbolic linking to resolve non-PIC intra-package-relocations
-         ++ [ "-Wl,-soname," ++ takeFileName output_fn ] -- set the library soname
-        ++ extra_ld_inputs
-        ++ lib_path_opts
-        ++ extra_ld_opts
-        ++ pkg_lib_path_opts
-        ++ pkg_link_opts
-       ))
+    let buildingRts = thisPackage dflags == rtsPackageId
+    let bsymbolicFlag = if buildingRts
+                        then -- -Bsymbolic breaks the way we implement
+                             -- hooks in the RTS
+                             []
+                        else -- we need symbolic linking to resolve
+                             -- non-PIC intra-package-relocations
+                             ["-Wl,-Bsymbolic"]
+
+    SysTools.runLink dflags (
+            map SysTools.Option verbFlags
+         ++ [ SysTools.Option "-o"
+            , SysTools.FileOption "" output_fn
+            ]
+         ++ map SysTools.Option (
+            o_files
+         ++ [ "-shared" ]
+         ++ bsymbolicFlag
+            -- Set the library soname. We use -h rather than -soname as
+            -- Solaris 10 doesn't support the latter:
+         ++ [ "-Wl,-h," ++ takeFileName output_fn ]
+         ++ extra_ld_inputs
+         ++ lib_path_opts
+         ++ extra_ld_opts
+         ++ pkg_lib_path_opts
+         ++ [extraLinkObj]
+         ++ pkg_link_opts
+        ))
 #endif
 -- -----------------------------------------------------------------------------
 -- Running CPP
@@ -1710,89 +1917,94 @@ doCpp dflags raw include_cc_opts input_fn output_fn = do
 
     pkg_include_dirs <- getPackageIncludePath dflags []
     let include_paths = foldr (\ x xs -> "-I" : x : xs) []
-                         (cmdline_include_paths ++ pkg_include_dirs)
+                          (cmdline_include_paths ++ pkg_include_dirs)
 
-    let verb = getVerbFlag dflags
+    let verbFlags = getVerbFlags dflags
 
     let cc_opts
-         | not include_cc_opts = []
-         | otherwise           = (optc ++ md_c_flags)
-               where 
-                     optc = getOpts dflags opt_c
-                     (md_c_flags, _) = machdepCCOpts dflags
+          | include_cc_opts = getOpts dflags opt_c
+          | otherwise       = []
 
     let cpp_prog args | raw       = SysTools.runCpp dflags args
-                     | otherwise = SysTools.runCc dflags (SysTools.Option "-E" : args)
-
-    let target_defs = 
-         [ "-D" ++ HOST_OS     ++ "_BUILD_OS=1",
-           "-D" ++ HOST_ARCH   ++ "_BUILD_ARCH=1",
-           "-D" ++ TARGET_OS   ++ "_HOST_OS=1",
-           "-D" ++ TARGET_ARCH ++ "_HOST_ARCH=1" ]
-       -- remember, in code we *compile*, the HOST is the same our TARGET,
-       -- and BUILD is the same as our HOST.
-
-    cpp_prog       ([SysTools.Option verb]
-                   ++ map SysTools.Option include_paths
-                   ++ map SysTools.Option hsSourceCppOpts
-                   ++ map SysTools.Option hscpp_opts
-                   ++ map SysTools.Option cc_opts
-                   ++ map SysTools.Option target_defs
-                   ++ [ SysTools.Option     "-x"
-                      , SysTools.Option     "c"
-                      , 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.
-                      , SysTools.Option     "-o"
-                      , SysTools.FileOption "" output_fn
-                      ])
-
-cHaskell1Version :: String
-cHaskell1Version = "5" -- i.e., Haskell 98
+                      | otherwise = SysTools.runCc dflags (SysTools.Option "-E" : args)
+
+    let target_defs =
+          [ "-D" ++ HOST_OS     ++ "_BUILD_OS=1",
+            "-D" ++ HOST_ARCH   ++ "_BUILD_ARCH=1",
+            "-D" ++ TARGET_OS   ++ "_HOST_OS=1",
+            "-D" ++ TARGET_ARCH ++ "_HOST_ARCH=1" ]
+        -- remember, in code we *compile*, the HOST is the same our TARGET,
+        -- and BUILD is the same as our HOST.
+
+    cpp_prog       (   map SysTools.Option verbFlags
+                    ++ map SysTools.Option include_paths
+                    ++ map SysTools.Option hsSourceCppOpts
+                    ++ map SysTools.Option target_defs
+                    ++ map SysTools.Option hscpp_opts
+                    ++ map SysTools.Option cc_opts
+                    ++ [ SysTools.Option     "-x"
+                       , SysTools.Option     "c"
+                       , 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.
+                       , SysTools.Option     "-o"
+                       , SysTools.FileOption "" output_fn
+                       ])
 
 hsSourceCppOpts :: [String]
 -- Default CPP defines in Haskell source
 hsSourceCppOpts =
-       [ "-D__HASKELL1__="++cHaskell1Version
-       , "-D__GLASGOW_HASKELL__="++cProjectVersionInt                          
-       , "-D__HASKELL98__"
-       , "-D__CONCURRENT_HASKELL__"
-       ]
+        [ "-D__GLASGOW_HASKELL__="++cProjectVersionInt ]
+
+-- ---------------------------------------------------------------------------
+-- join object files into a single relocatable object file, using ld -r
+
+joinObjectFiles :: DynFlags -> [FilePath] -> FilePath -> IO ()
+joinObjectFiles dflags o_files output_fn = do
+  let ld_r args = SysTools.runLink dflags ([
+                            SysTools.Option "-nostdlib",
+                            SysTools.Option "-nodefaultlibs",
+                            SysTools.Option "-Wl,-r",
+                            SysTools.Option ld_build_id,
+                            SysTools.Option ld_x_flag,
+                            SysTools.Option "-o",
+                            SysTools.FileOption "" output_fn ]
+                         ++ args)
 
+      ld_x_flag | null cLD_X = ""
+                | otherwise  = "-Wl,-x"
+
+      -- 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 | cLdHasBuildId == "YES"  = "-Wl,--build-id=none"
+                  | otherwise               = ""
+
+  if cLdIsGNULd == "YES"
+     then do
+          script <- newTempName dflags "ldscript"
+          writeFile script $ "INPUT(" ++ unwords o_files ++ ")"
+          ld_r [SysTools.FileOption "" script]
+     else do
+          ld_r (map (SysTools.FileOption "") o_files)
 
 -- -----------------------------------------------------------------------------
 -- Misc.
 
 hscNextPhase :: DynFlags -> HscSource -> HscTarget -> Phase
 hscNextPhase _ HsBootFile _        =  StopLn
-hscNextPhase dflags _ hsc_lang = 
+hscNextPhase dflags _ hsc_lang =
   case hsc_lang of
-       HscC -> HCc
-       HscAsm | dopt Opt_SplitObjs dflags -> SplitMangle
-              | otherwise -> As
-       HscNothing     -> StopLn
-       HscInterpreted -> StopLn
-       _other         -> StopLn
-
-
-hscMaybeAdjustTarget :: DynFlags -> Phase -> HscSource -> HscTarget -> HscTarget
-hscMaybeAdjustTarget dflags stop _ current_hsc_lang 
-  = hsc_lang 
-  where
-       keep_hc = dopt Opt_KeepHcFiles dflags
-       hsc_lang
-               -- don't change the lang if we're interpreting
-                | current_hsc_lang == HscInterpreted = current_hsc_lang
-
-               -- force -fvia-C if we are being asked for a .hc file
-                | HCc <- stop = HscC
-                | keep_hc     = HscC
-               -- otherwise, stick to the plan
-                | otherwise = current_hsc_lang
+        HscC -> HCc
+        HscAsm | dopt Opt_SplitObjs dflags -> SplitMangle
+               | otherwise -> As
+        HscLlvm        -> LlvmOpt
+        HscNothing     -> StopLn
+        HscInterpreted -> StopLn