summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/deSugar/Desugar.hs7
-rw-r--r--compiler/deSugar/DsBinds.hs24
-rw-r--r--compiler/deSugar/DsExpr.hs2
-rw-r--r--compiler/deSugar/DsListComp.hs2
-rw-r--r--compiler/deSugar/DsMonad.hs22
-rw-r--r--compiler/deSugar/DsUtils.hs39
-rw-r--r--compiler/deSugar/MatchLit.hs6
-rw-r--r--compiler/simplCore/CSE.hs9
-rw-r--r--compiler/simplCore/SimplUtils.hs8
-rw-r--r--compiler/typecheck/TcRnTypes.hs44
10 files changed, 134 insertions, 29 deletions
diff --git a/compiler/deSugar/Desugar.hs b/compiler/deSugar/Desugar.hs
index d5931d16e5..a02b1625a9 100644
--- a/compiler/deSugar/Desugar.hs
+++ b/compiler/deSugar/Desugar.hs
@@ -127,6 +127,7 @@ deSugar hsc_env
; (msgs, mb_res)
<- initDs hsc_env mod rdr_env type_env
fam_inst_env complete_matches $
+ withTopBinds $
do { ds_ev_binds <- dsEvBinds ev_binds
; core_prs <- dsTopLHsBinds binds_cvr
; (spec_prs, spec_rules) <- dsImpSpecs imp_specs
@@ -143,15 +144,15 @@ deSugar hsc_env
; case mb_res of {
Nothing -> return (msgs, Nothing) ;
- Just (ds_ev_binds, all_prs, all_rules, vects0, ds_fords) ->
+ Just ((ds_ev_binds, all_prs, all_rules, vects0, ds_fords), ds_top_binds) ->
do { -- Add export flags to bindings
keep_alive <- readIORef keep_var
; let (rules_for_locals, rules_for_imps) = partition isLocalRule all_rules
final_prs = addExportFlagsAndRules target export_set keep_alive
rules_for_locals (fromOL all_prs)
-
- final_pgm = combineEvBinds ds_ev_binds final_prs
+ final_binds = ds_ev_binds ++ ds_top_binds
+ final_pgm = combineEvBinds final_binds final_prs
-- Notice that we put the whole lot in a big Rec, even the foreign binds
-- When compiling PrelFloat, which defines data Float = F# Float#
-- we want F# to be in scope in the foreign marshalling code!
diff --git a/compiler/deSugar/DsBinds.hs b/compiler/deSugar/DsBinds.hs
index 0b115cb902..3414d55b63 100644
--- a/compiler/deSugar/DsBinds.hs
+++ b/compiler/deSugar/DsBinds.hs
@@ -41,6 +41,7 @@ import PrelNames
import TyCon
import TcEvidence
import TcType
+import TcRnMonad
import Type
import Coercion
import TysWiredIn ( typeNatKind, typeSymbolKind )
@@ -61,7 +62,6 @@ import BasicTypes
import DynFlags
import FastString
import Util
-import MonadUtils
import qualified GHC.LanguageExtensions as LangExt
import Control.Monad
@@ -1151,7 +1151,7 @@ dsEvTerm (EvId v) = return (Var v)
dsEvTerm (EvCallStack cs) = dsEvCallStack cs
dsEvTerm (EvTypeable ty ev) = dsEvTypeable ty ev
dsEvTerm (EvLit (EvNum n)) = mkNaturalExpr n
-dsEvTerm (EvLit (EvStr s)) = mkStringExprFS s
+dsEvTerm (EvLit (EvStr s)) = mkStringExprFSAtTopLevel s
dsEvTerm (EvCast tm co)
= do { tm' <- dsEvTerm tm
@@ -1174,14 +1174,15 @@ dsEvTerm (EvSelector sel_id tys tms)
= do { tms' <- mapM dsEvTerm tms
; return $ Var sel_id `mkTyApps` tys `mkApps` tms' }
-dsEvTerm (EvDelayedError ty msg) = return $ dsEvDelayedError ty msg
+dsEvTerm (EvDelayedError ty msg) = dsEvDelayedError ty msg
-dsEvDelayedError :: Type -> FastString -> CoreExpr
+dsEvDelayedError :: Type -> FastString -> DsM CoreExpr
dsEvDelayedError ty msg
- = Var errorId `mkTyApps` [getRuntimeRep "dsEvTerm" ty, ty] `mkApps` [litMsg]
+ = do { litMsg <- bindExprAtTopLevel (Lit (MachStr (fastStringToByteString msg)))
+ ; return $ Var errorId `mkTyApps` [getRuntimeRep "dsEvTerm" ty, ty]
+ `mkApps` [litMsg] }
where
errorId = tYPE_ERROR_ID
- litMsg = Lit (MachStr (fastStringToByteString msg))
{-**********************************************************************
* *
@@ -1321,11 +1322,11 @@ dsEvCallStack cs = do
df <- getDynFlags
m <- getModule
srcLocDataCon <- dsLookupDataCon srcLocDataConName
- let mkSrcLoc l =
+ let mkSrcLoc l = bindExprAtTopLevel =<<
liftM (mkCoreConApps srcLocDataCon)
- (sequence [ mkStringExprFS (unitIdFS $ moduleUnitId m)
- , mkStringExprFS (moduleNameFS $ moduleName m)
- , mkStringExprFS (srcSpanFile l)
+ (sequence [ mkStringExprFSAtTopLevel (unitIdFS $ moduleUnitId m)
+ , mkStringExprFSAtTopLevel (moduleNameFS $ moduleName m)
+ , mkStringExprFSAtTopLevel (srcSpanFile l)
, return $ mkIntExprInt df (srcSpanStartLine l)
, return $ mkIntExprInt df (srcSpanStartCol l)
, return $ mkIntExprInt df (srcSpanEndLine l)
@@ -1339,7 +1340,7 @@ dsEvCallStack cs = do
mkCoreApps (Var pushCSVar) [mkCoreTup [name, loc], rest]
let mkPush name loc tm = do
- nameExpr <- mkStringExprFS name
+ nameExpr <- mkStringExprFSAtTopLevel name
locExpr <- mkSrcLoc loc
case tm of
EvCallStack EvCsEmpty -> return (pushCS nameExpr locExpr emptyCS)
@@ -1350,6 +1351,7 @@ dsEvCallStack cs = do
-- See Note [Overview of implicit CallStacks]
let ip_co = unwrapIP (exprType tmExpr)
return (pushCS nameExpr locExpr (mkCastDs tmExpr ip_co))
+
case cs of
EvCsPushCall name loc tm -> mkPush (occNameFS $ getOccName name) loc tm
EvCsEmpty -> return emptyCS
diff --git a/compiler/deSugar/DsExpr.hs b/compiler/deSugar/DsExpr.hs
index 28254c93b4..6502c781f5 100644
--- a/compiler/deSugar/DsExpr.hs
+++ b/compiler/deSugar/DsExpr.hs
@@ -979,7 +979,7 @@ handle_failure :: LPat Id -> MatchResult -> SyntaxExpr Id -> DsM CoreExpr
handle_failure pat match fail_op
| matchCanFail match
= do { dflags <- getDynFlags
- ; fail_msg <- mkStringExpr (mk_fail_msg dflags pat)
+ ; fail_msg <- mkStringExprAtTopLevel (mk_fail_msg dflags pat)
; fail_expr <- dsSyntaxExpr fail_op [fail_msg]
; extractMatchResult match fail_expr }
| otherwise
diff --git a/compiler/deSugar/DsListComp.hs b/compiler/deSugar/DsListComp.hs
index 2bb303ec98..ba8085c6a2 100644
--- a/compiler/deSugar/DsListComp.hs
+++ b/compiler/deSugar/DsListComp.hs
@@ -824,7 +824,7 @@ dsMcBindStmt pat rhs' bind_op fail_op res1_ty stmts
handle_failure pat match fail_op
| matchCanFail match
= do { dflags <- getDynFlags
- ; fail_msg <- mkStringExpr (mk_fail_msg dflags pat)
+ ; fail_msg <- mkStringExprAtTopLevel (mk_fail_msg dflags pat)
; fail_expr <- dsSyntaxExpr fail_op [fail_msg]
; extractMatchResult match fail_expr }
| otherwise
diff --git a/compiler/deSugar/DsMonad.hs b/compiler/deSugar/DsMonad.hs
index 4f68100111..7242937ee6 100644
--- a/compiler/deSugar/DsMonad.hs
+++ b/compiler/deSugar/DsMonad.hs
@@ -6,7 +6,7 @@
@DsMonad@: monadery used in desugaring
-}
-{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE FlexibleInstances, TupleSections #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} -- instance MonadThings is necessarily an orphan
module DsMonad (
@@ -24,6 +24,7 @@ module DsMonad (
UniqSupply, newUniqueSupply,
getGhcModeDs, dsGetFamInstEnvs,
dsLookupGlobal, dsLookupGlobalId, dsDPHBuiltin, dsLookupTyCon, dsLookupDataCon,
+ withTopBinds,
PArrBuiltin(..),
dsLookupDPHRdrEnv, dsLookupDPHRdrEnv_maybe,
@@ -161,7 +162,7 @@ initDs :: HscEnv
initDs hsc_env mod rdr_env type_env fam_inst_env complete_matches thing_inside
= do { msg_var <- newIORef (emptyBag, emptyBag)
; let all_matches = (hptCompleteSigs hsc_env) ++ complete_matches
- ; pm_iter_var <- newIORef 0
+ ; pm_iter_var <- newIORef 0
; let dflags = hsc_dflags hsc_env
(ds_gbl_env, ds_lcl_env) = mkDsEnvs dflags mod rdr_env type_env
fam_inst_env msg_var
@@ -291,6 +292,7 @@ mkDsEnvs dflags mod rdr_env type_env fam_inst_env msg_var pmvar complete_matches
, ds_dph_env = emptyGlobalRdrEnv
, ds_parr_bi = panic "DsMonad: uninitialised ds_parr_bi"
, ds_complete_matches = completeMatchMap
+ , ds_top_binds = Nothing
}
lcl_env = DsLclEnv { dsl_meta = emptyNameEnv
, dsl_loc = real_span
@@ -318,6 +320,22 @@ loadModule doc mod
is_dloc = wiredInSrcSpan, is_as = name }
name = moduleName mod
+-- | Run the provided action and gather any additional top-level
+-- binders generated by it.
+withTopBinds :: DsM a -> DsM (a, [CoreBind])
+-- see Note [Adding Top-Level Binders in the Desugarer]
+withTopBinds thing_inside = do
+ dflags <- getDynFlags
+ if optLevel dflags < 1
+ -- don't actually bind things at the top at -O0.
+ -- See Note [Adding Top-Level Bindings in the Desugarer]
+ then (,[]) <$> thing_inside
+ else do
+ ref <- liftIO (newIORef [])
+ a <- updGblEnv (\env -> env { ds_top_binds = Just ref }) thing_inside
+ top_binds <- liftIO (readIORef ref)
+ return (a, top_binds)
+
{-
************************************************************************
* *
diff --git a/compiler/deSugar/DsUtils.hs b/compiler/deSugar/DsUtils.hs
index 165130aa94..331b42d922 100644
--- a/compiler/deSugar/DsUtils.hs
+++ b/compiler/deSugar/DsUtils.hs
@@ -25,6 +25,8 @@ module DsUtils (
wrapBind, wrapBinds,
mkErrorAppDs, mkCoreAppDs, mkCoreAppsDs, mkCastDs,
+ mkStringExprAtTopLevel, mkStringExprFSAtTopLevel,
+ bindExprAtTopLevel,
seqVar,
@@ -73,6 +75,8 @@ import SrcLoc
import Util
import DynFlags
import FastString
+import Data.IORef
+import TcRnMonad
import qualified GHC.LanguageExtensions as LangExt
import TcEvidence
@@ -466,10 +470,9 @@ mkErrorAppDs :: Id -- The error function
mkErrorAppDs err_id ty msg = do
src_loc <- getSrcSpanDs
dflags <- getDynFlags
- let
- full_msg = showSDoc dflags (hcat [ppr src_loc, vbar, msg])
- core_msg = Lit (mkMachString full_msg)
- -- mkMachString returns a result of type String#
+ let full_msg = showSDoc dflags (hcat [ppr src_loc, vbar, msg])
+ -- mkMachString returns a result of type String#
+ core_msg <- bindExprAtTopLevel (Lit (mkMachString full_msg))
return (mkApps (Var err_id) [Type (getRuntimeRep "mkErrorAppDs" ty), Type ty, core_msg])
{-
@@ -567,6 +570,34 @@ mkCastDs :: CoreExpr -> Coercion -> CoreExpr
mkCastDs e co | isReflCo co = e
| otherwise = Cast e co
+-- | Like 'mkStringExpr' except it makes the string a new top-level binder.
+mkStringExprAtTopLevel :: String -> DsM CoreExpr
+mkStringExprAtTopLevel = mkStringExprFSAtTopLevel . fsLit
+
+-- | Like 'mkStringExprFS' except it makes the string a new top-level binder.
+mkStringExprFSAtTopLevel :: FastString -> DsM CoreExpr
+mkStringExprFSAtTopLevel str = do
+ str_expr <- mkStringExprFS str
+ bindExprAtTopLevel str_expr
+
+-- | Attempt to bind an expression at the top level.
+--
+-- @bindExprAtTopLevel e@ returns a @Var v@ where @v@ is bound to @e@
+-- if we are compiling a whole module.
+-- If we are compiling an individual expression, e.g. in GHCi,
+-- it returns @e@ unmodified.
+bindExprAtTopLevel :: CoreExpr -> DsM CoreExpr
+-- see Note [Adding Top-Level Binders in the Desguarer]
+bindExprAtTopLevel expr = do
+ top_binds_var_maybe <- ds_top_binds <$> getGblEnv
+ case top_binds_var_maybe of
+ Nothing -> return expr
+ Just var -> do
+ id <- newSysLocalDs (exprType expr)
+ liftIO $ modifyIORef var ((NonRec id expr) :)
+ return (Var id)
+
+
{-
************************************************************************
* *
diff --git a/compiler/deSugar/MatchLit.hs b/compiler/deSugar/MatchLit.hs
index 2e9a5235bf..e955a4f556 100644
--- a/compiler/deSugar/MatchLit.hs
+++ b/compiler/deSugar/MatchLit.hs
@@ -73,7 +73,7 @@ See also below where we look for @DictApps@ for \tr{plusInt}, etc.
-}
dsLit :: HsLit -> DsM CoreExpr
-dsLit (HsStringPrim _ s) = return (Lit (MachStr s))
+dsLit (HsStringPrim _ s) = bindExprAtTopLevel (Lit (MachStr s))
dsLit (HsCharPrim _ c) = return (Lit (MachChar c))
dsLit (HsIntPrim _ i) = return (Lit (MachInt i))
dsLit (HsWordPrim _ w) = return (Lit (MachWord w))
@@ -83,7 +83,7 @@ dsLit (HsFloatPrim f) = return (Lit (MachFloat (fl_value f)))
dsLit (HsDoublePrim d) = return (Lit (MachDouble (fl_value d)))
dsLit (HsChar _ c) = return (mkCharExpr c)
-dsLit (HsString _ str) = mkStringExprFS str
+dsLit (HsString _ str) = mkStringExprFSAtTopLevel str
dsLit (HsInteger _ i _) = mkIntegerExpr i
dsLit (HsInt _ i) = do dflags <- getDynFlags
return (mkIntExpr dflags i)
@@ -366,7 +366,7 @@ matchLiterals (var:vars) ty sub_groups
= do { -- We now have to convert back to FastString. Perhaps there
-- should be separate MachBytes and MachStr constructors?
let s' = mkFastStringByteString s
- ; lit <- mkStringExprFS s'
+ ; lit <- mkStringExprFSAtTopLevel s'
; let pred = mkApps (Var eq_str) [Var var, lit]
; return (mkGuardedMatchResult pred mr) }
wrap_str_guard _ (l, _) = pprPanic "matchLiterals/wrap_str_guard" (ppr l)
diff --git a/compiler/simplCore/CSE.hs b/compiler/simplCore/CSE.hs
index b8e26b593e..6603abf14a 100644
--- a/compiler/simplCore/CSE.hs
+++ b/compiler/simplCore/CSE.hs
@@ -294,11 +294,14 @@ cseProgram binds = snd (mapAccumL (cseBind True) emptyCSEnv binds)
cseBind :: Bool -> CSEnv -> CoreBind -> (CSEnv, CoreBind)
cseBind toplevel env (NonRec b e)
- = (env2, NonRec b2 e1)
+ = (env2, NonRec b2 e2)
where
e1 = tryForCSE toplevel env e
(env1, b1) = addBinder env b
(env2, b2) = addBinding env1 b b1 e1
+ e2 -- See Note [Take care with literal strings]
+ | toplevel && exprIsLiteralString e = e
+ | otherwise = e1
cseBind _ env (Rec [(in_id, rhs)])
| noCSE in_id
@@ -402,9 +405,7 @@ the original RHS unmodified. This produces:
-}
tryForCSE :: Bool -> CSEnv -> InExpr -> OutExpr
-tryForCSE toplevel env expr
- | toplevel && exprIsLiteralString expr = expr
- -- See Note [Take care with literal strings]
+tryForCSE _toplevel env expr
| Just e <- lookupCSEnv env expr'' = mkTicks ticks e
| otherwise = expr'
-- The varToCoreExpr is needed if we have
diff --git a/compiler/simplCore/SimplUtils.hs b/compiler/simplCore/SimplUtils.hs
index 0fe262b2c7..6941f16e15 100644
--- a/compiler/simplCore/SimplUtils.hs
+++ b/compiler/simplCore/SimplUtils.hs
@@ -1030,6 +1030,12 @@ Note [Do not inline CoVars unconditionally]
Coercion variables appear inside coercions, and the RHS of a let-binding
is a term (not a coercion) so we can't necessarily inline the latter in
the former.
+
+Note [Do not inline string literals]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+We float out string literals and then common them up. So we must ensure
+that preInlineUnconditionally doesn't undo the work of FloatOut by inlining
+them right back.
-}
preInlineUnconditionally :: DynFlags -> SimplEnv -> TopLevelFlag -> InId -> InExpr -> Bool
@@ -1055,6 +1061,8 @@ preInlineUnconditionally dflags env top_lvl bndr rhs
-- See Note [pre/postInlineUnconditionally in gentle mode]
act = idInlineActivation bndr
try_once in_lam int_cxt -- There's one textual occurrence
+ -- See Note [Do not inline string literals]
+ | exprIsLiteralString rhs = False
| not in_lam = isNotTopLevel top_lvl || early_phase
| otherwise = int_cxt && canInlineInLam rhs
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index 67eb982b91..a7c9f57a32 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -381,6 +381,9 @@ data DsGblEnv
, ds_parr_bi :: PArrBuiltin -- desugarar names for '-XParallelArrays'
, ds_complete_matches :: CompleteMatchMap
-- Additional complete pattern matches
+ , ds_top_binds :: Maybe (IORef [CoreBind])
+ -- extra top-level bindings added by the desugarer, e.g. string literals and callstacks
+ -- see Note [Adding Top-Level Bindings in the Desugarer]
}
type CompleteMatchMap = UniqFM [CompleteMatch]
@@ -391,6 +394,47 @@ mkCompleteMatchMap cms = foldl' insertMatch emptyUFM cms
insertMatch :: CompleteMatchMap -> CompleteMatch -> CompleteMatchMap
insertMatch ufm c@(CompleteMatch _ t) = addToUFM_C (++) ufm t [c]
+-- Note [Adding Top-Level Bindings in the Desugarer]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Inlining can cause wasteful duplication of constant values like
+-- String literals or CallStacks. For example, if we have a function
+-- that adds a common prefix to an error message
+--
+-- myError msg = error ("some header: " ++ msg)
+--
+-- each time GHC inlines myError we will get a duplicate copy of the
+-- "some header: " literal, which can lead to a sizeable increase in
+-- binary size.
+--
+-- But why is this not already solved by FloatOut (which does indeed
+-- float such constants to the top)? The issue is that by the time
+-- FloatOut runs, myError has already been assigned a StableUnfolding
+-- that captures the string. FloatOut won't rewrite the unfolding
+-- because GHC promises to inline exactly the code the user wrote. Thus,
+-- even though we *have* floated the constant out, we are still forced
+-- to duplicate it when myError is inlined into another module, ugh!
+--
+-- Rather than changing FloatOut, we give the desugarer the ability to
+-- add new top-level bindings (stored in the new ds_top_binds field of
+-- the DsGblEnv), and pre-emptively float string literals before the
+-- unfoldings are produced.
+--
+-- We call the desugarer in two contexts: compiling an entire module, and
+-- compiling and individual expression (e.g. for ghci). In the context of
+-- an individual expression it makes no sense to add top-level bindings,
+-- so the ds_top_binds field is a Maybe.
+--
+-- The function DsUtils.bindExprAtTopLevel takes care of determining
+-- whether we can actually create a new binding, and returns a Var if
+-- able, and the original Expr otherwise.
+--
+-- The function DsMonad.withTopBinds initializes the ds_top_binds field
+-- to a fresh IORef for the duration of the wrapped action, and returns
+-- a pair of the action's result and any added top-level binders. But it
+-- only does so if we're compiling with optimizations, otherwise we don't
+-- gain anything by pre-emptively floating things and just slow down GHC.
+-- (see T1969 for an extreme example)
+
instance ContainsModule DsGblEnv where
extractModule = ds_mod