| java.lang.Object org.apache.derby.impl.services.bytecode.CodeChunk
CodeChunk | final class CodeChunk (Code) | | This class represents a chunk of code in a CodeAttribute.
Typically, a CodeAttribute represents the code in a method.
If there is a try/catch block, each catch block will get its
own code chunk. This allows the catch blocks to all be put at
the end of the generated code for a method, which eliminates
the need to generate a jump around each catch block, which
would be a forward reference.
|
Method Summary | |
void | addInstr(short opcode) Add an instruction that has no operand. | void | addInstrCPE(short opcode, int cpeNum) This takes an instruction that has a narrow
and a wide form for CPE access, and
generates accordingly the right one. | void | addInstrU1(short opcode, int operand) Add an instruction that has an 8 bit operand. | void | addInstrU2(short opcode, int operand) Add an instruction that has a 16 bit operand. | void | addInstrU2U1U1(short opcode, int operand1, short operand2, short operand3) For adding an instruction with 3 operands, a U2 and two U1's. | void | addInstrU4(short opcode, int operand) Add an instruction that has a 32 bit operand. | void | addInstrWide(short opcode, int varNum) This takes an instruction that can be wrapped in
a wide for large variable #s and does so. | void | complete(BCMethod mb, ClassHolder ch, ClassMember method, int maxStack, int maxLocals) wrap up the entry and stuff it in the class,
now that it holds all of the instructions and
the exception table. | short | getOpcode(int pc) Return the opcode at the given pc. | int | getPC() | CodeChunk | insertCodeSpace(int pc, int additionalBytes) Insert room for byteCount bytes after the instruction at pc
and prepare to replace the instruction at pc. | final int | splitExpressionOut(BCMethod mb, ClassHolder ch, int optimalMinLength, int maxStack) Split an expression out of a large method into its own
sub-method.
Method call expressions are of the form:
- expr.method(args) -- instance method call
- method(args) -- static method call
Two special cases of instance method calls will be handled
by the first incarnation of splitExpressionOut. | final int | splitZeroStack(BCMethod mb, ClassHolder ch, int split_pc, int optimalMinLength) Attempt to split the current method by pushing a chunk of
its code into a sub-method. |
ARRAY_ACCESS | final static short ARRAY_ACCESS(Code) | | |
ARRAY_STORE | final static short ARRAY_STORE(Code) | | |
CAST_CONVERSION_INFO | final static short CAST_CONVERSION_INFO(Code) | | |
LOAD_VARIABLE | final static short[] LOAD_VARIABLE(Code) | | |
LOAD_VARIABLE_FAST | final static short[] LOAD_VARIABLE_FAST(Code) | | |
RETURN_OPCODE | final static short[] RETURN_OPCODE(Code) | | |
STORE_VARIABLE | final static short[] STORE_VARIABLE(Code) | | |
STORE_VARIABLE_FAST | final static short[] STORE_VARIABLE_FAST(Code) | | |
cb | final BCClass cb(Code) | | The class we are generating code for, used to indicate that
some limit was hit during code generation.
|
addInstr | void addInstr(short opcode)(Code) | | Add an instruction that has no operand.
All opcodes are 1 byte large.
|
addInstrCPE | void addInstrCPE(short opcode, int cpeNum)(Code) | | This takes an instruction that has a narrow
and a wide form for CPE access, and
generates accordingly the right one.
We assume the narrow instruction is what
we were given, and that the wide form is
the next possible instruction.
|
addInstrU1 | void addInstrU1(short opcode, int operand)(Code) | | Add an instruction that has an 8 bit operand.
|
addInstrU2 | void addInstrU2(short opcode, int operand)(Code) | | Add an instruction that has a 16 bit operand.
|
addInstrU2U1U1 | void addInstrU2U1U1(short opcode, int operand1, short operand2, short operand3)(Code) | | For adding an instruction with 3 operands, a U2 and two U1's.
So far, this is used by VMOpcode.INVOKEINTERFACE.
|
addInstrU4 | void addInstrU4(short opcode, int operand)(Code) | | Add an instruction that has a 32 bit operand.
|
addInstrWide | void addInstrWide(short opcode, int varNum)(Code) | | This takes an instruction that can be wrapped in
a wide for large variable #s and does so.
|
complete | void complete(BCMethod mb, ClassHolder ch, ClassMember method, int maxStack, int maxLocals)(Code) | | wrap up the entry and stuff it in the class,
now that it holds all of the instructions and
the exception table.
|
getOpcode | short getOpcode(int pc)(Code) | | Return the opcode at the given pc.
|
getPC | int getPC()(Code) | | Get the current program counter
|
insertCodeSpace | CodeChunk insertCodeSpace(int pc, int additionalBytes)(Code) | | Insert room for byteCount bytes after the instruction at pc
and prepare to replace the instruction at pc. The instruction
at pc is not modified by this call, space is allocated after it.
The newly inserted space will be filled with NOP instructions.
Returns a CodeChunk positioned at pc and available to write
instructions upto (byteCode + length(existing instruction at pc) bytes.
This chunk is left correctly positioned at the end of the code
stream, ready to accept more code. Its pc will have increased by
additionalBytes.
It is the responsibility of the caller to patch up any
branches or gotos.
Parameters: pc - Parameters: additionalBytes - |
splitExpressionOut | final int splitExpressionOut(BCMethod mb, ClassHolder ch, int optimalMinLength, int maxStack)(Code) | | Split an expression out of a large method into its own
sub-method.
Method call expressions are of the form:
- expr.method(args) -- instance method call
- method(args) -- static method call
Two special cases of instance method calls will be handled
by the first incarnation of splitExpressionOut.
three categories:
- this.method(args)
- this.getter().method(args)
These calls are choosen as they are easier sub-cases
and map to the code generated for SQL statements.
Future coders can expand the method to cover more cases.
This method will split out such expressions in sub-methods
and replace the original code with a call to that submethod.
- this.method(args) ->> this.sub1([parameters])
- this.getter().method(args) ->> this.sub1([parameters])
The assumption is of course that the call to the sub-method
is much smaller than the code it replaces.
Looking at the byte code for such calls they would look like
(for an example three argument method):
this arg1 arg2 arg3 INVOKE // this.method(args)
this INVOKE arg1 arg2 arg3 INVOKE // this.getter().metod(args)
The bytecode for the arguments can be arbitary long and
consist of expressions, typical Derby code for generated
queries is deeply nested method calls.
If none of the arguments requred the parameters passed into
the method, then in both cases the replacement bytecode
would look like:
this.sub1();
Parameter handling is just as in the method splitZeroStack().
Because the VM is a stack machine the original byte code
sequences are self contained. The stack at the start of
is sequence is N and at the end (after the method call) will
be:
- N - void method
- N + 1 - method returning a single word
- N + 2 - method returning a double word (java long or double)
This code will handle the N+1 where the word is a reference,
the typical case for generated code.
The code is self contained because in general the byte code
for the arguments will push and pop values but never drop
below the stack value at the start of the byte code sequence.
E.g. in the examples the stack before the first arg will be
N+1 (the objectref for the method call) and at the end of the
byte code for arg1 will be N+2 or N+3 depending on if arg1 is
a single or double word argument. During the execution of
the byte code the stack may have had many arguments pushed
and popped, but will never have dropped below N+1. Thus the
code for arg1 is independent of the stack's previous values
and is self contained. This self-containment then extends to
all the arguements, the method call itself and pushing the
objectref for the method call, thus the complete
sequence is self-contained.
The self-containment breaks in a few cases, take the simple
method call this.method(3), the byte code for this could be:
push3 this swap invoke
In this case the byte code for arg1 (swap) is not self-contained
and relies on earlier stack values.
How to identify "self-contained blocks of code".
We walk through the byte code and maintain a history of
the program counter that indicates the start of the
independent sequence each stack word depends on.
Thus for a ALOAD_0 instruction which pushes 'this' the
dependent pc is that of the this. If a DUP instruction followed
then the top-word is now dependent on the previous word (this)
and thus the dependence of it is equal to the dependence of
the previous word. This information is kept in earliestIndepPC
array as we process the instruction stream.
When a INVOKE instruction is seen for an instance method
that returns a single or double word, the dependence of
the returned value is the dependence of the word in the
stack that is the objectref for the call. This complete
sequence from the pc the objectref depended on to the
INVOKE instruction is then a self contained sequence
and can be split into a sub-method.
If the block is self-contained then it can be split, following
similar logic to splitZeroStack().
WORK IN PROGRESS - Incremental development
Currently walks the method maintaining the
earliestIndepPC array and identifies potential blocks
to splt, performs splits as required.
Called by BCMethod but commented out in submitted code.
Tested with local changes from calls in BCMethod.
Splits generally work, though largeCodeGen shows
a problem that will be fixed before the code in
enabled for real.
|
splitZeroStack | final int splitZeroStack(BCMethod mb, ClassHolder ch, int split_pc, int optimalMinLength)(Code) | | Attempt to split the current method by pushing a chunk of
its code into a sub-method. The starting point of the split
(split_pc) must correspond to a stack depth of zero. It is the
reponsibility of the caller to ensure this.
Split is only made if there exists a chunk of code starting at
pc=split_pc, whose stack depth upon termination is zero.
The method will try to split a code section greater than
optimalMinLength but may split earlier if no such block exists.
The method is aimed at splitting methods that contain
many independent statements.
If a split is possible this method will perform the split and
create a void sub method, and move the code into the sub-method
and setup this method to call the sub-method before continuing.
This method's max stack and current pc will be correctly set
as though the method had just been created.
Parameters: mb - Method for this chunk. Parameters: ch - Class definition Parameters: optimalMinLength - minimum length required for split |
|
|