keepAlive# primop

Sylvain Henry sylvain at haskus.fr
Tue May 26 17:04:02 UTC 2020


Hi ghc-devs,

After a discussion today about `keepAlive#`, I think Option E [1] is 
even more appealing.

To recap the idea was to have keep-alive variable sets attached to 
case-expressions in Core. E.g. `case {k} x of ...`


1. One of the issue was the semantics of `keepAlive#`. `keepAlive#` is 
very intricate with evaluation. As Simon mentioned we want a semantics like:

keepAlive# k x ==> x `seq` touch# k `seq` x

with potentially a special `seq` to deal with diverging `x`.

With `case {k} x of ...` we have this semantics. Even if we throw the 
case alternatives if `x` diverges, we don't throw the keep-alive set so 
we're good.


2. Simon wanted to push `keepAlive#` into case-expressions. With this 
approach we should only have to fix case-of-case to take keep-alive sets 
into account.

case {k} (case {k2} x of .. -> a; ... -> b) of
C0 .. -> e1
C1 .. -> e2

===>

case {k,k2} x of
... -> case {k} a of { C0 .. -> e1; C1 ... -> e2 }
... -> case {k} b of { C0 .. -> e1; C1 ... -> e2 }


3. Compared to other approaches: we don't have to use stack frames (good 
for performance) and we don't have to deal with a continuation (good for 
Core optimizations, hence perf).

4. Implementing this approach is quite straightforward even if it 
modifies Core. I did it last month in [2]. This patch doesn't fully work 
yet with `-O` because some transformation (related to join points IIRC) 
doesn't take keep-alive sets into account but it should be pretty easy 
to fix if we want to use this approach.


Given how hard it is to come up with a good design/implementation of 
other approaches, this one strikes me as probably the more principled we 
have and yet it is relatively easy to implement. What do you think?

Cheers,
Sylvain


[1] 
https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/with-combinator#option-e-tag-core-case-expression-with-kept-alive-variables

[2] https://gitlab.haskell.org/hsyl20/ghc/-/commits/hsyl20-keepalive



On 13/04/2020 19:51, Ben Gamari wrote:
> Ccing ghc-devs@ since this discussion is something of general interest
> to the community.
>
>
> Sylvain Henry <sylvain at haskus.fr> writes:
>
>> Simon, Ben,
>>
>> I've been reading and thinking about `readRW#` issues which are very
>> related to issues we have with `keepAlive#` primop.
>>
>> To recap, the problem is that we want some transformations (that Simon
>> has listed in [1]) to consider:
>>
>> ```
>> case runRW# f of ...
>>
>> case keepAlive# k a of ...
>> ```
>>
>> as if they were really:
>>
>> ```
>> case f realWorld# of ...
>>
>> case a of ...
>> ```
>>
>> BUT without breaking the semantics of runRW# and keepAlive#.
>>
>> I have been thinking about a solution that I have described on the wiki:
>> https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/with-combinator#option-e-tag-core-case-expression-with-kept-alive-variables
>>
>> The idea is to keep a set of variable names in each Core case-expression
>> that are kept alive during the evaluation of the scrutinee.
>>
>> I think it would work very nicely with your `newState#` primop described
>> in [2], both for `runST` and for `unsafeDupablePerformIO` (details on
>> the wiki).
>>
>> It requires a little more upfront work to adapt the code involving
>> case-expressions. But it will force us to review all transformations to
>> check if they are sound when keep-alive sets are not empty, which we
>> would have to do anyway if we implemented another option. We could start
>> by disabling transformations involving non-empty keep-alive sets and
>> iterate to enable the sound ones.
>>
>> I would like your opinions on the approach. I may have totally missed
>> something.
> Thanks for writing this down!
>
> Indeed it is an interesting idea. However, as expressed on IRC, I
> wonder whether this problem rises to the level where it warrants an
> adaptation to our Core representation. It feels a bit like the
> tail is wagging the dog here, especially given how the "tail" here
> merely exists to support FFI.
>
> That being said, this is one of the few options which remain on the
> table that doesn't require changes to user code. Moreover, the
> applicability to runRW# is quite intriguing.
>
> Another (admittedly, more ad-hoc) option that would avoid modifying Core
> would be to teach the simplifier about the class of
> "continuation-passing" primops (e.g. `keepAlive#` and `runRW#`), allowing it
> to push case analyses into the continuation argument. That is,
>
>      case keepAlive# x expr of pat -> rhs
>
>              ~>
>
>      keepAlive# x (case expr of pat -> rhs)
>
> Of course, doing this is a bit tricky since one must rewrite the
> application of keepAlive# to ensure that the resulting application is
> well-typed. Admittedly, this doesn't help the runRW# case (although this
> could presumably be accommodated by touch#'ing the final state token in
> the runRW# desugaring emitted by CorePrep).
>
> On the whole, I'm not a fan of this ad-hoc option. It increases the
> complexity of the simplifier all to support a single operation. By
> comparison, the Core extension looks somewhat appealing.
>
> Cheers,
>
> - Ben
>
>
> [1] https://gitlab.haskell.org/ghc/ghc/-/wikis/proposal/with-combinator#option-e-tag-core-case-expression-with-kept-alive-variables
>
> P.S. A minor note: the keepAlive# "pseudo-instruction" mentioned on the
> Wiki [1] is precisely the touch# operation we have today.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-devs/attachments/20200526/d02ae888/attachment.html>


More information about the ghc-devs mailing list