-specialise :: ScEnv
- -> Id -- Functionn
- -> [CoreBndr] -> CoreExpr -- Its RHS
- -> ScUsage -- Info on usage
- -> UniqSM ([CoreRule], -- Rules
- [(Id,CoreExpr)]) -- Bindings
-
-specialise env fn bndrs body body_usg
- = do { let (_, bndr_occs) = lookupOccs body_usg bndrs
-
- ; mb_calls <- mapM (callToPats (scope env) bndr_occs)
- (lookupVarEnv (calls body_usg) fn `orElse` [])
-
- ; let good_calls :: [([Var], [CoreArg])]
- good_calls = catMaybes mb_calls
- in_scope = mkInScopeSet $ unionVarSets $
- [ exprsFreeVars pats `delVarSetList` vs
- | (vs,pats) <- good_calls ]
- uniq_calls = nubBy (same_call in_scope) good_calls
- in
- mapAndUnzipUs (spec_one env fn (mkLams bndrs body))
- (uniq_calls `zip` [1..]) }
- where
- -- Two calls are the same if they match both ways
- same_call in_scope (vs1,as1)(vs2,as2)
- = isJust (matchN in_scope vs1 as1 as2)
- && isJust (matchN in_scope vs2 as2 as1)
-
-callToPats :: InScopeEnv -> [ArgOcc] -> Call
- -> UniqSM (Maybe ([Var], [CoreExpr]))
- -- The VarSet is the variables to quantify over in the rule
- -- The [CoreExpr] are the argument patterns for the rule
-callToPats in_scope bndr_occs (con_env, args)
- | length args < length bndr_occs -- Check saturated
- = return Nothing
+data RhsInfo = RI OutId -- The binder
+ OutExpr -- The new RHS
+ [InVar] InExpr -- The *original* RHS (\xs.body)
+ -- Note [Specialise original body]
+ [ArgOcc] -- Info on how the xs occur in body
+
+data SpecInfo = SI [OneSpec] -- The specialisations we have generated
+
+ Int -- Length of specs; used for numbering them
+
+ (Maybe ScUsage) -- Nothing => we have generated specialisations
+ -- from calls in the *original* RHS
+ -- Just cs => we haven't, and this is the usage
+ -- of the original RHS
+ -- See Note [Local recursive groups]
+
+ -- One specialisation: Rule plus definition
+data OneSpec = OS CallPat -- Call pattern that generated this specialisation
+ CoreRule -- Rule connecting original id with the specialisation
+ OutId OutExpr -- Spec id + its rhs
+
+
+specLoop :: ScEnv
+ -> Bool -- force specialisation?
+ -- Note [Forcing specialisation]
+ -> CallEnv
+ -> [RhsInfo]
+ -> ScUsage -> [SpecInfo] -- One per binder; acccumulating parameter
+ -> UniqSM (ScUsage, [SpecInfo]) -- ...ditto...
+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
+ all_usg = usg_so_far `combineUsage` new_usg
+ ; if isEmptyVarEnv new_calls then
+ return (all_usg, all_specs)
+ else
+ specLoop env force_spec new_calls rhs_infos all_usg all_specs }
+
+specialise
+ :: ScEnv
+ -> Bool -- force specialisation?
+ -- Note [Forcing specialisation]
+ -> CallEnv -- Info on calls
+ -> RhsInfo
+ -> SpecInfo -- Original RHS plus patterns dealt with
+ -> UniqSM (ScUsage, SpecInfo) -- New specialised versions and their usage
+
+-- Note: the rhs here is the optimised version of the original rhs
+-- 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 force_spec bind_calls (RI fn _ arg_bndrs body arg_occs)
+ spec_info@(SI specs spec_count mb_unspec)
+ | not (isBottomingId fn) -- Note [Do not specialise diverging functions]
+ , not (isNeverActive (idInlineActivation fn)) -- See Note [Transfer activation]
+ , 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 <+> 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
+ ; let n_pats = length pats
+ spec_count' = n_pats + spec_count
+ ; case sc_count env of
+ Just max | not force_spec && spec_count' > max
+ -> pprTrace "SpecConstr" msg $
+ return (nullUsage, spec_info)
+ where
+ msg = vcat [ sep [ ptext (sLit "Function") <+> quotes (ppr fn)
+ , nest 2 (ptext (sLit "has") <+>
+ speakNOf spec_count' (ptext (sLit "call pattern")) <> comma <+>
+ ptext (sLit "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")
+ | otherwise = ptext (sLit "Specialisations:") <+> ppr (pats ++ [p | OS p _ _ _ <- specs])
+
+ _normal_case -> do {
+
+ let spec_env = decreaseSpecCount env n_pats
+ ; (spec_usgs, new_specs) <- mapAndUnzipM (spec_one spec_env fn arg_bndrs body)
+ (pats `zip` [spec_count..])
+ -- See Note [Specialise original body]
+
+ ; let spec_usg = combineUsages spec_usgs
+ (new_usg, mb_unspec')
+ = case mb_unspec of
+ Just rhs_usg | boring_call -> (spec_usg `combineUsage` rhs_usg, Nothing)
+ _ -> (spec_usg, mb_unspec)
+
+ ; return (new_usg, SI (new_specs ++ specs) spec_count' mb_unspec') } }