Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
6f212121
by Facundo Domínguez at 2026-06-26T20:56:27-04:00
-
adfbb179
by Facundo Domínguez at 2026-06-26T20:56:27-04:00
-
c745b11f
by Copilot at 2026-06-26T20:56:27-04:00
-
2c2a4a2a
by Copilot at 2026-06-26T20:56:27-04:00
-
e2262b0e
by Copilot at 2026-06-26T20:56:27-04:00
-
5f9d9268
by Copilot at 2026-06-26T20:56:27-04:00
7 changed files:
- + changelog.d/add_can_drop_to_occurence_analyser
- compiler/GHC/Core/Opt/OccurAnal.hs
- compiler/GHC/Core/Opt/Simplify.hs
- compiler/GHC/Core/SimpleOpt.hs
- compiler/GHC/Driver/Config.hs
- + testsuite/tests/ghc-api/T27240.hs
- testsuite/tests/ghc-api/all.T
Changes:
| 1 | +section: ghc-lib
|
|
| 2 | +synopsis: Add ``oa_can_drop`` option to the occurrence analyser which selects
|
|
| 3 | + bindings to preserve.
|
|
| 4 | + |
|
| 5 | +issues: #27240
|
|
| 6 | +mrs: !16253
|
|
| 7 | + |
|
| 8 | +description: {
|
|
| 9 | + This is only relevant to clients of the GHC API.
|
|
| 10 | + |
|
| 11 | + The ``oa_can_drop`` option of the occurrence analyser indicates whether a
|
|
| 12 | + binding is ok to drop. The option is also exposed in the simple optimiser as
|
|
| 13 | + ``so_can_drop``.
|
|
| 14 | + |
|
| 15 | + In addition, the function ``occurAnalysePgm`` earned a record parameter of
|
|
| 16 | + type ``OccurAnalOpts`` which aggregates former parameters of the function.
|
|
| 17 | + The record type ``OccEnv`` in turn, replaces its fields ``occ_unf_act`` and
|
|
| 18 | + ``occ_rule_act`` with a field ``occ_opts`` of type ``OccurAnalOpts``.
|
|
| 19 | +} |
| ... | ... | @@ -26,6 +26,7 @@ core expression with (hopefully) improved usage information. |
| 26 | 26 | -}
|
| 27 | 27 | |
| 28 | 28 | module GHC.Core.Opt.OccurAnal (
|
| 29 | + OccurAnalOpts(..),
|
|
| 29 | 30 | occurAnalysePgm,
|
| 30 | 31 | occurAnalyseExpr, occurAnalyseBndrsAndExpr,
|
| 31 | 32 | occurAnalyseExpr_Prep,
|
| ... | ... | @@ -103,12 +104,21 @@ occurAnalyseExpr_Prep expr = expr' |
| 103 | 104 | where
|
| 104 | 105 | WUD _ expr' = occAnal (initOccEnv { occ_allow_weak_joins = True }) expr
|
| 105 | 106 | |
| 107 | +-- | Options for occurrence analysis of a program
|
|
| 108 | +data OccurAnalOpts = OccurAnalOpts
|
|
| 109 | + { oa_active_unf :: Id -> Bool -- ^ Active unfoldings
|
|
| 110 | + , oa_active_rule :: ActivationGhc -> Bool -- ^ Active rules
|
|
| 111 | + , oa_can_drop :: Id -> Bool
|
|
| 112 | + -- ^ Can we drop this Id if it is dead?
|
|
| 113 | + -- See Note [Controlling elimination of dead bindings in occurrence analysis].
|
|
| 114 | + }
|
|
| 115 | + |
|
| 106 | 116 | occurAnalysePgm :: Module -- Used only in debug output
|
| 107 | - -> (Id -> Bool) -- Active unfoldings
|
|
| 108 | - -> (ActivationGhc -> Bool) -- Active rules
|
|
| 109 | - -> [CoreRule] -- Local rules for imported Ids
|
|
| 110 | - -> CoreProgram -> CoreProgram
|
|
| 111 | -occurAnalysePgm this_mod active_unf active_rule imp_rules binds
|
|
| 117 | + -> OccurAnalOpts
|
|
| 118 | + -> [CoreRule] -- Local rules for imported Ids
|
|
| 119 | + -> CoreProgram
|
|
| 120 | + -> CoreProgram
|
|
| 121 | +occurAnalysePgm this_mod opts imp_rules binds
|
|
| 112 | 122 | | isEmptyDetails final_usage
|
| 113 | 123 | = occ_anald_binds
|
| 114 | 124 | |
| ... | ... | @@ -116,8 +126,7 @@ occurAnalysePgm this_mod active_unf active_rule imp_rules binds |
| 116 | 126 | = warnPprTrace True "Glomming in" (hang (ppr this_mod <> colon) 2 (ppr final_usage))
|
| 117 | 127 | occ_anald_glommed_binds
|
| 118 | 128 | where
|
| 119 | - init_env = initOccEnv { occ_rule_act = active_rule
|
|
| 120 | - , occ_unf_act = active_unf }
|
|
| 129 | + init_env = initOccEnv { occ_opts = opts }
|
|
| 121 | 130 | |
| 122 | 131 | WUD final_usage occ_anald_binds = go binds init_env
|
| 123 | 132 | WUD _ occ_anald_glommed_binds = occAnalRecBind init_env TopLevel
|
| ... | ... | @@ -1033,6 +1042,27 @@ Note [Occurrences in stable unfoldings and RULES]: occurrences in an unfolding |
| 1033 | 1042 | or RULE are treated as ManyOcc anyway.
|
| 1034 | 1043 | |
| 1035 | 1044 | But NB that tail-call info is preserved so that we don't thereby lose join points.
|
| 1045 | + |
|
| 1046 | +Note [Controlling elimination of dead bindings in occurrence analysis]
|
|
| 1047 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 1048 | +Sometimes, plugins might want to retain dead bindings.
|
|
| 1049 | + |
|
| 1050 | +For instance, Liquid Haskell might be the sole consumer of a binding that is
|
|
| 1051 | +providing a proof. Or it might provide a lemma that is needed to check other
|
|
| 1052 | +parts of the program. Or it might provide a value that is only referred from a
|
|
| 1053 | +refinement type, but not from the Haskell code itself. See #27240 for more
|
|
| 1054 | +details.
|
|
| 1055 | + |
|
| 1056 | +For this reason, the occurrence analyser can be configured to retain
|
|
| 1057 | +some bindings even if they are dead. This is done by setting the `oa_can_drop`
|
|
| 1058 | +field of `OccAnalOpts` to a function that returns `False` for the bindings that
|
|
| 1059 | +should be retained. All calls to the occurrence analyser from within GHC itself
|
|
| 1060 | +use `const True` for this predicate; only calls from plugins might return
|
|
| 1061 | +`False` in some cases.
|
|
| 1062 | + |
|
| 1063 | +Alternatively, the plugin could avoid running the occurrence analyser, but that
|
|
| 1064 | +would also disable other effects, such as the split of the program in strongly
|
|
| 1065 | +connected components.
|
|
| 1036 | 1066 | -}
|
| 1037 | 1067 | |
| 1038 | 1068 | ------------------------------------------------------------------
|
| ... | ... | @@ -1078,7 +1108,7 @@ occAnalBind !env lvl ire (NonRec bndr rhs) thing_inside combine |
| 1078 | 1108 | !(WUD body_uds (occ, body)) = occAnalNonRecBody env_body bndr' $ \env ->
|
| 1079 | 1109 | thing_inside (addJoinPoint env bndr' rhs_uds)
|
| 1080 | 1110 | in
|
| 1081 | - if isDeadOcc occ -- Drop dead code; see Note [Dead code]
|
|
| 1111 | + if isDeadOcc occ && oa_can_drop (occ_opts env) bndr -- Drop dead code; see Note [Dead code]
|
|
| 1082 | 1112 | then WUD body_uds body
|
| 1083 | 1113 | else WUD (combineJoinPointUDs env rhs_uds body_uds) -- Note `orUDs`
|
| 1084 | 1114 | (combine [NonRec (fst (tagNonRecBinder lvl occ bndr')) rhs']
|
| ... | ... | @@ -1088,7 +1118,7 @@ occAnalBind !env lvl ire (NonRec bndr rhs) thing_inside combine |
| 1088 | 1118 | -- Analyse the body and /then/ the RHS
|
| 1089 | 1119 | | let env_body = addLocalLet env lvl bndr
|
| 1090 | 1120 | , WUD body_uds (occ,body) <- occAnalNonRecBody env_body bndr thing_inside
|
| 1091 | - = if isDeadOcc occ -- Drop dead code; see Note [Dead code]
|
|
| 1121 | + = if isDeadOcc occ && oa_can_drop (occ_opts env) bndr -- Drop dead code; see Note [Dead code]
|
|
| 1092 | 1122 | then WUD body_uds body
|
| 1093 | 1123 | else let
|
| 1094 | 1124 | -- Get the join info from the *new* decision; NB: bndr is not already a JoinId
|
| ... | ... | @@ -1225,10 +1255,10 @@ occAnalRec :: OccEnv -> TopLevelFlag |
| 1225 | 1255 | -> WithUsageDetails [CoreBind]
|
| 1226 | 1256 | |
| 1227 | 1257 | -- The NonRec case is just like a Let (NonRec ...) above
|
| 1228 | -occAnalRec !_ lvl
|
|
| 1258 | +occAnalRec !env lvl
|
|
| 1229 | 1259 | (AcyclicSCC (ND { nd_bndr = bndr, nd_rhs = wtuds }))
|
| 1230 | 1260 | (WUD body_uds binds)
|
| 1231 | - | isDeadOcc occ -- Check for dead code: see Note [Dead code]
|
|
| 1261 | + | isDeadOcc occ && oa_can_drop (occ_opts env) bndr -- Check for dead code: see Note [Dead code]
|
|
| 1232 | 1262 | = WUD body_uds binds
|
| 1233 | 1263 | | otherwise
|
| 1234 | 1264 | = let (bndr', mb_join) = tagNonRecBinder lvl occ bndr
|
| ... | ... | @@ -1463,8 +1493,8 @@ However, tagZero can only be inlined in phase 1 and later, while |
| 1463 | 1493 | the RULE is only active *before* phase 1. So there's no problem.
|
| 1464 | 1494 | |
| 1465 | 1495 | To make this work, we look for the RHS free vars only for
|
| 1466 | -*active* rules. That's the reason for the occ_rule_act field
|
|
| 1467 | -of the OccEnv.
|
|
| 1496 | +*active* rules. That's the reason for the oa_active_rule field
|
|
| 1497 | +of occ_opts in OccEnv.
|
|
| 1468 | 1498 | |
| 1469 | 1499 | Note [loopBreakNodes]
|
| 1470 | 1500 | ~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -1854,7 +1884,7 @@ makeNode !env imp_rule_edges bndr_set (bndr, rhs) |
| 1854 | 1884 | -- of Note [Join arity prediction based on joinRhsArity]
|
| 1855 | 1885 | |
| 1856 | 1886 | --------- IMP-RULES --------
|
| 1857 | - is_active = occ_rule_act env :: ActivationGhc -> Bool
|
|
| 1887 | + is_active = oa_active_rule (occ_opts env) :: ActivationGhc -> Bool
|
|
| 1858 | 1888 | imp_rule_info = lookupImpRules imp_rule_edges bndr
|
| 1859 | 1889 | imp_rule_uds = impRulesScopeUsage imp_rule_info
|
| 1860 | 1890 | imp_rule_fvs = impRulesActiveFvs is_active bndr_set imp_rule_info
|
| ... | ... | @@ -1963,8 +1993,8 @@ nodeScore !env new_bndr lb_deps |
| 1963 | 1993 | | old_bndr `elemVarSet` lb_deps -- Self-recursive things are great loop breakers
|
| 1964 | 1994 | = (0, 0, True) -- See Note [Self-recursion and loop breakers]
|
| 1965 | 1995 | |
| 1966 | - | not (occ_unf_act env old_bndr) -- A binder whose inlining is inactive (e.g. has
|
|
| 1967 | - = (0, 0, True) -- a NOINLINE pragma) makes a great loop breaker
|
|
| 1996 | + | not (oa_active_unf (occ_opts env) old_bndr) -- A binder whose inlining is inactive (e.g. has
|
|
| 1997 | + = (0, 0, True) -- a NOINLINE pragma) makes a great loop breaker
|
|
| 1968 | 1998 | |
| 1969 | 1999 | | exprIsTrivial rhs
|
| 1970 | 2000 | = mk_score 10 -- Practically certain to be inlined
|
| ... | ... | @@ -2985,8 +3015,7 @@ scrutinised y). |
| 2985 | 3015 | data OccEnv
|
| 2986 | 3016 | = OccEnv { occ_encl :: !OccEncl -- Enclosing context information
|
| 2987 | 3017 | , occ_one_shots :: !OneShots -- See Note [OneShots]
|
| 2988 | - , occ_unf_act :: Id -> Bool -- Which Id unfoldings are active
|
|
| 2989 | - , occ_rule_act :: ActivationGhc -> Bool -- Which rules are active
|
|
| 3018 | + , occ_opts :: !OccurAnalOpts
|
|
| 2990 | 3019 | -- See Note [Finding rule RHS free vars]
|
| 2991 | 3020 | |
| 2992 | 3021 | , occ_allow_weak_joins :: !Bool
|
| ... | ... | @@ -3055,18 +3084,19 @@ initOccEnv :: OccEnv |
| 3055 | 3084 | initOccEnv
|
| 3056 | 3085 | = OccEnv { occ_encl = OccVanilla
|
| 3057 | 3086 | , occ_one_shots = []
|
| 3058 | - |
|
| 3059 | - -- To be conservative, we say that all
|
|
| 3060 | - -- inlines and rules are active
|
|
| 3061 | - , occ_unf_act = \_ -> True
|
|
| 3062 | - , occ_rule_act = \_ -> True
|
|
| 3063 | - |
|
| 3064 | 3087 | , occ_allow_weak_joins = False
|
| 3065 | - |
|
| 3066 | 3088 | , occ_join_points = emptyVarEnv
|
| 3067 | 3089 | , occ_bs_env = emptyVarEnv
|
| 3068 | 3090 | , occ_bs_rng = emptyVarSet
|
| 3069 | - , occ_nested_lets = emptyVarSet }
|
|
| 3091 | + , occ_nested_lets = emptyVarSet
|
|
| 3092 | + -- To be conservative, we say that all
|
|
| 3093 | + -- inlines and rules are active
|
|
| 3094 | + , occ_opts = OccurAnalOpts
|
|
| 3095 | + { oa_active_rule = \_ -> True
|
|
| 3096 | + , oa_active_unf = \_ -> True
|
|
| 3097 | + , oa_can_drop = \_ -> True
|
|
| 3098 | + }
|
|
| 3099 | + }
|
|
| 3070 | 3100 | |
| 3071 | 3101 | noBinderSwaps :: OccEnv -> Bool
|
| 3072 | 3102 | noBinderSwaps (OccEnv { occ_bs_env = bs_env }) = isEmptyVarEnv bs_env
|
| ... | ... | @@ -12,7 +12,7 @@ import GHC.Driver.Flags |
| 12 | 12 | import GHC.Core
|
| 13 | 13 | import GHC.Core.Rules
|
| 14 | 14 | import GHC.Core.Ppr ( pprCoreBindings, pprCoreExpr )
|
| 15 | -import GHC.Core.Opt.OccurAnal ( occurAnalysePgm, occurAnalyseExpr )
|
|
| 15 | +import GHC.Core.Opt.OccurAnal ( OccurAnalOpts(..), occurAnalysePgm, occurAnalyseExpr )
|
|
| 16 | 16 | import GHC.Core.Stats ( coreBindsSize, coreBindsStats, exprSize )
|
| 17 | 17 | import GHC.Core.FVs ( exprFreeVars )
|
| 18 | 18 | import GHC.Core.Utils ( mkTicks, stripTicksTop )
|
| ... | ... | @@ -252,8 +252,15 @@ simplifyPgm logger unit_env name_ppr_ctx opts |
| 252 | 252 | = do {
|
| 253 | 253 | -- Occurrence analysis
|
| 254 | 254 | let { tagged_binds = {-# SCC "OccAnal" #-}
|
| 255 | - occurAnalysePgm this_mod active_unf active_rule
|
|
| 256 | - local_rules binds
|
|
| 255 | + occurAnalysePgm
|
|
| 256 | + this_mod
|
|
| 257 | + OccurAnalOpts
|
|
| 258 | + { oa_active_unf = active_unf
|
|
| 259 | + , oa_active_rule = active_rule
|
|
| 260 | + , oa_can_drop = const True
|
|
| 261 | + }
|
|
| 262 | + local_rules
|
|
| 263 | + binds
|
|
| 257 | 264 | } ;
|
| 258 | 265 | Logger.putDumpFileMaybe logger Opt_D_dump_occur_anal "Occurrence analysis"
|
| 259 | 266 | FormatCore
|
| ... | ... | @@ -28,7 +28,7 @@ import GHC.Core.FVs |
| 28 | 28 | import GHC.Core.Unfold
|
| 29 | 29 | import GHC.Core.Unfold.Make
|
| 30 | 30 | import GHC.Core.Make
|
| 31 | -import GHC.Core.Opt.OccurAnal( occurAnalyseExpr, occurAnalysePgm, zapLambdaBndrs )
|
|
| 31 | +import GHC.Core.Opt.OccurAnal( OccurAnalOpts(..), occurAnalyseExpr, occurAnalysePgm, zapLambdaBndrs )
|
|
| 32 | 32 | import GHC.Core.DataCon
|
| 33 | 33 | import GHC.Core.Coercion.Opt ( optCoercion, optTransCo, OptCoercionOpts (..) )
|
| 34 | 34 | import GHC.Core.Type hiding ( substTy, extendTvSubst, extendCvSubst, extendTvSubstList
|
| ... | ... | @@ -208,6 +208,8 @@ data SimpleOpts = SimpleOpts |
| 208 | 208 | -- used-once things
|
| 209 | 209 | --
|
| 210 | 210 | -- See Note [Controlling inlining in the simple optimiser]
|
| 211 | + , so_can_drop :: !(Var -> Bool) -- ^ True <=> can drop the given binding if it is dead
|
|
| 212 | + -- See 'oa_can_drop' in 'OccurAnalOpts'.
|
|
| 211 | 213 | }
|
| 212 | 214 | |
| 213 | 215 | -- | Default options for the Simple optimiser.
|
| ... | ... | @@ -217,6 +219,7 @@ defaultSimpleOpts = SimpleOpts |
| 217 | 219 | , so_co_opts = OptCoercionOpts { optCoercionEnabled = False }
|
| 218 | 220 | , so_eta_red = False
|
| 219 | 221 | , so_inline = const True
|
| 222 | + , so_can_drop = const True
|
|
| 220 | 223 | }
|
| 221 | 224 | |
| 222 | 225 | simpleOptExpr :: HasDebugCallStack => SimpleOpts -> CoreExpr -> CoreExpr
|
| ... | ... | @@ -282,10 +285,15 @@ simpleOptPgm :: SimpleOpts |
| 282 | 285 | simpleOptPgm opts this_mod binds rules =
|
| 283 | 286 | (reverse binds', rules', occ_anald_binds)
|
| 284 | 287 | where
|
| 285 | - occ_anald_binds = occurAnalysePgm this_mod
|
|
| 286 | - (\_ -> True) {- All unfoldings active -}
|
|
| 287 | - (\_ -> False) {- No rules active -}
|
|
| 288 | - rules binds
|
|
| 288 | + occ_anald_binds = occurAnalysePgm
|
|
| 289 | + this_mod
|
|
| 290 | + OccurAnalOpts
|
|
| 291 | + { oa_active_unf = \_ -> True {- All unfoldings active -}
|
|
| 292 | + , oa_active_rule = \_ -> False {- No rules active -}
|
|
| 293 | + , oa_can_drop = so_can_drop opts
|
|
| 294 | + }
|
|
| 295 | + rules
|
|
| 296 | + binds
|
|
| 289 | 297 | |
| 290 | 298 | (final_env, binds') = foldl' do_one (emptyEnv opts, []) occ_anald_binds
|
| 291 | 299 | final_subst = soe_subst final_env
|
| ... | ... | @@ -27,6 +27,7 @@ initSimpleOpts dflags = SimpleOpts |
| 27 | 27 | , so_co_opts = initOptCoercionOpts dflags
|
| 28 | 28 | , so_eta_red = gopt Opt_DoEtaReduction dflags
|
| 29 | 29 | , so_inline = const True
|
| 30 | + , so_can_drop = const True
|
|
| 30 | 31 | }
|
| 31 | 32 | |
| 32 | 33 | -- | Instruct the interpreter evaluation to break...
|
| 1 | + |
|
| 2 | +-- This test checks that bindings are preserved when configuring the occurrence
|
|
| 3 | +-- analyzer and the simple optimizer to not drop dead bindings with names
|
|
| 4 | +-- selected by a predicate.
|
|
| 5 | +--
|
|
| 6 | +-- This feature is important for the LiquidHaskell plugin, which relies on the
|
|
| 7 | +-- simple optimizer to make core programs easier to read, but needs to preserve
|
|
| 8 | +-- bindings that are relevant for verification.
|
|
| 9 | +--
|
|
| 10 | +-- See https://gitlab.haskell.org/ghc/ghc/-/issues/27240 for the full discussion.
|
|
| 11 | +--
|
|
| 12 | + |
|
| 13 | +import Control.Monad
|
|
| 14 | +import Data.List (find)
|
|
| 15 | +import Data.Time (getCurrentTime)
|
|
| 16 | +import GHC
|
|
| 17 | +import GHC.Core
|
|
| 18 | +import GHC.Core.SimpleOpt
|
|
| 19 | +import GHC.Data.StringBuffer
|
|
| 20 | +import GHC.Driver.Config
|
|
| 21 | +import GHC.Driver.DynFlags
|
|
| 22 | +import GHC.Driver.Env.Types
|
|
| 23 | +import GHC.Types.Name
|
|
| 24 | +import GHC.Unit.Module.ModGuts
|
|
| 25 | +import GHC.Unit.Types
|
|
| 26 | +import GHC.Utils.Error
|
|
| 27 | +import GHC.Utils.Outputable
|
|
| 28 | + |
|
| 29 | +import System.Environment (getArgs)
|
|
| 30 | + |
|
| 31 | + |
|
| 32 | +main :: IO ()
|
|
| 33 | +main =
|
|
| 34 | + testLocalBindingsDesugaring
|
|
| 35 | + |
|
| 36 | +testLocalBindingsDesugaring :: IO ()
|
|
| 37 | +testLocalBindingsDesugaring = do
|
|
| 38 | + let inputSource = unlines
|
|
| 39 | + [ "module LocalDeadBindingsDesugaring where"
|
|
| 40 | + , "f :: ()"
|
|
| 41 | + , "f = ()"
|
|
| 42 | + , " where"
|
|
| 43 | + , " w = ()"
|
|
| 44 | + , " z = ()"
|
|
| 45 | + ]
|
|
| 46 | + |
|
| 47 | + isExpectedDesugaring p = case findExpr "f" p of
|
|
| 48 | + Just (Let (NonRec b _) _)
|
|
| 49 | + -> isIdNamed "z" b
|
|
| 50 | + _ -> False
|
|
| 51 | + |
|
| 52 | + isIdNamed name v = occNameString (occName v) == name
|
|
| 53 | + |
|
| 54 | + coreProgram <-
|
|
| 55 | + compileToCore
|
|
| 56 | + (not . isIdNamed "z")
|
|
| 57 | + "LocalDeadBindingsDesugaring"
|
|
| 58 | + inputSource
|
|
| 59 | + unless (isExpectedDesugaring coreProgram) $
|
|
| 60 | + fail $ unlines $
|
|
| 61 | + "Unexpected desugaring: No local binding for `z` found in the Core program."
|
|
| 62 | + : map showPprQualified coreProgram
|
|
| 63 | + |
|
| 64 | +-- | Find the Core expression bound to the given name.
|
|
| 65 | +findExpr :: String -> CoreProgram -> Maybe CoreExpr
|
|
| 66 | +findExpr _ [] =
|
|
| 67 | + Nothing
|
|
| 68 | +findExpr name (p:ps) = case p of
|
|
| 69 | + NonRec b e
|
|
| 70 | + | occNameString (occName b) == name
|
|
| 71 | + -> Just e
|
|
| 72 | + Rec binds
|
|
| 73 | + | Just (_, e) <- find (\(b, _e) -> occNameString (occName b) == name) binds
|
|
| 74 | + -> Just e
|
|
| 75 | + _ -> findExpr name ps
|
|
| 76 | + |
|
| 77 | +showPprQualified :: Outputable a => a -> String
|
|
| 78 | +showPprQualified = showSDocQualified . ppr
|
|
| 79 | + |
|
| 80 | +showSDocQualified :: SDoc -> String
|
|
| 81 | +showSDocQualified = renderWithContext ctx
|
|
| 82 | + where
|
|
| 83 | + ctx = defaultSDocContext { sdocStyle = cmdlineParserStyle }
|
|
| 84 | + |
|
| 85 | + |
|
| 86 | + |
|
| 87 | +compileToCore :: (Id -> Bool) -> String -> String -> IO [CoreBind]
|
|
| 88 | +compileToCore canDrop modName inputSource = do
|
|
| 89 | + [libdir] <- getArgs
|
|
| 90 | + now <- getCurrentTime
|
|
| 91 | + runGhc (Just libdir) $ do
|
|
| 92 | + df1 <- getSessionDynFlags
|
|
| 93 | + GHC.setSessionDynFlags $ df1 { GHC.backend = GHC.bytecodeBackend }
|
|
| 94 | + let target = Target {
|
|
| 95 | + targetId = TargetFile (modName ++ ".hs") Nothing
|
|
| 96 | + , targetUnitId = homeUnitId_ df1
|
|
| 97 | + , targetAllowObjCode = False
|
|
| 98 | + , targetContents = Just (stringToStringBuffer inputSource, now)
|
|
| 99 | + }
|
|
| 100 | + setTargets [target]
|
|
| 101 | + void $ GHC.depanal [] False
|
|
| 102 | + |
|
| 103 | + dsMod <- getModSummary
|
|
| 104 | + (mkModule mainUnit (mkModuleName modName))
|
|
| 105 | + >>= parseModule
|
|
| 106 | + >>= typecheckModule NoTcMPlugins
|
|
| 107 | + >>= desugarModule
|
|
| 108 | + hsc_env <- getSession
|
|
| 109 | + return $ mg_binds $ simpleOptimize canDrop hsc_env $ dm_core_module dsMod
|
|
| 110 | + |
|
| 111 | +-- Run the simple optimizer
|
|
| 112 | +simpleOptimize :: (Id -> Bool) -> GHC.HscEnv -> ModGuts -> ModGuts
|
|
| 113 | +simpleOptimize canDrop hsc_env guts@(ModGuts
|
|
| 114 | + { mg_module = mgmod
|
|
| 115 | + , mg_binds = binds
|
|
| 116 | + , mg_rules = rules
|
|
| 117 | + }) =
|
|
| 118 | + let dflags = hsc_dflags hsc_env
|
|
| 119 | + simpl_opts = (initSimpleOpts dflags)
|
|
| 120 | + { so_inline = canDrop
|
|
| 121 | + , so_can_drop = canDrop
|
|
| 122 | + }
|
|
| 123 | + (binds2, rules2, _occ_anald_binds) =
|
|
| 124 | + simpleOptPgm simpl_opts mgmod binds rules
|
|
| 125 | + in guts
|
|
| 126 | + { mg_binds = binds2
|
|
| 127 | + , mg_rules = rules2
|
|
| 128 | + } |
| ... | ... | @@ -82,6 +82,7 @@ test('TypeMapStringLiteral', normal, compile_and_run, ['-package ghc']) |
| 82 | 82 | |
| 83 | 83 | test('T25121_status', normal, compile_and_run, ['-package ghc'])
|
| 84 | 84 | test('T24386', [extra_run_opts(f'"{config.libdir}"')], compile_and_run, ['-package ghc'])
|
| 85 | +test('T27240', [extra_run_opts(f'"{config.libdir}"')], compile_and_run, ['-package ghc'])
|
|
| 85 | 86 | test('T27273', [extra_run_opts(f'"{config.libdir}"')],
|
| 86 | 87 | compile_and_run,
|
| 87 | 88 | ['-package ghc']) |