===========================================
REDUCERON MEMO 35
Order of compilation passes: when to inline
Matthew N, 5 October 2009
===========================================

Consider the Cons-Cons case of the equalStrings function that Colin
discussed in Memo 34.

  equalStrings (Cons x xs) (Cons y ys) = con ((==) x y) (equalStrings xs ys);

The function con is boolean conjunction.

  con x y = case x of { False -> False; True -> y };

Compiling away the case expressions in con, as discussed in Memo 13,
gives:

  con x y = x [con#1,con#2] y;
  con#1 alts y = False;
  con#2 alts y = y;

In the Cons-Cons case of equalStrings, the call to con can be inlined.
The question is: should inlining be performed before case compilation,
or after, or both before and after?

If case compilation is performed first, followed by inlining, then the
body of the Cons-Cons case becomes

  (==) x y [con#1,con#2] (equalStrings xs ys);

At runtime, the expression "equalStrings xs ys" must be created on the
heap, and subsequently, if ever demanded, unwound from the heap.

Alternatively, if inlining is performed first then body of Cons-Cons
case first becomes

  case (==) x y of { False -> False ; True -> equalStrings xs ys };

and then after case elimination

  (==) x y [consCons#1, consCons#2] xs ys;

where

  consCons#1 alts xs ys = False;
  consCons#2 alts xs ys = equalStrings xs ys;

Now the expression "equalStrings xs ys" is never created on the heap.
Since it is in the spine position of a function body, it is only ever
pushed onto the stack, and even then, only whenever the first conjunct
is True.

So the order makes quite a big difference.  How does it affect
performance?  Here are some clock-tick counts on a handfull of
programs.  The inlining parameter to the F-lite compiler is "-i1",
i.e. only functions containing a maximum of one application are
inlined.

  +----------------+-----------+-----------+----------------+
  | PROGRAM        |     AFTER |    BEFORE | BEFORE & AFTER |
  +----------------+-----------+-----------+----------------+
  | EqualStrings   |       471 |       433 |            394 |
  | PermSort       |  17860578 |  20529552 |       16715749 |
  | Queens         |  48337765 |  43739670 |       41241925 |
  | While          |  46327136 |  50576148 |       46327136 |
  | Cichelli       |  55795881 |  61663685 |       55795880 |
  | Mate           | 856693903 | 849056292 |      798054513 |
  | KnuthBendix    |  19619755 |  22907202 |       19061795 |
  +----------------+-----------+-----------+----------------+

Notice that two cycles per recursive step are saved in the
equalStrings functon when inlining before & after: one is saved when
creating the recursive application, and the other when unwinding it.

Remarks:

  * Are there any programs where inlining before case compilation is a
    bad idea?  (Sometimes, one large function body might be preferable
    to serveral small ones.)

  * Is there any added benefit in increasing the "-i" control parameter?

  * The control parameters for inlining might usefully be different
    depending on whether it is done before or after case compilation.
