%
-% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1996
%
\section[SimplCore]{Driver for simplifying @Core@ programs}
\begin{code}
#include "HsVersions.h"
-module SimplCore (
- core2core,
-
- IdEnv(..),
- UnfoldingDetails,
- SpecialiseData(..),
- UniqFM, Unique, Bag
- ) where
-
-IMPORT_Trace
-import Outputable
-import Pretty
-
-import PlainCore
-
-import AbsUniType ( getTyConDataCons, alpha_ty, alpha_tyvar, beta_ty, beta_tyvar )
---SAVE:import ArityAnal ( arityAnalProgram )
-import Bag
-import BinderInfo ( BinderInfo) -- instances only
-import CgCompInfo ( uNFOLDING_CREATION_THRESHOLD,
- uNFOLDING_USE_THRESHOLD,
- uNFOLDING_OVERRIDE_THRESHOLD,
- uNFOLDING_CON_DISCOUNT_WEIGHT
+module SimplCore ( core2core ) where
+
+IMP_Ubiq(){-uitous-}
+IMPORT_1_3(IO(hPutStr,stderr))
+
+import AnalFBWW ( analFBWW )
+import Bag ( isEmptyBag, foldBag )
+import BinderInfo ( BinderInfo{-instance Outputable-} )
+import CmdLineOpts ( CoreToDo(..), SimplifierSwitch(..), switchIsOn,
+ opt_D_show_passes,
+ opt_D_simplifier_stats,
+ opt_D_verbose_core2core,
+ opt_DoCoreLinting,
+ opt_FoldrBuildOn,
+ opt_ReportWhyUnfoldingsDisallowed,
+ opt_ShowImportSpecs,
+ opt_LiberateCaseThreshold
)
-import CmdLineOpts
import CoreLint ( lintCoreBindings )
+import CoreSyn
+import CoreUtils ( coreExprType )
+import SimplUtils ( etaCoreExpr )
+import CoreUnfold
+import Literal ( Literal(..), literalType, mkMachInt )
+import ErrUtils ( ghcExit )
+import FiniteMap ( FiniteMap )
import FloatIn ( floatInwards )
import FloatOut ( floatOutwards )
-import Id ( getIdUnfolding,
- getIdUniType, toplevelishId,
- idWantsToBeINLINEd,
- unfoldingUnfriendlyId, isWrapperId,
- mkTemplateLocals
- IF_ATTACK_PRAGMAS(COMMA getIdStrictness)
+import FoldrBuildWW ( mkFoldrBuildWW )
+import Id ( mkSysLocal, setIdVisibility,
+ nullIdEnv, addOneToIdEnv, delOneFromIdEnv,
+ lookupIdEnv, SYN_IE(IdEnv),
+ GenId{-instance Outputable-}
+ )
+import Name ( isExported, isLocallyDefined )
+import TyCon ( TyCon )
+import PrimOp ( PrimOp(..) )
+import PrelVals ( unpackCStringId, unpackCString2Id,
+ integerZeroId, integerPlusOneId,
+ integerPlusTwoId, integerMinusOneId
)
-import IdEnv
-import IdInfo
+import Type ( maybeAppDataTyCon, getAppDataTyConExpandingDicts, maybeAppSpecDataTyConExpandingDicts )
+import TysWiredIn ( stringTy )
import LiberateCase ( liberateCase )
-import MainMonad
-import Maybes
+import MagicUFs ( MagicUnfoldingFun )
+import Outputable ( Outputable(..){-instance * (,) -} )
+import PprCore
+import PprStyle ( PprStyle(..) )
+import PprType ( GenType{-instance Outputable-}, GenTyVar{-ditto-} )
+import Pretty ( ppShow, ppAboves, ppAbove, ppCat )
import SAT ( doStaticArgs )
-import SCCauto
-import SimplEnv ( UnfoldingGuidance(..), SwitchChecker(..) ) -- instances
---ANDY:
---import SimplHaskell ( coreToHaskell )
-import SimplMonad ( zeroSimplCount, showSimplCount, TickType, SimplCount )
+import SimplMonad ( zeroSimplCount, showSimplCount, SimplCount )
import SimplPgm ( simplifyPgm )
-import SimplVar ( leastItCouldCost )
import Specialise
-import SpecTyFuns ( pprSpecErrs )
+import SpecUtils ( pprSpecErrs )
import StrictAnal ( saWwTopBinds )
-#if ! OMIT_FOLDR_BUILD
-import FoldrBuildWW
-import AnalFBWW
-#endif
-#if ! OMIT_DEFORESTER
+import TyVar ( nullTyVarEnv, GenTyVar{-instance Eq-} )
+import Unique ( integerTyConKey, ratioTyConKey, Unique{-instance Eq-} )
+import UniqSupply ( splitUniqSupply, getUnique )
+import Util ( mapAccumL, assertPanic, panic{-ToDo:rm-}, pprTrace, pprPanic )
+import SrcLoc ( noSrcLoc )
+import Constants ( tARGET_MIN_INT, tARGET_MAX_INT )
+import Bag
+import Maybes
+
+
+#ifndef OMIT_DEFORESTER
import Deforest ( deforestProgram )
import DefUtils ( deforestable )
#endif
-import TyVarEnv ( nullTyVarEnv )
-import SplitUniq
-import Unique
-import Util
+
\end{code}
\begin{code}
core2core :: [CoreToDo] -- spec of what core-to-core passes to do
- -> (GlobalSwitch->SwitchResult)-- "global" command-line info lookup fn
-> FAST_STRING -- module name (profiling only)
-> PprStyle -- printing style (for debugging only)
- -> SplitUniqSupply -- a name supply
+ -> UniqSupply -- a name supply
-> [TyCon] -- local data tycons and tycon specialisations
- -> FiniteMap TyCon [[Maybe UniType]]
- -> [PlainCoreBinding] -- input...
- -> MainIO
- ([PlainCoreBinding], -- results: program, plus...
- IdEnv UnfoldingDetails, -- unfoldings to be exported from here
+ -> FiniteMap TyCon [(Bool, [Maybe Type])]
+ -> [CoreBinding] -- input...
+ -> IO
+ ([CoreBinding], -- results: program, plus...
SpecialiseData) -- specialisation data
-core2core core_todos sw_chkr module_name ppr_style us local_tycons tycon_specs binds
- = BSCC("Core2Core")
- if null core_todos then -- very rare, I suspect...
- -- well, we still must do some renumbering
- returnMn (
- (snd (instCoreBindings (mkUniqueSupplyGrimily us) binds), nullIdEnv, init_specdata)
- )
- else
- (if do_verbose_core2core then
- writeMn stderr "VERBOSE CORE-TO-CORE:\n"
- else returnMn ()) `thenMn_`
-
- -- better do the main business
- foldl_mn do_core_pass
- (binds, us, nullIdEnv, init_specdata, zeroSimplCount)
+core2core core_todos module_name ppr_style us local_tycons tycon_specs binds
+ = -- Print heading
+ (if opt_D_verbose_core2core then
+ hPutStr stderr "VERBOSE CORE-TO-CORE:\n"
+ else return ()) >>
+
+ -- Do the main business
+ --case (splitUniqSupply us) of { (us1,us2) ->
+ foldl_mn do_core_pass
+ (binds, us, init_specdata, zeroSimplCount)
core_todos
- `thenMn` \ (processed_binds, _, inline_env, spec_data, simpl_stats) ->
-
- (if switch_is_on D_simplifier_stats
- then trace ("Simplifier Stats:\n" ++ showSimplCount simpl_stats) (returnMn ())
- else returnMn ()
- ) `thenMn_`
-
-{- LATER:
- (if do_dump_core_passes
- then trace (unlines (
- (nOfThem 78 '-'
- : "Core2Core"
- : "+------------------------------+"
- : reverse [ " " ++ take (30::Int) (what ++ repeat ' ') ++ "|"
- | what <- simpl_whats ])
- ++ ["+------------------------------+"]))
- else \x -> x) -- to the end
--}
- returnMn (processed_binds, inline_env, spec_data)
- ESCC
+ >>= \ (processed_binds, us', spec_data, simpl_stats) ->
+
+ -- Do the final tidy-up
+ let
+ final_binds = core_linter "TidyCorePgm" True $
+ tidyCorePgm module_name us' processed_binds
+ in
+
+ -- Report statistics
+ (if opt_D_simplifier_stats then
+ hPutStr stderr ("\nSimplifier Stats:\n") >>
+ hPutStr stderr (showSimplCount simpl_stats) >>
+ hPutStr stderr "\n"
+ else return ()) >>
+
+ --
+ return (final_binds, spec_data) --}
where
+-- (us1, us2) = splitUniqSupply us
init_specdata = initSpecData local_tycons tycon_specs
- switch_is_on = switchIsOn sw_chkr
-
- do_dump_core_passes = switch_is_on D_dump_core_passes -- an Andy flag
- do_verbose_core2core = switch_is_on D_verbose_core2core
-
- lib_case_threshold -- ToDo: HACK HACK HACK : FIX ME FIX ME FIX ME
- -- Use 4x a known threshold
- = case (intSwitchSet sw_chkr UnfoldingOverrideThreshold) of
- Nothing -> 4 * uNFOLDING_USE_THRESHOLD
- Just xx -> 4 * xx
-
-------------
- core_linter = if switch_is_on DoCoreLinting
- then lintCoreBindings ppr_style
- else ( \ whodunnit spec_done binds -> binds )
+ core_linter what spec_done
+ = if opt_DoCoreLinting
+ then (if opt_D_show_passes then
+ trace ("\n*** Core Lint result of " ++ what)
+ else id
+ )
+ lintCoreBindings ppr_style what spec_done
+ else id
--------------
- do_core_pass info@(binds, us, inline_env, spec_data, simpl_stats) to_do
- = let
- (us1, us2) = splitUniqSupply us
- in
+ do_core_pass info@(binds, us, spec_data, simpl_stats) to_do =
+-- let
+-- (us1, us2) = splitUniqSupply us
+-- in
+ case (splitUniqSupply us) of
+ (us1,us2) ->
case to_do of
CoreDoSimplify simpl_sw_chkr
- -> BSCC("CoreSimplify")
- case (simplifyPgm binds sw_chkr simpl_sw_chkr simpl_stats us1) of
+ -> _scc_ "CoreSimplify"
+ begin_pass ("Simplify" ++ if switchIsOn simpl_sw_chkr SimplDoFoldrBuild
+ then " (foldr/build)" else "") >>
+ case (simplifyPgm binds simpl_sw_chkr simpl_stats us1) of
(p, it_cnt, simpl_stats2)
- -> end_pass us2 p inline_env spec_data simpl_stats2 ("Simplify (" ++ show it_cnt ++ ")")
- ESCC
+ -> end_pass False us2 p spec_data simpl_stats2
+ ("Simplify (" ++ show it_cnt ++ ")"
+ ++ if switchIsOn simpl_sw_chkr SimplDoFoldrBuild
+ then " foldr/build" else "")
CoreDoFoldrBuildWorkerWrapper
-#if OMIT_FOLDR_BUILD
- -> error "ERROR: CoreDoFoldrBuildWorkerWrapper: not built into compiler\n"
-#else
- -> BSCC("CoreDoFoldrBuildWorkerWrapper")
- end_pass us2 (mkFoldrBuildWW switch_is_on us1 binds) inline_env spec_data simpl_stats "FBWW"
- ESCC
-#endif
+ -> _scc_ "CoreDoFoldrBuildWorkerWrapper"
+ begin_pass "FBWW" >>
+ case (mkFoldrBuildWW us1 binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "FBWW" }
CoreDoFoldrBuildWWAnal
-#if OMIT_FOLDR_BUILD
- -> error "ERROR: CoreDoFoldrBuildWWAnal: not built into compiler\n"
-#else
- -> BSCC("CoreDoFoldrBuildWWAnal")
- end_pass us2 (analFBWW switch_is_on binds) inline_env spec_data simpl_stats "AnalFBWW"
- ESCC
-#endif
+ -> _scc_ "CoreDoFoldrBuildWWAnal"
+ begin_pass "AnalFBWW" >>
+ case (analFBWW binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "AnalFBWW" }
CoreLiberateCase
- -> BSCC("LiberateCase")
- case (liberateCase lib_case_threshold binds) of { binds2 ->
- end_pass us2 binds2 inline_env spec_data simpl_stats "LiberateCase"
- }
- ESCC
-
- CoreDoCalcInlinings1 -- avoid inlinings w/ cost-centres
- -> BSCC("CoreInlinings1")
- case (calcInlinings False sw_chkr inline_env binds) of { inline_env2 ->
- end_pass us2 binds inline_env2 spec_data simpl_stats "Calc Inlinings"
- } ESCC
-
- CoreDoCalcInlinings2 -- allow inlinings w/ cost-centres
- -> BSCC("CoreInlinings2")
- case (calcInlinings True sw_chkr inline_env binds) of { inline_env2 ->
- end_pass us2 binds inline_env2 spec_data simpl_stats "Calc Inlinings"
- } ESCC
+ -> _scc_ "LiberateCase"
+ begin_pass "LiberateCase" >>
+ case (liberateCase opt_LiberateCaseThreshold binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "LiberateCase" }
CoreDoFloatInwards
- -> BSCC("FloatInwards")
- end_pass us2 (floatInwards binds) inline_env spec_data simpl_stats "FloatIn"
- ESCC
+ -> _scc_ "FloatInwards"
+ begin_pass "FloatIn" >>
+ case (floatInwards binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "FloatIn" }
CoreDoFullLaziness
- -> BSCC("CoreFloating")
- case (floatOutwards switch_is_on us1 binds) of { p ->
- end_pass us2 p inline_env spec_data simpl_stats "FloatOut"
- } ESCC
-
- CoreDoPrintCore ->
- let
- printed = ppShow 80 (ppr ppr_style binds)
- strict [] a = a
- strict (s:ss) a | ord s == 0 = error "0 in output string"
- | otherwise = strict ss a
- in
- end_pass us2 (strict printed (trace ("PrintCore:\n" ++ printed) binds)) inline_env spec_data simpl_stats "Print"
-
-{- ANDY:
- CoreDoHaskPrint ->
- let
- printed = coreToHaskell binds
- strict [] a = a
- strict (s:ss) a | ord s == 0 = error "0 in output string"
- | otherwise = strict ss a
- in
- strict printed (trace ("PrintCore:\n" ++ printed) binds), inline_env, spec_data, simpl_stats, "PrintHask"
--}
+ -> _scc_ "CoreFloating"
+ begin_pass "FloatOut" >>
+ case (floatOutwards us1 binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "FloatOut" }
CoreDoStaticArgs
- -> BSCC("CoreStaticArgs")
- end_pass us2 (doStaticArgs binds us1) inline_env spec_data simpl_stats "SAT"
+ -> _scc_ "CoreStaticArgs"
+ begin_pass "StaticArgs" >>
+ case (doStaticArgs binds us1) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "StaticArgs" }
-- Binds really should be dependency-analysed for static-
-- arg transformation... Not to worry, they probably are.
-- (I don't think it *dies* if they aren't [WDP 94/04/15])
- ESCC
CoreDoStrictness
- -> BSCC("CoreStranal")
- end_pass us2 (saWwTopBinds us1 switch_is_on binds) inline_env spec_data simpl_stats "StrAnal"
- ESCC
+ -> _scc_ "CoreStranal"
+ begin_pass "StrAnal" >>
+ case (saWwTopBinds us1 binds) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "StrAnal" }
CoreDoSpecialising
- -> BSCC("Specialise")
- case (specProgram switch_is_on us1 binds spec_data) of {
+ -> _scc_ "Specialise"
+ begin_pass "Specialise" >>
+ case (specProgram us1 binds spec_data) of {
(p, spec_data2@(SpecData _ spec_noerrs _ _ _
spec_errs spec_warn spec_tyerrs)) ->
-- if we got errors, we die straight away
- (if not spec_noerrs ||
- (switch_is_on ShowImportSpecs && not (isEmptyBag spec_warn)) then
- writeMn stderr (ppShow 1000 {-pprCols-}
- (pprSpecErrs PprForUser spec_errs spec_warn spec_tyerrs))
- `thenMn_` writeMn stderr "\n"
+ (if not spec_noerrs ||
+ (opt_ShowImportSpecs && not (isEmptyBag spec_warn)) then
+ hPutStr stderr (ppShow 1000 {-pprCols-}
+ (pprSpecErrs module_name spec_errs spec_warn spec_tyerrs))
+ >> hPutStr stderr "\n"
else
- returnMn ()) `thenMn_`
+ return ()) >>
(if not spec_noerrs then -- Stop here if specialisation errors occured
- exitMn 1
+ ghcExit 1
else
- returnMn ()) `thenMn_`
+ return ()) >>
- end_pass us2 p inline_env spec_data2 simpl_stats "Specialise"
+ end_pass False us2 p spec_data2 simpl_stats "Specialise"
}
- ESCC
- CoreDoDeforest
+ CoreDoDeforest
#if OMIT_DEFORESTER
-> error "ERROR: CoreDoDeforest: not built into compiler\n"
#else
- -> BSCC("Deforestation")
- case (deforestProgram sw_chkr binds us1) of { binds ->
- end_pass us2 binds inline_env spec_data simpl_stats "Deforestation"
- }
- ESCC
+ -> _scc_ "Deforestation"
+ begin_pass "Deforestation" >>
+ case (deforestProgram binds us1) of { binds2 ->
+ end_pass False us2 binds2 spec_data simpl_stats "Deforestation" }
#endif
-
- CoreDoAutoCostCentres
- -> BSCC("AutoSCCs")
- end_pass us2 (addAutoCostCentres sw_chkr module_name binds) inline_env spec_data simpl_stats "AutoSCCs"
- ESCC
+
+ CoreDoPrintCore -- print result of last pass
+ -> end_pass True us2 binds spec_data simpl_stats "Print"
-------------------------------------------------
- end_pass us2 binds2 inline_env2
+ begin_pass
+ = if opt_D_show_passes
+ then \ what -> hPutStr stderr ("*** Core2Core: "++what++"\n")
+ else \ what -> return ()
+
+ end_pass print us2 binds2
spec_data2@(SpecData spec_done _ _ _ _ _ _ _)
simpl_stats2 what
= -- report verbosely, if required
- (if do_verbose_core2core then
- writeMn stderr ("\n*** "++what++":\n")
- `thenMn_`
- writeMn stderr (ppShow 1000
- (ppAboves (map (pprPlainCoreBinding ppr_style) binds2)))
- `thenMn_`
- writeMn stderr "\n"
+ (if (opt_D_verbose_core2core && not print) ||
+ (print && not opt_D_verbose_core2core)
+ then
+ hPutStr stderr ("\n*** "++what++":\n")
+ >>
+ hPutStr stderr (ppShow 1000
+ (ppAboves (map (pprCoreBinding ppr_style) binds2)))
+ >>
+ hPutStr stderr "\n"
else
- returnMn ()) `thenMn_`
+ return ()) >>
let
linted_binds = core_linter what spec_done binds2
in
- returnMn
+ return
(linted_binds, -- processed binds, possibly run thru CoreLint
us2, -- UniqueSupply for the next guy
- inline_env2, -- possibly-updated inline env
spec_data2, -- possibly-updated specialisation info
simpl_stats2 -- accumulated simplifier stats
)
-- here so it can be inlined...
-foldl_mn f z [] = returnMn z
-foldl_mn f z (x:xs) = f z x `thenMn` \ zz ->
- foldl_mn f zz xs
+foldl_mn f z [] = return z
+foldl_mn f z (x:xs) = f z x >>= \ zz ->
+ foldl_mn f zz xs
\end{code}
---- ToDo: maybe move elsewhere ---
-For top-level, exported binders that either (a)~have been INLINEd by
-the programmer or (b)~are sufficiently ``simple'' that they should be
-inlined, we want to record this info in a suitable IdEnv.
-But: if something has a ``wrapper unfolding,'' we do NOT automatically
-give it a regular unfolding (exception below). We usually assume its
-worker will get a ``regular'' unfolding. We can then treat these two
-levels of unfolding separately (we tend to be very friendly towards
-wrapper unfoldings, for example), giving more fine-tuned control.
+%************************************************************************
+%* *
+\subsection[SimplCore-indirections]{Eliminating indirections in Core code, and globalising}
+%* *
+%************************************************************************
+
+Several tasks are done by @tidyCorePgm@
+
+1. Eliminate indirections. The point here is to transform
+ x_local = E
+ x_exported = x_local
+ ==>
+ x_exported = E
+
+2. Make certain top-level bindings into Globals. The point is that
+ Global things get externally-visible labels at code generation
+ time
+
+3. Make the representation of NoRep literals explicit, and
+ float their bindings to the top level
+
+4. Convert
+ case x of {...; x' -> ...x'...}
+ ==>
+ case x of {...; _ -> ...x... }
+ See notes in SimplCase.lhs, near simplDefault for the reasoning here.
+
+5. *Mangle* cases involving fork# and par# in the discriminant. The
+ original templates for these primops (see @PrelVals.lhs@) constructed
+ case expressions with boolean results solely to fool the strictness
+ analyzer, the simplifier, and anyone else who might want to fool with
+ the evaluation order. At this point in the compiler our evaluation
+ order is safe. Therefore, we convert expressions of the form:
+
+ case par# e of
+ True -> rhs
+ False -> parError#
+ ==>
+ case par# e of
+ _ -> rhs
+
+6. Eliminate polymorphic case expressions. We can't generate code for them yet.
+
+7. Do eta reduction for lambda abstractions appearing in:
+ - the RHS of case alternatives
+ - the body of a let
+ These will otherwise turn into local bindings during Core->STG; better to
+ nuke them if possible. (In general the simplifier does eta expansion not
+ eta reduction, up to this point.)
+
+
+Eliminate indirections
+~~~~~~~~~~~~~~~~~~~~~~
+In @elimIndirections@, we look for things at the top-level of the form...
+\begin{verbatim}
+ x_local = ....
+ x_exported = x_local
+\end{verbatim}
+In cases we find like this, we go {\em backwards} and replace
+\tr{x_local} with \tr{x_exported}. This save a gratuitous jump
+(from \tr{x_exported} to \tr{x_local}), and makes strictness
+information propagate better.
+
+We rely on prior eta reduction to simplify things like
+\begin{verbatim}
+ x_exported = /\ tyvars -> x_local tyvars
+==>
+ x_exported = x_local
+\end{verbatim}
+
+If more than one exported thing is equal to a local thing (i.e., the
+local thing really is shared), then we do one only:
+\begin{verbatim}
+ x_local = ....
+ x_exported1 = x_local
+ x_exported2 = x_local
+==>
+ x_exported1 = ....
+
+ x_exported2 = x_exported1
+\end{verbatim}
+
+There's a possibility of leaving unchanged something like this:
+\begin{verbatim}
+ x_local = ....
+ x_exported1 = x_local Int
+\end{verbatim}
+By the time we've thrown away the types in STG land this
+could be eliminated. But I don't think it's very common
+and it's dangerous to do this fiddling in STG land
+because we might elminate a binding that's mentioned in the
+unfolding for something.
+
+General Strategy: first collect the info; then make a \tr{Id -> Id} mapping.
+Then blast the whole program (LHSs as well as RHSs) with it.
-The exception is: If the ``regular unfolding'' mentions no other
-global Ids (i.e., it's all PrimOps and cases and local Ids) then we
-assume it must be really good and we take it anyway.
-We also need to check that everything in the RHS (values and types)
-will be visible on the other side of an interface, too.
\begin{code}
-calcInlinings :: Bool -- True => inlinings with _scc_s are OK
- -> (GlobalSwitch -> SwitchResult)
- -> IdEnv UnfoldingDetails
- -> [PlainCoreBinding]
- -> IdEnv UnfoldingDetails
-
-calcInlinings scc_s_OK sw_chkr inline_env_so_far top_binds
- = let
- result = foldl calci inline_env_so_far top_binds
- in
- --pprTrace "inline env:\n" (ppAboves (map pp_item (getIdEnvMapping result)))
- result
+tidyCorePgm :: Module -> UniqSupply -> [CoreBinding] -> [CoreBinding]
+
+tidyCorePgm mod us binds_in
+ = initTM mod indirection_env us $
+ tidyTopBindings (catMaybes reduced_binds) `thenTM` \ binds ->
+ returnTM (bagToList binds)
where
- pp_item (binder, details)
- = ppCat [ppr PprDebug binder, ppStr "=>", pp_det details]
- where
- pp_det NoUnfoldingDetails = ppStr "_N_"
- pp_det (IWantToBeINLINEd _) = ppStr "INLINE"
- pp_det (GeneralForm _ _ expr guide)
- = ppAbove (ppr PprDebug guide) (ppr PprDebug expr)
- pp_det other = ppStr "???"
-
- ------------
- switch_is_on = switchIsOn sw_chkr
-
- my_trace = if (switch_is_on ReportWhyUnfoldingsDisallowed)
- then trace
- else \ msg stuff -> stuff
-
- (unfolding_creation_threshold, explicit_creation_threshold)
- = case (intSwitchSet sw_chkr UnfoldingCreationThreshold) of
- Nothing -> (uNFOLDING_CREATION_THRESHOLD, False)
- Just xx -> (xx, True)
-
- unfold_use_threshold
- = case (intSwitchSet sw_chkr UnfoldingUseThreshold) of
- Nothing -> uNFOLDING_USE_THRESHOLD
- Just xx -> xx
-
- unfold_override_threshold
- = case (intSwitchSet sw_chkr UnfoldingOverrideThreshold) of
- Nothing -> uNFOLDING_OVERRIDE_THRESHOLD
- Just xx -> xx
-
- con_discount_weight = uNFOLDING_CON_DISCOUNT_WEIGHT
-
- calci inline_env (CoRec pairs)
- = foldl (calc True{-recursive-}) inline_env pairs
-
- calci inline_env bind@(CoNonRec binder rhs)
- = calc False{-not recursive-} inline_env (binder, rhs)
-
- ---------------------------------------
-
- calc is_recursive inline_env (binder, rhs)
- | not (toplevelishId binder)
- = --pprTrace "giving up on not top-level:" (ppr PprDebug binder)
- ignominious_defeat
-
- | rhs_mentions_an_unmentionable
- || (not explicit_INLINE_requested
- && (guidance_says_don't || guidance_size_just_too_big))
- = let
- my_my_trace
- = if explicit_INLINE_requested
- && not (isWrapperId binder) -- these always claim to be INLINEd
- && not have_inlining_already
- then trace -- we'd better have a look...
- else my_trace
-
- which = if scc_s_OK then " (late):" else " (early):"
- in
- --pprTrace "giving up on size:" (ppCat [ppr PprDebug binder, ppr PprDebug [rhs_mentions_an_unmentionable, explicit_INLINE_requested, guidance_says_don't, guidance_size_just_too_big]]) (
- my_my_trace ("unfolding disallowed for"++which++(ppShow 80 (ppr PprDebug binder))) (
- ignominious_defeat
- )
- --)
-
- | rhs `isWrapperFor` binder
- -- Don't add an explicit "unfolding"; let the worker/wrapper
- -- stuff do its thing. INLINE things don't get w/w'd, so
- -- they will be OK.
- = --pprTrace "giving up on isWrapperFor:" (ppr PprDebug binder)
- ignominious_defeat
-
-#if ! OMIT_DEFORESTER
- -- For the deforester: bypass the barbed wire for recursive
- -- functions that want to be inlined and are tagged deforestable
- -- by the user, allowing these things to be communicated
- -- across module boundaries.
-
- | is_recursive &&
- explicit_INLINE_requested &&
- deforestable binder &&
- scc_s_OK -- hack, only get them in
- -- calc_inlinings2
- = glorious_success UnfoldAlways
-#endif
-
- | is_recursive && not rhs_looks_like_a_data_val_to_me
- -- The only recursive defns we are prepared to tolerate at the
- -- moment is top-level very-obviously-a-data-value ones.
- -- We *need* these for dictionaries to be exported!
- = --pprTrace "giving up on rec:" (ppr PprDebug binder)
- ignominious_defeat
-
- -- Not really interested unless it's exported, but doing it
- -- this way (not worrying about export-ness) gets us all the
- -- workers/specs, etc., too; which we will need for generating
- -- interfaces. We are also not interested if this binder is
- -- in the environment we already have (perhaps from a previous
- -- run of calcInlinings -- "earlier" is presumed to mean
- -- "better").
-
- | explicit_INLINE_requested
- = glorious_success UnfoldAlways
-
- | otherwise
- = glorious_success guidance
-
- where
- guidance
- = calcUnfoldingGuidance scc_s_OK max_out_threshold rhs
- where
- max_out_threshold = if explicit_INLINE_requested
- then 100000 -- you asked for it, you got it
- else unfolding_creation_threshold
-
- guidance_says_don't = case guidance of { UnfoldNever -> True; _ -> False }
-
- guidance_size
- = case guidance of
- UnfoldAlways -> 0 -- *extremely* small
- EssentialUnfolding -> 0 -- ditto
- UnfoldIfGoodArgs _ _ _ size -> size
-
- guidance_size_just_too_big
- -- Does the guidance suggest that this unfolding will
- -- be of no use *no matter* the arguments given to it?
- -- Could be more sophisticated...
- = case guidance of
- UnfoldNever -> False -- debugging only (ToDo:rm)
- UnfoldAlways -> False
- EssentialUnfolding -> False
- UnfoldIfGoodArgs _ no_val_args arg_info_vec size
-
- -> if explicit_creation_threshold then
- False -- user set threshold; don't second-guess...
-
- else if no_val_args == 0 && rhs_looks_like_a_data_val_to_me then
- False -- probably a data value; we'd like the
- -- other guy to see the value, even if
- -- s/he doesn't unfold it.
- else
- let
- cost
- = leastItCouldCost con_discount_weight size no_val_args
- arg_info_vec rhs_arg_tys
- in
--- (if (unfold_use_threshold < cost) then (pprTrace "cost:" (ppInt cost)) else \x->x ) (
- unfold_use_threshold < cost
--- )
-
-
- rhs_arg_tys
- = let
- (_, val_binders, _) = digForLambdas rhs
- in
- map getIdUniType val_binders
-
- rhs_looks_like_a_data_val_to_me
- = let
- (_,val_binders,body) = digForLambdas rhs
- in
- case (val_binders, body) of
- ([], CoCon _ _ _) -> True
- other -> False
-
- (mentioned_ids, _, _, mentions_litlit)
- = mentionedInUnfolding (\x -> x) rhs
-
- rhs_mentions_an_unmentionable
- = --pprTrace "mentions:" (ppCat [ppr PprDebug binder, ppr PprDebug [(i,unfoldingUnfriendlyId i) | i <- mentioned_ids ]]) (
- any unfoldingUnfriendlyId mentioned_ids
- || mentions_litlit
- --)
- -- ToDo: probably need to chk tycons/classes...
-
- mentions_no_other_ids = null mentioned_ids
-
- explicit_INLINE_requested
- -- did it come from a user {-# INLINE ... #-}?
- -- (Warning: must avoid including wrappers.)
- = idWantsToBeINLINEd binder
- && not (rhs `isWrapperFor` binder)
-
- have_inlining_already = maybeToBool (lookupIdEnv inline_env binder)
-
- ignominious_defeat = inline_env -- just give back what we got
-
- {-
- "glorious_success" is ours if we've found a suitable unfolding.
-
- But we check for a couple of fine points.
-
- (1) If this Id already has an inlining in the inline_env,
- we don't automatically take it -- the earlier one is
- "likely" to be better.
-
- But if the new one doesn't mention any other global
- Ids, and it's pretty small (< UnfoldingOverrideThreshold),
- then we take the chance that the new one *is* better.
-
- (2) If we have an Id w/ a worker/wrapper split (with
- an unfolding for the wrapper), we tend to want to keep
- it -- and *nuke* any inlining that we conjured up
- earlier.
-
- But, again, if this unfolding doesn't mention any
- other global Ids (and small enough), then it is
- probably better than the worker/wrappery, so we take
- it.
- -}
- glorious_success guidance
- = let
- new_env = addOneToIdEnv inline_env binder (mkUnfolding guidance rhs)
-
- foldr_building = switch_is_on FoldrBuildOn
- in
- if (not have_inlining_already) then
- -- Not in env: we take it no matter what
- -- NB: we could check for worker/wrapper-ness,
- -- but the truth is we probably haven't run
- -- the strictness analyser yet.
- new_env
-
- else if explicit_INLINE_requested then
- -- If it was a user INLINE, then we know it's already
- -- in the inline_env; we stick with what we already
- -- have.
- --pprTrace "giving up on INLINE:" (ppr PprDebug binder)
- ignominious_defeat
-
- else if isWrapperId binder then
- -- It's in the env, but we have since worker-wrapperised;
- -- we either take this new one (because it's so good),
- -- or we *undo* the one in the inline_env, so the
- -- wrapper-inlining will take over.
-
- if mentions_no_other_ids {- *** && size <= unfold_override_threshold -} then
- new_env
- else
- delOneFromIdEnv inline_env binder
-
- else
- -- It's in the env, nothing to do w/ worker wrapper;
- -- we'll take it if it is better.
-
- if not foldr_building -- ANDY hates us... (see below)
- && mentions_no_other_ids
- && guidance_size <= unfold_override_threshold then
- new_env
- else
- --pprTrace "giving up on final hurdle:" (ppCat [ppr PprDebug binder, ppInt guidance_size, ppInt unfold_override_threshold])
- ignominious_defeat -- and at the last hurdle, too!
+ (indirection_env, reduced_binds) = mapAccumL try_bind nullIdEnv binds_in
+
+ try_bind :: IdEnv Id -> CoreBinding -> (IdEnv Id, Maybe CoreBinding)
+ try_bind env_so_far
+ (NonRec exported_binder (Var local_id))
+ | isExported exported_binder && -- Only if this is exported
+ isLocallyDefined local_id && -- Only if this one is defined in this
+ not (isExported local_id) && -- module, so that we *can* change its
+ -- binding to be the exported thing!
+ not (maybeToBool (lookupIdEnv env_so_far local_id))
+ -- Only if not already substituted for
+ = (addOneToIdEnv env_so_far local_id exported_binder, Nothing)
+
+ try_bind env_so_far bind
+ = (env_so_far, Just bind)
+\end{code}
+
+Top level bindings
+~~~~~~~~~~~~~~~~~~
+\begin{code}
+tidyTopBindings [] = returnTM emptyBag
+tidyTopBindings (b:bs)
+ = tidyTopBinding b $
+ tidyTopBindings bs
+
+tidyTopBinding :: CoreBinding
+ -> TidyM (Bag CoreBinding)
+ -> TidyM (Bag CoreBinding)
+
+tidyTopBinding (NonRec bndr rhs) thing_inside
+ = getFloats (tidyCoreExpr rhs) `thenTM` \ (rhs',floats) ->
+ mungeTopBinder bndr $ \ bndr' ->
+ thing_inside `thenTM` \ binds ->
+ returnTM ((floats `snocBag` NonRec bndr' rhs') `unionBags` binds)
+
+tidyTopBinding (Rec pairs) thing_inside
+ = mungeTopBinders binders $ \ binders' ->
+ getFloats (mapTM tidyCoreExpr rhss) `thenTM` \ (rhss', floats) ->
+ thing_inside `thenTM` \ binds_inside ->
+ returnTM ((floats `snocBag` Rec (binders' `zip` rhss')) `unionBags` binds_inside)
+ where
+ (binders, rhss) = unzip pairs
+\end{code}
+
+
+Local Bindings
+~~~~~~~~~~~~~~
+\begin{code}
+tidyCoreBinding (NonRec bndr rhs)
+ = tidyCoreExpr rhs `thenTM` \ rhs' ->
+ returnTM (NonRec bndr rhs')
+
+tidyCoreBinding (Rec pairs)
+ = mapTM do_one pairs `thenTM` \ pairs' ->
+ returnTM (Rec pairs')
+ where
+ do_one (bndr,rhs) = tidyCoreExpr rhs `thenTM` \ rhs' ->
+ returnTM (bndr, rhs')
+
+\end{code}
+
+
+Expressions
+~~~~~~~~~~~
+\begin{code}
+tidyCoreExpr (Var v) = lookupTM v `thenTM` \ v' ->
+ returnTM (Var v')
+
+tidyCoreExpr (Lit lit)
+ = litToRep lit `thenTM` \ (_, lit_expr) ->
+ returnTM lit_expr
+
+tidyCoreExpr (App fun arg)
+ = tidyCoreExpr fun `thenTM` \ fun' ->
+ tidyCoreArg arg `thenTM` \ arg' ->
+ returnTM (App fun' arg')
+
+tidyCoreExpr (Con con args)
+ = mapTM tidyCoreArg args `thenTM` \ args' ->
+ returnTM (Con con args')
+
+tidyCoreExpr (Prim prim args)
+ = mapTM tidyCoreArg args `thenTM` \ args' ->
+ returnTM (Prim prim args')
+
+tidyCoreExpr (Lam bndr body)
+ = tidyCoreExpr body `thenTM` \ body' ->
+ returnTM (Lam bndr body')
+
+tidyCoreExpr (Let bind body)
+ = tidyCoreBinding bind `thenTM` \ bind' ->
+ tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (Let bind' body')
+
+tidyCoreExpr (SCC cc body)
+ = tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (SCC cc body')
+
+tidyCoreExpr (Coerce coercion ty body)
+ = tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (Coerce coercion ty body')
+
+-- Wierd case for par, seq, fork etc. See notes above.
+tidyCoreExpr (Case scrut@(Prim op args) (PrimAlts _ (BindDefault binder rhs)))
+ | funnyParallelOp op
+ = tidyCoreExpr scrut `thenTM` \ scrut' ->
+ tidyCoreExprEta rhs `thenTM` \ rhs' ->
+ returnTM (Case scrut' (PrimAlts [] (BindDefault binder rhs')))
+
+-- Eliminate polymorphic case, for which we can't generate code just yet
+tidyCoreExpr (Case scrut (AlgAlts [] (BindDefault deflt_bndr rhs)))
+ | not (maybeToBool (maybeAppSpecDataTyConExpandingDicts (coreExprType scrut)))
+ = pprTrace "Warning: discarding polymorphic case:" (ppr PprDebug scrut) $
+ case scrut of
+ Var v -> extendEnvTM deflt_bndr v (tidyCoreExpr rhs)
+ other -> tidyCoreExpr (Let (NonRec deflt_bndr scrut) rhs)
+
+tidyCoreExpr (Case scrut alts)
+ = tidyCoreExpr scrut `thenTM` \ scrut' ->
+ tidy_alts scrut' alts `thenTM` \ alts' ->
+ returnTM (Case scrut' alts')
+ where
+ tidy_alts scrut (AlgAlts alts deflt)
+ = mapTM tidy_alg_alt alts `thenTM` \ alts' ->
+ tidy_deflt scrut deflt `thenTM` \ deflt' ->
+ returnTM (AlgAlts alts' deflt')
+
+ tidy_alts scrut (PrimAlts alts deflt)
+ = mapTM tidy_prim_alt alts `thenTM` \ alts' ->
+ tidy_deflt scrut deflt `thenTM` \ deflt' ->
+ returnTM (PrimAlts alts' deflt')
+
+ tidy_alg_alt (con,bndrs,rhs) = tidyCoreExprEta rhs `thenTM` \ rhs' ->
+ returnTM (con,bndrs,rhs')
+
+ tidy_prim_alt (lit,rhs) = tidyCoreExprEta rhs `thenTM` \ rhs' ->
+ returnTM (lit,rhs')
+
+ -- We convert case x of {...; x' -> ...x'...}
+ -- to
+ -- case x of {...; _ -> ...x... }
+ --
+ -- See notes in SimplCase.lhs, near simplDefault for the reasoning.
+ -- It's quite easily done: simply extend the environment to bind the
+ -- default binder to the scrutinee.
+
+ tidy_deflt scrut NoDefault = returnTM NoDefault
+ tidy_deflt scrut (BindDefault bndr rhs)
+ = extend_env (tidyCoreExprEta rhs) `thenTM` \ rhs' ->
+ returnTM (BindDefault bndr rhs')
+ where
+ extend_env = case scrut of
+ Var v -> extendEnvTM bndr v
+ other -> \x -> x
+
+tidyCoreExprEta e = tidyCoreExpr e `thenTM` \ e' ->
+ returnTM (etaCoreExpr e')
+\end{code}
+
+Arguments
+~~~~~~~~~
+\begin{code}
+tidyCoreArg :: CoreArg -> TidyM CoreArg
+
+tidyCoreArg (VarArg v)
+ = lookupTM v `thenTM` \ v' ->
+ returnTM (VarArg v')
+
+tidyCoreArg (LitArg lit)
+ = litToRep lit `thenTM` \ (lit_ty, lit_expr) ->
+ case lit_expr of
+ Var v -> returnTM (VarArg v)
+ Lit l -> returnTM (LitArg l)
+ other -> addTopFloat lit_ty lit_expr `thenTM` \ v ->
+ returnTM (VarArg v)
+
+tidyCoreArg (TyArg ty) = returnTM (TyArg ty)
+tidyCoreArg (UsageArg u) = returnTM (UsageArg u)
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection[coreToStg-lits]{Converting literals}
+%* *
+%************************************************************************
+
+Literals: the NoRep kind need to be de-no-rep'd.
+We always replace them with a simple variable, and float a suitable
+binding out to the top level.
+
+\begin{code}
+
+litToRep :: Literal -> TidyM (Type, CoreExpr)
+
+litToRep (NoRepStr s)
+ = returnTM (stringTy, rhs)
+ where
+ rhs = if (any is_NUL (_UNPK_ s))
+
+ then -- Must cater for NULs in literal string
+ mkGenApp (Var unpackCString2Id)
+ [LitArg (MachStr s),
+ LitArg (mkMachInt (toInteger (_LENGTH_ s)))]
+
+ else -- No NULs in the string
+ App (Var unpackCStringId) (LitArg (MachStr s))
+
+ is_NUL c = c == '\0'
+\end{code}
+
+If an Integer is small enough (Haskell implementations must support
+Ints in the range $[-2^29+1, 2^29-1]$), wrap it up in @int2Integer@;
+otherwise, wrap with @litString2Integer@.
+
+\begin{code}
+litToRep (NoRepInteger i integer_ty)
+ = returnTM (integer_ty, rhs)
+ where
+ rhs | i == 0 = Var integerZeroId -- Extremely convenient to look out for
+ | i == 1 = Var integerPlusOneId -- a few very common Integer literals!
+ | i == 2 = Var integerPlusTwoId
+ | i == (-1) = Var integerMinusOneId
+
+ | i > tARGET_MIN_INT && -- Small enough, so start from an Int
+ i < tARGET_MAX_INT
+ = Prim Int2IntegerOp [LitArg (mkMachInt i)]
+
+ | otherwise -- Big, so start from a string
+ = Prim Addr2IntegerOp [LitArg (MachStr (_PK_ (show i)))]
+
+
+litToRep (NoRepRational r rational_ty)
+ = tidyCoreArg (LitArg (NoRepInteger (numerator r) integer_ty)) `thenTM` \ num_arg ->
+ tidyCoreArg (LitArg (NoRepInteger (denominator r) integer_ty)) `thenTM` \ denom_arg ->
+ returnTM (rational_ty, Con ratio_data_con [TyArg integer_ty, num_arg, denom_arg])
+ where
+ (ratio_data_con, integer_ty)
+ = case (maybeAppDataTyCon rational_ty) of
+ Just (tycon, [i_ty], [con])
+ -> ASSERT(is_integer_ty i_ty && uniqueOf tycon == ratioTyConKey)
+ (con, i_ty)
+
+ _ -> (panic "ratio_data_con", panic "integer_ty")
+
+ is_integer_ty ty
+ = case (maybeAppDataTyCon ty) of
+ Just (tycon, [], _) -> uniqueOf tycon == integerTyConKey
+ _ -> False
+
+litToRep other_lit = returnTM (literalType other_lit, Lit other_lit)
\end{code}
-ANDY, on the hatred of the check above; why obliterate it? Consider
+\begin{code}
+funnyParallelOp SeqOp = True
+funnyParallelOp ParOp = True
+funnyParallelOp ForkOp = True
+funnyParallelOp _ = False
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection{The monad}
+%* *
+%************************************************************************
+
+\begin{code}
+type TidyM a = Module
+ -> IdEnv Id
+ -> (UniqSupply, Bag CoreBinding)
+ -> (a, (UniqSupply, Bag CoreBinding))
+
+initTM mod env us m
+ = case m mod env (us,emptyBag) of
+ (result, (us',floats)) -> result
+
+returnTM v mod env usf = (v, usf)
+thenTM m k mod env usf = case m mod env usf of
+ (r, usf') -> k r mod env usf'
+
+mapTM f [] = returnTM []
+mapTM f (x:xs) = f x `thenTM` \ r ->
+ mapTM f xs `thenTM` \ rs ->
+ returnTM (r:rs)
+\end{code}
+
+
+\begin{code}
+getFloats :: TidyM a -> TidyM (a, Bag CoreBinding)
+getFloats m mod env (us,floats)
+ = case m mod env (us,emptyBag) of
+ (r, (us',floats')) -> ((r, floats'), (us',floats))
+
+
+-- Need to extend the environment when we munge a binder, so that occurrences
+-- of the binder will print the correct way (i.e. as a global not a local)
+mungeTopBinder :: Id -> (Id -> TidyM a) -> TidyM a
+mungeTopBinder id thing_inside mod env usf
+ = case lookupIdEnv env id of
+ Just global -> thing_inside global mod env usf
+ Nothing -> thing_inside new_global mod new_env usf
+ where
+ new_env = addOneToIdEnv env id new_global
+ new_global = setIdVisibility mod id
+
+mungeTopBinders [] k = k []
+mungeTopBinders (b:bs) k = mungeTopBinder b $ \ b' ->
+ mungeTopBinders bs $ \ bs' ->
+ k (b' : bs')
+
+addTopFloat :: Type -> CoreExpr -> TidyM Id
+addTopFloat lit_ty lit_rhs mod env (us, floats)
+ = case splitUniqSupply us of
+ (us',us1) ->
+ let
+ lit_local = mkSysLocal SLIT("nrlit") uniq lit_ty noSrcLoc
+ lit_id = setIdVisibility mod lit_local
+ --(us', us1) = splitUniqSupply us
+ uniq = getUnique us1
+ in
+ (lit_id, (us', floats `snocBag` NonRec lit_id lit_rhs))
+{-
+ where
+ lit_local = mkSysLocal SLIT("nrlit") uniq lit_ty noSrcLoc
+ lit_id = setIdVisibility mod lit_local
+ (us', us1) = splitUniqSupply us
+ uniq = getUnique us1
+-}
+lookupTM v mod env usf
+ = case lookupIdEnv env v of
+ Nothing -> (v, usf)
+ Just v' -> (v', usf)
+
+extendEnvTM v v' m mod env usf
+ = m mod (addOneToIdEnv env v v') usf
+\end{code}
- head xs = foldr (\ x _ -> x) (_|_) xs
-This then is exported via a pragma. However,
-*if* you include the extra code above, you will
-export the non-foldr/build version.