summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/basicTypes/Demand.hs7
-rw-r--r--compiler/basicTypes/VarEnv.hs5
-rw-r--r--compiler/stranal/DmdAnal.hs188
-rw-r--r--compiler/utils/UniqFM.hs6
-rw-r--r--testsuite/tests/stranal/should_run/all.T3
5 files changed, 128 insertions, 81 deletions
diff --git a/compiler/basicTypes/Demand.hs b/compiler/basicTypes/Demand.hs
index 8dc7f3b895..2ada6b37b9 100644
--- a/compiler/basicTypes/Demand.hs
+++ b/compiler/basicTypes/Demand.hs
@@ -36,7 +36,9 @@ module Demand (
appIsBottom, isBottomingSig, pprIfaceStrictSig,
trimCPRInfo, returnsCPR_maybe,
StrictSig(..), mkStrictSig, mkClosedStrictSig, nopSig, botSig, cprProdSig,
- isTopSig, hasDemandEnvSig, splitStrictSig, increaseStrictSigArity,
+ isTopSig, hasDemandEnvSig,
+ splitStrictSig, strictSigDmdEnv,
+ increaseStrictSigArity,
seqDemand, seqDemandList, seqDmdType, seqStrictSig,
@@ -1682,6 +1684,9 @@ isTopSig (StrictSig ty) = isTopDmdType ty
hasDemandEnvSig :: StrictSig -> Bool
hasDemandEnvSig (StrictSig (DmdType env _ _)) = not (isEmptyVarEnv env)
+strictSigDmdEnv :: StrictSig -> DmdEnv
+strictSigDmdEnv (StrictSig (DmdType env _ _)) = env
+
isBottomingSig :: StrictSig -> Bool
-- True if the signature diverges or throws an exception
isBottomingSig (StrictSig (DmdType _ _ res)) = isBotRes res
diff --git a/compiler/basicTypes/VarEnv.hs b/compiler/basicTypes/VarEnv.hs
index 6e22417de8..146a2fce5f 100644
--- a/compiler/basicTypes/VarEnv.hs
+++ b/compiler/basicTypes/VarEnv.hs
@@ -12,7 +12,8 @@ module VarEnv (
elemVarEnv,
extendVarEnv, extendVarEnv_C, extendVarEnv_Acc, extendVarEnv_Directly,
extendVarEnvList,
- plusVarEnv, plusVarEnv_C, plusVarEnv_CD, alterVarEnv,
+ plusVarEnv, plusVarEnv_C, plusVarEnv_CD, plusVarEnvList,
+ alterVarEnv,
delVarEnvList, delVarEnv, delVarEnv_Directly,
minusVarEnv, intersectsVarEnv,
lookupVarEnv, lookupVarEnv_NF, lookupWithDefaultVarEnv,
@@ -435,6 +436,7 @@ extendVarEnv_C :: (a->a->a) -> VarEnv a -> Var -> a -> VarEnv a
extendVarEnv_Acc :: (a->b->b) -> (a->b) -> VarEnv b -> Var -> a -> VarEnv b
extendVarEnv_Directly :: VarEnv a -> Unique -> a -> VarEnv a
plusVarEnv :: VarEnv a -> VarEnv a -> VarEnv a
+plusVarEnvList :: [VarEnv a] -> VarEnv a
extendVarEnvList :: VarEnv a -> [(Var, a)] -> VarEnv a
lookupVarEnv_Directly :: VarEnv a -> Unique -> Maybe a
@@ -474,6 +476,7 @@ delVarEnv = delFromUFM
minusVarEnv = minusUFM
intersectsVarEnv e1 e2 = not (isEmptyVarEnv (e1 `intersectUFM` e2))
plusVarEnv = plusUFM
+plusVarEnvList = plusUFMList
lookupVarEnv = lookupUFM
filterVarEnv = filterUFM
lookupWithDefaultVarEnv = lookupWithDefaultUFM
diff --git a/compiler/stranal/DmdAnal.hs b/compiler/stranal/DmdAnal.hs
index 44d2d200fb..e2a1dc4493 100644
--- a/compiler/stranal/DmdAnal.hs
+++ b/compiler/stranal/DmdAnal.hs
@@ -62,10 +62,10 @@ dmdAnalTopBind :: AnalEnv
-> CoreBind
-> (AnalEnv, CoreBind)
dmdAnalTopBind sigs (NonRec id rhs)
- = (extendAnalEnv TopLevel sigs id sig, NonRec id2 rhs2)
+ = (extendAnalEnv TopLevel sigs id2 (idStrictness id2), NonRec id2 rhs2)
where
- ( _, _, _, rhs1) = dmdAnalRhsLetDown TopLevel Nothing sigs id rhs
- (sig, _, id2, rhs2) = dmdAnalRhsLetDown TopLevel Nothing (nonVirgin sigs) id rhs1
+ ( _, _, rhs1) = dmdAnalRhsLetDown TopLevel Nothing sigs id rhs
+ ( _, id2, rhs2) = dmdAnalRhsLetDown TopLevel Nothing (nonVirgin sigs) id rhs1
-- Do two passes to improve CPR information
-- See Note [CPR for thunks]
-- See Note [Optimistic CPR in the "virgin" case]
@@ -284,10 +284,11 @@ dmdAnal' env dmd (Let (NonRec id rhs) body)
dmdAnal' env dmd (Let (NonRec id rhs) body)
= (body_ty2, Let (NonRec id2 rhs') body')
where
- (sig, lazy_fv, id1, rhs') = dmdAnalRhsLetDown NotTopLevel Nothing env id rhs
- (body_ty, body') = dmdAnal (extendAnalEnv NotTopLevel env id sig) dmd body
- (body_ty1, id2) = annotateBndr env body_ty id1
- body_ty2 = addLazyFVs body_ty1 lazy_fv
+ (lazy_fv, id1, rhs') = dmdAnalRhsLetDown NotTopLevel Nothing env id rhs
+ env1 = extendAnalEnv NotTopLevel env id1 (idStrictness id1)
+ (body_ty, body') = dmdAnal env1 dmd body
+ (body_ty1, id2) = annotateBndr env body_ty id1
+ body_ty2 = addLazyFVs body_ty1 lazy_fv -- see Note [Lazy and unleasheable free variables]
-- If the actual demand is better than the vanilla call
-- demand, you might think that we might do better to re-analyse
@@ -307,7 +308,7 @@ dmdAnal' env dmd (Let (Rec pairs) body)
(env', lazy_fv, pairs') = dmdFix NotTopLevel env pairs
(body_ty, body') = dmdAnal env' dmd body
body_ty1 = deleteFVs body_ty (map fst pairs)
- body_ty2 = addLazyFVs body_ty1 lazy_fv
+ body_ty2 = addLazyFVs body_ty1 lazy_fv -- see Note [Lazy and unleasheable free variables]
in
body_ty2 `seq`
(body_ty2, Let (Rec pairs') body')
@@ -479,55 +480,53 @@ dmdTransform env var dmd
-- Recursive bindings
dmdFix :: TopLevelFlag
- -> AnalEnv -- Does not include bindings for this binding
+ -> AnalEnv -- Does not include bindings for this binding
-> [(Id,CoreExpr)]
- -> (AnalEnv, DmdEnv,
- [(Id,CoreExpr)]) -- Binders annotated with stricness info
+ -> (AnalEnv, DmdEnv, [(Id,CoreExpr)]) -- Binders annotated with stricness info
dmdFix top_lvl env orig_pairs
- = (updSigEnv env (sigEnv final_env), lazy_fv, pairs')
- -- Return to original virgin state, keeping new signatures
+ = loop 1 initial_pairs
where
- bndrs = map fst orig_pairs
- initial_env = addInitialSigs top_lvl env bndrs
- (final_env, lazy_fv, pairs') = loop 1 initial_env orig_pairs
-
- loop :: Int
- -> AnalEnv -- Already contains the current sigs
- -> [(Id,CoreExpr)]
- -> (AnalEnv, DmdEnv, [(Id,CoreExpr)])
- loop n env pairs
- = -- pprTrace "dmd loop" (ppr n <+> ppr bndrs $$ ppr env) $
- loop' n env pairs
-
- loop' n env pairs
- | found_fixpoint
- = (env', lazy_fv, pairs')
- -- Note: return pairs', not pairs. pairs' is the result of
- -- processing the RHSs with sigs (= sigs'), whereas pairs
- -- is the result of processing the RHSs with the *previous*
- -- iteration of sigs.
-
- | n >= 10
- = -- pprTrace "dmdFix loop" (ppr n <+> (vcat
- -- [ text "Sigs:" <+> ppr [ (id,lookupVarEnv (sigEnv env) id,
- -- lookupVarEnv (sigEnv env') id)
- -- | (id,_) <- pairs],
- -- text "env:" <+> ppr env,
- -- text "binds:" <+> pprCoreBinding (Rec pairs)]))
- (env, lazy_fv, orig_pairs) -- Safe output
- -- The lazy_fv part is really important! orig_pairs has no strictness
- -- info, including nothing about free vars. But if we have
- -- letrec f = ....y..... in ...f...
- -- where 'y' is free in f, we must record that y is mentioned,
- -- otherwise y will get recorded as absent altogether
+ bndrs = map fst orig_pairs
+
+ -- See Note [Initialising strictness]
+ initial_pairs | ae_virgin env = [(setIdStrictness id botSig, rhs) | (id, rhs) <- orig_pairs ]
+
+ | otherwise = orig_pairs
+
+ -- If fixed-point iteration does not yield a result we use this instead
+ -- See Note [Safe abortion in the fixed-point iteration]
+ abort :: (AnalEnv, DmdEnv, [(Id,CoreExpr)])
+ abort = (env, lazy_fv', zapped_pairs)
+ where (lazy_fv, pairs') = step True (zapIdStrictness orig_pairs)
+ -- Note [Lazy and unleasheable free variables]
+ non_lazy_fvs = plusVarEnvList $ map (strictSigDmdEnv . idStrictness . fst) pairs'
+ lazy_fv' = lazy_fv `plusVarEnv` mapVarEnv (const topDmd) non_lazy_fvs
+ zapped_pairs = zapIdStrictness pairs'
+
+ -- The fixed-point varies the idStrictness field of the binders, and terminates if that
+ -- annotation does not change any more.
+ loop :: Int -> [(Id,CoreExpr)] -> (AnalEnv, DmdEnv, [(Id,CoreExpr)])
+ loop n pairs
+ | found_fixpoint = (final_anal_env, lazy_fv, pairs')
+ | n == 10 = abort
+ | otherwise = loop (n+1) pairs'
+ where
+ found_fixpoint = map (idStrictness . fst) pairs' == map (idStrictness . fst) pairs
+ first_round = n == 1
+ (lazy_fv, pairs') = step first_round pairs
+ final_anal_env = extendAnalEnvs top_lvl env (map fst pairs')
- | otherwise
- = loop (n+1) (nonVirgin env') pairs'
+ step :: Bool -> [(Id, CoreExpr)] -> (DmdEnv, [(Id, CoreExpr)])
+ step first_round pairs = (lazy_fv, pairs')
where
- found_fixpoint = all (same_sig (sigEnv env) (sigEnv env')) bndrs
+ -- In all but the first iteration, delete the virgin flag
+ start_env | first_round = env
+ | otherwise = nonVirgin env
+
+ start = (extendAnalEnvs top_lvl start_env (map fst pairs), emptyDmdEnv)
- ((env',lazy_fv), pairs') = mapAccumL my_downRhs (env, emptyDmdEnv) pairs
+ ((_,lazy_fv), pairs') = mapAccumL my_downRhs start pairs
-- mapAccumL: Use the new signature to do the next pair
-- The occurrence analyser has arranged them in a good order
-- so this can significantly reduce the number of iterations needed
@@ -535,23 +534,39 @@ dmdFix top_lvl env orig_pairs
my_downRhs (env, lazy_fv) (id,rhs)
= ((env', lazy_fv'), (id', rhs'))
where
- (sig, lazy_fv1, id', rhs') = dmdAnalRhsLetDown top_lvl (Just bndrs) env id rhs
- lazy_fv' = plusVarEnv_C bothDmd lazy_fv lazy_fv1
- env' = extendAnalEnv top_lvl env id sig
+ (lazy_fv1, id', rhs') = dmdAnalRhsLetDown top_lvl (Just bndrs) env id rhs
+ lazy_fv' = plusVarEnv_C bothDmd lazy_fv lazy_fv1
+ env' = extendAnalEnv top_lvl env id (idStrictness id')
+
+
+ zapIdStrictness :: [(Id, CoreExpr)] -> [(Id, CoreExpr)]
+ zapIdStrictness pairs = [(setIdStrictness id nopSig, rhs) | (id, rhs) <- pairs ]
+
+{-
+Note [Safe abortion in the fixed-point iteration]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Fixed-point iteration may fail to terminate. But we cannot simply give up and
+return the environment and code unchanged! We still need to do one additional
+round, for two reasons:
+
+ * To get information on used free variables (both lazy and strict!)
+ (see Note [Lazy and unleasheable free variables])
+ * To ensure that all expressions have been traversed at least once, and any left-over
+ strictness annotations have been updated.
- same_sig sigs sigs' var = lookup sigs var == lookup sigs' var
- lookup sigs var = case lookupVarEnv sigs var of
- Just (sig,_) -> sig
- Nothing -> pprPanic "dmdFix" (ppr var)
+This final iteration does not add the variables to the strictness signature
+environment, which effectively assigns them 'nopSig' (see "getStrictness")
+-}
-- Trivial RHS
-- See Note [Demand analysis for trivial right-hand sides]
dmdAnalTrivialRhs ::
AnalEnv -> Id -> CoreExpr -> Var ->
- (StrictSig, VarEnv Demand, Id, CoreExpr)
+ (DmdEnv, Id, CoreExpr)
dmdAnalTrivialRhs env id rhs fn
- = (fn_str, fn_fv, set_idStrictness env id fn_str, rhs)
+ = (fn_fv, set_idStrictness env id fn_str, rhs)
where
fn_str = getStrictness env fn
fn_fv | isLocalId fn = unitVarEnv fn topDmd
@@ -579,7 +594,7 @@ dmdAnalTrivialRhs env id rhs fn
dmdAnalRhsLetDown :: TopLevelFlag
-> Maybe [Id] -- Just bs <=> recursive, Nothing <=> non-recursive
-> AnalEnv -> Id -> CoreExpr
- -> (StrictSig, DmdEnv, Id, CoreExpr)
+ -> (DmdEnv, Id, CoreExpr)
-- Process the RHS of the binding, add the strictness signature
-- to the Id, and augment the environment with the signature as well.
dmdAnalRhsLetDown top_lvl rec_flag env id rhs
@@ -587,7 +602,7 @@ dmdAnalRhsLetDown top_lvl rec_flag env id rhs
= dmdAnalTrivialRhs env id rhs fn
| otherwise
- = (sig_ty, lazy_fv, id', mkLams bndrs' body')
+ = (lazy_fv, id', mkLams bndrs' body')
where
(bndrs, body) = collectBinders rhs
env_body = foldl extendSigsWithLam env bndrs
@@ -604,12 +619,12 @@ dmdAnalRhsLetDown top_lvl rec_flag env id rhs
Nothing -> cleanEvalDmd
Just (dc, _, _, _) -> cleanEvalProdDmd (dataConRepArity dc)
- -- See Note [Lazy and unleashable free variables]
-- See Note [Aggregated demand for cardinality]
rhs_fv1 = case rec_flag of
Just bs -> reuseEnv (delVarEnvList rhs_fv bs)
Nothing -> rhs_fv
+ -- See Note [Lazy and unleashable free variables]
(lazy_fv, sig_fv) = splitFVs is_thunk rhs_fv1
rhs_res' = trimCPRInfo trim_all trim_sums rhs_res
@@ -946,7 +961,7 @@ error stub, but which has RULES, you may want it not to be eliminated
in favour of error!
Note [Lazy and unleasheable free variables]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We put the strict and once-used FVs in the DmdType of the Id, so
that at its call sites we unleash demands on its strict fvs.
An example is 'roll' in imaginary/wheel-sieve2
@@ -974,9 +989,32 @@ Incidentally, here's a place where lambda-lifting h would
lose the cigar --- we couldn't see the joint strictness in t/x
ON THE OTHER HAND
+
We don't want to put *all* the fv's from the RHS into the
-DmdType, because that makes fixpointing very slow --- the
-DmdType gets full of lazy demands that are slow to converge.
+DmdType. Because
+
+ * it makes the strictness signatures larger, and hence slows down fixpointing
+
+and
+
+ * it is useless information at the call site anyways:
+ For lazy, used-many times fv's we will never get any better result than
+ that, no matter how good the actual demand on the function at the call site
+ is (unless it is always absent, but then the whole binder is useless).
+
+Therefore we exclude lazy multiple-used fv's from the environment in the
+DmdType.
+
+But now the signature lies! (Missing variables are assumed to be absent.) To
+make up for this, the code that analyses the binding keeps the demand on those
+variable separate (usually called "lazy_fv") and adds it to the demand of the
+whole binding later.
+
+What if we decide _not_ to store a strictness signature for a binding at all, as
+we do when aborting a fixed-point iteration? The we risk losing the information
+that the strict variables are being used. In that case, we take all free variables
+mentioned in the (unsound) strictness signature, conservatively approximate the
+demand put on them (topDmd), and add that to the "lazy_fv" returned by "dmdFix".
Note [Lamba-bound unfoldings]
@@ -1037,11 +1075,14 @@ emptyAnalEnv dflags fam_envs
emptySigEnv :: SigEnv
emptySigEnv = emptyVarEnv
-sigEnv :: AnalEnv -> SigEnv
-sigEnv = ae_sigs
+-- | Extend an environment with the strictness IDs attached to the id
+extendAnalEnvs :: TopLevelFlag -> AnalEnv -> [Id] -> AnalEnv
+extendAnalEnvs top_lvl env vars
+ = env { ae_sigs = extendSigEnvs top_lvl (ae_sigs env) vars }
-updSigEnv :: AnalEnv -> SigEnv -> AnalEnv
-updSigEnv env sigs = env { ae_sigs = sigs }
+extendSigEnvs :: TopLevelFlag -> SigEnv -> [Id] -> SigEnv
+extendSigEnvs top_lvl sigs vars
+ = extendVarEnvList sigs [ (var, (idStrictness var, top_lvl)) | var <- vars]
extendAnalEnv :: TopLevelFlag -> AnalEnv -> Id -> StrictSig -> AnalEnv
extendAnalEnv top_lvl env var sig
@@ -1059,15 +1100,6 @@ getStrictness env fn
| Just (sig, _) <- lookupSigEnv env fn = sig
| otherwise = nopSig
-addInitialSigs :: TopLevelFlag -> AnalEnv -> [Id] -> AnalEnv
--- See Note [Initialising strictness]
-addInitialSigs top_lvl env@(AE { ae_sigs = sigs, ae_virgin = virgin }) ids
- = env { ae_sigs = extendVarEnvList sigs [ (id, (init_sig id, top_lvl))
- | id <- ids ] }
- where
- init_sig | virgin = \_ -> botSig
- | otherwise = idStrictness
-
nonVirgin :: AnalEnv -> AnalEnv
nonVirgin env = env { ae_virgin = False }
diff --git a/compiler/utils/UniqFM.hs b/compiler/utils/UniqFM.hs
index 244969cc91..be5da8373b 100644
--- a/compiler/utils/UniqFM.hs
+++ b/compiler/utils/UniqFM.hs
@@ -49,6 +49,7 @@ module UniqFM (
plusUFM,
plusUFM_C,
plusUFM_CD,
+ plusUFMList,
minusUFM,
intersectUFM,
intersectUFM_C,
@@ -71,6 +72,8 @@ module UniqFM (
import Unique ( Uniquable(..), Unique, getKey )
import Outputable
+import Data.List (foldl')
+
import qualified Data.IntMap as M
import qualified Data.IntSet as S
import Data.Typeable
@@ -214,6 +217,9 @@ plusUFM_CD f (UFM xm) dx (UFM ym) dy
(M.map (\y -> dx `f` y))
xm ym
+plusUFMList :: [UniqFM elt] -> UniqFM elt
+plusUFMList = foldl' plusUFM emptyUFM
+
minusUFM :: UniqFM elt1 -> UniqFM elt2 -> UniqFM elt1
minusUFM (UFM x) (UFM y) = UFM (M.difference x y)
diff --git a/testsuite/tests/stranal/should_run/all.T b/testsuite/tests/stranal/should_run/all.T
index 6846c8281c..d3d4aaff9d 100644
--- a/testsuite/tests/stranal/should_run/all.T
+++ b/testsuite/tests/stranal/should_run/all.T
@@ -12,4 +12,5 @@ test('T10148', normal, compile_and_run, [''])
test('T10218', normal, compile_and_run, [''])
test('T11076', normal, multimod_compile_and_run, ['T11076.hs', 'T11076_prim.cmm'])
test('T11555a', normal, compile_and_run, [''])
-test('T12368', [ exit_code(1), expect_broken(12368) ], compile_and_run, [''])
+test('T12368', exit_code(1), compile_and_run, [''])
+test('T12368a', exit_code(1), compile_and_run, [''])