X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Fspecialise%2FSpecConstr.lhs;h=222bcd188611ae02a2f9e656b38889e34e1303fb;hb=7804af8c4a5bc02798423900a641a4aa77e5abfb;hp=c545fade7a107f988d24cd555cee9a4fc6765e03;hpb=94fd9ad3745d9d270648aa5d7d87f49a0dafabd4;p=ghc-hetmet.git diff --git a/compiler/specialise/SpecConstr.lhs b/compiler/specialise/SpecConstr.lhs index c545fad..222bcd1 100644 --- a/compiler/specialise/SpecConstr.lhs +++ b/compiler/specialise/SpecConstr.lhs @@ -38,10 +38,8 @@ import VarSet import Name import DynFlags ( DynFlags(..) ) import StaticFlags ( opt_PprStyle_Debug ) -import StaticFlags ( opt_SpecInlineJoinPoints ) -import BasicTypes ( Activation(..) ) import Maybes ( orElse, catMaybes, isJust, isNothing ) -import NewDemand +import Demand import DmdAnal ( both ) import Serialized ( deserializeWithData ) import Util @@ -476,7 +474,8 @@ Annotating a type with NoSpecConstr will make SpecConstr not specialise for arguments of that type. \begin{code} -data SpecConstrAnnotation = NoSpecConstr deriving( Data, Typeable ) +data SpecConstrAnnotation = NoSpecConstr | ForceSpecConstr + deriving( Data, Typeable, Eq ) \end{code} %************************************************************************ @@ -491,7 +490,7 @@ specConstrProgram guts = do dflags <- getDynFlags us <- getUniqueSupplyM - annos <- deserializeAnnotations deserializeWithData + annos <- getFirstAnnotations deserializeWithData guts let binds' = fst $ initUs us (go (initScEnv dflags annos) (mg_binds guts)) return (guts { mg_binds = binds' }) where @@ -547,14 +546,14 @@ instance Outputable Value where ppr LambdaVal = ptext (sLit "") --------------------- -initScEnv :: DynFlags -> L.UniqFM [SpecConstrAnnotation] -> ScEnv -initScEnv dflags annos +initScEnv :: DynFlags -> L.UniqFM SpecConstrAnnotation -> ScEnv +initScEnv dflags anns = SCE { sc_size = specConstrThreshold dflags, sc_count = specConstrCount dflags, sc_subst = emptySubst, sc_how_bound = emptyVarEnv, sc_vals = emptyVarEnv, - sc_annotations = L.mapUFM head $ L.filterUFM (not . null) annos } + sc_annotations = anns } data HowBound = RecFun -- These are the recursive functions for which -- we seek interesting call patterns @@ -570,7 +569,7 @@ lookupHowBound :: ScEnv -> Id -> Maybe HowBound lookupHowBound env id = lookupVarEnv (sc_how_bound env) id scSubstId :: ScEnv -> Id -> CoreExpr -scSubstId env v = lookupIdSubst (sc_subst env) v +scSubstId env v = lookupIdSubst (text "scSubstId") (sc_subst env) v scSubstTy :: ScEnv -> Type -> Type scSubstTy env ty = substTy (sc_subst env) ty @@ -656,9 +655,7 @@ extendCaseBndrs env case_bndr con alt_bndrs ignoreTyCon :: ScEnv -> TyCon -> Bool ignoreTyCon env tycon - = case L.lookupUFM (sc_annotations env) tycon of - Just NoSpecConstr -> True - _ -> False + = L.lookupUFM (sc_annotations env) tycon == Just NoSpecConstr ignoreType :: ScEnv -> Type -> Bool ignoreType env ty @@ -670,6 +667,24 @@ ignoreAltCon :: ScEnv -> AltCon -> Bool ignoreAltCon env (DataAlt dc) = ignoreTyCon env (dataConTyCon dc) ignoreAltCon env (LitAlt lit) = ignoreType env (literalType lit) ignoreAltCon _ DEFAULT = True + +forceSpecBndr :: ScEnv -> Var -> Bool +forceSpecBndr env var = forceSpecFunTy env . varType $ var + +forceSpecFunTy :: ScEnv -> Type -> Bool +forceSpecFunTy env = any (forceSpecArgTy env) . fst . splitFunTys + +forceSpecArgTy :: ScEnv -> Type -> Bool +forceSpecArgTy env ty + | Just ty' <- coreView ty = forceSpecArgTy env ty' + +forceSpecArgTy env ty + | Just (tycon, tys) <- splitTyConApp_maybe ty + , tycon /= funTyCon + = L.lookupUFM (sc_annotations env) tycon == Just ForceSpecConstr + || any (forceSpecArgTy env) tys + +forceSpecArgTy _ _ = False \end{code} @@ -862,50 +877,37 @@ scExpr' env (Case scrut b ty alts) scExpr' env (Let (NonRec bndr rhs) body) | isTyVar bndr -- Type-lets may be created by doBeta = scExpr' (extendScSubst env bndr rhs) body - | otherwise - = do { let (body_env, bndr') = extendBndr env bndr - ; (rhs_usg, (_, args', rhs_body', _)) <- scRecRhs env (bndr',rhs) - ; let rhs' = mkLams args' rhs_body' - - ; if not opt_SpecInlineJoinPoints || null args' || isEmptyVarEnv (scu_calls rhs_usg) then do - do { -- Vanilla case - let body_env2 = extendValEnv body_env bndr' (isValue (sc_vals env) rhs') - -- Record if the RHS is a value - ; (body_usg, body') <- scExpr body_env2 body - ; return (body_usg `combineUsage` rhs_usg, Let (NonRec bndr' rhs') body') } - else -- For now, just brutally inline the join point - do { let body_env2 = extendScSubst env bndr rhs' - ; scExpr body_env2 body } } - - -{- Old code - do { -- Join-point case - let body_env2 = extendHowBound body_env [bndr'] RecFun - -- If the RHS of this 'let' contains calls - -- to recursive functions that we're trying - -- to specialise, then treat this let too - -- as one to specialise - ; (body_usg, body') <- scExpr body_env2 body - ; (spec_usg, _, specs) <- specialise env (scu_calls body_usg) ([], rhs_info) - - ; return (body_usg { scu_calls = scu_calls body_usg `delVarEnv` bndr' } + | otherwise -- Note [Local let bindings] + = do { let (body_env, bndr') = extendBndr env bndr + ; (rhs_usg, rhs_info) <- scRecRhs env (bndr',rhs) + ; let force_spec = False + ; let body_env2 = extendHowBound body_env [bndr'] RecFun + ; (body_usg, body') <- scExpr body_env2 body + ; (spec_usg, specs) <- specialise env force_spec + (scu_calls body_usg) + rhs_info + (SI [] 0 Nothing) + + ; return (body_usg { scu_calls = scu_calls body_usg `delVarEnv` bndr' } `combineUsage` rhs_usg `combineUsage` spec_usg, mkLets [NonRec b r | (b,r) <- specInfoBinds rhs_info specs] body') } --} + -- A *local* recursive group: see Note [Local recursive groups] scExpr' env (Let (Rec prs) body) = do { let (bndrs,rhss) = unzip prs (rhs_env1,bndrs') = extendRecBndrs env bndrs rhs_env2 = extendHowBound rhs_env1 bndrs' RecFun + force_spec = any (forceSpecBndr env) bndrs' ; (rhs_usgs, rhs_infos) <- mapAndUnzipM (scRecRhs rhs_env2) (bndrs' `zip` rhss) ; (body_usg, body') <- scExpr rhs_env2 body -- NB: start specLoop from body_usg - ; (spec_usg, specs) <- specLoop rhs_env2 (scu_calls body_usg) rhs_infos nullUsage + ; (spec_usg, specs) <- specLoop rhs_env2 force_spec + (scu_calls body_usg) rhs_infos nullUsage [SI [] 0 (Just usg) | usg <- rhs_usgs] ; let all_usg = spec_usg `combineUsage` body_usg @@ -913,8 +915,26 @@ scExpr' env (Let (Rec prs) body) ; return (all_usg { scu_calls = scu_calls all_usg `delVarEnvList` bndrs' }, Let bind' body') } +\end{code} + +Note [Local let bindings] +~~~~~~~~~~~~~~~~~~~~~~~~~ +It is not uncommon to find this + + let $j = \x. in ...$j True...$j True... + +Here $j is an arbitrary let-bound function, but it often comes up for +join points. We might like to specialise $j for its call patterns. +Notice the difference from a letrec, where we look for call patterns +in the *RHS* of the function. Here we look for call patterns in the +*body* of the let. + +At one point I predicated this on the RHS mentioning the outer +recursive function, but that's not essential and might even be +harmful. I'm not sure. ------------------------------------ + +\begin{code} scApp :: ScEnv -> (InExpr, [InExpr]) -> UniqSM (ScUsage, CoreExpr) scApp env (Var fn, args) -- Function is a variable @@ -959,6 +979,7 @@ scApp env (other_fn, args) scTopBind :: ScEnv -> CoreBind -> UniqSM (ScEnv, CoreBind) scTopBind env (Rec prs) | Just threshold <- sc_size env + , not force_spec , not (all (couldBeSmallEnoughToInline threshold) rhss) -- No specialisation = do { let (rhs_env,bndrs') = extendRecBndrs env bndrs @@ -971,13 +992,15 @@ scTopBind env (Rec prs) ; (rhs_usgs, rhs_infos) <- mapAndUnzipM (scRecRhs rhs_env2) (bndrs' `zip` rhss) ; let rhs_usg = combineUsages rhs_usgs - ; (_, specs) <- specLoop rhs_env2 (scu_calls rhs_usg) rhs_infos nullUsage + ; (_, specs) <- specLoop rhs_env2 force_spec + (scu_calls rhs_usg) rhs_infos nullUsage [SI [] 0 Nothing | _ <- bndrs] ; return (rhs_env1, -- For the body of the letrec, delete the RecFun business Rec (concat (zipWith specInfoBinds rhs_infos specs))) } where (bndrs,rhss) = unzip prs + force_spec = any (forceSpecBndr env) bndrs scTopBind env (NonRec bndr rhs) = do { (_, rhs') <- scExpr env rhs @@ -1042,12 +1065,13 @@ data OneSpec = OS CallPat -- Call pattern that generated this specialisation specLoop :: ScEnv + -> Bool -- force specialisation? -> CallEnv -> [RhsInfo] -> ScUsage -> [SpecInfo] -- One per binder; acccumulating parameter -> UniqSM (ScUsage, [SpecInfo]) -- ...ditto... -specLoop env all_calls rhs_infos usg_so_far specs_so_far - = do { specs_w_usg <- zipWithM (specialise env all_calls) rhs_infos specs_so_far +specLoop env force_spec all_calls rhs_infos usg_so_far specs_so_far + = do { specs_w_usg <- zipWithM (specialise env force_spec all_calls) rhs_infos specs_so_far ; let (new_usg_s, all_specs) = unzip specs_w_usg new_usg = combineUsages new_usg_s new_calls = scu_calls new_usg @@ -1055,10 +1079,11 @@ specLoop env all_calls rhs_infos usg_so_far specs_so_far ; if isEmptyVarEnv new_calls then return (all_usg, all_specs) else - specLoop env new_calls rhs_infos all_usg all_specs } + specLoop env force_spec new_calls rhs_infos all_usg all_specs } specialise :: ScEnv + -> Bool -- force specialisation? -> CallEnv -- Info on calls -> RhsInfo -> SpecInfo -- Original RHS plus patterns dealt with @@ -1068,26 +1093,29 @@ specialise -- So when we make a specialised copy of the RHS, we're starting -- from an RHS whose nested functions have been optimised already. -specialise env bind_calls (fn, arg_bndrs, body, arg_occs) +specialise env force_spec bind_calls (fn, arg_bndrs, body, arg_occs) spec_info@(SI specs spec_count mb_unspec) | not (isBottomingId fn) -- Note [Do not specialise diverging functions] , notNull arg_bndrs -- Only specialise functions , Just all_calls <- lookupVarEnv bind_calls fn = do { (boring_call, pats) <- callsToPats env specs arg_occs all_calls --- ; pprTrace "specialise" (vcat [ppr fn <+> ppr arg_occs, --- text "calls" <+> ppr all_calls, --- text "good pats" <+> ppr pats]) $ +-- ; pprTrace "specialise" (vcat [ ppr fn <+> text "with" <+> int (length pats) <+> text "good patterns" +-- , text "arg_occs" <+> ppr arg_occs, +-- , text "calls" <+> ppr all_calls, +-- , text "good pats" <+> ppr pats]) $ -- return () -- Bale out if too many specialisations -- Rather a hacky way to do so, but it'll do for now ; let spec_count' = length pats + spec_count ; case sc_count env of - Just max | spec_count' > max - -> WARN( True, msg ) return (nullUsage, spec_info) + Just max | not force_spec && spec_count' > max + -> pprTrace "SpecConstr" msg $ + return (nullUsage, spec_info) where - msg = vcat [ sep [ ptext (sLit "SpecConstr: specialisation of") <+> quotes (ppr fn) - , nest 2 (ptext (sLit "limited by bound of")) <+> int max ] + msg = vcat [ sep [ ptext (sLit "Function") <+> quotes (ppr fn) + , nest 2 (ptext (sLit "has") <+> int spec_count' <+> + ptext (sLit "call patterns, but the limit is") <+> int max) ] , ptext (sLit "Use -fspec-constr-count=n to set the bound") , extra ] extra | not opt_PprStyle_Debug = ptext (sLit "Use -dppr-debug to see specialisations") @@ -1155,18 +1183,19 @@ spec_one env fn arg_bndrs body (call_pat@(qvars, pats), rule_number) -- Usual w/w hack to avoid generating -- a spec_rhs of unlifted type and no args - fn_name = idName fn - fn_loc = nameSrcSpan fn_name - spec_occ = mkSpecOcc (nameOccName fn_name) - rule_name = mkFastString ("SC:" ++ showSDoc (ppr fn <> int rule_number)) - spec_rhs = mkLams spec_lam_args spec_body - spec_str = calcSpecStrictness fn spec_lam_args pats - spec_id = mkUserLocal spec_occ spec_uniq (mkPiTypes spec_lam_args body_ty) fn_loc - `setIdNewStrictness` spec_str -- See Note [Transfer strictness] - `setIdArity` count isId spec_lam_args - body_ty = exprType spec_body - rule_rhs = mkVarApps (Var spec_id) spec_call_args - rule = mkLocalRule rule_name specConstrActivation fn_name qvars pats rule_rhs + fn_name = idName fn + fn_loc = nameSrcSpan fn_name + spec_occ = mkSpecOcc (nameOccName fn_name) + rule_name = mkFastString ("SC:" ++ showSDoc (ppr fn <> int rule_number)) + spec_rhs = mkLams spec_lam_args spec_body + spec_str = calcSpecStrictness fn spec_lam_args pats + spec_id = mkUserLocal spec_occ spec_uniq (mkPiTypes spec_lam_args body_ty) fn_loc + `setIdStrictness` spec_str -- See Note [Transfer strictness] + `setIdArity` count isId spec_lam_args + body_ty = exprType spec_body + rule_rhs = mkVarApps (Var spec_id) spec_call_args + inline_act = idInlineActivation fn + rule = mkLocalRule rule_name inline_act fn_name qvars pats rule_rhs ; return (spec_usg, OS call_pat rule spec_id spec_rhs) } calcSpecStrictness :: Id -- The original function @@ -1177,7 +1206,7 @@ calcSpecStrictness fn qvars pats = StrictSig (mkTopDmdType spec_dmds TopRes) where spec_dmds = [ lookupVarEnv dmd_env qv `orElse` lazyDmd | qv <- qvars, isId qv ] - StrictSig (DmdType _ dmds _) = idNewStrictness fn + StrictSig (DmdType _ dmds _) = idStrictness fn dmd_env = go emptyVarEnv dmds pats @@ -1191,18 +1220,23 @@ calcSpecStrictness fn qvars pats | (Var _, args) <- collectArgs e = go env ds args go_one env _ _ = env --- In which phase should the specialise-constructor rules be active? --- Originally I made them always-active, but Manuel found that --- this defeated some clever user-written rules. So Plan B --- is to make them active only in Phase 0; after all, currently, --- the specConstr transformation is only run after the simplifier --- has reached Phase 0. In general one would want it to be --- flag-controllable, but for now I'm leaving it baked in --- [SLPJ Oct 01] -specConstrActivation :: Activation -specConstrActivation = ActiveAfter 0 -- Baked in; see comments above \end{code} +Note [Transfer activation] +~~~~~~~~~~~~~~~~~~~~~~~~~~ +In which phase should the specialise-constructor rules be active? +Originally I made them always-active, but Manuel found that this +defeated some clever user-written rules. Then I made them active only +in Phase 0; after all, currently, the specConstr transformation is +only run after the simplifier has reached Phase 0, but that meant +that specialisations didn't fire inside wrappers; see test +simplCore/should_compile/spec-inline. + +So now I just use the inline-activation of the parent Id, as the +activation for the specialiation RULE, just like the main specialiser; +see Note [Auto-specialisation and RULES] in Specialise. + + Note [Transfer strictness] ~~~~~~~~~~~~~~~~~~~~~~~~~~ We must transfer strictness information from the original function to