Class CodeChunk
- java.lang.Object
-
- org.apache.derby.impl.services.bytecode.CodeChunk
-
final class CodeChunk extends java.lang.Object
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.
-
-
Field Summary
Fields 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.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods 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 counterprivate 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 hitprivate 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.
-
-
-
Field Detail
-
CODE_OFFSET
private static final int CODE_OFFSET
Starting point of the byte code stream in the underlying stream/array.- See Also:
- Constant Field Values
-
LOAD_VARIABLE
static final short[] LOAD_VARIABLE
-
LOAD_VARIABLE_FAST
static final short[] LOAD_VARIABLE_FAST
-
STORE_VARIABLE
static final short[] STORE_VARIABLE
-
STORE_VARIABLE_FAST
static final short[] STORE_VARIABLE_FAST
-
ARRAY_ACCESS
static final short[] ARRAY_ACCESS
-
ARRAY_STORE
static final short[] ARRAY_STORE
-
RETURN_OPCODE
static final short[] RETURN_OPCODE
-
CAST_CONVERSION_INFO
static final short[][][] CAST_CONVERSION_INFO
-
push1_1i
private static final byte[] push1_1i
Constant used by OPCODE_ACTION to represent the common action of push one word, 1 byte for the instruction.
-
push2_1i
private static final byte[] push2_1i
Constant used by OPCODE_ACTION to represent the common action of push two words, 1 byte for the instruction.
-
NS
private static final byte[] NS
Constant used by OPCODE_ACTION to the opcode is not yet supported.
-
VARIABLE_STACK
private static final byte VARIABLE_STACK
Value for OPCODE_ACTION[opcode][0] to represent the number of words popped or pushed in variable.- See Also:
- Constant Field Values
-
OPCODE_ACTION
private static final byte[][] OPCODE_ACTION
Array that provides two pieces of information about each VM opcode. Each opcode has a two byte array.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.
-
pcDelta
private final int pcDelta
The delta between cout.size() and the pc. For an initial code chunk this is -8 (CODE_OFFSET) since 8 bytes are written. For a nested CodeChunk return by insertCodeSpace the delta corresponds to the original starting pc.- See Also:
insertCodeSpace(int, int)
-
cb
final BCClass cb
The class we are generating code for, used to indicate that some limit was hit during code generation.
-
cout
private final ClassFormatOutput cout
-
-
Method Detail
-
limitHit
private void limitHit(java.io.IOException ioe)
Assume an IOException means some limit of the class file format was hit
-
addInstr
void addInstr(short opcode)
Add an instruction that has no operand. All opcodes are 1 byte large.
-
addInstrU2
void addInstrU2(short opcode, int operand)
Add an instruction that has a 16 bit operand.
-
addInstrU4
void addInstrU4(short opcode, int operand)
Add an instruction that has a 32 bit operand.
-
addInstrU1
void addInstrU1(short opcode, int operand)
Add an instruction that has an 8 bit operand.
-
addInstrCPE
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. We assume the narrow instruction is what we were given, and that the wide form is the next possible instruction.
-
addInstrWide
void addInstrWide(short opcode, int varNum)
This takes an instruction that can be wrapped in a wide for large variable #s and does so.
-
addInstrU2U1U1
void addInstrU2U1U1(short opcode, int operand1, short operand2, short operand3)
For adding an instruction with 3 operands, a U2 and two U1's. So far, this is used by VMOpcode.INVOKEINTERFACE.
-
getPC
int getPC()
Get the current program counter
-
instructionLength
private static int instructionLength(short opcode)
Return the complete instruction length for the passed in opcode. This will include the space for the opcode and its operand.
-
fixLengths
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. Limits checked here are from these sections of the JVM spec.- 4.7.3 The Code Attribute
- 4.10 Limitations of the Java Virtual Machine
-
complete
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.
-
getOpcode
short getOpcode(int pc)
Return the opcode at the given pc.
-
getU2
private int getU2(int pc)
Get the unsigned short value for the opcode at the program counter pc.
-
getU4
private int getU4(int pc)
Get the unsigned 32 bit value for the opcode at the program counter pc.
-
insertCodeSpace
CodeChunk insertCodeSpace(int pc, int additionalBytes)
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
-additionalBytes
-
-
findMaxStack
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.
-
stackWordDelta
private int stackWordDelta(ClassHolder ch, int pc, short opcode)
Return the number of stack words pushed (positive) or popped (negative) by this instruction.
-
getTypeDescriptor
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.
-
getDescriptorWordCount
private static int getDescriptorWordCount(java.lang.String vmDescriptor)
Get the word count for a type descriptor in the format of the virual machine. For a method this returns the the word count for the return type.
-
getVariableStackDelta
private int getVariableStackDelta(ClassHolder ch, int pc, int opcode)
Get the number of words pushed (positive) or popped (negative) by this instruction. The instruction is a get/put field or a method call, thus the size of the words is defined by the field or method being access.
-
parameterWordCount
private static int parameterWordCount(java.lang.String methodDescriptor)
Calculate the number of stack words in the arguments pushed for this method descriptor.
-
findConditionalPCs
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.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.- Returns:
- Null if the opcode is not the start of a conditional otherwise the array of values.
-
splitZeroStack
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. 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.ch
- Class definitionoptimalMinLength
- minimum length required for split
-
startSubMethod
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. Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed in return type and same parameters as mb if the code block to be moved uses parameters.
-
usesParameters
private boolean usesParameters(BCMethod mb, int pc, int codeLength)
Does a section of code use parameters. Any load, exception ALOAD_0 in an instance method, is seen as using parameters, as this complete byte code implementation does not use local variables.
-
splitCodeIntoSubMethod
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. Returns the pc of this method just after the call to the sub-method.- Parameters:
mb
- My methodch
- My classsubMethod
- Sub-method code was pushed intosplit_pc
- Program counter the split started atsplitLength
- Length of code split
-
removePushedCode
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. Returns the pc of this method just after the call to the sub-method.- Parameters:
mb
- My methodch
- My classsubMethod
- Sub-method code was pushed intosplit_pc
- Program counter the split started atsplitLength
- Length of code split
-
splitExpressionOut
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
- this.method(args)
- this.getter().method(args)
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])
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)
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.
-
isReturn
private static boolean isReturn(short opcode)
See if the opcode is a return instruction.- Parameters:
opcode
- opcode to be checked- Returns:
- true for is a return instruction, false otherwise.
-
splitMinLength
private static int splitMinLength(BCMethod mb)
Minimum split length for a sub-method. If the number of instructions to call the sub-method exceeds the length of the sub-method, then there's no point splitting. The number of bytes in the code stream to call a generated sub-method can take is based upon the number of method args. A method can have maximum of 255 words of arguments (section 4.10 JVM spec) which in the worst case would be 254 (one-word) parameters and this. For a sub-method the arguments will come from the parameters to the method, i.e. ALOAD, ILOAD etc.
This leads to this number of instructions.- 4 - 'this' and first 3 parameters have single byte instructions
- (N-4)*2 - Remaining parameters have two byte instructions
- 3 for the invoke instruction.
-
-