org.apache.derby.impl.services.bytecode
Class CodeChunk

java.lang.Object
  extended by 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
(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.
 
Constructor Summary
(package private) CodeChunk(BCClass cb)
           
private CodeChunk(CodeChunk main, int pc, int byteCount)
          Return a CodeChunk that has limited visibility into this CodeChunk.
 
Method Summary
(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.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

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
Constructor Detail

CodeChunk

CodeChunk(BCClass cb)

CodeChunk

private CodeChunk(CodeChunk main,
                  int pc,
                  int byteCount)
Return a CodeChunk that has limited visibility into this CodeChunk. Used when a caller needs to insert instructions into an existing stream.

Parameters:
pc -
byteCount -
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.


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. 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 definition
optimalMinLength - 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 method
ch - My class
subMethod - Sub-method code was pushed into
split_pc - Program counter the split started at
splitLength - 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 method
ch - My class
subMethod - Sub-method code was pushed into
split_pc - Program counter the split started at
splitLength - 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:

Two special cases of instance method calls will be handled by the first incarnation of splitExpressionOut. three categories: 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.

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:

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.


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.


Built on Thu 2012-03-29 21:53:33+0000, from revision ???

Apache Derby V10.6 Internals - Copyright © 2004,2007 The Apache Software Foundation. All Rights Reserved.