ConstantOptimization uses abstract interpretation to approximate for each instruction what constants a variable or stack slot might hold or cannot hold.
This optimization phase inlines the exception handlers so that further phases can optimize the code better
This optimization phase inlines the exception handlers so that further phases can optimize the code better
try { ... if (condition) throw IllegalArgumentException("sth") } catch { case e: IllegalArgumentException => <handler code> case e: ... => ... }
will inline the exception handler code to:
try { ... if (condition) <handler code> // + jump to the end of the catch statement } catch { case e: IllegalArgumentException => <handler code> case e: ... => ... }
Q: How does the inlining work, ICode level? A: if a block contains a THROW(A) instruction AND there is a handler that takes A or a superclass of A we do:
Q: Why do we need to duplicate the handler? A: An exception might be thrown in a method that we invoke in the function and we cannot see that THROW command directly. In order to catch such exceptions, we keep the exception handler in place and duplicate it in order to inline its code.
Inliner balances two competing goals: (a) aggressive inlining of: (a.1) the apply methods of anonymous closures, so that their anon-classes can be eliminated; (a.2) higher-order-methods defined in an external library, e.g.
Inliner balances two competing goals:
(a) aggressive inlining of:
(a.1) the apply methods of anonymous closures, so that their anon-classes can be eliminated;
(a.2) higher-order-methods defined in an external library, e.g. Range.foreach()
among many others.
(b) circumventing the barrier to inter-library inlining that private accesses in the callee impose.
Summing up the discussion in SI-5442 and SI-5891, the current implementation achieves to a large degree both goals above, and overcomes a problem exhibited by previous versions:
(1) Problem: Attempting to access a private member p
at runtime resulting in an IllegalAccessError
,
where p
is defined in a library L, and is accessed from a library C (for Client),
where C was compiled against L', an optimized version of L where the inliner made p
public at the bytecode level.
The only such members are fields, either synthetic or isParamAccessor, and thus having a dollar sign in their name
(the accessibility of methods and constructors isn't touched by the inliner).
Thus we add one more goal to our list: (c) Compile C (either optimized or not) against any of L or L', so that it runs with either L or L' (in particular, compile against L' and run with L).
The chosen strategy is described in some detail in the comments for accessRequirements()
and potentiallyPublicized()
.
Documentation at http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2011Q4/Inliner.pdf
ConstantOptimization uses abstract interpretation to approximate for each instruction what constants a variable or stack slot might hold or cannot hold. From this it will eliminate unreachable conditionals where only one branch is reachable, e.g. to eliminate unnecessary null checks.
With some more work it could be extended to - cache stable values (final fields, modules) in locals - replace the copy propagation in ClosureElimination - fold constants - eliminate unnecessary stores and loads - propagate knowledge gathered from conditionals for further optimization