GenBCode turned on by default
dropModule instead of deprecated stripModuleSuffix
Level 1 intra-method optimiz: ConstantFolder

Intra-method optimizations are performed until a fixpoint is reached.

Constant folding comprises:
  - loads that can be expressed as shorthand (e.g. ICONST_1)
  - binary operations with constant result are left in place,
    but a DROP and a load of the constant-result are inserted
    rigth after them. Successive iterations of the fixpoint will
    reduce code to its final form.
  - conditional jumps, as well as lookup and table switches
    are simplified when their operand is constant.
Level 1 intra-method optimiz: NullnessPropagator

Intra-method optimizations are performed until a fixpoint is reached.

Nullness propagation comprises:
  - simplify control-flow when a conditional jump
    will always be taken or avoided
  - simplify an ALOAD for a local-var known to contain null
  - Scala unbox of null is replaced by a zero load
    of the appropriate sort (I, L, F, or D).
Level 1 intra-method optimiz: Copy propagation

Intra-method optimizations are performed until a fixpoint is reached.

"Copy propagation" replaces the last link in a chain of data accesses
by a direct access to the chain-start.

A "chain of data accesses" refers to a assignments of the form shown below,
without any intervening rewriting of v, v1, v2, ..., v9:

      v1 =  v
       ...
      v2 = v1
       ...
      v9 = v8

After CopyPropagator.transform has run,
`LOAD v9` has been replaced with `LOAD v`.
Similarly for `LOAD v1` to `LOAD v8`.

Other than that, this method changes nothing else:
in particular STORE v1` to `STORE v9` are left as-is.
Removing dead-stores is the job of `DeadStoreElim`.
Level 1 intra-method optimiz: Dead Store Elimination
Level 1 intra-method optimiz: PushPopCollapser
Level 1 intra-method optimiz: JumpReducer
Level 1 intra-method optimiz: Type-Flow Analysis
Level 1 intra-method optimiz: cancel out box-unbox pairs
Level 1 intra-method optimiz: compacting method-local vars

"Compacting" of non-param local-vars involves:
   - detecting gaps in the index sequence, followed by
   - re-numbering that shifts indexes to close the gaps.

As already hinted, "compacting" excludes parameters.
Also, a local-var that at least gets initialized won't be removed.
intra-method optimizations activated by default
specializeTypes.specializedFunctionX for use by Late-Closure-Classes
fix to future-spec test, req for Late-Closure-Classes
Late Anonymous-Closure Classes: GenBCode dehydrates them (+ TODO)

Transform a Function node
  (x_1,...,x_n) => body
of type
  FunctionN[T_1, .., T_N, R]
into a Block expression of the form:

    {
      def hoisted(x_1: T_1, ..., x_N: T_n): R = body

       ScalaRunTime.lccMethodDisguiser(
         hoisted(zeroes-for-params-above)
       ).asInstanceOf[AbstractFunctionN[T_1, .., T_N, R]]
    }

where `ScalaRunTime.lccMethodDisguiser()` denotes a synthetic, artifact
method symbol created in Definitions, that thus doesn't occur
in user programs.

In place of the above, GenBCode will emit
under `-closurify:delegating` the following:
  - an anonymous closure class, and
  - its instantiation.

That way, the compiler has less work to do during specialize,
explicitouter, erasure, and lambdalift, among others.

The resulting anon-closure-classes have smaller constant pools,
which significantly shrinks output size (good for mobile devices).
Further classfile-size reductions can be obtained using -neo:o1
(ie intra-method optimizations, which lower the instruction count).

TODO an assertion is tripped in GenBCode upon compiling
    test/files/continuations-run/t3223.scala
  with options
    -P:continuations:enable -Xpluginsdir build/quick/misc/scala-devel/plugins -Xplugin-require:continuations
squash outer pointer of Late-Closure-Classes

During UnCurry, closureConversionModern() lowers a Function node into
a Block of the form:

    {
      def hoisted(x_1: T_1, ..., x_N: T_n): R = body

       ScalaRunTime.lccMethodDisguiser(
         hoisted(zeroes-for-params-above)
       ).asInstanceOf[AbstractFunctionN[T_1, .., T_N, R]]
    }

The "hoisted" method becomes a bytecode-level instance-method,
unless the class declaring it is an implementation-class derived
from a trait (in which case "hoisted" becomes
a bytecode-level static method).

An instance "hoisted" method usually implies that an outer-pointer
is required by the anon-closure-class (unless the class declaring
the "hoisted method" is a Scala-level static module, in which case
the anon-closure knows how to grab the correct instance via
GETSTATIC MODULE$ thus doing without an outer-pointer)

Oftentimes an instance "hoisted method" actually does not depend
on THIS, or depends only on lifted-methods (thus not part of
the public API) which themselves exhibit such property (not depend
on THIS or depend only on methods that don't actually require THIS).

In class LCCOuterSquasher, method squashOuterForLCC() detects
the largest subset of "hoisted methods" (aka endpoints)
that can be made static, under the constraint that only
lifted-methods can be made static. This constraint amounts
to leaving the public API unchanged.

Once that subset has been determined, all usages of those
"must-be-made-static" methods are found, and rewritten as appropriate.

Finally, each Late-Closure-Class amenable to this transformation
is refactored:
  - its $outer field is removed,
  - its constructor is adapted removing the param for outer-pointer.

Compiler flag -Ylog:jvm informs which anon-closures have
their outer-pointer squashed.
tracking Late Closure Classes
bytecode-repository for method-inlining purposes
making final methods we want to be inlined

The final status shows up in .check files for presentation tests,
in particular for the more complete output produced since commit
12dd8c02bbb71233f7b8ab9b2cbf8e84cda2080d

Thus those check files are updated in tandem with
the methods made final in this commit.
new optimizer: method inlining

The ICode-based inliner has a few problems:

  (a) closure elimination is implemented as repeated method inlinings.
      Upon being forced to stop with that (e.g., recursive method)
      none of the previous inlinings is undone, leaving both
      the closure class and a trail of duplicate code.

  (b) code may be inlined from third-party libraries or the JDK.
      In general methods not marked @inline may be inlined as discussed
      at the scala-internals thread "the perils of inlining"

  (c) invocation cycles (ie M1() calling M2() calling M1() etc)
      are "broken" only after hitting the maximum method size
      threshold, leaving a trail of duplicate code behind.

Instead, the new optimizer just follows a simple principle:

    only inline @inline-marked methods, and always inline them,
    including under separate-compilation

Thus the new inliner is deterministic, not dependent on heuristics
about method sizes or similar. The only additional requirement
(if you will) is for the method to be dispatched (the one marked
@inline) to be determined via the static type of the receiver.
For example, in a Range.foreach() callsite the type of the receiver
must be Range or subtype (in general, not a super type of another
which overrides the @inline method). After all, inlining is a conscious
decision: making that explicit via the type of the receiver is
 straightforward. As a result, the @noinline annotation doesn't play
a role anymore.

Under -Ylog:jvm , the new optimizer provides detailed logging about
performed inlinings, as well as diagnostics when inlining proves
unfeasible (down to the culprit bytecode instructions).