Jaro Reinders pushed to branch wip/reduce-type-in-stg at Glasgow Haskell Compiler / GHC
Commits:
-
d400c2d8
by Jaro Reinders at 2026-06-19T12:47:45+02:00
10 changed files:
- compiler/GHC/CoreToStg.hs
- compiler/GHC/Stg/BcPrep.hs
- compiler/GHC/Stg/Syntax.hs
- compiler/GHC/Stg/Unarise.hs
- compiler/GHC/StgToByteCode.hs
- compiler/GHC/StgToCmm/Foreign.hs
- compiler/GHC/StgToCmm/Prim.hs
- compiler/GHC/StgToCmm/Utils.hs
- compiler/GHC/StgToJS/Expr.hs
- compiler/GHC/StgToJS/Utils.hs
Changes:
| ... | ... | @@ -447,7 +447,7 @@ coreToStgExpr expr@(Lam {}) |
| 447 | 447 | fun_ty = mkLamTypes val_bndrs body_ty
|
| 448 | 448 | -- This type is a bit ill-formed but it doesn't matter
|
| 449 | 449 | rhs = StgRhsClosure noExtFieldSilent currentCCS
|
| 450 | - ReEntrant val_bndrs body' (MkStgKind (typeKind body_ty))
|
|
| 450 | + ReEntrant val_bndrs body' (typeStgKind body_ty)
|
|
| 451 | 451 | tmp_fun = mkSysLocal (fsLit "pap") uniq ManyTy fun_ty
|
| 452 | 452 | ; return (StgLet noExtFieldSilent (StgNonRec tmp_fun rhs) $
|
| 453 | 453 | StgApp tmp_fun []) }
|
| ... | ... | @@ -607,7 +607,7 @@ mkStgApp f how_bound core_args stg_args res_ty |
| 607 | 607 | f_arity = stgArity f how_bound
|
| 608 | 608 | n_val_args = length stg_args -- StgArgs are all value arguments
|
| 609 | 609 | exactly_saturated = f_arity == n_val_args
|
| 610 | - res_kind = MkStgKind (typeKind res_ty)
|
|
| 610 | + res_kind = typeStgKind res_ty
|
|
| 611 | 611 | |
| 612 | 612 | |
| 613 | 613 | -- Given Core arguments to an unboxed sum datacon, return the 'PrimRep's
|
| ... | ... | @@ -751,7 +751,7 @@ coreToMkStgRhs bndr expr = do |
| 751 | 751 | let mk_rhs = MkStgRhs
|
| 752 | 752 | { rhs_args = bndrs
|
| 753 | 753 | , rhs_expr = body'
|
| 754 | - , rhs_kind = MkStgKind (typeKind (exprType body))
|
|
| 754 | + , rhs_kind = typeStgKind (exprType body)
|
|
| 755 | 755 | , rhs_is_join = isJoinId bndr
|
| 756 | 756 | }
|
| 757 | 757 | pure mk_rhs
|
| ... | ... | @@ -50,7 +50,7 @@ bcPrepRHS con@StgRhsCon{} = pure con |
| 50 | 50 | bcPrepExpr :: StgExpr -> BcPrepM StgExpr
|
| 51 | 51 | -- explicitly match all constructors so we get a warning if we miss any
|
| 52 | 52 | bcPrepExpr (StgTick bp@(Breakpoint tick_ty _ _) rhs)
|
| 53 | - | isLiftedTypeKind tick_kind = do
|
|
| 53 | + | isLiftedTypeStgKind tick_kind = do
|
|
| 54 | 54 | id <- newId tick_ty
|
| 55 | 55 | rhs' <- bcPrepExpr rhs
|
| 56 | 56 | let expr' = StgTick bp rhs'
|
| ... | ... | @@ -59,7 +59,7 @@ bcPrepExpr (StgTick bp@(Breakpoint tick_ty _ _) rhs) |
| 59 | 59 | ReEntrant
|
| 60 | 60 | []
|
| 61 | 61 | expr'
|
| 62 | - (MkStgKind tick_kind)
|
|
| 62 | + tick_kind
|
|
| 63 | 63 | )
|
| 64 | 64 | letExp = StgLet noExtFieldSilent bnd (StgApp id [])
|
| 65 | 65 | pure letExp
|
| ... | ... | @@ -72,10 +72,10 @@ bcPrepExpr (StgTick bp@(Breakpoint tick_ty _ _) rhs) |
| 72 | 72 | ReEntrant
|
| 73 | 73 | [voidArgId]
|
| 74 | 74 | expr'
|
| 75 | - (MkStgKind tick_kind)
|
|
| 75 | + tick_kind
|
|
| 76 | 76 | )
|
| 77 | 77 | pure $ StgLet noExtFieldSilent bnd (StgApp id [StgVarArg realWorldPrimId])
|
| 78 | - where tick_kind = typeKind tick_ty
|
|
| 78 | + where tick_kind = typeStgKind tick_ty
|
|
| 79 | 79 | bcPrepExpr (StgTick tick rhs) =
|
| 80 | 80 | StgTick tick <$> bcPrepExpr rhs
|
| 81 | 81 | bcPrepExpr (StgLet xlet bnds expr) =
|
| ... | ... | @@ -14,7 +14,8 @@ generation. |
| 14 | 14 | -}
|
| 15 | 15 | |
| 16 | 16 | module GHC.Stg.Syntax (
|
| 17 | - StgKind(..),
|
|
| 17 | + StgKind(getStgKind), typeStgKind, stgKindPrimRep, stgKindPrimRep1,
|
|
| 18 | + stgKindPrimRepU, isUnboxedTupleStgKind, isLiftedTypeStgKind,
|
|
| 18 | 19 | |
| 19 | 20 | StgFArgType(..),
|
| 20 | 21 | collectStgFArgTypes,
|
| ... | ... | @@ -80,7 +81,13 @@ import GHC.Types.CostCentre ( CostCentreStack ) |
| 80 | 81 | import GHC.Core ( AltCon )
|
| 81 | 82 | import GHC.Core.DataCon
|
| 82 | 83 | import GHC.Core.TyCon ( PrimRep(..), PrimOrVoidRep(..), TyCon )
|
| 83 | -import GHC.Core.Type ( Type, tyConAppTyCon )
|
|
| 84 | +import GHC.Core.Type
|
|
| 85 | + ( Type,
|
|
| 86 | + tyConAppTyCon,
|
|
| 87 | + typeKind,
|
|
| 88 | + isUnboxedTupleKind,
|
|
| 89 | + kindRep_maybe,
|
|
| 90 | + isLiftedRuntimeRep )
|
|
| 84 | 91 | import GHC.Core.Ppr( {- instances -} )
|
| 85 | 92 | |
| 86 | 93 | import GHC.Types.ForeignCall ( ForeignCall )
|
| ... | ... | @@ -88,10 +95,19 @@ import GHC.Types.Id |
| 88 | 95 | import GHC.Types.Tickish ( StgTickish )
|
| 89 | 96 | import GHC.Types.Var.Set
|
| 90 | 97 | import GHC.Types.Literal ( Literal, literalType )
|
| 91 | -import GHC.Types.RepType ( typePrimRep, typePrimRep1, typePrimRepU, typePrimRep_maybe, unwrapType )
|
|
| 98 | +import GHC.Types.RepType
|
|
| 99 | + ( typePrimRep,
|
|
| 100 | + typePrimRep1,
|
|
| 101 | + typePrimRepU,
|
|
| 102 | + typePrimRep_maybe,
|
|
| 103 | + kindPrimRep,
|
|
| 104 | + kindPrimRep1,
|
|
| 105 | + kindPrimRep_maybe,
|
|
| 106 | + unwrapType )
|
|
| 92 | 107 | |
| 93 | 108 | import GHC.Utils.Outputable
|
| 94 | 109 | import GHC.Utils.Panic.Plain
|
| 110 | +import GHC.Utils.Panic ( pprPanic )
|
|
| 95 | 111 | |
| 96 | 112 | import GHC.Builtin.PrimOps ( PrimOp, PrimCall )
|
| 97 | 113 | |
| ... | ... | @@ -112,27 +128,54 @@ import GHC.Builtin.Types.Prim |
| 112 | 128 | -- Kind is otherwise equal to.
|
| 113 | 129 | -- See Note [Kinds in STG]
|
| 114 | 130 | newtype StgKind = MkStgKind { getStgKind :: Kind }
|
| 131 | +-- getStgKind is only used to do some silly pretty printing in the JS backend.
|
|
| 132 | + |
|
| 133 | +typeStgKind :: Type -> StgKind
|
|
| 134 | +typeStgKind = MkStgKind . typeKind
|
|
| 135 | + |
|
| 136 | +stgKindPrimRep1 :: StgKind -> PrimRep
|
|
| 137 | +stgKindPrimRep1 = kindPrimRep1 . getStgKind
|
|
| 138 | + |
|
| 139 | +stgKindPrimRepU :: StgKind -> PrimOrVoidRep
|
|
| 140 | +stgKindPrimRepU (MkStgKind kind) = case kindPrimRep_maybe kind of
|
|
| 141 | + Just [] -> VoidRep
|
|
| 142 | + Just [r] -> NVRep r
|
|
| 143 | + r -> pprPanic "stgKindPrimRepU" (ppr r)
|
|
| 144 | + |
|
| 145 | +stgKindPrimRep :: StgKind -> [PrimRep]
|
|
| 146 | +stgKindPrimRep = kindPrimRep . getStgKind
|
|
| 147 | + |
|
| 148 | +isUnboxedTupleStgKind :: StgKind -> Bool
|
|
| 149 | +isUnboxedTupleStgKind = isUnboxedTupleKind . getStgKind
|
|
| 150 | + |
|
| 151 | +isLiftedTypeStgKind :: StgKind -> Bool
|
|
| 152 | +isLiftedTypeStgKind (MkStgKind kind)
|
|
| 153 | + = case kindRep_maybe kind of
|
|
| 154 | + Just rep -> isLiftedRuntimeRep rep
|
|
| 155 | + Nothing -> False
|
|
| 115 | 156 | |
| 116 | 157 | {-
|
| 117 | 158 | Note [Kinds in STG]
|
| 118 | 159 | ~~~~~~~~~~~~~~~~~~~
|
| 119 | - |
|
| 120 | -Whereas Core is type-annotated, STG is kind-annotated.
|
|
| 160 | +Whereas Core is well-typed, STG is well-kinded.
|
|
| 121 | 161 | |
| 122 | 162 | Just as many different values may have a single type, so many different
|
| 123 | 163 | types may have a single kind. So kinds are a "coarser approximation" to the
|
| 124 | 164 | values being manipulated; and that is what we want in STG.
|
| 125 | 165 | |
| 126 | -There are two reasons for this:
|
|
| 166 | +There are two reasons for wanting a coarser type system:
|
|
| 127 | 167 | |
| 128 | 168 | (1) It is easier for third party projects to compile to STG. The type system of
|
| 129 | 169 | another language might not be compatible with GHC's type system. In such a
|
| 130 | 170 | case the kind system is often still compatible because it is so much coarser.
|
| 131 | - Example projects are Jaro Reinders' agda2stg and Csaba Hruska's external-stg.
|
|
| 171 | + Examples of such projects are:
|
|
| 172 | +
|
|
| 173 | + - agda2stg: https://github.com/noughtmare/agda2stg
|
|
| 174 | + - external-stg-interpreter: https://github.com/grin-compiler/ghc-whole-program-compiler-project/tree/master/external-stg-interpreter
|
|
| 132 | 175 | |
| 133 | 176 | (2) It allows for more aggressive optimizations. In STG we may do
|
| 134 | - type-incorrect things that are kind-correct. For example consider
|
|
| 135 | - the following function:
|
|
| 177 | + type-incorrect things that are still kind-correct. For example
|
|
| 178 | + consider the following function:
|
|
| 136 | 179 | |
| 137 | 180 | f :: Either a b -> Either a c
|
| 138 | 181 | f = \x -> case x of r
|
| ... | ... | @@ -146,10 +189,12 @@ There are two reasons for this: |
| 146 | 189 | Left _ -> r <------------- NB
|
| 147 | 190 | Right _ -> error "urk"
|
| 148 | 191 | |
| 192 | + See Note [Case 2: CSEing case binders] for the full details of this
|
|
| 193 | + optimization.
|
|
| 194 | + |
|
| 149 | 195 | This is not type-safe in Core, but it is kind-safe in STG. So, using
|
| 150 | 196 | the coarser notion of kinds in STG allows us to do more aggressive
|
| 151 | - optimizations. Note, however, that we do not implement any such
|
|
| 152 | - optimizations yet.
|
|
| 197 | + optimizations.
|
|
| 153 | 198 | |
| 154 | 199 | Note that the kinds do not always accurately reflect the final runtime
|
| 155 | 200 | representation. For example, on the JS backend the kind 'TYPE Int64Rep'
|
| ... | ... | @@ -813,7 +858,6 @@ to move these around together, notably in StgOpApp and COpStmt. |
| 813 | 858 | |
| 814 | 859 | Note [tagToEnum# in STG]
|
| 815 | 860 | ~~~~~~~~~~~~~~~~~~~~~~~~
|
| 816 | - |
|
| 817 | 861 | STG is untyped, but 'tagToEnum#' needs type information, so we make it a special
|
| 818 | 862 | STG operation which stores the type constructor information alongside it.
|
| 819 | 863 | |
| ... | ... | @@ -833,7 +877,6 @@ To preserve this information we desugar the 'tagToEnum#' primop into a special |
| 833 | 877 | 'StgTagToEnumOp' which has an extra field to store the type constructor
|
| 834 | 878 | information. This desugaring happens when converting Core to STG, which is the
|
| 835 | 879 | last moment that we still have access to the type information.
|
| 836 | - |
|
| 837 | 880 | -}
|
| 838 | 881 | |
| 839 | 882 | data StgOp
|
| ... | ... | @@ -889,7 +889,7 @@ castArgRename ops in_arg rhs = |
| 889 | 889 | ((op,ty,uq):rest_ops) ->
|
| 890 | 890 | let out_id' = mkCastVar uq ty -- out_name `setIdUnique` uq `setIdType` ty
|
| 891 | 891 | sub_cast = castArgRename rest_ops (StgVarArg out_id')
|
| 892 | - in mkCast in_arg op out_id' (MkStgKind (typeKind ty)) $ sub_cast rhs
|
|
| 892 | + in mkCast in_arg op out_id' (typeStgKind ty) $ sub_cast rhs
|
|
| 893 | 893 | |
| 894 | 894 | -- Construct a case binder used when casting sums, of a given type and unique.
|
| 895 | 895 | mkCastVar :: Unique -> Type -> Id
|
| ... | ... | @@ -899,7 +899,7 @@ mkCast :: StgArg -> PrimOp -> OutId -> StgKind -> StgExpr -> StgExpr |
| 899 | 899 | mkCast arg_in cast_op out_id out_kind in_rhs =
|
| 900 | 900 | let scrut = StgOpApp (StgPrimOp cast_op) [arg_in]
|
| 901 | 901 | alt = GenStgAlt { alt_con = DEFAULT, alt_bndrs = [], alt_rhs = in_rhs}
|
| 902 | - alt_ty = PrimAlt (kindPrimRep1 (getStgKind out_kind))
|
|
| 902 | + alt_ty = PrimAlt (stgKindPrimRep1 out_kind)
|
|
| 903 | 903 | in (StgCase scrut out_id alt_ty [alt])
|
| 904 | 904 | |
| 905 | 905 | -- | Build a unboxed sum term from arguments of an alternative.
|
| ... | ... | @@ -645,12 +645,6 @@ schemeE d s p (StgCase scrut _ _ []) = schemeE d s p scrut |
| 645 | 645 | schemeE d s p (StgCase scrut bndr _ alts)
|
| 646 | 646 | = doCase d s p scrut bndr alts
|
| 647 | 647 | |
| 648 | -stgKindPrimRepU :: StgKind -> PrimOrVoidRep
|
|
| 649 | -stgKindPrimRepU (MkStgKind kind) = case kindPrimRep_maybe kind of
|
|
| 650 | - Just [] -> VoidRep
|
|
| 651 | - Just [r] -> NVRep r
|
|
| 652 | - r -> pprPanic "stgKindPrimRepU" (ppr r)
|
|
| 653 | - |
|
| 654 | 648 | {-
|
| 655 | 649 | Ticked Expressions
|
| 656 | 650 | ------------------
|
| ... | ... | @@ -72,7 +72,7 @@ cgForeignCall :: ForeignCall -- the op |
| 72 | 72 | cgForeignCall (CCall (CCallSpec target cconv safety)) arg_tys stg_args res_kind
|
| 73 | 73 | = do { cmm_args <- getFCallArgs stg_args arg_tys
|
| 74 | 74 | -- ; traceM $ show cmm_args
|
| 75 | - ; (res_regs, res_hints) <- newUnboxedTupleRegs (getStgKind res_kind)
|
|
| 75 | + ; (res_regs, res_hints) <- newUnboxedTupleRegs res_kind
|
|
| 76 | 76 | ; let ((call_args, arg_hints), cmm_target)
|
| 77 | 77 | = case target of
|
| 78 | 78 | StaticTarget _ _ ForeignValue ->
|
| ... | ... | @@ -36,7 +36,6 @@ import GHC.Cmm.Graph |
| 36 | 36 | import GHC.Stg.Syntax
|
| 37 | 37 | import GHC.Cmm
|
| 38 | 38 | import GHC.Unit ( rtsUnit )
|
| 39 | -import GHC.Core.Type ( typeKind )
|
|
| 40 | 39 | import GHC.Core.TyCon ( isEnumerationTyCon )
|
| 41 | 40 | import GHC.Cmm.CLabel
|
| 42 | 41 | import GHC.Cmm.Info ( closureInfoPtr )
|
| ... | ... | @@ -105,7 +104,7 @@ cgOpApp (StgTagToEnumOp tyc) args = do |
| 105 | 104 | cmmPrimOpApp :: StgToCmmConfig -> PrimOp -> [CmmExpr] -> FCode ReturnKind
|
| 106 | 105 | cmmPrimOpApp cfg primop cmm_args =
|
| 107 | 106 | let PrimopCmmEmit _inline f = emitPrimOp cfg primop cmm_args
|
| 108 | - in f (MkStgKind (typeKind (primOpResultType primop)))
|
|
| 107 | + in f (typeStgKind (primOpResultType primop))
|
|
| 109 | 108 | |
| 110 | 109 | externalPrimop :: PrimOp -> [CmmExpr] -> PrimopCmmEmit
|
| 111 | 110 | externalPrimop primop args = outOfLinePrimop (callExternalPrimop primop args)
|
| ... | ... | @@ -1919,7 +1918,7 @@ emitPrimOp cfg primop = |
| 1919 | 1918 | pure [reg]
|
| 1920 | 1919 | |
| 1921 | 1920 | ReturnsTuple
|
| 1922 | - -> do (regs, _hints) <- newUnboxedTupleRegs (getStgKind res_kind)
|
|
| 1921 | + -> do (regs, _hints) <- newUnboxedTupleRegs res_kind
|
|
| 1923 | 1922 | pure regs
|
| 1924 | 1923 | f res_kind regs
|
| 1925 | 1924 | emitReturn (map (CmmReg . CmmLocal) regs)
|
| ... | ... | @@ -50,6 +50,7 @@ module GHC.StgToCmm.Utils ( |
| 50 | 50 | import GHC.Prelude hiding ( head, init, last, tail )
|
| 51 | 51 | |
| 52 | 52 | import GHC.Platform
|
| 53 | +import GHC.Stg.Syntax
|
|
| 53 | 54 | import GHC.StgToCmm.Monad
|
| 54 | 55 | import GHC.StgToCmm.Closure
|
| 55 | 56 | import GHC.StgToCmm.Lit (mkSimpleLit, newStringCLit)
|
| ... | ... | @@ -65,7 +66,6 @@ import GHC.StgToCmm.CgUtils |
| 65 | 66 | |
| 66 | 67 | import GHC.Types.ForeignCall
|
| 67 | 68 | import GHC.Types.Id.Info
|
| 68 | -import GHC.Core.Type
|
|
| 69 | 69 | import GHC.Core.TyCon
|
| 70 | 70 | import GHC.Runtime.Heap.Layout
|
| 71 | 71 | import GHC.Unit
|
| ... | ... | @@ -76,7 +76,6 @@ import GHC.Types.Unique |
| 76 | 76 | import GHC.Data.FastString
|
| 77 | 77 | import GHC.Utils.Outputable
|
| 78 | 78 | import GHC.Utils.Panic
|
| 79 | -import GHC.Types.RepType
|
|
| 80 | 79 | import GHC.Types.CostCentre
|
| 81 | 80 | import GHC.Types.IPE
|
| 82 | 81 | |
| ... | ... | @@ -320,22 +319,20 @@ assignTemp e = do { platform <- getPlatform |
| 320 | 319 | ; emitAssign (CmmLocal reg) e
|
| 321 | 320 | ; return reg }
|
| 322 | 321 | |
| 323 | -newUnboxedTupleRegs :: HasDebugCallStack => Kind -> FCode ([LocalReg], [ForeignHint])
|
|
| 322 | +newUnboxedTupleRegs :: HasDebugCallStack => StgKind -> FCode ([LocalReg], [ForeignHint])
|
|
| 324 | 323 | -- Choose suitable local regs to use for the components
|
| 325 | 324 | -- of an unboxed tuple that we are about to return to
|
| 326 | 325 | -- the Sequel. If the Sequel is a join point, using the
|
| 327 | 326 | -- regs it wants will save later assignments.
|
| 328 | 327 | newUnboxedTupleRegs res_kind
|
| 329 | - = assert (isUnboxedTupleKind res_kind) $
|
|
| 330 | - case kindPrimRep_maybe res_kind of
|
|
| 331 | - Just reps ->
|
|
| 332 | - do { platform <- getPlatform
|
|
| 333 | - ; sequel <- getSequel
|
|
| 334 | - ; regs <- case sequel of
|
|
| 335 | - AssignTo regs _ -> regs <$ massert (regs `equalLength` reps)
|
|
| 336 | - _ -> mapM (newTemp . primRepCmmType platform) reps
|
|
| 337 | - ; return (regs, map primRepForeignHint reps) }
|
|
| 338 | - Nothing -> pprPanic "newUnboxedTupleRegs applied to non-unboxed-tuple kind" (ppr res_kind)
|
|
| 328 | + = assert (isUnboxedTupleStgKind res_kind) $
|
|
| 329 | + let reps = stgKindPrimRep res_kind
|
|
| 330 | + in do { platform <- getPlatform
|
|
| 331 | + ; sequel <- getSequel
|
|
| 332 | + ; regs <- case sequel of
|
|
| 333 | + AssignTo regs _ -> regs <$ massert (regs `equalLength` reps)
|
|
| 334 | + _ -> mapM (newTemp . primRepCmmType platform) reps
|
|
| 335 | + ; return (regs, map primRepForeignHint reps) }
|
|
| 339 | 336 | |
| 340 | 337 | -------------------------------------------------------------------------
|
| 341 | 338 | -- emitMultiAssign
|
| ... | ... | @@ -366,7 +366,7 @@ resultSize kind = result |
| 366 | 366 | where
|
| 367 | 367 | result = result_reps `zip` result_slots
|
| 368 | 368 | result_slots = fmap (slotCount . primRepSize) result_reps
|
| 369 | - result_reps = kindPrimRep (getStgKind kind)
|
|
| 369 | + result_reps = stgKindPrimRep kind
|
|
| 370 | 370 | |
| 371 | 371 | -- | Ensure that the set of identifiers has valid 'RuntimeRep's. This function
|
| 372 | 372 | -- returns a no-op when 'csRuntimeAssert' in 'StgToJSConfig' is False.
|
| ... | ... | @@ -205,9 +205,7 @@ typeJSRep :: HasDebugCallStack => Type -> [JSRep] |
| 205 | 205 | typeJSRep t = map primRepToJSRep (typePrimRep t)
|
| 206 | 206 | |
| 207 | 207 | stgKindJSRep :: HasDebugCallStack => StgKind -> [JSRep]
|
| 208 | -stgKindJSRep (MkStgKind k) = case kindPrimRep_maybe k of
|
|
| 209 | - Just rs -> map primRepToJSRep rs
|
|
| 210 | - Nothing -> pprPanic "kindJSRep" (ppr k)
|
|
| 208 | +stgKindJSRep = map primRepToJSRep . stgKindPrimRep
|
|
| 211 | 209 | |
| 212 | 210 | -- only use if you know it's not an unboxed tuple
|
| 213 | 211 | unaryTypeJSRep :: HasDebugCallStack => UnaryType -> JSRep
|