On Nov 6, 2010, at 11:38 AM, Rémi Forax wrote:

> I've wanted to write that several times, so before doing something else,
> I want to try to convince all VM hackers that they should stop doing
> what they currently do and starts to implement a way to export
> values from the VM stack into a Java object.
> ...

Yes, JVM languages should be able to do the "deoptimization trick".  It works 
great for the JVM itself.

Here is a bytecode shape that might make it possible to do so, without the API 
you mentioned:

myfn(a,b,c) {
  var i,j,k;  // init to null if needed
  try {
    entry point of my fn...
    ... more stuff ...
    ... if (bail out #1) throw makeDeopt("cookie #1");
    ... more stuff ...
    ... if (bail out #2) throw makeDeopt("cookie #2");
    ... more stuff ...
    return x;
  } catch (MyDeopt deopt) {
     return executeDeopt(deopt, new Object[]{ a, b, c, i, j, k });
  }
}

Here, the cookies passed into the deopts are simple constants.  This (probably) 
simplifies data movement, since the complicated bundling is done once, at the 
catch point.  The local variable values could also be bundled up as arguments 
to makeDeopt.  The throws could be replaced by gotos, but JITs are more likely 
to optimize the code the way you want if you use a throw (which the JIT will 
probably guess as uncommon).

Would this work for you?

Another problem here is where the executeDeopt guy should go.

Probably you want to fake up some AST interpreter state that expresses the 
pending continuations (for enclosing expressions and callers of inlined 
functions).  Then the AST engine, after it digests the app. some more, can 
generate new bytecodes.

Another option would be to generate bytecodes with multiple entry points.  This 
cannot be directly expressed but could be encoded with a leading pseudo-BCI 
parameter, with a switch at the entry point.  Again, the above "i,j,k" 
structure would be needed.  And all the entry points would have to have a 
common functional type.

Here's an idea:  For each byte-compiled method, compile the normal optimized 
version, and also compile a "fallback" version which can be used in corner 
cases:

myfn(int bci, args[], locals[]) {
  var a=args[0], ...;
  var i=locals[0],j,k;  // init to null if locals = null
  try {
    switch (bci) {
    case 0:
    entry point of my fn...
    case 1:
    ... more stuff ...
    ... if (bail out #1) throw makeDeopt("cookie #1");
    case 2:
    ... more stuff ...
    ... if (bail out #2) throw makeDeopt("cookie #2");
    case 3:
    ... more stuff ...
    return x;
    }
  } catch (MyDeopt deopt) {
     return executeDeopt(deopt, new Object[]{ a, b, c, i, j, k });
  }
}

The fallback could be used for type misses and untaken paths.  It could also be 
used for continuations (if your language has those).  The advantage of a 
pre-compiled fallback would be that you would reuse your bytecode compilation 
analysis to improve more execution paths, not just the fastest.  If that 
doesn't matter, a AST-based fallback is perhaps easier to work with.

-- John
_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to