diff options
Diffstat (limited to 'ghc/compiler/specialise/Specialise.lhs')
-rw-r--r-- | ghc/compiler/specialise/Specialise.lhs | 2535 |
1 files changed, 2535 insertions, 0 deletions
diff --git a/ghc/compiler/specialise/Specialise.lhs b/ghc/compiler/specialise/Specialise.lhs new file mode 100644 index 0000000000..5962ca7ac4 --- /dev/null +++ b/ghc/compiler/specialise/Specialise.lhs @@ -0,0 +1,2535 @@ +% +% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995 +% +\section[Specialise]{Stamping out overloading, and (optionally) polymorphism} + +\begin{code} +#include "HsVersions.h" + +module Specialise ( + specProgram, + initSpecData, + + SpecialiseData(..), + FiniteMap, Bag + + ) where + +import PlainCore +import SpecTyFuns + +IMPORT_Trace +import Outputable -- ToDo: these may be removable... +import Pretty + +import AbsPrel ( liftDataCon, PrimOp(..), PrimKind -- for CCallOp + IF_ATTACK_PRAGMAS(COMMA tagOf_PrimOp) + IF_ATTACK_PRAGMAS(COMMA pprPrimOp) + ) +import AbsUniType +import Bag +import CmdLineOpts ( GlobalSwitch(..) ) +import CoreLift ( mkLiftedId, liftExpr, bindUnlift, applyBindUnlifts ) +import FiniteMap +import Id +import IdEnv +import IdInfo -- All of it +import InstEnv ( lookupClassInstAtSimpleType ) +import Maybes ( catMaybes, firstJust, maybeToBool, Maybe(..) ) +import TyVarEnv -- ( growTyVarEnvList, nullTyVarEnv, TyVarEnv, TypeEnv(..) ) +import UniqSet -- All of it +import Util +import SplitUniq + +infixr 9 `thenSM` +\end{code} + +%************************************************************************ +%* * +\subsection[notes-Specialise]{Implementation notes [SLPJ, Aug 18 1993]} +%* * +%************************************************************************ + +These notes describe how we implement specialisation to eliminate +overloading, and optionally to eliminate unboxed polymorphism, and +full polymorphism. + +The specialisation pass is a partial evaluator which works on Core +syntax, complete with all the explicit dictionary application, +abstraction and construction as added by the type checker. The +existing type checker remains largely as it is. + +One important thought: the {\em types} passed to an overloaded +function, and the {\em dictionaries} passed are mutually redundant. +If the same function is applied to the same type(s) then it is sure to +be applied to the same dictionary(s)---or rather to the same {\em +values}. (The arguments might look different but they will evaluate +to the same value.) + +Second important thought: we know that we can make progress by +treating dictionary arguments as static and worth specialising on. So +we can do without binding-time analysis, and instead specialise on +dictionary arguments and no others. + +The basic idea +~~~~~~~~~~~~~~ +Suppose we have + + let f = <f_rhs> + in <body> + +and suppose f is overloaded. + +STEP 1: CALL-INSTANCE COLLECTION + +We traverse <body>, accumulating all applications of f to types and +dictionaries. + +(Might there be partial applications, to just some of its types and +dictionaries? In principle yes, but in practice the type checker only +builds applications of f to all its types and dictionaries, so partial +applications could only arise as a result of transformation, and even +then I think it's unlikely. In any case, we simply don't accumulate such +partial applications.) + +There's a choice of whether to collect details of all *polymorphic* functions +or simply all *overloaded* ones. How to sort this out? + Pass in a predicate on the function to say if it is "interesting"? + This is dependent on the user flags: SpecialiseOverloaded + SpecialiseUnboxed + SpecialiseAll + +STEP 2: EQUIVALENCES + +So now we have a collection of calls to f: + f t1 t2 d1 d2 + f t3 t4 d3 d4 + ... +Notice that f may take several type arguments. To avoid ambiguity, we +say that f is called at type t1/t2 and t3/t4. + +We take equivalence classes using equality of the *types* (ignoring +the dictionary args, which as mentioned previously are redundant). + +STEP 3: SPECIALISATION + +For each equivalence class, choose a representative (f t1 t2 d1 d2), +and create a local instance of f, defined thus: + + f@t1/t2 = <f_rhs> t1 t2 d1 d2 + +(f_rhs presumably has some big lambdas and dictionary lambdas, so lots +of simplification will now result.) Then we should recursively do +everything again. + +The new id has its own unique, but its print-name (if exported) has +an explicit representation of the instance types t1/t2. + +Add this new id to f's IdInfo, to record that f has a specialised version. + +Before doing any of this, check that f's IdInfo doesn't already +tell us about an existing instance of f at the required type/s. +(This might happen if specialisation was applied more than once, or +it might arise from user SPECIALIZE pragmas.) + +Recursion +~~~~~~~~~ +Wait a minute! What if f is recursive? Then we can't just plug in +its right-hand side, can we? + +But it's ok. The type checker *always* creates non-recursive definitions +for overloaded recursive functions. For example: + + f x = f (x+x) -- Yes I know its silly + +becomes + + f a (d::Num a) = let p = +.sel a d + in + letrec fl (y::a) = fl (p y y) + in + fl + +We still have recusion for non-overloadd functions which we +speciailise, but the recursive call should get speciailised to the +same recursive version. + + +Polymorphism 1 +~~~~~~~~~~~~~~ + +All this is crystal clear when the function is applied to *constant +types*; that is, types which have no type variables inside. But what if +it is applied to non-constant types? Suppose we find a call of f at type +t1/t2. There are two possibilities: + +(a) The free type variables of t1, t2 are in scope at the definition point +of f. In this case there's no problem, we proceed just as before. A common +example is as follows. Here's the Haskell: + + g y = let f x = x+x + in f y + f y + +After typechecking we have + + g a (d::Num a) (y::a) = let f b (d'::Num b) (x::b) = +.sel b d' x x + in +.sel a d (f a d y) (f a d y) + +Notice that the call to f is at type type "a"; a non-constant type. +Both calls to f are at the same type, so we can specialise to give: + + g a (d::Num a) (y::a) = let f@a (x::a) = +.sel a d x x + in +.sel a d (f@a y) (f@a y) + + +(b) The other case is when the type variables in the instance types +are *not* in scope at the definition point of f. The example we are +working with above is a good case. There are two instances of (+.sel a d), +but "a" is not in scope at the definition of +.sel. Can we do anything? +Yes, we can "common them up", a sort of limited common sub-expression deal. +This would give: + + g a (d::Num a) (y::a) = let +.sel@a = +.sel a d + f@a (x::a) = +.sel@a x x + in +.sel@a (f@a y) (f@a y) + +This can save work, and can't be spotted by the type checker, because +the two instances of +.sel weren't originally at the same type. + +Further notes on (b) + +* There are quite a few variations here. For example, the defn of + +.sel could be floated ouside the \y, to attempt to gain laziness. + It certainly mustn't be floated outside the \d because the d has to + be in scope too. + +* We don't want to inline f_rhs in this case, because +that will duplicate code. Just commoning up the call is the point. + +* Nothing gets added to +.sel's IdInfo. + +* Don't bother unless the equivalence class has more than one item! + +Not clear whether this is all worth it. It is of course OK to +simply discard call-instances when passing a big lambda. + +Polymorphism 2 -- Overloading +~~~~~~~~~~~~~~ +Consider a function whose most general type is + + f :: forall a b. Ord a => [a] -> b -> b + +There is really no point in making a version of g at Int/Int and another +at Int/Bool, because it's only instancing the type variable "a" which +buys us any efficiency. Since g is completely polymorphic in b there +ain't much point in making separate versions of g for the different +b types. + +That suggests that we should identify which of g's type variables +are constrained (like "a") and which are unconstrained (like "b"). +Then when taking equivalence classes in STEP 2, we ignore the type args +corresponding to unconstrained type variable. In STEP 3 we make +polymorphic versions. Thus: + + f@t1/ = /\b -> <f_rhs> t1 b d1 d2 + +This seems pretty simple, and a Good Thing. + +Polymorphism 3 -- Unboxed +~~~~~~~~~~~~~~ + +If we are speciailising at unboxed types we must speciailise +regardless of the overloading constraint. In the exaple above it is +worth speciailising at types Int/Int#, Int/Bool# and a/Int#, Int#/Int# +etc. + +Note that specialising an overloaded type at an uboxed type requires +an unboxed instance -- we cannot default to an unspecialised version! + + +Dictionary floating +~~~~~~~~~~~~~~~~~~~ +Consider + + f x = let g p q = p==q + h r s = (r+s, g r s) + in + h x x + + +Before specialisation, leaving out type abstractions we have + + f df x = let g :: Eq a => a -> a -> Bool + g dg p q = == dg p q + h :: Num a => a -> a -> (a, Bool) + h dh r s = let deq = eqFromNum dh + in (+ dh r s, g deq r s) + in + h df x x + +After specialising h we get a specialised version of h, like this: + + h' r s = let deq = eqFromNum df + in (+ df r s, g deq r s) + +But we can't naively make an instance for g from this, because deq is not in scope +at the defn of g. Instead, we have to float out the (new) defn of deq +to widen its scope. Notice that this floating can't be done in advance -- it only +shows up when specialisation is done. + +DELICATE MATTER: the way we tell a dictionary binding is by looking to +see if it has a Dict type. If the type has been "undictify'd", so that +it looks like a tuple, then the dictionary binding won't be floated, and +an opportunity to specialise might be lost. + +User SPECIALIZE pragmas +~~~~~~~~~~~~~~~~~~~~~~~ +Specialisation pragmas can be digested by the type checker, and implemented +by adding extra definitions along with that of f, in the same way as before + + f@t1/t2 = <f_rhs> t1 t2 d1 d2 + +Indeed the pragmas *have* to be dealt with by the type checker, because +only it knows how to build the dictionaries d1 and d2! For example + + g :: Ord a => [a] -> [a] + {-# SPECIALIZE f :: [Tree Int] -> [Tree Int] #-} + +Here, the specialised version of g is an application of g's rhs to the +Ord dictionary for (Tree Int), which only the type checker can conjure +up. There might not even *be* one, if (Tree Int) is not an instance of +Ord! (All the other specialision has suitable dictionaries to hand +from actual calls.) + +Problem. The type checker doesn't have to hand a convenient <f_rhs>, because +it is buried in a complex (as-yet-un-desugared) binding group. +Maybe we should say + + f@t1/t2 = f* t1 t2 d1 d2 + +where f* is the Id f with an IdInfo which says "inline me regardless!". +Indeed all the specialisation could be done in this way. +That in turn means that the simplifier has to be prepared to inline absolutely +any in-scope let-bound thing. + + +Again, the pragma should permit polymorphism in unconstrained variables: + + h :: Ord a => [a] -> b -> b + {-# SPECIALIZE h :: [Int] -> b -> b #-} + +We *insist* that all overloaded type variables are specialised to ground types, +(and hence there can be no context inside a SPECIALIZE pragma). +We *permit* unconstrained type variables to be specialised to + - a ground type + - or left as a polymorphic type variable +but nothing in between. So + + {-# SPECIALIZE h :: [Int] -> [c] -> [c] #-} + +is *illegal*. (It can be handled, but it adds complication, and gains the +programmer nothing.) + + +SPECIALISING INSTANCE DECLARATIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Consider + + instance Foo a => Foo [a] where + ... + {-# SPECIALIZE instance Foo [Int] #-} + +The original instance decl creates a dictionary-function +definition: + + dfun.Foo.List :: forall a. Foo a -> Foo [a] + +The SPECIALIZE pragma just makes a specialised copy, just as for +ordinary function definitions: + + dfun.Foo.List@Int :: Foo [Int] + dfun.Foo.List@Int = dfun.Foo.List Int dFooInt + +The information about what instance of the dfun exist gets added to +the dfun's IdInfo in the same way as a user-defined function too. + +In fact, matters are a little bit more complicated than this. +When we make one of these specialised instances, we are defining +a constant dictionary, and so we want immediate access to its constant +methods and superclasses. Indeed, these constant methods and superclasses +must be in the IdInfo for the class selectors! We need help from the +typechecker to sort this out, perhaps by generating a separate IdInfo +for each. + +Automatic instance decl specialisation? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Can instance decls be specialised automatically? It's tricky. +We could collect call-instance information for each dfun, but +then when we specialised their bodies we'd get new call-instances +for ordinary functions; and when we specialised their bodies, we might get +new call-instances of the dfuns, and so on. This all arises because of +the unrestricted mutual recursion between instance decls and value decls. + +Furthermore, instance decls are usually exported and used non-locally, +so we'll want to compile enough to get those specialisations done. + +Lastly, there's no such thing as a local instance decl, so we can +survive solely by spitting out *usage* information, and then reading that +back in as a pragma when next compiling the file. So for now, +we only specialise instance decls in response to pragmas. + +That means that even if an instance decl ain't otherwise exported it +needs to be spat out as with a SPECIALIZE pragma. Furthermore, it needs +something to say which module defined the instance, so the usage info +can be fed into the right reqts info file. Blegh. + + +SPECIAILISING DATA DECLARATIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With unboxed specialisation (or full specialisation) we also require +data types (and their constructors) to be speciailised on unboxed +type arguments. + +In addition to normal call instances we gather TyCon call instances at +unboxed types, determine equivalence classes for the locally defined +TyCons and build speciailised data constructor Ids for each TyCon and +substitute these in the CoCon calls. + +We need the list of local TyCons to partition the TyCon instance info. +We pass out a FiniteMap from local TyCons to Specialised Instances to +give to the interface and code genertors. + +N.B. The specialised data constructors reference the original data +constructor and type constructor which do not have the updated +specialisation info attached. Any specialisation info must be +extracted from the TyCon map returned. + + +SPITTING OUT USAGE INFORMATION +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To spit out usage information we need to traverse the code collecting +call-instance information for all imported (non-prelude?) functions +and data types. Then we equivalence-class it and spit it out. + +This is done at the top-level when all the call instances which escape +must be for imported functions and data types. + + +Partial specialisation by pragmas +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +What about partial specialisation: + + k :: (Ord a, Eq b) => [a] -> b -> b -> [a] + {-# SPECIALIZE k :: Eq b => [Int] -> b -> b -> [a] #-} + +or even + + {-# SPECIALIZE k :: Eq b => [Int] -> [b] -> [b] -> [a] #-} + +Seems quite reasonable. Similar things could be done with instance decls: + + instance (Foo a, Foo b) => Foo (a,b) where + ... + {-# SPECIALIZE instance Foo a => Foo (a,Int) #-} + {-# SPECIALIZE instance Foo b => Foo (Int,b) #-} + +Ho hum. Things are complex enough without this. I pass. + + +Requirements for the simplifer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The simplifier has to be able to take advantage of the specialisation. + +* When the simplifier finds an application of a polymorphic f, it looks in +f's IdInfo in case there is a suitable instance to call instead. This converts + + f t1 t2 d1 d2 ===> f_t1_t2 + +Note that the dictionaries get eaten up too! + +* Dictionary selection operations on constant dictionaries must be + short-circuited: + + +.sel Int d ===> +Int + +The obvious way to do this is in the same way as other specialised +calls: +.sel has inside it some IdInfo which tells that if it's applied +to the type Int then it should eat a dictionary and transform to +Int. + +In short, dictionary selectors need IdInfo inside them for constant +methods. + +* Exactly the same applies if a superclass dictionary is being + extracted: + + Eq.sel Int d ===> dEqInt + +* Something similar applies to dictionary construction too. Suppose +dfun.Eq.List is the function taking a dictionary for (Eq a) to +one for (Eq [a]). Then we want + + dfun.Eq.List Int d ===> dEq.List_Int + +Where does the Eq [Int] dictionary come from? It is built in +response to a SPECIALIZE pragma on the Eq [a] instance decl. + +In short, dfun Ids need IdInfo with a specialisation for each +constant instance of their instance declaration. + + +What does the specialisation IdInfo look like? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + SpecInfo + [Maybe UniType] -- Instance types + Int -- No of dicts to eat + Id -- Specialised version + +For example, if f has this SpecInfo: + + SpecInfo [Just t1, Nothing, Just t3] 2 f' + +then + + f t1 t2 t3 d1 d2 ===> f t2 + +The "Nothings" identify type arguments in which the specialised +version is polymorphic. + +What can't be done this way? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +There is no way, post-typechecker, to get a dictionary for (say) +Eq a from a dictionary for Eq [a]. So if we find + + ==.sel [t] d + +we can't transform to + + eqList (==.sel t d') + +where + eqList :: (a->a->Bool) -> [a] -> [a] -> Bool + +Of course, we currently have no way to automatically derive +eqList, nor to connect it to the Eq [a] instance decl, but you +can imagine that it might somehow be possible. Taking advantage +of this is permanently ruled out. + +Still, this is no great hardship, because we intend to eliminate +overloading altogether anyway! + + +Mutter mutter +~~~~~~~~~~~~~ +What about types/classes mentioned in SPECIALIZE pragmas spat out, +but not otherwise exported. Even if they are exported, what about +their original names. + +Suggestion: use qualified names in pragmas, omitting module for +prelude and "this module". + + +Mutter mutter 2 +~~~~~~~~~~~~~~~ +Consider this + + f a (d::Num a) = let g = ... + in + ...(let d1::Ord a = Num.Ord.sel a d in g a d1)... + +Here, g is only called at one type, but the dictionary isn't in scope at the +definition point for g. Usually the type checker would build a +definition for d1 which enclosed g, but the transformation system +might have moved d1's defn inward. + + +Unboxed bindings +~~~~~~~~~~~~~~~~ + +What should we do when a value is specialised to a *strict* unboxed value? + + map_*_* f (x:xs) = let h = f x + t = map f xs + in h:t + +Could convert let to case: + + map_*_Int# f (x:xs) = case f x of h# -> + let t = map f xs + in h#:t + +This may be undesirable since it forces evaluation here, but the value +may not be used in all branches of the body. In the general case this +transformation is impossible since the mutual recursion in a letrec +cannot be expressed as a case. + +There is also a problem with top-level unboxed values, since our +implementation cannot handle unboxed values at the top level. + +Solution: Lift the binding of the unboxed value and extract it when it +is used: + + map_*_Int# f (x:xs) = let h = case (f x) of h# -> _Lift h# + t = map f xs + in case h of + _Lift h# -> h#:t + +Now give it to the simplifier and the _Lifting will be optimised away. + +The benfit is that we have given the specialised "unboxed" values a +very simple lifted semantics and then leave it up to the simplifier to +optimise it --- knowing that the overheads will be removed in nearly +all cases. + +In particular, the value will only be evaluted in the branches of the +program which use it, rather than being forced at the point where the +value is bound. For example: + + filtermap_*_* p f (x:xs) + = let h = f x + t = ... + in case p x of + True -> h:t + False -> t + ==> + filtermap_*_Int# p f (x:xs) + = let h = case (f x) of h# -> _Lift h# + t = ... + in case p x of + True -> case h of _Lift h# + -> h#:t + False -> t + +The binding for h can still be inlined in the one branch and the +_Lifting eliminated. + + +Question: When won't the _Lifting be eliminated? + +Answer: When they at the top-level (where it is necessary) or when +inlining would duplicate work (or possibly code depending on +options). However, the _Lifting will still be eliminated if the +strictness analyser deems the lifted binding strict. + + + +%************************************************************************ +%* * +\subsubsection[CallInstances]{@CallInstances@ data type} +%* * +%************************************************************************ + +\begin{code} +type FreeVarsSet = UniqSet Id +type FreeTyVarsSet = UniqSet TyVar + +data CallInstance + = CallInstance + Id -- This Id; *new* ie *cloned* id + [Maybe UniType] -- Specialised at these types (*new*, cloned) + -- Nothing => no specialisation on this type arg + -- is required (flag dependent). + [PlainCoreArg] -- And these dictionaries; all ValArgs + FreeVarsSet -- Free vars of the dict-args in terms of *new* ids + (Maybe SpecInfo) -- For specialisation with explicit SpecId +\end{code} + +\begin{code} +pprCI :: CallInstance -> Pretty +pprCI (CallInstance id spec_tys dicts _ maybe_specinfo) + = ppHang (ppCat [ppStr "Call inst for", ppr PprDebug id]) + 4 (ppAboves [ppCat (ppStr "types" : [pprMaybeTy PprDebug ty | ty <- spec_tys]), + case maybe_specinfo of + Nothing -> ppCat (ppStr "dicts" : [ppr PprDebug dict | dict <- dicts]) + Just (SpecInfo _ _ spec_id) + -> ppCat [ppStr "Explicit SpecId", ppr PprDebug spec_id] + ]) + +isUnboxedCI :: CallInstance -> Bool +isUnboxedCI (CallInstance _ spec_tys _ _ _) + = any isUnboxedDataType (catMaybes spec_tys) + +isExplicitCI :: CallInstance -> Bool +isExplicitCI (CallInstance _ _ _ _ (Just _)) + = True +isExplicitCI (CallInstance _ _ _ _ Nothing) + = False +\end{code} + +Comparisons are based on the {\em types}, ignoring the dictionary args: + +\begin{code} + +cmpCI :: CallInstance -> CallInstance -> TAG_ +cmpCI (CallInstance id1 tys1 _ _ _) (CallInstance id2 tys2 _ _ _) + = case cmpId id1 id2 of { EQ_ -> cmpUniTypeMaybeList tys1 tys2; other -> other } + +cmpCI_tys :: CallInstance -> CallInstance -> TAG_ +cmpCI_tys (CallInstance _ tys1 _ _ _) (CallInstance _ tys2 _ _ _) + = cmpUniTypeMaybeList tys1 tys2 + +isCIofTheseIds :: [Id] -> CallInstance -> Bool +isCIofTheseIds ids (CallInstance ci_id _ _ _ _) = any (eqId ci_id) ids + +singleCI :: Id -> [Maybe UniType] -> [PlainCoreArg] -> UsageDetails +singleCI id tys dicts + = UsageDetails (unitBag (CallInstance id tys dicts fv_set Nothing)) + emptyBag [] emptyUniqSet + where + fv_set = mkUniqSet (id : [dict | ValArg (CoVarAtom dict) <- dicts]) + +explicitCI :: Id -> [Maybe UniType] -> SpecInfo -> UsageDetails +explicitCI id tys specinfo + = UsageDetails (unitBag call_inst) emptyBag [] emptyUniqSet + where + call_inst = CallInstance id tys dicts fv_set (Just specinfo) + dicts = panic "Specialise:explicitCI:dicts" + fv_set = singletonUniqSet id + +getCIs :: [Id] -> UsageDetails -> ([CallInstance], UsageDetails) +getCIs ids (UsageDetails cis tycon_cis dbs fvs) + = let + (cis_here, cis_not_here) = partitionBag (isCIofTheseIds ids) cis + cis_here_list = bagToList cis_here + in + -- pprTrace "getCIs:" + -- (ppHang (ppBesides [ppStr "{", ppr PprDebug ids, ppStr "}"]) + -- 4 (ppAboves (map pprCI cis_here_list))) + (cis_here_list, UsageDetails cis_not_here tycon_cis dbs fvs) + +dumpCIs :: Bag CallInstance -- The call instances + -> [Id] -- Bound ids *new* + -> Bag CallInstance -- Kept call instances +dumpCIs cis bound_ids + = (if not (isEmptyBag cis_dict_bound_arg) then + (if isEmptyBag unboxed_cis_dict_bound_arg + then (\ x y -> y) -- pprTrace "dumpCIs: bound dictionary arg ... \n" + else pprTrace "dumpCIs: bound dictionary arg ... WITH UNBOXED TYPES!\n") + (ppHang (ppBesides [ppStr "{", ppr PprDebug bound_ids, ppStr "}"]) + 4 (ppAboves (map pprCI (bagToList cis_dump)))) + else id) + cis_keep + where + (cis_dump, cis_keep) = partitionBag mentions_bound_ids cis + + mentions_bound_ids (CallInstance _ _ _ fv_set _) + = or [i `elementOfUniqSet` fv_set | i <- bound_ids] + + (cis_of_bound_id, cis_dict_bound_arg) = partitionBag (isCIofTheseIds bound_ids) cis_dump + (unboxed_cis_dict_bound_arg, _) = partitionBag isUnboxedCI cis_dict_bound_arg + +\end{code} + +Any call instances of a bound_id can be safely dumped, because any +recursive calls should be at the same instance as the parent instance. + + letrec f = /\a -> \x::a -> ...(f t x')... + +Here, the type, t, at which f is used in its own RHS should be +just "a"; that is, the recursive call is at the same type as +the original call. That means that when specialising f at some +type, say Int#, we shouldn't find any *new* instances of f +arising from specialising f's RHS. The only instance we'll find +is another call of (f Int#). + +ToDo: We should check this rather than just dumping them. + +However, we do report any call instances which are mysteriously dumped +because they have a dictionary argument which is bound here ... + +ToDo: Under what circumstances does this occur, if at all? + +%************************************************************************ +%* * +\subsubsection[TyConInstances]{@TyConInstances@ data type} +%* * +%************************************************************************ + +\begin{code} +data TyConInstance + = TyConInstance TyCon -- Type Constructor + [Maybe UniType] -- Applied to these specialising types + +cmpTyConI :: TyConInstance -> TyConInstance -> TAG_ +cmpTyConI (TyConInstance tc1 tys1) (TyConInstance tc2 tys2) + = case cmpTyCon tc1 tc2 of { EQ_ -> cmpUniTypeMaybeList tys1 tys2; other -> other } + +cmpTyConI_tys :: TyConInstance -> TyConInstance -> TAG_ +cmpTyConI_tys (TyConInstance _ tys1) (TyConInstance _ tys2) + = cmpUniTypeMaybeList tys1 tys2 + +singleTyConI :: TyCon -> [Maybe UniType] -> UsageDetails +singleTyConI ty_con spec_tys + = UsageDetails emptyBag (unitBag (TyConInstance ty_con spec_tys)) [] emptyUniqSet + +isTyConIofThisTyCon :: TyCon -> TyConInstance -> Bool +isTyConIofThisTyCon ty_con (TyConInstance inst_ty_con _) = eqTyCon ty_con inst_ty_con + +isLocalSpecTyConI :: Bool -> TyConInstance -> Bool +isLocalSpecTyConI comp_prel (TyConInstance inst_ty_con _) = isLocalSpecTyCon comp_prel inst_ty_con + +getLocalSpecTyConIs :: Bool -> UsageDetails -> ([TyConInstance], UsageDetails) +getLocalSpecTyConIs comp_prel (UsageDetails cis tycon_cis dbs fvs) + = let + (tycon_cis_local, tycon_cis_global) + = partitionBag (isLocalSpecTyConI comp_prel) tycon_cis + tycon_cis_local_list = bagToList tycon_cis_local + in + (tycon_cis_local_list, UsageDetails cis tycon_cis_global dbs fvs) +\end{code} + + +%************************************************************************ +%* * +\subsubsection[UsageDetails]{@UsageDetails@ data type} +%* * +%************************************************************************ + +\begin{code} +data UsageDetails + = UsageDetails + (Bag CallInstance) -- The collection of call-instances + (Bag TyConInstance) -- Constructor call-instances + [DictBindDetails] -- Dictionary bindings in data-dependence order! + FreeVarsSet -- Free variables (excl imported ones, incl top level) (cloned) +\end{code} + +The DictBindDetails are fully processed; their call-instance information is +incorporated in the call-instances of the +UsageDetails which includes the DictBindDetails. The free vars in a usage details +will *include* the binders of the DictBind details. + +A @DictBindDetails@ contains bindings for dictionaries *only*. + +\begin{code} +data DictBindDetails + = DictBindDetails + [Id] -- Main binders, originally visible in scope of binding (cloned) + PlainCoreBinding -- Fully processed + FreeVarsSet -- Free in binding group (cloned) + FreeTyVarsSet -- Free in binding group +\end{code} + +\begin{code} +emptyUDs :: UsageDetails +unionUDs :: UsageDetails -> UsageDetails -> UsageDetails +unionUDList :: [UsageDetails] -> UsageDetails + +emptyUDs = UsageDetails emptyBag emptyBag [] emptyUniqSet + +unionUDs (UsageDetails cis1 tycon_cis1 dbs1 fvs1) (UsageDetails cis2 tycon_cis2 dbs2 fvs2) + = UsageDetails (unionBags cis1 cis2) (unionBags tycon_cis1 tycon_cis2) + (dbs1 ++ dbs2) (fvs1 `unionUniqSets` fvs2) + -- The append here is really redundant, since the bindings don't + -- scope over each other. ToDo. + +unionUDList = foldr unionUDs emptyUDs + +singleFvUDs (CoVarAtom v) | not (isImportedId v) + = UsageDetails emptyBag emptyBag [] (singletonUniqSet v) +singleFvUDs other + = emptyUDs + +singleConUDs con = UsageDetails emptyBag emptyBag [] (singletonUniqSet con) + +dumpDBs :: [DictBindDetails] + -> [TyVar] -- TyVars being bound (cloned) + -> [Id] -- Ids being bound (cloned) + -> FreeVarsSet -- Fvs of body + -> ([PlainCoreBinding], -- These ones have to go here + [DictBindDetails], -- These can float further + [Id], -- Incoming list + names of dicts bound here + FreeVarsSet -- Incominf fvs + fvs of dicts bound here + ) +dumpDBs [] bound_tyvars bound_ids fvs = ([], [], bound_ids, fvs) + +dumpDBs ((db@(DictBindDetails dbinders dbind db_fvs db_ftv)):dbs) + bound_tyvars bound_ids fvs + | or [i `elementOfUniqSet` db_fvs | i <- bound_ids] + || + or [tv `elementOfUniqSet` db_ftv | tv <- bound_tyvars] + = let -- Ha! Dump it! + (dbinds_here, dbs_outer, full_bound_ids, full_fvs) + = dumpDBs dbs bound_tyvars (dbinders ++ bound_ids) (db_fvs `unionUniqSets` fvs) + in + (dbind : dbinds_here, dbs_outer, full_bound_ids, full_fvs) + + | otherwise -- This one can float out further + = let + (dbinds_here, dbs_outer, full_bound_ids, full_fvs) + = dumpDBs dbs bound_tyvars bound_ids fvs + in + (dbinds_here, db : dbs_outer, full_bound_ids, full_fvs) + + + +dumpUDs :: UsageDetails + -> [Id] -- Ids which are just being bound; *new* + -> [TyVar] -- TyVars which are just being bound + -> ([PlainCoreBinding], -- Bindings from UsageDetails which mention the ids + UsageDetails) -- The above bindings removed, and + -- any call-instances which mention the ids dumped too + +dumpUDs (UsageDetails cis tycon_cis dbs fvs) bound_ids tvs + = let + (dict_binds_here, dbs_outer, full_bound_ids, full_fvs) = dumpDBs dbs tvs bound_ids fvs + cis_outer = dumpCIs cis full_bound_ids + fvs_outer = full_fvs `minusUniqSet` (mkUniqSet full_bound_ids) + in + (dict_binds_here, UsageDetails cis_outer tycon_cis dbs_outer fvs_outer) +\end{code} + +\begin{code} +addDictBinds :: [Id] -> PlainCoreBinding -> UsageDetails -- Dict binding and RHS usage + -> UsageDetails -- The usage to augment + -> UsageDetails +addDictBinds dbinders dbind (UsageDetails db_cis db_tycon_cis db_dbs db_fvs) + (UsageDetails cis tycon_cis dbs fvs) + = UsageDetails (db_cis `unionBags` cis) + (db_tycon_cis `unionBags` tycon_cis) + (db_dbs ++ [DictBindDetails dbinders dbind db_fvs db_ftvs] ++ dbs) + fvs + where + -- The free tyvars of the dictionary bindings should really be + -- gotten from the RHSs, but I'm pretty sure it's good enough just + -- to look at the type of the dictionary itself. + -- Doing the proper job would entail keeping track of free tyvars as + -- well as free vars, which would be a bore. + db_ftvs = mkUniqSet (extractTyVarsFromTys (map getIdUniType dbinders)) +\end{code} + +%************************************************************************ +%* * +\subsection[cloning-binders]{The Specialising IdEnv and CloneInfo} +%* * +%************************************************************************ + +@SpecIdEnv@ maps old Ids to their new "clone". There are three cases: + +1) (NoLift CoLitAtom l) : an Id which is bound to a literal + +2) (NoLift CoLitAtom l) : an Id bound to a "new" Id + The new Id is a possibly-type-specialised clone of the original + +3) Lifted lifted_id unlifted_id : + + This indicates that the original Id has been specialised to an + unboxed value which must be lifted (see "Unboxed bindings" above) + @unlifted_id@ is the unboxed clone of the original Id + @lifted_id@ is a *lifted* version of the original Id + + When you lookup Ids which are Lifted, you have to insert a case + expression to un-lift the value (done with @bindUnlift@) + + You also have to insert a case to lift the value in the binding + (done with @liftExpr@) + + +\begin{code} +type SpecIdEnv = IdEnv CloneInfo + +data CloneInfo + = NoLift PlainCoreAtom -- refers to cloned id or literal + + | Lifted Id -- lifted, cloned id + Id -- unlifted, cloned id + +\end{code} + +%************************************************************************ +%* * +\subsection[specialise-data]{Data returned by specialiser} +%* * +%************************************************************************ + +\begin{code} +data SpecialiseData + = SpecData Bool + -- True <=> Specialisation performed + Bool + -- False <=> Specialisation completed with errors + + [TyCon] + -- Local tycons declared in this module + + [TyCon] + -- Those in-scope data types for which we want to + -- generate code for their constructors. + -- Namely: data types declared in this module + + -- any big tuples used in this module + -- The initial (and default) value is the local tycons + + (FiniteMap TyCon [[Maybe UniType]]) + -- TyCon specialisations to be generated + -- We generate specialisations for data types defined + -- in this module and any tuples used in this module + -- The initial (and default) value is the specialisations + -- requested by source-level SPECIALIZE data pragmas + -- and _SPECIALISE_ pragmas in the interface files + + (Bag (Id,[Maybe UniType])) + -- Imported specialisation errors + (Bag (Id,[Maybe UniType])) + -- Imported specialisation warnings + (Bag (TyCon,[Maybe UniType])) + -- Imported TyCon specialisation errors + +initSpecData local_tycons tycon_specs + = SpecData False True local_tycons local_tycons tycon_specs emptyBag emptyBag emptyBag +\end{code} + +ToDo[sansom]: Transformation data to process specialisation requests. + +%************************************************************************ +%* * +\subsection[specProgram]{Specialising a core program} +%* * +%************************************************************************ + +\begin{code} +specProgram :: (GlobalSwitch -> Bool) + -> SplitUniqSupply + -> [PlainCoreBinding] -- input ... + -> SpecialiseData + -> ([PlainCoreBinding], -- main result + SpecialiseData) -- result specialise data + +specProgram sw_chker uniqs binds + (SpecData False _ local_tycons _ init_specs init_errs init_warn init_tyerrs) + = case (initSM (specTyConsAndScope (specTopBinds binds)) sw_chker uniqs) of + (final_binds, tycon_specs_list, + UsageDetails import_cis import_tycis _ fvs) + -> let + used_conids = filter isDataCon (uniqSetToList fvs) + used_tycons = map getDataConTyCon used_conids + used_gen = filter isLocalGenTyCon used_tycons + gen_tycons = setToList (mkSet local_tycons `union` mkSet used_gen) + + result_specs = addListToFM_C (++) init_specs tycon_specs_list + + uniq_cis = map head (equivClasses cmpCI (bagToList import_cis)) + cis_list = [(id, tys) | CallInstance id tys _ _ _ <- uniq_cis] + (cis_unboxed, cis_other) = partition (isUnboxedSpecialisation . snd) cis_list + cis_warn = init_warn `unionBags` listToBag cis_other + cis_errs = init_errs `unionBags` listToBag cis_unboxed + + uniq_tycis = map head (equivClasses cmpTyConI (bagToList import_tycis)) + tycis_unboxed = [(con, tys) | TyConInstance con tys <- uniq_tycis] + tycis_errs = init_tyerrs `unionBags` listToBag tycis_unboxed + + no_errs = isEmptyBag cis_errs && isEmptyBag tycis_errs + && (not (sw_chker SpecialiseImports) || isEmptyBag cis_warn) + in + (final_binds, + SpecData True no_errs local_tycons gen_tycons result_specs + cis_errs cis_warn tycis_errs) + +specProgram sw_chker uniqs binds (SpecData True _ _ _ _ _ _ _) + = panic "Specialise:specProgram: specialiser called more than once" + +-- It may be possible safely to call the specialiser more than once, +-- but I am not sure there is any benefit in doing so (Patrick) + +-- ToDo: What about unfoldings performed after specialisation ??? +\end{code} + +%************************************************************************ +%* * +\subsection[specTyConsAndScope]{Specialising data constructors within tycons} +%* * +%************************************************************************ + +In the specialiser we just collect up the specialisations which will +be required. We don't create the specialised constructors in +Core. These are only introduced when we convert to StgSyn. + +ToDo: Perhaps this should be done in CoreToStg to ensure no inconsistencies! + +\begin{code} +specTyConsAndScope :: SpecM ([PlainCoreBinding], UsageDetails) + -> SpecM ([PlainCoreBinding], [(TyCon,[[Maybe UniType]])], UsageDetails) + +specTyConsAndScope scopeM + = scopeM `thenSM` \ (binds, scope_uds) -> + getSwitchCheckerSM `thenSM` \ sw_chkr -> + let + (tycons_cis, gotci_scope_uds) + = getLocalSpecTyConIs (sw_chkr CompilingPrelude) scope_uds + + tycon_specs_list = collectTyConSpecs tycons_cis + in + (if sw_chkr SpecialiseTrace && not (null tycon_specs_list) then + pprTrace "Specialising TyCons:\n" + (ppAboves [ if not (null specs) then + ppHang (ppCat [(ppr PprDebug tycon), ppStr "at types"]) + 4 (ppAboves (map pp_specs specs)) + else ppNil + | (tycon, specs) <- tycon_specs_list]) + else id) ( + returnSM (binds, tycon_specs_list, gotci_scope_uds) + ) + where + collectTyConSpecs [] + = [] + collectTyConSpecs tycons_cis@(TyConInstance tycon _ : _) + = (tycon, tycon_specs) : collectTyConSpecs other_tycons_cis + where + (tycon_cis, other_tycons_cis) = partition (isTyConIofThisTyCon tycon) tycons_cis + uniq_cis = map head (equivClasses cmpTyConI_tys tycon_cis) + tycon_specs = [spec_tys | TyConInstance _ spec_tys <- uniq_cis] + + pp_specs specs = ppInterleave ppNil [pprMaybeTy PprDebug ty | ty <- specs] + + +{- UNUSED: create specialised constructors in Core + +NB: this code may have some bitrot (Andy & Will 95/06) + +specTyConsAndScope spec_tycons scopeM + = fixSM (\ ~(_, _, _, rec_spec_infos) -> + bindConIds cons_tospec rec_spec_infos ( + scopeM `thenSM` \ (binds, scope_uds) -> + let + (tycons_cis, gotci_scope_uds) + = getLocalSpecTyConIs (sw_chkr CompilingPrelude) scope_uds + in + mapAndUnzipSM (inst_tycon tycons_cis) spec_tycons + `thenSM` \ (tycon_specs_list, spec_infoss) -> + returnSM (binds, tycon_specs_list, gotci_scope_uds, concat spec_infoss) + ) + + ) `thenSM` \ (binds, tycon_specs_list, final_uds, spec_infos) -> + returnSM (binds, tycon_specs_list, final_uds) + + where + conss_tospec = map getTyConDataCons spec_tycons + cons_tospec = concat conss_tospec + + inst_tycon tycons_cis tycon + = mapSM mk_con_specs (getTyConDataCons tycon) `thenSM` \ spec_infos -> + getSwitchCheckerSM `thenSM` \ sw_chkr -> + (if sw_chkr SpecialiseTrace && not (null tycon_cis) then + pprTrace "Specialising:" + (ppHang (ppCat [ppr PprDebug tycon, ppStr "at types"]) + 4 (ppAboves (map pp_inst uniq_cis))) + else id) ( + returnSM ((tycon, tycon_specs), spec_infos) + ) + where + tycon_cis = filter (isTyConIofThisTyCon tycon) tycons_cis + uniq_cis = map head (equivClasses cmpTyConI_tys tycon_cis) + + tycon_specs = [spec_tys | TyConInstance _ spec_tys <- uniq_cis] + + mk_con_specs con_id + = mapSM (mk_con_spec con_id) uniq_cis + mk_con_spec con_id (TyConInstance _ spec_tys) + = newSpecIds [con_id] spec_tys 0 copy_arity_info_and `thenSM` \ [spec_id] -> + returnSM (SpecInfo spec_tys 0 spec_id) + + copy_arity_info old new = addIdArity new (getDataConArity old) + + pp_inst (TyConInstance _ spec_tys) + = ppInterleave ppNil [pprMaybeTy PprDebug ty | ty <- spec_tys] +-} +\end{code} + +%************************************************************************ +%* * +\subsection[specTopBinds]{Specialising top-level bindings} +%* * +%************************************************************************ + +\begin{code} +specTopBinds :: [PlainCoreBinding] + -> SpecM ([PlainCoreBinding], UsageDetails) + +specTopBinds binds + = spec_top_binds binds `thenSM` \ (binds, UsageDetails cis tycis dbind_details fvs) -> + let + -- Add bindings for floated dbinds and collect fvs + -- In actual fact many of these bindings are dead code since dict + -- arguments are dropped when a specialised call is created + -- The simplifier should be able to cope ... + + (dbinders_s, dbinds, dfvs_s) + = unzip3 [(dbinders, dbind, dfvs) | DictBindDetails dbinders dbind dfvs _ <- dbind_details] + + full_fvs = fvs `unionUniqSets` unionManyUniqSets dfvs_s + fvs_outer = full_fvs `minusUniqSet` (mkUniqSet (concat dbinders_s)) + in + returnSM (dbinds ++ binds, UsageDetails cis tycis [] fvs_outer) + + where + spec_top_binds (first_bind:rest_binds) + = specBindAndScope True {- top level -} first_bind ( + spec_top_binds rest_binds `thenSM` \ (rest_binds, rest_uds) -> + returnSM (ItsABinds rest_binds, rest_uds) + ) `thenSM` \ (first_binds, ItsABinds rest_binds, all_uds) -> + returnSM (first_binds ++ rest_binds, all_uds) + + spec_top_binds [] + = returnSM ([], emptyUDs) +\end{code} + +%************************************************************************ +%* * +\subsection[specExpr]{Specialising expressions} +%* * +%************************************************************************ + +\begin{code} +specExpr :: PlainCoreExpr + -> [PlainCoreArg] -- The arguments: + -- TypeArgs are speced + -- ValArgs are unprocessed + -> SpecM (PlainCoreExpr, -- Result expression with specialised versions installed + UsageDetails) -- Details of usage of enclosing binders in the result + -- expression. + +specExpr (CoVar v) args + = lookupId v `thenSM` \ vlookup -> + case vlookup of + Lifted vl vu + -> -- Binding has been lifted, need to extract un-lifted value + -- NB: a function binding will never be lifted => args always null + -- i.e. no call instance required or call to be constructed + ASSERT (null args) + returnSM (bindUnlift vl vu (CoVar vu), singleFvUDs (CoVarAtom vl)) + + NoLift vatom@(CoVarAtom new_v) + -> mapSM specArg args `thenSM` \ arg_info -> + mkCallInstance v new_v arg_info `thenSM` \ uds -> + mkCall new_v arg_info `thenSM` \ call -> + returnSM (call, uds) + +specExpr expr@(CoLit _) null_args + = ASSERT (null null_args) + returnSM (expr, emptyUDs) + +specExpr (CoCon con tys args) null_args + = ASSERT (null null_args) + mapSM specTy tys `thenSM` \ tys -> + mapAndUnzip3SM specAtom args `thenSM` \ (args, args_uds_s, unlifts) -> + mkTyConInstance con tys `thenSM` \ con_uds -> + returnSM (applyBindUnlifts unlifts (CoCon con tys args), + unionUDList args_uds_s `unionUDs` con_uds) + +{- UNUSED: create specialised constructors in CoCon +specExpr (CoCon con tys args) null_args + = ASSERT (null null_args) + mapSM specTy tys `thenSM` \ tys -> + mapAndUnzipSM specAtom args `thenSM` \ (args, args_uds_s) -> + mkTyConInstance con tys `thenSM` \ con_con -> + lookupId con `thenSM` \ con -> + mkConstrCall con tys `thenSM` \ ~(spec_con, spec_tys) -> + returnSM (CoCon spec_con spec_tys args, + unionUDList args_uds_s `unionUDs` con_uds) +-} + +specExpr (CoPrim op@(CCallOp str is_asm may_gc arg_tys res_ty) tys args) null_args + = ASSERT (null null_args) + ASSERT (null tys) + mapSM specTy arg_tys `thenSM` \ arg_tys -> + specTy res_ty `thenSM` \ res_ty -> + mapAndUnzip3SM specAtom args `thenSM` \ (args, args_uds_s, unlifts) -> + returnSM (applyBindUnlifts unlifts (CoPrim (CCallOp str is_asm may_gc arg_tys res_ty) tys args), + unionUDList args_uds_s) + +specExpr (CoPrim prim tys args) null_args + = ASSERT (null null_args) + mapSM specTy tys `thenSM` \ tys -> + mapAndUnzip3SM specAtom args `thenSM` \ (args, args_uds_s, unlifts) -> + -- specPrimOp prim tys `thenSM` \ (prim, tys, prim_uds) -> + returnSM (applyBindUnlifts unlifts (CoPrim prim tys args), + unionUDList args_uds_s {-`unionUDs` prim_uds-} ) + +{- ToDo: specPrimOp + +specPrimOp :: PrimOp + -> [UniType] + -> SpecM (PrimOp, + [UniType], + UsageDetails) + +-- Checks that PrimOp can handle (possibly unboxed) tys passed +-- and/or chooses PrimOp specialised to any unboxed tys +-- Errors are dealt with by returning a PrimOp call instance +-- which will result in a cis_errs message + +-- ToDo: Deal with checkSpecTyApp for CoPrim in CoreLint +-} + + +specExpr (CoApp fun arg) args + = -- Arg is passed on unprocessed + specExpr fun (ValArg arg : args) `thenSM` \ (expr,uds) -> + returnSM (expr, uds) + +specExpr (CoTyApp fun ty) args + = -- Spec the tyarg and pass it on + specTy ty `thenSM` \ ty -> + specExpr fun (TypeArg ty : args) + +specExpr (CoLam bound_ids body) args + = specLam bound_ids body args + +specExpr (CoTyLam tyvar body) (TypeArg ty : args) + = -- Type lambda with argument; argument already spec'd + bindTyVar tyvar ty ( + specExpr body args + ) + +specExpr (CoTyLam tyvar body) [] + = -- No arguments + cloneTyVarSM tyvar `thenSM` \ new_tyvar -> + bindTyVar tyvar (mkTyVarTy new_tyvar) ( + specExpr body [] `thenSM` \ (body, body_uds) -> + let + (binds_here, final_uds) = dumpUDs body_uds [] [new_tyvar] + in + returnSM (CoTyLam new_tyvar (mkCoLetsNoUnboxed binds_here body), final_uds) + ) + +specExpr (CoCase scrutinee alts) args + = specExpr scrutinee [] `thenSM` \ (scrutinee, scrut_uds) -> + specAlts alts scrutinee_type args `thenSM` \ (alts, alts_uds) -> + returnSM (CoCase scrutinee alts, scrut_uds `unionUDs` alts_uds) + where + scrutinee_type = typeOfCoreExpr scrutinee + + +specExpr (CoLet bind body) args + = specBindAndScope False {- not top level -} bind ( + specExpr body args `thenSM` \ (body, body_uds) -> + returnSM (ItsAnExpr body, body_uds) + ) `thenSM` \ (binds, ItsAnExpr body, all_uds) -> + returnSM (mkCoLetsNoUnboxed binds body, all_uds) + +specExpr (CoSCC cc expr) args + = specExpr expr [] `thenSM` \ (expr, expr_uds) -> + mapAndUnzip3SM specArg args `thenSM` \ (args, args_uds_s, unlifts) -> + let + scc_expr + = if squashableDictishCcExpr cc expr -- can toss the _scc_ + then expr + else CoSCC cc expr + in + returnSM (applyBindUnlifts unlifts (applyToArgs scc_expr args), + unionUDList args_uds_s `unionUDs` expr_uds) + +-- ToDo:DPH: add stuff here! +\end{code} + +%************************************************************************ +%* * +\subsubsection{Specialising a lambda} +%* * +%************************************************************************ + +\begin{code} +specLam :: [Id] -> PlainCoreExpr -> [PlainCoreArg] + -> SpecM (PlainCoreExpr, UsageDetails) + +specLam [] body args + = -- All lambdas saturated + specExpr body args + +specLam (binder:binders) body (ValArg arg : args) + = -- Lambda with an unprocessed argument + lookup_arg arg `thenSM` \ arg -> + bindId binder arg ( + specLam binders body args + ) + where + lookup_arg (CoLitAtom l) = returnSM (NoLift (CoLitAtom l)) + lookup_arg (CoVarAtom v) = lookupId v + +specLam bound_ids body [] + = -- Lambda with no arguments + specLambdaOrCaseBody bound_ids body [] `thenSM` \ (bound_ids, body, uds) -> + returnSM (CoLam bound_ids body, uds) +\end{code} + +\begin{code} +specLambdaOrCaseBody :: [Id] -- The binders + -> PlainCoreExpr -- The body + -> [PlainCoreArg] -- Its args + -> SpecM ([Id], -- New binders + PlainCoreExpr, -- New body + UsageDetails) + +specLambdaOrCaseBody bound_ids body args + = cloneLambdaOrCaseBinders bound_ids `thenSM` \ (new_ids, clone_infos) -> + bindIds bound_ids clone_infos ( + + specExpr body args `thenSM` \ (body, body_uds) -> + + let + -- Dump any dictionary bindings (and call instances) + -- from the scope which mention things bound here + (binds_here, final_uds) = dumpUDs body_uds new_ids [] + in + returnSM (new_ids, mkCoLetsNoUnboxed binds_here body, final_uds) + ) + +-- ToDo: Opportunity here to common-up dictionaries with same type, +-- thus avoiding recomputation. +\end{code} + +A variable bound in a lambda or case is normally monomorphic so no +specialised versions will be required. This is just as well since we +do not know what code to specialise! + +Unfortunately this is not always the case. For example a class Foo +with polymorphic methods gives rise to a dictionary with polymorphic +components as follows: + +\begin{verbatim} +class Foo a where + op1 :: a -> b -> a + op2 :: a -> c -> a + +instance Foo Int where + op1 = op1Int + op2 = op2Int + +... op1 1 3# ... + +==> + +d.Foo.Int :: ( \/b . Int -> b -> Int, \/c . Int -> c -> Int ) +d.Foo.Int = (op1_Int, op2_Int) + +op1 = /\ a b -> \ dFoo -> case dFoo of (meth1, _) -> meth1 b + +... op1 {Int Int#} d.Foo.Int 1 3# ... +\end{verbatim} + +N.B. The type of the dictionary is not Hindley Milner! + +Now we must specialise op1 at {* Int#} which requires a version of +meth1 at {Int#}. But since meth1 was extracted from a dictionary we do +not have access to its code to create the specialised version. + + +If we specialise on overloaded types as well we specialise op1 at +{Int Int#} d.Foo.Int: + +op1_Int_Int# = case d.Foo.Int of (meth1, _) -> meth1 {Int#} + +Though this is still invalid, after further simplification we get: + +op1_Int_Int# = opInt1 {Int#} + +Another round of specialisation will result in the specialised +version of op1Int being called directly. + +For now we PANIC if a polymorphic lambda/case bound variable is found +in a call instance with an unboxed type. Other call instances, arising +from overloaded type arguments, are discarded since the unspecialised +version extracted from the method can be called as normal. + +ToDo: Implement and test second round of specialisation. + + +%************************************************************************ +%* * +\subsubsection{Specialising case alternatives} +%* * +%************************************************************************ + + +\begin{code} +specAlts (CoAlgAlts alts deflt) scrutinee_ty args + = mapSM specTy ty_args `thenSM` \ ty_args -> + mapAndUnzipSM (specAlgAlt ty_args) alts `thenSM` \ (alts, alts_uds_s) -> + specDeflt deflt args `thenSM` \ (deflt, deflt_uds) -> + returnSM (CoAlgAlts alts deflt, + unionUDList alts_uds_s `unionUDs` deflt_uds) + + where + -- We use ty_args of scrutinee type to identify specialisation of alternatives + (_, ty_args, _) = getUniDataTyCon scrutinee_ty + + specAlgAlt ty_args (con,binders,rhs) + = specLambdaOrCaseBody binders rhs args `thenSM` \ (binders, rhs, rhs_uds) -> + mkTyConInstance con ty_args `thenSM` \ con_uds -> + returnSM ((con,binders,rhs), rhs_uds `unionUDs` con_uds) + +{- UNUSED: creating specialised constructors in case alts + specAlgAlt ty_args (con,binders,rhs) + = specLambdaOrCaseBody binders rhs args `thenSM` \ (binders, rhs, rhs_uds) -> + mkTyConInstance con ty_args `thenSM` \ con_uds -> + lookupId con `thenSM` \ con -> + mkConstrCall con ty_args `thenSM` \ ~(spec_con, _) -> + returnSM ((spec_con,binders,rhs), rhs_uds `unionUDs` con_uds) +-} + +specAlts (CoPrimAlts alts deflt) scrutinee_ty args + = mapAndUnzipSM specPrimAlt alts `thenSM` \ (alts, alts_uds_s) -> + specDeflt deflt args `thenSM` \ (deflt, deflt_uds) -> + returnSM (CoPrimAlts alts deflt, + unionUDList alts_uds_s `unionUDs` deflt_uds) + where + specPrimAlt (lit,rhs) = specExpr rhs args `thenSM` \ (rhs, uds) -> + returnSM ((lit,rhs), uds) + + +specDeflt CoNoDefault args = returnSM (CoNoDefault, emptyUDs) +specDeflt (CoBindDefault binder rhs) args + = specLambdaOrCaseBody [binder] rhs args `thenSM` \ ([binder], rhs, uds) -> + returnSM (CoBindDefault binder rhs, uds) +\end{code} + + +%************************************************************************ +%* * +\subsubsection{Specialising an atom} +%* * +%************************************************************************ + +\begin{code} +specAtom :: PlainCoreAtom -> SpecM (PlainCoreAtom, UsageDetails, + PlainCoreExpr -> PlainCoreExpr) + +specAtom (CoLitAtom lit) + = returnSM (CoLitAtom lit, emptyUDs, id) + +specAtom (CoVarAtom v) + = lookupId v `thenSM` \ vlookup -> + case vlookup of + Lifted vl vu + -> returnSM (CoVarAtom vu, singleFvUDs (CoVarAtom vl), bindUnlift vl vu) + + NoLift vatom + -> returnSM (vatom, singleFvUDs vatom, id) + + +specArg :: PlainCoreArg -> SpecM (PlainCoreArg, UsageDetails, + PlainCoreExpr -> PlainCoreExpr) + +specArg (ValArg arg) -- unprocessed; spec the atom + = specAtom arg `thenSM` \ (arg, uds, unlift) -> + returnSM (ValArg arg, uds, unlift) + +specArg (TypeArg ty) -- already speced; no action + = returnSM (TypeArg ty, emptyUDs, id) +\end{code} + + +%************************************************************************ +%* * +\subsubsection{Specialising bindings} +%* * +%************************************************************************ + +A classic case of when having a polymorphic recursive function would help! + +\begin{code} +data BindsOrExpr = ItsABinds [PlainCoreBinding] + | ItsAnExpr PlainCoreExpr +\end{code} + +\begin{code} +specBindAndScope + :: Bool -- True <=> a top level group + -> PlainCoreBinding -- As yet unprocessed + -> SpecM (BindsOrExpr, UsageDetails) -- Something to do the scope of the bindings + -> SpecM ([PlainCoreBinding], -- Processed + BindsOrExpr, -- Combined result + UsageDetails) -- Usage details of the whole lot + +specBindAndScope is_top_level_group bind scopeM + = cloneLetrecBinders binders `thenSM` \ (new_binders, clone_infos) -> + + -- Two cases now: either this is a bunch of dictionaries, in + -- which case we float them; or its a bunch of other values, + -- in which case we see if they correspond to any + -- call-instances we have in hand. + + if all (\id -> isDictTy (getIdUniType id) || isConstMethodId id) binders then + -- Ha! A group of dictionary bindings, or constant methods. + -- The reason for the latter is interesting. Consider + -- + -- dfun.Eq.Foo = /\a \ d -> ... + -- + -- constmeth1 = ... + -- constmeth2 = ... + -- dict = (constmeth1,constmeth2) + -- + -- ...(dfun.Eq.Foo dict)... + -- + -- Now, the defn of dict can't float above the constant-method + -- decls, so the call-instance for dfun.Eq.Foo will be dropped. + -- + -- Solution: float the constant methods in the same way as dictionaries + -- + -- The other interesting bit is the test for dictionary-hood. + -- Constant dictionaries, like dict above, are sometimes built + -- as zero-arity dfuns, so isDictId alone won't work. + + bindIds binders clone_infos ( + + -- Process the dictionary bindings themselves + specBind new_binders bind `thenSM` \ (bind, rhs_uds) -> + + -- Process their scope + scopeM `thenSM` \ (thing, scope_uds) -> + let + -- Add the bindings to the current stuff + final_uds = addDictBinds new_binders bind rhs_uds scope_uds + in + returnSM ([], thing, final_uds) + ) + else + -- Ho! A group of ordinary (non-dict) bindings + fixSM (\ ~(_, _, _, rec_spec_infos) -> + + bindSpecIds binders clone_infos rec_spec_infos ( + -- It's ok to have new binders in scope in + -- non-recursive decls too, cos name shadowing is gone by now + + -- Do the scope of the bindings + scopeM `thenSM` \ (thing, scope_uds) -> + let + (call_insts_these_binders, gotci_scope_uds) = getCIs new_binders scope_uds + in + + -- Do the bindings themselves + specBind new_binders bind `thenSM` \ (spec_bind, spec_uds) -> + + -- Create any necessary instances + instBind new_binders bind call_insts_these_binders + `thenSM` \ (inst_binds, inst_uds, spec_infos) -> + + let + -- Dump any dictionary bindings from the scope + -- which mention things bound here + (dict_binds, final_scope_uds) = dumpUDs gotci_scope_uds new_binders [] + -- The spec_ids can't appear anywhere in uds, because they only + -- appear in SpecInfos. + + -- Build final binding group + -- see note below about dependecies + final_binds = [spec_bind, + CoRec (pairsFromCoreBinds (inst_binds ++ dict_binds)) + ] + + in + -- Combine the results together + returnSM (final_binds, + thing, + spec_uds `unionUDs` final_scope_uds `unionUDs` inst_uds, + -- inst_uds comes last, because there may be dict bindings + -- floating outward in final_scope_uds which are mentioned + -- in the call-instances, and hence in spec_uds. + -- This ordering makes sure that the precedence order + -- among the dict bindings finally floated out is maintained. + spec_infos) + ) + ) `thenSM` \ (binds, thing, final_uds, spec_infos) -> + returnSM (binds, thing, final_uds) + where + binders = bindersOf bind +\end{code} + +We place the spec_binds and dict_binds in a CoRec as there may be some +nasty dependencies. These don't actually require a CoRec, but its the +simplest solution. (The alternative would require some tricky dependency +analysis.) We leave it to the real dependency analyser to sort it all +out during a subsequent simplification pass. + +Where do these dependencies arise? Consider this case: + + data Foo a = ... + + {- instance Eq a => Eq (Foo a) where ... -} + dfun.Eq.(Foo *) d.eq.a = <wurble> + + d2 = dfun.Eq.(Foo *) Char# d.Eq.Char# + d1 = dfun.Eq.(Foo *) (Foo Char#) d2 + +Now, when specialising we must write the Char# instance of dfun.Eq.(Foo *) before +that for the (Foo Char#) instance: + + dfun.Eq.(Foo *) d.eq.a = <wurble> + + dfun.Eq.(Foo *)@Char# = <wurble>[d.Eq.Char#/d.eq.a] + d2 = dfun.Eq.(Foo *)@Char# + + dfun.Eq.(Foo *)@(Foo Char#) = <wurble>[d2/d.eq.a] + d1 = dfun.Eq.(Foo *)@(Foo Char#) + +The definition of dfun.Eq.(Foo *)@(Foo Char#) uses d2!!! So it must +come after the definition of dfun.Eq.(Foo *)@Char#. +AAARGH! + + + +\begin{code} +specBind :: [Id] -> PlainCoreBinding -> SpecM (PlainCoreBinding, UsageDetails) + -- The UsageDetails returned has already had stuff to do with this group + -- of binders deleted; that's why new_binders is passed in. +specBind new_binders (CoNonRec binder rhs) + = specOneBinding new_binders (binder,rhs) `thenSM` \ ((binder,rhs), rhs_uds) -> + returnSM (CoNonRec binder rhs, rhs_uds) + +specBind new_binders (CoRec pairs) + = mapAndUnzipSM (specOneBinding new_binders) pairs `thenSM` \ (pairs, rhs_uds_s) -> + returnSM (CoRec pairs, unionUDList rhs_uds_s) + + +specOneBinding :: [Id] -> (Id,PlainCoreExpr) -> SpecM ((Id,PlainCoreExpr), UsageDetails) + +specOneBinding new_binders (binder, rhs) + = lookupId binder `thenSM` \ blookup -> + specExpr rhs [] `thenSM` \ (rhs, rhs_uds) -> + let + specid_maybe_maybe = isSpecPragmaId_maybe binder + is_specid = maybeToBool specid_maybe_maybe + Just specinfo_maybe = specid_maybe_maybe + specid_with_info = maybeToBool specinfo_maybe + Just spec_info = specinfo_maybe + + pragma_uds + = if is_specid && specid_with_info then + -- Have a SpecInfo stored in a SpecPragmaId binder + -- This contains the SpecInfo for a specialisation pragma + -- with an explicit SpecId specified + -- We remove any cis for orig_id (there should only be one) + -- and add the explicit ci to the usage details + let + (SpecInfo spec_tys _ spec_id) = spec_info + Just (orig_id, _) = isSpecId_maybe spec_id + in + ASSERT(toplevelishId orig_id) -- must not be cloned! + explicitCI orig_id spec_tys spec_info + else + emptyUDs + + (binds_here, final_uds) = dumpUDs rhs_uds new_binders [] + in + case blookup of + Lifted lift_binder unlift_binder + -> -- We may need to record an unboxed instance of + -- the _Lift data type in the usage details + mkTyConInstance liftDataCon [getIdUniType unlift_binder] + `thenSM` \ lift_uds -> + returnSM ((lift_binder, + mkCoLetsNoUnboxed binds_here (liftExpr unlift_binder rhs)), + final_uds `unionUDs` pragma_uds `unionUDs` lift_uds) + + NoLift (CoVarAtom binder) + -> returnSM ((binder, mkCoLetsNoUnboxed binds_here rhs), + final_uds `unionUDs` pragma_uds) +\end{code} + + +%************************************************************************ +%* * +\subsection{@instBind@} +%* * +%************************************************************************ + +\begin{code} +instBind main_ids@(first_binder:other_binders) bind call_insts_for_main_ids + | all same_overloading other_binders + = let + -- Collect up identical call instances + equiv_classes = equivClasses cmpCI_tys call_insts_for_main_ids + in + -- For each equivalence class, build an instance + mapAndUnzip3SM do_this_class equiv_classes `thenSM` \ (inst_binds, inst_uds_s, spec_infos) -> + + -- Add in the remaining UDs + returnSM (catMaybes inst_binds, + unionUDList inst_uds_s, + spec_infos + ) + + | otherwise -- Incompatible overloadings; see below by same_overloading + = (if null (filter isUnboxedCI call_insts_for_main_ids) + then (\ x y -> y) -- pprTrace "dumpCIs: not same overloading ... \n" + else pprTrace "dumpCIs: not same overloading ... WITH UNBOXED TYPES!\n") + (ppHang (ppBesides [ppStr "{", ppr PprDebug main_ids, ppStr "}"]) + 4 (ppAboves (map pprCI call_insts_for_main_ids))) + (returnSM ([], emptyUDs, [])) + + where + (tyvar_tmpls, class_tyvar_pairs) = getIdOverloading first_binder + tyvar_tmpl_tys = map mkTyVarTemplateTy tyvar_tmpls + + no_of_tyvars = length tyvar_tmpls + no_of_dicts = length class_tyvar_pairs + + do_this_class equiv_cis + | not (null explicit_cis) + = if (length main_ids > 1 || length explicit_cis > 1) then + -- ToDo: If this situation arose we would need to go through + -- checking cis for each main_id and only creating an + -- instantiation if we had no explicit_cis for that main_id + pprPanic "Specialise:instBind:explicit call instances\n" + (ppAboves [ppCat [ppStr "{", ppr PprDebug main_ids, ppStr "}"], + ppAboves (map pprCI equiv_cis)]) + else + getSwitchCheckerSM `thenSM` \ sw_chkr -> + (if sw_chkr SpecialiseTrace then + let + SpecInfo spec_tys _ spec_id = explicit_spec_info + in + pprTrace "Specialising:" + (ppHang (ppBesides [ppStr "{", ppr PprDebug main_ids, ppStr "}"]) + 4 (ppAboves [ + ppCat (ppStr "at types:" : [pprMaybeTy PprDebug ty | ty <- spec_tys]), + ppCat [ppStr "spec ids:", ppr PprDebug [spec_id], ppStr "(explicit)"]])) + else id) ( + + returnSM (Nothing, emptyUDs, [explicit_spec_info]) + ) + | otherwise + = mkOneInst (head equiv_cis) no_of_dicts main_ids bind + where + explicit_cis = filter isExplicitCI equiv_cis + [CallInstance _ _ _ _ (Just explicit_spec_info)] = explicit_cis + + + -- same_overloading tests whether the types of all the binders + -- are "compatible"; ie have the same type and dictionary abstractions + -- Almost always this is the case, because a recursive group is abstracted + -- all together. But, it can happen that it ain't the case, because of + -- code generated from instance decls: + -- + -- rec + -- dfun.Foo.Int :: (forall a. a -> Int, Int) + -- dfun.Foo.Int = (const.op1.Int, const.op2.Int) + -- + -- const.op1.Int :: forall a. a -> Int + -- const.op1.Int a = defm.Foo.op1 Int a dfun.Foo.Int + -- + -- const.op2.Int :: Int + -- const.op2.Int = 3 + -- + -- Note that the first two defns have different polymorphism, but they are + -- mutually recursive! + + same_overloading :: Id -> Bool + same_overloading id + = no_of_tyvars == length this_id_tyvars -- Same no of tyvars + && + no_of_dicts == length this_id_class_tyvar_pairs -- Same no of vdicts + && + and (zipWith same_ov class_tyvar_pairs this_id_class_tyvar_pairs) -- Same overloading + where + (this_id_tyvars, this_id_class_tyvar_pairs) = getIdOverloading id + tyvar_pairs = this_id_tyvars `zip` tyvar_tmpls + + same_ov (clas1,tyvar1) (clas2,tyvar2) + = clas1 == clas2 && + tyvar1 == assoc "same_overloading" tyvar_pairs tyvar2 +\end{code} + +OK, so we have: + - a call instance eg f [t1,t2,t3] [d1,d2] + - the rhs of the function eg orig_rhs + - a constraint vector, saying which of eg [T,F,T] + the functions type args are constrained + (ie overloaded) + +We return a new definition + + f@t1//t3 = /\a -> orig_rhs t1 a t3 d1 d2 + +The SpecInfo for f will be (the "2" indicates 2 dictionaries to eat) + + SpecInfo [Just t1, Nothing, Just t3] 2 f@t1//t3 + +Based on this SpecInfo, a call instance of f + + ...(f t1 t2 t3 d1 d2)... + +should get replaced by + + ...(f@t1//t3 t2)... + +(But that is the business of @mkCall@.) + +\begin{code} +mkOneInst :: CallInstance + -> Int -- No of dicts to specialise + -> [Id] -- New binders + -> PlainCoreBinding -- Unprocessed + -> SpecM (Maybe PlainCoreBinding, -- Instantiated version of input + UsageDetails, + [SpecInfo] -- One for each id in the original binding + ) + +mkOneInst (CallInstance _ spec_tys dict_args _ _) no_of_dicts_to_specialise main_ids orig_bind + = ASSERT (no_of_dicts_to_specialise == length dict_args) + newSpecIds main_ids spec_tys no_of_dicts_to_specialise copy_inline_info + `thenSM` \ spec_ids -> + newTyVars (length [() | Nothing <- spec_tys]) `thenSM` \ poly_tyvars -> + let + -- arg_tys is spec_tys with tyvars instead of the Nothing spec_tys + -- which correspond to unspeciailsed args + arg_tys :: [UniType] + (_,arg_tys) = mapAccumL do_the_wotsit poly_tyvars spec_tys + + args :: [PlainCoreArg] + args = map TypeArg arg_tys ++ dict_args + + (one_spec_id:_) = spec_ids + + do_bind (CoNonRec binder rhs) + = do_one_rhs rhs `thenSM` \ (rhs, rhs_uds) -> + returnSM (CoNonRec one_spec_id rhs, rhs_uds) + + do_bind (CoRec pairs) + = mapAndUnzipSM do_one_rhs [rhs | (_,rhs) <- pairs] `thenSM` \ (rhss, rhss_uds_s) -> + returnSM (CoRec (spec_ids `zip` rhss), unionUDList rhss_uds_s) + + -- Apply the specialiser to (orig_rhs t1 a t3 d1 d2) + do_one_rhs orig_rhs = specExpr orig_rhs args `thenSM` \ (inst_rhs, inst_uds) -> + let + (binds_here, final_uds) = dumpUDs inst_uds main_ids [] + -- NB: main_ids!! not spec_ids!! Why? Because the free-var + -- stuff knows nowt about spec_ids; it'll just have the + -- original polymorphic main_ids as free. Belgh + in + returnSM (mkCoLetsNoUnboxed binds_here (mkCoTyLam poly_tyvars inst_rhs), + final_uds) + in + getSwitchCheckerSM `thenSM` \ sw_chkr -> + (if sw_chkr SpecialiseTrace then + pprTrace "Specialising:" + (ppHang (ppBesides [ppStr "{", ppr PprDebug main_ids, ppStr "}"]) + 4 (ppAboves [ + ppBesides [ppStr "with args: ", ppInterleave ppNil (map pp_arg args)], + ppBesides [ppStr "spec ids: ", ppr PprDebug spec_ids]])) + else id) ( + + do_bind orig_bind `thenSM` \ (inst_bind, inst_uds) -> + + returnSM (Just inst_bind, + inst_uds, + [SpecInfo spec_tys no_of_dicts_to_specialise spec_id | spec_id <- spec_ids] + ) + ) + where + -- debugging + pp_arg (ValArg a) = ppBesides [ppLparen, ppStr "ValArg ", ppr PprDebug a, ppRparen] + pp_arg (TypeArg t) = ppBesides [ppLparen, ppStr "TypeArg ", ppr PprDebug t, ppRparen] + + do_the_wotsit (tyvar:tyvars) Nothing = (tyvars, mkTyVarTy tyvar) + do_the_wotsit tyvars (Just ty) = (tyvars, ty) + + copy_inline_info new_id old_uf_info = addIdUnfolding new_id old_uf_info +\end{code} + +%************************************************************************ +%* * +\subsection[Misc]{Miscellaneous junk} +%* * +%************************************************************************ + +@getIdOverloading@ grabs the type of an Id, and returns a +list of its polymorphic variables, and the initial segment of +its ThetaType, in which the classes constrain only type variables. +For example, if the Id's type is + + forall a,b,c. Eq a -> Ord [a] -> tau + +we'll return + + ([a,b,c], [(Eq,a)]) + +This seems curious at first. For a start, the type above looks odd, +because we usually only have dictionary args whose types are of +the form (C a) where a is a type variable. But this doesn't hold for +the functions arising from instance decls, which sometimes get +arguements with types of form (C (T a)) for some type constructor T. + +Should we specialise wrt this compound-type dictionary? This is +a heuristic judgement, as indeed is the fact that we specialise wrt +only dictionaries. We choose *not* to specialise wrt compound dictionaries +because at the moment the only place they show up is in instance decls, +where they are simply plugged into a returned dictionary. So nothing is +gained by specialising wrt them. + +\begin{code} +getIdOverloading :: Id + -> ([TyVarTemplate], [(Class,TyVarTemplate)]) +getIdOverloading id + = (tyvars, tyvar_part_of theta) + where + (tyvars, theta, _) = splitType (getIdUniType id) + + tyvar_part_of [] = [] + tyvar_part_of ((clas,ty) : theta) = case getTyVarTemplateMaybe ty of + Nothing -> [] + Just tyvar -> (clas, tyvar) : tyvar_part_of theta +\end{code} + +\begin{code} +mkCallInstance :: Id + -> Id + -> [(PlainCoreArg, UsageDetails, PlainCoreExpr -> PlainCoreExpr)] + -> SpecM UsageDetails + +mkCallInstance old_id new_id args + = recordCallInst old_id args `thenSM` \ record_call -> + case record_call of + Nothing -- No specialisation required + -> -- pprTrace "NoSpecReqd:" + -- (ppCat [ppr PprDebug old_id, ppStr "at", ppCat (map (ppr PprDebug) args)]) + + (returnSM call_fv_uds) + + Just (True, spec_tys, dict_args, rest_args) -- Requires specialisation: spec already exists + -> -- pprTrace "SpecExists:" + -- (ppCat [ppr PprDebug old_id, ppStr " at ", ppCat (map (ppr PprDebug) args), + -- ppBesides [ppStr "(", ppCat [pprMaybeTy PprDebug ty | ty <- spec_tys], + -- ppCat [ppr PprDebug dict | dict <- dict_args], + -- ppStr ")"]]) + + (returnSM call_fv_uds) + + Just (False, spec_tys, dict_args, rest_args) -- Requires specialisation: record call-instance + -> -- pprTrace "CallInst:" + -- (ppCat [ppr PprDebug old_id, ppStr " at ", ppCat (map (ppr PprDebug) args), + -- ppBesides [ppStr "(", ppCat [pprMaybeTy PprDebug ty | ty <- spec_tys], + -- ppCat [ppr PprDebug dict | dict <- dict_args], + -- ppStr ")"]]) + + (returnSM (singleCI new_id spec_tys dict_args `unionUDs` call_fv_uds)) + where + call_fv_uds = singleFvUDs (CoVarAtom new_id) `unionUDs` unionUDList [uds | (_,uds,_) <- args] +\end{code} + +\begin{code} +recordCallInst :: Id + -> [(PlainCoreArg, UsageDetails, PlainCoreExpr -> PlainCoreExpr)] + -> SpecM (Maybe (Bool, [Maybe UniType], [PlainCoreArg], + [(PlainCoreArg, UsageDetails, PlainCoreExpr -> PlainCoreExpr)])) + +recordCallInst id [] -- No args => no call instance + = returnSM Nothing + +recordCallInst id args + | isBottomingId id -- No specialised versions for "error" and friends are req'd. + = returnSM Nothing -- This is a special case in core lint etc. + + -- No call instances for Ids associated with a Class declaration, + -- i.e. default methods, super-dict selectors and class ops. + -- We rely on the instance declarations to provide suitable specialisations. + -- These are dealt with in mkCall. + + | isDefaultMethodId id + = returnSM Nothing + + | maybeToBool (isSuperDictSelId_maybe id) + = returnSM Nothing + + | isClassOpId id + = returnSM Nothing + + -- Finally, the default case ... + + | otherwise + = getSwitchCheckerSM `thenSM` \ sw_chkr -> + let + spec_overloading = sw_chkr SpecialiseOverloaded + spec_unboxed = sw_chkr SpecialiseUnboxed + spec_all = sw_chkr SpecialiseAll + + (tyvar_tmpls, class_tyvar_pairs) = getIdOverloading id + constraint_vec = mkConstraintVector tyvar_tmpls class_tyvar_pairs + + arg_res = take_type_args tyvar_tmpls class_tyvar_pairs args + enough_args = maybeToBool arg_res + + (Just (inst_tys, dict_args, rest_args)) = arg_res + spec_tys = specialiseCallTys spec_all spec_unboxed spec_overloading + constraint_vec inst_tys + + spec_exists = maybeToBool (lookupSpecEnv + (getIdSpecialisation id) + inst_tys) + + -- We record the call instance if there is some meaningful + -- type which we want to specialise on ... + record_spec = any (not . isTyVarTy) (catMaybes spec_tys) + in + if (not enough_args) then + pprPanic "Specialise:recordCallInst: Unsaturated Type & Dict Application:\n\t" + (ppCat [ppr PprDebug id, ppr PprDebug [arg | (arg,_,_) <- args] ]) + else + if record_spec then + returnSM (Just (spec_exists, spec_tys, dict_args, rest_args)) + else + returnSM Nothing + + +take_type_args (_:tyvars) class_tyvar_pairs ((TypeArg ty,_,_):args) + = case take_type_args tyvars class_tyvar_pairs args of + Nothing -> Nothing + Just (tys, dicts, others) -> Just (ty:tys, dicts, others) +take_type_args (_:tyvars) class_tyvar_pairs [] + = Nothing +take_type_args [] class_tyvar_pairs args + = case take_dict_args class_tyvar_pairs args of + Nothing -> Nothing + Just (dicts, others) -> Just ([], dicts, others) + +take_dict_args (_:class_tyvar_pairs) ((dict@(ValArg _),_,_):args) + = case take_dict_args class_tyvar_pairs args of + Nothing -> Nothing + Just (dicts, others) -> Just (dict:dicts, others) +take_dict_args (_:class_tyvar_pairs) [] + = Nothing +take_dict_args [] args + = Just ([], args) +\end{code} + +\begin{code} +mkCall :: Id + -> [(PlainCoreArg, UsageDetails, PlainCoreExpr -> PlainCoreExpr)] + -> SpecM PlainCoreExpr + +mkCall main_id args + | isDefaultMethodId main_id + && any isUnboxedDataType ty_args + -- No specialisations for default methods + -- Unboxed calls to DefaultMethodIds should not occur + -- The method should be specified in the instance declaration + = panic "Specialise:mkCall:DefaultMethodId" + + | maybeToBool (isSuperDictSelId_maybe main_id) + && any isUnboxedDataType ty_args + -- No specialisations for super-dict selectors + -- Specialise unboxed calls to SuperDictSelIds by extracting + -- the super class dictionary directly form the super class + -- NB: This should be dead code since all uses of this dictionary should + -- have been specialised. We only do this to keep keep core-lint happy. + = let + Just (_, super_class) = isSuperDictSelId_maybe main_id + super_dict_id = case lookupClassInstAtSimpleType super_class (head ty_args) of + Nothing -> panic "Specialise:mkCall:SuperDictId" + Just id -> id + in + returnSM (CoVar super_dict_id) + + | otherwise + = case lookupSpecEnv (getIdSpecialisation main_id) ty_args of + Nothing -> checkUnspecOK main_id ty_args ( + returnSM unspec_call + ) + + Just (spec_id, tys_left, dicts_to_toss) + -> checkSpecOK main_id ty_args spec_id tys_left ( + let + args_left = toss_dicts dicts_to_toss val_args + in + + -- The resulting spec_id may be an unboxed constant method + -- eg: pi Double# d.Floating.Double# ==> pi.Double# + -- Since it is a top level id pi.Double# will have been lifted. + -- We must add code to unlift such a spec_id + + if isUnboxedDataType (getIdUniType spec_id) then + ASSERT (null tys_left && null args_left) + if isConstMethodId spec_id then + liftId spec_id `thenSM` \ (lifted_spec_id, unlifted_spec_id) -> + returnSM (bindUnlift lifted_spec_id unlifted_spec_id + (CoVar unlifted_spec_id)) + else + -- ToDo: Are there other cases where we have an unboxed spec_id ??? + pprPanic "Specialise:mkCall: unboxed spec_id ...\n" + (ppCat [ppr PprDebug main_id, + ppInterleave ppNil (map (pprParendUniType PprDebug) ty_args), + ppStr "==>", + ppr PprDebug spec_id]) + else + let + (vals_left, _, unlifts_left) = unzip3 args_left + applied_tys = mkCoTyApps (CoVar spec_id) tys_left + applied_vals = applyToArgs applied_tys vals_left + in + returnSM (applyBindUnlifts unlifts_left applied_vals) + ) + where + (tys_and_vals, _, unlifts) = unzip3 args + unspec_call = applyBindUnlifts unlifts (applyToArgs (CoVar main_id) tys_and_vals) + + + -- ty_args is the types at the front of the arg list + -- val_args is the rest of the arg-list + + (ty_args, val_args) = get args + where + get ((TypeArg ty,_,_) : args) = (ty : tys, rest) where (tys,rest) = get args + get args = ([], args) + + -- toss_dicts chucks away dict args, checking that they ain't types! + toss_dicts 0 args = args + toss_dicts n ((ValArg _,_,_) : args) = toss_dicts (n-1) args +\end{code} + +\begin{code} +checkUnspecOK :: Id -> [UniType] -> a -> a +checkUnspecOK check_id tys + = if isLocallyDefined check_id && any isUnboxedDataType tys + then pprPanic "Specialise:checkUnspecOK: unboxed instance for local id not found\n" + (ppCat [ppr PprDebug check_id, + ppInterleave ppNil (map (pprParendUniType PprDebug) tys)]) + else id + +checkSpecOK :: Id -> [UniType] -> Id -> [UniType] -> a -> a +checkSpecOK check_id tys spec_id tys_left + = if any isUnboxedDataType tys_left + then pprPanic "Specialise:checkSpecOK: unboxed type args in specialised application\n" + (ppAboves [ppCat [ppr PprDebug check_id, + ppInterleave ppNil (map (pprParendUniType PprDebug) tys)], + ppCat [ppr PprDebug spec_id, + ppInterleave ppNil (map (pprParendUniType PprDebug) tys_left)]]) + else id +\end{code} + +\begin{code} +mkTyConInstance :: Id + -> [UniType] + -> SpecM UsageDetails +mkTyConInstance con tys + = recordTyConInst con tys `thenSM` \ record_inst -> + case record_inst of + Nothing -- No TyCon instance + -> -- pprTrace "NoTyConInst:" + -- (ppCat [ppr PprDebug tycon, ppStr "at", + -- ppr PprDebug con, ppCat (map (ppr PprDebug) tys)]) + (returnSM (singleConUDs con)) + + Just spec_tys -- Record TyCon instance + -> -- pprTrace "TyConInst:" + -- (ppCat [ppr PprDebug tycon, ppStr "at", + -- ppr PprDebug con, ppCat (map (ppr PprDebug) tys), + -- ppBesides [ppStr "(", + -- ppCat [pprMaybeTy PprDebug ty | ty <- spec_tys], + -- ppStr ")"]]) + (returnSM (singleTyConI tycon spec_tys `unionUDs` singleConUDs con)) + where + tycon = getDataConTyCon con +\end{code} + +\begin{code} +recordTyConInst :: Id + -> [UniType] + -> SpecM (Maybe [Maybe UniType]) + +recordTyConInst con tys + = let + spec_tys = specialiseConstrTys tys + + do_tycon_spec = maybeToBool (firstJust spec_tys) + + spec_exists = maybeToBool (lookupSpecEnv + (getIdSpecialisation con) + tys) + in + -- pprTrace "ConSpecExists?: " + -- (ppAboves [ppStr (if spec_exists then "True" else "False"), + -- ppr PprShowAll con, ppCat (map (ppr PprDebug) tys)]) + (if (not spec_exists && do_tycon_spec) + then returnSM (Just spec_tys) + else returnSM Nothing) +\end{code} + +\begin{code} +{- UNUSED: create specilaised constructor calls in Core +mkConstrCall :: PlainCoreAtom -> [UniType] -- This constructor at these types + -> SpecM (Id, [UniType]) -- The specialised constructor and reduced types + +mkConstrCall (CoVarAtom con_id) tys + = case lookupSpecEnv (getIdSpecialisation con_id) tys of + Nothing -> checkUnspecOK con_id tys ( + returnSM (con_id, tys) + ) + Just (spec_id, tys_left, 0) + -> checkSpecOK con_id tys spec_id tys_left ( + returnSM (spec_id, tys_left) + ) +-} +\end{code} + +%************************************************************************ +%* * +\subsection[monad-Specialise]{Monad used in specialisation} +%* * +%************************************************************************ + +Monad has: + + inherited: control flags and + recordInst functions with flags cached + + environment mapping tyvars to types + environment mapping Ids to Atoms + + threaded in and out: unique supply + +\begin{code} +type SpecM result + = (GlobalSwitch -> Bool) + -> TypeEnv + -> SpecIdEnv + -> SplitUniqSupply + -> result + +initSM m sw_chker uniqs + = m sw_chker nullTyVarEnv nullIdEnv uniqs + +returnSM :: a -> SpecM a +thenSM :: SpecM a -> (a -> SpecM b) -> SpecM b +fixSM :: (a -> SpecM a) -> SpecM a + +thenSM m k sw_chkr tvenv idenv us + = case splitUniqSupply us of { (s1, s2) -> + case (m sw_chkr tvenv idenv s1) of { r -> + k r sw_chkr tvenv idenv s2 }} + +returnSM r sw_chkr tvenv idenv us = r + +fixSM k sw_chkr tvenv idenv us + = r + where + r = k r sw_chkr tvenv idenv us -- Recursive in r! +\end{code} + +\begin{code} +getSwitchCheckerSM sw_chkr tvenv idenv us = sw_chkr +\end{code} + +The only interesting bit is figuring out the type of the SpecId! + +\begin{code} +newSpecIds :: [Id] -- The id of which to make a specialised version + -> [Maybe UniType] -- Specialise to these types + -> Int -- No of dicts to specialise + -> (Id -> UnfoldingDetails -> Id) -- copies any arity info required + -> SpecM [Id] + +newSpecIds main_ids maybe_tys dicts_to_ignore copy_id_info sw_chkr tvenv idenv us + = spec_ids + where + uniqs = getSUniques (length main_ids) us + spec_id_ty id = specialiseTy (getIdUniType id) maybe_tys dicts_to_ignore + spec_ids = [ copy_id_info (mkSpecId uniq id maybe_tys (spec_id_ty id) noIdInfo) (getIdUnfolding id) + | (id,uniq) <- main_ids `zip` uniqs + ] + +newTyVars :: Int -> SpecM [TyVar] +newTyVars n sw_chkr tvenv idenv us + = map mkPolySysTyVar uniqs + where + uniqs = getSUniques n us +\end{code} + +@cloneLambdaOrCaseBinders@ and @cloneLetrecBinders@ take a bunch of +binders, and build ``clones'' for them. The clones differ from the +originals in three ways: + + (a) they have a fresh unique + (b) they have the current type environment applied to their type + (c) for letrec binders which have been specialised to unboxed values + the clone will have a lifted type + +As well as returning the list of cloned @Id@s they also return a list of +@CloneInfo@s which the original binders should be bound to. + +\begin{code} +cloneLambdaOrCaseBinders :: [Id] -- Old binders + -> SpecM ([Id], [CloneInfo]) -- New ones + +cloneLambdaOrCaseBinders old_ids sw_chkr tvenv idenv us + = let + uniqs = getSUniques (length old_ids) us + in + unzip (zipWith clone_it old_ids uniqs) + where + clone_it old_id uniq + = (new_id, NoLift (CoVarAtom new_id)) + where + new_id = applyTypeEnvToId tvenv (mkIdWithNewUniq old_id uniq) + +cloneLetrecBinders :: [Id] -- Old binders + -> SpecM ([Id], [CloneInfo]) -- New ones + +cloneLetrecBinders old_ids sw_chkr tvenv idenv us + = let + uniqs = getSUniques (2 * length old_ids) us + in + unzip (clone_them old_ids uniqs) + where + clone_them [] [] = [] + + clone_them (old_id:olds) (u1:u2:uniqs) + | toplevelishId old_id + = (old_id, + NoLift (CoVarAtom old_id)) : clone_rest + + -- Don't clone if it is a top-level thing. Why not? + -- (a) we don't want to change the uniques + -- on such things (see TopLevId in Id.lhs) + -- (b) we don't have to be paranoid about name capture + -- (c) the thing is polymorphic so no need to subst + + | otherwise + = if (isUnboxedDataType new_ty && not (isUnboxedDataType old_ty)) + then (lifted_id, + Lifted lifted_id unlifted_id) : clone_rest + else (new_id, + NoLift (CoVarAtom new_id)) : clone_rest + + where + clone_rest = clone_them olds uniqs + + new_id = applyTypeEnvToId tvenv (mkIdWithNewUniq old_id u1) + new_ty = getIdUniType new_id + old_ty = getIdUniType old_id + + (lifted_id, unlifted_id) = mkLiftedId new_id u2 + + +cloneTyVarSM :: TyVar -> SpecM TyVar + +cloneTyVarSM old_tyvar sw_chkr tvenv idenv us + = let + uniq = getSUnique us + in + cloneTyVar old_tyvar uniq -- new_tyvar + +bindId :: Id -> CloneInfo -> SpecM thing -> SpecM thing + +bindId id val specm sw_chkr tvenv idenv us + = specm sw_chkr tvenv (addOneToIdEnv idenv id val) us + +bindIds :: [Id] -> [CloneInfo] -> SpecM thing -> SpecM thing + +bindIds olds news specm sw_chkr tvenv idenv us + = specm sw_chkr tvenv (growIdEnvList idenv (zip olds news)) us + +bindSpecIds :: [Id] -- Old + -> [(CloneInfo)] -- New + -> [[SpecInfo]] -- Corresponding specialisations + -- Each sub-list corresponds to a different type, + -- and contains one spec_info for each id + -> SpecM thing + -> SpecM thing + +bindSpecIds olds clones spec_infos specm sw_chkr tvenv idenv us + = specm sw_chkr tvenv (growIdEnvList idenv old_to_clone) us + where + old_to_clone = mk_old_to_clone olds clones spec_infos + + -- The important thing here is that we are *lazy* in spec_infos + mk_old_to_clone [] [] _ = [] + mk_old_to_clone (old:rest_olds) (clone:rest_clones) spec_infos + = (old, add_spec_info clone) : + mk_old_to_clone rest_olds rest_clones spec_infos_rest + where + add_spec_info (NoLift (CoVarAtom new)) + = NoLift (CoVarAtom (new `addIdSpecialisation` + (mkSpecEnv spec_infos_this_id))) + add_spec_info lifted + = lifted -- no specialised instances for unboxed lifted values + + spec_infos_this_id = map head spec_infos + spec_infos_rest = map tail spec_infos + +{- UNUSED: creating specialised constructors +bindConIds :: [Id] -- Old constructors + -> [[SpecInfo]] -- Corresponding specialisations to be added + -- Each sub-list corresponds to one constructor, and + -- gives all its specialisations + -> SpecM thing + -> SpecM thing + +bindConIds ids spec_infos specm sw_chkr tvenv idenv us + = specm sw_chkr tvenv (growIdEnvList idenv id_to_newspec) us + where + id_to_newspec = mk_id_to_newspec ids spec_infos + + -- The important thing here is that we are *lazy* in spec_infos + mk_id_to_newspec [] _ = [] + mk_id_to_newspec (id:rest_ids) spec_infos + = (id, CoVarAtom id_with_spec) : + mk_id_to_newspec rest_ids spec_infos_rest + where + id_with_spec = id `addIdSpecialisation` (mkSpecEnv spec_infos_this_id) + spec_infos_this_id = head spec_infos + spec_infos_rest = tail spec_infos +-} + +bindTyVar :: TyVar -> UniType -> SpecM thing -> SpecM thing + +bindTyVar tyvar ty specm sw_chkr tvenv idenv us + = specm sw_chkr (growTyVarEnvList tvenv [(tyvar,ty)]) idenv us +\end{code} + +\begin{code} +lookupId :: Id -> SpecM CloneInfo + +lookupId id sw_chkr tvenv idenv us + = case lookupIdEnv idenv id of + Nothing -> NoLift (CoVarAtom id) + Just info -> info +\end{code} + +\begin{code} +specTy :: UniType -> SpecM UniType -- Apply the current type envt to the type + +specTy ty sw_chkr tvenv idenv us + = applyTypeEnvToTy tvenv ty +\end{code} + +\begin{code} +liftId :: Id -> SpecM (Id, Id) +liftId id sw_chkr tvenv idenv us + = let + uniq = getSUnique us + in + mkLiftedId id uniq +\end{code} + +In other monads these @mapSM@ things are usually called @listM@. +I think @mapSM@ is a much better name. The `2' and `3' variants are +when you want to return two or three results, and get at them +separately. It saves you having to do an (unzip stuff) right after. + +\begin{code} +mapSM :: (a -> SpecM b) -> [a] -> SpecM [b] +mapAndUnzipSM :: (a -> SpecM (b1, b2)) -> [a] -> SpecM ([b1],[b2]) +mapAndUnzip3SM :: (a -> SpecM (b1, b2, b3)) -> [a] -> SpecM ([b1],[b2],[b3]) +mapAndUnzip4SM :: (a -> SpecM (b1, b2, b3, b4)) -> [a] -> SpecM ([b1],[b2],[b3],[b4]) + +mapSM f [] = returnSM [] +mapSM f (x:xs) = f x `thenSM` \ r -> + mapSM f xs `thenSM` \ rs -> + returnSM (r:rs) + +mapAndUnzipSM f [] = returnSM ([],[]) +mapAndUnzipSM f (x:xs) = f x `thenSM` \ (r1, r2) -> + mapAndUnzipSM f xs `thenSM` \ (rs1,rs2) -> + returnSM ((r1:rs1),(r2:rs2)) + +mapAndUnzip3SM f [] = returnSM ([],[],[]) +mapAndUnzip3SM f (x:xs) = f x `thenSM` \ (r1,r2,r3) -> + mapAndUnzip3SM f xs `thenSM` \ (rs1,rs2,rs3) -> + returnSM ((r1:rs1),(r2:rs2),(r3:rs3)) + +mapAndUnzip4SM f [] = returnSM ([],[],[],[]) +mapAndUnzip4SM f (x:xs) = f x `thenSM` \ (r1,r2,r3,r4) -> + mapAndUnzip4SM f xs `thenSM` \ (rs1,rs2,rs3,rs4) -> + returnSM ((r1:rs1),(r2:rs2),(r3:rs3),(r4:rs4)) +\end{code} |