db-derby-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel John Debrunner <...@apache.org>
Subject Re: Code generation into multiple methods to avoid 64k code limit
Date Thu, 16 Feb 2006 18:49:21 GMT

The next step as mentioned before is to split methods in the cases where
the stack does not drop to zero, these are typically methods that return
a language ResultSet and have deeply nested method calls.

[ Again just a dump of thoughts and assumes knowledge of the JVM spec
which describes the byte code instructions and how they are executed. ]


My thinking is this:

Walk the bytecode maintaining a history of the program counter of the
last instruction that pushed words onto the stack by stack depth. E.g.
it might be
     stack depth    pc
        1           0
        2           6
        3           19
        4           28

So, the instruction at pc = 0 pushed a word to make the stack depth 1,
then the instruction at pc=6 pushed a word to make the stack depth 2 etc.

Then when walking the code if certain instructions are seen that have
the potential be the end of a self contained block more analysis is
done, these instructions correspond to method calls and field assignments.

The analysis looks at the number of words the instruction pushes off the
stack, using the history above then one can determine from the current
pc how big the block is and if it's worth splitting it. If not then
continue searching, if it is then more analysis is required.

So if the current instruction is at pc = 78, the current stack is 7 and
it pushes 5 words off the stack, then the potentially self contained
block is at least 59 bytes (78 - 19), since the last word popped of the
stack would be the word that was pushed when the stack depth last grew
to 3. (words 3,4,5,6,7 are popped of the stack).

Assuming this was a good block of code to push into a sub-method
(typically thought the block to be pushed would be several kbytes, this
is just a simple example) then they type of the method call or put field
instruction needs to be looked at.

This actually breaks into two cases, static method/field and instance
method/field.

For the static method, the block defined is self contained, this
corresponds to a Java language call like:

          MyClass.mymethod(a(), b(), f(), g(f(2)), "hello")

This is not a separate statement, just a self contained call, its value
is most likely being passed into another method call.
Thus this block can be pushed into a sub-method and the in-line code
replaced with a call to the sub-method, returning the same type as the
method call.

In the instance case the instruction that created the reference used to
call the method needs to be inspected. In Java byte code the instance
reference is the fist item pushed in a method call or field assignment
sequence, it is then followed by the arguments for the method call, or
the value for the field assignment. In this simple case it would be the
instruction at pc 19, if this is a self contained instruction, like
ALOAD_0 (push 'this') or a load corresponding to a parameter then the
block is self contained and can be split. An example of this in Java
langauge would be

        this.mymethod2(a(), b(), f(), g(f(2)), "hello")

or
        // p1 is a parameter to the method
        p1.mymethod2(a(), b(), f(), g(f(2)), "hello")


Unfortunately I think the above two cases are rare in the way we
generate byte code, but I will code them up as a step onto the more
complicated case. If there are some cases where the split will occur
then I will commit as a separate checkin.

The more complicated case is when the reference for the method call is
obtained from method call or field access, and this is the typical case,
e.g.:

        this.getResultSetFactory()  .   getInsertResultSet(...)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^^^^^^^
                reference                    method call

The instruction for the method call to getInsertResultSet is the one
that has the potential for a lot of code that could be moved and the one
the above analysis begins on, but its reference is
this.getResultSetFactory(). Thus the split code needs to look futher
back in the byte code instruction to find the self contained block for
that method call. Of course a longer sequence could be something like:

       this.a().b().c(e()).mymethod2(a(), b(), f(), g(f(2)), "hello")

So the analysis needs to back track until it finds a self contained
block, anchored by a static call/field, 'this' or a parameter value.
There are probably two ways to do this:

   - recursive walking of the byte array, e.g. in this example walk the
byte code again from 0 to 19 looking for the block of code that defines
the reference and continuing scans from 0 to the next until the anchor
point is found. Each ending point of this scan is less than the starting
point of the previous.

   - maintain more history of stack depth transistions than the last
transition to stack depth N.

(one issue is here that I think the byte code stream can only be walked
forward, you can not be at pc=100 and figure out if the previous
instruction starts at pc 99, 98,97, 96 or 95.

Have't thought in detail what the best way here is, want to get the
simple cases solved first.

With any of these non-zero stack depth splits there are some minor
complexities to work through, one is to ensure the arguments after the
split point do not depend on code before the split point. Which is
really do they depend on stack values before the split point, in the
exzample above there are two values on the stack at the split point.
Does the instruction stream depend on them. I don't think the byte code
compiler generates code that makes that the case, but I will check on
it, basically one has to look for instructions that can use those values
while leaving them on the stack. Examples of instructions that need to
be handled carefully are DUP_X2, duplicate the top operand stack word
and push three down. The zero stack split case did not have to be
concerned about this as the stack depth was zero, so no values could be
accessed.

Dan.





Mime
View raw message