diff options
author | Ben Gamari <ben@smart-cactus.org> | 2020-04-24 17:22:44 -0400 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2020-05-29 01:39:55 -0400 |
commit | 277c2f26e6966e0cfaa1ddcd297af44766f37958 (patch) | |
tree | fab5159ddfdf399e43a7660938c974b8d12c8eb1 /compiler/GHC/Builtin | |
parent | 46720997a0b1fa2971a884adf43de096ce130a7e (diff) | |
download | haskell-277c2f26e6966e0cfaa1ddcd297af44766f37958.tar.gz |
Eta expand un-saturated primops
Now since we no longer try to predict CAFfyness we have no need for the
solution to #16846. Eta expanding unsaturated primop applications is
conceptually simpler, especially in the presence of levity polymorphism.
This essentially reverts cac8dc9f51e31e4c0a6cd9bc302f7e1bc7c03beb,
as suggested in #18079.
Closes #18079.
Diffstat (limited to 'compiler/GHC/Builtin')
-rw-r--r-- | compiler/GHC/Builtin/PrimOps.hs | 92 |
1 files changed, 72 insertions, 20 deletions
diff --git a/compiler/GHC/Builtin/PrimOps.hs b/compiler/GHC/Builtin/PrimOps.hs index 8ad0b731f8..96d5a5ad0d 100644 --- a/compiler/GHC/Builtin/PrimOps.hs +++ b/compiler/GHC/Builtin/PrimOps.hs @@ -590,33 +590,85 @@ primOpOcc op = case primOpInfo op of {- Note [Primop wrappers] ~~~~~~~~~~~~~~~~~~~~~~~~~ -Previously hasNoBinding would claim that PrimOpIds didn't have a curried -function definition. This caused quite some trouble as we would be forced to -eta expand unsaturated primop applications very late in the Core pipeline. Not -only would this produce unnecessary thunks, but it would also result in nasty -inconsistencies in CAFfy-ness determinations (see #16846 and -Note [CAFfyness inconsistencies due to late eta expansion] in GHC.Iface.Tidy). - -However, it was quite unnecessary for hasNoBinding to claim this; primops in -fact *do* have curried definitions which are found in GHC.PrimopWrappers, which -is auto-generated by utils/genprimops from prelude/primops.txt.pp. These wrappers -are standard Haskell functions mirroring the types of the primops they wrap. -For instance, in the case of plusInt# we would have: + +To support (limited) use of primops in GHCi genprimopcode generates the +GHC.PrimopWrappers module. This module contains a "primop wrapper" +binding for each primop. These are standard Haskell functions mirroring the +types of the primops they wrap. For instance, in the case of plusInt# we would +have: module GHC.PrimopWrappers where import GHC.Prim as P + + plusInt# :: Int# -> Int# -> Int# plusInt# a b = P.plusInt# a b -We now take advantage of these curried definitions by letting hasNoBinding -claim that PrimOpIds have a curried definition and then rewrite any unsaturated -PrimOpId applications that we find during CoreToStg as applications of the -associated wrapper (e.g. `GHC.Prim.plusInt# 3#` will get rewritten to -`GHC.PrimopWrappers.plusInt# 3#`).` The Id of the wrapper for a primop can be -found using 'PrimOp.primOpWrapperId'. +The Id for the wrapper of a primop can be found using +'GHC.Builtin.PrimOp.primOpWrapperId'. However, GHCi does not use this mechanism +to link primops; it rather does a rather hacky symbol lookup (see +GHC.ByteCode.Linker.primopToCLabel). TODO: Perhaps this should be changed? + +Note that these wrappers aren't *quite* +as expressive as their unwrapped breathern in that they may exhibit less levity +polymorphism. For instance, consider the case of mkWeakNoFinalizer# which has +type: + + mkWeakNoFinalizer# :: forall (r :: RuntimeRep) (k :: TYPE r) (v :: Type). + k -> v + -> State# RealWorld + -> (# State# RealWorld, Weak# v #) + +Naively we could generate a wrapper of the form, + + + mkWeakNoFinalizer# k v s = GHC.Prim.mkWeakNoFinalizer# k v s + +However, this would require that 'k' bind the levity-polymorphic key, +which is disallowed by our levity polymorphism validity checks (see Note +[Levity polymorphism invariants] in GHC.Core). Consequently, we give the +wrapper the simpler, less polymorphic type + + mkWeakNoFinalizer# :: forall (k :: Type) (v :: Type). + k -> v + -> State# RealWorld + -> (# State# RealWorld, Weak# v #) + +This simplification tends to be good enough for GHCi uses given that there are +few levity polymorphic primops and we do little simplification on interpreted +code anyways. + +TODO: This behavior is actually wrong; a program becomes ill-typed upon +replacing a real primop occurrence with one of its wrapper due to the fact that +the former has an additional type binder. Hmmm.... + +Note [Eta expanding primops] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +STG requires that primop applications be saturated. This makes code generation +significantly simpler since otherwise we would need to define a calling +convention for curried applications that can accomodate levity polymorphism. + +To ensure saturation, CorePrep eta expands expand all primop applications as +described in Note [Eta expansion of hasNoBinding things in CorePrep] in +GHC.Core.Prep. + +Historical Note: + +For a short period around GHC 8.8 we rewrote unsaturated primop applications to +rather use the primop's wrapper (see Note [Primop wrappers] in +GHC.Builtin.PrimOps) instead of eta expansion. This was because at the time +CoreTidy would try to predict the CAFfyness of bindings that would be produced +by CorePrep for inclusion in interface files. Eta expanding during CorePrep +proved to be very difficult to predict, leading to nasty inconsistencies in +CAFfyness determinations (see #16846). -Nota Bene: GHC.PrimopWrappers is needed *regardless*, because it's -used by GHCi, which does not implement primops direct at all. +Thankfully, we now no longer try to predict CAFfyness but rather compute it on +GHC STG (see Note [SRTs] in GHC.Cmm.Info.Build) and inject it into the interface +file after code generation (see TODO: Refer to whatever falls out of #18096). +This is much simpler and avoids the potential for inconsistency, allowing us to +return to the somewhat simpler eta expansion approach for unsaturated primops. +See #18079. -} -- | Returns the 'Id' of the wrapper associated with the given 'PrimOp'. |