- 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)