- 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 (digForLambdas rhs) of
- (_, _, [], Con _ _ _) -> True
- other -> False
-
- rhs_arg_tys
- = case (digForLambdas 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!
+
+
+%************************************************************************
+%* *
+\subsection{PostSimplification}
+%* *
+%************************************************************************
+
+Several tasks are performed by the post-simplification pass
+
+1. Make the representation of NoRep literals explicit, and
+ float their bindings to the top level. We only do the floating
+ part for NoRep lits inside a lambda (else no gain). We need to
+ take care with let x = "foo" in e
+ that we don't end up with a silly binding
+ let x = y in e
+ with a floated "foo". What a bore.
+
+2. *Mangle* cases involving par# in the discriminant. The unfolding
+ for par in PrelConc.lhs include case expressions with integer
+ 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
+ 0# -> rhs
+ _ -> parError#
+ ==>
+ case par# e of
+ _ -> rhs
+
+ fork# isn't handled like this - it's an explicit IO operation now.
+ The reason is that fork# returns a ThreadId#, which gets in the
+ way of the above scheme. And anyway, IO is the only guaranteed
+ way to enforce ordering --SDM.
+
+3. Mangle cases involving seq# in the discriminant. Up to this
+ point, seq# will appear like this:
+
+ case seq# e of
+ 0# -> seqError#
+ _ -> ...
+
+ where the 0# branch is purely to bamboozle the strictness analyser
+ (see case 4 above). This code comes from an unfolding for 'seq'
+ in Prelude.hs. We translate this into
+
+ case e of
+ _ -> ...
+
+ Now that the evaluation order is safe.
+
+4. 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. It does eta
+ on the RHSs of bindings but not the RHSs of case alternatives and
+ let bodies)
+
+
+------------------- NOT DONE ANY MORE ------------------------
+[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
+
+[Dec 98] [Not now done because there is no penalty in the code
+ generator for using the former form]
+2. Convert
+ case x of {...; x' -> ...x'...}
+ ==>
+ case x of {...; _ -> ...x... }
+ See notes in SimplCase.lhs, near simplDefault for the reasoning here.
+--------------------------------------------------------------
+
+Special case
+~~~~~~~~~~~~
+
+NOT ENABLED AT THE MOMENT (because the floated Ids are global-ish
+things, and we need local Ids for non-floated stuff):
+
+ Don't float stuff out of a binder that's marked as a bottoming Id.
+ Reason: it doesn't do any good, and creates more CAFs that increase
+ the size of SRTs.
+
+eg.
+
+ f = error "string"
+
+is translated to
+
+ f' = unpackCString# "string"
+ f = error f'
+
+hence f' and f become CAFs. Instead, the special case for
+tidyTopBinding below makes sure this comes out as
+
+ f = let f' = unpackCString# "string" in error f'
+
+and we can safely ignore f as a CAF, since it can only ever be entered once.
+
+
+
+\begin{code}
+doPostSimplification :: UniqSupply -> [CoreBind] -> IO [CoreBind]
+doPostSimplification us binds_in
+ = do
+ beginPass "Post-simplification pass"
+ let binds_out = initPM us (postSimplTopBinds binds_in)
+ endPass "Post-simplification pass" opt_D_verbose_core2core binds_out
+
+postSimplTopBinds :: [CoreBind] -> PostM [CoreBind]
+postSimplTopBinds binds
+ = mapPM postSimplTopBind binds `thenPM` \ binds' ->
+ returnPM (bagToList (unionManyBags binds'))
+
+postSimplTopBind :: CoreBind -> PostM (Bag CoreBind)
+postSimplTopBind (NonRec bndr rhs)
+ | isBottomingId bndr -- Don't lift out floats for bottoming Ids
+ -- See notes above
+ = getFloatsPM (postSimplExpr rhs) `thenPM` \ (rhs', floats) ->
+ returnPM (unitBag (NonRec bndr (foldrBag Let rhs' floats)))
+
+postSimplTopBind bind
+ = getFloatsPM (postSimplBind bind) `thenPM` \ (bind', floats) ->
+ returnPM (floats `snocBag` bind')
+
+postSimplBind (NonRec bndr rhs)
+ = postSimplExpr rhs `thenPM` \ rhs' ->
+ returnPM (NonRec bndr rhs')
+
+postSimplBind (Rec pairs)
+ = mapPM postSimplExpr rhss `thenPM` \ rhss' ->
+ returnPM (Rec (bndrs `zip` rhss'))
+ where
+ (bndrs, rhss) = unzip pairs
+\end{code}
+
+
+Expressions
+~~~~~~~~~~~
+\begin{code}
+postSimplExpr (Var v) = returnPM (Var v)
+postSimplExpr (Type ty) = returnPM (Type ty)
+
+postSimplExpr (App fun arg)
+ = postSimplExpr fun `thenPM` \ fun' ->
+ postSimplExpr arg `thenPM` \ arg' ->
+ returnPM (App fun' arg')
+
+postSimplExpr (Con (Literal lit) args)
+ = ASSERT( null args )
+ litToRep lit `thenPM` \ (lit_ty, lit_expr) ->
+ getInsideLambda `thenPM` \ in_lam ->
+ if in_lam && not (exprIsTrivial lit_expr) then
+ -- It must have been a no-rep literal with a
+ -- non-trivial representation; and we're inside a lambda;
+ -- so float it to the top
+ addTopFloat lit_ty lit_expr `thenPM` \ v ->
+ returnPM (Var v)
+ else
+ returnPM lit_expr
+
+postSimplExpr (Con con args)
+ = mapPM postSimplExpr args `thenPM` \ args' ->
+ returnPM (Con con args')
+
+postSimplExpr (Lam bndr body)
+ = insideLambda bndr $
+ postSimplExpr body `thenPM` \ body' ->
+ returnPM (Lam bndr body')
+
+postSimplExpr (Let bind body)
+ = postSimplBind bind `thenPM` \ bind' ->
+ postSimplExprEta body `thenPM` \ body' ->
+ returnPM (Let bind' body')
+
+postSimplExpr (Note note body)
+ = postSimplExprEta body `thenPM` \ body' ->
+ returnPM (Note note body')
+
+-- seq#: see notes above.
+-- NB: seq# :: forall a. a -> Int#
+postSimplExpr (Case scrut@(Con (PrimOp SeqOp) [Type ty, e]) bndr alts)
+ = postSimplExpr e `thenPM` \ e' ->
+ let
+ -- The old binder can't have been used, so we
+ -- can gaily re-use it (yuk!)
+ new_bndr = setIdType bndr ty
+ in
+ postSimplExprEta default_rhs `thenPM` \ rhs' ->
+ returnPM (Case e' new_bndr [(DEFAULT,[],rhs')])
+ where
+ (other_alts, maybe_default) = findDefault alts
+ Just default_rhs = maybe_default
+
+-- par#: see notes above.
+postSimplExpr (Case scrut@(Con (PrimOp op) args) bndr alts)
+ | funnyParallelOp op && maybeToBool maybe_default
+ = postSimplExpr scrut `thenPM` \ scrut' ->
+ postSimplExprEta default_rhs `thenPM` \ rhs' ->
+ returnPM (Case scrut' bndr [(DEFAULT,[],rhs')])
+ where
+ (other_alts, maybe_default) = findDefault alts
+ Just default_rhs = maybe_default
+
+postSimplExpr (Case scrut case_bndr alts)
+ = postSimplExpr scrut `thenPM` \ scrut' ->
+ mapPM ps_alt alts `thenPM` \ alts' ->
+ returnPM (Case scrut' case_bndr alts')
+ where
+ ps_alt (con,bndrs,rhs) = postSimplExprEta rhs `thenPM` \ rhs' ->
+ returnPM (con, bndrs, rhs')
+
+postSimplExprEta e = postSimplExpr e `thenPM` \ e' ->
+ returnPM (etaCoreExpr e')