Note [WW for calling convention]

GHC/Core/Opt/WorkWrap/Utils.hs:871 compiler 1 ticket

If we know a function f will always evaluate a particular argument
we might decide that it should rather get evaluated by the caller.
We call this "unlifting" the argument.
Sometimes the caller knows that the argument is already evaluated,
so we won't generate any code to enter/evaluate the argument.
This evaluation avoidance can be quite beneficial.
Especially for recursive functions who pass the same lifted argument
along on each iteration or walk over strict data structures.

One way to achieve this is to do a W/W split, where the wrapper does
the evaluation, and the worker can treat its arguments as unlifted.
The wrapper is small and will be inlined at almost all call sites and
the evaluation code in the wrapper can then cancel out with evaluation
done by the calling context if the argument is evaluated there.
Same idea as W/W to avoid allocation really, just for a different kind
of work.

Performing W/W might not always be a win. In particular it's easy to break
(badly written, but common) rule frameworks by doing additional W/W splits.
See #20364 for a more detailed explanation.

Hence we have the following strategies with different trade-offs:

A) Never do W/W *just* for unlifting of arguments.
  + Very conservative - doesn't break any rules
  - Lot's of performance left on the table

B) Do W/W on just about anything where it might be
  beneficial.
  + Exploits pretty much every opportunity for unlifting.
  - A bit of compile time/code size cost for all the wrappers.
  - Can break rules which would otherwise fire. See #20364.

C) Unlift *any* (non-boot exported) functions arguments if they are strict.
  That is instead of creating a Worker with the new calling convention we
  change the calling convention of the binding itself.
  + Exploits every opportunity for unlifting.
  + Maybe less bad interactions with rules.
  - Requires tracking of boot-exported definitions.
  - Requires either:
    ~ Eta-expansion at *all* call sites in order to generate
      an impedance matcher function. Leading to massive code bloat.
      Essentially we end up creating a impromptu wrapper function
      wherever we wouldn't inline the wrapper with a W/W approach.
    ~ There is the option of achieving this without eta-expansion if we instead expand
      the partial application code to check for demands on the calling convention and
      for it to evaluate the arguments. The main downsides there would be the complexity
      of the implementation and that it carries a certain overhead even for functions who
      don't take advantage of this functionality. I haven't tried this approach because it's
      not trivial to implement and doing W/W splits seems to work well enough.

Currently we use the first approach A) by default, with a flag that allows users to fall back to the
more aggressive approach B).

I also tried the third approach C) using eta-expansion at call sites to avoid modifying the PAP-handling
code which wasn't fruitful. See https://gitlab.haskell.org/ghc/ghc/-/merge_requests/5614#note_389903.
We could still try to do C) in the future by having PAP calls which will evaluate the required arguments
before calling the partially applied function. But this would be neither a small nor simple change so we
stick with A) and a flag for B) for now.

See also Note [EPT enforcement] and Note [CBV Function Ids]

References 2

Referenced by 3