summaryrefslogtreecommitdiff
path: root/compiler/specialise
diff options
context:
space:
mode:
authorSimon Peyton Jones <simonpj@microsoft.com>2018-07-30 13:43:56 +0100
committerSimon Peyton Jones <simonpj@microsoft.com>2018-07-31 13:19:43 +0100
commit2110738b280543698407924a16ac92b6d804dc36 (patch)
treef5a5bbb377b4554e99eae18a146894908d88f9d5 /compiler/specialise
parent56590db07a776ce81eb89d4a4d86bd0f953fb44e (diff)
downloadhaskell-2110738b280543698407924a16ac92b6d804dc36.tar.gz
Don't inline functions with RULES too early
Trac #15445 showed that a function with an automatically generated specialisation RULE coudl be inlined before the RULE had a chance to fire. This patch attaches a NOINLINE[2] activation to the Id, to stop this happening.
Diffstat (limited to 'compiler/specialise')
-rw-r--r--compiler/specialise/Rules.hs49
1 files changed, 42 insertions, 7 deletions
diff --git a/compiler/specialise/Rules.hs b/compiler/specialise/Rules.hs
index 3380d02f99..850dba64cd 100644
--- a/compiler/specialise/Rules.hs
+++ b/compiler/specialise/Rules.hs
@@ -46,7 +46,8 @@ import TysWiredIn ( anyTypeOfKind )
import Coercion
import CoreTidy ( tidyRules )
import Id
-import IdInfo ( RuleInfo( RuleInfo ) )
+import IdInfo ( IdInfo( ruleInfo, inlinePragInfo )
+ , RuleInfo( RuleInfo ), setRuleInfo, setInlinePragInfo )
import Var
import VarEnv
import VarSet
@@ -55,7 +56,7 @@ import NameSet
import NameEnv
import UniqFM
import Unify ( ruleMatchTyKiX )
-import BasicTypes ( Activation, CompilerPhase, isActive, pprRuleName )
+import BasicTypes
import DynFlags ( DynFlags )
import Outputable
import FastString
@@ -290,11 +291,23 @@ addRuleInfo (RuleInfo rs1 fvs1) (RuleInfo rs2 fvs2)
= RuleInfo (rs1 ++ rs2) (fvs1 `unionDVarSet` fvs2)
addIdSpecialisations :: Id -> [CoreRule] -> Id
-addIdSpecialisations id []
- = id
+-- See Note [Adding specialisations to an Id]
addIdSpecialisations id rules
- = setIdSpecialisation id $
- extendRuleInfo (idSpecialisation id) rules
+ | null rules
+ = id
+ | otherwise
+ = modifyIdInfo (add_rules . add_activation) id
+ where
+ add_rules, add_activation :: IdInfo -> IdInfo
+ add_rules info = info `setRuleInfo` extendRuleInfo (ruleInfo info) rules
+ add_activation info
+ | AlwaysActive <- inlinePragmaActivation inl_prag
+ = info `setInlinePragInfo` inl_prag'
+ | otherwise
+ = info
+ where
+ inl_prag = inlinePragInfo info
+ inl_prag' = inl_prag `setInlinePragmaActivation` activeAfterInitial
-- | Gather all the rules for locally bound identifiers from the supplied bindings
rulesOfBinds :: [CoreBind] -> [CoreRule]
@@ -312,7 +325,29 @@ ruleIsVisible _ BuiltinRule{} = True
ruleIsVisible vis_orphs Rule { ru_orphan = orph, ru_origin = origin }
= notOrphan orph || origin `elemModuleSet` vis_orphs
-{-
+{- Note [Adding specialisations to an Id]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose (Trac #15445) we have
+ f,g :: Num a => a -> a
+ f x = ...f (x-1).....
+ g y = ...g (y-1) ....
+and we make some specialisations of 'g', either automatically, or via
+a SPECIALISE pragma. Then CSE kicks in and notices that the RHSs of
+'f' and 'g' are identical, so we get
+ f x = ...f (x-1)...
+ g = f
+ {-# RULES g @Int _ = $sg #-}
+
+Now there is terrible danger that, in an importing module, we'll inline
+'g' before we have a chance to run its specialisation!
+
+This is admittedly a bit of an exotic case; but in general with RULES
+we want to delay inlining to give the rule a chance to fire. So we
+attach a NOINLINE[2] activation to it, to ensure it's not inlined
+right away. c.f. other uses of activeAfterInitial in the compiler
+e.g. Note [Wrapper activation] in WorkWrap, and
+ Note [Activation for data constructor wrappers] in MkId
+
Note [Where rules are found]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The rules for an Id come from two places: