question about GHC API on GHC plugin

Ömer Sinan Ağacan omeragacan at gmail.com
Sat Sep 5 04:16:34 UTC 2015


Hi Mike,

I'll try to hack an example for you some time tomorrow(I'm returning from ICFP
and have some long flights ahead of me).

But in the meantime, here's a working Core code, generated by GHC:

    f_rjH :: forall a_alz. Ord a_alz => a_alz -> Bool
    f_rjH =
      \ (@ a_aCH) ($dOrd_aCI :: Ord a_aCH) (eta_B1 :: a_aCH) ->
        == @ a_aCH (GHC.Classes.$p1Ord @ a_aCH $dOrd_aCI) eta_B1 eta_B1

You can clearly see here how Eq dictionary is selected from Ord
dicitonary($dOrd_aCI in the example), it's just an application of selector to
type and dictionary, that's all.

This is generated from this code:

    {-# NOINLINE f #-}
    f :: Ord a => a -> Bool
    f x = x == x

Compile it with this:

    ghc --make -fforce-recomp -O0 -ddump-simpl -ddump-to-file Main.hs
-dsuppress-idinfo

> Can anyone help me figure this out?  Is there any chance this is a bug in how
> GHC parses Core?

This seems unlikely, because GHC doesn't have a Core parser and there's no Core
parsing going on here, you're parsing your Code in the form of AST(CoreExpr,
CoreProgram etc. defined in CoreSyn.hs). Did you mean something else and am I
misunderstanding?

2015-09-04 19:39 GMT-04:00 Mike Izbicki <mike at izbicki.me>:
> I'm still having trouble creating Core code that can extract
> superclass dictionaries from a given dictionary.  I suspect the
> problem is that I don't actually understand what the Core code to do
> this is supposed to look like.  I keep getting the errors mentioned
> above when I try what I think should work.
>
> Can anyone help me figure this out?  Is there any chance this is a bug
> in how GHC parses Core?
>
> On Tue, Aug 25, 2015 at 9:24 PM, Mike Izbicki <mike at izbicki.me> wrote:
>> The purpose of the plugin is to automatically improve the numerical
>> stability of Haskell code.  It is supposed to identify numeric
>> expressions, then use Herbie (https://github.com/uwplse/herbie) to
>> generate a numerically stable version, then rewrite the numerically
>> stable version back into the code.  The first two steps were really
>> easy.  It's the last step of inserting back into the code that I'm
>> having tons of trouble with.  Core is a lot more complicated than I
>> thought :)
>>
>> I'm not sure what you mean by the CoreExpr representation?  Here's the
>> output of the pretty printer you gave:
>>  App (App (App (App (Var Id{+,r2T,ForAllTy TyVar{a} (FunTy (TyConApp
>> Num [TyVarTy TyVar{a}]) (FunTy (TyVarTy TyVar{a}) (FunTy (TyVarTy
>> TyVar{a}) (TyVarTy TyVar{a})))),VanillaId,Info{0,SpecInfo []
>> <UniqFM>,NoUnfolding,MayHaveCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [] (Dunno NoCPR)),JD
>> {strd = Lazy, absd = Use Many Used},0}}) (Type (TyVarTy TyVar{a})))
>> (App (Var Id{$p1Fractional,rh3,ForAllTy TyVar{a} (FunTy (TyConApp
>> Fractional [TyVarTy TyVar{a}]) (TyConApp Num [TyVarTy
>> TyVar{a}])),ClassOpId <Class>,Info{1,SpecInfo [BuiltinRule {ru_name =
>> "Class op $p1Fractional", ru_fn = $p1Fractional, ru_nargs = 2, ru_try
>> = <RuleFun>}] <UniqFM>,NoUnfolding,NoCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [JD {strd = Str (SProd
>> [Str HeadStr,Lazy,Lazy,Lazy]), absd = Use Many (UProd [Use Many
>> Used,Abs,Abs,Abs])}] (Dunno NoCPR)),JD {strd = Lazy, absd = Use Many
>> Used},0}}) (App (Var Id{$p1Floating,rh2,ForAllTy TyVar{a} (FunTy
>> (TyConApp Floating [TyVarTy TyVar{a}]) (TyConApp Fractional [TyVarTy
>> TyVar{a}])),ClassOpId <Class>,Info{1,SpecInfo [BuiltinRule {ru_name =
>> "Class op $p1Floating", ru_fn = $p1Floating, ru_nargs = 2, ru_try =
>> <RuleFun>}] <UniqFM>,NoUnfolding,NoCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [JD {strd = Str (SProd
>> [Str HeadStr,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy,Lazy]),
>> absd = Use Many (UProd [Use Many
>> Used,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs,Abs])}]
>> (Dunno NoCPR)),JD {strd = Lazy, absd = Use Many Used},0}}) (Var
>> Id{$dFloating,aBM,TyConApp Floating [TyVarTy
>> TyVar{a}],VanillaId,Info{0,SpecInfo []
>> <UniqFM>,NoUnfolding,MayHaveCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [] (Dunno NoCPR)),JD
>> {strd = Lazy, absd = Use Many Used},0}})))) (Var Id{x1,anU,TyVarTy
>> TyVar{a},VanillaId,Info{0,SpecInfo []
>> <UniqFM>,NoUnfolding,MayHaveCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [] (Dunno NoCPR)),JD
>> {strd = Lazy, absd = Use Many Used},0}})) (Var Id{x1,anU,TyVarTy
>> TyVar{a},VanillaId,Info{0,SpecInfo []
>> <UniqFM>,NoUnfolding,MayHaveCafRefs,NoOneShotInfo,InlinePragma
>> {inl_src = "{-# INLINE", inl_inline = EmptyInlineSpec, inl_sat =
>> Nothing, inl_act = AlwaysActive, inl_rule =
>> FunLike},NoOccInfo,StrictSig (DmdType <UniqFM> [] (Dunno NoCPR)),JD
>> {strd = Lazy, absd = Use Many Used},0}})
>>
>> You can find my pretty printer (and all the other code for the plugin)
>> at: https://github.com/mikeizbicki/herbie-haskell/blob/master/src/Herbie.hs#L627
>>
>> The function getDictMap
>> (https://github.com/mikeizbicki/herbie-haskell/blob/master/src/Herbie.hs#L171)
>> is where I'm constructing the dictionaries that are getting inserted
>> back into the Core.
>>
>> On Tue, Aug 25, 2015 at 7:17 PM, Ömer Sinan Ağacan <omeragacan at gmail.com> wrote:
>>> It seems like in your App syntax you're having a non-function in function
>>> position. You can see this by looking at what failing function
>>> (splitFunTy_maybe) is doing:
>>>
>>>     splitFunTy_maybe :: Type -> Maybe (Type, Type)
>>>     -- ^ Attempts to extract the argument and result types from a type
>>>     ... (definition is not important) ...
>>>
>>> Then it's used like this at the error site:
>>>
>>>     (arg_ty, res_ty) = expectJust "cpeBody:collect_args" $
>>>                        splitFunTy_maybe fun_ty
>>>
>>> In your case this function is returning Nothing and then exceptJust is
>>> signalling the panic.
>>>
>>> Your code looked correct to me, I don't see any problems with that. Maybe you're
>>> using something wrong as selectors. Could you paste CoreExpr representation of
>>> your program?
>>>
>>> It may also be the case that the panic is caused by something else, maybe your
>>> syntax is invalidating some assumptions/invariants in GHC but it's not
>>> immediately checked etc. Working at the Core level is frustrating at times.
>>>
>>> Can I ask what kind of plugin are you working on?
>>>
>>> (Btw, how did you generate this representation of AST? Did you write it
>>> manually? If you have a pretty-printer, would you mind sharing it?)
>>>
>>> 2015-08-25 18:50 GMT-04:00 Mike Izbicki <mike at izbicki.me>:
>>>> Thanks Ömer!
>>>>
>>>> I'm able to get dictionaries for the superclasses of a class now, but
>>>> I get an error whenever I try to get a dictionary for a
>>>> super-superclass.  Here's the Haskell expression I'm working with:
>>>>
>>>> test1 :: Floating a => a -> a
>>>> test1 x1 = x1+x1
>>>>
>>>> The original core is:
>>>>
>>>> + @ a $dNum_aJu x1 x1
>>>>
>>>> But my plugin is replacing it with the core:
>>>>
>>>> + @ a ($p1Fractional ($p1Floating $dFloating_aJq)) x1 x1
>>>>
>>>> The only difference is the way I'm getting the Num dictionary.  The
>>>> corresponding AST (annotated with variable names and types) is:
>>>>
>>>> App
>>>>     (App
>>>>         (App
>>>>             (App
>>>>                 (Var +::forall a. Num a => a -> a -> a)
>>>>                 (Type a)
>>>>             )
>>>>             (App
>>>>                 (Var $p1Fractional::forall a. Fractional a => Num a)
>>>>                 (App
>>>>                     (Var $p1Floating::forall a. Floating a => Fractional a)
>>>>                     (Var $dFloating_aJq::Floating a)
>>>>                 )
>>>>             )
>>>>         )
>>>>         (Var x1::'a')
>>>>     )
>>>>     (Var x1::'a')
>>>>
>>>> When I insert, GHC gives the following error:
>>>>
>>>> ghc: panic! (the 'impossible' happened)
>>>>   (GHC version 7.10.1 for x86_64-unknown-linux):
>>>>         expectJust cpeBody:collect_args
>>>>
>>>> What am I doing wrong with extracting these super-superclass
>>>> dictionaries?  I've looked up the code for cpeBody in GHC, but I can't
>>>> figure out what it's trying to do, so I'm not sure why it's failing on
>>>> my core.
>>>>
>>>> On Mon, Aug 24, 2015 at 7:10 PM, Ömer Sinan Ağacan <omeragacan at gmail.com> wrote:
>>>>> Mike, here's a piece of code that may be helpful to you:
>>>>>
>>>>> https://github.com/osa1/sc-plugin/blob/master/src/Supercompilation/Show.hs
>>>>>
>>>>> Copy this module to your plugin, it doesn't have any dependencies other than
>>>>> ghc itself. When your plugin is initialized, update `dynFlags_ref` with your
>>>>> DynFlags as first thing to do. Then use Show instance to print AST directly.
>>>>>
>>>>> Horrible hack, but very useful for learning purposes. In fact, I don't know how
>>>>> else we can learn what Core is generated for a given code, and reverse-engineer
>>>>> to figure out details.
>>>>>
>>>>> Hope it helps.
>>>>>
>>>>> 2015-08-24 21:59 GMT-04:00 Ömer Sinan Ağacan <omeragacan at gmail.com>:
>>>>>>> Lets say I'm running the plugin on a function with signature `Floating a => a
>>>>>>> -> a`, then the plugin has access to the `Floating` dictionary for the type.
>>>>>>> But if I want to add two numbers together, I need the `Num` dictionary.  I
>>>>>>> know I should have access to `Num` since it's a superclass of `Floating`.
>>>>>>> How can I get access to these superclass dictionaries?
>>>>>>
>>>>>> I don't have a working code for this but this should get you started:
>>>>>>
>>>>>>     let ord_dictionary :: Id     = ...
>>>>>>         ord_class      :: Class  = ...
>>>>>>      in
>>>>>>         mkApps (Var (head (classSCSels ord_class))) [Var ord_dictionary]
>>>>>>
>>>>>> I don't know how to get Class for Ord. I do `head` here because in the case of
>>>>>> Ord we only have one superclass so `classSCSels` should have one Id. Then I
>>>>>> apply ord_dictionary to this selector and it should return dictionary for Eq.
>>>>>>
>>>>>> I assumed you already have ord_dictionary, it should be passed to your function
>>>>>> already if you had `(Ord a) => ` in your function.
>>>>>>
>>>>>>
>>>>>> Now I realized you asked for getting Num from Floating. I think you should
>>>>>> follow a similar path except you need two applications, first to get Fractional
>>>>>> from Floating and second to get Num from Fractional:
>>>>>>
>>>>>>     mkApps (Var (head (classSCSels fractional_class)))
>>>>>>            [mkApps (Var (head (classSCSels floating_class)))
>>>>>>                    [Var floating_dictionary]]
>>>>>>
>>>>>> Return value should be a Num dictionary.
> _______________________________________________
> ghc-devs mailing list
> ghc-devs at haskell.org
> http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs


More information about the ghc-devs mailing list