Note [Simplification of runRW#]
Consider the program,
case runRW# (\s -> I# 42#) of
I# n# -> f n#
There is no reason why we should allocate an I# constructor given that we
immediately destructure it.
To avoid this the simplifier has a special transformation rule, specific to
runRW#, that pushes a strict context into runRW#'s continuation. See the
`runRW#` guard in `GHC.Core.Opt.Simplify.rebuildCall`. That is, it transforms
K[ runRW# @r @ty cont ]
~>
runRW# @r @ty (\s -> K[cont s])
This has a few interesting implications. Consider, for instance, this program:
join j = ...
in case runRW# @r @ty cont of
result -> jump j result
Performing the transform described above would result in:
join j x = ...
in runRW# @r @ty (\s ->
case cont of in
result -> jump j result
)
If runRW# were a "normal" function this call to join point j would not be
allowed in its continuation argument. However, since runRW# is inlined (as
described in Note [runRW magic] above), such join point occurrences are
completely fine. Both occurrence analysis (see the runRW guard in occAnalApp)
and Core Lint (see the App case of lintCoreExpr) have special treatment for
runRW# applications. See Note [Linting of runRW#] for details on the latter.
Moreover, it's helpful to ensure that runRW's continuation isn't floated out
For instance, if we have
runRW# (\s -> do_something)
where do_something contains only top-level free variables, we may be tempted to
float the argument to the top-level. However, we must resist this urge as since
doing so would then require that runRW# produce an allocation and call, e.g.:
let lvl = \s -> do_somethign
in
....(runRW# lvl)....
whereas without floating the inlining of the definition of runRW would result
in straight-line code. Consequently, GHC.Core.Opt.SetLevels.lvlApp has special
treatment for runRW# applications, ensure the arguments are not floated as
MFEs.
Now that we float evaluation context into runRW#, we also have to give runRW# a
special higher-order CPR transformer lest we risk #19822. E.g.,
case runRW# (\s -> doThings) of x -> Data.Text.Text x something something'
~>
runRW# (\s -> case doThings s of x -> Data.Text.Text x something something')
The former had the CPR property, and so should the latter.
Other considered designs
One design that was rejected was to *require* that runRW#'s continuation be
headed by a lambda. However, this proved to be quite fragile. For instance,
SetLevels is very eager to float bottoming expressions. For instance given
something of the form,
runRW# @r @ty (\s -> case expr of x -> undefined)
SetLevels will see that the body the lambda is bottoming and will consequently
float it to the top-level (assuming expr has no free coercion variables which
prevent this). We therefore end up with
runRW# @r @ty (\s -> lvl s)
Which the simplifier will beta reduce, leaving us with
runRW# @r @ty lvl
Breaking our desired invariant. Ultimately we decided to simply accept that
the continuation may not be a manifest lambda.
---------------------------------------------------------------------------
CpeArg: produces a result satisfying CpeArg
--------------------------------------------------------------------------- References 2
- Linting of runRW# GHC.Core.Lint
- runRW magic GHC.CoreToStg.Prep
Referenced by 8
- GHC.CoreToStg.Prep call site ×3
- GHC.Core.Opt.CprAnal call site
- GHC.Core.Opt.OccurAnal call site
- GHC.Core.Opt.SetLevels call site
- magicIds GHC.Types.Id.Make
- Detecting illegal captures is not guaranteed