input-output-hk / tools

5 stars 5 forks source link

Example plugin #8

Open angerman opened 4 years ago

angerman commented 4 years ago

This is an example use case for the Extensible Interface Files, ghc!2948 GHC extension. The idea is to use store and load GHCs core representation from the interface files without having to resort to -fexpose-all-unfoldings or excessive INLINE pragmas. A high level overview is given in README.

Example.hs is the plugin code to be used with GHC, and which contains all the logic to write out, as well as read in the core expressions and inline them.

The setup consists of a Lib module, and a Main module, where the Main module depends on the Lib module. Thus compiling the Main module with the Example plugin should demonstrate the inlining of core expressions.

NOTE: There is also a Makefile that ties this all together and provides a reproducible setup.

Discussion

The Main module contains the following function t: https://github.com/input-output-hk/tools/blob/23c7b7c1d0d7fc4de649ebfe124a0ed2b5a7c329/examples/extensible-interface-files/core-plugins/basic-example/Main.hs#L19-L20

which we call in the main function of the Main module here:

https://github.com/input-output-hk/tools/blob/23c7b7c1d0d7fc4de649ebfe124a0ed2b5a7c329/examples/extensible-interface-files/core-plugins/basic-example/Main.hs#L6-L9

Thus to evaluate t we need f, g as well as x and y, from the Lib module. https://github.com/input-output-hk/tools/blob/23c7b7c1d0d7fc4de649ebfe124a0ed2b5a7c329/examples/extensible-interface-files/core-plugins/basic-example/Lib.hs#L21-L23

https://github.com/input-output-hk/tools/blob/23c7b7c1d0d7fc4de649ebfe124a0ed2b5a7c329/examples/extensible-interface-files/core-plugins/basic-example/Lib.hs#L25-L28

https://github.com/input-output-hk/tools/blob/23c7b7c1d0d7fc4de649ebfe124a0ed2b5a7c329/examples/extensible-interface-files/core-plugins/basic-example/Lib.hs#L35-L37

(1) Without using the plugin we can see that the Main module produces the following core:

 main :: IO ()
 [LclIdX,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
          WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 570 0}]
 main
   = >>
       @IO
       $fMonadIO
       @()
       @()
       (print @Bool $fShowBool (g (+ @Int $fNumInt (f x) y)))
       (>>
          @IO
          $fMonadIO
          @()
          @()
          (print
             @(MyData Bool Int)
             ($fShowMyData @Bool @Int $fShowBool $fShowInt)
             (mapMyData @Int @Int @Bool @Int fn_md md1))
          (>>
             @IO
             $fMonadIO
             @()
             @()
             (print @Int $fShowInt (sumMyData @Int @Int md1))
             (>>
                @IO
                $fMonadIO
                @()
                @()
                (print
                   @Int
                   $fShowInt
                   (sumMyData @Bool @Int (mapMyData @Int @Int @Bool @Int fn_md md1)))
                (print
                   @Int
                   $fShowInt
                   (sumMyData
                      @Bool @Int (mapMyData @Int @Int @Bool @Int fn_md md1))))))

(2) when using the Example plugin, we observe that the Main modules core now looks like this:

 main :: IO ()
 [LclIdX,
  Unf=Unf{Src=<vanilla>, TopLvl=True, Value=False, ConLike=False,
          WorkFree=False, Expandable=False, Guidance=IF_ARGS [] 670 0}]
 main
   = >>
       @IO
       $fMonadIO
       @()
       @()
       (print
          @Bool
          $fShowBool
          (case + @Int
                  $fNumInt
                  (+ @Int $fNumInt (I# 1#) (I# 1#))
                  (+ @Int $fNumInt x (I# 2#))
           of
           { I# ds ->
           case ds of {
             __DEFAULT -> False;
             0# -> True
           }
           }))
       (>>
          @IO
          $fMonadIO
          @()
          @()
          (print
             @(MyData Bool Int)
             ($fShowMyData @Bool @Int $fShowBool $fShowInt)
             (mapMyData @Int @Int @Bool @Int fn_md md1))
          (>>
             @IO
             $fMonadIO
             @()
             @()
             (print @Int $fShowInt (sumMyData @Int @Int md1))
             (>>
                @IO
                $fMonadIO
                @()
                @()
                (print
                   @Int
                   $fShowInt
                   (sumMyData @Bool @Int (mapMyData @Int @Int @Bool @Int fn_md md1)))
                (print
                   @Int
                   $fShowInt
                   (sumMyData
                      @Bool @Int (mapMyData @Int @Int @Bool @Int fn_md md1))))))

Please observe that we can see in (2), that the following expression from (1)

g (+ @Int $fNumInt (f x) y)

was replaced by

case + @Int
                  $fNumInt
                  (+ @Int $fNumInt (I# 1#) (I# 1#))
                  (+ @Int $fNumInt x (I# 2#))
           of
           { I# ds ->
           case ds of {
             __DEFAULT -> False;
             0# -> True
           }
           }

If we now treat Lib as it's own cabal package, this also works - with identical results. This is because the loading process is agnostic to package, so as long as the bindings exist, they will be loaded. In the case of one-shot compilation, modules appear as external anyway, so we would have to explicitly test for external package names if we wanted (for some reason) to exclude this working outside of the home package.