summaryrefslogtreecommitdiff
path: root/compiler/GHC/CoreToStg/Prep.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/CoreToStg/Prep.hs')
-rw-r--r--compiler/GHC/CoreToStg/Prep.hs88
1 files changed, 83 insertions, 5 deletions
diff --git a/compiler/GHC/CoreToStg/Prep.hs b/compiler/GHC/CoreToStg/Prep.hs
index 1a2e634063..15a70cab6e 100644
--- a/compiler/GHC/CoreToStg/Prep.hs
+++ b/compiler/GHC/CoreToStg/Prep.hs
@@ -829,14 +829,23 @@ cpeApp top_env expr
-- rather than the far superior "f x y". Test case is par01.
= let (terminal, args', depth') = collect_args arg
in cpe_app env terminal (args' ++ args) (depth + depth' - 1)
- cpe_app env (Var f) [CpeApp _runtimeRep@Type{}, CpeApp _type@Type{}, CpeApp arg] 1
+ cpe_app env (Var f) (CpeApp _runtimeRep@Type{} : CpeApp _type@Type{} : CpeApp arg : rest) n
| f `hasKey` runRWKey
+ -- N.B. While it may appear that n == 1 in the case of runRW#
+ -- applications, keep in mind that we may have applications that return
+ , n >= 1
-- See Note [runRW magic]
-- Replace (runRW# f) by (f realWorld#), beta reducing if possible (this
-- is why we return a CorePrepEnv as well)
= case arg of
- Lam s body -> cpe_app (extendCorePrepEnv env s realWorldPrimId) body [] 0
- _ -> cpe_app env arg [CpeApp (Var realWorldPrimId)] 1
+ Lam s body -> cpe_app (extendCorePrepEnv env s realWorldPrimId) body rest (n-2)
+ _ -> cpe_app env arg (CpeApp (Var realWorldPrimId) : rest) (n-1)
+ -- TODO: What about casts?
+
+ cpe_app _env (Var f) args n
+ | f `hasKey` runRWKey
+ = pprPanic "cpe_app(runRW#)" (ppr args $$ ppr n)
+
cpe_app env (Var v) args depth
= do { v1 <- fiddleCCall v
; let e2 = lookupCorePrepEnv env v1
@@ -965,8 +974,77 @@ pragma. It is levity-polymorphic.
=> (State# RealWorld -> (# State# RealWorld, o #))
-> (# State# RealWorld, o #)
-It needs no special treatment in GHC except this special inlining here
-in CorePrep (and in GHC.CoreToByteCode).
+It's correctness needs no special treatment in GHC except this special inlining
+here in CorePrep (and in GHC.CoreToByteCode).
+
+However, there are a variety of optimisation opportunities that the simplifier
+takes advantage of. See Note [Simplification of runRW#].
+
+
+Note [Simplification of runRW#]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Consider the program,
+
+ case runRW# (\s -> let n = I# 42# in n) 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 will push strict
+contexts into runRW's continuation. That is, it transforms
+
+ K[ runRW# @r @ty cont ]
+ ~>
+ runRW# @r @ty K[cont]
+
+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 occurences are
+completely fine. Both occurrence analysis and Core Lint 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
+(since doing so would then require a call, whereas we would otherwise end up
+with straight-line). Consequently, GHC.Core.Opt.SetLevels.lvlApp has special
+treatment for runRW# applications, ensure the arguments are not floated if
+MFEs.
+
+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