Constructor wrappers vs workers in generated Core

Hi all, I'm compiling Haskell modules with this simple function. I'd like to interpret the Core for practical use and also educational use. compile :: GHC.GhcMonad m => GHC.ModSummary -> m GHC.ModGuts compile modSummary = do parsedModule <- GHC.parseModule modSummary typecheckedModule <- GHC.typecheckModule parsedModule desugared <- GHC.desugarModule typecheckedModule pure (GHC.dm_core_module desugared) And then I'm taking the mg_binds from ModGuts. I want to work with the simplest, least-transformed Core as possible. One thing that's a problem for me is that e.g. the constructor GHC.Integer.Type.S# in this expression \ (ds_dnHN :: Integer) -> case ds_dnHN of _ [Occ=Dead] { S# i# -> case isTrue# (># i# 1#) of _ [Occ=Dead] { False -> (\ _ [Occ=Dead, OS=OneShot] -> $WS# 2#) void#; True -> case nextPrimeWord# (int2Word# i#) of wild_Xp { __DEFAULT -> wordToInteger wild_Xp } }; Jp# bn -> $WJp# (nextPrimeBigNat bn); Jn# _ [Occ=Dead] -> $WS# 2# } is referred to by its wrapper, "$WS#". In general, I'd prefer if it Core always constructed the worker S# directly. It would reduce the number of cases I have to handle. Additionally, what if a worker gets transformed by GHC from e.g. "Wibble !(Int,Int)" to "Wibble !Int !Int", are then case alt patterns going to scrutinize this transformed two-arg version? (As documented here https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/DataTypes#Thelifec...) So my question is: is it possible to disable this wrapper transformation of data constructors? If not it seems like I'll have no option but to handle this extra wrapper stuff, due to the case analyses. That wouldn't be the end of the world, it'd just delay me by another week or so. For strict fields in constructors I was planning on simply forcing the fields in my interpreter when a constructor becomes saturated (and thereby enabling some nice inspection capabilities), rather than generating extra wrapper code that would force the arguments. Cheers

There is no way to turn off wrappers and I don't think it would be
possible to implement easily if at all.
However, they will all probably be inlined after the optimiser runs
but it seems that you don't want to run the optimiser at all on the
generated core?
Perhaps it would be possible to set the inliner parameters so that
only wrappers ended up being inlined and nothing else and then call
the relevant function from the simplifier on your bindings to get rid
of them again.
Cheers,
Matt
On Sat, Feb 2, 2019 at 2:43 PM Christopher Done
Hi all,
I'm compiling Haskell modules with this simple function. I'd like to interpret the Core for practical use and also educational use.
compile :: GHC.GhcMonad m => GHC.ModSummary -> m GHC.ModGuts compile modSummary = do parsedModule <- GHC.parseModule modSummary typecheckedModule <- GHC.typecheckModule parsedModule desugared <- GHC.desugarModule typecheckedModule pure (GHC.dm_core_module desugared)
And then I'm taking the mg_binds from ModGuts. I want to work with the simplest, least-transformed Core as possible. One thing that's a problem for me is that e.g. the constructor
GHC.Integer.Type.S#
in this expression
\ (ds_dnHN :: Integer) -> case ds_dnHN of _ [Occ=Dead] { S# i# -> case isTrue# (># i# 1#) of _ [Occ=Dead] { False -> (\ _ [Occ=Dead, OS=OneShot] -> $WS# 2#) void#; True -> case nextPrimeWord# (int2Word# i#) of wild_Xp { __DEFAULT -> wordToInteger wild_Xp } }; Jp# bn -> $WJp# (nextPrimeBigNat bn); Jn# _ [Occ=Dead] -> $WS# 2# }
is referred to by its wrapper, "$WS#". In general, I'd prefer if it Core always constructed the worker S# directly. It would reduce the number of cases I have to handle.
Additionally, what if a worker gets transformed by GHC from e.g. "Wibble !(Int,Int)" to "Wibble !Int !Int", are then case alt patterns going to scrutinize this transformed two-arg version? (As documented here https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/DataTypes#Thelifec...)
So my question is: is it possible to disable this wrapper transformation of data constructors?
If not it seems like I'll have no option but to handle this extra wrapper stuff, due to the case analyses. That wouldn't be the end of the world, it'd just delay me by another week or so.
For strict fields in constructors I was planning on simply forcing the fields in my interpreter when a constructor becomes saturated (and thereby enabling some nice inspection capabilities), rather than generating extra wrapper code that would force the arguments.
Cheers _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

On Sat, 2 Feb 2019 at 14:50, Matthew Pickering
There is no way to turn off wrappers and I don't think it would be possible to implement easily if at all.
Fair enough.
However, they will all probably be inlined after the optimiser runs but it seems that you don't want to run the optimiser at all on the generated core?
Yeah, I'm trying to avoid as much instability in the output shape as possible, and for educational purposes, optimizations make fairly readable code unreadable. Wait. Can I rely on case alt patterns having the same arity as the original user-defined data type before optimization passes are run? If the answer to that is yes, then I could just replace all wrapper calls with worker calls, which is an easy enough transformation. As a precaution, I could add a check on all case alt patterns that the arity matches the worker arity and barf if not. Thanks for your help! Chris

If you want your core to look at much like the source program as
possible then you could print `$WFoo` as just `Foo`?
The existence of wrappers is a crucial part of desugaring so perhaps
it's useful for users to see them in the output of your program if
it's intended to be educational?
Matt
On Sat, Feb 2, 2019 at 3:06 PM Christopher Done
On Sat, 2 Feb 2019 at 14:50, Matthew Pickering
wrote: There is no way to turn off wrappers and I don't think it would be possible to implement easily if at all.
Fair enough.
However, they will all probably be inlined after the optimiser runs but it seems that you don't want to run the optimiser at all on the generated core?
Yeah, I'm trying to avoid as much instability in the output shape as possible, and for educational purposes, optimizations make fairly readable code unreadable.
Wait. Can I rely on case alt patterns having the same arity as the original user-defined data type before optimization passes are run?
If the answer to that is yes, then I could just replace all wrapper calls with worker calls, which is an easy enough transformation. As a precaution, I could add a check on all case alt patterns that the arity matches the worker arity and barf if not.
Thanks for your help!
Chris

Sorry, here's my explanation.
In summary, I was trying to go for the cleanest, tidiest, simplest AST
possible for interpretation. My long-term goal is to have a slow but
working interpreter of GHC Haskell written in Haskell that is capable
of hot-swapping without segfaulting, by gracefully handling changing
types and functions (just report a type error like the Lisps do), and
the short-term goal is to export that interpretable AST as a web
service so that a simple JS interpreter and/or substitution stepper
could be written for the purposes of education, like
https://chrisdone.com/toys/duet-delta/.
I think this mailing list thread turns out to be an x/y problem. I need STG.
I ended up doing lots of cleaning steps to generate a more well-formed
Core. I learned that the Core that comes from the desugarer is
incomplete and realized today that I was duplicating work done by the
transformation that converts Core to STG. I'd initially avoided STG,
thinking that Core would suffice, but it seems that STG is the AST
that I was converging on anyway. It seems that STG will have all class
methods generated, wrappers generated (and inlined as appropriate),
primops and ffi calls are saturated, etc. no Types or coercions, etc.
It even has info about whether closures can be updated and how.
I wish I'd chosen STG instead of Core from the beginning, but it
doesn't set me back by much so I'll consider this a learning exercise.
If anything, it makes me appreciate almost everything going on in STG
for why it's there and is useful.
I'm aware of two projects that interpret their own form of STG:
http://hackage.haskell.org/package/stgi
http://hackage.haskell.org/package/ministg
But I intend on consuming directly the STG AST from GHC.
The bulk of the work I did that will stay useful is managing a global
mapping of Ids, in terms of global, local ids, and conids in separate
namespaces. IOW I replace all Ids in the AST with a globally unique
Int, like Unique, but it preserves across separate runs, rather than
being per GHC run. So I can plug that work into the STG AST instead. I
have a Docker file that compiles a patched GHC and outputs my AST for
ghc-prim, integer-gmp and base, that leads to all these files:
https://gist.github.com/chrisdone/5ed9adf9dba5fd82d582e9f2bbc30c9f
which have Ids that reference eachother cross-module/package without
any need for more fiddling/munging. The AST looks like this
https://github.com/chrisdone/prana/blob/eaa5b2111631c13eb6b41c9a47400a4ba6a0...
then I have a mapping from Int64->Name for debugging. I think having
an STG representation that tools like ministg/stgi can consume that
includes everything (ghc-prim, integer-gmp and base) is handy, aside
from my own use-cases (giving the AST to a web app and "real"
interpreting).
Cheers!
On Mon, 4 Feb 2019 at 17:56, Matthew Pickering
If you want your core to look at much like the source program as possible then you could print `$WFoo` as just `Foo`?
The existence of wrappers is a crucial part of desugaring so perhaps it's useful for users to see them in the output of your program if it's intended to be educational?
Matt
On Sat, Feb 2, 2019 at 3:06 PM Christopher Done
wrote: On Sat, 2 Feb 2019 at 14:50, Matthew Pickering
wrote: There is no way to turn off wrappers and I don't think it would be possible to implement easily if at all.
Fair enough.
However, they will all probably be inlined after the optimiser runs but it seems that you don't want to run the optimiser at all on the generated core?
Yeah, I'm trying to avoid as much instability in the output shape as possible, and for educational purposes, optimizations make fairly readable code unreadable.
Wait. Can I rely on case alt patterns having the same arity as the original user-defined data type before optimization passes are run?
If the answer to that is yes, then I could just replace all wrapper calls with worker calls, which is an easy enough transformation. As a precaution, I could add a check on all case alt patterns that the arity matches the worker arity and barf if not.
Thanks for your help!
Chris

| is referred to by its wrapper, "$WS#". In general, I'd prefer if it Core
| always constructed the worker S# directly. It would reduce the number of
| cases I have to handle.
What do you mean by "constructed the worker directly"? How does that differ from "call the wrapper, and then (in a simplifier pass) inline the wrapper?
In general, the wrapper of a data constructor can do quite a bit of work: evaluating arguments, unboxing them, casting newtypes, reducing type families.
Simon
| -----Original Message-----
| From: ghc-devs
participants (3)
-
Christopher Done
-
Matthew Pickering
-
Simon Peyton Jones