summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/coreSyn/CoreFVs.hs105
-rw-r--r--compiler/simplCore/FloatIn.hs369
2 files changed, 255 insertions, 219 deletions
diff --git a/compiler/coreSyn/CoreFVs.hs b/compiler/coreSyn/CoreFVs.hs
index 5997a9c4b6..4dc1ed2f4a 100644
--- a/compiler/coreSyn/CoreFVs.hs
+++ b/compiler/coreSyn/CoreFVs.hs
@@ -55,9 +55,7 @@ module CoreFVs (
freeVars, -- CoreExpr -> CoreExprWithFVs
freeVarsBind, -- CoreBind -> DVarSet -> (DVarSet, CoreBindWithFVs)
freeVarsOf, -- CoreExprWithFVs -> DIdSet
- freeVarsOfType, -- CoreExprWithFVs -> TyCoVarSet
- freeVarsOfAnn, freeVarsOfTypeAnn,
- exprTypeFV -- CoreExprWithFVs -> Type
+ freeVarsOfAnn
) where
#include "HsVersions.h"
@@ -68,7 +66,6 @@ import IdInfo
import NameSet
import UniqSet
import Unique (Uniquable (..))
-import Literal ( literalType )
import Name
import VarSet
import Var
@@ -78,7 +75,6 @@ import TyCon
import CoAxiom
import FamInstEnv
import TysPrim( funTyConName )
-import Coercion
import Maybes( orElse )
import Util
import BasicTypes( Activation )
@@ -539,10 +535,7 @@ The free variable pass annotates every node in the expression with its
NON-GLOBAL free variables and type variables.
-}
-data FVAnn = FVAnn { fva_fvs :: DVarSet -- free in expression
- , fva_ty_fvs :: DVarSet -- free only in expression's type
- , fva_ty :: Type -- expression's type
- }
+type FVAnn = DVarSet
-- | Every node in a binding group annotated with its
-- (non-global) free variables, both Ids and TyVars, and type.
@@ -558,23 +551,11 @@ type CoreAltWithFVs = AnnAlt Id FVAnn
freeVarsOf :: CoreExprWithFVs -> DIdSet
-- ^ Inverse function to 'freeVars'
-freeVarsOf (FVAnn { fva_fvs = fvs }, _) = fvs
-
--- | Extract the vars free in an annotated expression's type
-freeVarsOfType :: CoreExprWithFVs -> DTyCoVarSet
-freeVarsOfType (FVAnn { fva_ty_fvs = ty_fvs }, _) = ty_fvs
-
--- | Extract the type of an annotated expression. (This is cheap.)
-exprTypeFV :: CoreExprWithFVs -> Type
-exprTypeFV (FVAnn { fva_ty = ty }, _) = ty
+freeVarsOf (fvs, _) = fvs
-- | Extract the vars reported in a FVAnn
freeVarsOfAnn :: FVAnn -> DIdSet
-freeVarsOfAnn = fva_fvs
-
--- | Extract the type-level vars reported in a FVAnn
-freeVarsOfTypeAnn :: FVAnn -> DTyCoVarSet
-freeVarsOfTypeAnn = fva_ty_fvs
+freeVarsOfAnn fvs = fvs
noFVs :: VarSet
noFVs = emptyVarSet
@@ -594,10 +575,9 @@ delBindersFV bs fvs = foldr delBinderFV fvs bs
delBinderFV :: Var -> DVarSet -> DVarSet
-- This way round, so we can do it multiple times using foldr
--- (b `delBinderFV` s) removes the binder b from the free variable set s,
--- but *adds* to s
---
--- the free variables of b's type
+-- (b `delBinderFV` s)
+-- * removes the binder b from the free variable set s,
+-- * AND *adds* to s the free variables of b's type
--
-- This is really important for some lambdas:
-- In (\x::a -> x) the only mention of "a" is in the binder.
@@ -733,48 +713,33 @@ freeVars = go
where
go :: CoreExpr -> CoreExprWithFVs
go (Var v)
- = (FVAnn fvs ty_fvs (idType v), AnnVar v)
+ | isLocalVar v = (aFreeVar v `unionFVs` ty_fvs, AnnVar v)
+ | otherwise = (emptyDVarSet, AnnVar v)
where
- -- ToDo: insert motivating example for why we *need*
- -- to include the idSpecVars in the FV list.
- -- Actually [June 98] I don't think it's necessary
- -- fvs = fvs_v `unionVarSet` idSpecVars v
-
- (fvs, ty_fvs)
- | isLocalVar v = (aFreeVar v `unionFVs` ty_fvs, dVarTypeTyCoVars v)
- | otherwise = (emptyDVarSet, emptyDVarSet)
+ ty_fvs = dVarTypeTyCoVars v -- Do we need this?
- go (Lit lit) = (FVAnn emptyDVarSet emptyDVarSet (literalType lit), AnnLit lit)
+ go (Lit lit) = (emptyDVarSet, AnnLit lit)
go (Lam b body)
- = ( FVAnn { fva_fvs = b_fvs `unionFVs` (b `delBinderFV` body_fvs)
- , fva_ty_fvs = b_fvs `unionFVs` (b `delBinderFV` body_ty_fvs)
- , fva_ty = mkFunTy b_ty body_ty }
+ = ( b_fvs `unionFVs` (b `delBinderFV` body_fvs)
, AnnLam b body' )
where
- body'@(FVAnn { fva_fvs = body_fvs, fva_ty_fvs = body_ty_fvs
- , fva_ty = body_ty }, _) = go body
+ body'@(body_fvs, _) = go body
b_ty = idType b
b_fvs = tyCoVarsOfTypeDSet b_ty
go (App fun arg)
- = ( FVAnn { fva_fvs = freeVarsOf fun' `unionFVs` freeVarsOf arg'
- , fva_ty_fvs = tyCoVarsOfTypeDSet res_ty
- , fva_ty = res_ty }
+ = ( freeVarsOf fun' `unionFVs` freeVarsOf arg'
, AnnApp fun' arg' )
where
fun' = go fun
- fun_ty = exprTypeFV fun'
arg' = go arg
- res_ty = applyTypeToArg fun_ty arg
go (Case scrut bndr ty alts)
- = ( FVAnn { fva_fvs = (bndr `delBinderFV` alts_fvs)
- `unionFVs` freeVarsOf scrut2
- `unionFVs` tyCoVarsOfTypeDSet ty
- -- don't need to look at (idType bndr)
- -- b/c that's redundant with scrut
- , fva_ty_fvs = tyCoVarsOfTypeDSet ty
- , fva_ty = ty }
+ = ( (bndr `delBinderFV` alts_fvs)
+ `unionFVs` freeVarsOf scrut2
+ `unionFVs` tyCoVarsOfTypeDSet ty
+ -- don't need to look at (idType bndr)
+ -- b/c that's redundant with scrut
, AnnCase scrut2 bndr ty alts2 )
where
scrut2 = go scrut
@@ -788,45 +753,25 @@ freeVars = go
rhs2 = go rhs
go (Let bind body)
- = ( FVAnn { fva_fvs = bind_fvs
- , fva_ty_fvs = freeVarsOfType body2
- , fva_ty = exprTypeFV body2 }
- , AnnLet bind2 body2 )
+ = (bind_fvs, AnnLet bind2 body2)
where
(bind2, bind_fvs) = freeVarsBind bind (freeVarsOf body2)
body2 = go body
go (Cast expr co)
- = ( FVAnn (freeVarsOf expr2 `unionFVs` cfvs) (tyCoVarsOfTypeDSet to_ty) to_ty
- , AnnCast expr2 (c_ann, co) )
+ = ( freeVarsOf expr2 `unionFVs` cfvs
+ , AnnCast expr2 (cfvs, co) )
where
expr2 = go expr
cfvs = tyCoVarsOfCoDSet co
- c_ann = FVAnn cfvs (tyCoVarsOfTypeDSet co_ki) co_ki
- co_ki = coercionType co
- Just (_, to_ty) = splitCoercionType_maybe co_ki
-
go (Tick tickish expr)
- = ( FVAnn { fva_fvs = tickishFVs tickish `unionFVs` freeVarsOf expr2
- , fva_ty_fvs = freeVarsOfType expr2
- , fva_ty = exprTypeFV expr2 }
+ = ( tickishFVs tickish `unionFVs` freeVarsOf expr2
, AnnTick tickish expr2 )
where
expr2 = go expr
tickishFVs (Breakpoint _ ids) = mkDVarSet ids
tickishFVs _ = emptyDVarSet
- go (Type ty) = ( FVAnn (tyCoVarsOfTypeDSet ty)
- (tyCoVarsOfTypeDSet ki)
- ki
- , AnnType ty)
- where
- ki = typeKind ty
-
- go (Coercion co) = ( FVAnn (tyCoVarsOfCoDSet co)
- (tyCoVarsOfTypeDSet ki)
- ki
- , AnnCoercion co)
- where
- ki = coercionType co
+ go (Type ty) = (tyCoVarsOfTypeDSet ty, AnnType ty)
+ go (Coercion co) = (tyCoVarsOfCoDSet co, AnnCoercion co)
diff --git a/compiler/simplCore/FloatIn.hs b/compiler/simplCore/FloatIn.hs
index 4d5a564257..34b0b42365 100644
--- a/compiler/simplCore/FloatIn.hs
+++ b/compiler/simplCore/FloatIn.hs
@@ -13,6 +13,7 @@ then discover that they aren't needed in the chosen branch.
-}
{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -fprof-auto #-}
module FloatIn ( floatInwards ) where
@@ -21,18 +22,17 @@ module FloatIn ( floatInwards ) where
import CoreSyn
import MkCore
import HscTypes ( ModGuts(..) )
-import CoreUtils ( exprIsDupable, exprIsExpandable,
- exprOkForSideEffects, mkTicks )
+import CoreUtils
import CoreFVs
import CoreMonad ( CoreM )
import Id ( isOneShotBndr, idType, isJoinId, isJoinId_maybe )
import Var
-import Type ( isUnliftedType )
+import Type
import VarSet
import Util
import DynFlags
import Outputable
-import Data.List ( mapAccumL )
+-- import Data.List ( mapAccumL )
import BasicTypes ( RecFlag(..), isRec )
{-
@@ -151,7 +151,6 @@ fiExpr dflags to_drop (_, AnnCast expr (co_ann, co))
[drop_here, e_drop, co_drop]
= sepBindsByDropPoint dflags False
[freeVarsOf expr, freeVarsOfAnn co_ann]
- (freeVarsOfType expr `unionDVarSet` freeVarsOfTypeAnn co_ann)
to_drop
{-
@@ -161,33 +160,45 @@ pull out any silly ones.
-}
fiExpr dflags to_drop ann_expr@(_,AnnApp {})
- = mkTicks ticks $ wrapFloats drop_here $ wrapFloats extra_drop $
+ = wrapFloats drop_here $ wrapFloats extra_drop $
+ mkTicks ticks $
mkApps (fiExpr dflags fun_drop ann_fun)
(zipWith (fiExpr dflags) arg_drops ann_args)
where
(ann_fun, ann_args, ticks) = collectAnnArgsTicks tickishFloatable ann_expr
- (extra_fvs0, fun_fvs)
- | (_, AnnVar _) <- ann_fun = (freeVarsOf ann_fun, emptyDVarSet)
+ fun_ty = exprType (deAnnotate ann_fun)
+ fun_fvs = freeVarsOf ann_fun
+ arg_fvs = map freeVarsOf ann_args
+
+ (drop_here : extra_drop : fun_drop : arg_drops)
+ = sepBindsByDropPoint dflags False
+ (extra_fvs : fun_fvs : arg_fvs)
+ to_drop
+ -- Shortcut behaviour: if to_drop is empty,
+ -- sepBindsByDropPoint returns a suitable bunch of empty
+ -- lists without evaluating extra_fvs, and hence without
+ -- peering into each argument
+
+ (_, extra_fvs) = foldl add_arg (fun_ty, extra_fvs0) ann_args
+ extra_fvs0 = case ann_fun of
+ (_, AnnVar _) -> fun_fvs
+ _ -> emptyDVarSet
-- Don't float the binding for f into f x y z; see Note [Join points]
-- for why we *can't* do it when f is a join point. (If f isn't a
-- join point, floating it in isn't especially harmful but it's
-- useless since the simplifier will immediately float it back out.)
- | otherwise = (emptyDVarSet, freeVarsOf ann_fun)
- (extra_fvs, arg_fvs) = mapAccumL mk_arg_fvs extra_fvs0 ann_args
- mk_arg_fvs :: FreeVarSet -> CoreExprWithFVs -> (FreeVarSet, FreeVarSet)
- mk_arg_fvs extra_fvs ann_arg
- | noFloatIntoRhs False NonRecursive ann_arg
- = (extra_fvs `unionDVarSet` freeVarsOf ann_arg, emptyDVarSet)
- | otherwise
- = (extra_fvs, freeVarsOf ann_arg)
+ add_arg :: (Type,FreeVarSet) -> CoreExprWithFVs -> (Type,FreeVarSet)
+ add_arg (fun_ty, extra_fvs) (_, AnnType ty)
+ = (piResultTy fun_ty ty, extra_fvs)
- drop_here : extra_drop : fun_drop : arg_drops
- = sepBindsByDropPoint dflags False
- (extra_fvs : fun_fvs : arg_fvs)
- (freeVarsOfType ann_fun `unionDVarSet`
- mapUnionDVarSet freeVarsOfType ann_args)
- to_drop
+ add_arg (fun_ty, extra_fvs) (arg_fvs, arg)
+ | noFloatIntoArg arg arg_ty
+ = (res_ty, extra_fvs `unionDVarSet` arg_fvs)
+ | otherwise
+ = (res_ty, extra_fvs)
+ where
+ (arg_ty, res_ty) = splitFunTy fun_ty
{-
Note [Do not destroy the let/app invariant]
@@ -260,16 +271,37 @@ it's non-recursive, so we float only into non-recursive join points.)
Urk! if all are tyvars, and we don't float in, we may miss an
opportunity to float inside a nested case branch
+
+
+Note [Floating coercions]
+~~~~~~~~~~~~~~~~~~~~~~~~~
+We could, in principle, have a coercion binding like
+ case f x of co { DEFAULT -> e1 e2 }
+It's not common to have a function that returns a coercion, but nothing
+in Core prohibits it. If so, 'co' might be mentioned in e1 or e2
+/only in a type/. E.g. suppose e1 was
+ let (x :: Int |> co) = blah in blah2
+
+
+But, with coercions appearing in types, there is a complication: we
+might be floating in a "strict let" -- that is, a case. Case expressions
+mention their return type. We absolutely can't float a coercion binding
+inward to the point that the type of the expression it's about to wrap
+mentions the coercion. So we include the union of the sets of free variables
+of the types of all the drop points involved. If any of the floaters
+bind a coercion variable mentioned in any of the types, that binder must
+be dropped right away.
+
-}
fiExpr dflags to_drop lam@(_, AnnLam _ _)
- | okToFloatInside bndrs -- Float in
+ | noFloatIntoLam bndrs -- Dump it all here
-- NB: Must line up with noFloatIntoRhs (AnnLam...); see Trac #7088
- = mkLams bndrs (fiExpr dflags to_drop body)
-
- | otherwise -- Dump it all here
= wrapFloats to_drop (mkLams bndrs (fiExpr dflags [] body))
+ | otherwise -- Float inside
+ = mkLams bndrs (fiExpr dflags to_drop body)
+
where
(bndrs, body) = collectAnnBndrs lam
@@ -342,42 +374,69 @@ fiExpr dflags to_drop (_,AnnLet bind body)
= fiExpr dflags (after ++ new_float : before) body
-- to_drop is in reverse dependency order
where
- (before, new_float, after) = fiBind dflags to_drop bind body_fvs body_ty_fvs
+ (before, new_float, after) = fiBind dflags to_drop bind body_fvs
body_fvs = freeVarsOf body
- body_ty_fvs = freeVarsOfType body
-{-
-For @Case@, the possible ``drop points'' for the \tr{to_drop}
-bindings are: (a)~inside the scrutinee, (b)~inside one of the
-alternatives/default [default FVs always {\em first}!].
-
-Floating case expressions inward was added to fix Trac #5658: strict bindings
-not floated in. In particular, this change allows array indexing operations,
-which have a single DEFAULT alternative without any binders, to be floated
-inward. SIMD primops for unpacking SIMD vectors into an unboxed tuple of unboxed
+{- Note [Floating primops]
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+We try to float-in a case expression over an unlifted type. The
+motivating example was Trac #5658: in particular, this change allows
+array indexing operations, which have a single DEFAULT alternative
+without any binders, to be floated inward.
+
+SIMD primops for unpacking SIMD vectors into an unboxed tuple of unboxed
scalars also need to be floated inward, but unpacks have a single non-DEFAULT
alternative that binds the elements of the tuple. We now therefore also support
floating in cases with a single alternative that may bind values.
+
+But there are wrinkles
+
+* Which unlifted cases do we float? See PrimOp.hs
+ Note [PrimOp can_fail and has_side_effects] which explains:
+ - We can float-in can_fail primops, but we can't float them out.
+ - But we can float a has_side_effects primop, but NOT inside a lambda,
+ so for now we don't float them at all.
+ Hence exprOkForSideEffects
+
+* Because we can float can-fail primops (array indexing, division) inwards
+ but not outwards, we must be careful not to transform
+ case a /# b of r -> f (F# r)
+ ===>
+ f (case a /# b of r -> F# r)
+ because that creates a new thunk that wasn't there before. And
+ because it can't be floated out (can_fail), the thunk will stay
+ there. Disaster! (This happened in nofib 'simple' and 'scs'.)
+
+ Solution: only float cases into the branches of other cases, and
+ not into the arguments of an application, or the RHS of a let. This
+ is somewhat conservative, but it's simple. And it stil hits the
+ cases like Trac #5658. This is implemented in sepBindsByJoinPoint;
+ if is_case is False we dump all floating cases right here.
+
+For @Case@, the possible drop points for the 'to_drop'
+bindings are:
+ (a) inside the scrutinee
+ (b) inside one of the alternatives/default (default FVs always /first/!).
+
-}
fiExpr dflags to_drop (_, AnnCase scrut case_bndr _ [(con,alt_bndrs,rhs)])
| isUnliftedType (idType case_bndr)
, exprOkForSideEffects (deAnnotate scrut)
- -- See PrimOp, Note [PrimOp can_fail and has_side_effects]
+ -- See Note [Floating primops]
= wrapFloats shared_binds $
fiExpr dflags (case_float : rhs_binds) rhs
where
case_float = FB (mkDVarSet (case_bndr : alt_bndrs)) scrut_fvs
(FloatCase scrut' case_bndr con alt_bndrs)
- scrut' = fiExpr dflags scrut_binds scrut
+ scrut' = fiExpr dflags scrut_binds scrut
+ rhs_fvs = freeVarsOf rhs `delDVarSetList` (case_bndr : alt_bndrs)
+ scrut_fvs = freeVarsOf scrut
+
[shared_binds, scrut_binds, rhs_binds]
= sepBindsByDropPoint dflags False
[scrut_fvs, rhs_fvs]
- (freeVarsOfType scrut `unionDVarSet` rhs_ty_fvs)
to_drop
- rhs_fvs = freeVarsOf rhs `delDVarSetList` (case_bndr : alt_bndrs)
- rhs_ty_fvs = freeVarsOfType rhs `delDVarSetList` (case_bndr : alt_bndrs)
- scrut_fvs = freeVarsOf scrut
fiExpr dflags to_drop (_, AnnCase scrut case_bndr ty alts)
= wrapFloats drop_here1 $
@@ -389,25 +448,20 @@ fiExpr dflags to_drop (_, AnnCase scrut case_bndr ty alts)
[drop_here1, scrut_drops, alts_drops]
= sepBindsByDropPoint dflags False
[scrut_fvs, all_alts_fvs]
- (freeVarsOfType scrut `unionDVarSet` all_alts_ty_fvs)
to_drop
-- Float into the alts with the is_case flag set
(drop_here2 : alts_drops_s)
- = sepBindsByDropPoint dflags True alts_fvs all_alts_ty_fvs
- alts_drops
-
- scrut_fvs = freeVarsOf scrut
- alts_fvs = map alt_fvs alts
- all_alts_fvs = unionDVarSets alts_fvs
- alts_ty_fvs = map alt_ty_fvs alts
- all_alts_ty_fvs = unionDVarSets alts_ty_fvs
+ | [ _ ] <- alts = [] : [alts_drops]
+ | otherwise = sepBindsByDropPoint dflags True alts_fvs alts_drops
+
+ scrut_fvs = freeVarsOf scrut
+ alts_fvs = map alt_fvs alts
+ all_alts_fvs = unionDVarSets alts_fvs
alt_fvs (_con, args, rhs)
- = foldl delDVarSet (freeVarsOf rhs) (case_bndr:args)
- alt_ty_fvs (_con, args, rhs)
- = foldl delDVarSet (freeVarsOfType rhs) (case_bndr:args)
- -- Delete case_bndr and args from free vars of rhs
- -- to get free vars of alt
+ = foldl delDVarSet (freeVarsOf rhs) (case_bndr:args)
+ -- Delete case_bndr and args from free vars of rhs
+ -- to get free vars of alt
fi_alt to_drop (con, args, rhs) = (con, args, fiExpr dflags to_drop rhs)
@@ -417,12 +471,11 @@ fiBind :: DynFlags
-- as far "inwards" as possible
-> CoreBindWithFVs -- Input binding
-> DVarSet -- Free in scope of binding
- -> DVarSet -- Free in type of body of binding
-> ( FloatInBinds -- Land these before
, FloatInBind -- The binding itself
, FloatInBinds) -- Land these after
-fiBind dflags to_drop (AnnNonRec id rhs) body_fvs body_ty_fvs
+fiBind dflags to_drop (AnnNonRec id ann_rhs@(rhs_fvs, rhs)) body_fvs
= ( extra_binds ++ shared_binds -- Land these before
-- See Note [extra_fvs (1,2)]
, FB (unitDVarSet id) rhs_fvs' -- The new binding itself
@@ -431,11 +484,10 @@ fiBind dflags to_drop (AnnNonRec id rhs) body_fvs body_ty_fvs
where
body_fvs2 = body_fvs `delDVarSet` id
- rhs_fvs = freeVarsOf rhs
rule_fvs = idRuleAndUnfoldingVarsDSet id -- See Note [extra_fvs (2): free variables of rules]
- extra_fvs | noFloatIntoRhs (isJoinId id) NonRecursive rhs
- = rule_fvs `unionDVarSet` freeVarsOf rhs
+ extra_fvs | noFloatIntoRhs NonRecursive id rhs
+ = rule_fvs `unionDVarSet` rhs_fvs
| otherwise
= rule_fvs
-- See Note [extra_fvs (1): avoid floating into RHS]
@@ -446,15 +498,14 @@ fiBind dflags to_drop (AnnNonRec id rhs) body_fvs body_ty_fvs
[shared_binds, extra_binds, rhs_binds, body_binds]
= sepBindsByDropPoint dflags False
[extra_fvs, rhs_fvs, body_fvs2]
- (freeVarsOfType rhs `unionDVarSet` body_ty_fvs)
to_drop
-- Push rhs_binds into the right hand side of the binding
- rhs' = fiRhs dflags rhs_binds id rhs
+ rhs' = fiRhs dflags rhs_binds id ann_rhs
rhs_fvs' = rhs_fvs `unionDVarSet` floatedBindsFVs rhs_binds `unionDVarSet` rule_fvs
-- Don't forget the rule_fvs; the binding mentions them!
-fiBind dflags to_drop (AnnRec bindings) body_fvs body_ty_fvs
+fiBind dflags to_drop (AnnRec bindings) body_fvs
= ( extra_binds ++ shared_binds
, FB (mkDVarSet ids) rhs_fvs'
(FloatLet (Rec (fi_bind rhss_binds bindings)))
@@ -466,13 +517,12 @@ fiBind dflags to_drop (AnnRec bindings) body_fvs body_ty_fvs
-- See Note [extra_fvs (1,2)]
rule_fvs = mapUnionDVarSet idRuleAndUnfoldingVarsDSet ids
extra_fvs = rule_fvs `unionDVarSet`
- unionDVarSets [ freeVarsOf rhs | (bndr, rhs) <- bindings
- , noFloatIntoRhs (isJoinId bndr) Recursive rhs ]
+ unionDVarSets [ rhs_fvs | (bndr, (rhs_fvs, rhs)) <- bindings
+ , noFloatIntoRhs Recursive bndr rhs ]
(shared_binds:extra_binds:body_binds:rhss_binds)
= sepBindsByDropPoint dflags False
(extra_fvs:body_fvs:rhss_fvs)
- (body_ty_fvs `unionDVarSet` mapUnionDVarSet freeVarsOfType rhss)
to_drop
rhs_fvs' = unionDVarSets rhss_fvs `unionDVarSet`
@@ -498,45 +548,75 @@ fiRhs dflags to_drop bndr rhs
= fiExpr dflags to_drop rhs
------------------
-okToFloatInside :: [Var] -> Bool
-okToFloatInside bndrs = all ok bndrs
+noFloatIntoLam :: [Var] -> Bool
+noFloatIntoLam bndrs = any bad bndrs
where
- ok b = not (isId b) || isOneShotBndr b
- -- Push the floats inside there are no non-one-shot value binders
+ bad b = isId b && not (isOneShotBndr b)
+ -- Don't float inside a non-one-shot lambda
-noFloatIntoRhs :: Bool -> RecFlag -> CoreExprWithFVs -> Bool
+noFloatIntoRhs :: RecFlag -> Id -> CoreExprWithFVs' -> Bool
-- ^ True if it's a bad idea to float bindings into this RHS
--- Preconditio: rhs :: rhs_ty
-noFloatIntoRhs is_join is_rec rhs@(_, rhs')
- | is_join
- = isRec is_rec -- Joins are one-shot iff non-recursive
- | otherwise
- = isUnliftedType rhs_ty
- -- See Note [Do not destroy the let/app invariant]
- || noFloatIntoExpr rhs'
+noFloatIntoRhs is_rec bndr rhs
+ | isJoinId bndr
+ = isRec is_rec -- Joins are one-shot iff non-recursive
+
+ | otherwise
+ = noFloatIntoArg rhs (idType bndr)
+
+noFloatIntoArg :: CoreExprWithFVs' -> Type -> Bool
+noFloatIntoArg expr expr_ty
+ | isUnliftedType expr_ty
+ = True -- See Note [Do not destroy the let/app invariant]
+
+ | AnnLam bndr e <- expr
+ , (bndrs, _) <- collectAnnBndrs e
+ = noFloatIntoLam (bndr:bndrs) -- Wrinkle 1 (a)
+ || all isTyVar (bndr:bndrs) -- Wrinkle 1 (b)
+ -- See Note [noFloatInto considerations] wrinkle 2
+
+ | otherwise -- Note [noFloatInto considerations] wrinkle 2
+ = exprIsTrivial deann_expr || exprIsHNF deann_expr
where
- rhs_ty = exprTypeFV rhs
-
-noFloatIntoExpr :: CoreExprWithFVs' -> Bool
-noFloatIntoExpr (AnnLam bndr e)
- = not (okToFloatInside (bndr:bndrs))
- -- NB: Must line up with fiExpr (AnnLam...); see Trac #7088
- where
- (bndrs, _) = collectAnnBndrs e
- -- IMPORTANT: don't say 'True' for a RHS with a one-shot lambda at the top.
- -- This makes a big difference for things like
- -- f x# = let x = I# x#
- -- in let j = \() -> ...x...
- -- in if <condition> then normal-path else j ()
- -- If x is used only in the error case join point, j, we must float the
- -- boxing constructor into it, else we box it every time which is very bad
- -- news indeed.
-
-noFloatIntoExpr rhs = exprIsExpandable (deAnnotate' rhs)
- -- We'd just float right back out again...
- -- Should match the test in SimplEnv.doFloatFromRhs
+ deann_expr = deAnnotate' expr
+
+{- Note [noFloatInto considerations]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+When do we want to float bindings into
+ - noFloatIntoRHs: the RHS of a let-binding
+ - noFloatIntoArg: the argument of a function application
+
+Definitely don't float in if it has unlifted type; that
+would destroy the let/app invariant.
+
+* Wrinkle 1: do not float in if
+ (a) any non-one-shot value lambdas
+ or (b) all type lambdas
+ In both cases we'll float straight back out again
+ NB: Must line up with fiExpr (AnnLam...); see Trac #7088
+
+ (a) is important: we /must/ float into a one-shot lambda group
+ (which includes join points). This makes a big difference
+ for things like
+ f x# = let x = I# x#
+ in let j = \() -> ...x...
+ in if <condition> then normal-path else j ()
+ If x is used only in the error case join point, j, we must float the
+ boxing constructor into it, else we box it every time which is very
+ bad news indeed.
+
+* Wrinkle 2: for RHSs, do not float into a HNF; we'll just float right
+ back out again... not tragic, but a waste of time.
+
+ For function arguments we will still end up with this
+ in-then-out stuff; consider
+ letrec x = e in f x
+ Here x is not a HNF, so we'll produce
+ f (letrec x = e in x)
+ which is OK... it's not that common, and we'll end up
+ floating out again, in CorePrep if not earlier.
+ Still, we use exprIsTrivial to catch this case (sigh)
+
-{-
************************************************************************
* *
\subsection{@sepBindsByDropPoint@}
@@ -556,27 +636,21 @@ in it goes. If a binding is used inside {\em multiple} drop points,
then it has to go in a you-must-drop-it-above-all-these-drop-points
point.
-But, with coercions appearing in types, there is a complication: we
-might be floating in a "strict let" -- that is, a case. Case expressions
-mention their return type. We absolutely can't float a coercion binding
-inward to the point that the type of the expression it's about to wrap
-mentions the coercion. So we include the union of the sets of free variables
-of the types of all the drop points involved. If any of the floaters
-bind a coercion variable mentioned in any of the types, that binder must
-be dropped right away.
-
We have to maintain the order on these drop-point-related lists.
-}
+-- pprFIB :: FloatInBinds -> SDoc
+-- pprFIB fibs = text "FIB:" <+> ppr [b | FB _ _ b <- fibs]
+
sepBindsByDropPoint
:: DynFlags
- -> Bool -- True <=> is case expression
- -> [FreeVarSet] -- One set of FVs per drop point
- -> FreeVarSet -- Vars free in all the types of the drop points
- -> FloatInBinds -- Candidate floaters
+ -> Bool -- True <=> is case expression
+ -> [FreeVarSet] -- One set of FVs per drop point
+ -- Always at least two long!
+ -> FloatInBinds -- Candidate floaters
-> [FloatInBinds] -- FIRST one is bindings which must not be floated
- -- inside any drop point; the rest correspond
- -- one-to-one with the input list of FV sets
+ -- inside any drop point; the rest correspond
+ -- one-to-one with the input list of FV sets
-- Every input floater is returned somewhere in the result;
-- none are dropped, not even ones which don't seem to be
@@ -586,12 +660,16 @@ sepBindsByDropPoint
type DropBox = (FreeVarSet, FloatInBinds)
-sepBindsByDropPoint _ _is_case drop_pts _ty_fvs []
- = [] : [[] | _ <- drop_pts] -- cut to the chase scene; it happens
+sepBindsByDropPoint dflags is_case drop_pts floaters
+ | null floaters -- Shortcut common case
+ = [] : [[] | _ <- drop_pts]
-sepBindsByDropPoint dflags is_case drop_pts ty_fvs floaters
- = go floaters (map (\fvs -> (fvs, [])) (emptyDVarSet : drop_pts))
+ | otherwise
+ = ASSERT( length drop_pts >= 2 )
+ go floaters (map (\fvs -> (fvs, [])) (emptyDVarSet : drop_pts))
where
+ n_alts = length drop_pts
+
go :: FloatInBinds -> [DropBox] -> [FloatInBinds]
-- The *first* one in the argument list is the drop_here set
-- The FloatInBinds in the lists are in the reverse of
@@ -606,32 +684,25 @@ sepBindsByDropPoint dflags is_case drop_pts ty_fvs floaters
(used_here : used_in_flags) = [ fvs `intersectsDVarSet` bndrs
| (fvs, _) <- drop_boxes]
- used_in_ty = ty_fvs `intersectsDVarSet` bndrs
- drop_here = used_here || not can_push || used_in_ty
+ drop_here = used_here || cant_push
- -- For case expressions we duplicate the binding if it is
- -- reasonably small, and if it is not used in all the RHSs
- -- This is good for situations like
- -- let x = I# y in
- -- case e of
- -- C -> error x
- -- D -> error x
- -- E -> ...not mentioning x...
-
- n_alts = length used_in_flags
n_used_alts = count id used_in_flags -- returns number of Trues in list.
- can_push = n_used_alts == 1 -- Used in just one branch
- || (is_case && -- We are looking at case alternatives
- n_used_alts > 1 && -- It's used in more than one
- n_used_alts < n_alts && -- ...but not all
- floatIsDupable dflags bind) -- and we can duplicate the binding
+ cant_push
+ | is_case = n_used_alts == n_alts -- Used in all, don't push
+ -- Remember n_alts > 1
+ || (n_used_alts > 1 && not (floatIsDupable dflags bind))
+ -- floatIsDupable: see Note [Duplicating floats]
+
+ | otherwise = floatIsCase bind || n_used_alts > 1
+ -- floatIsCase: see Note [Floating primops]
new_boxes | drop_here = (insert here_box : fork_boxes)
| otherwise = (here_box : new_fork_boxes)
- new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe fork_boxes used_in_flags
+ new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe
+ fork_boxes used_in_flags
insert :: DropBox -> DropBox
insert (fvs,drops) = (fvs `unionDVarSet` bind_fvs, bind_w_fvs:drops)
@@ -642,6 +713,22 @@ sepBindsByDropPoint dflags is_case drop_pts ty_fvs floaters
go _ _ = panic "sepBindsByDropPoint/go"
+{- Note [Duplicating floats]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For case expressions we duplicate the binding if it is reasonably
+small, and if it is not used in all the RHSs This is good for
+situations like
+ let x = I# y in
+ case e of
+ C -> error x
+ D -> error x
+ E -> ...not mentioning x...
+
+If the thing is used in all RHSs there is nothing gained,
+so we don't duplicate then.
+-}
+
floatedBindsFVs :: FloatInBinds -> FreeVarSet
floatedBindsFVs binds = mapUnionDVarSet fbFVs binds
@@ -657,3 +744,7 @@ floatIsDupable :: DynFlags -> FloatBind -> Bool
floatIsDupable dflags (FloatCase scrut _ _ _) = exprIsDupable dflags scrut
floatIsDupable dflags (FloatLet (Rec prs)) = all (exprIsDupable dflags . snd) prs
floatIsDupable dflags (FloatLet (NonRec _ r)) = exprIsDupable dflags r
+
+floatIsCase :: FloatBind -> Bool
+floatIsCase (FloatCase {}) = True
+floatIsCase (FloatLet {}) = False