diff options
Diffstat (limited to 'compiler/GHC/Core')
-rw-r--r-- | compiler/GHC/Core/Opt/DmdAnal.hs | 249 | ||||
-rw-r--r-- | compiler/GHC/Core/Opt/WorkWrap/Utils.hs | 54 |
2 files changed, 161 insertions, 142 deletions
diff --git a/compiler/GHC/Core/Opt/DmdAnal.hs b/compiler/GHC/Core/Opt/DmdAnal.hs index add2c922e4..c3fd09c6e0 100644 --- a/compiler/GHC/Core/Opt/DmdAnal.hs +++ b/compiler/GHC/Core/Opt/DmdAnal.hs @@ -432,35 +432,38 @@ dmdAnal' env dmd (Lam var body) in WithDmdType new_dmd_type (Lam var' body') -dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt bndrs rhs]) +dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt_con bndrs rhs]) -- Only one alternative. -- If it's a DataAlt, it should be the only constructor of the type and we -- can consider its field demands when analysing the scrutinee. - | want_precise_field_dmds alt + | want_precise_field_dmds alt_con = let WithDmdType rhs_ty rhs' = dmdAnal env dmd rhs WithDmdType alt_ty1 fld_dmds = findBndrsDmds env rhs_ty bndrs WithDmdType alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd + -- Evaluation cardinality on the case binder is irrelevant and a no-op. -- What matters is its nested sub-demand! -- NB: If case_bndr_dmd is absDmd, boxity will say Unboxed, which is -- what we want, because then `seq` will put a `seqDmd` on its scrut. (_ :* case_bndr_sd) = strictifyDmd case_bndr_dmd + -- Compute demand on the scrutinee -- FORCE the result, otherwise thunks will end up retaining the -- whole DmdEnv !(!bndrs', !scrut_sd) - | DataAlt _ <- alt + | DataAlt _ <- alt_con -- See Note [Demand on the scrutinee of a product case] -- See Note [Demand on case-alternative binders] , (!scrut_sd, fld_dmds') <- addCaseBndrDmd case_bndr_sd fld_dmds , let !bndrs' = setBndrsDemandInfo bndrs fld_dmds' = (bndrs', scrut_sd) | otherwise - -- __DEFAULT and literal alts. Simply add demands and discard the - -- evaluation cardinality, as we evaluate the scrutinee exactly once. + -- DEFAULT alts. Simply add demands and discard the evaluation + -- cardinality, as we evaluate the scrutinee exactly once. = assert (null bndrs) (bndrs, case_bndr_sd) + alt_ty3 -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand" | exprMayThrowPreciseException (ae_fam_envs env) scrut @@ -478,35 +481,27 @@ dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt bndrs rhs]) -- , text "scrut_ty" <+> ppr scrut_ty -- , text "alt_ty" <+> ppr alt_ty2 -- , text "res_ty" <+> ppr res_ty ]) $ - WithDmdType res_ty (Case scrut' case_bndr' ty [Alt alt bndrs' rhs']) + WithDmdType res_ty (Case scrut' case_bndr' ty [Alt alt_con bndrs' rhs']) where - want_precise_field_dmds alt = case alt of - (DataAlt dc) - | Nothing <- tyConSingleAlgDataCon_maybe $ dataConTyCon dc -> False - | DefinitelyRecursive <- ae_rec_dc env dc -> False - -- See Note [Demand analysis for recursive data constructors] - _ -> True - - - + want_precise_field_dmds (DataAlt dc) + | Nothing <- tyConSingleAlgDataCon_maybe $ dataConTyCon dc + = False -- Not a product type, even though this is the + -- only remaining possible data constructor + | DefinitelyRecursive <- ae_rec_dc env dc + = False -- See Note [Demand analysis for recursive data constructors] + | otherwise + = True + want_precise_field_dmds (LitAlt {}) = False -- Like the non-product datacon above + want_precise_field_dmds DEFAULT = True dmdAnal' env dmd (Case scrut case_bndr ty alts) = let -- Case expression with multiple alternatives - WithDmdType alt_ty alts' = combineAltDmds alts - - combineAltDmds [] = WithDmdType botDmdType [] - combineAltDmds (a:as) = - let - WithDmdType cur_ty a' = dmdAnalSumAlt env dmd case_bndr a - WithDmdType rest_ty as' = combineAltDmds as - in WithDmdType (lubDmdType cur_ty rest_ty) (a':as') + WithDmdType scrut_ty scrut' = dmdAnal env topSubDmd scrut WithDmdType alt_ty1 case_bndr_dmd = findBndrDmd env alt_ty case_bndr !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd - WithDmdType scrut_ty scrut' = dmdAnal env topSubDmd scrut - -- NB: Base case is botDmdType, for empty case alternatives - -- This is a unit for lubDmdType, and the right result - -- when there really are no alternatives + WithDmdType alt_ty alts' = dmdAnalSumAlts env dmd case_bndr alts + fam_envs = ae_fam_envs env alt_ty2 -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand" @@ -564,7 +559,20 @@ forcesRealWorld fam_envs ty | otherwise = False -dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> Alt Var -> WithDmdType (Alt Var) +dmdAnalSumAlts :: AnalEnv -> SubDemand -> Id -> [CoreAlt] -> WithDmdType [CoreAlt] + +dmdAnalSumAlts _ _ _ [] = WithDmdType botDmdType [] + -- Base case is botDmdType, for empty case alternatives + -- This is a unit for lubDmdType, and the right result + -- when there really are no alternatives +dmdAnalSumAlts env dmd case_bndr (alt:alts) + = let + WithDmdType cur_ty alt' = dmdAnalSumAlt env dmd case_bndr alt + WithDmdType rest_ty alts' = dmdAnalSumAlts env dmd case_bndr alts + in WithDmdType (lubDmdType cur_ty rest_ty) (alt':alts') + + +dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> CoreAlt -> WithDmdType CoreAlt dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs) | WithDmdType rhs_ty rhs' <- dmdAnal env dmd rhs , WithDmdType alt_ty dmds <- findBndrsDmds env rhs_ty bndrs @@ -752,10 +760,13 @@ T11545 features a single-product, recursive data type Naturally, `(==)` is deeply strict in `A` and in fact will never terminate. That leads to very large (exponential in the depth) demand signatures and fruitless churn in boxity analysis, demand analysis and worker/wrapper. -So we detect `A` as a recursive data constructor -(see Note [Detecting recursive data constructors]) analysing `case x of A ...` + +So we detect `A` as a recursive data constructor (see +Note [Detecting recursive data constructors]) analysing `case x of A ...` and simply assume L for the demand on field binders, which is the same code -path as we take for sum types. +path as we take for sum types. This code happens in want_precise_field_dmds +in the Case equation for dmdAnal. + Combined with the B demand on the case binder, we get the very small demand signature <1S><1S>b on `(==)`. This improves ghc/alloc performance on T11545 tenfold! See also Note [CPR for recursive data constructors] which describes the @@ -1267,38 +1278,67 @@ this, that actually happened in practice. Note [Boxity for bottoming functions] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Consider -```hs -indexError :: Show a => (a, a) -> a -> String -> b --- Str=<..><1!P(S,S)><1S><S>b -indexError rng i s = error (show rng ++ show i ++ show s) - -get :: (Int, Int) -> Int -> [a] -> a -get p@(l,u) i xs - | l <= i, i < u = xs !! (i-u) - | otherwise = indexError p i "get" -``` -The hot path of `get` certainly wants to unbox `p` as well as `l` and `u`, but -the unimportant, diverging error path needs `l` and `u` boxed (although the -wrapper for `indexError` *will* unbox `p`). This pattern often occurs in -performance sensitive code that does bounds-checking. - -It would be a shame to let `Boxed` win for the fields! So here's what we do: -While summarising `indexError`'s boxity signature in `finaliseArgBoxities`, -we `unboxDeeplyDmd` all its argument demands and are careful not to discard -excess boxity in the `StopUnboxing` case, to get the signature -`<1!P(!S,!S)><1!S><S!S>b`. - -Then worker/wrapper will not only unbox the pair passed to `indexError` (as it -would do anyway), demand analysis will also pretend that `indexError` needs `l` -and `u` unboxed (and the two other args). Which is a lie, because `indexError`'s -type abstracts over their types and could never unbox them. - -The important change is at the *call sites* of `$windexError`: Boxity analysis -will conclude to unbox `l` and `u`, which *will* incur reboxing of crud that -should better float to the call site of `$windexError`. There we don't care -much, because it's in the slow, diverging code path! And that floating often -happens, but not always. See Note [Reboxed crud for bottoming calls]. +Consider (A) + indexError :: Show a => (a, a) -> a -> String -> b + -- Str=<..><1!P(S,S)><1S><S>b + indexError rng i s = error (show rng ++ show i ++ show s) + + get :: (Int, Int) -> Int -> [a] -> a + get p@(l,u) i xs + | l <= i, i < u = xs !! (i-u) + | otherwise = indexError p i "get" + +The hot path of `get` certainly wants to unbox `p` as well as `l` and +`u`, but the unimportant, diverging error path needs `l::a` and `u::a` +boxed, since `indexError` can't unbox them because they are polymorphic. +This pattern often occurs in performance sensitive code that does +bounds-checking. + +So we want to give `indexError` a signature like `<1!P(!S,!S)><1!S><S!S>b` +where the !S (meaning Poly Unboxed C1N) says that the polymorphic arguments +are unboxed (recursively). The wrapper for `indexError` won't /acutally/ +unbox them (because their polymorphic type doesn't allow that) but when +demand-analysing /callers/, we'll behave as if that call needs the args +unboxed. + +Then at call sites of `indexError`, we will end up doing some +reboxing, because `$windexError` still takes boxed arguments. This +reboxing should usually float into the slow, diverging code path; but +sometimes (sadly) it doesn't: see Note [Reboxed crud for bottoming calls]. + +Here is another important case (B): + f x = Just x -- Suppose f is not inlined for some reason + -- Main point: f takes its argument boxed + + wombat x = error (show (f x)) + + g :: Bool -> Int -> a + g True x = x+1 + g False x = wombat x + +Again we want `wombat` to pretend to take its Int-typed argument unboxed, +even though it has to pass it boxed to `f`, so that `g` can take its +arugment unboxed (and rebox it before calling `wombat`). + +So here's what we do: while summarising `indexError`'s boxity signature in +`finaliseArgBoxities`: + +* To address (B), for bottoming functions, we start by using `unboxDeeplyDmd` + to make all its argument demands unboxed, right to the leaves; regardless + of what the analysis said. + +* To address (A), for bottoming functions, in the DontUnbox case when the + argument is a type variable, we /refrain/ from using trimBoxity. + (Remember the previous bullet: we have already doen `unboxDeeplyDmd`.) + +Wrinkle: + +* Remember Note [No lazy, Unboxed demands in demand signature]. So + unboxDeeplyDmd doesn't recurse into lazy demands. It's extremely unusual + to have lazy demands in the arguments of a bottoming function anyway. + But it can happen, when the demand analyser gives up because it + encounters a recursive data type; see Note [Demand analysis for recursive + data constructors]. Note [Reboxed crud for bottoming calls] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1617,7 +1657,11 @@ finaliseArgBoxities env fn arity rhs div -- simply want to give f the same demand signature as g | otherwise - = Just (arg_dmds', add_demands arg_dmds' rhs) + = -- pprTrace "finaliseArgBoxities" ( + -- vcat [text "function:" <+> ppr fn + -- , text "dmds before:" <+> ppr (map idDemandInfo (filter isId bndrs)) + -- , text "dmds after: " <+> ppr arg_dmds' ]) $ + Just (arg_dmds', add_demands arg_dmds' rhs) -- add_demands: we must attach the final boxities to the lambda-binders -- of the function, both because that's kosher, and because CPR analysis -- uses the info on the binders directly. @@ -1649,7 +1693,7 @@ finaliseArgBoxities env fn arity rhs div -- NB: even for bottoming functions, don't unbox dictionaries | is_bot_fn = unboxDeeplyDmd dmd - -- See Note [Boxity for bottoming functions] + -- See Note [Boxity for bottoming functions], case (B) | is_opaque = trimBoxity dmd -- See Note [OPAQUE pragma] @@ -1669,19 +1713,15 @@ finaliseArgBoxities env fn arity rhs div go_arg :: Budgets -> (Type,StrictnessMark,Demand) -> (Budgets, Demand) go_arg bg@(MkB bg_top bg_inner) (ty, str_mark, dmd@(n :* _)) = case wantToUnboxArg env ty str_mark dmd of - FD_Absent -> (bg, dmd) + DropAbsent -> (bg, dmd) - FD_Box | is_bot_fn -> (decremented_bg, dmd) - | otherwise -> (decremented_bg, trimBoxity dmd) + DontUnbox | is_bot_fn, isTyVarTy ty -> (decremented_bg, dmd) + | otherwise -> (decremented_bg, trimBoxity dmd) -- If bot: Keep deep boxity even though WW won't unbox - -- See Note [Boxity for bottoming functions] + -- See Note [Boxity for bottoming functions] case (A) -- trimBoxity: see Note [No lazy, Unboxed demands in demand signature] - FD_RecBox -> (decremented_bg, trimBoxity dmd) - -- Important: must do trimBoxity for FD_RecBox, even for bottoming fns, - -- else we unbox infinitely (simpl019, T11545, T18304, T4903) - - FD_Unbox triples -> (MkB (bg_top-1) final_bg_inner, final_dmd) + DoUnbox triples -> (MkB (bg_top-1) final_bg_inner, final_dmd) where (bg_inner', dmds') = go_args (incTopBudget bg_inner) triples -- incTopBudget: give one back for the arg we are unboxing @@ -1715,57 +1755,34 @@ finaliseLetBoxity env ty dmd go :: (Type,StrictnessMark,Demand) -> Demand go (ty, str, dmd@(n :* _)) = case wantToUnboxArg env ty str dmd of - FD_Absent -> dmd - FD_Box -> trimBoxity dmd - FD_RecBox -> trimBoxity dmd - FD_Unbox triples -> n :* (mkProd Unboxed $! map go triples) + DropAbsent -> dmd + DontUnbox -> trimBoxity dmd + DoUnbox triples -> n :* (mkProd Unboxed $! map go triples) --- The data type `FinalDecision` is entirely local to this one module, --- and is used only to express the result of `wantToUnboxArg`. --- --- `FinalDecision` is a bit like WorkWrap.Utils.UnboxingDecision, but --- not quite the same, notably because of the need for FD_RecBox. But --- I decided make `FinalDecision` do just what was needed, rather than --- to attempt to generalise `UnboxingDecision`, or make it GADT-ish or --- something. -data FinalDecision - = FD_Absent - | FD_Box -- Keep this argument boxed - | FD_RecBox -- Special case of a strict recursive product! Take care! - | FD_Unbox [(Type, StrictnessMark, Demand)] - -wantToUnboxArg :: AnalEnv -> Type -> StrictnessMark -> Demand -> FinalDecision +wantToUnboxArg :: AnalEnv -> Type -> StrictnessMark -> Demand + -> UnboxingDecision [(Type, StrictnessMark, Demand)] wantToUnboxArg env ty str_mark dmd@(n :* _) - | isAbs n - = FD_Absent - - | not (isStrict n || isMarkedStrict str_mark) - = FD_Box -- Don't unbox a lazy field - -- isMarkedStrict: see Note [Unboxing evaluated arguments] in DmdAnal - - | otherwise = case canUnboxArg (ae_fam_envs env) ty dmd of - DropAbsent -> FD_Absent - -- The earlier isAbs guard means this case won't happen, in fact; - -- but it does no harm. We can't drop the isAbs guard or we'd - -- wrongly return StopUnboxing for absent args. + DropAbsent -> DropAbsent + DontUnbox -> DontUnbox - StopUnboxing -> FD_Box - - Unbox (DataConPatContext{ dcpc_dc = dc - , dcpc_tc_args = tc_args - , dcpc_args = dmds }) + DoUnbox (DataConPatContext{ dcpc_dc = dc + , dcpc_tc_args = tc_args + , dcpc_args = dmds }) -- OK, so we /can/ unbox it; but do we /want/ to? + | not (isStrict n || isMarkedStrict str_mark) -- Don't unbox a lazy field + -- isMarkedStrict: see Note [Unboxing evaluated arguments] in DmdAnal + -> DontUnbox + | DefinitelyRecursive <- ae_rec_dc env dc -- See Note [Which types are unboxed?] -- and Note [Demand analysis for recursive data constructors] - -> FD_RecBox - - | otherwise - -> FD_Unbox (zip3 (dubiousDataConInstArgTys dc tc_args) - (dataConRepStrictness dc) - dmds) + -> DontUnbox + | otherwise -- Bad cases dealt with: we want to unbox! + -> DoUnbox (zip3 (dubiousDataConInstArgTys dc tc_args) + (dataConRepStrictness dc) + dmds) {- ********************************************************************* * * diff --git a/compiler/GHC/Core/Opt/WorkWrap/Utils.hs b/compiler/GHC/Core/Opt/WorkWrap/Utils.hs index f23384d134..d3f3928f7a 100644 --- a/compiler/GHC/Core/Opt/WorkWrap/Utils.hs +++ b/compiler/GHC/Core/Opt/WorkWrap/Utils.hs @@ -224,7 +224,8 @@ mkWwBodies opts fun_id arg_vars res_ty demands res_cpr init_str_marks = map (const NotMarkedStrict) cloned_arg_vars ; (useful1, work_args_str, wrap_fn_str, fn_args) - <- mkWWstr opts cloned_arg_vars init_str_marks + <- -- pprTrace "mkWWbodies" (ppr fun_id $$ ppr (arg_vars `zip` cloned_arg_vars) $$ ppr demands) $ + mkWWstr opts cloned_arg_vars init_str_marks ; let (work_args, work_marks) = unzip work_args_str @@ -567,13 +568,10 @@ data DataConPatContext s -- | Describes the outer shape of an argument to be unboxed or left as-is -- Depending on how @s@ is instantiated (e.g., 'Demand' or 'Cpr'). data UnboxingDecision unboxing_info - = StopUnboxing - -- ^ We ran out of strictness info. Leave untouched. - | DropAbsent - -- ^ The argument/field was absent. Drop it. - | Unbox !unboxing_info - -- ^ The argument is used strictly or the returned product - -- was constructed, so unbox it. + = DontUnbox -- ^ We ran out of strictness info. Leave untouched. + | DoUnbox !unboxing_info -- ^ The argument is used strictly or the + -- returned product was constructed, so unbox it. + | DropAbsent -- ^ The argument/field was absent. Drop it. -- Do we want to create workers just for unlifting? wwForUnlifting :: WwOpts -> Bool @@ -612,11 +610,11 @@ canUnboxArg fam_envs ty (n :* sd) , let arity = dataConRepArity dc , Just (Unboxed, dmds) <- viewProd arity sd -- See Note [Boxity analysis] , dmds `lengthIs` dataConRepArity dc - = Unbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args - , dcpc_co = co, dcpc_args = dmds }) + = DoUnbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args + , dcpc_co = co, dcpc_args = dmds }) | otherwise - = StopUnboxing + = DontUnbox -- | Unboxing strategy for constructed results. @@ -638,11 +636,11 @@ canUnboxResult fam_envs ty cpr -- Deactivates CPR worker/wrapper splits on constructors with non-linear -- arguments, for the moment, because they require unboxed tuple with variable -- multiplicity fields. - = Unbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args - , dcpc_co = co, dcpc_args = arg_cprs }) + = DoUnbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args + , dcpc_co = co, dcpc_args = arg_cprs }) | otherwise - = StopUnboxing + = DontUnbox where -- See Note [non-algebraic or open body type warning] @@ -809,7 +807,8 @@ mkWWstr :: WwOpts -- original RHS. Corresponds one-to-one -- with the wrapper arg vars mkWWstr opts args str_marks - = go args str_marks + = -- pprTrace "mkWWstr" (ppr args) $ + go args str_marks where go [] _ = return (badWorker, [], nop_fn, []) go (arg : args) (str:strs) @@ -833,29 +832,32 @@ mkWWstr_one :: WwOpts -> StrictnessMark -> UniqSM (Bool, [(Var,StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr) mkWWstr_one opts arg str_mark = + -- pprTrace "mkWWstr_one" (ppr arg <+> (if isId arg then ppr arg_ty $$ ppr arg_dmd else text "type arg")) $ case canUnboxArg fam_envs arg_ty arg_dmd of _ | isTyVar arg -> do_nothing DropAbsent | Just absent_filler <- mkAbsentFiller opts arg str_mark - -- Absent case. Dropt the argument from the worker. + -- Absent case. Drop the argument from the worker. -- We can't always handle absence for arbitrary -- unlifted types, so we need to choose just the cases we can -- (that's what mkAbsentFiller does) -> return (goodWorker, [], nop_fn, absent_filler) + | otherwise -> do_nothing - Unbox dcpc -> unbox_one_arg opts arg dcpc + DoUnbox dcpc -> -- pprTrace "mkWWstr_one:1" (ppr (dcpc_dc dcpc) <+> ppr (dcpc_tc_args dcpc) $$ ppr (dcpc_args dcpc)) $ + unbox_one_arg opts arg dcpc - _ | isStrictDmd arg_dmd || isMarkedStrict str_mark - , wwForUnlifting opts + DontUnbox + | isStrictDmd arg_dmd || isMarkedStrict str_mark + , wwForUnlifting opts -- See Note [CBV Function Ids] , not (isFunTy arg_ty) , not (isUnliftedType arg_ty) -- Already unlifted! -- NB: function arguments have a fixed RuntimeRep, -- so it's OK to call isUnliftedType here - -> -- See Note [CBV Function Ids] - return (goodWorker, [(arg, MarkedStrict)], nop_fn, varToCoreExpr arg ) + -> return (goodWorker, [(arg, MarkedStrict)], nop_fn, varToCoreExpr arg ) - _ -> do_nothing -- Other cases, like StopUnboxing + | otherwise -> do_nothing where fam_envs = wo_fam_envs opts @@ -954,7 +956,7 @@ function is worthy for splitting: 2. If the argument is evaluated strictly (or known to be eval'd), we can take a view into the product demand ('viewProd'). In accordance - with Note [Boxity analysis], 'canUnboxArg' will say 'Unbox'. + with Note [Boxity analysis], 'canUnboxArg' will say 'DoUnbox'. 'mkWWstr_one' then follows suit it and recurses into the fields of the product demand. For example @@ -976,7 +978,7 @@ function is worthy for splitting: $gw c a b = if c then a else b 2a But do /not/ unbox if Boxity Analysis said "Boxed". - In this case, 'canUnboxArg' returns 'StopUnboxing'. + In this case, 'canUnboxArg' returns 'DontUnbox'. Otherwise we risk decomposing and reboxing a massive tuple which is barely used. Example: @@ -997,7 +999,7 @@ function is worthy for splitting: 3. In all other cases (e.g., lazy, used demand and not eval'd), 'finaliseArgBoxities' will have cleared the Boxity flag to 'Boxed' (see Note [Finalising boxity for demand signatures] in GHC.Core.Opt.DmdAnal) - and 'canUnboxArg' returns 'StopUnboxing' so that 'mkWWstr_one' + and 'canUnboxArg' returns 'DontUnbox' so that 'mkWWstr_one' stops unboxing. Note [Worker/wrapper for bottoming functions] @@ -1391,7 +1393,7 @@ mkWWcpr_one :: WwOpts -> Id -> Cpr -> UniqSM CprWwResultOne -- ^ See if we want to unbox the result and hand off to 'unbox_one_result'. mkWWcpr_one opts res_bndr cpr | assert (not (isTyVar res_bndr) ) True - , Unbox dcpc <- canUnboxResult (wo_fam_envs opts) (idType res_bndr) cpr + , DoUnbox dcpc <- canUnboxResult (wo_fam_envs opts) (idType res_bndr) cpr = unbox_one_result opts res_bndr dcpc | otherwise = return (badWorker, unitOL res_bndr, varToCoreExpr res_bndr, nop_fn) |