Note [Call-by-value for worker args]
If we unbox a constructor with strict fields we want to preserve the information that some of the arguments came out of strict fields and therefore should be already properly tagged, however we can't express this directly in core. Instead what we do is generate a worker like this: data T = MkT A !B foo = case T of MkT a b -> $wfoo a b $wfoo a b = case b of b' -> rhs[b/b'] This makes the worker strict in b causing us to use a more efficient calling convention for `b` where the caller needs to ensure `b` is properly tagged and evaluated before it's passed to $wfoo. See Note [CBV Function Ids]. Usually the argument will be known to be properly tagged at the call site so there is no additional work for the caller and the worker can be more efficient since it can assume the presence of a tag. This is especially true for recursive functions like this: myPred expect it's argument properly tagged myPred !x = ... loop :: MyPair -> Int loop (MyPair !x !y) = case x of A -> 1 B -> 2 _ -> loop (MyPair (myPred x) (myPred y)) Here we would ordinarily not be strict in y after unboxing. However if we pass it as a regular argument then this means on every iteration of loop we will incur an extra seq on y before we can pass it to `myPred` which isn't great! That is in STG after tag inference we get: Rec { Find.$wloop [InlPrag=[2], Occ=LoopBreaker] :: Find.MyEnum -> Find.MyEnum -> GHC.Prim.Int# [GblId[StrictWorker([!, ~])], Arity=2, Str=<1L><ML>, Unf=OtherCon []] = {} \r [x y] case x<TagProper> of x' [Occ=Once1] { __DEFAULT -> case y of y' [Occ=Once1] { __DEFAULT -> case Find.$wmyPred y' of pred_y [Occ=Once1] { __DEFAULT -> case Find.$wmyPred x' of pred_x [Occ=Once1] { __DEFAULT -> Find.$wloop pred_x pred_y; }; }; Find.A -> 1#; Find.B -> 2#; }; end Rec } Here comes the tricky part: If we make $wloop strict in both x/y and we get: Rec { Find.$wloop [InlPrag=[2], Occ=LoopBreaker] :: Find.MyEnum -> Find.MyEnum -> GHC.Prim.Int# [GblId[StrictWorker([!, !])], Arity=2, Str=<1L><!L>, Unf=OtherCon []] = {} \r [x y] case y<TagProper> of y' [Occ=Once1] { __DEFAULT -> case x<TagProper> of x' [Occ=Once1] { __DEFAULT -> case Find.$wmyPred y' of pred_y [Occ=Once1] { __DEFAULT -> case Find.$wmyPred x' of pred_x [Occ=Once1] { __DEFAULT -> Find.$wloop pred_x pred_y; }; }; Find.A -> 1#; Find.B -> 2#; }; end Rec } Here both x and y are known to be tagged in the function body since we pass strict worker args using unlifted cbv. This means the seqs on x and y both become no-ops and compared to the first version the seq on `y` disappears at runtime. The downside is that the caller of $wfoo potentially has to evaluate `y` once if we can't prove it isn't already evaluated. But y coming out of a strict field is in WHNF so safe to evaluated. And most of the time it will be properly tagged+evaluated already at the call site because of the EPT Invariant! See Note [EPT enforcement] for more in this. This makes GHC itself around 1% faster despite doing slightly more work! So this is generally quite good. We only apply this when we think there is a benefit in doing so however. There are a number of cases in which it would be useless to insert an extra seq. ShouldStrictifyIdForCbv tries to identify these to avoid churn in the simplifier. See Note [Which Ids should be strictified] for details on this.
References 3
- Which Ids should be strictified GHC.Core.Utils
- EPT enforcement GHC.Stg.EnforceEpt
- CBV Function Ids GHC.Types.Id.Info
Referenced by 6
- GHC.Core.Opt.WorkWrap.Utils call site ×2
- GHC.Core.Opt.Simplify.Iteration call site
- SpecConstr and strict fields GHC.Core.Opt.SpecConstr
- Which Ids should be strictified GHC.Core.Utils
- CBV Function Ids GHC.Types.Id.Info