diff options
author | Simon Peyton Jones <simonpj@microsoft.com> | 2021-04-15 15:20:26 +0100 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2021-04-26 23:56:53 -0400 |
commit | 6c7fff0b6f9514d6572cbe6bbfa4aafc259caebe (patch) | |
tree | befd67e0d63bef6c6aee6b318e4ef6e287a62797 /compiler/GHC/CoreToStg/Prep.hs | |
parent | 06654a6e0e4c1f9eb58947439092ae27b00d8c10 (diff) | |
download | haskell-6c7fff0b6f9514d6572cbe6bbfa4aafc259caebe.tar.gz |
Eliminate unsafeEqualityProof in CorePrep
The main idea here is to avoid treating
* case e of {}
* case unsafeEqualityProof of UnsafeRefl co -> blah
specially in CoreToStg. Instead, nail them in CorePrep,
by converting
case e of {}
==> e |> unsafe-co
case unsafeEqualityProof of UnsafeRefl cv -> blah
==> blah[unsafe-co/cv]
in GHC.Core.Prep. Now expressions that we want to treat as trivial
really are trivial. We can get rid of cpExprIsTrivial.
And we fix #19700.
A downside is that, at least under unsafeEqualityProof, we substitute
in types and coercions, which is more work. But a big advantage is
that it's all very simple and principled: CorePrep really gets rid of
the unsafeCoerce stuff, as it does empty case, runRW#, lazyId etc.
I've updated the overview in GHC.Core.Prep, and added
Note [Unsafe coercions] in GHC.Core.Prep
Note [Implementing unsafeCoerce] in base:Unsafe.Coerce
We get 3% fewer bytes allocated when compiling perf/compiler/T5631,
which uses a lot of unsafeCoerces. (It's a happy-generated parser.)
Metric Decrease:
T5631
Diffstat (limited to 'compiler/GHC/CoreToStg/Prep.hs')
-rw-r--r-- | compiler/GHC/CoreToStg/Prep.hs | 498 |
1 files changed, 341 insertions, 157 deletions
diff --git a/compiler/GHC/CoreToStg/Prep.hs b/compiler/GHC/CoreToStg/Prep.hs index 74616683e3..c4169b5029 100644 --- a/compiler/GHC/CoreToStg/Prep.hs +++ b/compiler/GHC/CoreToStg/Prep.hs @@ -49,6 +49,7 @@ import GHC.Core.Coercion import GHC.Core.TyCon import GHC.Core.DataCon import GHC.Core.Opt.OccurAnal +import GHC.Core.TyCo.Rep( UnivCoProvenance(..) ) import GHC.Data.Maybe @@ -77,7 +78,9 @@ import GHC.Types.TyThing import GHC.Types.CostCentre ( CostCentre, ccFromThisModule ) import GHC.Types.Unique.Supply +import GHC.Data.Pair import Data.List ( unfoldr ) +import Data.Functor.Identity import Control.Monad import qualified Data.Set as S @@ -143,10 +146,58 @@ The goal of this pass is to prepare for code generation. profiling mode. We have to do this here beucase we won't have unfoldings after this pass (see `zapUnfolding` and Note [Drop unfoldings and rules]. +13. Eliminate case clutter in favour of unsafe coercions. + See Note [Unsafe coercions] + +14. Eliminate some magic Ids, specifically + runRW# (\s. e) ==> e[readWorldId/s] + lazy e ==> e + noinline e ==> e + ToDo: keepAlive# ... + This is done in cpeApp + This is all done modulo type applications and abstractions, so that when type erasure is done for conversion to STG, we don't end up with any trivial or useless bindings. +Note [Unsafe coercions] +~~~~~~~~~~~~~~~~~~~~~~~ +CorePrep does these two transformations: + +* Convert empty case to cast with an unsafe coercion + (case e of {}) ===> e |> unsafe-co + See Note [Empty case alternatives] in GHC.Core: if the case + alternatives are empty, the scrutinee must diverge or raise an + exception, so we can just dive into it. + + Of course, if the scrutinee *does* return, we may get a seg-fault. + A belt-and-braces approach would be to persist empty-alternative + cases to code generator, and put a return point anyway that calls a + runtime system error function. + + Notice that eliminating empty case can lead to an ill-kinded coercion + case error @Int "foo" of {} :: Int# + ===> error @Int "foo" |> unsafe-co + where unsafe-co :: Int ~ Int# + But that's fine because the expression diverges anyway. And it's + no different to what happened before. + +* Eliminate unsafeEqualityProof in favour of an unsafe coercion + case unsafeEqualityProof of UnsafeRefl g -> e + ===> e[unsafe-co/g] + See (U2) in Note [Implementing unsafeCoerce] in base:Unsafe.Coerce + + Note that this requiresuse ot substitute 'unsafe-co' for 'g', and + that is the main (current) reason for cpe_tyco_env in CorePrepEnv. + Tiresome, but not difficult. + +These transformations get rid of "case clutter", leaving only casts. +We are doing no further significant tranformations, so the reasons +for the case forms have disappeared. And it is extremely helpful for +the ANF-ery, CoreToStg, and backends, if trivial expressions really do +look trivial. #19700 was an example. + +In both cases, the "unsafe-co" is just (UnivCo ty1 ty2 CorePrepProv). Note [CorePrep invariants] ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -402,7 +453,7 @@ cpeBind top_lvl env (NonRec bndr rhs) dmd is_unlifted env bndr1 rhs -- See Note [Inlining in CorePrep] - ; let triv_rhs = cpExprIsTrivial rhs1 + ; let triv_rhs = exprIsTrivial rhs1 env2 | triv_rhs = extendCorePrepEnvExpr env1 bndr rhs1 | otherwise = env1 floats1 | triv_rhs, isInternalName (idName bndr) @@ -584,8 +635,10 @@ cpeRhsE :: CorePrepEnv -> CoreExpr -> UniqSM (Floats, CpeRhs) -- For example -- f (g x) ===> ([v = g x], f v) -cpeRhsE _env expr@(Type {}) = return (emptyFloats, expr) -cpeRhsE _env expr@(Coercion {}) = return (emptyFloats, expr) +cpeRhsE env (Type ty) + = return (emptyFloats, Type (cpSubstTy env ty)) +cpeRhsE env (Coercion co) + = return (emptyFloats, Coercion (cpSubstCo env co)) cpeRhsE env expr@(Lit (LitNumber nt i)) = case cpe_convertNumLit env nt i of Nothing -> return (emptyFloats, expr) @@ -618,7 +671,7 @@ cpeRhsE env (Tick tickish expr) cpeRhsE env (Cast expr co) = do { (floats, expr') <- cpeRhsE env expr - ; return (floats, Cast expr' co) } + ; return (floats, Cast expr' (cpSubstCo env co)) } cpeRhsE env expr@(Lam {}) = do { let (bndrs,body) = collectBinders expr @@ -626,19 +679,30 @@ cpeRhsE env expr@(Lam {}) ; body' <- cpeBodyNF env' body ; return (emptyFloats, mkLams bndrs' body') } -cpeRhsE env (Case scrut bndr ty alts) +-- Eliminate empty case +-- See Note [Unsafe coercions] +cpeRhsE env (Case scrut _ ty []) + = do { (floats, scrut') <- cpeRhsE env scrut + ; let ty' = cpSubstTy env ty + co' = mkUnsafeCo Representational (exprType scrut') ty' + ; return (floats, Cast scrut' co') } + -- This can give rise to + -- Warning: Unsafe coercion: between unboxed and boxed value + -- but it's fine because 'scrut' diverges + +-- Eliminate unsafeEqualityProof +-- See Note [Unsafe coercions] +cpeRhsE env (Case scrut bndr _ alts) | isUnsafeEqualityProof scrut - , [Alt con bs rhs] <- alts - = do { (floats1, scrut') <- cpeBody env scrut - ; (env1, bndr') <- cpCloneBndr env bndr - ; (env2, bs') <- cpCloneBndrs env1 bs - ; (floats2, rhs') <- cpeBody env2 rhs - ; let case_float = FloatCase scrut' bndr' con bs' True - floats' = (floats1 `addFloat` case_float) - `appendFloats` floats2 - ; return (floats', rhs') } + , isDeadBinder bndr -- We can only discard the case if the case-binder + -- is dead. It usually is, but see #18227 + , [Alt _ [co_var] rhs] <- alts + , let Pair ty1 ty2 = coVarTypes co_var + the_co = mkUnsafeCo Nominal (cpSubstTy env ty1) (cpSubstTy env ty2) + env' = extendCoVarEnv env co_var the_co + = cpeRhsE env' rhs - | otherwise +cpeRhsE env (Case scrut bndr ty alts) = do { (floats, scrut') <- cpeBody env scrut ; (env', bndr2) <- cpCloneBndr env bndr ; let alts' @@ -714,9 +778,9 @@ rhsToBody expr@(Lam {}) | all isTyVar bndrs -- Type lambdas are ok = return (emptyFloats, expr) | otherwise -- Some value lambdas - = do { fn <- newVar (exprType expr) - ; let rhs = cpeEtaExpand (exprArity expr) expr - float = FloatLet (NonRec fn rhs) + = do { let rhs = cpeEtaExpand (exprArity expr) expr + ; fn <- newVar (exprType rhs) + ; let float = FloatLet (NonRec fn rhs) ; return (unitFloat float, Var fn) } where (bndrs,body) = collectBinders expr @@ -807,7 +871,7 @@ cpeApp top_env expr : CpeApp s0 : CpeApp k : rest <- args - = do { y <- newVar result_ty + = do { y <- newVar (cpSubstTy env result_ty) ; s2 <- newVar realWorldStatePrimTy ; -- beta reduce if possible ; (floats, k') <- case k of @@ -845,7 +909,7 @@ cpeApp top_env expr -- Apps it is under are type applications only (c.f. -- exprIsTrivial). But note that we need the type of the -- expression, not the id. - ; (app, floats) <- rebuild_app args e2 (exprType e2) emptyFloats stricts + ; (app, floats) <- rebuild_app env args e2 emptyFloats stricts ; mb_saturate hd app floats depth } where stricts = case idDmdSig v of @@ -865,13 +929,11 @@ cpeApp top_env expr -- N-variable fun, better let-bind it cpe_app env fun args depth - = do { (fun_floats, fun') <- cpeArg env evalDmd fun ty + = do { (fun_floats, fun') <- cpeArg env evalDmd fun -- The evalDmd says that it's sure to be evaluated, -- so we'll end up case-binding it - ; (app, floats) <- rebuild_app args fun' ty fun_floats [] + ; (app, floats) <- rebuild_app env args fun' fun_floats [] ; mb_saturate Nothing app floats depth } - where - ty = exprType fun -- Saturate if necessary mb_saturate head app floats depth = @@ -886,38 +948,45 @@ cpeApp top_env expr -- all of which are used to possibly saturate this application if it -- has a constructor or primop at the head. rebuild_app - :: [ArgInfo] -- The arguments (inner to outer) + :: CorePrepEnv + -> [ArgInfo] -- The arguments (inner to outer) -> CpeApp - -> Type -> Floats -> [Demand] -> UniqSM (CpeApp, Floats) - rebuild_app [] app _ floats ss = do - MASSERT(null ss) -- make sure we used all the strictness info - return (app, floats) - rebuild_app (a : as) fun' fun_ty floats ss = case a of - CpeApp arg@(Type arg_ty) -> - rebuild_app as (App fun' arg) (piResultTy fun_ty arg_ty) floats ss - CpeApp arg@(Coercion {}) -> - rebuild_app as (App fun' arg) (funResultTy fun_ty) floats ss + rebuild_app _ [] app floats ss + = ASSERT(null ss) -- make sure we used all the strictness info + return (app, floats) + + rebuild_app env (a : as) fun' floats ss = case a of + + CpeApp (Type arg_ty) + -> rebuild_app env as (App fun' (Type arg_ty')) floats ss + where + arg_ty' = cpSubstTy env arg_ty + + CpeApp (Coercion co) + -> rebuild_app env as (App fun' (Coercion co')) floats ss + where + co' = cpSubstCo env co + CpeApp arg -> do let (ss1, ss_rest) -- See Note [lazyId magic] in GHC.Types.Id.Make = case (ss, isLazyExpr arg) of (_ : ss_rest, True) -> (topDmd, ss_rest) (ss1 : ss_rest, False) -> (ss1, ss_rest) ([], _) -> (topDmd, []) - (_, arg_ty, res_ty) = - case splitFunTy_maybe fun_ty of - Just as -> as - Nothing -> pprPanic "cpeBody" (ppr fun_ty $$ ppr expr) - (fs, arg') <- cpeArg top_env ss1 arg arg_ty - rebuild_app as (App fun' arg') res_ty (fs `appendFloats` floats) ss_rest - CpeCast co -> - let ty2 = coercionRKind co - in rebuild_app as (Cast fun' co) ty2 floats ss - CpeTick tickish -> + (fs, arg') <- cpeArg top_env ss1 arg + rebuild_app env as (App fun' arg') (fs `appendFloats` floats) ss_rest + + CpeCast co + -> rebuild_app env as (Cast fun' co') floats ss + where + co' = cpSubstCo env co + + CpeTick tickish -- See [Floating Ticks in CorePrep] - rebuild_app as fun' fun_ty (addFloat floats (FloatTick tickish)) ss + -> rebuild_app env as fun' (addFloat floats (FloatTick tickish)) ss isLazyExpr :: CoreExpr -> Bool -- See Note [lazyId magic] in GHC.Types.Id.Make @@ -1142,30 +1211,24 @@ However, until then we simply add a special case excluding literals from the floating done by cpeArg. -} +mkUnsafeCo :: Role -> Type -> Type -> Coercion +mkUnsafeCo role ty1 ty2 = mkUnivCo CorePrepProv role ty1 ty2 + -- | Is an argument okay to CPE? okCpeArg :: CoreExpr -> Bool -- Don't float literals. See Note [ANF-ising literal string arguments]. okCpeArg (Lit _) = False -- Do not eta expand a trivial argument -okCpeArg expr = not (cpExprIsTrivial expr) - -cpExprIsTrivial :: CoreExpr -> Bool -cpExprIsTrivial e - | Tick t e <- e - , not (tickishIsCode t) - = cpExprIsTrivial e - | Case scrut _ _ alts <- e - , isUnsafeEqualityProof scrut - , [Alt _ _ rhs] <- alts - = cpExprIsTrivial rhs - | otherwise - = exprIsTrivial e +okCpeArg expr = not (exprIsTrivial expr) -- This is where we arrange that a non-trivial argument is let-bound cpeArg :: CorePrepEnv -> Demand - -> CoreArg -> Type -> UniqSM (Floats, CpeArg) -cpeArg env dmd arg arg_ty + -> CoreArg -> UniqSM (Floats, CpeArg) +cpeArg env dmd arg = do { (floats1, arg1) <- cpeRhsE env arg -- arg1 can be a lambda + ; let arg_ty = exprType arg1 + is_unlifted = isUnliftedType arg_ty + want_float = wantFloatNested NonRecursive dmd is_unlifted ; (floats2, arg2) <- if want_float floats1 arg1 then return (floats1, arg1) else dontFloat floats1 arg1 @@ -1179,9 +1242,6 @@ cpeArg env dmd arg arg_ty ; return (addFloat floats2 arg_float, varToCoreExpr v) } else return (floats2, arg2) } - where - is_unlifted = isUnliftedType arg_ty - want_float = wantFloatNested NonRecursive dmd is_unlifted {- Note [Floating unlifted arguments] @@ -1621,104 +1681,20 @@ data CorePrepEnv -- see Note [lazyId magic], Note [Inlining in CorePrep] -- and Note [CorePrep inlines trivial CoreExpr not Id] (#12076) + , cpe_tyco_env :: Maybe CpeTyCoEnv -- See Note [CpeTyCoEnv] + , cpe_convertNumLit :: LitNumType -> Integer -> Maybe CoreExpr -- ^ Convert some numeric literals (Integer, Natural) into their -- final Core form } --- | Create a function that converts Bignum literals into their final CoreExpr -mkConvertNumLiteral - :: HscEnv - -> IO (LitNumType -> Integer -> Maybe CoreExpr) -mkConvertNumLiteral hsc_env = do - let - dflags = hsc_dflags hsc_env - platform = targetPlatform dflags - home_unit = hsc_home_unit hsc_env - guardBignum act - | isHomeUnitInstanceOf home_unit primUnitId - = return $ panic "Bignum literals are not supported in ghc-prim" - | isHomeUnitInstanceOf home_unit bignumUnitId - = return $ panic "Bignum literals are not supported in ghc-bignum" - | otherwise = act - - lookupBignumId n = guardBignum (tyThingId <$> lookupGlobal hsc_env n) - - -- The lookup is done here but the failure (panic) is reported lazily when we - -- try to access the `bigNatFromWordList` function. - -- - -- If we ever get built-in ByteArray# literals, we could avoid the lookup by - -- directly using the Integer/Natural wired-in constructors for big numbers. - - bignatFromWordListId <- lookupBignumId bignatFromWordListName - - let - convertNumLit nt i = case nt of - LitNumInteger -> Just (convertInteger i) - LitNumNatural -> Just (convertNatural i) - _ -> Nothing - - convertInteger i - | platformInIntRange platform i -- fit in a Int# - = mkConApp integerISDataCon [Lit (mkLitInt platform i)] - - | otherwise -- build a BigNat and embed into IN or IP - = let con = if i > 0 then integerIPDataCon else integerINDataCon - in mkBigNum con (convertBignatPrim (abs i)) - - convertNatural i - | platformInWordRange platform i -- fit in a Word# - = mkConApp naturalNSDataCon [Lit (mkLitWord platform i)] - - | otherwise --build a BigNat and embed into NB - = mkBigNum naturalNBDataCon (convertBignatPrim i) - - -- we can't simply generate: - -- - -- NB (bigNatFromWordList# [W# 10, W# 20]) - -- - -- using `mkConApp` because it isn't in ANF form. Instead we generate: - -- - -- case bigNatFromWordList# [W# 10, W# 20] of ba { DEFAULT -> NB ba } - -- - -- via `mkCoreApps` - - mkBigNum con ba = mkCoreApps (Var (dataConWorkId con)) [ba] - - convertBignatPrim i = - let - target = targetPlatform dflags - - -- ByteArray# literals aren't supported (yet). Were they supported, - -- we would use them directly. We would need to handle - -- wordSize/endianness conversion between host and target - -- wordSize = platformWordSize platform - -- byteOrder = platformByteOrder platform - - -- For now we build a list of Words and we produce - -- `bigNatFromWordList# list_of_words` - - words = mkListExpr wordTy (reverse (unfoldr f i)) - where - f 0 = Nothing - f x = let low = x .&. mask - high = x `shiftR` bits - in Just (mkConApp wordDataCon [Lit (mkLitWord platform low)], high) - bits = platformWordSizeInBits target - mask = 2 ^ bits - 1 - - in mkApps (Var bignatFromWordListId) [words] - - - return convertNumLit - - mkInitialCorePrepEnv :: HscEnv -> IO CorePrepEnv mkInitialCorePrepEnv hsc_env = do convertNumLit <- mkConvertNumLiteral hsc_env return $ CPE - { cpe_dynFlags = hsc_dflags hsc_env - , cpe_env = emptyVarEnv + { cpe_dynFlags = hsc_dflags hsc_env + , cpe_env = emptyVarEnv + , cpe_tyco_env = Nothing , cpe_convertNumLit = convertNumLit } @@ -1742,6 +1718,117 @@ lookupCorePrepEnv cpe id Just exp -> exp ------------------------------------------------------------------------------ +-- CpeTyCoEnv +-- --------------------------------------------------------------------------- + +{- Note [CpeTyCoEnv] +~~~~~~~~~~~~~~~~~~~~ +The cpe_tyco_env :: Maybe CpeTyCoEnv field carries a substitution +for type and coercion varibles + +* We need the coercion substitution to support the elimination of + unsafeEqualityProof (see Note [Unsafe coercions]) + +* We need the type substitution in case one of those unsafe + coercions occurs in the kind of tyvar binder (sigh) + +We don't need an in-scope set because we don't clone any of these +binders at all, so no new capture can take place. + +The cpe_tyco_env is almost always empty -- it only gets populated +when we get under an usafeEqualityProof. Hence the Maybe CpeTyCoEnv, +which makes everything into a no-op in the common case. +-} + +data CpeTyCoEnv = TCE TvSubstEnv CvSubstEnv + +emptyTCE :: CpeTyCoEnv +emptyTCE = TCE emptyTvSubstEnv emptyCvSubstEnv + +extend_tce_cv :: CpeTyCoEnv -> CoVar -> Coercion -> CpeTyCoEnv +extend_tce_cv (TCE tv_env cv_env) cv co + = TCE tv_env (extendVarEnv cv_env cv co) + +extend_tce_tv :: CpeTyCoEnv -> TyVar -> Type -> CpeTyCoEnv +extend_tce_tv (TCE tv_env cv_env) tv ty + = TCE (extendVarEnv tv_env tv ty) cv_env + +lookup_tce_cv :: CpeTyCoEnv -> CoVar -> Coercion +lookup_tce_cv (TCE _ cv_env) cv + = case lookupVarEnv cv_env cv of + Just co -> co + Nothing -> mkCoVarCo cv + +lookup_tce_tv :: CpeTyCoEnv -> TyVar -> Type +lookup_tce_tv (TCE tv_env _) tv + = case lookupVarEnv tv_env tv of + Just ty -> ty + Nothing -> mkTyVarTy tv + +extendCoVarEnv :: CorePrepEnv -> CoVar -> Coercion -> CorePrepEnv +extendCoVarEnv cpe@(CPE { cpe_tyco_env = mb_tce }) cv co + = cpe { cpe_tyco_env = Just (extend_tce_cv tce cv co) } + where + tce = mb_tce `orElse` emptyTCE + + +cpSubstTy :: CorePrepEnv -> Type -> Type +cpSubstTy (CPE { cpe_tyco_env = mb_env }) ty + = case mb_env of + Just env -> runIdentity (subst_ty env ty) + Nothing -> ty + +cpSubstCo :: CorePrepEnv -> Coercion -> Coercion +cpSubstCo (CPE { cpe_tyco_env = mb_env }) co + = case mb_env of + Just tce -> runIdentity (subst_co tce co) + Nothing -> co + +subst_tyco_mapper :: TyCoMapper CpeTyCoEnv Identity +subst_tyco_mapper = TyCoMapper + { tcm_tyvar = \env tv -> return (lookup_tce_tv env tv) + , tcm_covar = \env cv -> return (lookup_tce_cv env cv) + , tcm_hole = \_ hole -> pprPanic "subst_co_mapper:hole" (ppr hole) + , tcm_tycobinder = \env tcv _vis -> if isTyVar tcv + then return (subst_tv_bndr env tcv) + else return (subst_cv_bndr env tcv) + , tcm_tycon = \tc -> return tc } + +subst_ty :: CpeTyCoEnv -> Type -> Identity Type +subst_co :: CpeTyCoEnv -> Coercion -> Identity Coercion +(subst_ty, _, subst_co, _) = mapTyCoX subst_tyco_mapper + +cpSubstTyVarBndr :: CorePrepEnv -> TyVar -> (CorePrepEnv, TyVar) +cpSubstTyVarBndr env@(CPE { cpe_tyco_env = mb_env }) tv + = case mb_env of + Nothing -> (env, tv) + Just tce -> (env { cpe_tyco_env = Just tce' }, tv') + where + (tce', tv') = subst_tv_bndr tce tv + +subst_tv_bndr :: CpeTyCoEnv -> TyVar -> (CpeTyCoEnv, TyVar) +subst_tv_bndr tce tv + = (extend_tce_tv tce tv (mkTyVarTy tv'), tv') + where + tv' = mkTyVar (tyVarName tv) kind' + kind' = runIdentity $ subst_ty tce $ tyVarKind tv + +cpSubstCoVarBndr :: CorePrepEnv -> CoVar -> (CorePrepEnv, CoVar) +cpSubstCoVarBndr env@(CPE { cpe_tyco_env = mb_env }) cv + = case mb_env of + Nothing -> (env, cv) + Just tce -> (env { cpe_tyco_env = Just tce' }, cv') + where + (tce', cv') = subst_cv_bndr tce cv + +subst_cv_bndr :: CpeTyCoEnv -> CoVar -> (CpeTyCoEnv, CoVar) +subst_cv_bndr tce cv + = (extend_tce_cv tce cv (mkCoVarCo cv'), cv') + where + cv' = mkCoVar (varName cv) ty' + ty' = runIdentity (subst_ty tce $ varType cv) + +------------------------------------------------------------------------------ -- Cloning binders -- --------------------------------------------------------------------------- @@ -1750,8 +1837,11 @@ cpCloneBndrs env bs = mapAccumLM cpCloneBndr env bs cpCloneBndr :: CorePrepEnv -> InVar -> UniqSM (CorePrepEnv, OutVar) cpCloneBndr env bndr - | not (isId bndr) - = return (env, bndr) + | isTyVar bndr + = return (cpSubstTyVarBndr env bndr) + + | isCoVar bndr + = return (cpSubstCoVarBndr env bndr) | otherwise = do { bndr' <- clone_it bndr @@ -1768,11 +1858,13 @@ cpCloneBndr env bndr ; return (extendCorePrepEnv env bndr bndr'', bndr'') } where clone_it bndr - | isLocalId bndr, not (isCoVar bndr) - = do { uniq <- getUniqueM; return (setVarUnique bndr uniq) } + | isLocalId bndr + = do { uniq <- getUniqueM + ; let ty' = cpSubstTy env (idType bndr) + ; return (setVarUnique (setIdType bndr ty') uniq) } + | otherwise -- Top level things, which we don't want -- to clone, have become GlobalIds by now - -- And we don't clone tyvars, or coercion variables = return bndr {- Note [Drop unfoldings and rules] @@ -1905,3 +1997,95 @@ collectCostCentres mod_name -- Unfoldings may have cost centres that in the original definion are -- optimized away, see #5889. get_unf = maybeUnfoldingTemplate . realIdUnfolding + + +------------------------------------------------------------------------------ +-- Numeric literals +-- --------------------------------------------------------------------------- + +-- | Create a function that converts Bignum literals into their final CoreExpr +mkConvertNumLiteral + :: HscEnv + -> IO (LitNumType -> Integer -> Maybe CoreExpr) +mkConvertNumLiteral hsc_env = do + let + dflags = hsc_dflags hsc_env + platform = targetPlatform dflags + home_unit = hsc_home_unit hsc_env + guardBignum act + | isHomeUnitInstanceOf home_unit primUnitId + = return $ panic "Bignum literals are not supported in ghc-prim" + | isHomeUnitInstanceOf home_unit bignumUnitId + = return $ panic "Bignum literals are not supported in ghc-bignum" + | otherwise = act + + lookupBignumId n = guardBignum (tyThingId <$> lookupGlobal hsc_env n) + + -- The lookup is done here but the failure (panic) is reported lazily when we + -- try to access the `bigNatFromWordList` function. + -- + -- If we ever get built-in ByteArray# literals, we could avoid the lookup by + -- directly using the Integer/Natural wired-in constructors for big numbers. + + bignatFromWordListId <- lookupBignumId bignatFromWordListName + + let + convertNumLit nt i = case nt of + LitNumInteger -> Just (convertInteger i) + LitNumNatural -> Just (convertNatural i) + _ -> Nothing + + convertInteger i + | platformInIntRange platform i -- fit in a Int# + = mkConApp integerISDataCon [Lit (mkLitInt platform i)] + + | otherwise -- build a BigNat and embed into IN or IP + = let con = if i > 0 then integerIPDataCon else integerINDataCon + in mkBigNum con (convertBignatPrim (abs i)) + + convertNatural i + | platformInWordRange platform i -- fit in a Word# + = mkConApp naturalNSDataCon [Lit (mkLitWord platform i)] + + | otherwise --build a BigNat and embed into NB + = mkBigNum naturalNBDataCon (convertBignatPrim i) + + -- we can't simply generate: + -- + -- NB (bigNatFromWordList# [W# 10, W# 20]) + -- + -- using `mkConApp` because it isn't in ANF form. Instead we generate: + -- + -- case bigNatFromWordList# [W# 10, W# 20] of ba { DEFAULT -> NB ba } + -- + -- via `mkCoreApps` + + mkBigNum con ba = mkCoreApps (Var (dataConWorkId con)) [ba] + + convertBignatPrim i = + let + target = targetPlatform dflags + + -- ByteArray# literals aren't supported (yet). Were they supported, + -- we would use them directly. We would need to handle + -- wordSize/endianness conversion between host and target + -- wordSize = platformWordSize platform + -- byteOrder = platformByteOrder platform + + -- For now we build a list of Words and we produce + -- `bigNatFromWordList# list_of_words` + + words = mkListExpr wordTy (reverse (unfoldr f i)) + where + f 0 = Nothing + f x = let low = x .&. mask + high = x `shiftR` bits + in Just (mkConApp wordDataCon [Lit (mkLitWord platform low)], high) + bits = platformWordSizeInBits target + mask = 2 ^ bits - 1 + + in mkApps (Var bignatFromWordListId) [words] + + + return convertNumLit + |