final class CodeChunk
extends java.lang.Object
Modifier and Type | Field | Description |
---|---|---|
(package private) static short[] |
ARRAY_ACCESS |
|
(package private) static short[] |
ARRAY_STORE |
|
(package private) static short[][][] |
CAST_CONVERSION_INFO |
|
(package private) BCClass |
cb |
The class we are generating code for, used to indicate that
some limit was hit during code generation.
|
private static int |
CODE_OFFSET |
Starting point of the byte code stream in the underlying stream/array.
|
private ClassFormatOutput |
cout |
|
(package private) static short[] |
LOAD_VARIABLE |
|
(package private) static short[] |
LOAD_VARIABLE_FAST |
|
private static byte[] |
NS |
Constant used by OPCODE_ACTION to the opcode is
not yet supported.
|
private static byte[][] |
OPCODE_ACTION |
Array that provides two pieces of information about
each VM opcode.
|
private int |
pcDelta |
The delta between cout.size() and the pc.
|
private static byte[] |
push1_1i |
Constant used by OPCODE_ACTION to represent the
common action of push one word, 1 byte
for the instruction.
|
private static byte[] |
push2_1i |
Constant used by OPCODE_ACTION to represent the
common action of push two words, 1 byte
for the instruction.
|
(package private) static short[] |
RETURN_OPCODE |
|
(package private) static short[] |
STORE_VARIABLE |
|
(package private) static short[] |
STORE_VARIABLE_FAST |
|
private static byte |
VARIABLE_STACK |
Value for OPCODE_ACTION[opcode][0] to represent
the number of words popped or pushed in variable.
|
Modifier | Constructor | Description |
---|---|---|
(package private) |
CodeChunk(BCClass cb) |
|
private |
CodeChunk(CodeChunk main,
int pc,
int byteCount) |
Return a CodeChunk that has limited visibility into
this CodeChunk.
|
Modifier and Type | Method | Description |
---|---|---|
(package private) void |
addInstr(short opcode) |
Add an instruction that has no operand.
|
(package private) 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.
|
(package private) void |
addInstrU1(short opcode,
int operand) |
Add an instruction that has an 8 bit operand.
|
(package private) void |
addInstrU2(short opcode,
int operand) |
Add an instruction that has a 16 bit operand.
|
(package private) void |
addInstrU2U1U1(short opcode,
int operand1,
short operand2,
short operand3) |
For adding an instruction with 3 operands, a U2 and two U1's.
|
(package private) void |
addInstrU4(short opcode,
int operand) |
Add an instruction that has a 32 bit operand.
|
(package private) void |
addInstrWide(short opcode,
int varNum) |
This takes an instruction that can be wrapped in
a wide for large variable #s and does so.
|
(package private) 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.
|
private int[] |
findConditionalPCs(int pc,
short opcode) |
Find the limits of a conditional block starting at the instruction with
the given opcode at the program counter pc.
|
private int |
findMaxStack(ClassHolder ch,
int pc,
int codeLength) |
For a block of byte code starting at program counter pc for codeLength
bytes return the maximum stack value, assuming a initial stack depth of
zero.
|
private void |
fixLengths(BCMethod mb,
int maxStack,
int maxLocals,
int codeLength) |
now that we have codeBytes, fix the lengths fields in it
to reflect what was stored.
|
private static int |
getDescriptorWordCount(java.lang.String vmDescriptor) |
Get the word count for a type descriptor in the format of the virual
machine.
|
(package private) short |
getOpcode(int pc) |
Return the opcode at the given pc.
|
(package private) int |
getPC() |
Get the current program counter
|
private java.lang.String |
getTypeDescriptor(ClassHolder ch,
int pc) |
Get the type descriptor in the virtual machine format for the type
defined by the constant pool index for the instruction at pc.
|
private int |
getU2(int pc) |
Get the unsigned short value for the opcode at the program
counter pc.
|
private int |
getU4(int pc) |
Get the unsigned 32 bit value for the opcode at the program
counter pc.
|
private int |
getVariableStackDelta(ClassHolder ch,
int pc,
int opcode) |
Get the number of words pushed (positive) or popped (negative) by this
instruction.
|
(package private) CodeChunk |
insertCodeSpace(int pc,
int additionalBytes) |
Insert room for byteCount bytes after the instruction at pc
and prepare to replace the instruction at pc.
|
private static int |
instructionLength(short opcode) |
Return the complete instruction length for the
passed in opcode.
|
private static boolean |
isReturn(short opcode) |
See if the opcode is a return instruction.
|
private void |
limitHit(java.io.IOException ioe) |
Assume an IOException means some limit of the class file
format was hit
|
private static int |
parameterWordCount(java.lang.String methodDescriptor) |
Calculate the number of stack words in the arguments pushed for this
method descriptor.
|
private int |
removePushedCode(BCMethod mb,
ClassHolder ch,
BCMethod subMethod,
int split_pc,
int splitLength) |
Remove a block of code from this method that was pushed into a sub-method
and call the sub-method.
|
private int |
splitCodeIntoSubMethod(BCMethod mb,
ClassHolder ch,
BCMethod subMethod,
int split_pc,
int splitLength) |
Split a block of code from this method into a sub-method
and call it.
|
(package private) int |
splitExpressionOut(BCMethod mb,
ClassHolder ch,
int optimalMinLength,
int maxStack) |
Split an expression out of a large method into its own
sub-method.
|
private static int |
splitMinLength(BCMethod mb) |
Minimum split length for a sub-method.
|
(package private) 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.
|
private int |
stackWordDelta(ClassHolder ch,
int pc,
short opcode) |
Return the number of stack words pushed (positive) or popped (negative)
by this instruction.
|
private BCMethod |
startSubMethod(BCMethod mb,
java.lang.String returnType,
int split_pc,
int blockLength) |
Start a sub method that we will split the portion of our current code to,
starting from start_pc and including codeLength bytes of code.
|
private boolean |
usesParameters(BCMethod mb,
int pc,
int codeLength) |
Does a section of code use parameters.
|
private static final int CODE_OFFSET
static final short[] LOAD_VARIABLE
static final short[] LOAD_VARIABLE_FAST
static final short[] STORE_VARIABLE
static final short[] STORE_VARIABLE_FAST
static final short[] ARRAY_ACCESS
static final short[] ARRAY_STORE
static final short[] RETURN_OPCODE
static final short[][][] CAST_CONVERSION_INFO
private static final byte[] push1_1i
private static final byte[] push2_1i
private static final byte[] NS
private static final byte VARIABLE_STACK
private static final byte[][] OPCODE_ACTION
The first element in the array [0] is the number of stack words (double/long count as two) pushed by the opcode. Will be negative if the opcode pops values.
The second element in the array [1] is the number of bytes in the instruction stream that this opcode's instruction takes up, including the opocode.
private final int pcDelta
insertCodeSpace(int, int)
final BCClass cb
private final ClassFormatOutput cout
CodeChunk(BCClass cb)
private CodeChunk(CodeChunk main, int pc, int byteCount)
pc
- byteCount
- private void limitHit(java.io.IOException ioe)
void addInstr(short opcode)
void addInstrU2(short opcode, int operand)
void addInstrU4(short opcode, int operand)
void addInstrU1(short opcode, int operand)
void addInstrCPE(short opcode, int cpeNum)
void addInstrWide(short opcode, int varNum)
void addInstrU2U1U1(short opcode, int operand1, short operand2, short operand3)
int getPC()
private static int instructionLength(short opcode)
private void fixLengths(BCMethod mb, int maxStack, int maxLocals, int codeLength)
void complete(BCMethod mb, ClassHolder ch, ClassMember method, int maxStack, int maxLocals)
short getOpcode(int pc)
private int getU2(int pc)
private int getU4(int pc)
CodeChunk insertCodeSpace(int pc, int additionalBytes)
pc
- additionalBytes
- private int findMaxStack(ClassHolder ch, int pc, int codeLength)
private int stackWordDelta(ClassHolder ch, int pc, short opcode)
private java.lang.String getTypeDescriptor(ClassHolder ch, int pc)
private static int getDescriptorWordCount(java.lang.String vmDescriptor)
private int getVariableStackDelta(ClassHolder ch, int pc, int opcode)
private static int parameterWordCount(java.lang.String methodDescriptor)
private int[] findConditionalPCs(int pc, short opcode)
Returns a six element integer array of program counters and lengths.
[0] - program counter of the IF opcode (passed in as pc) [1] -
program counter of the start of the then block [2] - length of the then
block [3] - program counter of the else block, -1 if no else block
exists. [4] - length of of the else block, -1 if no else block exists.
[5] - program counter of the common end point.
Looks for and handles conditionals that are written by the Conditional
class.
final int splitZeroStack(BCMethod mb, ClassHolder ch, int split_pc, int optimalMinLength)
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.
mb
- Method for this chunk.ch
- Class definitionoptimalMinLength
- minimum length required for splitprivate BCMethod startSubMethod(BCMethod mb, java.lang.String returnType, int split_pc, int blockLength)
private boolean usesParameters(BCMethod mb, int pc, int codeLength)
private int splitCodeIntoSubMethod(BCMethod mb, ClassHolder ch, BCMethod subMethod, int split_pc, int splitLength)
mb
- My methodch
- My classsubMethod
- Sub-method code was pushed intosplit_pc
- Program counter the split started atsplitLength
- Length of code splitprivate int removePushedCode(BCMethod mb, ClassHolder ch, BCMethod subMethod, int split_pc, int splitLength)
mb
- My methodch
- My classsubMethod
- Sub-method code was pushed intosplit_pc
- Program counter the split started atsplitLength
- Length of code splitfinal int splitExpressionOut(BCMethod mb, ClassHolder ch, int optimalMinLength, int maxStack)
Method call expressions are of the form:
This method will split out such expressions in sub-methods and replace the original code with a call to that submethod.
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:
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.
private static boolean isReturn(short opcode)
opcode
- opcode to be checkedprivate static int splitMinLength(BCMethod mb)
Apache Derby V10.14 Internals - Copyright © 2004,2018 The Apache Software Foundation. All Rights Reserved.