%
-% (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 ) where
-module SimplCore (
- core2core
- ) where
+#include "HsVersions.h"
-import Type ( getTyConDataCons )
---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
+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_dump_simpl,
+ 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, typeOkForCase )
+import CoreUnfold
+import Literal ( Literal(..), literalType, mkMachInt )
+import ErrUtils ( ghcExit, dumpIfSet, doIfSet )
+import FiniteMap ( FiniteMap, emptyFM )
import FloatIn ( floatInwards )
import FloatOut ( floatOutwards )
-import Id ( getIdUnfolding,
- idType, toplevelishId,
- idWantsToBeINLINEd,
- unfoldingUnfriendlyId, isWrapperId,
- mkTemplateLocals
+import FoldrBuildWW ( mkFoldrBuildWW )
+import Id ( mkSysLocal, mkUserId, setIdVisibility, replaceIdInfo,
+ replacePragmaInfo, getIdDemandInfo, idType,
+ getIdInfo, getPragmaInfo, mkIdWithNewUniq,
+ nullIdEnv, addOneToIdEnv, delOneFromIdEnv,
+ lookupIdEnv, IdEnv,
+ Id
+ )
+import IdInfo ( willBeDemanded, DemandInfo )
+import Name ( isExported, isLocallyDefined,
+ isLocalName, uniqToOccName,
+ setNameVisibility,
+ Module, NamedThing(..), OccName(..)
)
-import IdInfo
+import TyCon ( TyCon )
+import PrimOp ( PrimOp(..) )
+import PrelVals ( unpackCStringId, unpackCString2Id,
+ integerZeroId, integerPlusOneId,
+ integerPlusTwoId, integerMinusOneId
+ )
+import Type ( splitAlgTyConApp_maybe, isUnpointedType, Type )
+import TysWiredIn ( stringTy, isIntegerTy )
import LiberateCase ( liberateCase )
-import MainMonad
-import Maybes
+import MagicUFs ( MagicUnfoldingFun )
+import PprCore
+import PprType ( GenType{-instance Outputable-}, GenTyVar{-ditto-},
+ nmbrType
+ )
import SAT ( doStaticArgs )
-import SCCauto
---ANDY:
---import SimplHaskell ( coreToHaskell )
-import SimplMonad ( zeroSimplCount, showSimplCount, TickType, SimplCount )
+import SimplMonad ( zeroSimplCount, showSimplCount, SimplCount )
import SimplPgm ( simplifyPgm )
-import SimplVar ( leastItCouldCost )
import Specialise
import SpecUtils ( pprSpecErrs )
import StrictAnal ( saWwTopBinds )
-import FoldrBuildWW
-import AnalFBWW
-#if ! OMIT_DEFORESTER
-import Deforest ( deforestProgram )
-import DefUtils ( deforestable )
-#endif
-import UniqSupply
-import Util
+import TyVar ( TyVar, nameTyVar )
+import Unique ( Unique{-instance Eq-}, Uniquable(..),
+ integerTyConKey, ratioTyConKey,
+ mkUnique, incrUnique,
+ initTidyUniques
+ )
+import UniqSupply ( UniqSupply, mkSplitUniqSupply,
+ splitUniqSupply, getUnique
+ )
+import UniqFM ( UniqFM, lookupUFM, addToUFM )
+import Util ( mapAccumL )
+import SrcLoc ( noSrcLoc )
+import Constants ( tARGET_MIN_INT, tARGET_MAX_INT )
+import Bag
+import Maybes
+import IO ( hPutStr, stderr )
+import Outputable
\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)
-> UniqSupply -- a name supply
-> [TyCon] -- local data tycons and tycon specialisations
- -> FiniteMap TyCon [(Bool, [Maybe Type])]
-> [CoreBinding] -- input...
- -> MainIO
- ([CoreBinding], -- results: program, plus...
- IdEnv UnfoldingDetails, -- unfoldings to be exported from here
- 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)
+ -> IO [CoreBinding] -- results: program
+
+core2core core_todos module_name us local_tycons binds
+ = -- Do the main business
+ foldl_mn do_core_pass
+ (binds, us, zeroSimplCount)
core_todos
- `thenMn` \ (processed_binds, _, inline_env, spec_data, simpl_stats) ->
-
- (if switch_is_on D_simplifier_stats
- then writeMn stderr ("\nSimplifier Stats:\n")
- `thenMn_`
- writeMn stderr (showSimplCount simpl_stats)
- `thenMn_`
- writeMn stderr "\n"
- else returnMn ()
- ) `thenMn_`
-
- returnMn (processed_binds, inline_env, spec_data)
- ESCC
- where
- init_specdata = initSpecData local_tycons tycon_specs
+ >>= \ (processed_binds, us', simpl_stats) ->
- switch_is_on = switchIsOn sw_chkr
+ -- Do the final tidy-up
+ let
+ final_binds = tidyCorePgm module_name processed_binds
+ in
+ lintCoreBindings "TidyCorePgm" True final_binds >>
- 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
+ -- Dump output
+ dumpIfSet (opt_D_dump_simpl || opt_D_verbose_core2core)
+ "Core transformations"
+ (pprCoreBindings final_binds) >>
- -------------
- core_linter = if switch_is_on DoCoreLinting
- then lintCoreBindings ppr_style
- else ( \ whodunnit spec_done binds -> binds )
+ -- Report statistics
+ doIfSet opt_D_simplifier_stats
+ (hPutStr stderr ("\nSimplifier Stats:\n") >>
+ hPutStr stderr (showSimplCount simpl_stats) >>
+ hPutStr stderr "\n") >>
+ -- Return results
+ return final_binds
+ where
--------------
- 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, simpl_stats) to_do =
+ case (splitUniqSupply us) of
+ (us1,us2) ->
case to_do of
CoreDoSimplify simpl_sw_chkr
- -> BSCC("CoreSimplify")
+ -> _scc_ "CoreSimplify"
begin_pass ("Simplify" ++ if switchIsOn simpl_sw_chkr SimplDoFoldrBuild
- then " (foldr/build)" else "") `thenMn_`
- case (simplifyPgm binds sw_chkr simpl_sw_chkr simpl_stats us1) of
+ then " (foldr/build)" else "") >>
+ case (simplifyPgm binds simpl_sw_chkr simpl_stats us1) of
(p, it_cnt, simpl_stats2)
- -> end_pass False us2 p inline_env spec_data simpl_stats2
+ -> end_pass us2 p simpl_stats2
("Simplify (" ++ show it_cnt ++ ")"
++ if switchIsOn simpl_sw_chkr SimplDoFoldrBuild
then " foldr/build" else "")
- ESCC
CoreDoFoldrBuildWorkerWrapper
- -> BSCC("CoreDoFoldrBuildWorkerWrapper")
- begin_pass "FBWW" `thenMn_`
- case (mkFoldrBuildWW switch_is_on us1 binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "FBWW"
- } ESCC
+ -> _scc_ "CoreDoFoldrBuildWorkerWrapper"
+ begin_pass "FBWW" >>
+ case (mkFoldrBuildWW us1 binds) of { binds2 ->
+ end_pass us2 binds2 simpl_stats "FBWW" }
CoreDoFoldrBuildWWAnal
- -> BSCC("CoreDoFoldrBuildWWAnal")
- begin_pass "AnalFBWW" `thenMn_`
- case (analFBWW switch_is_on binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "AnalFBWW"
- } ESCC
+ -> _scc_ "CoreDoFoldrBuildWWAnal"
+ begin_pass "AnalFBWW" >>
+ case (analFBWW binds) of { binds2 ->
+ end_pass us2 binds2 simpl_stats "AnalFBWW" }
CoreLiberateCase
- -> BSCC("LiberateCase")
- begin_pass "LiberateCase" `thenMn_`
- case (liberateCase lib_case_threshold binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "LiberateCase"
- } ESCC
-
- CoreDoCalcInlinings1 -- avoid inlinings w/ cost-centres
- -> BSCC("CoreInlinings1")
- begin_pass "CalcInlinings" `thenMn_`
- case (calcInlinings False sw_chkr inline_env binds) of { inline_env2 ->
- end_pass False us2 binds inline_env2 spec_data simpl_stats "CalcInlinings"
- } ESCC
-
- CoreDoCalcInlinings2 -- allow inlinings w/ cost-centres
- -> BSCC("CoreInlinings2")
- begin_pass "CalcInlinings" `thenMn_`
- case (calcInlinings True sw_chkr inline_env binds) of { inline_env2 ->
- end_pass False us2 binds inline_env2 spec_data simpl_stats "CalcInlinings"
- } ESCC
+ -> _scc_ "LiberateCase"
+ begin_pass "LiberateCase" >>
+ case (liberateCase opt_LiberateCaseThreshold binds) of { binds2 ->
+ end_pass us2 binds2 simpl_stats "LiberateCase" }
CoreDoFloatInwards
- -> BSCC("FloatInwards")
- begin_pass "FloatIn" `thenMn_`
+ -> _scc_ "FloatInwards"
+ begin_pass "FloatIn" >>
case (floatInwards binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "FloatIn"
- } ESCC
+ end_pass us2 binds2 simpl_stats "FloatIn" }
CoreDoFullLaziness
- -> BSCC("CoreFloating")
- begin_pass "FloatOut" `thenMn_`
- case (floatOutwards switch_is_on us1 binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "FloatOut"
- } ESCC
+ -> _scc_ "CoreFloating"
+ begin_pass "FloatOut" >>
+ case (floatOutwards us1 binds) of { binds2 ->
+ end_pass us2 binds2 simpl_stats "FloatOut" }
CoreDoStaticArgs
- -> BSCC("CoreStaticArgs")
- begin_pass "StaticArgs" `thenMn_`
+ -> _scc_ "CoreStaticArgs"
+ begin_pass "StaticArgs" >>
case (doStaticArgs binds us1) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "StaticArgs"
+ end_pass us2 binds2 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")
- begin_pass "StrAnal" `thenMn_`
- case (saWwTopBinds us1 switch_is_on binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "StrAnal"
- } ESCC
+ -> _scc_ "CoreStranal"
+ begin_pass "StrAnal" >>
+ case (saWwTopBinds us1 binds) of { binds2 ->
+ end_pass us2 binds2 simpl_stats "StrAnal" }
CoreDoSpecialising
- -> BSCC("Specialise")
- begin_pass "Specialise" `thenMn_`
- case (specProgram switch_is_on 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 module_name spec_errs spec_warn spec_tyerrs))
- `thenMn_` writeMn stderr "\n"
- else
- returnMn ()) `thenMn_`
-
- (if not spec_noerrs then -- Stop here if specialisation errors occured
- exitMn 1
- else
- returnMn ()) `thenMn_`
-
- end_pass False us2 p inline_env spec_data2 simpl_stats "Specialise"
- }
- ESCC
-
- CoreDoDeforest
-#if OMIT_DEFORESTER
- -> error "ERROR: CoreDoDeforest: not built into compiler\n"
-#else
- -> BSCC("Deforestation")
- begin_pass "Deforestation" `thenMn_`
- case (deforestProgram sw_chkr binds us1) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "Deforestation"
+ -> _scc_ "Specialise"
+ begin_pass "Specialise" >>
+ case (specProgram us1 binds) of { p ->
+ end_pass us2 p simpl_stats "Specialise"
}
- ESCC
-#endif
-
- CoreDoAutoCostCentres
- -> BSCC("AutoSCCs")
- begin_pass "AutoSCCs" `thenMn_`
- case (addAutoCostCentres sw_chkr module_name binds) of { binds2 ->
- end_pass False us2 binds2 inline_env spec_data simpl_stats "AutoSCCs"
- }
- ESCC
CoreDoPrintCore -- print result of last pass
- -> end_pass True us2 binds inline_env spec_data simpl_stats "Print"
-
+ -> dumpIfSet (not opt_D_verbose_core2core) "Print Core"
+ (pprCoreBindings binds) >>
+ return (binds, us1, simpl_stats)
-------------------------------------------------
- begin_pass
- = if switch_is_on D_show_passes
- then \ what -> writeMn stderr ("*** Core2Core: "++what++"\n")
- else \ what -> returnMn ()
+ begin_pass what
+ = if opt_D_show_passes
+ then hPutStr stderr ("*** Core2Core: "++what++"\n")
+ else return ()
- end_pass print us2 binds2 inline_env2
- spec_data2@(SpecData spec_done _ _ _ _ _ _ _)
+ end_pass us2 binds2
simpl_stats2 what
- = -- report verbosely, if required
- (if (do_verbose_core2core && not print) ||
- (print && not do_verbose_core2core)
- then
- writeMn stderr ("\n*** "++what++":\n")
- `thenMn_`
- writeMn stderr (ppShow 1000
- (ppAboves (map (pprPlainCoreBinding ppr_style) binds2)))
- `thenMn_`
- writeMn stderr "\n"
- else
- returnMn ()) `thenMn_`
- let
- linted_binds = core_linter what spec_done binds2
- in
- returnMn
- (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
- )
+ = -- Report verbosely, if required
+ dumpIfSet opt_D_verbose_core2core what
+ (pprCoreBindings binds2) >>
+
+ lintCoreBindings what True {- spec_done -} binds2 >>
+ -- The spec_done flag tells the linter to
+ -- complain about unboxed let-bindings
+ -- But we're not specialising unboxed types any more,
+ -- so its irrelevant.
+
+ return
+ (binds2, -- processed binds, possibly run thru CoreLint
+ us2, -- UniqSupply for the next guy
+ 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@
+
+----------------
+ [March 98] Indirections are now elimianted by the occurrence analyser
+ -- 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.)
+
+8. Do let-to-case. See notes in Simplify.lhs for why we defer let-to-case
+ for multi-constructor types.
+
+9. Give all binders a nice print-name. Their uniques aren't changed; rather we give
+ them lexically unique occ-names, so that we can safely print the OccNae only
+ in the interface file. [Bad idea to change the uniques, because the code
+ generator makes global labels from the uniques for local thunks etc.]
+
-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
- -> [CoreBinding]
- -> IdEnv UnfoldingDetails
+tidyCorePgm :: Module -> [CoreBinding] -> [CoreBinding]
+
+tidyCorePgm mod binds_in
+ = initTM mod nullIdEnv $
+ tidyTopBindings binds_in `thenTM` \ binds ->
+ returnTM (bagToList binds)
+\end{code}
+
+Top level bindings
+~~~~~~~~~~~~~~~~~~
+\begin{code}
+tidyTopBindings [] = returnTM emptyBag
+tidyTopBindings (b:bs)
+ = tidyTopBinding b $
+ tidyTopBindings bs
+
+tidyTopBinding :: CoreBinding
+ -> TopTidyM (Bag CoreBinding)
+ -> TopTidyM (Bag CoreBinding)
+
+tidyTopBinding (NonRec bndr rhs) thing_inside
+ = initNestedTM (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' ->
+ initNestedTM (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}
+
+
+
+Expressions
+~~~~~~~~~~~
+\begin{code}
+tidyCoreExpr (Var v) = lookupId 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)
+ = tidyPrimOp prim `thenTM` \ prim' ->
+ mapTM tidyCoreArg args `thenTM` \ args' ->
+ returnTM (Prim prim' args')
+
+tidyCoreExpr (Lam (ValBinder v) body)
+ = newId v $ \ v' ->
+ tidyCoreExpr body `thenTM` \ body' ->
+ returnTM (Lam (ValBinder v') body')
+
+tidyCoreExpr (Lam (TyBinder tv) body)
+ = newTyVar tv $ \ tv' ->
+ tidyCoreExpr body `thenTM` \ body' ->
+ returnTM (Lam (TyBinder tv') body')
+
+ -- Try for let-to-case (see notes in Simplify.lhs for why
+ -- some let-to-case stuff is deferred to now).
+tidyCoreExpr (Let (NonRec bndr rhs) body)
+ | willBeDemanded (getIdDemandInfo bndr) &&
+ not rhs_is_whnf && -- Don't do it if RHS is already in WHNF
+ typeOkForCase (idType bndr)
+ = ASSERT( not (isUnpointedType (idType bndr)) )
+ tidyCoreExpr (Case rhs (AlgAlts [] (BindDefault bndr body)))
+ where
+ rhs_is_whnf = case mkFormSummary rhs of
+ VarForm -> True
+ ValueForm -> True
+ other -> False
+
+tidyCoreExpr (Let (NonRec bndr rhs) body)
+ = tidyCoreExpr rhs `thenTM` \ rhs' ->
+ newId bndr $ \ bndr' ->
+ tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (Let (NonRec bndr' rhs') body')
+
+tidyCoreExpr (Let (Rec pairs) body)
+ = newIds bndrs $ \ bndrs' ->
+ mapTM tidyCoreExpr rhss `thenTM` \ rhss' ->
+ tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (Let (Rec (bndrs' `zip` rhss')) body')
+ where
+ (bndrs, rhss) = unzip pairs
+
+tidyCoreExpr (SCC cc body)
+ = tidyCoreExprEta body `thenTM` \ body' ->
+ returnTM (SCC cc body')
+
+tidyCoreExpr (Coerce coercion ty body)
+ = tidyCoreExprEta body `thenTM` \ body' ->
+ tidyTy ty `thenTM` \ ty' ->
+ 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' ->
+ newId binder $ \ binder' ->
+ 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 (typeOkForCase (idType deflt_bndr))
+ = pprTrace "Warning: discarding polymorphic case:" (ppr scrut) $
+ case scrut of
+ Var v -> lookupId v `thenTM` \ 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) = newIds bndrs $ \ bndrs' ->
+ 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)
+ = newId bndr $ \ bndr' ->
+ 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 -> NestTidyM CoreArg
+
+tidyCoreArg (VarArg v)
+ = lookupId 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) = tidyTy ty `thenTM` \ ty' ->
+ returnTM (TyArg ty')
+\end{code}
+
+\begin{code}
+tidyPrimOp (CCallOp fn casm gc tys ty)
+ = mapTM tidyTy tys `thenTM` \ tys' ->
+ tidyTy ty `thenTM` \ ty' ->
+ returnTM (CCallOp fn casm gc tys' ty')
+
+tidyPrimOp other_prim_op = returnTM other_prim_op
+\end{code}
+
-calcInlinings scc_s_OK sw_chkr inline_env_so_far top_binds
+%************************************************************************
+%* *
+\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 -> NestTidyM (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 (splitAlgTyConApp_maybe rational_ty) of
+ Just (tycon, [i_ty], [con])
+ -> ASSERT(isIntegerTy i_ty && uniqueOf tycon == ratioTyConKey)
+ (con, i_ty)
+
+ _ -> (panic "ratio_data_con", panic "integer_ty")
+
+litToRep other_lit = returnTM (literalType other_lit, Lit other_lit)
+\end{code}
+
+\begin{code}
+funnyParallelOp SeqOp = True
+funnyParallelOp ParOp = True
+funnyParallelOp ForkOp = True
+funnyParallelOp _ = False
+\end{code}
+
+
+%************************************************************************
+%* *
+\subsection{The monad}
+%* *
+%************************************************************************
+
+\begin{code}
+type TidyM a state = Module
+ -> UniqFM CoreBinder -- Maps Ids to Ids, TyVars to TyVars etc
+ -> state
+ -> (a, state)
+
+type TopTidyM a = TidyM a Unique
+type NestTidyM a = TidyM a (Unique, -- Global names
+ Unique, -- Local names
+ Bag CoreBinding) -- Floats
+
+
+(initialTopTidyUnique, initialNestedTidyUnique) = initTidyUniques
+
+initTM :: Module -> UniqFM CoreBinder -> TopTidyM a -> a
+initTM mod env m
+ = case m mod env initialTopTidyUnique of
+ (result, _) -> result
+
+initNestedTM :: NestTidyM a -> TopTidyM (a, Bag CoreBinding)
+initNestedTM m mod env global_us
+ = case m mod env (global_us, initialNestedTidyUnique, emptyBag) of
+ (result, (global_us', _, floats)) -> ((result, floats), global_us')
+
+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}
+-- 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 -> TopTidyM a) -> TopTidyM a
+mungeTopBinder id thing_inside mod env us
+ = case lookupIdEnv env id of
+ Just (ValBinder global) -> thing_inside global mod env us -- Already bound
+
+ other -> -- Give it a new print-name unless it's an exported thing
+ -- setNameVisibility also does the local/global thing
+ let
+ (id', us') | isExported id = (id, us)
+ | otherwise
+ = (setIdVisibility (Just mod) us id,
+ incrUnique us)
+
+ new_env = addToUFM env id (ValBinder id')
+ in
+ thing_inside id' mod new_env us'
+
+mungeTopBinders [] k = k []
+mungeTopBinders (b:bs) k = mungeTopBinder b $ \ b' ->
+ mungeTopBinders bs $ \ bs' ->
+ k (b' : bs')
+
+addTopFloat :: Type -> CoreExpr -> NestTidyM Id
+addTopFloat lit_ty lit_rhs mod env (gus, lus, floats)
= let
- result = foldl calci inline_env_so_far top_binds
+ gus' = incrUnique gus
+ lit_local = mkSysLocal SLIT("lit") gus lit_ty noSrcLoc
+ lit_id = setIdVisibility (Just mod) gus lit_local
in
- --pprTrace "inline env:\n" (ppAboves (map pp_item (getIdEnvMapping result)))
- result
- 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 (GenForm _ _ 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 (Rec pairs)
- = foldl (calc True{-recursive-}) inline_env pairs
-
- calci inline_env bind@(NonRec 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
- && (rhs_looks_like_a_caf || guidance_says_don't || guidance_size_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,
- -- rhs_looks_like_a_caf, guidance_says_don't, guidance_size_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
- -- 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_size
- = case guidance of
- UnfoldAlways -> 0 -- *extremely* small
- EssentialUnfolding -> 0 -- ditto
- UnfoldIfGoodArgs _ _ _ size -> size
-
- guidance_says_don't = case guidance of { UnfoldNever -> True; _ -> False }
-
- guidance_size_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
- 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 then
- False -- we'd like a top-level data constr to be
- -- visible even if it is never unfolded
- 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_looks_like_a_caf = not (manifestlyWHNF rhs)
-
- rhs_looks_like_a_data_val
- = case (collectBinders rhs) of
- (_, _, [], Con _ _ _) -> True
- other -> False
-
- rhs_arg_tys
- = case (collectBinders rhs) of
- (_, _, val_binders, _) -> map idType val_binders
-
- (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!
+ (lit_id, (gus', lus, floats `snocBag` NonRec lit_id lit_rhs))
+
+lookupId :: Id -> TidyM Id state
+lookupId v mod env usf
+ = case lookupUFM env v of
+ Nothing -> (v, usf)
+ Just (ValBinder v') -> (v', usf)
+
+extendEnvTM :: Id -> Id -> (TidyM a state) -> TidyM a state
+extendEnvTM v v' m mod env usf
+ = m mod (addOneToIdEnv env v (ValBinder v')) usf
\end{code}
-ANDY, on the hatred of the check above; why obliterate it? Consider
- head xs = foldr (\ x _ -> x) (_|_) xs
+Making new local binders
+~~~~~~~~~~~~~~~~~~~~~~~~
+\begin{code}
+newId id thing_inside mod env (gus, local_uniq, floats)
+ = let
+ -- Give the Id a fresh print-name, *and* rename its type
+ local_uniq' = incrUnique local_uniq
+ name' = setNameVisibility Nothing local_uniq (getName id)
+ ty' = nmbr_ty env local_uniq' (idType id)
+ id' = mkUserId name' ty'
+ -- NB: This throws away the IdInfo of the Id, which we
+ -- no longer need. That means we don't need to
+ -- run over it with env, nor renumber it
+ --
+ -- NB: the Id's unique remains unchanged; it's only
+ -- its print name that is affected by local_uniq
+
+ env' = addToUFM env id (ValBinder id')
+ in
+ thing_inside id' mod env' (gus, local_uniq', floats)
+
+newIds [] thing_inside
+ = thing_inside []
+newIds (bndr:bndrs) thing_inside
+ = newId bndr $ \ bndr' ->
+ newIds bndrs $ \ bndrs' ->
+ thing_inside (bndr' : bndrs')
+
+
+newTyVar tyvar thing_inside mod env (gus, local_uniq, floats)
+ = let
+ local_uniq' = incrUnique local_uniq
+ tyvar' = nameTyVar tyvar (uniqToOccName local_uniq)
+ env' = addToUFM env tyvar (TyBinder tyvar')
+ in
+ thing_inside tyvar' mod env' (gus, local_uniq', floats)
+\end{code}
+
+Re-numbering types
+~~~~~~~~~~~~~~~~~~
+\begin{code}
+tidyTy ty mod env usf@(_, local_uniq, _)
+ = (nmbr_ty env local_uniq ty, usf)
+ -- We can use local_uniq as a base for renaming forall'd variables
+ -- in the type; we don't need to know how many are consumed.
+
+-- This little impedance-matcher calls nmbrType with the right arguments
+nmbr_ty env uniq ty
+ = nmbrType tv_env uniq ty
+ where
+ tv_env :: TyVar -> TyVar
+ tv_env tyvar = case lookupUFM env tyvar of
+ Just (TyBinder tyvar') -> tyvar'
+ other -> tyvar
+\end{code}
+
-This then is exported via a pragma. However,
-*if* you include the extra code above, you will
-export the non-foldr/build version.