diff options
-rw-r--r-- | compiler/main/DriverPhases.hs | 59 | ||||
-rw-r--r-- | compiler/main/DriverPipeline.hs | 149 | ||||
-rw-r--r-- | compiler/main/DynFlags.hs | 60 | ||||
-rw-r--r-- | compiler/main/GhcMake.hs | 4 | ||||
-rw-r--r-- | compiler/main/HscMain.hs | 3 | ||||
-rw-r--r-- | compiler/nativeGen/SPARC/Base.hs | 55 | ||||
-rw-r--r-- | compiler/nativeGen/X86/CodeGen.hs | 12 | ||||
-rw-r--r-- | compiler/prelude/PrelRules.lhs | 24 | ||||
-rw-r--r-- | compiler/typecheck/TcForeign.lhs | 18 | ||||
-rw-r--r-- | compiler/typecheck/TcValidity.lhs | 8 | ||||
-rw-r--r-- | compiler/types/FunDeps.lhs | 66 | ||||
-rw-r--r-- | docs/users_guide/flags.xml | 7 | ||||
-rw-r--r-- | docs/users_guide/packages.xml | 4 | ||||
-rw-r--r-- | docs/users_guide/sooner.xml | 2 | ||||
-rw-r--r-- | ghc.mk | 4 | ||||
-rw-r--r-- | ghc/Main.hs | 10 | ||||
m--------- | libraries/bytestring | 0 | ||||
m--------- | libraries/terminfo | 0 | ||||
-rw-r--r-- | mk/config.mk.in | 2 | ||||
-rw-r--r-- | rules/build-package-way.mk | 2 | ||||
-rw-r--r-- | rules/build-package.mk | 2 | ||||
-rw-r--r-- | rules/build-prog.mk | 2 | ||||
-rw-r--r-- | rules/hi-rule.mk | 46 | ||||
-rw-r--r-- | rules/hs-suffix-rules-srcdir.mk | 2 | ||||
-rw-r--r-- | rules/hs-suffix-rules.mk | 6 |
25 files changed, 359 insertions, 188 deletions
diff --git a/compiler/main/DriverPhases.hs b/compiler/main/DriverPhases.hs index a1eac536b6..2de19b9795 100644 --- a/compiler/main/DriverPhases.hs +++ b/compiler/main/DriverPhases.hs @@ -35,6 +35,7 @@ module DriverPhases ( #include "HsVersions.h" +import {-# SOURCE #-} DynFlags import Outputable import Platform import System.FilePath @@ -131,33 +132,39 @@ eqPhase _ _ = False -- Partial ordering on phases: we want to know which phases will occur before -- which others. This is used for sanity checking, to ensure that the -- pipeline will stop at some point (see DriverPipeline.runPipeline). -happensBefore :: Phase -> Phase -> Bool -StopLn `happensBefore` _ = False -x `happensBefore` y = after_x `eqPhase` y || after_x `happensBefore` y - where - after_x = nextPhase x +happensBefore :: DynFlags -> Phase -> Phase -> Bool +happensBefore dflags p1 p2 = p1 `happensBefore'` p2 + where StopLn `happensBefore'` _ = False + x `happensBefore'` y = after_x `eqPhase` y + || after_x `happensBefore'` y + where after_x = nextPhase dflags x -nextPhase :: Phase -> Phase --- A conservative approximation to the next phase, used in happensBefore -nextPhase (Unlit sf) = Cpp sf -nextPhase (Cpp sf) = HsPp sf -nextPhase (HsPp sf) = Hsc sf -nextPhase (Hsc _) = HCc -nextPhase Splitter = SplitAs -nextPhase LlvmOpt = LlvmLlc -nextPhase LlvmLlc = LlvmMangle -nextPhase LlvmMangle = As -nextPhase SplitAs = MergeStub -nextPhase As = MergeStub -nextPhase Ccpp = As -nextPhase Cc = As -nextPhase Cobjc = As -nextPhase Cobjcpp = As -nextPhase CmmCpp = Cmm -nextPhase Cmm = HCc -nextPhase HCc = As -nextPhase MergeStub = StopLn -nextPhase StopLn = panic "nextPhase: nothing after StopLn" +nextPhase :: DynFlags -> Phase -> Phase +nextPhase dflags p + -- A conservative approximation to the next phase, used in happensBefore + = case p of + Unlit sf -> Cpp sf + Cpp sf -> HsPp sf + HsPp sf -> Hsc sf + Hsc _ -> maybeHCc + Splitter -> SplitAs + LlvmOpt -> LlvmLlc + LlvmLlc -> LlvmMangle + LlvmMangle -> As + SplitAs -> MergeStub + As -> MergeStub + Ccpp -> As + Cc -> As + Cobjc -> As + Cobjcpp -> As + CmmCpp -> Cmm + Cmm -> maybeHCc + HCc -> As + MergeStub -> StopLn + StopLn -> panic "nextPhase: nothing after StopLn" + where maybeHCc = if platformUnregisterised (targetPlatform dflags) + then HCc + else As -- the first compilation phase for a given file is determined -- by its suffix. diff --git a/compiler/main/DriverPipeline.hs b/compiler/main/DriverPipeline.hs index 849532de79..b128c1f107 100644 --- a/compiler/main/DriverPipeline.hs +++ b/compiler/main/DriverPipeline.hs @@ -503,70 +503,96 @@ runPipeline -> IO (DynFlags, FilePath) -- ^ (final flags, output filename) runPipeline stop_phase hsc_env0 (input_fn, mb_phase) mb_basename output maybe_loc maybe_stub_o - = do r <- runPipeline' stop_phase hsc_env0 (input_fn, mb_phase) - mb_basename output maybe_loc maybe_stub_o - let dflags = extractDynFlags hsc_env0 - whenCannotGenerateDynamicToo dflags $ do + + = do let + dflags0 = hsc_dflags hsc_env0 + + -- Decide where dump files should go based on the pipeline output + dflags = dflags0 { dumpPrefix = Just (basename ++ ".") } + hsc_env = hsc_env0 {hsc_dflags = dflags} + + (input_basename, suffix) = splitExtension input_fn + suffix' = drop 1 suffix -- strip off the . + basename | Just b <- mb_basename = b + | otherwise = input_basename + + -- If we were given a -x flag, then use that phase to start from + start_phase = fromMaybe (startPhase suffix') mb_phase + + isHaskell (Unlit _) = True + isHaskell (Cpp _) = True + isHaskell (HsPp _) = True + isHaskell (Hsc _) = True + isHaskell _ = False + + isHaskellishFile = isHaskell start_phase + + env = PipeEnv{ pe_isHaskellishFile = isHaskellishFile, + stop_phase, + src_basename = basename, + src_suffix = suffix', + output_spec = output } + + -- We want to catch cases of "you can't get there from here" before + -- we start the pipeline, because otherwise it will just run off the + -- end. + -- + -- There is a partial ordering on phases, where A < B iff A occurs + -- before B in a normal compilation pipeline. + + let happensBefore' = happensBefore dflags + when (not (start_phase `happensBefore'` stop_phase)) $ + throwGhcException (UsageError + ("cannot compile this file to desired target: " + ++ input_fn)) + + debugTraceMsg dflags 4 (text "Running the pipeline") + r <- runPipeline' start_phase stop_phase hsc_env env input_fn + output maybe_loc maybe_stub_o + + -- If we are compiling a Haskell module, and doing + -- -dynamic-too, but couldn't do the -dynamic-too fast + -- path, then rerun the pipeline for the dyn way + let dflags = extractDynFlags hsc_env + when isHaskellishFile $ whenCannotGenerateDynamicToo dflags $ do + debugTraceMsg dflags 4 + (text "Running the pipeline again for -dynamic-too") let dflags' = doDynamicToo dflags - hsc_env1 <- newHscEnv dflags' - _ <- runPipeline' stop_phase hsc_env1 (input_fn, mb_phase) - mb_basename output maybe_loc maybe_stub_o + -- TODO: This should use -dyno + output' = case output of + SpecificFile fn -> SpecificFile (replaceExtension fn (objectSuf dflags')) + Persistent -> Persistent + Temporary -> Temporary + hsc_env' <- newHscEnv dflags' + _ <- runPipeline' start_phase stop_phase hsc_env' env input_fn + output' maybe_loc maybe_stub_o return () return r runPipeline' - :: Phase -- ^ When to stop + :: Phase -- ^ When to start + -> Phase -- ^ When to stop -> HscEnv -- ^ Compilation environment - -> (FilePath,Maybe Phase) -- ^ Input filename (and maybe -x suffix) - -> Maybe FilePath -- ^ original basename (if different from ^^^) + -> PipeEnv + -> FilePath -- ^ Input filename -> PipelineOutput -- ^ Output filename -> Maybe ModLocation -- ^ A ModLocation, if this is a Haskell module -> 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 maybe_stub_o + -> IO (DynFlags, FilePath) -- ^ (final flags, output filename) +runPipeline' start_phase stop_phase hsc_env env input_fn + output maybe_loc maybe_stub_o = do - let dflags0 = hsc_dflags hsc_env0 - (input_basename, suffix) = splitExtension input_fn - suffix' = drop 1 suffix -- strip off the . - basename | Just b <- mb_basename = b - | otherwise = input_basename - - -- Decide where dump files should go based on the pipeline output - 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 - start_phase = fromMaybe (startPhase suffix') mb_phase - - -- We want to catch cases of "you can't get there from here" before - -- we start the pipeline, because otherwise it will just run off the - -- end. - -- - -- There is a partial ordering on phases, where A < B iff A occurs - -- before B in a normal compilation pipeline. - - when (not (start_phase `happensBefore` stop_phase)) $ - throwGhcException (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 + let get_output_fn = getOutputFilename stop_phase output (src_basename env) -- Execute the pipeline... - 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 } + let 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' + 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 @@ -575,20 +601,21 @@ runPipeline' stop_phase hsc_env0 (input_fn, mb_phase) -- further compilation stages can tell what the original filename was. case output of Temporary -> - return (dflags', output_fn) - _other -> - do final_fn <- get_output_fn dflags' stop_phase maybe_loc + return (dflags, output_fn) + _ -> + 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) + return (dflags, final_fn) -- ----------------------------------------------------------------------------- -- The pipeline uses a monad to carry around various bits of information -- PipeEnv: invariant information passed down data PipeEnv = PipeEnv { + pe_isHaskellishFile :: Bool, stop_phase :: Phase, -- ^ Stop just before this phase src_basename :: String, -- ^ basename of original input source src_suffix :: String, -- ^ its extension @@ -656,12 +683,13 @@ phaseOutputFilename next_phase = do pipeLoop :: Phase -> FilePath -> CompPipeline FilePath pipeLoop phase input_fn = do PipeEnv{stop_phase} <- getPipeEnv - PipeState{hsc_env} <- getPipeState + dflags <- getDynFlags + let happensBefore' = happensBefore dflags case () of _ | phase `eqPhase` stop_phase -- All done -> return input_fn - | not (phase `happensBefore` stop_phase) + | 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 @@ -670,9 +698,8 @@ pipeLoop phase input_fn = do " but I wanted to stop at phase " ++ show stop_phase) | otherwise - -> do liftIO $ debugTraceMsg (hsc_dflags hsc_env) 4 + -> do liftIO $ debugTraceMsg dflags 4 (ptext (sLit "Running phase") <+> ppr phase) - dflags <- getDynFlags (next_phase, output_fn) <- runPhase phase input_fn dflags pipeLoop next_phase output_fn @@ -1457,6 +1484,12 @@ runPhase MergeStub input_fn dflags panic "runPhase(MergeStub): no stub" Just stub_o -> do liftIO $ joinObjectFiles dflags [input_fn, stub_o] output_fn + whenGeneratingDynamicToo dflags $ do + liftIO $ debugTraceMsg dflags 4 + (text "Merging stub again for -dynamic-too") + let dyn_input_fn = replaceExtension input_fn (dynObjectSuf dflags) + dyn_output_fn = replaceExtension output_fn (dynObjectSuf dflags) + liftIO $ joinObjectFiles dflags [dyn_input_fn, stub_o] dyn_output_fn return (StopLn, output_fn) -- warning suppression @@ -1956,12 +1989,20 @@ doCpp dflags raw include_cc_opts input_fn output_fn = do -- remember, in code we *compile*, the HOST is the same our TARGET, -- and BUILD is the same as our HOST. + let sse2 = isSse2Enabled dflags + sse4_2 = isSse4_2Enabled dflags + sse_defs = + [ "-D__SSE__=1" | sse2 || sse4_2 ] ++ + [ "-D__SSE2__=1" | sse2 || sse4_2 ] ++ + [ "-D__SSE4_2__=1" | sse4_2 ] + 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 + ++ map SysTools.Option sse_defs ++ [ SysTools.Option "-x" , SysTools.Option "c" , SysTools.Option input_fn diff --git a/compiler/main/DynFlags.hs b/compiler/main/DynFlags.hs index 35386296f0..4edeb23ef4 100644 --- a/compiler/main/DynFlags.hs +++ b/compiler/main/DynFlags.hs @@ -118,6 +118,10 @@ module DynFlags ( tAG_MASK, mAX_PTR_TAG, tARGET_MIN_INT, tARGET_MAX_INT, tARGET_MAX_WORD, + + -- * SSE + isSse2Enabled, + isSse4_2Enabled, ) where #include "HsVersions.h" @@ -590,6 +594,7 @@ data DynFlags = DynFlags { dynHiSuf :: String, outputFile :: Maybe String, + dynOutputFile :: Maybe String, outputHi :: Maybe String, dynLibLoader :: DynLibLoader, @@ -1144,6 +1149,7 @@ doDynamicToo :: DynFlags -> DynFlags doDynamicToo dflags0 = let dflags1 = unSetGeneralFlag' Opt_Static dflags0 dflags2 = addWay' WayDyn dflags1 dflags3 = dflags2 { + outputFile = dynOutputFile dflags2, hiSuf = dynHiSuf dflags2, objectSuf = dynObjectSuf dflags2 } @@ -1222,6 +1228,7 @@ defaultDynFlags mySettings = pluginModNameOpts = [], outputFile = Nothing, + dynOutputFile = Nothing, outputHi = Nothing, dynLibLoader = SystemDependent, dumpPrefix = Nothing, @@ -1594,7 +1601,7 @@ setObjectDir, setHiDir, setStubDir, setDumpDir, setOutputDir, addCmdlineFramework, addHaddockOpts, addGhciScript, setInteractivePrint :: String -> DynFlags -> DynFlags -setOutputFile, setOutputHi, setDumpPrefixForce +setOutputFile, setDynOutputFile, setOutputHi, setDumpPrefixForce :: Maybe String -> DynFlags -> DynFlags setObjectDir f d = d{ objectDir = Just f} @@ -1614,6 +1621,7 @@ setDynHiSuf f d = d{ dynHiSuf = f} setHcSuf f d = d{ hcSuf = f} setOutputFile f d = d{ outputFile = f} +setDynOutputFile f d = d{ dynOutputFile = f} setOutputHi f d = d{ outputHi = f} addPluginModuleName :: String -> DynFlags -> DynFlags @@ -1796,11 +1804,31 @@ parseDynamicFlagsFull activeFlags cmdline dflags0 args = do throwGhcException (CmdLineError ("combination not supported: " ++ intercalate "/" (map wayDesc theWays))) - let (dflags4, consistency_warnings) = makeDynFlagsConsistent dflags3 + -- TODO: This is an ugly hack. Do something better. + -- -fPIC affects the CMM code we generate, so if + -- we are in -dynamic-too mode we need -fPIC to be on during the + -- shared part of the compilation. + let doingDynamicToo = gopt Opt_BuildDynamicToo dflags3 + platform = targetPlatform dflags3 + dflags4 = if doingDynamicToo + then foldr setGeneralFlag' dflags3 + (wayGeneralFlags platform WayDyn) + else dflags3 + + {- + TODO: This test doesn't quite work: We don't want to give an error + when e.g. compiling a C file, only when compiling Haskell files. + when doingDynamicToo $ + unless (isJust (outputFile dflags4) == isJust (dynOutputFile dflags4)) $ + throwGhcException $ CmdLineError + "With -dynamic-too, must give -dyno iff giving -o" + -} + + let (dflags5, consistency_warnings) = makeDynFlagsConsistent dflags4 - liftIO $ setUnsafeGlobalDynFlags dflags4 + liftIO $ setUnsafeGlobalDynFlags dflags5 - return (dflags4, leftover, consistency_warnings ++ sh_warns ++ warns) + return (dflags5, leftover, consistency_warnings ++ sh_warns ++ warns) updateWays :: DynFlags -> DynFlags updateWays dflags @@ -1992,6 +2020,7 @@ dynamic_flags = [ ------- Output Redirection ------------------------------------------ , Flag "odir" (hasArg setObjectDir) , Flag "o" (sepArg (setOutputFile . Just)) + , Flag "dyno" (sepArg (setDynOutputFile . Just)) , Flag "ohi" (hasArg (setOutputHi . Just )) , Flag "osuf" (hasArg setObjectSuf) , Flag "dynosuf" (hasArg setDynObjectSuf) @@ -2153,6 +2182,11 @@ dynamic_flags = [ , Flag "monly-4-regs" (NoArg (addWarn "The -monly-4-regs flag does nothing; it will be removed in a future GHC release")) , Flag "msse2" (NoArg (setGeneralFlag Opt_SSE2)) , Flag "msse4.2" (NoArg (setGeneralFlag Opt_SSE4_2)) + -- at some point we should probably have a single SSE flag that + -- contains the SSE version, instead of having a different flag + -- per version. That would make it easier to e.g. check if SSE2 is + -- enabled as you wouldn't have to check if either Opt_SSE2 or + -- Opt_SSE4_2 is set (as the latter implies the former). ------ Warning opts ------------------------------------------------- , Flag "W" (NoArg (mapM_ setWarningFlag minusWOpts)) @@ -3371,3 +3405,21 @@ makeDynFlagsConsistent dflags arch = platformArch platform os = platformOS platform +-- ----------------------------------------------------------------------------- +-- SSE + +isSse2Enabled :: DynFlags -> Bool +isSse2Enabled dflags = isSse4_2Enabled dflags || isSse2Enabled' + where + isSse2Enabled' = case platformArch (targetPlatform dflags) of + ArchX86_64 -> -- SSE2 is fixed on for x86_64. It would be + -- possible to make it optional, but we'd need to + -- fix at least the foreign call code where the + -- calling convention specifies the use of xmm regs, + -- and possibly other places. + True + ArchX86 -> gopt Opt_SSE2 dflags + _ -> False + +isSse4_2Enabled :: DynFlags -> Bool +isSse4_2Enabled dflags = gopt Opt_SSE4_2 dflags diff --git a/compiler/main/GhcMake.hs b/compiler/main/GhcMake.hs index 34898a92a3..80227cd3f3 100644 --- a/compiler/main/GhcMake.hs +++ b/compiler/main/GhcMake.hs @@ -709,9 +709,9 @@ upsweep_mod hsc_env old_hpt (stable_obj, stable_bco) summary mod_index nmods prevailing_target = hscTarget (hsc_dflags hsc_env) local_target = hscTarget dflags - -- If OPTIONS_GHC contains -fasm or -fvia-C, be careful that + -- If OPTIONS_GHC contains -fasm or -fllvm, be careful that -- we don't do anything dodgy: these should only work to change - -- from -fvia-C to -fasm and vice-versa, otherwise we could + -- from -fllvm to -fasm and vice-versa, otherwise we could -- end up trying to link object code to byte code. target = if prevailing_target /= local_target && (not (isObjectTarget prevailing_target) diff --git a/compiler/main/HscMain.hs b/compiler/main/HscMain.hs index 2a838168dd..3562f27d5c 100644 --- a/compiler/main/HscMain.hs +++ b/compiler/main/HscMain.hs @@ -1250,8 +1250,9 @@ hscWriteIface iface no_change mod_summary = do -- TODO: We should do a no_change check for the dynamic -- interface file too let dynIfaceFile = replaceExtension ifaceFile (dynHiSuf dflags) + dynIfaceFile' = addBootSuffix_maybe (mi_boot iface) dynIfaceFile dynDflags = doDynamicToo dflags - writeIfaceFile dynDflags dynIfaceFile iface + writeIfaceFile dynDflags dynIfaceFile' iface -- | Compile to hard-code. hscGenHardCode :: CgGuts -> ModSummary diff --git a/compiler/nativeGen/SPARC/Base.hs b/compiler/nativeGen/SPARC/Base.hs index aa7b057e69..96fb5e7797 100644 --- a/compiler/nativeGen/SPARC/Base.hs +++ b/compiler/nativeGen/SPARC/Base.hs @@ -1,26 +1,19 @@ -- | Bits and pieces on the bottom of the module dependency tree. --- Also import the required constants, so we know what we're using. --- --- In the interests of cross-compilation, we want to free ourselves --- from the autoconf generated modules like main/Constants +-- Also import the required constants, so we know what we're using. -- -{-# OPTIONS -fno-warn-tabs #-} --- The above warning supression flag is a temporary kludge. --- While working on this module you are encouraged to remove it and --- detab the module (please do the detabbing in a separate patch). See --- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#TabsvsSpaces --- for details +-- In the interests of cross-compilation, we want to free ourselves +-- from the autoconf generated modules like main/Constants module SPARC.Base ( - wordLength, - wordLengthInBits, - spillAreaLength, - spillSlotSize, - extraStackArgsHere, - fits13Bits, - is32BitInteger, - largeOffsetError + wordLength, + wordLengthInBits, + spillAreaLength, + spillSlotSize, + extraStackArgsHere, + fits13Bits, + is32BitInteger, + largeOffsetError ) where @@ -36,13 +29,13 @@ wordLength :: Int wordLength = 4 wordLengthInBits :: Int -wordLengthInBits - = wordLength * 8 +wordLengthInBits + = wordLength * 8 -- Size of the available spill area spillAreaLength :: DynFlags -> Int spillAreaLength - = rESERVED_C_STACK_BYTES + = rESERVED_C_STACK_BYTES -- | We need 8 bytes because our largest registers are 64 bit. spillSlotSize :: Int @@ -50,7 +43,7 @@ spillSlotSize = 8 -- | We (allegedly) put the first six C-call arguments in registers; --- where do we start putting the rest of them? +-- where do we start putting the rest of them? extraStackArgsHere :: Int extraStackArgsHere = 23 @@ -61,22 +54,22 @@ fits13Bits :: Integral a => a -> Bool fits13Bits x = x >= -4096 && x < 4096 -- | Check whether an integer will fit in 32 bits. --- A CmmInt is intended to be truncated to the appropriate --- number of bits, so here we truncate it to Int64. This is --- important because e.g. -1 as a CmmInt might be either --- -1 or 18446744073709551615. +-- A CmmInt is intended to be truncated to the appropriate +-- number of bits, so here we truncate it to Int64. This is +-- important because e.g. -1 as a CmmInt might be either +-- -1 or 18446744073709551615. -- is32BitInteger :: Integer -> Bool -is32BitInteger i - = i64 <= 0x7fffffff && i64 >= -0x80000000 - where i64 = fromIntegral i :: Int64 +is32BitInteger i + = i64 <= 0x7fffffff && i64 >= -0x80000000 + where i64 = fromIntegral i :: Int64 -- | Sadness. largeOffsetError :: (Integral a, Show a) => a -> b largeOffsetError i = panic ("ERROR: SPARC native-code generator cannot handle large offset (" - ++ show i ++ ");\nprobably because of large constant data structures;" ++ - "\nworkaround: use -fvia-C on this module.\n") + ++ show i ++ ");\nprobably because of large constant data structures;" ++ + "\nworkaround: use -fllvm on this module.\n") diff --git a/compiler/nativeGen/X86/CodeGen.hs b/compiler/nativeGen/X86/CodeGen.hs index 30cf060e74..d01470926b 100644 --- a/compiler/nativeGen/X86/CodeGen.hs +++ b/compiler/nativeGen/X86/CodeGen.hs @@ -71,20 +71,12 @@ is32BitPlatform = do sse2Enabled :: NatM Bool sse2Enabled = do dflags <- getDynFlags - case platformArch (targetPlatform dflags) of - ArchX86_64 -> -- SSE2 is fixed on for x86_64. It would be - -- possible to make it optional, but we'd need to - -- fix at least the foreign call code where the - -- calling convention specifies the use of xmm regs, - -- and possibly other places. - return True - ArchX86 -> return (gopt Opt_SSE2 dflags || gopt Opt_SSE4_2 dflags) - _ -> panic "sse2Enabled: Not an X86* arch" + return (isSse2Enabled dflags) sse4_2Enabled :: NatM Bool sse4_2Enabled = do dflags <- getDynFlags - return (gopt Opt_SSE4_2 dflags) + return (isSse4_2Enabled dflags) if_sse2 :: NatM a -> NatM a -> NatM a if_sse2 sse2 x87 = do diff --git a/compiler/prelude/PrelRules.lhs b/compiler/prelude/PrelRules.lhs index b58eb0a47e..b21d546ef7 100644 --- a/compiler/prelude/PrelRules.lhs +++ b/compiler/prelude/PrelRules.lhs @@ -142,28 +142,28 @@ primOpRules nm Int2WordOp = mkPrimOpRule nm 1 [ liftLitDynFlags int2WordLit , inversePrimOp Word2IntOp ] primOpRules nm Narrow8IntOp = mkPrimOpRule nm 1 [ liftLit narrow8IntLit , subsumedByPrimOp Narrow8IntOp - , subsumedByPrimOp Narrow16IntOp - , subsumedByPrimOp Narrow32IntOp ] + , Narrow8IntOp `subsumesPrimOp` Narrow16IntOp + , Narrow8IntOp `subsumesPrimOp` Narrow32IntOp ] primOpRules nm Narrow16IntOp = mkPrimOpRule nm 1 [ liftLit narrow16IntLit - , Narrow16IntOp `subsumesPrimOp` Narrow8IntOp + , subsumedByPrimOp Narrow8IntOp , subsumedByPrimOp Narrow16IntOp - , subsumedByPrimOp Narrow32IntOp ] + , Narrow16IntOp `subsumesPrimOp` Narrow32IntOp ] primOpRules nm Narrow32IntOp = mkPrimOpRule nm 1 [ liftLit narrow32IntLit - , Narrow32IntOp `subsumesPrimOp` Narrow8IntOp - , Narrow32IntOp `subsumesPrimOp` Narrow16IntOp + , subsumedByPrimOp Narrow8IntOp + , subsumedByPrimOp Narrow16IntOp , subsumedByPrimOp Narrow32IntOp , removeOp32 ] primOpRules nm Narrow8WordOp = mkPrimOpRule nm 1 [ liftLit narrow8WordLit , subsumedByPrimOp Narrow8WordOp - , subsumedByPrimOp Narrow16WordOp - , subsumedByPrimOp Narrow32WordOp ] + , Narrow8WordOp `subsumesPrimOp` Narrow16WordOp + , Narrow8WordOp `subsumesPrimOp` Narrow32WordOp ] primOpRules nm Narrow16WordOp = mkPrimOpRule nm 1 [ liftLit narrow16WordLit - , Narrow16WordOp `subsumesPrimOp` Narrow8WordOp + , subsumedByPrimOp Narrow8WordOp , subsumedByPrimOp Narrow16WordOp - , subsumedByPrimOp Narrow32WordOp ] + , Narrow16WordOp `subsumesPrimOp` Narrow32WordOp ] primOpRules nm Narrow32WordOp = mkPrimOpRule nm 1 [ liftLit narrow32WordLit - , Narrow32WordOp `subsumesPrimOp` Narrow8WordOp - , Narrow32WordOp `subsumesPrimOp` Narrow16WordOp + , subsumedByPrimOp Narrow8WordOp + , subsumedByPrimOp Narrow16WordOp , subsumedByPrimOp Narrow32WordOp , removeOp32 ] primOpRules nm OrdOp = mkPrimOpRule nm 1 [ liftLit char2IntLit diff --git a/compiler/typecheck/TcForeign.lhs b/compiler/typecheck/TcForeign.lhs index 4ac5f48dd6..b1aef2fd77 100644 --- a/compiler/typecheck/TcForeign.lhs +++ b/compiler/typecheck/TcForeign.lhs @@ -255,7 +255,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety mh (CFunction ta dflags <- getDynFlags check (xopt Opt_GHCForeignImportPrim dflags) (text "Use -XGHCForeignImportPrim to allow `foreign import prim'.") - checkCg (checkCOrAsmOrLlvmOrDotNetOrInterp) + checkCg checkCOrAsmOrLlvmOrInterp checkCTarget target check (playSafe safety) (text "The safe/unsafe annotation should not be used with `foreign import prim'.") @@ -264,7 +264,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety mh (CFunction ta checkForeignRes nonIOok checkSafe (isFFIPrimResultTy dflags) res_ty return idecl | otherwise = do -- Normal foreign import - checkCg checkCOrAsmOrLlvmOrDotNetOrInterp + checkCg checkCOrAsmOrLlvmOrInterp cconv' <- checkCConv cconv checkCTarget target dflags <- getDynFlags @@ -283,7 +283,7 @@ tcCheckFIType sig_ty arg_tys res_ty idecl@(CImport cconv safety mh (CFunction ta -- that the C identifier is valid for C checkCTarget :: CCallTarget -> TcM () checkCTarget (StaticTarget str _ _) = do - checkCg checkCOrAsmOrLlvmOrDotNetOrInterp + checkCg checkCOrAsmOrLlvmOrInterp check (isCLabelString str) (badCName str) checkCTarget DynamicTarget = panic "checkCTarget DynamicTarget" @@ -427,7 +427,7 @@ checkCOrAsmOrLlvm HscC = Nothing checkCOrAsmOrLlvm HscAsm = Nothing checkCOrAsmOrLlvm HscLlvm = Nothing checkCOrAsmOrLlvm _ - = Just (text "requires via-C, llvm (-fllvm) or native code generation (-fvia-C)") + = Just (text "requires unregisterised, llvm (-fllvm) or native code generation (-fasm)") checkCOrAsmOrLlvmOrInterp :: HscTarget -> Maybe SDoc checkCOrAsmOrLlvmOrInterp HscC = Nothing @@ -435,15 +435,7 @@ checkCOrAsmOrLlvmOrInterp HscAsm = Nothing checkCOrAsmOrLlvmOrInterp HscLlvm = Nothing checkCOrAsmOrLlvmOrInterp HscInterpreted = Nothing checkCOrAsmOrLlvmOrInterp _ - = Just (text "requires interpreted, C, Llvm or native code generation") - -checkCOrAsmOrLlvmOrDotNetOrInterp :: HscTarget -> Maybe SDoc -checkCOrAsmOrLlvmOrDotNetOrInterp HscC = Nothing -checkCOrAsmOrLlvmOrDotNetOrInterp HscAsm = Nothing -checkCOrAsmOrLlvmOrDotNetOrInterp HscLlvm = Nothing -checkCOrAsmOrLlvmOrDotNetOrInterp HscInterpreted = Nothing -checkCOrAsmOrLlvmOrDotNetOrInterp _ - = Just (text "requires interpreted, C, Llvm or native code generation") + = Just (text "requires interpreted, unregisterised, llvm or native code generation") checkCg :: (HscTarget -> Maybe SDoc) -> TcM () checkCg check = do diff --git a/compiler/typecheck/TcValidity.lhs b/compiler/typecheck/TcValidity.lhs index 80e7aa0415..44d7d4c6db 100644 --- a/compiler/typecheck/TcValidity.lhs +++ b/compiler/typecheck/TcValidity.lhs @@ -827,7 +827,9 @@ checkValidInstance ctxt hs_type ty -- in the constraint than in the head
; undecidable_ok <- xoptM Opt_UndecidableInstances
; if undecidable_ok
- then checkAmbiguity ctxt ty
+ then do checkAmbiguity ctxt ty
+ checkTc (checkInstLiberalCoverage clas theta inst_tys)
+ (instTypeErr clas inst_tys liberal_msg)
else do { checkInstTermination inst_tys theta
; checkTc (checkInstCoverage clas inst_tys)
(instTypeErr clas inst_tys msg) }
@@ -837,6 +839,10 @@ checkValidInstance ctxt hs_type ty msg = parens (vcat [ptext (sLit "the Coverage Condition fails for one of the functional dependencies;"),
undecidableMsg])
+ liberal_msg = vcat
+ [ ptext $ sLit "Multiple uses of this instance may be inconsistent"
+ , ptext $ sLit "with the functional dependencies of the class."
+ ]
-- The location of the "head" of the instance
head_loc = case hs_type of
L _ (HsForAllTy _ _ _ (L loc _)) -> loc
diff --git a/compiler/types/FunDeps.lhs b/compiler/types/FunDeps.lhs index 09d0be07bb..fe8781b1f8 100644 --- a/compiler/types/FunDeps.lhs +++ b/compiler/types/FunDeps.lhs @@ -19,7 +19,7 @@ module FunDeps ( FDEq (..), Equation(..), pprEquation, oclose, improveFromInstEnv, improveFromAnother, - checkInstCoverage, checkFunDeps, + checkInstCoverage, checkInstLiberalCoverage, checkFunDeps, pprFundeps ) where @@ -145,6 +145,53 @@ oclose preds fixed_tvs ClassPred cls tys -> [(cls, tys)] TuplePred ts -> concatMap classesOfPredTy ts _ -> [] + +-- An alternative implementation of `oclose`. Differences: +-- 1. The empty set of variables is allowed to determine stuff, +-- 2. We also use equality predicates as FDs. +-- +-- I (Iavor) believe that this is the correct implementation of oclose. +-- For 1: the above argument about `t` being monomorphic seems incorrect. +-- The correct behavior is to quantify over `t`, even though we know that +-- it may be instantiated to at most one type. The point is that we might +-- only find out what that type is later, at the call site to the function. +-- In general, we should be quantifying all variables that are (i) not in +-- mentioned in the environment, and (ii) not FD-determined by something in +-- the environment. +-- For 2: This is just a nicity, but it makes things a bit more general: +-- if we have an assumption `t1 ~ t2`, then we use the fact that if we know +-- `t1` we also know `t2` and the other way. + +oclose1 :: [PredType] -> TyVarSet -> TyVarSet +oclose1 preds fixed_tvs + | null tv_fds = fixed_tvs -- Fast escape hatch for common case. + | otherwise = loop fixed_tvs + where + loop fixed_tvs + | new_fixed_tvs `subVarSet` fixed_tvs = fixed_tvs + | otherwise = loop new_fixed_tvs + where new_fixed_tvs = foldl extend fixed_tvs tv_fds + + extend fixed_tvs (ls,rs) + | ls `subVarSet` fixed_tvs = fixed_tvs `unionVarSet` rs + | otherwise = fixed_tvs + + tv_fds :: [(TyVarSet,TyVarSet)] + tv_fds = [ (tyVarsOfTypes xs, tyVarsOfTypes ys) + | (xs, ys) <- concatMap deterimned preds + ] + + deterimned :: PredType -> [([Type],[Type])] + deterimned pred + = case classifyPredType pred of + ClassPred cls tys -> + do let (cls_tvs, cls_fds) = classTvsFds cls + fd <- cls_fds + return (instFD fd cls_tvs tys) + EqPred t1 t2 -> [([t1],[t2]), ([t2],[t1])] + TuplePred ts -> concatMap deterimned ts + _ -> [] + \end{code} @@ -471,6 +518,23 @@ checkInstCoverage clas inst_taus fundep_ok fd = tyVarsOfTypes rs `subVarSet` tyVarsOfTypes ls where (ls,rs) = instFD fd tyvars inst_taus + +checkInstLiberalCoverage :: Class -> [PredType] -> [Type] -> Bool +-- Check that the Liberal Coverage Condition is obeyed in an instance decl +-- For example, if we have: +-- class C a b | a -> b +-- instance theta => C t1 t2 +-- Then we require fv(t2) `subset` oclose(fv(t1), theta) +-- This ensures the self-consistency of the instance, but +-- it does not guarantee termination. +-- See Note [Coverage Condition] below + +checkInstLiberalCoverage clas theta inst_taus + = all fundep_ok fds + where + (tyvars, fds) = classTvsFds clas + fundep_ok fd = tyVarsOfTypes rs `subVarSet` oclose1 theta (tyVarsOfTypes ls) + where (ls,rs) = instFD fd tyvars inst_taus \end{code} Note [Coverage condition] diff --git a/docs/users_guide/flags.xml b/docs/users_guide/flags.xml index e084315586..5e2e52d38d 100644 --- a/docs/users_guide/flags.xml +++ b/docs/users_guide/flags.xml @@ -460,8 +460,9 @@ <tbody> <row> <entry><option>-fforce-recomp</option></entry> - <entry>Turn off recompilation checking; implied by any - <option>-ddump-X</option> option</entry> + <entry>Turn off recompilation checking. This is implied by any + <option>-ddump-X</option> option when compiling a single + file (i.e. when using <literal>-c</literal>).</entry> <entry>dynamic</entry> <entry><option>-fno-force-recomp</option></entry> </row> @@ -638,7 +639,7 @@ </row> <row> <entry><option>-no-auto-link-packages</option></entry> - <entry>Don't automatically link in the haskell98 package.</entry> + <entry>Don't automatically link in the base and rts packages.</entry> <entry>dynamic</entry> <entry>-</entry> </row> diff --git a/docs/users_guide/packages.xml b/docs/users_guide/packages.xml index d1df2d4712..c6a1d089a2 100644 --- a/docs/users_guide/packages.xml +++ b/docs/users_guide/packages.xml @@ -252,8 +252,8 @@ exposed-modules: Network.BSD, </indexterm></term> <listitem> <para>By default, GHC will automatically link in the - <literal>haskell98</literal> package. This flag disables that - behaviour.</para> + <literal>base</literal> and <literal>rts</literal> packages. + This flag disables that behaviour.</para> </listitem> </varlistentry> diff --git a/docs/users_guide/sooner.xml b/docs/users_guide/sooner.xml index ad798aca32..4a6430c9db 100644 --- a/docs/users_guide/sooner.xml +++ b/docs/users_guide/sooner.xml @@ -295,7 +295,7 @@ f (Wibble x y) # ugly, and proud of it <literal>Strictness: <string></literal>. The <literal><string></literal> gives the strictness of the function's arguments: see <ulink url="http://hackage.haskell.org/trac/ghc/wiki/Commentary/Compiler/Demand"> - the GHC Commentary</ulink> for a description of the stricntess notation. + the GHC Commentary</ulink> for a description of the strictness notation. </para> <para>For an “unpackable” @@ -229,10 +229,6 @@ ifneq "$(CLEANING)" "YES" include rules/hs-suffix-rules-srcdir.mk include rules/hs-suffix-rules.mk include rules/hi-rule.mk - -$(foreach way,$(ALL_WAYS),\ - $(eval $(call hi-rule,$(way)))) - include rules/c-suffix-rules.mk include rules/cmm-suffix-rules.mk diff --git a/ghc/Main.hs b/ghc/Main.hs index 05a986daae..cca4581061 100644 --- a/ghc/Main.hs +++ b/ghc/Main.hs @@ -545,7 +545,7 @@ mode_flags = addFlag "-no-link" f)) , Flag "M" (PassFlag (setMode doMkDependHSMode)) , Flag "E" (PassFlag (setMode (stopBeforeMode anyHsc))) - , Flag "C" (PassFlag setGenerateC) + , Flag "C" (PassFlag (setMode (stopBeforeMode HCc))) , Flag "S" (PassFlag (setMode (stopBeforeMode As))) , Flag "-make" (PassFlag (setMode doMakeMode)) , Flag "-interactive" (PassFlag (setMode doInteractiveMode)) @@ -553,14 +553,6 @@ mode_flags = , Flag "e" (SepArg (\s -> setMode (doEvalMode s) "-e")) ] -setGenerateC :: String -> EwM ModeM () -setGenerateC f = do -- TODO: We used to warn and ignore when - -- unregisterised, but we no longer know whether - -- we are unregisterised at this point. Should - -- we check later on? - setMode (stopBeforeMode HCc) f - addFlag "-fvia-C" f - setMode :: Mode -> String -> EwM ModeM () setMode newMode newFlag = liftEwM $ do (mModeFlag, errs, flags') <- getCmdLineState diff --git a/libraries/bytestring b/libraries/bytestring -Subproject 6bd69fe27af33e878e38f4c579983f6a23120a8 +Subproject aaf84424aee2bac53b5121115b95ae47bcce17a diff --git a/libraries/terminfo b/libraries/terminfo -Subproject 579d2c324e69856ff8d1ea8b5036e30c920e197 +Subproject 116d3ee6840d52bab69c880d775ae290a20d64b diff --git a/mk/config.mk.in b/mk/config.mk.in index 3d7918d001..19c369dbfa 100644 --- a/mk/config.mk.in +++ b/mk/config.mk.in @@ -233,6 +233,8 @@ include $(TOP)/mk/install.mk # portable as possible. BeConservative = NO +ExtraMakefileSanityChecks = NO + # # Building various ways? # (right now, empty if not). diff --git a/rules/build-package-way.mk b/rules/build-package-way.mk index 76598debfa..aade4e93af 100644 --- a/rules/build-package-way.mk +++ b/rules/build-package-way.mk @@ -17,8 +17,6 @@ $(call profStart, build-package-way($1,$2,$3)) $(call distdir-way-opts,$1,$2,$3,$4) $(call hs-suffix-rules,$1,$2,$3) -$$(foreach dir,$$($1_$2_HS_SRC_DIRS),\ - $$(eval $$(call hs-suffix-rules-srcdir,$1,$2,$3,$$(dir)))) $(call hs-objs,$1,$2,$3) diff --git a/rules/build-package.mk b/rules/build-package.mk index c0b9902b55..e64754cb3f 100644 --- a/rules/build-package.mk +++ b/rules/build-package.mk @@ -43,8 +43,6 @@ $(call clean-target,$1,$2,$1/$2) distclean : clean_$1_$2_config -maintainer-clean : distclean - .PHONY: clean_$1_$2_config clean_$1_$2_config: $$(call removeFiles,$1/config.log $1/config.status $(wildcard $1/include/Hs*Config.h)) diff --git a/rules/build-prog.mk b/rules/build-prog.mk index 2c1836abed..4111e172b9 100644 --- a/rules/build-prog.mk +++ b/rules/build-prog.mk @@ -155,8 +155,6 @@ endif endif $(call hs-suffix-rules,$1,$2,$$($1_$2_PROGRAM_WAY)) -$$(foreach dir,$$($1_$2_HS_SRC_DIRS),\ - $$(eval $$(call hs-suffix-rules-srcdir,$1,$2,$$($1_$2_PROGRAM_WAY),$$(dir)))) $(call c-objs,$1,$2,$$($1_$2_PROGRAM_WAY)) $(call hs-objs,$1,$2,$$($1_$2_PROGRAM_WAY)) diff --git a/rules/hi-rule.mk b/rules/hi-rule.mk index 35baffd11a..e478c17aea 100644 --- a/rules/hi-rule.mk +++ b/rules/hi-rule.mk @@ -32,11 +32,13 @@ # exit 1; \ # fi # -# This version adds a useful sanity check; but it is also expensive on -# Windows where spawning a shell takes a while (about 0.3s). We'd -# like to avoid the shell if necessary. This also hides the message -# "nothing to be done for 'all'", since make thinks it has actually done -# something. +# This version adds a useful sanity check, and is a good solution, +# except that it means spawning a shell. This can be expensive, +# especially on Windows where spawning a shell takes about 0.3s. +# We'd like to avoid the shell if necessary. This also hides the +# message "nothing to be done for 'all'", since make thinks it has +# actually done something. Therefore we only use this version +# if ExtraMakefileSanityChecks is enabled. # # %.hi : %.o # @@ -61,12 +63,40 @@ # the ';' at the end signifies an "empty command" (see the GNU make # documentation). An empty command is enough to get GNU make to think # it has updated %.hi, but without actually spawning a shell to do so. +# +# However, given that rule, make thinks that it can make .hi files +# for any object file, even if the object file was created from e.g. +# a C source file. We therefore also add a dependency on the .hs/.lhs +# source file, which means we finally end up with rules like: +# +# a/%.hi : a/%.o b/%.hs ; + +define hi-rule # $1 = source directory, $2 = object directory, $3 = way + +$(call hi-rule-helper,$2/%.$$($3_hisuf) : $2/%.$$($3_osuf) $1/%.hs) +$(call hi-rule-helper,$2/%.$$($3_hisuf) : $2/%.$$($3_osuf) $1/%.lhs) + +$(call hi-rule-helper,$2/%.$$($3_way_)hi-boot : $2/%.$$($3_way_)o-boot $1/%.hs) +$(call hi-rule-helper,$2/%.$$($3_way_)hi-boot : $2/%.$$($3_way_)o-boot $1/%.lhs) -define hi-rule # $1 = way +endef + +ifeq "$(ExtraMakefileSanityChecks)" "NO" -%.$$($1_hisuf) : %.$$($1_osuf) ; +define hi-rule-helper # $1 = rule header +$1 ; +endef -%.$$($1_way_)hi-boot : %.$$($1_way_)o-boot ; +else + +define hi-rule-helper # $1 = rule header +$1 + @if [ ! -f $$@ ] ; then \ + echo "Panic! $$< exists, but $$@ does not."; \ + exit 1; \ + fi endef +endif + diff --git a/rules/hs-suffix-rules-srcdir.mk b/rules/hs-suffix-rules-srcdir.mk index 94a41d5e73..776d1ce0f6 100644 --- a/rules/hs-suffix-rules-srcdir.mk +++ b/rules/hs-suffix-rules-srcdir.mk @@ -52,6 +52,8 @@ $1/$2/build/%.$$($3_hcsuf) : $1/$4/%.hs $$(LAX_DEPS_FOLLOW) $$($1_$2_HC_DEP) $$( $1/$2/build/%.$$($3_hcsuf) : $1/$4/%.lhs $$(LAX_DEPS_FOLLOW) $$($1_$2_HC_DEP) $$($1_$2_PKGDATA_DEP) $$(call cmd,$1_$2_HC) $$($1_$2_$3_ALL_HC_OPTS) -C $$< -o $$@ +$(call hi-rule,$1/$4,$1/$2/build,$3) + endif # XXX: for some reason these get used in preference to the direct diff --git a/rules/hs-suffix-rules.mk b/rules/hs-suffix-rules.mk index 9d547533fe..fead7d1d41 100644 --- a/rules/hs-suffix-rules.mk +++ b/rules/hs-suffix-rules.mk @@ -28,8 +28,14 @@ $1/$2/build/%.$$($3_hcsuf) : $1/$2/build/autogen/%.hs $$(LAX_DEPS_FOLLOW) $$($1_ $1/$2/build/%.$$($3_osuf) : $1/$2/build/autogen/%.hs $$(LAX_DEPS_FOLLOW) $$($1_$2_HC_DEP) $$(call cmd,$1_$2_HC) $$($1_$2_$3_ALL_HC_OPTS) -c $$< -o $$@ +$(call hi-rule,$1/$2/build,$1/$2/build,$3) +$(call hi-rule,$1/$2/build/autogen,$1/$2/build,$3) + endif endif +$$(foreach dir,$$($1_$2_HS_SRC_DIRS),\ + $$(eval $$(call hs-suffix-rules-srcdir,$1,$2,$3,$$(dir)))) + endef # hs-suffix-rules |