PrimOpIds and PrimOpWrappers
I discussed with @bgamari two related issues
-
Use the primop wrappers, instead of always eta-expanding. The strange module
GHC.PrimopWrappersis generated byutils/genprimopcodefromprimops.txt.pp. It contains bindings like(+#) :: Int# -> Int# -> Int# (+#) x y = (+#) x yThis defines a curried function
GHC.PrimopWrappers.(+#), whose implementation is an unboxed machine add. The code generator spots that saturated(+#) x yin the right hand side, and generates a machine add.Why do we need these curried functions? So that in a higher order call like
f (+#), we can passGHC.PrimopWrappers.(+#).An alternative, is to eta-expand, so that we have
f (\x y. (+#) x y); now we don't need that wrapper.We must eta-expand for levity-polymorphic primops, because we can't compile levity-polymorphic code in
GHC.PrimopWrappers. ThehasNoBindingpredicate is true for functions that have no binding -- and that must include levity-polymorphic primops.In !6197 (closed) I tried making
hasNoBindingreturn False for all non-levity-polymorphic primops. But that causedghci> data A <interactive>: internal error: stg_ap_v_retThis error comes from somewhere near this code in GHC.UI.Monad
runDecls' :: GhciMonad m => [LHsDecl GhcPs] -> m (Maybe [GHC.Name]) runDecls' decls = do st <- getGHCiState liftIO (print "hello2") reifyGHCi $ \x -> withProgName (progname st) $ withArgs (args st) $ reflectGHCi x $ GHC.handleSourceError (\e -> do GHC.printException e; return Nothing) (Just <$> GHC.runParsedDecls decls)If you comment out both the
withProgNameandwithArgscalls, it works; if not, you crash.I'm not sure why; @bgamari may try to find out.
-
De-duplicate primop wrappers. GHC currently builds two different Ids for PrimOps. In GHC.Types.Id.Make:
mkPrimOpId :: PrimOp -> Id mkPrimOpId prim_opIn GHC.Builtin.PrimOps:
primOpWrapperId :: PrimOp -> Id primOpWrapperId op = mkVanillaGlobalWithInfo name ty infoWe put both into the symbol table.
We also currently generate (using
genprimopcode) two modules:-
GHC.Prim.hswhich contains accurate type signatures and Haddock docs; all RHS are bottom. Alsodata Char#etc (no RHS) for primitive types. This module is used only for Haddock to generate docs. -
GHC.Primopwrappers.hswhich contains no Haddock, just wrappers. Used for unsaturated applications.
There seems no reason for all this duplication. Instead:
-
Kill off
GHC.PrimopWrappers.hs; instead generate one file, but with module nameGHC.Prim, with accurate types, Haddock, and (for rep-mono primops) wrappers.- This module is generated by
genprimopcodefromprimops.txt.pp. - It contains Haddock docs for the primops; this documentation comes form
primops.txt.pp - Even representation-polymorphic primops (for which a wrapper makes no sense) get a defn in
GHC.Prim, solely to carry the Haddock documentation. - Primitive types get
data Char#with Haddock docs inGHC.Prim. - Pseudo-ops like
nullAddr#have a type and Haddock, but a dummy (bottom) RHS. They get compulsory unfoldings, so we will never call them.
- This module is generated by
-
Kill off
primOpWrapperId; useprimOpIdinstead. (KillmkPrimOpWrapperUniquetoo.)
Ben is going to look into this. Slightly fiddly, but makes things simpler; and the symbol table will be smaller.
-
NB: three kinds of things are defined in primops.txt.pp:
- Primitive types.
genprimopcodeignores them. - Primops.
genprimopcodegenerates a wrapper function. - Pseudo-ops.
genprimopcode....
Things to watch out for: #18079 (closed), #19982