Note [CBV Function Ids]
A WorkerLikeId essentially allows us to constrain the calling convention for the given Id. Each such Id carries with it a list of CbvMarks with each element representing a value argument. Arguments who have a matching `MarkedCbv` entry in the list need to be passed evaluated+*properly tagged*. CallByValueFunIds give us additional expressiveness which we use to improve runtime. This is all part of the EPT enforcement work. See also Note [EPT enforcement]. They allows us to express the fact that an argument is not only evaluated to WHNF once we entered it's RHS but also that an lifted argument is already *properly tagged* once we jump into the RHS. This means when e.g. branching on such an argument the RHS doesn't needed to perform an eval check to ensure the argument isn't an indirection. All seqs on such an argument in the functions body become no-ops as well. The invariants around the arguments of call by value function like Ids are then: * In any call `(f e1 .. en)`, if `f`'s i'th argument is marked `MarkedCbv`, then the caller must ensure that the i'th argument * points directly to the value (and hence is certainly evaluated before the call) * is a properly tagged pointer to that value * The following functions (and only these functions) have `CbvMarks`: * Any `WorkerLikeId` * Some `JoinId` bindings. This works analogous to the EPT Invariant. See also Note [EPT enforcement]. To make this work what we do is: * During W/W and SpecConstr any worker/specialized binding we introduce is marked as a worker binding by `asWorkerLikeId`. * W/W and SpecConstr further set OtherCon[] unfoldings on arguments which represent contents of a strict fields. * During Tidy we look at all bindings. For any callByValueLike Id and join point we mark arguments as cbv if they Are strict. We don't do so for regular bindings. See Note [Use CBV semantics only for join points and workers] for why. We might have made some ids rhs *more* strict in order to make their arguments be passed CBV. See Note [Call-by-value for worker args] for why. * During CorePrep calls to CallByValueFunIds are eta expanded. * During Stg CodeGen: * When we see a call to a callByValueLike Id: * We check if all arguments marked to be passed unlifted are already tagged. * If they aren't we will wrap the call in case expressions which will evaluate+tag these arguments before jumping to the function. * During Cmm codeGen: * When generating code for the RHS of a StrictWorker binding we omit tag checks when using arguments marked as tagged. We only use this for workers and specialized versions of SpecConstr But we also check other functions during tidy and potentially turn some of them into call by value functions and mark some of their arguments as call-by-value by looking at argument unfoldings. NB: I choose to put the information into a new Id constructor since these are loaded at all optimization levels. This makes it trivial to ensure the additional calling convention demands are available at all call sites. Putting it into IdInfo would require us at the very least to always decode the IdInfo just to decide if we need to throw it away or not after.
References 3
- Call-by-value for worker args GHC.Core.Utils
- EPT enforcement GHC.Stg.EnforceEpt
- Use CBV semantics only for join points and workers GHC.Types.Id.Info
Referenced by 23
- EPT enforcement GHC.Stg.EnforceEpt ×4
- GHC.Core.Tidy call site ×3
- Attaching CBV Marks to ids GHC.Core.Tidy ×2
- GHC.Types.Id call site ×2
- GHC.Types.Id.Info call site ×2
- Eta reduction soundness GHC.Core.Opt.Arity
- GHC.Core.Opt.Arity call site
- GHC.Core.Opt.WorkWrap.Utils call site
- Absent fillers GHC.Core.Opt.WorkWrap.Utils
- WW for calling convention GHC.Core.Opt.WorkWrap.Utils
- Worker/wrapper for strict arguments GHC.Core.Opt.WorkWrap.Utils
- Call-by-value for worker args GHC.Core.Utils
- GHC.Core.Utils call site
- GHC.CoreToStg.Prep call site
- GHC.Stg.Lint call site