X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2FsimplCore%2FSimplUtils.lhs;h=c33bc3d90c8dc4b7813175be24909dacad214e23;hb=27061b5b4008a831eba4784358b040bb1250dcef;hp=5c9d5d53d94edf4ab0fa1052bfff87f3ae0cfb0f;hpb=30c39066cfbbb9380fff1f3266405d37af798009;p=ghc-hetmet.git diff --git a/compiler/simplCore/SimplUtils.lhs b/compiler/simplCore/SimplUtils.lhs index 5c9d5d5..c33bc3d 100644 --- a/compiler/simplCore/SimplUtils.lhs +++ b/compiler/simplCore/SimplUtils.lhs @@ -20,7 +20,7 @@ module SimplUtils ( activeInline, activeRule, inlineMode, -- The continuation type - SimplCont(..), DupFlag(..), LetRhsFlag(..), + SimplCont(..), DupFlag(..), ArgInfo(..), contIsDupable, contResultType, contIsTrivial, contArgs, dropArgs, countValArgs, countArgs, splitInlineCont, mkBoringStop, mkLazyArgStop, mkRhsStop, contIsRhsOrArg, @@ -56,7 +56,10 @@ import Unify ( dataConCannotMatch ) import VarSet import BasicTypes import Util +import MonadUtils import Outputable +import FastString + import List( nub ) \end{code} @@ -91,14 +94,11 @@ Key points: data SimplCont = Stop -- An empty context, or hole, [] OutType -- Type of the result - LetRhsFlag - Bool -- True <=> There is something interesting about + CallCtxt -- True <=> There is something interesting about -- the context, and hence the inliner -- should be a bit keener (see interestingCallContext) - -- Two cases: - -- (a) This is the RHS of a thunk whose type suggests - -- that update-in-place would be possible - -- (b) This is an argument of a function that has RULES + -- Specifically: + -- This is an argument of a function that has RULES -- Inlining the call might allow the rule to fire | CoerceIt -- C `cast` co @@ -123,22 +123,28 @@ data SimplCont | StrictArg -- e C OutExpr OutType -- e and its type - (Bool,[Bool]) -- Whether the function at the head of e has rules, - SimplCont -- plus strictness flags for further args - -data LetRhsFlag = AnArg -- It's just an argument not a let RHS - | AnRhs -- It's the RHS of a let (so please float lets out of big lambdas) - -instance Outputable LetRhsFlag where - ppr AnArg = ptext SLIT("arg") - ppr AnRhs = ptext SLIT("rhs") + CallCtxt -- Whether *this* argument position is interesting + ArgInfo -- Whether the function at the head of e has rules, etc + SimplCont -- plus strictness flags for *further* args + +data ArgInfo + = ArgInfo { + ai_rules :: Bool, -- Function has rules (recursively) + -- => be keener to inline in all args + ai_strs :: [Bool], -- Strictness of arguments + -- Usually infinite, but if it is finite it guarantees + -- that the function diverges after being given + -- that number of args + ai_discs :: [Int] -- Discounts for arguments; non-zero => be keener to inline + -- Always infinite + } instance Outputable SimplCont where - ppr (Stop ty is_rhs _) = ptext SLIT("Stop") <> brackets (ppr is_rhs) <+> ppr ty + ppr (Stop ty _) = ptext SLIT("Stop") <+> ppr ty ppr (ApplyTo dup arg se cont) = ((ptext SLIT("ApplyTo") <+> ppr dup <+> pprParendExpr arg) {- $$ nest 2 (pprSimplEnv se) -}) $$ ppr cont ppr (StrictBind b _ _ _ cont) = (ptext SLIT("StrictBind") <+> ppr b) $$ ppr cont - ppr (StrictArg f _ _ cont) = (ptext SLIT("StrictArg") <+> ppr f) $$ ppr cont + ppr (StrictArg f _ _ _ cont) = (ptext SLIT("StrictArg") <+> ppr f) $$ ppr cont ppr (Select dup bndr alts se cont) = (ptext SLIT("Select") <+> ppr dup <+> ppr bndr) $$ (nest 4 (ppr alts)) $$ ppr cont ppr (CoerceIt co cont) = (ptext SLIT("CoerceIt") <+> ppr co) $$ ppr cont @@ -153,13 +159,13 @@ instance Outputable DupFlag where ------------------- mkBoringStop :: OutType -> SimplCont -mkBoringStop ty = Stop ty AnArg False +mkBoringStop ty = Stop ty BoringCtxt -mkLazyArgStop :: OutType -> Bool -> SimplCont -mkLazyArgStop ty has_rules = Stop ty AnArg (canUpdateInPlace ty || has_rules) +mkLazyArgStop :: OutType -> CallCtxt -> SimplCont +mkLazyArgStop ty cci = Stop ty cci mkRhsStop :: OutType -> SimplCont -mkRhsStop ty = Stop ty AnRhs (canUpdateInPlace ty) +mkRhsStop ty = Stop ty BoringCtxt ------------------- contIsRhsOrArg (Stop {}) = True @@ -184,8 +190,8 @@ contIsTrivial other = False ------------------- contResultType :: SimplCont -> OutType -contResultType (Stop to_ty _ _) = to_ty -contResultType (StrictArg _ _ _ cont) = contResultType cont +contResultType (Stop to_ty _) = to_ty +contResultType (StrictArg _ _ _ _ cont) = contResultType cont contResultType (StrictBind _ _ _ _ cont) = contResultType cont contResultType (ApplyTo _ _ _ cont) = contResultType cont contResultType (CoerceIt _ cont) = contResultType cont @@ -226,9 +232,9 @@ splitInlineCont :: SimplCont -> Maybe (SimplCont, SimplCont) splitInlineCont (ApplyTo dup (Type ty) se c) | Just (c1, c2) <- splitInlineCont c = Just (ApplyTo dup (Type ty) se c1, c2) -splitInlineCont cont@(Stop ty _ _) = Just (mkBoringStop ty, cont) +splitInlineCont cont@(Stop ty _) = Just (mkBoringStop ty, cont) splitInlineCont cont@(StrictBind bndr _ _ se _) = Just (mkBoringStop (substTy se (idType bndr)), cont) -splitInlineCont cont@(StrictArg _ fun_ty _ _) = Just (mkBoringStop (funArgTy fun_ty), cont) +splitInlineCont cont@(StrictArg _ fun_ty _ _ _) = Just (mkBoringStop (funArgTy fun_ty), cont) splitInlineCont other = Nothing -- NB: the calculation of the type for mkBoringStop is an annoying -- duplication of the same calucation in mkDupableCont @@ -304,23 +310,26 @@ default case. \begin{code} -interestingCallContext :: SimplCont -> CallContInfo +interestingCallContext :: SimplCont -> CallCtxt interestingCallContext cont = interesting cont where + interestingCtxt = ArgCtxt False 2 -- Give *some* incentive! + interesting (Select _ bndr _ _ _) - | isDeadBinder bndr = CaseCont - | otherwise = InterestingCont + | isDeadBinder bndr = CaseCtxt + | otherwise = interestingCtxt - interesting (ApplyTo {}) = InterestingCont - -- Can happen if we have (coerce t (f x)) y - -- Perhaps True is a bit over-keen, but I've - -- seen (coerce f) x, where f has an INLINE prag, - -- So we have to give some motivation for inlining it - interesting (StrictArg {}) = InterestingCont - interesting (StrictBind {}) = InterestingCont - interesting (Stop ty _ yes) = if yes then InterestingCont else BoringCont - interesting (CoerceIt _ cont) = interesting cont + interesting (ApplyTo {}) = interestingCtxt + -- Can happen if we have (coerce t (f x)) y + -- Perhaps interestingCtxt is a bit over-keen, but I've + -- seen (coerce f) x, where f has an INLINE prag, + -- So we have to give some motivation for inlining it + + interesting (StrictArg _ _ cci _ _) = cci + interesting (StrictBind {}) = BoringCtxt + interesting (Stop ty cci) = cci + interesting (CoerceIt _ cont) = interesting cont -- If this call is the arg of a strict function, the context -- is a bit interesting. If we inline here, we may get useful -- evaluation information to avoid repeated evals: e.g. @@ -341,21 +350,29 @@ interestingCallContext cont mkArgInfo :: Id -> Int -- Number of value args -> SimplCont -- Context of the cal - -> (Bool, [Bool]) -- Arg info --- The arg info consists of --- * A Bool indicating if the function has rules (recursively) --- * A [Bool] indicating strictness for each arg --- The [Bool] is usually infinite, but if it is finite it --- guarantees that the function diverges after being given --- that number of args + -> ArgInfo mkArgInfo fun n_val_args call_cont - = (interestingArgContext fun call_cont, fun_stricts) + | n_val_args < idArity fun -- Note [Unsaturated functions] + = ArgInfo { ai_rules = False + , ai_strs = vanilla_stricts + , ai_discs = vanilla_discounts } + | otherwise + = ArgInfo { ai_rules = interestingArgContext fun call_cont + , ai_strs = arg_stricts + , ai_discs = arg_discounts } where - vanilla_stricts, fun_stricts :: [Bool] + vanilla_discounts, arg_discounts :: [Int] + vanilla_discounts = repeat 0 + arg_discounts = case idUnfolding fun of + CoreUnfolding _ _ _ _ (UnfoldIfGoodArgs _ discounts _ _) + -> discounts ++ vanilla_discounts + other -> vanilla_discounts + + vanilla_stricts, arg_stricts :: [Bool] vanilla_stricts = repeat False - fun_stricts + arg_stricts = case splitStrictSig (idNewStrictness fun) of (demands, result_info) | not (demands `lengthExceeds` n_val_args) @@ -371,7 +388,20 @@ mkArgInfo fun n_val_args call_cont else map isStrictDmd demands ++ vanilla_stricts - other -> vanilla_stricts -- Not enough args, or no strictness + | otherwise + -> WARN( True, text "More demands than arity" <+> ppr fun <+> ppr (idArity fun) + <+> ppr n_val_args <+> ppr demands ) + vanilla_stricts -- Not enough args, or no strictness + +{- Note [Unsaturated functions] + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider (test eyeball/inline4) + x = a:as + y = f x +where f has arity 2. Then we do not want to inline 'x', because +it'll just be floated out again. Even if f has lots of discounts +on its first argument -- it must be saturated for these to kick in +-} interestingArgContext :: Id -> SimplCont -> Bool -- If the argument has form (f x y), where x,y are boring, @@ -394,33 +424,15 @@ interestingArgContext :: Id -> SimplCont -> Bool interestingArgContext fn call_cont = idHasRules fn || go call_cont where - go (Select {}) = False - go (ApplyTo {}) = False - go (StrictArg {}) = True - go (StrictBind {}) = False -- ?? - go (CoerceIt _ c) = go c - go (Stop _ _ interesting) = interesting - -------------------- -canUpdateInPlace :: Type -> Bool --- Consider let x = in ... --- If returns an explicit constructor, we might be able --- to do update in place. So we treat even a thunk RHS context --- as interesting if update in place is possible. We approximate --- this by seeing if the type has a single constructor with a --- small arity. But arity zero isn't good -- we share the single copy --- for that case, so no point in sharing. - -canUpdateInPlace ty - | not opt_UF_UpdateInPlace = False - | otherwise - = case splitTyConApp_maybe ty of - Nothing -> False - Just (tycon, _) -> case tyConDataCons_maybe tycon of - Just [dc] -> arity == 1 || arity == 2 - where - arity = dataConRepArity dc - other -> False + go (Select {}) = False + go (ApplyTo {}) = False + go (StrictArg _ _ cci _ _) = interesting cci + go (StrictBind {}) = False -- ?? + go (CoerceIt _ c) = go c + go (Stop _ cci) = interesting cci + + interesting (ArgCtxt rules _) = rules + interesting other = False \end{code} @@ -440,7 +452,7 @@ settings: (d) Simplifying a GHCi expression or Template Haskell splice - SimplPhase n Used at all other times + SimplPhase n _ Used at all other times The key thing about SimplGently is that it does no call-site inlining. Before full laziness we must be careful not to inline wrappers, @@ -589,8 +601,8 @@ preInlineUnconditionally env top_lvl bndr rhs where phase = getMode env active = case phase of - SimplGently -> isAlwaysActive prag - SimplPhase n -> isActive n prag + SimplGently -> isAlwaysActive prag + SimplPhase n _ -> isActive n prag prag = idInlinePragma bndr try_once in_lam int_cxt -- There's one textual occurrence @@ -624,8 +636,8 @@ preInlineUnconditionally env top_lvl bndr rhs canInlineInLam _ = False early_phase = case phase of - SimplPhase 0 -> False - other -> True + SimplPhase 0 _ -> False + other -> True -- If we don't have this early_phase test, consider -- x = length [1,2,3] -- The full laziness pass carefully floats all the cons cells to @@ -745,8 +757,8 @@ postInlineUnconditionally env top_lvl bndr occ_info rhs unfolding where active = case getMode env of - SimplGently -> isAlwaysActive prag - SimplPhase n -> isActive n prag + SimplGently -> isAlwaysActive prag + SimplPhase n _ -> isActive n prag prag = idInlinePragma bndr activeInline :: SimplEnv -> OutId -> Bool @@ -754,7 +766,7 @@ activeInline env id = case getMode env of SimplGently -> False -- No inlining at all when doing gentle stuff, - -- except for local things that occur once + -- except for local things that occur once (pre/postInlineUnconditionally) -- The reason is that too little clean-up happens if you -- don't inline use-once things. Also a bit of inlining is *good* for -- full laziness; it can expose constant sub-expressions. @@ -768,7 +780,7 @@ activeInline env id -- and they are now constructed as Compulsory unfoldings (in MkId) -- so they'll happen anyway. - SimplPhase n -> isActive n prag + SimplPhase n _ -> isActive n prag where prag = idInlinePragma id @@ -779,13 +791,13 @@ activeRule dflags env = Nothing -- Rewriting is off | otherwise = case getMode env of - SimplGently -> Just isAlwaysActive + SimplGently -> Just isAlwaysActive -- Used to be Nothing (no rules in gentle mode) -- Main motivation for changing is that I wanted -- lift String ===> ... -- to work in Template Haskell when simplifying -- splices, so we get simpler code for literal strings - SimplPhase n -> Just (isActive n) + SimplPhase n _ -> Just (isActive n) \end{code} @@ -829,7 +841,7 @@ mkLam bndrs body ; return (mkLams bndrs body') } | otherwise - = returnSmpl (mkLams bndrs body) + = return (mkLams bndrs body) \end{code} Note [Casts and lambdas] @@ -875,55 +887,97 @@ because the latter is not well-kinded. -- if this is indeed a right-hand side; otherwise -- we end up floating the thing out, only for float-in -- to float it right back in again! - = tryRhsTyLam env bndrs body `thenSmpl` \ (floats, body') -> - returnSmpl (floats, mkLams bndrs body') + = do (floats, body') <- tryRhsTyLam env bndrs body + return (floats, mkLams bndrs body') -} %************************************************************************ %* * -\subsection{Eta expansion and reduction} + Eta reduction %* * %************************************************************************ -We try for eta reduction here, but *only* if we get all the -way to an exprIsTrivial expression. -We don't want to remove extra lambdas unless we are going -to avoid allocating this thing altogether +Note [Eta reduction conditions] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We try for eta reduction here, but *only* if we get all the way to an +trivial expression. We don't want to remove extra lambdas unless we +are going to avoid allocating this thing altogether. + +There are some particularly delicate points here: + +* Eta reduction is not valid in general: + \x. bot /= bot + This matters, partly for old-fashioned correctness reasons but, + worse, getting it wrong can yield a seg fault. Consider + f = \x.f x + h y = case (case y of { True -> f `seq` True; False -> False }) of + True -> ...; False -> ... + + If we (unsoundly) eta-reduce f to get f=f, the strictness analyser + says f=bottom, and replaces the (f `seq` True) with just + (f `cast` unsafe-co). BUT, as thing stand, 'f' got arity 1, and it + *keeps* arity 1 (perhaps also wrongly). So CorePrep eta-expands + the definition again, so that it does not termninate after all. + Result: seg-fault because the boolean case actually gets a function value. + See Trac #1947. + + So it's important to to the right thing. + +* We need to be careful if we just look at f's arity. Currently (Dec07), + f's arity is visible in its own RHS (see Note [Arity robustness] in + SimplEnv) so we must *not* trust the arity when checking that 'f' is + a value. Instead, look at the unfolding. + + However for GlobalIds we can look at the arity; and for primops we + must, since they have no unfolding. + +* Regardless of whether 'f' is a vlaue, we always want to + reduce (/\a -> f a) to f + This came up in a RULE: foldr (build (/\a -> g a)) + did not match foldr (build (/\b -> ...something complex...)) + The type checker can insert these eta-expanded versions, + with both type and dictionary lambdas; hence the slightly + ad-hoc isDictId + +These delicacies are why we don't use exprIsTrivial and exprIsHNF here. +Alas. \begin{code} tryEtaReduce :: [OutBndr] -> OutExpr -> Maybe OutExpr tryEtaReduce bndrs body - -- We don't use CoreUtils.etaReduce, because we can be more - -- efficient here: - -- (a) we already have the binders - -- (b) we can do the triviality test before computing the free vars = go (reverse bndrs) body where go (b : bs) (App fun arg) | ok_arg b arg = go bs fun -- Loop round go [] fun | ok_fun fun = Just fun -- Success! go _ _ = Nothing -- Failure! - ok_fun fun = exprIsTrivial fun - && not (any (`elemVarSet` (exprFreeVars fun)) bndrs) - && (exprIsHNF fun || all ok_lam bndrs) + -- Note [Eta reduction conditions] + ok_fun (App fun (Type ty)) + | not (any (`elemVarSet` tyVarsOfType ty) bndrs) + = ok_fun fun + ok_fun (Var fun_id) + = not (fun_id `elem` bndrs) + && (ok_fun_id fun_id || all ok_lam bndrs) + ok_fun _fun = False + + ok_fun_id fun + | isLocalId fun = isEvaldUnfolding (idUnfolding fun) + | isDataConWorkId fun = True + | isGlobalId fun = idArity fun > 0 + ok_lam v = isTyVar v || isDictId v - -- The exprIsHNF is because eta reduction is not - -- valid in general: \x. bot /= bot - -- So we need to be sure that the "fun" is a value. - -- - -- However, we always want to reduce (/\a -> f a) to f - -- This came up in a RULE: foldr (build (/\a -> g a)) - -- did not match foldr (build (/\b -> ...something complex...)) - -- The type checker can insert these eta-expanded versions, - -- with both type and dictionary lambdas; hence the slightly - -- ad-hoc isDictTy ok_arg b arg = varToCoreExpr b `cheapEqExpr` arg \end{code} - Try eta expansion for RHSs +%************************************************************************ +%* * + Eta expansion +%* * +%************************************************************************ + We go for: f = \x1..xn -> N ==> f = \x1..xn y1..ym -> N y1..ym @@ -938,6 +992,16 @@ where (in both cases) * N is a NORMAL FORM (i.e. no redexes anywhere) wanting a suitable number of extra args. +The biggest reason for doing this is for cases like + + f = \x -> case x of + True -> \y -> e1 + False -> \y -> e2 + +Here we want to get the lambdas together. A good exmaple is the nofib +program fibheaps, which gets 25% more allocation if you don't do this +eta-expansion. + We may have to sandwich some coerces between the lambdas to make the types work. exprEtaExpandArity looks through coerces when computing arity; and etaExpand adds the coerces as necessary when @@ -946,9 +1010,9 @@ actually computing the expansion. \begin{code} tryEtaExpansion :: DynFlags -> OutExpr -> SimplM OutExpr -- There is at least one runtime binder in the binders -tryEtaExpansion dflags body - = getUniquesSmpl `thenSmpl` \ us -> - returnSmpl (etaExpand fun_arity us body (exprType body)) +tryEtaExpansion dflags body = do + us <- getUniquesM + return (etaExpand fun_arity us body (exprType body)) where fun_arity = exprEtaExpandArity dflags body \end{code} @@ -1040,7 +1104,7 @@ it is guarded by the doFloatFromRhs call in simplLazyBind. abstractFloats :: [OutTyVar] -> SimplEnv -> OutExpr -> SimplM ([OutBind], OutExpr) abstractFloats main_tvs body_env body = ASSERT( notNull body_floats ) - do { (subst, float_binds) <- mapAccumLSmpl abstract empty_subst body_floats + do { (subst, float_binds) <- mapAccumLM abstract empty_subst body_floats ; return (float_binds, CoreSubst.substExpr subst body) } where main_tv_set = mkVarSet main_tvs @@ -1076,7 +1140,7 @@ abstractFloats main_tvs body_env body -- gives rise to problems. SLPJ June 98 abstract subst (Rec prs) - = do { (poly_ids, poly_apps) <- mapAndUnzipSmpl (mk_poly tvs_here) ids + = do { (poly_ids, poly_apps) <- mapAndUnzipM (mk_poly tvs_here) ids ; let subst' = CoreSubst.extendSubstList subst (ids `zip` poly_apps) poly_rhss = [mkLams tvs_here (CoreSubst.substExpr subst' rhs) | rhs <- rhss] ; return (subst', Rec (poly_ids `zip` poly_rhss)) } @@ -1098,10 +1162,11 @@ abstractFloats main_tvs body_env body tvs_here = main_tvs mk_poly tvs_here var - = do { uniq <- getUniqueSmpl + = do { uniq <- getUniqueM ; let poly_name = setNameUnique (idName var) uniq -- Keep same name poly_ty = mkForAllTys tvs_here (idType var) -- But new type of course - poly_id = mkLocalId poly_name poly_ty + poly_id = transferPolyIdInfo var $ -- Note [transferPolyIdInfo] in Id.lhs + mkLocalId poly_name poly_ty ; return (poly_id, mkTyApps (Var poly_id) (mkTyVarTys tvs_here)) } -- In the olden days, it was crucial to copy the occInfo of the original var, -- because we were looking at occurrence-analysed but as yet unsimplified code! @@ -1342,7 +1407,7 @@ prepareDefault dflags env case_bndr (Just (tycon, inst_tys)) imposs_cons (Just d [con] -> -- It matches exactly one constructor, so fill it in do { tick (FillInCaseDefault case_bndr) - ; us <- getUniquesSmpl + ; us <- getUniquesM ; let (ex_tvs, co_tvs, arg_ids) = dataConRepInstPat us con inst_tys ; return [(DataAlt con, ex_tvs ++ co_tvs ++ arg_ids, deflt_rhs)] } @@ -1399,8 +1464,8 @@ mkCase scrut case_bndr ty [] mkCase scrut case_bndr ty alts -- Identity case | all identity_alt alts - = tick (CaseIdentity case_bndr) `thenSmpl_` - returnSmpl (re_cast scrut) + = do tick (CaseIdentity case_bndr) + return (re_cast scrut) where identity_alt (con, args, rhs) = check_eq con args (de_cast rhs) @@ -1433,7 +1498,7 @@ mkCase scrut case_bndr ty alts -- Identity case -------------------------------------------------- -- Catch-all -------------------------------------------------- -mkCase scrut bndr ty alts = returnSmpl (Case scrut bndr ty alts) +mkCase scrut bndr ty alts = return (Case scrut bndr ty alts) \end{code}