Use weak sybols to share orphan specializations of functions.
Consider something like this:
module Def where
foo :: Class c => c -> c -> T
-----------------
module Use1 where
import Def
import MyType
-- This specialization might get auto-generated by the compiler
{-# SPECIALIZE foo :: MyType -> MyType -> T #-}
baz = ... foo ...
-----------------
module Use2 where
import Def
import MyType
-- This specialization might get auto-generated by the compiler
{-# SPECIALIZE foo :: MyType -> MyType -> T #-}
bar = ... foo ...
GHC will create a specialized version for for foo
in both Use* modules.
Ideally we would want to compile the specialized version only once and share the result. But this is really hard to fit into our compilation model so might not happen any time soon.
Besides the obvious compile time issue of this approach it also comes at a code size/runtime cost as we clog up the executable and caches with two identical versions of the same code.
What we could do instead is define both versions as a weak symbol (on supported platforms at least) and then the linker would throw one of them away. Which is fine because they do the exact same thing!
Conceptually this doesn't seem hard. We just have to be careful how we pick the name for the specialization.
But if the name only depends on things mentioned in the SPECIALIZE
pragma I think we can guarantee that
each compilation will end up with one shared name. Even if the name itself might be different between compilations.
The name would need to be something similar like $spec_M.Def_foo_M.MyType_MyType_M.MyType_MyType_M.Def_T
.
That is the name of the specialized function can't depend on the place where it's instantiated. Rather it only depends
on the things it's instantiated over.
Re-doing specializations repeatedly is still pretty harmful. But this would at least eliminate the associated runtime/codesize cost.