Avoiding full laziness xform / floating-out (Re: What's the benefit of taking "do" blocks apart? Is there a way to turn that off?)
Erdi, Gergo
Gergo.Erdi at sc.com
Thu Dec 30 15:04:58 UTC 2021
PUBLIC
Ah, at least I've figured out why exactly the simplifier does this (regardless of floating out due to full laziness or not): it is because the definition of the default implementation of (>>) is inlined. I have (>>) defined as such:
ma >> mb = ma >>= \_ -> mb
So when the simplifier encounters a call site "foo >> bar", it needs to bind "foo" and "bar" to avoid duplication (since "ma" and "mb" could occur multiple times on the right-hand side of (>>)'s definition).
This means that the best I can hope for would be to avoid floating out "ma" and "mb", but the variable bindings themselves are unavoidable in the face of inlining (and of course I do want inlining). So I'll need to rethink my approach anyway.
-----Original Message-----
From: Erdi, Gergo
Sent: Thursday, December 30, 2021 12:56 PM
To: Matthew Pickering <matthewtpickering at gmail.com>
Cc: Joachim Breitner <mail at joachim-breitner.de>; ghc-devs at haskell.org
Subject: RE: [External] Re: Avoiding full laziness xform / floating-out (Re: What's the benefit of taking "do" blocks apart? Is there a way to turn that off?)
PUBLIC
Turning on various Opt_D_dump flags, I can see that this tranformation happens in the first iteration of the simplifier. Looking at GHC.Core.Opt.getCoreToDo, I would guess this corresponds to this line:
-- initial simplify: mk specialiser happy: minimum effort please
runWhen do_presimplify simpl_gently,
If I wrote my own optimisation pass, I'm not sure what it could do to inhibit GHC from doing this transformation later on in its pipeline. And I don't want to give up on the simplifier pipeline completely, since there are parts of it that I very much need. One of them is, unfortunately, the specializer itself -- and that comment in getCoreToDo makes me worried that if I somehow managed to get rid of this transformation (or if I skipped the simplifier pipeline completely and just ran the specializer out-of-band), I would break specialization anyway...
-----Original Message-----
From: Matthew Pickering <matthewtpickering at gmail.com>
Sent: Thursday, December 30, 2021 10:42 AM
To: Erdi, Gergo <Gergo.Erdi at sc.com>
Cc: Joachim Breitner <mail at joachim-breitner.de>; ghc-devs at haskell.org
Subject: [External] Re: Avoiding full laziness xform / floating-out (Re: What's the benefit of taking "do" blocks apart? Is there a way to turn that off?)
Hi Gergo,
Sounds like you might be better off writing your own optimisation pass rather than relying on making GHC do what you want.
Cheers
Matt
On Thu, Dec 30, 2021 at 9:05 AM Erdi, Gergo via ghc-devs <ghc-devs at haskell.org> wrote:
>
> PUBLIC
>
> Hi Joachim,
>
> Thanks for the hints!
>
> > Hi Gergo,
> >
> > Am Dienstag, dem 28.12.2021 um 15:57 +0000 schrieb Erdi, Gergo via
> > ghc-
> > devs:
> > > PUBLIC
> >
> > phew
>
> Yeah obviously I'm sitting here not only adding these tags, but also
> coming up with the automated systems and also the company policies
> forcing the usage of said systems ;)
>
> > didn't investigate deeper (maybe if you provide a small example I would), but just from looking at this:
>
> Unfortunately, it's hard to make a small example because this all
> lives in bizarro world where "IO" isn't IO, "String" isn't [Char] etc,
> so I can't just take the input program and pass it to vanilla GHC.
>
> >
> > * It is generally preferable to turn local lambda expressions
> > into top-level functions. This way, instead of dynamically
> > allocating a FUN heap object, it's just a static function.
> >
> > * sat_sKv is an IO expression? Then it is actually a function in a way
> > (taking the "State token" as an argument). So the above applies.
>
> This is "IO" but not GHC's IO, just another type with an opaque
> definition and a Monad instance. There's no reason for GHC to think
> that sat_sKv would be a function.
>
> Of course, sat_sKw is necessarily a function since it is the second
> argument to bind. But only now I am noticing that even sat_sKw's
> definition includes *yet another* floated-out variable:
>
> sat_sKv = some complicated expression 1 sat_sKu = some complicated
> expression 2 sat_sKw = \_ -> sat_sKu main = bindIO sat_sKv sat_sKw
>
> Here, I don't see why GHC should think that sat_sKv and sat_sKu are
> functions. For example, here's the definition of sat_sKu:
>
> sat_sKu :: IO ()
> [LclId]
> sat_sKu
> = let {
> sat_sRy [Occ=Once1] :: String
> [LclId]
> sat_sRy
> = let {
> sat_sRx [Occ=Once1] :: String
> [LclId]
> sat_sRx = unpackCStringUtf8# "\n"# } in
> let {
> sat_sRw [Occ=Once1] :: String
> [LclId]
> sat_sRw
> = case foobar True of {
> False -> unpackCStringUtf8# "False"#;
> True -> unpackCStringUtf8# "True"#
> } } in
> sApp# sat_sRw sat_sRx } in
> putStrOut sat_sRy
>
> Here, putStrOut is so opaque that it doesn't even have a definition,
> it is merely a name registered into GHC's name tables. So I really
> don't see why this looks "function-y" to GHC.
>
> The reason this is all a problem for me is because I would like to
> then interpret all these let-bindings, including the toplevel ones, as
> eagerly defined variables. But then there is of course a difference
> between
>
> main =
> let a1 = someIOAction1
> a2 = someOtherIOAction2
> a2' = \_ -> a2
> in bindIO a1 a2'
>
> and
>
> main =
> let a1 = someIOAction1
> a2 = \_ -> someIOAction2
> in bindIO a1 a2
>
> because if the *definition* of "a2" throws (not its action), then the
> first version will throw immediately whereas the second will only
> throw when "main" is *run*, after running "a1".
>
> > * I think this is the FloatOut pass. You can turn it out using
> > -fno-full-laziness. Not sure if some others passes might
> > do similar things, though.
>
> I tried turning off Opt_FullLaziness, but unfortunately I am still
> getting the same result. I was also hopeful when I saw there's an
> Opt_FloatIn flag, but alas that doesn't change this behaviour either
> (and it's turned on by default anyway...)
>
> I'll try to make a self-contained minimal example next week.
>
> Thanks,
> Gergo
>
> -----Original Message-----
> From: ghc-devs <ghc-devs-bounces at haskell.org> On Behalf Of Joachim
> Breitner
> Sent: Wednesday, December 29, 2021 8:39 PM
> To: ghc-devs at haskell.org
> Subject: [External] Re: What's the benefit of taking "do" blocks apart? Is there a way to turn that off?
>
>
> Hi Gergo,
>
> Am Dienstag, dem 28.12.2021 um 15:57 +0000 schrieb Erdi, Gergo via
> ghc-
> devs:
> > PUBLIC
>
> phew
>
> > I’m seeing ‘do’ blocks getting taking apart into top-level
> > definitions, so e.g.
> >
> > main = do
> > some complicated expression 1
> > some complicated expression 2
> >
> > is compiled into
> >
> > sat_sKv = some complicated expression 1 sat_sKw = \_ -> some
> > complicated expression 2 main = bindIO sat_sKv sat_sKw
> >
> > This seems to happen regardless of any common subexpressions, i.e.
> > it is not the case that sat_sKv or sat_sKw are used anywhere else.
> >
> > What is the intended benefit of this floating-out? Is there a
> > particular Core-to-Core pass that causes this? Is it possible to
> > turn it off?
>
>
> didn’t investigate deeper (maybe if you provide a small example I would), but just from looking at this:
>
> * It is generally preferable to turn local lambda expressions
> into top-level functions. This way, instead of dynamically
> allocating a FUN heap object, it’s just a static function.
>
> * sat_sKv is an IO expression? Then it is actually a function in a way
> (taking the “State token” as an argument). So the above applies.
>
> * I think this is the FloatOut pass. You can turn it out using
> -fno-full-laziness. Not sure if some others passes might
> do similar things, though.
>
> Cheers,
> Joachim
>
>
> --
> Joachim Breitner
> mail at joachim-breitner.de
>
This email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please delete all copies and notify the sender immediately. You may wish to refer to the incorporation details of Standard Chartered PLC, Standard Chartered Bank and their subsidiaries at https: //www.sc.com/en/our-locations
Where you have a Financial Markets relationship with Standard Chartered PLC, Standard Chartered Bank and their subsidiaries (the "Group"), information on the regulatory standards we adhere to and how it may affect you can be found in our Regulatory Compliance Statement at https: //www.sc.com/rcs/ and Regulatory Compliance Disclosures at http: //www.sc.com/rcs/fm
Insofar as this communication is not sent by the Global Research team and contains any market commentary, the market commentary has been prepared by the sales and/or trading desk of Standard Chartered Bank or its affiliate. It is not and does not constitute research material, independent research, recommendation or financial advice. Any market commentary is for information purpose only and shall not be relied on for any other purpose and is subject to the relevant disclaimers available at https: //www.sc.com/en/regulatory-disclosures/#market-disclaimer.
Insofar as this communication is sent by the Global Research team and contains any research materials prepared by members of the team, the research material is for information purpose only and shall not be relied on for any other purpose, and is subject to the relevant disclaimers available at https: //research.sc.com/research/api/application/static/terms-and-conditions.
Insofar as this e-mail contains the term sheet for a proposed transaction, by responding affirmatively to this e-mail, you agree that you have understood the terms and conditions in the attached term sheet and evaluated the merits and risks of the transaction. We may at times also request you to sign the term sheet to acknowledge the same.
Please visit https: //www.sc.com/en/regulatory-disclosures/dodd-frank/ for important information with respect to derivative products.
More information about the ghc-devs
mailing list