Andreas Klebinger pushed to branch wip/andreask/spec_tyfams at Glasgow Haskell Compiler / GHC
Commits:
-
fcb5f7ee
by Andreas Klebinger at 2025-05-23T10:09:16+02:00
15 changed files:
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Predicate.hs
- compiler/GHC/Tc/Solver.hs
- compiler/GHC/Tc/Solver/Dict.hs
- compiler/GHC/Tc/Solver/InertSet.hs
- compiler/GHC/Tc/Solver/Monad.hs
- compiler/GHC/Tc/Utils/TcType.hs
- + testsuite/tests/perf/should_run/SpecTyFamRun.hs
- + testsuite/tests/perf/should_run/SpecTyFamRun.stdout
- + testsuite/tests/perf/should_run/SpecTyFam_Import.hs
- testsuite/tests/perf/should_run/all.T
- + testsuite/tests/simplCore/should_compile/T26051.hs
- + testsuite/tests/simplCore/should_compile/T26051.stderr
- + testsuite/tests/simplCore/should_compile/T26051_Import.hs
- testsuite/tests/simplCore/should_compile/all.T
Changes:
1 | +{-# LANGUAGE MultiWayIf #-}
|
|
2 | + |
|
1 | 3 | {-
|
2 | 4 | (c) The GRASP/AQUA Project, Glasgow University, 1993-1998
|
3 | 5 | |
... | ... | @@ -14,9 +16,9 @@ import GHC.Driver.Config.Diagnostic |
14 | 16 | import GHC.Driver.Config.Core.Rules ( initRuleOpts )
|
15 | 17 | |
16 | 18 | import GHC.Core.Type hiding( substTy, substCo, extendTvSubst, zapSubst )
|
17 | -import GHC.Core.Multiplicity
|
|
18 | -import GHC.Core.SimpleOpt( defaultSimpleOpts, simpleOptExprWith )
|
|
19 | +import GHC.Core.SimpleOpt( defaultSimpleOpts, simpleOptExprWith, exprIsConApp_maybe )
|
|
19 | 20 | import GHC.Core.Predicate
|
21 | +import GHC.Core.Class( classMethods )
|
|
20 | 22 | import GHC.Core.Coercion( Coercion )
|
21 | 23 | import GHC.Core.Opt.Monad
|
22 | 24 | import qualified GHC.Core.Subst as Core
|
... | ... | @@ -26,12 +28,12 @@ import GHC.Core.Make ( mkLitRubbish ) |
26 | 28 | import GHC.Core.Unify ( tcMatchTy )
|
27 | 29 | import GHC.Core.Rules
|
28 | 30 | import GHC.Core.Utils ( exprIsTrivial, exprIsTopLevelBindable
|
29 | - , mkCast, exprType
|
|
31 | + , mkCast, exprType, exprIsHNF
|
|
30 | 32 | , stripTicksTop, mkInScopeSetBndrs )
|
31 | 33 | import GHC.Core.FVs
|
32 | 34 | import GHC.Core.TyCo.FVs ( tyCoVarsOfTypeList )
|
33 | 35 | import GHC.Core.Opt.Arity( collectBindersPushingCo )
|
34 | --- import GHC.Core.Ppr( pprIds )
|
|
36 | +import GHC.Core.Ppr( pprIds )
|
|
35 | 37 | |
36 | 38 | import GHC.Builtin.Types ( unboxedUnitTy )
|
37 | 39 | |
... | ... | @@ -64,8 +66,11 @@ import GHC.Unit.Module.ModGuts |
64 | 66 | import GHC.Core.Unfold
|
65 | 67 | |
66 | 68 | import Data.List( partition )
|
67 | --- import Data.List.NonEmpty ( NonEmpty (..) )
|
|
68 | 69 | import GHC.Core.Subst (substTickish)
|
70 | +import GHC.Core.TyCon (tyConClass_maybe)
|
|
71 | +import GHC.Core.DataCon (dataConTyCon)
|
|
72 | + |
|
73 | +import Control.Monad
|
|
69 | 74 | |
70 | 75 | {-
|
71 | 76 | ************************************************************************
|
... | ... | @@ -1585,9 +1590,9 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
1585 | 1590 | -- switch off specialisation for inline functions
|
1586 | 1591 | |
1587 | 1592 | = -- pprTrace "specCalls: some" (vcat
|
1588 | - -- [ text "function" <+> ppr fn
|
|
1589 | - -- , text "calls:" <+> ppr calls_for_me
|
|
1590 | - -- , text "subst" <+> ppr (se_subst env) ]) $
|
|
1593 | + -- [ text "function" <+> ppr fn
|
|
1594 | + -- , text "calls:" <+> ppr calls_for_me
|
|
1595 | + -- , text "subst" <+> ppr (se_subst env) ]) $
|
|
1591 | 1596 | foldlM spec_call ([], [], emptyUDs) calls_for_me
|
1592 | 1597 | |
1593 | 1598 | | otherwise -- No calls or RHS doesn't fit our preconceptions
|
... | ... | @@ -1635,21 +1640,21 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
1635 | 1640 | , rule_bndrs, rule_lhs_args
|
1636 | 1641 | , spec_bndrs1, dx_binds, spec_args) <- specHeader env rhs_bndrs all_call_args
|
1637 | 1642 | |
1638 | --- ; pprTrace "spec_call" (vcat
|
|
1639 | --- [ text "fun: " <+> ppr fn
|
|
1640 | --- , text "call info: " <+> ppr _ci
|
|
1641 | --- , text "useful: " <+> ppr useful
|
|
1642 | --- , text "rule_bndrs:" <+> ppr rule_bndrs
|
|
1643 | --- , text "lhs_args: " <+> ppr rule_lhs_args
|
|
1644 | --- , text "spec_bndrs1:" <+> ppr spec_bndrs1
|
|
1645 | --- , text "leftover_bndrs:" <+> pprIds leftover_bndrs
|
|
1646 | --- , text "spec_args: " <+> ppr spec_args
|
|
1647 | --- , text "dx_binds: " <+> ppr dx_binds
|
|
1648 | --- , text "rhs_bndrs" <+> ppr rhs_bndrs
|
|
1649 | --- , text "rhs_body" <+> ppr rhs_body
|
|
1650 | --- , text "rhs_env2: " <+> ppr (se_subst rhs_env2)
|
|
1651 | --- , ppr dx_binds ]) $
|
|
1652 | --- return ()
|
|
1643 | + ; when False $ pprTrace "spec_call" (vcat
|
|
1644 | + [ text "fun: " <+> ppr fn
|
|
1645 | + , text "call info: " <+> ppr _ci
|
|
1646 | + , text "useful: " <+> ppr useful
|
|
1647 | + , text "rule_bndrs:" <+> ppr rule_bndrs
|
|
1648 | + , text "lhs_args: " <+> ppr rule_lhs_args
|
|
1649 | + , text "spec_bndrs1:" <+> ppr spec_bndrs1
|
|
1650 | + , text "leftover_bndrs:" <+> pprIds leftover_bndrs
|
|
1651 | + , text "spec_args: " <+> ppr spec_args
|
|
1652 | + , text "dx_binds: " <+> ppr dx_binds
|
|
1653 | + , text "rhs_bndrs" <+> ppr rhs_bndrs
|
|
1654 | + , text "rhs_body" <+> ppr rhs_body
|
|
1655 | + , text "rhs_env2: " <+> ppr (se_subst rhs_env2)
|
|
1656 | + , ppr dx_binds ]) $
|
|
1657 | + return ()
|
|
1653 | 1658 | |
1654 | 1659 | ; let all_rules = rules_acc ++ existing_rules
|
1655 | 1660 | -- all_rules: we look both in the rules_acc (generated by this invocation
|
... | ... | @@ -3043,30 +3048,15 @@ mkCallUDs' env f args |
3043 | 3048 | -- For "invisibleFunArg", which are the type-class dictionaries,
|
3044 | 3049 | -- we decide on a case by case basis if we want to specialise
|
3045 | 3050 | -- on this argument; if so, SpecDict, if not UnspecArg
|
3046 | - mk_spec_arg arg (Anon pred af)
|
|
3051 | + mk_spec_arg arg (Anon _pred af)
|
|
3047 | 3052 | | isInvisibleFunArg af
|
3048 | - , interestingDict arg (scaledThing pred)
|
|
3053 | + , interestingDict env arg
|
|
3054 | + -- , interestingDict arg (scaledThing pred)
|
|
3049 | 3055 | -- See Note [Interesting dictionary arguments]
|
3050 | 3056 | = SpecDict arg
|
3051 | 3057 | |
3052 | 3058 | | otherwise = UnspecArg
|
3053 | 3059 | |
3054 | -{-
|
|
3055 | -Note [Ticks on applications]
|
|
3056 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
3057 | -Ticks such as source location annotations can sometimes make their way
|
|
3058 | -onto applications (see e.g. #21697). So if we see something like
|
|
3059 | - |
|
3060 | - App (Tick _ f) e
|
|
3061 | - |
|
3062 | -we need to descend below the tick to find what the real function being
|
|
3063 | -applied is.
|
|
3064 | - |
|
3065 | -The resulting RULE also has to be able to match this annotated use
|
|
3066 | -site, so we only look through ticks that RULE matching looks through
|
|
3067 | -(see Note [Tick annotations in RULE matching] in GHC.Core.Rules).
|
|
3068 | --}
|
|
3069 | - |
|
3070 | 3060 | wantCallsFor :: SpecEnv -> Id -> Bool
|
3071 | 3061 | -- See Note [wantCallsFor]
|
3072 | 3062 | wantCallsFor _env f
|
... | ... | @@ -3086,8 +3076,60 @@ wantCallsFor _env f |
3086 | 3076 | WorkerLikeId {} -> True
|
3087 | 3077 | RepPolyId {} -> True
|
3088 | 3078 | |
3089 | -{- Note [wantCallsFor]
|
|
3090 | -~~~~~~~~~~~~~~~~~~~~~~
|
|
3079 | +interestingDict :: SpecEnv -> CoreExpr -> Bool
|
|
3080 | +-- This is a subtle and important function
|
|
3081 | +-- See Note [Interesting dictionary arguments]
|
|
3082 | +interestingDict env (Var v) -- See (ID3) and (ID5)
|
|
3083 | + | Just rhs <- maybeUnfoldingTemplate (idUnfolding v)
|
|
3084 | + -- might fail for loop breaker dicts but that seems fine.
|
|
3085 | + = interestingDict env rhs
|
|
3086 | + |
|
3087 | +interestingDict env arg -- Main Plan: use exprIsConApp_maybe
|
|
3088 | + | Cast inner_arg _ <- arg -- See (ID5)
|
|
3089 | + = if | isConstraintKind $ typeKind $ exprType inner_arg
|
|
3090 | + -- If coercions were always homo-kinded, we'd know
|
|
3091 | + -- that this would be the only case
|
|
3092 | + -> interestingDict env inner_arg
|
|
3093 | + |
|
3094 | + -- Check for an implicit parameter at the top
|
|
3095 | + | Just (cls,_) <- getClassPredTys_maybe arg_ty
|
|
3096 | + , isIPClass cls -- See (ID4)
|
|
3097 | + -> False
|
|
3098 | + |
|
3099 | + -- Otherwise we are unwrapping a unary type class
|
|
3100 | + | otherwise
|
|
3101 | + -> exprIsHNF arg -- See (ID7)
|
|
3102 | + |
|
3103 | + | Just (_, _, data_con, _tys, args) <- exprIsConApp_maybe in_scope_env arg
|
|
3104 | + , Just cls <- tyConClass_maybe (dataConTyCon data_con)
|
|
3105 | + , not_ip_like -- See (ID4)
|
|
3106 | + = if null (classMethods cls) -- See (ID6)
|
|
3107 | + then any (interestingDict env) args
|
|
3108 | + else True
|
|
3109 | + |
|
3110 | + | otherwise
|
|
3111 | + = not (exprIsTrivial arg) && not_ip_like -- See (ID8)
|
|
3112 | + where
|
|
3113 | + arg_ty = exprType arg
|
|
3114 | + not_ip_like = not (couldBeIPLike arg_ty)
|
|
3115 | + in_scope_env = ISE (substInScopeSet $ se_subst env) realIdUnfolding
|
|
3116 | + |
|
3117 | +{- Note [Ticks on applications]
|
|
3118 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
3119 | +Ticks such as source location annotations can sometimes make their way
|
|
3120 | +onto applications (see e.g. #21697). So if we see something like
|
|
3121 | + |
|
3122 | + App (Tick _ f) e
|
|
3123 | + |
|
3124 | +we need to descend below the tick to find what the real function being
|
|
3125 | +applied is.
|
|
3126 | + |
|
3127 | +The resulting RULE also has to be able to match this annotated use
|
|
3128 | +site, so we only look through ticks that RULE matching looks through
|
|
3129 | +(see Note [Tick annotations in RULE matching] in GHC.Core.Rules).
|
|
3130 | + |
|
3131 | +Note [wantCallsFor]
|
|
3132 | +~~~~~~~~~~~~~~~~~~~
|
|
3091 | 3133 | `wantCallsFor env f` says whether the Specialiser should collect calls for
|
3092 | 3134 | function `f`; other thing being equal, the fewer calls we collect the better. It
|
3093 | 3135 | is False for things we can't specialise:
|
... | ... | @@ -3113,44 +3155,91 @@ collect usage info for imported overloaded functions. |
3113 | 3155 | |
3114 | 3156 | Note [Interesting dictionary arguments]
|
3115 | 3157 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
3116 | -In `mkCallUDs` we only use `SpecDict` for dictionaries of which
|
|
3117 | -`interestingDict` holds. Otherwise we use `UnspecArg`. Two reasons:
|
|
3118 | - |
|
3119 | -* Consider this
|
|
3120 | - \a.\d:Eq a. let f = ... in ...(f d)...
|
|
3121 | - There really is not much point in specialising f wrt the dictionary d,
|
|
3122 | - because the code for the specialised f is not improved at all, because
|
|
3123 | - d is lambda-bound. We simply get junk specialisations.
|
|
3124 | - |
|
3125 | -* Consider this (#25703):
|
|
3126 | - f :: (Eq a, Show b) => a -> b -> INt
|
|
3127 | - goo :: forall x. (Eq x) => x -> blah
|
|
3128 | - goo @x (d:Eq x) (arg:x) = ...(f @x @Int d $fShowInt)...
|
|
3129 | - If we built a `ci_key` with a (SpecDict d) for `d`, we would end up
|
|
3130 | - discarding the call at the `\d`. But if we use `UnspecArg` for that
|
|
3131 | - uninteresting `d`, we'll get a `ci_key` of
|
|
3132 | - f @x @Int UnspecArg (SpecDict $fShowInt)
|
|
3133 | - and /that/ can float out to f's definition and specialise nicely.
|
|
3134 | - Hooray. (NB: the call can float only if `-fpolymorphic-specialisation`
|
|
3135 | - is on; otherwise it'll be trapped by the `\@x -> ...`.)(
|
|
3136 | - |
|
3137 | -What is "interesting"? (See `interestingDict`.) Just that it has *some*
|
|
3138 | -structure. But what about variables? We look in the variable's /unfolding/.
|
|
3139 | -And that means that we must be careful to ensure that dictionaries /have/
|
|
3140 | -unfoldings,
|
|
3141 | - |
|
3142 | -* cloneBndrSM discards non-Stable unfoldings
|
|
3143 | -* specBind updates the unfolding after specialisation
|
|
3144 | - See Note [Update unfolding after specialisation]
|
|
3145 | -* bindAuxiliaryDict adds an unfolding for an aux dict
|
|
3146 | - see Note [Specialisation modulo dictionary selectors]
|
|
3147 | -* specCase adds unfoldings for the new bindings it creates
|
|
3148 | - |
|
3149 | -We accidentally lost accurate tracking of local variables for a long
|
|
3150 | -time, because cloned variables didn't have unfoldings. But makes a
|
|
3151 | -massive difference in a few cases, eg #5113. For nofib as a
|
|
3152 | -whole it's only a small win: 2.2% improvement in allocation for ansi,
|
|
3153 | -1.2% for bspt, but mostly 0.0! Average 0.1% increase in binary size.
|
|
3158 | +Consider this
|
|
3159 | + \a.\d:Eq a. let f = ... in ...(f d)...
|
|
3160 | +There really is not much point in specialising f wrt the dictionary d,
|
|
3161 | +because the code for the specialised f is not improved at all, because
|
|
3162 | +d is lambda-bound. We simply get junk specialisations.
|
|
3163 | + |
|
3164 | +What is "interesting"? Our Main Plan is to use `exprIsConApp_maybe` to see
|
|
3165 | +if the argument is a dictionary constructor applied to some arguments, in which
|
|
3166 | +case we can clearly specialise. But there are wrinkles:
|
|
3167 | + |
|
3168 | +(ID1) Note that we look at the argument /term/, not its /type/. Suppose the
|
|
3169 | + argument is
|
|
3170 | + (% d1, d2 %) |> co
|
|
3171 | + where co :: (% Eq [a], Show [a] %) ~ F Int a, and `F` is a type family.
|
|
3172 | + Then its type (F Int a) looks very un-informative, but the term is super
|
|
3173 | + helpful. See #19747 (where missing this point caused a 70x slow down)
|
|
3174 | + and #7785.
|
|
3175 | + |
|
3176 | +(ID2) Note that the Main Plan works fine for an argument that is a DFun call,
|
|
3177 | + e.g. $fOrdList $dOrdInt
|
|
3178 | + because `exprIsConApp_maybe` cleverly deals with DFunId applications. Good!
|
|
3179 | + |
|
3180 | +(ID3) For variables, we look in the variable's /unfolding/. And that means
|
|
3181 | + that we must be careful to ensure that dictionaries /have/ unfoldings:
|
|
3182 | + * cloneBndrSM discards non-Stable unfoldings
|
|
3183 | + * specBind updates the unfolding after specialisation
|
|
3184 | + See Note [Update unfolding after specialisation]
|
|
3185 | + * bindAuxiliaryDict adds an unfolding for an aux dict
|
|
3186 | + see Note [Specialisation modulo dictionary selectors]
|
|
3187 | + * specCase adds unfoldings for the new bindings it creates
|
|
3188 | + |
|
3189 | + We accidentally lost accurate tracking of local variables for a long
|
|
3190 | + time, because cloned variables didn't have unfoldings. But makes a
|
|
3191 | + massive difference in a few cases, eg #5113. For nofib as a
|
|
3192 | + whole it's only a small win: 2.2% improvement in allocation for ansi,
|
|
3193 | + 1.2% for bspt, but mostly 0.0! Average 0.1% increase in binary size.
|
|
3194 | + |
|
3195 | +(ID4) We must be very careful not to specialise on a "dictionary" that is, or contains
|
|
3196 | + an implicit parameter, because implicit parameters are emphatically not singleton
|
|
3197 | + types. See #25999:
|
|
3198 | + useImplicit :: (?i :: Int) => Int
|
|
3199 | + useImplicit = ?i + 1
|
|
3200 | + |
|
3201 | + foo = let ?i = 1 in (useImplicit, let ?i = 2 in useImplicit)
|
|
3202 | + Both calls to `useImplicit` are at type `?i::Int`, but they pass different values.
|
|
3203 | + We must not specialise on implicit parameters! Hence the call to `couldBeIPLike`.
|
|
3204 | + |
|
3205 | +(ID5) Suppose the argument is (e |> co). Can we rely on `exprIsConApp_maybe` to deal
|
|
3206 | + with the coercion. No! That only works if (co :: C t1 ~ C t2) with the same type
|
|
3207 | + constructor at the top of both sides. But see the example in (ID1), where that
|
|
3208 | + is not true. For thes same reason, we can't rely on `exprIsConApp_maybe` to look
|
|
3209 | + through unfoldings (because there might be a cast inside), hence dealing with
|
|
3210 | + expandable unfoldings in `interestingDict` directly.
|
|
3211 | + |
|
3212 | +(ID6) The Main Plan says that it's worth specialising if the argument is an application
|
|
3213 | + of a dictionary contructor. But what if the dictionary has no methods? Then we
|
|
3214 | + gain nothing by specialising, unless the /superclasses/ are interesting. A case
|
|
3215 | + in point is constraint tuples (% d1, .., dn %); a constraint N-tuple is a class
|
|
3216 | + with N superclasses and no methods.
|
|
3217 | + |
|
3218 | +(ID7) A unary (single-method) class is currently represented by (meth |> co). We
|
|
3219 | + will unwrap the cast (see (ID5)) and then want to reply "yes" if the method
|
|
3220 | + has any struture. We rather arbitrarily use `exprIsHNF` for this. (We plan a
|
|
3221 | + new story for unary classes, see #23109, and this special case will become
|
|
3222 | + irrelevant.)
|
|
3223 | + |
|
3224 | +(ID8) Sadly, if `exprIsConApp_maybe` says Nothing, we still want to treat a
|
|
3225 | + non-trivial argument as interesting. In T19695 we have this:
|
|
3226 | + askParams :: Monad m => blah
|
|
3227 | + mhelper :: MonadIO m => blah
|
|
3228 | + mhelper (d:MonadIO m) = ...(askParams @m ($p1 d))....
|
|
3229 | + where `$p1` is the superclass selector for `MonadIO`. Now, if `mhelper` is
|
|
3230 | + specialised at `Handler` we'll get this call in the specialised `$smhelper`:
|
|
3231 | + askParams @Handler ($p1 $fMonadIOHandler)
|
|
3232 | + and we /definitely/ want to specialise that, even though the argument isn't
|
|
3233 | + visibly a dictionary application. In fact the specialiser fires the superclass
|
|
3234 | + selector rule (see Note [Fire rules in the specialiser]), so we get
|
|
3235 | + askParams @Handler ($cp1MonadIO $fMonadIOIO)
|
|
3236 | + but it /still/ doesn't look like a dictionary application.
|
|
3237 | + |
|
3238 | + Conclusion: we optimistically assume that any non-trivial argument is worth
|
|
3239 | + specialising on.
|
|
3240 | + |
|
3241 | + So why do the `exprIsConApp_maybe` and `Cast` stuff? Because we want to look
|
|
3242 | + under type-family casts (ID1) and constraint tuples (ID6).
|
|
3154 | 3243 | |
3155 | 3244 | Note [Update unfolding after specialisation]
|
3156 | 3245 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
... | ... | @@ -3178,6 +3267,7 @@ Consider (#21848) |
3178 | 3267 | Now `f` turns into:
|
3179 | 3268 | |
3180 | 3269 | f @a @b (dd :: D a) (ds :: Show b) a b
|
3270 | + |
|
3181 | 3271 | = let dc :: D a = %p1 dd -- Superclass selection
|
3182 | 3272 | in meth @a dc ....
|
3183 | 3273 | meth @a dc ....
|
... | ... | @@ -3193,27 +3283,6 @@ in the NonRec case of specBind. (This is too exotic to trouble with |
3193 | 3283 | the Rec case.)
|
3194 | 3284 | -}
|
3195 | 3285 | |
3196 | -interestingDict :: CoreExpr -> Type -> Bool
|
|
3197 | --- A dictionary argument is interesting if it has *some* structure,
|
|
3198 | --- see Note [Interesting dictionary arguments]
|
|
3199 | --- NB: "dictionary" arguments include constraints of all sorts,
|
|
3200 | --- including equality constraints; hence the Coercion case
|
|
3201 | --- To make this work, we need to ensure that dictionaries have
|
|
3202 | --- unfoldings in them.
|
|
3203 | -interestingDict arg arg_ty
|
|
3204 | - | not (typeDeterminesValue arg_ty) = False -- See Note [Type determines value]
|
|
3205 | - | otherwise = go arg
|
|
3206 | - where
|
|
3207 | - go (Var v) = hasSomeUnfolding (idUnfolding v)
|
|
3208 | - || isDataConWorkId v
|
|
3209 | - go (Type _) = False
|
|
3210 | - go (Coercion _) = False
|
|
3211 | - go (App fn (Type _)) = go fn
|
|
3212 | - go (App fn (Coercion _)) = go fn
|
|
3213 | - go (Tick _ a) = go a
|
|
3214 | - go (Cast e _) = go e
|
|
3215 | - go _ = True
|
|
3216 | - |
|
3217 | 3286 | thenUDs :: UsageDetails -> UsageDetails -> UsageDetails
|
3218 | 3287 | thenUDs (MkUD {ud_binds = db1, ud_calls = calls1})
|
3219 | 3288 | (MkUD {ud_binds = db2, ud_calls = calls2})
|
... | ... | @@ -24,7 +24,7 @@ module GHC.Core.Predicate ( |
24 | 24 | classMethodTy, classMethodInstTy,
|
25 | 25 | |
26 | 26 | -- Implicit parameters
|
27 | - isIPLikePred, mentionsIP, isIPTyCon, isIPClass,
|
|
27 | + couldBeIPLike, mightMentionIP, isIPTyCon, isIPClass,
|
|
28 | 28 | isCallStackTy, isCallStackPred, isCallStackPredTy,
|
29 | 29 | isExceptionContextPred, isExceptionContextTy,
|
30 | 30 | isIPPred_maybe,
|
... | ... | @@ -126,9 +126,12 @@ isDictTy ty = isClassPred pred |
126 | 126 | where
|
127 | 127 | (_, pred) = splitInvisPiTys ty
|
128 | 128 | |
129 | +-- | Is the type *guaranteed* to determine the value?
|
|
130 | +--
|
|
131 | +-- Might say No even if the type does determine the value. (See the Note)
|
|
129 | 132 | typeDeterminesValue :: Type -> Bool
|
130 | 133 | -- See Note [Type determines value]
|
131 | -typeDeterminesValue ty = isDictTy ty && not (isIPLikePred ty)
|
|
134 | +typeDeterminesValue ty = isDictTy ty && not (couldBeIPLike ty)
|
|
132 | 135 | |
133 | 136 | getClassPredTys :: HasDebugCallStack => PredType -> (Class, [Type])
|
134 | 137 | getClassPredTys ty = case getClassPredTys_maybe ty of
|
... | ... | @@ -171,6 +174,10 @@ So we treat implicit params just like ordinary arguments for the |
171 | 174 | purposes of specialisation. Note that we still want to specialise
|
172 | 175 | functions with implicit params if they have *other* dicts which are
|
173 | 176 | class params; see #17930.
|
177 | + |
|
178 | +It's also not always possible to infer that a type determines the value
|
|
179 | +if type families are in play. See #19747 for one such example.
|
|
180 | + |
|
174 | 181 | -}
|
175 | 182 | |
176 | 183 | -- --------------------- Equality predicates ---------------------------------
|
... | ... | @@ -421,44 +428,44 @@ isCallStackTy ty |
421 | 428 | | otherwise
|
422 | 429 | = False
|
423 | 430 | |
424 | --- --------------------- isIPLike and mentionsIP --------------------------
|
|
431 | +-- --------------------- couldBeIPLike and mightMentionIP --------------------------
|
|
425 | 432 | -- See Note [Local implicit parameters]
|
426 | 433 | |
427 | -isIPLikePred :: Type -> Bool
|
|
434 | +couldBeIPLike :: Type -> Bool
|
|
428 | 435 | -- Is `pred`, or any of its superclasses, an implicit parameter?
|
429 | 436 | -- See Note [Local implicit parameters]
|
430 | -isIPLikePred pred =
|
|
431 | - mentions_ip_pred initIPRecTc (const True) (const True) pred
|
|
432 | - |
|
433 | -mentionsIP :: (Type -> Bool) -- ^ predicate on the string
|
|
434 | - -> (Type -> Bool) -- ^ predicate on the type
|
|
435 | - -> Class
|
|
436 | - -> [Type] -> Bool
|
|
437 | --- ^ @'mentionsIP' str_cond ty_cond cls tys@ returns @True@ if:
|
|
437 | +couldBeIPLike pred
|
|
438 | + = might_mention_ip1 initIPRecTc (const True) (const True) pred
|
|
439 | + |
|
440 | +mightMentionIP :: (Type -> Bool) -- ^ predicate on the string
|
|
441 | + -> (Type -> Bool) -- ^ predicate on the type
|
|
442 | + -> Class
|
|
443 | + -> [Type] -> Bool
|
|
444 | +-- ^ @'mightMentionIP' str_cond ty_cond cls tys@ returns @True@ if:
|
|
438 | 445 | --
|
439 | 446 | -- - @cls tys@ is of the form @IP str ty@, where @str_cond str@ and @ty_cond ty@
|
440 | 447 | -- are both @True@,
|
441 | 448 | -- - or any superclass of @cls tys@ has this property.
|
442 | 449 | --
|
443 | 450 | -- See Note [Local implicit parameters]
|
444 | -mentionsIP = mentions_ip initIPRecTc
|
|
451 | +mightMentionIP = might_mention_ip initIPRecTc
|
|
445 | 452 | |
446 | -mentions_ip :: RecTcChecker -> (Type -> Bool) -> (Type -> Bool) -> Class -> [Type] -> Bool
|
|
447 | -mentions_ip rec_clss str_cond ty_cond cls tys
|
|
453 | +might_mention_ip :: RecTcChecker -> (Type -> Bool) -> (Type -> Bool) -> Class -> [Type] -> Bool
|
|
454 | +might_mention_ip rec_clss str_cond ty_cond cls tys
|
|
448 | 455 | | Just (str_ty, ty) <- isIPPred_maybe cls tys
|
449 | 456 | = str_cond str_ty && ty_cond ty
|
450 | 457 | | otherwise
|
451 | - = or [ mentions_ip_pred rec_clss str_cond ty_cond (classMethodInstTy sc_sel_id tys)
|
|
458 | + = or [ might_mention_ip1 rec_clss str_cond ty_cond (classMethodInstTy sc_sel_id tys)
|
|
452 | 459 | | sc_sel_id <- classSCSelIds cls ]
|
453 | 460 | |
454 | 461 | |
455 | -mentions_ip_pred :: RecTcChecker -> (Type -> Bool) -> (Type -> Bool) -> Type -> Bool
|
|
456 | -mentions_ip_pred rec_clss str_cond ty_cond ty
|
|
462 | +might_mention_ip1 :: RecTcChecker -> (Type -> Bool) -> (Type -> Bool) -> Type -> Bool
|
|
463 | +might_mention_ip1 rec_clss str_cond ty_cond ty
|
|
457 | 464 | | Just (cls, tys) <- getClassPredTys_maybe ty
|
458 | 465 | , let tc = classTyCon cls
|
459 | 466 | , Just rec_clss' <- if isTupleTyCon tc then Just rec_clss
|
460 | 467 | else checkRecTc rec_clss tc
|
461 | - = mentions_ip rec_clss' str_cond ty_cond cls tys
|
|
468 | + = might_mention_ip rec_clss' str_cond ty_cond cls tys
|
|
462 | 469 | | otherwise
|
463 | 470 | = False -- Includes things like (D []) where D is
|
464 | 471 | -- a Constraint-ranged family; #7785
|
... | ... | @@ -471,7 +478,7 @@ initIPRecTc = setRecTcMaxBound 1 initRecTc |
471 | 478 | See also wrinkle (SIP1) in Note [Shadowing of implicit parameters] in
|
472 | 479 | GHC.Tc.Solver.Dict.
|
473 | 480 | |
474 | -The function isIPLikePred tells if this predicate, or any of its
|
|
481 | +The function couldBeIPLike tells if this predicate, or any of its
|
|
475 | 482 | superclasses, is an implicit parameter.
|
476 | 483 | |
477 | 484 | Why are implicit parameters special? Unlike normal classes, we can
|
... | ... | @@ -479,7 +486,7 @@ have local instances for implicit parameters, in the form of |
479 | 486 | let ?x = True in ...
|
480 | 487 | So in various places we must be careful not to assume that any value
|
481 | 488 | of the right type will do; we must carefully look for the innermost binding.
|
482 | -So isIPLikePred checks whether this is an implicit parameter, or has
|
|
489 | +So couldBeIPLike checks whether this is an implicit parameter, or has
|
|
483 | 490 | a superclass that is an implicit parameter.
|
484 | 491 | |
485 | 492 | Several wrinkles
|
... | ... | @@ -520,16 +527,16 @@ Small worries (Sept 20): |
520 | 527 | think nothing does.
|
521 | 528 | * I'm a little concerned about type variables; such a variable might
|
522 | 529 | be instantiated to an implicit parameter. I don't think this
|
523 | - matters in the cases for which isIPLikePred is used, and it's pretty
|
|
530 | + matters in the cases for which couldBeIPLike is used, and it's pretty
|
|
524 | 531 | obscure anyway.
|
525 | 532 | * The superclass hunt stops when it encounters the same class again,
|
526 | 533 | but in principle we could have the same class, differently instantiated,
|
527 | 534 | and the second time it could have an implicit parameter
|
528 | 535 | I'm going to treat these as problems for another day. They are all exotic.
|
529 | 536 | |
530 | -Note [Using typesAreApart when calling mentionsIP]
|
|
531 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
532 | -We call 'mentionsIP' in two situations:
|
|
537 | +Note [Using typesAreApart when calling mightMentionIP]
|
|
538 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
539 | +We call 'mightMentionIP' in two situations:
|
|
533 | 540 | |
534 | 541 | (1) to check that a predicate does not contain any implicit parameters
|
535 | 542 | IP str ty, for a fixed literal str and any type ty,
|
... | ... | @@ -1914,7 +1914,7 @@ growThetaTyVars theta tcvs |
1914 | 1914 | | otherwise = transCloVarSet mk_next seed_tcvs
|
1915 | 1915 | where
|
1916 | 1916 | seed_tcvs = tcvs `unionVarSet` tyCoVarsOfTypes ips
|
1917 | - (ips, non_ips) = partition isIPLikePred theta
|
|
1917 | + (ips, non_ips) = partition couldBeIPLike theta
|
|
1918 | 1918 | -- See Note [Inheriting implicit parameters]
|
1919 | 1919 | |
1920 | 1920 | mk_next :: VarSet -> VarSet -- Maps current set to newly-grown ones
|
... | ... | @@ -749,7 +749,7 @@ shortCutSolver dflags ev_w ev_i |
749 | 749 | -- programs should typecheck regardless of whether we take this step or
|
750 | 750 | -- not. See Note [Shortcut solving]
|
751 | 751 | |
752 | - , not (isIPLikePred (ctEvPred ev_w)) -- Not for implicit parameters (#18627)
|
|
752 | + , not (couldBeIPLike (ctEvPred ev_w)) -- Not for implicit parameters (#18627)
|
|
753 | 753 | |
754 | 754 | , not (xopt LangExt.IncoherentInstances dflags)
|
755 | 755 | -- If IncoherentInstances is on then we cannot rely on coherence of proofs
|
... | ... | @@ -2040,10 +2040,10 @@ solveOneFromTheOther ct_i ct_w |
2040 | 2040 | is_wsc_orig_w = isWantedSuperclassOrigin orig_w
|
2041 | 2041 | |
2042 | 2042 | different_level_strategy -- Both Given
|
2043 | - | isIPLikePred pred = if lvl_w `strictlyDeeperThan` lvl_i then KeepWork else KeepInert
|
|
2044 | - | otherwise = if lvl_w `strictlyDeeperThan` lvl_i then KeepInert else KeepWork
|
|
2043 | + | couldBeIPLike pred = if lvl_w `strictlyDeeperThan` lvl_i then KeepWork else KeepInert
|
|
2044 | + | otherwise = if lvl_w `strictlyDeeperThan` lvl_i then KeepInert else KeepWork
|
|
2045 | 2045 | -- See Note [Replacement vs keeping] part (1)
|
2046 | - -- For the isIPLikePred case see Note [Shadowing of implicit parameters]
|
|
2046 | + -- For the couldBeIPLike case see Note [Shadowing of implicit parameters]
|
|
2047 | 2047 | -- in GHC.Tc.Solver.Dict
|
2048 | 2048 | |
2049 | 2049 | same_level_strategy -- Both Given
|
... | ... | @@ -401,8 +401,8 @@ updInertDicts dict_ct@(DictCt { di_cls = cls, di_ev = ev, di_tys = tys }) |
401 | 401 | -- an implicit parameter (?str :: ty) for the given 'str' and any type 'ty'?
|
402 | 402 | does_not_mention_ip_for :: Type -> DictCt -> Bool
|
403 | 403 | does_not_mention_ip_for str_ty (DictCt { di_cls = cls, di_tys = tys })
|
404 | - = not $ mentionsIP (not . typesAreApart str_ty) (const True) cls tys
|
|
405 | - -- See Note [Using typesAreApart when calling mentionsIP]
|
|
404 | + = not $ mightMentionIP (not . typesAreApart str_ty) (const True) cls tys
|
|
405 | + -- See Note [Using typesAreApart when calling mightMentionIP]
|
|
406 | 406 | -- in GHC.Core.Predicate
|
407 | 407 | |
408 | 408 | updInertIrreds :: IrredCt -> TcS ()
|
... | ... | @@ -534,7 +534,7 @@ updSolvedDicts what dict_ct@(DictCt { di_cls = cls, di_tys = tys, di_ev = ev }) |
534 | 534 | = do { is_callstack <- is_tyConTy isCallStackTy callStackTyConName
|
535 | 535 | ; is_exceptionCtx <- is_tyConTy isExceptionContextTy exceptionContextTyConName
|
536 | 536 | ; let contains_callstack_or_exceptionCtx =
|
537 | - mentionsIP
|
|
537 | + mightMentionIP
|
|
538 | 538 | (const True)
|
539 | 539 | -- NB: the name of the call-stack IP is irrelevant
|
540 | 540 | -- e.g (?foo :: CallStack) counts!
|
... | ... | @@ -552,9 +552,9 @@ updSolvedDicts what dict_ct@(DictCt { di_cls = cls, di_tys = tys, di_ev = ev }) |
552 | 552 | |
553 | 553 | -- Return a predicate that decides whether a type is CallStack
|
554 | 554 | -- or ExceptionContext, accounting for e.g. type family reduction, as
|
555 | - -- per Note [Using typesAreApart when calling mentionsIP].
|
|
555 | + -- per Note [Using typesAreApart when calling mightMentionIP].
|
|
556 | 556 | --
|
557 | - -- See Note [Using isCallStackTy in mentionsIP].
|
|
557 | + -- See Note [Using isCallStackTy in mightMentionIP].
|
|
558 | 558 | is_tyConTy :: (Type -> Bool) -> Name -> TcS (Type -> Bool)
|
559 | 559 | is_tyConTy is_eq tc_name
|
560 | 560 | = do { (mb_tc, _) <- wrapTcS $ TcM.tryTc $ TcM.tcLookupTyCon tc_name
|
... | ... | @@ -582,14 +582,14 @@ in a different context! |
582 | 582 | See also Note [Shadowing of implicit parameters], which deals with a similar
|
583 | 583 | problem with Given implicit parameter constraints.
|
584 | 584 | |
585 | -Note [Using isCallStackTy in mentionsIP]
|
|
586 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
585 | +Note [Using isCallStackTy in mightMentionIP]
|
|
586 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
587 | 587 | To implement Note [Don't add HasCallStack constraints to the solved set],
|
588 | 588 | we need to check whether a constraint contains a HasCallStack or HasExceptionContext
|
589 | 589 | constraint. We do this using the 'mentionsIP' function, but as per
|
590 | -Note [Using typesAreApart when calling mentionsIP] we don't want to simply do:
|
|
590 | +Note [Using typesAreApart when calling mightMentionIP] we don't want to simply do:
|
|
591 | 591 | |
592 | - mentionsIP
|
|
592 | + mightMentionIP
|
|
593 | 593 | (const True) -- (ignore the implicit parameter string)
|
594 | 594 | (isCallStackTy <||> isExceptionContextTy)
|
595 | 595 |
... | ... | @@ -155,7 +155,7 @@ module GHC.Tc.Utils.TcType ( |
155 | 155 | mkTyConTy, mkTyVarTy, mkTyVarTys,
|
156 | 156 | mkTyCoVarTy, mkTyCoVarTys,
|
157 | 157 | |
158 | - isClassPred, isEqPred, isIPLikePred, isEqClassPred,
|
|
158 | + isClassPred, isEqPred, couldBeIPLike, isEqClassPred,
|
|
159 | 159 | isEqualityClass, mkClassPred,
|
160 | 160 | tcSplitQuantPredTy, tcSplitDFunTy, tcSplitDFunHead, tcSplitMethodTy,
|
161 | 161 | isRuntimeRepVar, isFixedRuntimeRepKind,
|
... | ... | @@ -1819,7 +1819,7 @@ pickCapturedPreds |
1819 | 1819 | pickCapturedPreds qtvs theta
|
1820 | 1820 | = filter captured theta
|
1821 | 1821 | where
|
1822 | - captured pred = isIPLikePred pred || (tyCoVarsOfType pred `intersectsVarSet` qtvs)
|
|
1822 | + captured pred = couldBeIPLike pred || (tyCoVarsOfType pred `intersectsVarSet` qtvs)
|
|
1823 | 1823 | |
1824 | 1824 | |
1825 | 1825 | -- Superclasses
|
1 | +{-# OPTIONS_GHC -fspecialise-aggressively #-}
|
|
2 | +{-# OPTIONS_GHC -fno-spec-constr #-}
|
|
3 | +module Main(main) where
|
|
4 | + |
|
5 | +import SpecTyFam_Import (specMe, MaybeShowNum)
|
|
6 | +import GHC.Exts
|
|
7 | + |
|
8 | +-- We want to see a specialization of `specMe` which doesn't take a dictionary at runtime.
|
|
9 | + |
|
10 | +{-# NOINLINE foo #-}
|
|
11 | +foo :: Int -> (String,Int)
|
|
12 | +-- We want specMe to be specialized, but not inlined
|
|
13 | +foo x = specMe True x
|
|
14 | + |
|
15 | +main = print $ sum $ map (snd . foo) [1..1000 :: Int] |
1 | +500500 |
1 | +{-# LANGUAGE TypeFamilies #-}
|
|
2 | +{-# LANGUAGE BangPatterns #-}
|
|
3 | + |
|
4 | +module SpecTyFam_Import (specMe, MaybeShowNum) where
|
|
5 | + |
|
6 | +import Data.Kind
|
|
7 | + |
|
8 | +type family MaybeShowNum a n :: Constraint where
|
|
9 | + MaybeShowNum a n = (Show a, Num n)
|
|
10 | + |
|
11 | +{-# INLINABLE specMe #-}
|
|
12 | +specMe :: (Integral n, MaybeShowNum a n) => a -> n -> (String,n)
|
|
13 | +specMe s !n = (show s, n+1 `div` 2) |
... | ... | @@ -423,3 +423,12 @@ test('ByteCodeAsm', |
423 | 423 | ],
|
424 | 424 | compile_and_run,
|
425 | 425 | ['-package ghc'])
|
426 | + |
|
427 | +# Check that $s$wspecMe doesn't have any dictionary args after specialization in addition to perf stats
|
|
428 | +# See also #19747
|
|
429 | +test('SpecTyFamRun', [ grep_errmsg(r'foo')
|
|
430 | + , extra_files(['SpecTyFam_Import.hs'])
|
|
431 | + , only_ways(['optasm'])
|
|
432 | + , collect_stats('bytes allocated', 5)],
|
|
433 | + multimod_compile_and_run,
|
|
434 | + ['SpecTyFamRun', '-O2']) |
1 | +{-# OPTIONS_GHC -fspecialise-aggressively #-}
|
|
2 | +{-# OPTIONS_GHC -fno-spec-constr #-}
|
|
3 | + |
|
4 | +module T26051(main, foo) where
|
|
5 | + |
|
6 | +import T26051_Import (specMe, MaybeShowNum)
|
|
7 | +import GHC.Exts
|
|
8 | + |
|
9 | +-- We want to see a specialization of `specMe` which doesn't take a dictionary at runtime.
|
|
10 | + |
|
11 | +{-# OPAQUE foo #-}
|
|
12 | +foo :: Int -> (String,Int)
|
|
13 | +foo x = specMe True x
|
|
14 | + |
|
15 | +main = print $ sum $ map (snd . foo) [1..1000 :: Int] |
1 | +[1 of 2] Compiling T26051_Import ( T26051_Import.hs, T26051_Import.o )
|
|
2 | + |
|
3 | +==================== Specialise ====================
|
|
4 | +Result size of Specialise = {terms: 31, types: 39, coercions: 8, joins: 0/1}
|
|
5 | + |
|
6 | +-- RHS size: {terms: 30, types: 27, coercions: 8, joins: 0/1}
|
|
7 | +specMe [InlPrag=INLINABLE] :: forall n a. (Integral n, MaybeShowNum a n) => a -> n -> (String, n)
|
|
8 | +[LclIdX,
|
|
9 | + Arity=4,
|
|
10 | + Unf=Unf{Src=StableUser, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [30 0 0 20] 260 10
|
|
11 | + Tmpl= \ (@n) (@a) ($dIntegral [Occ=Once1] :: Integral n) (irred :: MaybeShowNum a n) (eta [Occ=Once1] :: a) (eta [Occ=Once1] :: n) ->
|
|
12 | + let {
|
|
13 | + $dNum :: Num n
|
|
14 | + [LclId, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=False, ConLike=False, WorkFree=False, Expandable=True, Guidance=IF_ARGS [] 20 0}]
|
|
15 | + $dNum = GHC.Internal.Classes.$p1CTuple2 @(Show a) @(Num n) (irred `cast` (Sub (T26051_Import.D:R:MaybeShowNum[0] <a>_N <n>_N) :: MaybeShowNum a n ~R# (Show a, Num n))) } in
|
|
16 | + case eta of n [Occ=Once1] { __DEFAULT -> (show @a (GHC.Internal.Classes.$p0CTuple2 @(Show a) @(Num n) (irred `cast` (Sub (T26051_Import.D:R:MaybeShowNum[0] <a>_N <n>_N) :: MaybeShowNum a n ~R# (Show a, Num n)))) eta, + @n $dNum n (div @n $dIntegral (fromInteger @n $dNum (GHC.Internal.Bignum.Integer.IS 1#)) (fromInteger @n $dNum (GHC.Internal.Bignum.Integer.IS 2#)))) }}]
|
|
17 | +specMe
|
|
18 | + = \ (@n) (@a) ($dIntegral :: Integral n) (irred :: MaybeShowNum a n) (eta :: a) (eta :: n) ->
|
|
19 | + let {
|
|
20 | + $dNum :: Num n
|
|
21 | + [LclId, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=False, ConLike=False, WorkFree=False, Expandable=True, Guidance=IF_ARGS [] 20 0}]
|
|
22 | + $dNum = GHC.Internal.Classes.$p1CTuple2 @(Show a) @(Num n) (irred `cast` (Sub (T26051_Import.D:R:MaybeShowNum[0] <a>_N <n>_N) :: MaybeShowNum a n ~R# (Show a, Num n))) } in
|
|
23 | + case eta of n { __DEFAULT -> (show @a (GHC.Internal.Classes.$p0CTuple2 @(Show a) @(Num n) (irred `cast` (Sub (T26051_Import.D:R:MaybeShowNum[0] <a>_N <n>_N) :: MaybeShowNum a n ~R# (Show a, Num n)))) eta, + @n $dNum n (div @n $dIntegral (fromInteger @n $dNum (GHC.Internal.Bignum.Integer.IS 1#)) (fromInteger @n $dNum (GHC.Internal.Bignum.Integer.IS 2#)))) }
|
|
24 | + |
|
25 | + |
|
26 | + |
|
27 | +[2 of 2] Compiling T26051 ( T26051.hs, T26051.o )
|
|
28 | + |
|
29 | +==================== Specialise ====================
|
|
30 | +Result size of Specialise = {terms: 84, types: 86, coercions: 13, joins: 0/1}
|
|
31 | + |
|
32 | +Rec {
|
|
33 | +-- RHS size: {terms: 3, types: 4, coercions: 0, joins: 0/0}
|
|
34 | +$dCTuple2 :: (Show Bool, Num Int)
|
|
35 | +[LclId, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
|
|
36 | +$dCTuple2 = (GHC.Internal.Show.$fShowBool, GHC.Internal.Num.$fNumInt)
|
|
37 | + |
|
38 | +-- RHS size: {terms: 19, types: 9, coercions: 0, joins: 0/1}
|
|
39 | +$s$wspecMe [InlPrag=INLINABLE[2]] :: Bool -> Int -> (# String, Int #)
|
|
40 | +[LclId, Arity=2]
|
|
41 | +$s$wspecMe
|
|
42 | + = \ (eta [Occ=Once1] :: Bool) (eta1 [Occ=Once1] :: Int) ->
|
|
43 | + let {
|
|
44 | + $dNum :: Num Int
|
|
45 | + [LclId, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
|
|
46 | + $dNum = GHC.Internal.Num.$fNumInt } in
|
|
47 | + case eta1 of n1 [Occ=Once1] { __DEFAULT -> (# GHC.Internal.Show.$fShowBool_$cshow eta, GHC.Internal.Num.$fNumInt_$c+ n1 (GHC.Internal.Real.$fIntegralInt_$cdiv (GHC.Internal.Num.$fNumInt_$cfromInteger (GHC.Internal.Bignum.Integer.IS 1#)) (GHC.Internal.Num.$fNumInt_$cfromInteger (GHC.Internal.Bignum.Integer.IS 2#))) #) }
|
|
48 | + |
|
49 | +-- RHS size: {terms: 12, types: 13, coercions: 5, joins: 0/0}
|
|
50 | +$sspecMe [InlPrag=INLINABLE[2]] :: Bool -> Int -> (String, Int)
|
|
51 | +[LclId,
|
|
52 | + Arity=2,
|
|
53 | + Unf=Unf{Src=StableSystem, TopLvl=True, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=2,unsat_ok=True,boring_ok=False)
|
|
54 | + Tmpl= \ (eta [Occ=Once1] :: Bool) (eta1 [Occ=Once1] :: Int) -> case T26051_Import.$wspecMe @Int @Bool GHC.Internal.Real.$fIntegralInt ($dCTuple2 `cast` (Sub (Sym (T26051_Import.D:R:MaybeShowNum[0] <Bool>_N <Int>_N)) :: (Show Bool, Num Int) ~R# MaybeShowNum Bool Int)) eta eta1 of { (# ww [Occ=Once1], ww1 [Occ=Once1] #) -> (ww, ww1) }}]
|
|
55 | +$sspecMe = \ (eta [Occ=Once1] :: Bool) (eta1 [Occ=Once1] :: Int) -> case T26051_Import.$wspecMe @Int @Bool GHC.Internal.Real.$fIntegralInt ($dCTuple2 `cast` (Sub (Sym (T26051_Import.D:R:MaybeShowNum[0] <Bool>_N <Int>_N)) :: (Show Bool, Num Int) ~R# MaybeShowNum Bool Int)) eta eta1 of { (# ww [Occ=Once1], ww1 [Occ=Once1] #) -> (ww, ww1) }
|
|
56 | +end Rec }
|
|
57 | + |
|
58 | +-- RHS size: {terms: 6, types: 3, coercions: 5, joins: 0/0}
|
|
59 | +foo [InlPrag=OPAQUE] :: Int -> (String, Int)
|
|
60 | +[LclIdX, Arity=1, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 50 0}]
|
|
61 | +foo = \ (x :: Int) -> specMe @Int @Bool GHC.Internal.Real.$fIntegralInt ($dCTuple2 `cast` (Sub (Sym (T26051_Import.D:R:MaybeShowNum[0] <Bool>_N <Int>_N)) :: (Show Bool, Num Int) ~R# MaybeShowNum Bool Int)) GHC.Internal.Types.True x
|
|
62 | + |
|
63 | +-- RHS size: {terms: 37, types: 26, coercions: 0, joins: 0/0}
|
|
64 | +main :: State# RealWorld -> (# State# RealWorld, () #)
|
|
65 | +[LclId, Arity=1, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=IF_ARGS [0] 301 0}]
|
|
66 | +main = \ (eta [OS=OneShot] :: State# RealWorld) -> GHC.Internal.IO.Handle.Text.hPutStr2 GHC.Internal.IO.StdHandles.stdout (case GHC.Internal.Enum.eftIntFB @(Int -> Int) (GHC.Internal.Base.mapFB @Int @(Int -> Int) @Int (\ (ds :: Int) (ds1 [OS=OneShot] :: Int -> Int) (v [OS=OneShot] :: Int) -> case v of { I# ipv -> ds1 (case ds of { I# y -> GHC.Internal.Types.I# (+# ipv y) }) }) (\ (x :: Int) -> case foo x of { (_ [Occ=Dead], y) -> y })) (breakpoint @Int) 1# 1000# (GHC.Internal.Types.I# 0#) of { I# n -> GHC.Internal.Show.itos n (GHC.Internal.Types.[] @Char) }) GHC.Internal.Types.True eta
|
|
67 | + |
|
68 | +-- RHS size: {terms: 1, types: 0, coercions: 3, joins: 0/0}
|
|
69 | +main :: IO ()
|
|
70 | +[LclIdX, Arity=1, Unf=Unf{Src=<vanilla>, TopLvl=False, Value=True, ConLike=True, WorkFree=True, Expandable=True, Guidance=ALWAYS_IF(arity=0,unsat_ok=True,boring_ok=True)}]
|
|
71 | +main = main `cast` (Sym (GHC.Internal.Types.N:IO <()>_R) :: (State# RealWorld -> (# State# RealWorld, () #)) ~R# IO ())
|
|
72 | + |
|
73 | + |
|
74 | +------ Local rules for imported ids --------
|
|
75 | +"SPEC/T26051 $wspecMe @Int @Bool" [2] forall ($dIntegral :: Integral Int) (irred :: MaybeShowNum Bool Int). T26051_Import.$wspecMe @Int @Bool $dIntegral irred = $s$wspecMe
|
|
76 | +"SPEC/T26051 specMe @Int @Bool" [2] forall ($dIntegral :: Integral Int) (irred :: MaybeShowNum Bool Int). specMe @Int @Bool $dIntegral irred = $sspecMe
|
|
77 | + |
|
78 | + |
1 | +{-# LANGUAGE TypeFamilies #-}
|
|
2 | +{-# LANGUAGE BangPatterns #-}
|
|
3 | +{-# LANGUAGE ImplicitParams #-}
|
|
4 | + |
|
5 | +module T26051_Import (specMe, MaybeShowNum) where
|
|
6 | + |
|
7 | +import Data.Kind
|
|
8 | + |
|
9 | +type family MaybeShowNum a n :: Constraint where
|
|
10 | + MaybeShowNum a n = (Show a, Num n)
|
|
11 | + |
|
12 | +{-# INLINABLE specMe #-}
|
|
13 | +specMe :: (Integral n, MaybeShowNum a n) => a -> n -> (String,n)
|
|
14 | +specMe s !n = (show s, n+1 `div` 2) |
... | ... | @@ -548,3 +548,9 @@ test('T25965', normal, compile, ['-O']) |
548 | 548 | test('T25703', [grep_errmsg(r'SPEC')], compile, ['-O -fpolymorphic-specialisation -ddump-rule-firings'])
|
549 | 549 | test('T25703a', [grep_errmsg(r'SPEC')], compile, ['-O -fpolymorphic-specialisation -ddump-rule-firings'])
|
550 | 550 | |
551 | +# Check that $s$wspecMe doesn't have any dictionary args after specialization in addition to perf stats
|
|
552 | +test('T26051', [ grep_errmsg(r'\$wspecMe')
|
|
553 | + , extra_files(['T26051_Import.hs'])
|
|
554 | + , only_ways(['optasm'])],
|
|
555 | + multimod_compile,
|
|
556 | + ['T26051', '-O2 -ddump-spec -dsuppress-uniques -dno-typeable-binds -dppr-cols=1000']) |