[JVM source code analysis] the template interpreter interprets and executes Java bytecode instructions

This article is compiled and published by jiumo (Ma Zhi), chief lecturer of HeapDump performance community

Part 22 - virtual machine bytecode operation instructions

The operation related bytecode instructions in the virtual machine specification are shown in the following table.

0x60iaddAdd two int values at the top of the stack and press the result into the top of the stack
0x61laddAdd the two long values at the top of the stack and press the result into the top of the stack
0x62faddAdd two float type values at the top of the stack and press the result into the top of the stack
0x63daddAdd two double type values at the top of the stack and press the result into the top of the stack
0x64isubSubtract two int values at the top of the stack and push the result into the top of the stack
0x65lsubSubtract two long values at the top of the stack and press the result into the top of the stack
0x66fsubSubtract two float type values at the top of the stack and press the result into the top of the stack
0x67dsubSubtract two double type values at the top of the stack and press the result into the top of the stack
0x68imulMultiply the two int values at the top of the stack and press the result into the top of the stack
0x69lmulMultiply the two long values at the top of the stack and press the result into the top of the stack
0x6afmulMultiply two float type values at the top of the stack and press the result into the top of the stack
0x6bdmulMultiply two double type values at the top of the stack and press the result into the top of the stack
0x6cidivDivide the two int values at the top of the stack and push the result into the top of the stack
0x6dldivDivide two long type values at the top of the stack and push the result into the top of the stack
0x6efdivDivide two float type values at the top of the stack and press the result into the top of the stack
0x6fddivDivide two double type values at the top of the stack and push the result into the top of the stack
0x70iremModulo the two int values at the top of the stack and press the result into the top of the stack
0x71lremModulo the two long values at the top of the stack and press the result into the top of the stack
0x72fremModulo the two float type values at the top of the stack and press the results into the top of the stack
0x73dremModulo the two double type values at the top of the stack and press the result into the top of the stack
0x74inegTake the int value at the top of the stack as negative and press the result into the top of the stack
0x75lnegTake the long value at the top of the stack as negative and press the result into the top of the stack
0x76fnegTake the float type value at the top of the stack as negative and press the result into the top of the stack
0x77dnegTake the double value at the top of the stack as negative and press the result into the top of the stack
0x78ishlShift the int value left by the specified number of bits and push the result to the top of the stack
0x79lshlShift the long value left by the specified number of bits and push the result to the top of the stack
0x7aishrShift the int value right (sign) by the specified number of bits and push the result to the top of the stack
0x7blshrShift the long value right (sign) by the specified number of bits and push the result to the top of the stack
0x7ciushrShift the int value to the right (unsigned) by the specified number of bits and push the result to the top of the stack
0x7dlushrShift the long value to the right (unsigned) by the specified number of bits and push the result to the top of the stack
0x7eiandMake the two int values at the top of the stack "bitwise and" and press the result into the top of the stack
0x7flandMake the two long type values at the top of the stack "bitwise and" and press the results into the top of the stack
0x80iorMake the two int values at the top of the stack "bitwise or" and press the result into the top of the stack
0x81lorMake the two long type values at the top of the stack "bitwise or" and press the results into the top of the stack
0x82ixorMake the two int values at the top of the stack "bitwise XOR" and press the result into the top of the stack
0x83lxorMake the two long type values at the top of the stack "bitwise XOR" and press the results into the top of the stack
0x84iincIncrease the specified int variable by the specified value (i + +, i --, i+=2)
0x94lcmpCompare the size of two long type values at the top of the stack, and press the result (1, 0 or - 1) into the top of the stack
0x95fcmplCompare the size of two float type values at the top of the stack, and press the result (1, 0 or - 1) into the top of the stack; When one of the values is NaN, press - 1 into the top of the stack
0x96fcmpgCompare the size of two float type values at the top of the stack, and press the result (1, 0 or - 1) into the top of the stack; When one of the values is NaN, press 1 into the top of the stack
0x97dcmplCompare the size of two double type values at the top of the stack, and press the result (1, 0 or - 1) into the top of the stack; When one of the values is NaN, press - 1 into the top of the stack
0x98dcmpgCompare the size of two double type values at the top of the stack, and press the result (1, 0 or - 1) into the top of the stack; When one of the values is NaN, press 1 into the top of the stack

 

1. Basic add, subtract, multiply and divide instructions

1. iadd instruction

The iadd instruction adds two integers at the top of the stack, and then pushes the addition result into the top of the stack. The format of the instruction is as follows:

iadd  val1,val2 

Val1 and val2 represent two int type integers. When the instruction is executed, val1 and val3 are taken out of the operand stack, the two values are added to obtain the int type data result, and the result is pressed into the operand stack.

The template of the iadd instruction is defined as follows:

def(Bytecodes::_iadd , ____|____|____|____, itos, itos, iop2 , add);

The generation function is TemplateTable::iop2(), which is implemented as follows:

void TemplateTable::iop2(Operation op) {
  switch (op) {
  case add  :                    __ pop_i(rdx); __ addl (rax, rdx); break;
  case sub  : __ movl(rdx, rax); __ pop_i(rax); __ subl (rax, rdx); break;
  case mul  :                    __ pop_i(rdx); __ imull(rax, rdx); break;
  case _and :                    __ pop_i(rdx); __ andl (rax, rdx); break;
  case _or  :                    __ pop_i(rdx); __ orl  (rax, rdx); break;
  case _xor :                    __ pop_i(rdx); __ xorl (rax, rdx); break;
  case shl  : __ movl(rcx, rax); __ pop_i(rax); __ shll (rax);      break;
  case shr  : __ movl(rcx, rax); __ pop_i(rax); __ sarl (rax);      break;
  case ushr : __ movl(rcx, rax); __ pop_i(rax); __ shrl (rax);      break;
  default   : ShouldNotReachHere();
  }
}

As you can see, this function is the generation function of many instructions, such as iadd, isub, imul, iand, ior, ixor, ishl, ishr and iushr.

The assembly code generated for the iadd instruction is as follows:  

mov    (%rsp),%edx
add    $0x8,%rsp
add    %edx,%eax

Store the result of adding% eax cached at the top of the stack to% eax.  

2. isub instruction

The assembly code generated by the isub instruction is as follows:

mov    %eax,%edx
mov    (%rsp),%eax
add    $0x8,%rsp
sub    %edx,%eax

The code implementation is relatively simple and will not be introduced here.    

3. idiv instruction

idiv is a bytecode division instruction. The format of this instruction is as follows:

idiv val1,val2

Both val1 and val2 must be int type data. When the instruction is executed, val1 and val2 get out of the operand stack and divide the two values (val1 ÷ val2). The result is converted to the int type value result, and finally the result is pushed into the operand stack.

The template of the idiv instruction is defined as follows:

def(Bytecodes::_idiv , ____|____|____|____, itos, itos, idiv ,  _  );

The generated function called is TemplateTable::idiv(), and the generated assembly is as follows:

0x00007fffe1019707: mov    %eax,%ecx
0x00007fffe1019709: mov    (%rsp),%eax
0x00007fffe101970c: add    $0x8,%rsp

// Test whether the dividend is 0x80000000. If not, jump to normal_case
0x00007fffe1019710: cmp    $0x80000000,%eax
0x00007fffe1019716: jne    0x00007fffe1019727

// The divisor is 0x80000000, and if the divisor is - 1, jump to special_case
0x00007fffe101971c: xor    %edx,%edx
0x00007fffe101971e: cmp    $0xffffffff,%ecx
0x00007fffe1019721: je     0x00007fffe101972a

// -- normal_case --

// cltd extends the data symbols in the eax register to edx:eax, specifically
// The 32-bit integer of eax is extended to 64 bits, and the high 32 bits are filled with the sign bits of eax and saved to edx
0x00007fffe1019727: cltd   
0x00007fffe1019728: idiv   %ecx

// -- special_case --

The idiv function will use the specified register, as shown in the figure below.

The assembler checked the special division of 0x80000000 / -1. reference resources: Using disassembly debugging and complement to explain the inconsistency of 0x80000000 / -1 shaping output exception 

2. Comparison instruction

The lcmp instruction compares the size of two long type values at the top of the stack and pushes the result (1, 0 or - 1) into the top of the stack. The format of the instruction is as follows:

lcmp val1,val2

Both val1 and val2 must be long data. When the instruction is executed,   Val1 and val2 exit the operand stack and use an int value as the comparison result:

  • If   If val1 is greater than val2, the result is 1;
  • If   val1 equals val2 and the result is 0;
  • If val1 is less than val2, the result is - 1.

Finally, the comparison result is pushed into the operand stack.

The template of lcmp bytecode instruction is defined as follows:

def(Bytecodes::_lcmp , ____|____|____|____, ltos, itos, lcmp ,  _ );

The generation function is TemplateTable::lcmp(),   The resulting assembly is as follows:

0x00007fffe101a6c8: mov     (%rsp),%rdx
0x00007fffe101a6cc: add     $0x10,%rsp

// The cmp instruction is described as follows:
// When the first operand < the second operand, ZF=0
// When the first operand = the second operand, ZF=1
// When the first operand > the second operand, ZF=0
0x00007fffe101a6d0: cmp     %rax,%rdx
0x00007fffe101a6d3: mov     $0xffffffff,%eax // Move - 1 to% eax

// If the first operand is less than the second operand, jump to done
0x00007fffe101a6d8: jl      0x00007fffe101a6e0

// After the cmp instruction is executed, the result of comparison can be obtained by executing the setne instruction
// Set the target operand to 0 or 1 according to the status flags (CF,SF,OF,ZF, and PF) in eflags
0x00007fffe101a6da: setne   %al
0x00007fffe101a6dd: movzbl  %al,%eax

//  -- done --

The logic of the above assembly code is very simple and will not be introduced here.

The logic of other bytecode instructions is also relatively simple, and those interested can study it by themselves.

Part 23 - type conversion of virtual machine bytecode instructions

The bytecode instructions related to type conversion defined in the Java virtual machine specification are shown in the following table.

0x85i2lCast an int value at the top of the stack to a long value and push the result into the top of the stack
0x86i2fCast the int value at the top of the stack to a float value and push the result into the top of the stack
0x87i2dCast an int value at the top of the stack to a double value and push the result into the top of the stack
0x88l2iCast the long value at the top of the stack to an int value and push the result into the top of the stack
0x89l2fCast the long value at the top of the stack into a float value and push the result into the top of the stack
0x8al2dCast the long value at the top of the stack into a double value and push the result into the top of the stack
0x8bf2iCast the float type value at the top of the stack to an int type value and push the result into the top of the stack
0x8cf2lForce the float type value at the top of the stack to a long type value and push the result into the top of the stack
0x8df2dCast the float type value at the top of the stack into a double type value and push the result into the top of the stack
0x8ed2iCast the double value at the top of the stack to an int value and push the result into the top of the stack
0x8fd2lCast the double value at the top of the stack into a long value and push the result into the top of the stack
0x90d2fCast the double value at the top of the stack into a float value and push the result into the top of the stack
0x91i2bCast the int value at the top of the stack to a byte value and push the result into the top of the stack
0x92i2cCast the int value at the top of the stack to a char value and push the result into the top of the stack
0x93i2sCast the int value at the top of the stack to a short value and push the result into the top of the stack

The template definition of bytecode instruction in the above table is as follows:

def(Bytecodes::_i2l   , ____|____|____|____, itos, ltos, convert ,  _           );
def(Bytecodes::_i2f   , ____|____|____|____, itos, ftos, convert ,  _           );
def(Bytecodes::_i2d   , ____|____|____|____, itos, dtos, convert ,  _           );
def(Bytecodes::_l2i   , ____|____|____|____, ltos, itos, convert ,  _           );
def(Bytecodes::_l2f   , ____|____|____|____, ltos, ftos, convert ,  _           );
def(Bytecodes::_l2d   , ____|____|____|____, ltos, dtos, convert ,  _           );
def(Bytecodes::_f2i   , ____|____|____|____, ftos, itos, convert ,  _           );
def(Bytecodes::_f2l   , ____|____|____|____, ftos, ltos, convert ,  _           );
def(Bytecodes::_f2d   , ____|____|____|____, ftos, dtos, convert ,  _           );
def(Bytecodes::_d2i   , ____|____|____|____, dtos, itos, convert ,  _           );
def(Bytecodes::_d2l   , ____|____|____|____, dtos, ltos, convert ,  _           );
def(Bytecodes::_d2f   , ____|____|____|____, dtos, ftos, convert ,  _           );
def(Bytecodes::_i2b   , ____|____|____|____, itos, itos, convert ,  _           );
def(Bytecodes::_i2c   , ____|____|____|____, itos, itos, convert ,  _           );
def(Bytecodes::_i2s   , ____|____|____|____, itos, itos, convert ,  _           ); 

The generation function of relevant bytecode conversion instructions is TemplateTable::convert(). The implementation of this function is as follows:

void TemplateTable::convert() {
  static const int64_t is_nan = 0x8000000000000000L;

  // Conversion
  switch (bytecode()) {
  case Bytecodes::_i2l:
    __ movslq(rax, rax);
    break;
  case Bytecodes::_i2f:
    __ cvtsi2ssl(xmm0, rax);
    break;
  case Bytecodes::_i2d:
    __ cvtsi2sdl(xmm0, rax);
    break;
  case Bytecodes::_i2b:
    __ movsbl(rax, rax);
    break;
  case Bytecodes::_i2c:
    __ movzwl(rax, rax);
    break;
  case Bytecodes::_i2s:
    __ movswl(rax, rax);
    break;
  case Bytecodes::_l2i:
    __ movl(rax, rax);
    break;
  case Bytecodes::_l2f:
    __ cvtsi2ssq(xmm0, rax);
    break;
  case Bytecodes::_l2d:
    __ cvtsi2sdq(xmm0, rax);
    break;
  case Bytecodes::_f2i:
  {
    Label L;
    __ cvttss2sil(rax, xmm0);
    __ cmpl(rax, 0x80000000); // NaN or overflow/underflow?
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_f2l:
  {
    Label L;
    __ cvttss2siq(rax, xmm0);
    // NaN or overflow/underflow?
    __ cmp64(rax, ExternalAddress((address) &is_nan));
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_f2d:
    __ cvtss2sd(xmm0, xmm0);
    break;
  case Bytecodes::_d2i:
  {
    Label L;
    __ cvttsd2sil(rax, xmm0);
    __ cmpl(rax, 0x80000000); // NaN or overflow/underflow?
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_d2l:
  {
    Label L;
    __ cvttsd2siq(rax, xmm0);
    // NaN or overflow/underflow?
    __ cmp64(rax, ExternalAddress((address) &is_nan));
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_d2f:
    __ cvtsd2ss(xmm0, xmm0);
    break;
  default:
    ShouldNotReachHere();
  }
}

As_ The i2l instruction converts an int value at the top of the stack into a long value and pushes the result into the top of the stack. The corresponding assembly code is as follows:

movslq %eax,%rax  // Extends a double word into a quadword

The conversion of floating point numbers from float or long to int or long is relatively complex. Let's take a look at the f2i instruction of converting float to int.

// Converts a scalar single precision number to a scalar integer occupying a doubleword
0x00007fffe1019189: vcvttss2si %xmm0,%eax
// Compare with 0x80000000. If it is not equal, jump to L
0x00007fffe101918d: cmp    $0x80000000,%eax
0x00007fffe1019193: jne    0x00007fffe10191bc

// If the stack top pointer has been aligned by 16 bytes, you can directly call the SharedRuntime::f2i() function. Otherwise
// Align the stack top instruction with 16 bytes before calling

0x00007fffe1019199: test   $0xf,%esp
0x00007fffe101919f: je     0x00007fffe10191b7
0x00007fffe10191a5: sub    $0x8,%rsp
// Call the SharedRuntime::f2i() function
0x00007fffe10191a9: callq  0x00007ffff6a0f946
0x00007fffe10191ae: add    $0x8,%rsp
0x00007fffe10191b2: jmpq   0x00007fffe10191bc
// Call the SharedRuntime::f2i() function
0x00007fffe10191b7: callq  0x00007ffff6a0f946 

---- L ----

The generated assembly instruction vcvttss2si means to convert a scalar single precision number into a scalar integer occupying double words. The origin of the name is interpreted as follows:

cvt: convert, conversion;

t: truncation;

ss: scalar single, scalar single precision number;

2: to;

si: scalar integer, scalar integer.

The implementation of the called SharedRuntime::f2i() function is as follows:

JRT_LEAF(jint, SharedRuntime::f2i(jfloat  x))
  if (g_isnan(x))  // If it is a non numeric value, it returns 0 directly
    return 0;
  if (x >= (jfloat) max_jint)
    return max_jint;
  if (x <= (jfloat) min_jint)
    return min_jint;
  return (jint) x;
JRT_END

When calling C + + functions, you need a parameter x, which follows the calling convention of System V AMD64 ABI on GNU / Linux. Registers RDI, RSI, RDX, RCX, R8 and R9 are parameters for integers and memory addresses, and xmm0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 and XMM7 are floating-point parameters. Therefore, xmm0 will be used as the first parameter. This parameter happens to be the register at the top of the stack used to cache floating-point numbers, so no operation is required by default.  

The return value is stored in% rax due to tos_out is itos, and the% rax register is used for stack top cache, so no additional operation is required.

Part 24 - getstatic of virtual machine object operation instructions

The bytecode instructions related to object operations defined in the Java virtual machine specification are shown in the following table.  

0xb2getstaticGets the static field of the specified class and pushes its value to the top of the stack
0xb3putstaticAssigns a value to the static field of the specified class
0xb4getfieldGets the instance field of the specified class and pushes its value to the top of the stack
0xb5putfieldAssigns a value to the instance field of the specified class
0xbbnewCreate an object and push its reference value to the top of the stack
0xbcnewarrayCreate an array of specified primitive types (such as int, float, char, etc.) and push its reference value to the top of the stack
0xbdanewarrayCreate an array of reference type (such as class, interface or array) and push its reference value to the top of the stack
0xbearraylengthGet the length value of the array and push it into the top of the stack
0xc0checkcastVerify the type conversion. If the verification fails, ClassCastException will be thrown
0xc1instanceofCheck whether the object is an instance of the specified class. If yes, press 1 to the top of the stack, otherwise press 0 to the top of the stack
0xc5multianewarrayCreate a multidimensional array of the specified type and dimension (when executing this instruction, the operation stack must contain the length value of each dimension), and push its reference value into the top of the stack

The bytecode instruction template is defined as follows:

def(Bytecodes::_getstatic           , ubcp|____|clvm|____, vtos, vtos, getstatic           , f1_byte      );
def(Bytecodes::_putstatic           , ubcp|____|clvm|____, vtos, vtos, putstatic           , f2_byte      );
def(Bytecodes::_getfield            , ubcp|____|clvm|____, vtos, vtos, getfield            , f1_byte      );
def(Bytecodes::_putfield            , ubcp|____|clvm|____, vtos, vtos, putfield            , f2_byte      );

def(Bytecodes::_new                 , ubcp|____|clvm|____, vtos, atos, _new                ,  _           );
def(Bytecodes::_newarray            , ubcp|____|clvm|____, itos, atos, newarray            ,  _           );
def(Bytecodes::_anewarray           , ubcp|____|clvm|____, itos, atos, anewarray           ,  _           );
def(Bytecodes::_multianewarray      , ubcp|____|clvm|____, vtos, atos, multianewarray      ,  _           );

def(Bytecodes::_arraylength         , ____|____|____|____, atos, itos, arraylength         ,  _           );

def(Bytecodes::_checkcast           , ubcp|____|clvm|____, atos, atos, checkcast           ,  _           );

def(Bytecodes::_instanceof          , ubcp|____|clvm|____, atos, itos, instanceof          ,  _           );

The generation function of new bytecode instruction is templatetable:_ New(), which was introduced in detail in Chapter 9 class object creation of in-depth analysis of Java virtual machine: source code analysis and example explanation (basic volume), and will not be introduced here.

The getstatic bytecode instruction obtains the static field of the specified class and pushes its value to the top of the stack. The format is as follows:

getstatic indexbyte1 indexbyte2

The unsigned numbers indexbyte1 and indexbyte2 are constructed as (indexbyte1 < < 8) |indexbyte2. This value indicates the runtime constant pool index value of the current class, and the runtime constant pool item pointed to is the symbolic reference of a field.

The generating function of getstatic bytecode instruction is TemplateTable::getstatic(), and there is a similar getfield instruction. These generating functions are as follows:

void TemplateTable::getfield(int byte_no) {
  getfield_or_static(byte_no, false); // Byte of getfield_ The no value is 1
}

void TemplateTable::getstatic(int byte_no) {
  getfield_or_static(byte_no, true); // Byte of getstatic_ The value of no is 1
}

Getfield will eventually be called_ or_ The static () function generates machine instruction fragments. The assembly code corresponding to the machine instruction fragment generated by this function is as follows:

// Gets the index of the ConstantPoolCacheEntry in the ConstantPoolCache
0x00007fffe101fd10: movzwl 0x1(%r13),%edx
// Get the first address of ConstantPoolCache from the stack
0x00007fffe101fd15: mov    -0x28(%rbp),%rcx
// Shift 2 bits to the left because the ConstantPoolCacheEntry index is stored in% edx,
// The left shift of 2 bits is because the memory occupation of ConstantPoolCacheEntry is 4 words
0x00007fffe101fd19: shl    $0x2,%edx
// Calculate% rcx+%rdx*8+0x10 and obtain the values in constantpoolcacheentry [_indexes, _f1, _f2, _flags]_ indices
// Because the size of ConstantPoolCache is 0x16 bytes,% rcx+0x10 locates to the start of the first ConstantPoolCacheEntry
// %rdx*8 calculates the byte offset relative to the first ConstantPoolCacheEntry
0x00007fffe101fd1c: mov    0x10(%rcx,%rdx,8),%ebx
// _ After moving indexes to the right by 16 bits, get bytecode and set bytecode in [get bytecode,set bytecode,original constant pool index] are obtained
0x00007fffe101fd20: shr    $0x10,%ebx
// Gets the value of the set bytecode field
0x00007fffe101fd23: and    $0xff,%ebx
// 0xb2 is the Opcode of the getstatic instruction. Compare the values. If they are equal, it indicates that they have been connected. Jump to resolved
0x00007fffe101fd29: cmp    $0xb2,%ebx
0x00007fffe101fd2f: je     0x00007fffe101fdce


// Store Opcode of getstatic bytecode in% ebx
0x00007fffe101fd35: mov    $0xb2,%ebx

// Omit by calling MacroAssembler::call_VM() function to execute interpreterruntime:: resolve_ get_ Assembly code for the put() function
// ...

Call macroassembler:: call_ The VM () function generates the following code to execute interpreter Runtime:: resolve_ get_ Put() function. MacroAssembler::call_ The assembly of vm() function has been described in detail before. It will not be introduced here. The assembly code is directly given as follows:

0x00007fffe101fd3a: callq  0x00007fffe101fd44
0x00007fffe101fd3f: jmpq   0x00007fffe101fdc2

0x00007fffe101fd44: mov    %rbx,%rsi
0x00007fffe101fd47: lea    0x8(%rsp),%rax
0x00007fffe101fd4c: mov    %r13,-0x38(%rbp)
0x00007fffe101fd50: mov    %r15,%rdi
0x00007fffe101fd53: mov    %rbp,0x200(%r15)
0x00007fffe101fd5a: mov    %rax,0x1f0(%r15)
0x00007fffe101fd61: test   $0xf,%esp
0x00007fffe101fd67: je     0x00007fffe101fd7f
0x00007fffe101fd6d: sub    $0x8,%rsp
0x00007fffe101fd71: callq  0x00007ffff66b567c
0x00007fffe101fd76: add    $0x8,%rsp
0x00007fffe101fd7a: jmpq   0x00007fffe101fd84
0x00007fffe101fd7f: callq  0x00007ffff66b567c
0x00007fffe101fd84: movabs $0x0,%r10
0x00007fffe101fd8e: mov    %r10,0x1f0(%r15)
0x00007fffe101fd95: movabs $0x0,%r10
0x00007fffe101fd9f: mov    %r10,0x200(%r15)
0x00007fffe101fda6: cmpq   $0x0,0x8(%r15)
0x00007fffe101fdae: je     0x00007fffe101fdb9
0x00007fffe101fdb4: jmpq   0x00007fffe1000420
0x00007fffe101fdb9: mov    -0x38(%rbp),%r13
0x00007fffe101fdbd: mov    -0x30(%rbp),%r14
0x00007fffe101fdc1: retq   

The above code is very simple. It calls the interpreter Runtime:: resolve written by the C + + function_ get_ Put() function, which will fill in the ConstantPoolCacheEntry information in the constant pool cache. The meaning of ConstantPoolCache, ConstantPoolCacheEntry, and the fields in ConstantPoolCacheEntry have been described in detail in deep analysis of Java virtual machine: source code analysis and detailed explanation of instances (basic volume), and will not be introduced here.  

InterpreterRuntime::resolve_ get_ There are many implementations of put() function. Let's first look at some implementations, as follows:

IRT_ENTRY(void, InterpreterRuntime::resolve_get_put(JavaThread* thread, Bytecodes::Code bytecode))
  // resolve field
  fieldDescriptor      info;
  constantPoolHandle   pool(thread, method(thread)->constants());
  bool  is_put    = (bytecode == Bytecodes::_putfield  || bytecode == Bytecodes::_putstatic);
  bool  is_static = (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic);

  {
    JvmtiHideSingleStepping jhss(thread);
    int x = get_index_u2_cpcache(thread, bytecode); // Get the constant pool cache index according to the bcp in the thread stack
    LinkResolver::resolve_field_access(info, pool, x ,bytecode, CHECK); // Collect information from info
  } 

  // check if link resolution caused cpCache to be updated
  if (already_resolved(thread)){
      return;
  }

   ...
}

Call get_ index_ u2_ The cpcache () function obtains bcp from the corresponding stack frame in the current method, and then obtains the operand of bytecode instruction through bcp, that is, constant pool index. After obtaining the constant pool index, it calls LinkResolver:: resolve_. field_ The access () function may connect classes and fields, and then store the queried field related information in the fieldDescriptor. resolve_ field_ The access() function is implemented as follows:

void LinkResolver::resolve_field_access(
 fieldDescriptor&     result,
 constantPoolHandle   pool,
 int                  index, // Constant pool index
 Bytecodes::Code      byte,
 TRAPS
) { 
  Symbol* field = pool->name_ref_at(index);
  Symbol* sig   = pool->signature_ref_at(index);

  // resolve specified klass connects to a specific class
  KlassHandle resolved_klass;
  resolve_klass(resolved_klass, pool, index, CHECK);

  KlassHandle  current_klass(THREAD, pool->pool_holder());
  resolve_field(result, resolved_klass, field, sig, current_klass, byte, true, true, CHECK);
} 

The index entry at index found in the pool is CONSTANT_NameAndType_info in the following format:

CONSTANT_NameAndType_info {
   u1 tag;
   u2 name_index;       // Occupied 16 bits
   u2 descriptor_index; // Occupied 16 bits
}

A constant in the constant pool_ NameAndType_ Info data item can be regarded as constant_ An instance of the nameandtype Type. As can be seen from the Name of this data item, it describes two types of information. The first is the Name and the second is the Type. The Name here refers to the Name of the method or the Name of the field, while Type is a Type in a broad sense. It actually describes the descriptor of the field or the descriptor of the method. That is, if the Name part is a field Name, the Type part is the descriptor of the corresponding field; If the Name part describes the Name of a method, the Type part is the descriptor of the corresponding method. That is, a CONSTANT_NameAndType_info represents a method or a field.

Call resolve_klass() connects the class and calls resolve_field() connects the field. In resolve_ The field() function has the following implementation:

InstanceKlass* tmp = InstanceKlass::cast(resolved_klass());
KlassHandle    sel_klass(THREAD, tmp->find_field(field, sig, &fd));

The most important thing is to call the find of InstanceKlass_ The field() function finds the field and stores the found relevant information in fd of fieldDescriptor type. The storage and specific layout of fields in InstanceKlass have been described in detail in in-depth analysis of Java virtual machine: source code analysis and example details (basic volume), which will not be introduced here.  

The fieldDescriptor class and important attributes are defined as follows:

class fieldDescriptor VALUE_OBJ_CLASS_SPEC {
 private:
  AccessFlags          _access_flags;
  int                  _index; // the field index
  constantPoolHandle   _cp;
  ...
}

Among them_ access_flags can be used to indicate whether the field is decorated with volatile, final and other keywords_ index indicates that the field is stored in the tuple of the corresponding array in InstanceKlass_ cp represents the constant pool of the class that defines the current field.

By calling resolve_klass() and resolve_ After the field() function, you can get the information, and then return to the interpreter Runtime:: resolve_ get_ The put() function continues to view the implementation logic:

  TosState state  = as_TosState(info.field_type());

  Bytecodes::Code put_code = (Bytecodes::Code)0;


  InstanceKlass* klass = InstanceKlass::cast(info.field_holder());
  bool uninitialized_static = (  (bytecode == Bytecodes::_getstatic || bytecode == Bytecodes::_putstatic) &&
                                 !klass->is_initialized()    );
  Bytecodes::Code get_code = (Bytecodes::Code)0;

  if (!uninitialized_static) {
    get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield);
    // 1. Is a putfield or putstatic instruction
    // 2. Is a getstatic or getfield instruction and does not get the value of the final variable
    if (is_put || !info.access_flags().is_final()) {
      put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield);
    }
  }

  ConstantPoolCacheEntry* cpce = cache_entry(thread);
  cpce->set_field(
    get_code,            // Set yes_ b1 in indices. When it is getstatic or getfield, Opcode is stored in it
    put_code,            // Set yes_ When b2 in indices is setstatic or setfield, Opcode is stored in it, so get_code and put_code if you want to connect, its value is not 0
    info.field_holder(), // Set yes_ The f1 field represents the owner of the field
    info.index(),                      // field_index, set flags
    info.offset(),                     // field_offset, set yes_ Field, Offset (in words) of field from start of instanceOop / Klass*
    state,                             // field_type, set flags
    info.access_flags().is_final(),    // Set flags
    info.access_flags().is_volatile(), // Set flags
    pool->pool_holder()
  );

You can get various information of the field through the information in info, and then fill in the ConstantPoolEntry information, so that you don't need to connect the field next time, or you don't need to find the field information from InstanceKlass. You can directly find all the desired information from ConstantPoolCacheEntry.  

  

The above figure has been described in detail in the book "in depth analysis of Java virtual machine: source code analysis and detailed explanation of examples (basic volume)". By interpreting the interpretation and execution process of getstatic bytecode, we can clearly know the role of constant pool buffer items. For getstatic, it will be judged at the beginning_ The upper 8 bits in indices store the opcode of getstatic. If not, it means there is no connection, so you need to call interpreter Runtime:: resolve_ get_ The put() function performs the join operation.  

When the connection is completed or has been completed, the following assembly code will continue to be executed:

// Store the index of ConstantPoolCacheEntry% edx
0x00007fffe101fdc2: movzwl 0x1(%r13),%edx
// Store the first address of ConstantPoolCache in% rcx
0x00007fffe101fdc7: mov    -0x28(%rbp),%rcx
// Get the index corresponding to the corresponding ConstantPoolCacheEntry
0x00007fffe101fdcb: shl    $0x2,%edx

// --resolved --

// Get the in [_indices,_f1,_f2,_flags]_ Since the ConstantPoolCache occupies 16 bytes, the_ indices
// And_ Each occupies 8 bytes, so_ The offset of f2 is 32 bytes, that is 0x32
// _ The byte offset of the field in the java.lang.Class instance is saved in f2. Through this offset, the field can be obtained and stored in
// Value of java.lang.Class instance
0x00007fffe101fdce: mov    0x20(%rcx,%rdx,8),%rbx
// Get the in [_indices,_f1,_f2,_flags]_ flags 
0x00007fffe101fdd3: mov 0x28(%rcx,%rdx,8),%eax
// Get the in [_indices,_f1,_f2,_flags]_ f1,_f1 saves the field owner,
// That is, the java.lang.Class object
0x00007fffe101fdd7: mov 0x18(%rcx,%rdx,8),%rcx

// From_ Get in f1_ java_ The value of the mirror property
0x00007fffe101fddc: mov    0x70(%rcx),%rcx
// Will_ flags moves 28 bits to the right, leaving TosState
0x00007fffe101fde0: shr    $0x1c,%eax
0x00007fffe101fde3: and    $0xf,%eax
// If it is not equal, it means that the value of TosState is not 0, then jump to notByte
0x00007fffe101fde6: jne    0x00007fffe101fdf6

// btos
// The number of btos is 0. When the code is executed here, the cache at the top of the stack may require btos
// %Stored in rcx is_ java_ What is stored in mirror,% rbx_ Since static variables are stored in_ java_ In the mirror, so you need to get
// The corresponding first address and push it into the stack
0x00007fffe101fdec: movsbl (%rcx,%rbx,1),%eax
0x00007fffe101fdf0: push   %rax
// Jump to Done
0x00007fffe101fdf1: jmpq   0x00007fffe101ff0c
// -- notByte --
// %TosState is stored in eax. If it is not atos, jump to notObj
0x00007fffe101fdf6: cmp    $0x7,%eax
0x00007fffe101fdf9: jne    0x00007fffe101fe90

// atos
// %Stored in rcx is_ java_ What is stored in mirror,% rbx_ f2,
// So get the first address of the static variable and push it into the stack
0x00007fffe101fdff: mov    (%rcx,%rbx,1),%eax
0x00007fffe101fe02: push   %r10
0x00007fffe101fe04: cmp    0x163a8d45(%rip),%r12   # 0x00007ffff73c8b50 
0x00007fffe101fe0b: je     0x00007fffe101fe88
0x00007fffe101fe11: mov    %rsp,-0x28(%rsp)
0x00007fffe101fe16: sub    $0x80,%rsp
0x00007fffe101fe1d: mov    %rax,0x78(%rsp)
0x00007fffe101fe22: mov    %rcx,0x70(%rsp)
0x00007fffe101fe27: mov    %rdx,0x68(%rsp)
0x00007fffe101fe2c: mov    %rbx,0x60(%rsp)
0x00007fffe101fe31: mov    %rbp,0x50(%rsp)
0x00007fffe101fe36: mov    %rsi,0x48(%rsp)
0x00007fffe101fe3b: mov    %rdi,0x40(%rsp)
0x00007fffe101fe40: mov    %r8,0x38(%rsp)
0x00007fffe101fe45: mov    %r9,0x30(%rsp)
0x00007fffe101fe4a: mov    %r10,0x28(%rsp)
0x00007fffe101fe4f: mov    %r11,0x20(%rsp)
0x00007fffe101fe54: mov    %r12,0x18(%rsp)
0x00007fffe101fe59: mov    %r13,0x10(%rsp)
0x00007fffe101fe5e: mov    %r14,0x8(%rsp)
0x00007fffe101fe63: mov    %r15,(%rsp)
0x00007fffe101fe67: movabs $0x7ffff6d4d828,%rdi
0x00007fffe101fe71: movabs $0x7fffe101fe11,%rsi
0x00007fffe101fe7b: mov    %rsp,%rdx
0x00007fffe101fe7e: and    $0xfffffffffffffff0,%rsp
0x00007fffe101fe82: callq  0x00007ffff6872e3a
0x00007fffe101fe87: hlt 
0x00007fffe101fe88: pop    %r10
0x00007fffe101fe8a: push   %rax
0x00007fffe101fe8b: jmpq   0x00007fffe101ff0c

// -- notObj --
0x00007fffe101fe90: cmp    $0x3,%eax
// If it is not itos, jump to notInt
0x00007fffe101fe93: jne    0x00007fffe101fea2

// itos
0x00007fffe101fe99: mov    (%rcx,%rbx,1),%eax
0x00007fffe101fe9c: push   %rax
// Jump to Done
0x00007fffe101fe9d: jmpq   0x00007fffe101ff0c
// -- notInt --
// If not ctos, jump to notChar
0x00007fffe101fea2: cmp    $0x1,%eax
0x00007fffe101fea5: jne    0x00007fffe101feb5

// ctos
0x00007fffe101feab: movzwl (%rcx,%rbx,1),%eax
0x00007fffe101feaf: push   %rax
// Jump to Done
0x00007fffe101feb0: jmpq   0x00007fffe101ff0c
// -- notChar --
// If not stos, jump to notShort
0x00007fffe101feb5: cmp    $0x2,%eax
0x00007fffe101feb8: jne    0x00007fffe101fec8

// stos
0x00007fffe101febe: movswl (%rcx,%rbx,1),%eax
0x00007fffe101fec2: push   %rax
// Jump to done
0x00007fffe101fec3: jmpq   0x00007fffe101ff0c
// -- notShort --
// If not ltos, jump to notLong
0x00007fffe101fec8: cmp    $0x4,%eax
0x00007fffe101fecb: jne    0x00007fffe101fee2

// ltos
0x00007fffe101fed1: mov    (%rcx,%rbx,1),%rax
0x00007fffe101fed5: sub    $0x10,%rsp
0x00007fffe101fed9: mov    %rax,(%rsp)
// Jump to Done
0x00007fffe101fedd: jmpq   0x00007fffe101ff0c
// -- notLong --
// If not ftos, jump to notFloat
0x00007fffe101fee2: cmp    $0x5,%eax
0x00007fffe101fee5: jne    0x00007fffe101fefe

// ftos
0x00007fffe101feeb: vmovss (%rcx,%rbx,1),%xmm0
0x00007fffe101fef0: sub    $0x8,%rsp
0x00007fffe101fef4: vmovss %xmm0,(%rsp)
// Jump to Done
0x00007fffe101fef9: jmpq   0x00007fffe101ff0c
// -- notFloat --
0x00007fffe101fefe: vmovsd (%rcx,%rbx,1),%xmm0
0x00007fffe101ff03: sub    $0x10,%rsp
0x00007fffe101ff07: vmovsd %xmm0,(%rsp)  
  
// -- Done --
  

Although there are many assembly codes above, the logic of completion is very simple, that is, the logic of stack pressing is completed through the information stored in ConstantPoolCacheEntry (the so-called bytecode connection completion means that the information of the corresponding constant pool cache item has been improved). Since the value of the static field is stored in the java.lang.Class instance, you need to obtain the corresponding value, and then press the value into the expression stack according to the state required by the cache at the top of the stack.

Part 25 - getfield of virtual machine object operation instruction

The getfield instruction means to get the instance field of the specified class and push its value to the top of the stack. The format is as follows:

getstatic indexbyte1 indexbyte2

The unsigned numbers indexbyte1 and indexbyte2 are constructed as (indexbyte1 < < 8) |indexbyte2. This value indicates the runtime constant pool index value of the current class, and the runtime constant pool item pointed to is the symbolic reference of a field.

The generating functions of getfield bytecode instructions are TemplateTable::getfield(). These generating functions are as follows:

void TemplateTable::getfield(int byte_no) {
  getfield_or_static(byte_no, false); // Byte of getfield_ The no value is 1
}

Getfield will eventually be called_ or_ The static () function generates machine instruction fragments. The assembly code corresponding to the machine instruction fragment generated by this function is as follows:

0x00007fffe10202d0: movzwl 0x1(%r13),%edx
0x00007fffe10202d5: mov    -0x28(%rbp),%rcx
0x00007fffe10202d9: shl    $0x2,%edx
0x00007fffe10202dc: mov    0x10(%rcx,%rdx,8),%ebx
0x00007fffe10202e0: shr    $0x10,%ebx
0x00007fffe10202e3: and    $0xff,%ebx
// 0xb4 is the Opcode of the getfield instruction. If it is equal, it indicates that it has been connected and directly jumps to resolved
0x00007fffe10202e9: cmp    $0xb4,%ebx
0x00007fffe10202ef: je     0x00007fffe102038e

0x00007fffe10202f5: mov    $0xb4,%ebx
// Omit by calling MacroAssembler::call_VM() function 
// InterpreterRuntime::resolve_ get_ Assembly code for the put() function 
// ...

Call macroassembler:: call_ The VM () function generates the following code to execute interpreter Runtime:: resolve_ get_ Put() function. MacroAssembler::call_ The assembly of vm() function has been described in detail before. It will not be introduced here. The assembly code is directly given as follows:

0x00007fffe10202fa: callq  0x00007fffe1020304
0x00007fffe10202ff: jmpq   0x00007fffe1020382

0x00007fffe1020304: mov    %rbx,%rsi
0x00007fffe1020307: lea    0x8(%rsp),%rax
0x00007fffe102030c: mov    %r13,-0x38(%rbp)
0x00007fffe1020310: mov    %r15,%rdi
0x00007fffe1020313: mov    %rbp,0x200(%r15)
0x00007fffe102031a: mov    %rax,0x1f0(%r15)
0x00007fffe1020321: test   $0xf,%esp
0x00007fffe1020327: je     0x00007fffe102033f
0x00007fffe102032d: sub    $0x8,%rsp
0x00007fffe1020331: callq  0x00007ffff66b567c
0x00007fffe1020336: add    $0x8,%rsp
0x00007fffe102033a: jmpq   0x00007fffe1020344
0x00007fffe102033f: callq  0x00007ffff66b567c
0x00007fffe1020344: movabs $0x0,%r10
0x00007fffe102034e: mov    %r10,0x1f0(%r15)
0x00007fffe1020355: movabs $0x0,%r10
0x00007fffe102035f: mov    %r10,0x200(%r15)
0x00007fffe1020366: cmpq   $0x0,0x8(%r15)
0x00007fffe102036e: je     0x00007fffe1020379
0x00007fffe1020374: jmpq   0x00007fffe1000420
0x00007fffe1020379: mov    -0x38(%rbp),%r13
0x00007fffe102037d: mov    -0x30(%rbp),%r14
0x00007fffe1020381: retq   

The above code is very simple. It calls the interpreter Runtime:: resolve written by the C + + function_ get_ Put() function, which will fill in the ConstantPoolCacheEntry information in the constant pool cache. The meaning of ConstantPoolCache, ConstantPoolCacheEntry, and the fields in ConstantPoolCacheEntry have been described in detail in deep analysis of Java virtual machine: source code analysis and detailed explanation of instances (basic volume), and will not be introduced here.

0x00007fffe1020382: movzwl 0x1(%r13),%edx
0x00007fffe1020387: mov    -0x28(%rbp),%rcx
0x00007fffe102038b: shl    $0x2,%edx

---- resolved ---- 

// Get the in [_indices,_f1,_f2,_flags]_ Since the ConstantPoolCache occupies 16 bytes, the_ indices
// And_ Each occupies 8 bytes, so_ The offset of f2 is 32 bytes, that is 0x32
// _ The byte offset of the field in the oop instance is saved in f2. This offset can be used to obtain the byte offset of the field stored in
// Value in oop
0x00007fffe102038e: mov    0x20(%rcx,%rdx,8),%rbx

// Get the in [_indices,_f1,_f2,_flags]_ flags 
0x00007fffe1020393: mov    0x28(%rcx,%rdx,8),%eax

// Pop the objectref object from the stack to% rcx
0x00007fffe1020397: pop    %rcx

// Evoke OS NULL exception if reg = NULL by
// accessing M[reg] w/o changing any (non-CC) registers
// NOTE: cmpl is plenty (enough) here to provoke a segv
0x00007fffe1020398: cmp    (%rcx),%rax

// Will_ flags moves 28 bits to the right, leaving TosState
0x00007fffe102039b: shr    $0x1c,%eax
0x00007fffe102039e: and    $0xf,%eax
// If it is not equal, it means that the value of TosState is not 0, then jump to notByte
0x00007fffe10203a1: jne    0x00007fffe10203ba

// btos

// The number of btos is 0. When the code is executed here, the cache at the top of the stack may require btos
// %Objectref is stored in rcx and objectref is stored in rbx_ Get the value corresponding to the field and store it in% rax
0x00007fffe10203a7: movsbl (%rcx,%rbx,1),%eax
0x00007fffe10203ab: push   %rax

// Rewrite the bytecode instruction to bytecodes::_ fast_ Opcode of bgetfield is stored in% ecx
0x00007fffe10203ac: mov    $0xcc,%ecx
// Bytecodes::_ fast_ Opcode of bgetfield is updated to opcode of bytecode instruction
0x00007fffe10203b1: mov    %cl,0x0(%r13)
// Jump to -- Done----
0x00007fffe10203b5: jmpq   0x00007fffe102050f
---- notByte ----
0x00007fffe10203ba: cmp    $0x7,%eax
0x00007fffe10203bd: jne    0x00007fffe102045d  // Jump to notObj


// atos

// Call macroassembler:: load_ heap_ The OOP () function generates the following code
0x00007fffe10203c3: mov    (%rcx,%rbx,1),%eax
// ... omit some code
// End macroassembler:: load_ heap_ Call of oop() function
0x00007fffe102044e: push   %rax
// Rewrite bytecode instruction as Bytecodes::_fast_agetfield
0x00007fffe102044f: mov    $0xcb,%ecx
0x00007fffe1020454: mov    %cl,0x0(%r13)
0x00007fffe1020458: jmpq   0x00007fffe102050f
// -- notObj --
0x00007fffe102045d: cmp    $0x3,%eax
0x00007fffe1020460: jne    0x00007fffe1020478 // Jump to notInt

// itos

0x00007fffe1020466: mov    (%rcx,%rbx,1),%eax
0x00007fffe1020469: push   %rax
// Rewrite bytecode instruction o Bytecodes::_fast_igetfield
0x00007fffe102046a: mov    $0xd0,%ecx
0x00007fffe102046f: mov    %cl,0x0(%r13)
0x00007fffe1020473: jmpq   0x00007fffe102050f
// --- notInt ----
0x00007fffe1020478: cmp    $0x1,%eax
0x00007fffe102047b: jne    0x00007fffe1020494 // Jump to notChar


// ctos

0x00007fffe1020481: movzwl (%rcx,%rbx,1),%eax
0x00007fffe1020485: push   %rax
// Rewrite bytecode instruction as Bytecodes::_fast_cgetfield
0x00007fffe1020486: mov    $0xcd,%ecx
0x00007fffe102048b: mov    %cl,0x0(%r13)
0x00007fffe102048f: jmpq   0x00007fffe102050f
// ---- notChar ----
0x00007fffe1020494: cmp    $0x2,%eax
0x00007fffe1020497: jne    0x00007fffe10204b0 // Jump to notShort

// stos

0x00007fffe102049d: movswl (%rcx,%rbx,1),%eax
0x00007fffe10204a1: push   %rax
// Rewrite bytecode instruction as Bytecodes::_fast_sgetfield
0x00007fffe10204a2: mov    $0xd2,%ecx
0x00007fffe10204a7: mov    %cl,0x0(%r13)
0x00007fffe10204ab: jmpq   0x00007fffe102050f
// ---- notShort ----
0x00007fffe10204b0: cmp    $0x4,%eax
0x00007fffe10204b3: jne    0x00007fffe10204d3 // Jump to notLong

// ltos

0x00007fffe10204b9: mov    (%rcx,%rbx,1),%rax
0x00007fffe10204bd: sub    $0x10,%rsp
0x00007fffe10204c1: mov    %rax,(%rsp)
// Rewrite bytecode instruction as Bytecodes::_fast_lgetfield,
0x00007fffe10204c5: mov    $0xd1,%ecx
0x00007fffe10204ca: mov    %cl,0x0(%r13)
0x00007fffe10204ce: jmpq   0x00007fffe102050f
// ---- notLong ----
0x00007fffe10204d3: cmp    $0x5,%eax
0x00007fffe10204d6: jne    0x00007fffe10204f8 // Jump to notFloat


// ftos
0x00007fffe10204dc: vmovss (%rcx,%rbx,1),%xmm0
0x00007fffe10204e1: sub    $0x8,%rsp
0x00007fffe10204e5: vmovss %xmm0,(%rsp)
// Rewrite bytecode instruction as Bytecodes::_fast_fgetfield
0x00007fffe10204ea: mov    $0xcf,%ecx
0x00007fffe10204ef: mov    %cl,0x0(%r13)
0x00007fffe10204f3: jmpq   0x00007fffe102050f
// ---- notFloat ----
0x00007fffe10204f8: vmovsd (%rcx,%rbx,1),%xmm0
0x00007fffe10204fd: sub    $0x10,%rsp
0x00007fffe1020501: vmovsd %xmm0,(%rsp)
0x00007fffe1020506: mov    $0xce,%ecx
0x00007fffe102050b: mov    %cl,0x0(%r13)

// -- Done --  
  

We need to introduce some custom instructions inside the virtual machine. The templates of these custom instructions are as follows:

// JVM bytecodes
def(Bytecodes::_fast_agetfield      , ubcp|____|____|____, atos, atos, fast_accessfield    ,  atos        );
def(Bytecodes::_fast_bgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
def(Bytecodes::_fast_cgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
def(Bytecodes::_fast_dgetfield      , ubcp|____|____|____, atos, dtos, fast_accessfield    ,  dtos        );
def(Bytecodes::_fast_fgetfield      , ubcp|____|____|____, atos, ftos, fast_accessfield    ,  ftos        );
def(Bytecodes::_fast_igetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );
def(Bytecodes::_fast_lgetfield      , ubcp|____|____|____, atos, ltos, fast_accessfield    ,  ltos        );
def(Bytecodes::_fast_sgetfield      , ubcp|____|____|____, atos, itos, fast_accessfield    ,  itos        );

With_ fast_ For example, the bytecode instruction defined inside the agetfield generates TemplateTable::fast_accessfield() function, assembly code is as follows:

0x00007fffe101e4e1: movzwl 0x1(%r13),%ebx
0x00007fffe101e4e6: mov    -0x28(%rbp),%rcx
0x00007fffe101e4ea: shl    $0x2,%ebx
// Calculate% rcx+%rdx*8+0x20 and obtain the values in constantpoolcacheentry [_indexes, _f1, _f2, _flags]_ f2
// Because the size of ConstantPoolCache is 0x16 bytes,% rcx+0x20 locates to the start of the first ConstantPoolCacheEntry
// %rdx*8 calculates the byte offset relative to the first ConstantPoolCacheEntry
0x00007fffe101e4ed: mov    0x20(%rcx,%rbx,8),%rbx

// Check null exception
0x00007fffe101e4f2: cmp    (%rax),%rax
// %objectref is stored in rax, that is, to get the value of the field from this instance, offset it by% rbx
// The offset value can be obtained and loaded into% eax
0x00007fffe101e4f5: mov    (%rax,%rbx,1),%eax
  

Other bytecode instructions are similar and will not be introduced here. It can be seen from here that we do not need to execute the assembly instructions corresponding to getfield, but only execute_ These instructions are much simpler than getfield instructions and greatly improve the speed of interpretation and execution.   

Part 26 - putstatic of virtual machine object operation instructions

The assembly code execution logic of getstatic and getfield instructions has been introduced before. This article introduces the execution logic of putstatic instructions. putfield will not introduce it. You can study it yourself. I believe you have this strength.

The putstatic instruction assigns a value to the static field of the specified class. The format of bytecode instruction is as follows:

putstatic indexbyte1 indexbyte2

The unsigned numbers indexbyte1 and indexbyte2 are constructed as (indexbyte1 < < 8) |indexbyte2. The runtime constant pool item pointed to by the index should be a symbolic reference to a field.

The template of the instruction is defined as follows:

def(Bytecodes::_putstatic           , ubcp|____|clvm|____, vtos, vtos, putstatic           , f2_byte      );  

The generating function is putstatic(), and the implementation of the function is as follows:

void TemplateTable::putstatic(int byte_no) {
  putfield_or_static(byte_no, false);
}

Call templatetable:: putfield_ or_ The assembly code corresponding to the machine instruction generated by the static() function is as follows:

0x00007fffe101ff90: movzwl 0x1(%r13),%edx
0x00007fffe101ff95: mov    -0x28(%rbp),%rcx
0x00007fffe101ff99: shl    $0x2,%edx
0x00007fffe101ff9c: mov    0x10(%rcx,%rdx,8),%ebx
0x00007fffe101ffa0: shr    $0x18,%ebx
0x00007fffe101ffa3: and    $0xff,%ebx
// Whether the putstatic instruction has been connected. If so, jump to resolved
0x00007fffe101ffa9: cmp    $0xb3,%ebx
0x00007fffe101ffaf: je     0x00007fffe102004e 

Call templatetable:: Resolve_ cache_ and_ The index() function generates the following assembly code:

// After execution, the description field is not connected yet
0x00007fffe101ffb5: mov    $0xb3,%ebx

// Call macroassembler:: call_ The VM () function generates the following code,
// Used to execute InterpreterRuntime::resolve_get_put() function
0x00007fffe101ffba: callq  0x00007fffe101ffc4
0x00007fffe101ffbf: jmpq   0x00007fffe1020042
0x00007fffe101ffc4: mov    %rbx,%rsi
0x00007fffe101ffc7: lea    0x8(%rsp),%rax
0x00007fffe101ffcc: mov    %r13,-0x38(%rbp)
0x00007fffe101ffd0: mov    %r15,%rdi
0x00007fffe101ffd3: mov    %rbp,0x200(%r15)
0x00007fffe101ffda: mov    %rax,0x1f0(%r15)
0x00007fffe101ffe1: test   $0xf,%esp
0x00007fffe101ffe7: je     0x00007fffe101ffff
0x00007fffe101ffed: sub    $0x8,%rsp
0x00007fffe101fff1: callq  0x00007ffff66b567c
0x00007fffe101fff6: add    $0x8,%rsp
0x00007fffe101fffa: jmpq   0x00007fffe1020004
0x00007fffe101ffff: callq  0x00007ffff66b567c
0x00007fffe1020004: movabs $0x0,%r10
0x00007fffe102000e: mov    %r10,0x1f0(%r15)
0x00007fffe1020015: movabs $0x0,%r10
0x00007fffe102001f: mov    %r10,0x200(%r15)
0x00007fffe1020026: cmpq   $0x0,0x8(%r15)
0x00007fffe102002e: je     0x00007fffe1020039
0x00007fffe1020034: jmpq   0x00007fffe1000420
0x00007fffe1020039: mov    -0x38(%rbp),%r13
0x00007fffe102003d: mov    -0x30(%rbp),%r14
0x00007fffe1020041: retq   


0x00007fffe1020042: movzwl 0x1(%r13),%edx
0x00007fffe1020047: mov    -0x28(%rbp),%rcx
0x00007fffe102004b: shl    $0x2,%edx

The assembly code generated next is as follows:

// ---- resolved ----

// When the following code is executed, it indicates that the field has been connected

0x00007fffe102004e: mov    0x20(%rcx,%rdx,8),%rbx
0x00007fffe1020053: mov    0x28(%rcx,%rdx,8),%eax
0x00007fffe1020057: mov    0x18(%rcx,%rdx,8),%rcx
0x00007fffe102005c: mov    0x70(%rcx),%rcx
0x00007fffe1020060: mov    %eax,%edx
// Will_ flags moves 21 bits to the right to judge whether there is volatile keyword
0x00007fffe1020062: shr    $0x15,%edx
0x00007fffe1020065: and    $0x1,%edx
// Will_ flags moves 28 bits to the right, leaving TosState
0x00007fffe1020068: shr    $0x1c,%eax

// If not btos, jump to notByte
0x00007fffe102006b: and    $0xf,%eax
0x00007fffe102006e: jne    0x00007fffe1020083

// btos

// Store the value at the top of the stack in% eax, and this value will be written to the corresponding field
0x00007fffe1020074: mov    (%rsp),%eax
0x00007fffe1020077: add    $0x8,%rsp
// %rcx is_ java_mirror,% rbx is_ f2, represents the offset of the domain in the class
0x00007fffe102007b: mov    %al,(%rcx,%rbx,1)
0x00007fffe102007e: jmpq   0x00007fffe10201be  // Jump to Done
// -- notByte --
// If not atos, jump to notObj
0x00007fffe1020083: cmp    $0x7,%eax
0x00007fffe1020086: jne    0x00007fffe1020130

// atos
// Pop the value at the top of the stack into% rax, which will be used to update the value of the corresponding field
0x00007fffe102008c: pop    %rax
// ...
// Update the value to the corresponding field
0x00007fffe1020115: mov    %eax,(%rcx,%rbx,1)
// Where 0x9 is CardTableModRefBS::card_shift, shr indicates logical shift to the right, because%rcx points to
// The first address of the java.lang.Class instance. Move it to the right and%rcx calculate the index of the card table
0x00007fffe1020118: shr    $0x9,%rcx
// The address constant $0x7fffe07ff000 represents the base address of the card table
0x00007fffe102011c: movabs $0x7fffe07ff000,%r10 
// Mark the corresponding card table entry as dirty, where the constant 0x0 indicates a dirty card
0x00007fffe1020126: movb $0x0,(%r10,%rcx,1) 
0x00007fffe102012b: jmpq 
0x00007fffe10201be // Jump to Done
// ---- notObj ----
// If it is not itos, jump to notInt
0x00007fffe1020130: cmp    $0x3,%eax
0x00007fffe1020133: jne    0x00007fffe1020148

// itos
0x00007fffe1020139: mov    (%rsp),%eax
// If not ctos, jump to notChar
0x00007fffe102013c: add    $0x8,%rsp
0x00007fffe1020140: mov    %eax,(%rcx,%rbx,1)
0x00007fffe1020143: jmpq   0x00007fffe10201be   // Jump to Done
0x00007fffe1020148: cmp    $0x1,%eax
0x00007fffe102014b: jne    0x00007fffe1020161

// ctos
0x00007fffe1020151: mov    (%rsp),%eax
0x00007fffe1020154: add    $0x8,%rsp
0x00007fffe1020158: mov    %ax,(%rcx,%rbx,1)
0x00007fffe102015c: jmpq   0x00007fffe10201be  // Jump to Done
0x00007fffe1020161: cmp    $0x2,%eax
0x00007fffe1020164: jne    0x00007fffe102017a

// stos
0x00007fffe102016a: mov    (%rsp),%eax
0x00007fffe102016d: add    $0x8,%rsp
0x00007fffe1020171: mov    %ax,(%rcx,%rbx,1)
0x00007fffe1020175: jmpq   0x00007fffe10201be  // Jump to Done
0x00007fffe102017a: cmp    $0x4,%eax
0x00007fffe102017d: jne    0x00007fffe1020194

// ltos
0x00007fffe1020183: mov    (%rsp),%rax
0x00007fffe1020187: add    $0x10,%rsp
0x00007fffe102018b: mov    %rax,(%rcx,%rbx,1)
0x00007fffe102018f: jmpq   0x00007fffe10201be  // Jump to Done
0x00007fffe1020194: cmp    $0x5,%eax
0x00007fffe1020197: jne    0x00007fffe10201b0

// ftos
0x00007fffe102019d: vmovss (%rsp),%xmm0
0x00007fffe10201a2: add    $0x8,%rsp
0x00007fffe10201a6: vmovss %xmm0,(%rcx,%rbx,1)
0x00007fffe10201ab: jmpq   0x00007fffe10201be   // Jump to Done

// dtos
0x00007fffe10201b0: vmovsd (%rsp),%xmm0
0x00007fffe10201b5: add    $0x10,%rsp
0x00007fffe10201b9: vmovsd %xmm0,(%rcx,%rbx,1)


// ---- Done ----

0x00007fffe10201be: test   %edx,%edx
0x00007fffe10201c0: je     0x00007fffe10201cb
0x00007fffe10201c6: lock addl $0x0,(%rsp)

// ---- notVolatile ---- 
  

In the above code, the two most noteworthy points are as follows:

(1) When updating the reference field, mark the corresponding card table item as dirty through the barrier, so that the dirty card can be scanned in the GC process to mark the active object without omission;

(2) When the field is decorated with volatile keyword, you need to fill in the prefix of lock instruction. This prefix has not been introduced before when introducing x86-64 machine instructions. Here is an excerpt from others' introduction to this instruction:

The Intel manual describes the lock prefix as follows:

  1. Ensure the atomicity of the modified instruction execution;
  2. It is forbidden to reorder the instruction with the previous and subsequent read-write instructions;
  3. After the instruction is executed, all data in the write buffer is flushed to memory (so that other modifications before the instruction are visible to all processors).

All x86 CPUs have the ability to lock a specific memory address. When the specific memory address is locked, it can prevent other system buses from reading or modifying the memory address. This ability is through   The lock instruction prefix is implemented by adding the following assembly instructions. When used   When the lock instruction is prefixed, it will cause the CPU to declare a lock# signal, so as to ensure the mutually exclusive use of this memory address in a multiprocessor system or a multi-threaded competitive environment. When the command is executed, the locking action will disappear.

Part 27 - operand stack management instructions for virtual machine bytecode instructions

The bytecode instructions related to operand stack management are shown in the following table.

0x57popPop up stack top value   (value cannot be of type long or double)
0x58pop2Pop up one (long or double) or two values at the top of the stack (other)
0x59dupCopy the value at the top of the stack and push the copied value into the top of the stack
0x5adup_x1Copy the value at the top of the stack and press the two copied values into the top of the stack
0x5bdup_x2Copy the top value and push three (or two) copied values into the top of the stack
0x5cdup2Copy one (long or double type) or two (other) values at the top of the stack and push the copied value into the top of the stack
0x5ddup2_x1dup_x1   Double version of instruction
0x5edup2_x2dup_x2   Double version of instruction
0x5fswapSwap the two values at the top of the stack (values cannot be of type long or double)

The template corresponding to bytecode instruction is defined as follows:

def(Bytecodes::_pop         , ____|____|____|____, vtos, vtos, pop         ,  _           );
def(Bytecodes::_pop2        , ____|____|____|____, vtos, vtos, pop2        ,  _           );
def(Bytecodes::_dup         , ____|____|____|____, vtos, vtos, dup         ,  _           );
def(Bytecodes::_dup_x1      , ____|____|____|____, vtos, vtos, dup_x1      ,  _           );
def(Bytecodes::_dup_x2      , ____|____|____|____, vtos, vtos, dup_x2      ,  _           );
def(Bytecodes::_dup2        , ____|____|____|____, vtos, vtos, dup2        ,  _           );
def(Bytecodes::_dup2_x1     , ____|____|____|____, vtos, vtos, dup2_x1     ,  _           );
def(Bytecodes::_dup2_x2     , ____|____|____|____, vtos, vtos, dup2_x2     ,  _           );
def(Bytecodes::_swap        , ____|____|____|____, vtos, vtos, swap        ,  _           );

pop instruction pops up the value at the top of the stack. The corresponding assembly code is as follows:

add    $0x8,%rsp   

The pop2 instruction pops up the top value of the stack. The corresponding assembly code is as follows:

add    $0x10,%rsp  

The dup instruction copies the stack top value and pushes the copied value into the stack top. The corresponding assembly code is as follows:

mov    (%rsp),%rax
push   %rax

The swap instruction interchanges the two values at the top of the stack (the value cannot be of type long or double). The corresponding assembly code is as follows:

mov    0x8(%rsp),%rcx
mov    (%rsp),%rax
mov    %rcx,(%rsp)
mov    %rax,0x8(%rsp)

The execution logic of the instruction is relatively simple and will not be introduced here.

Part 28 - control transfer instructions for virtual machine bytecode instructions

The bytecode instructions related to control transfer are shown in the table below.  

0x99ifeqJump when the int value at the top of the stack is equal to 0
0x9aifneJump when the int value at the top of the stack is not equal to 0
0x9bifltJump when the int value at the top of the stack is less than 0
0x9cifgeJump when the int value at the top of the stack is greater than or equal to 0
0x9difgtJump when the int value at the top of the stack is greater than 0
0x9eifleJump when the int value at the top of the stack is less than or equal to 0
0x9fif_icmpeqCompare the size of two int values at the top of the stack. Jump when the result is equal to 0
0xa0if_icmpneCompare the size of two int values at the top of the stack. Jump when the result is not equal to 0
0xa1if_icmpltCompare the size of two int values at the top of the stack. Jump when the result is less than 0
0xa2if_icmpgeCompare the size of two int values at the top of the stack. Jump when the result is greater than or equal to 0
0xa3if_icmpgtCompare the size of two int values at the top of the stack. Jump when the result is greater than 0
0xa4if_icmpleCompare the size of two int values at the top of the stack. Jump when the result is less than or equal to 0
0xa5if_acmpeqCompare the two reference values at the top of the stack, and jump when the results are equal
0xa6if_acmpneCompare the two reference values at the top of the stack, and jump when the results are not equal
0xa7gotoUnconditional jump
0xa8jsrJump to the specified 16 bit offset position and push the address of the next instruction of jsr into the top of the stack
0xa9retReturn to the instruction location of the index of the local variable instruction (generally used in combination with JSR or jsr_w)
0xaatableswitchFor switch conditional jump, case value is continuous (variable length instruction)
0xablookupswitchFor switch conditional jump, case value is discontinuous (variable length instruction)
0xacireturnReturns int from the current method
0xadlreturnReturns long from the current method
0xaefreturnReturns float from the current method
0xafdreturnReturns double from the current method
0xb0areturnReturns an object reference from the current method
0xb1returnReturns void from the current method
0xc6ifnullJump when null
0xc7ifnonnullJump when not null
0xc8goto_wUnconditional jump (wide index)
0xc9jsr_wJump to the specified 32-bit offset position and push the jsr_w next instruction address to the top of the stack

 

The template is defined as follows:

def(Bytecodes::_ifeq                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , equal        );
def(Bytecodes::_ifne                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , not_equal    );
def(Bytecodes::_iflt                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , less         );
def(Bytecodes::_ifge                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , greater_equal);
def(Bytecodes::_ifgt                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , greater      );
def(Bytecodes::_ifle                , ubcp|____|clvm|____, itos, vtos, if_0cmp             , less_equal   );
def(Bytecodes::_if_icmpeq           , ubcp|____|clvm|____, itos, vtos, if_icmp             , equal        );
def(Bytecodes::_if_icmpne           , ubcp|____|clvm|____, itos, vtos, if_icmp             , not_equal    );
def(Bytecodes::_if_icmplt           , ubcp|____|clvm|____, itos, vtos, if_icmp             , less         );
def(Bytecodes::_if_icmpge           , ubcp|____|clvm|____, itos, vtos, if_icmp             , greater_equal);
def(Bytecodes::_if_icmpgt           , ubcp|____|clvm|____, itos, vtos, if_icmp             , greater      );
def(Bytecodes::_if_icmple           , ubcp|____|clvm|____, itos, vtos, if_icmp             , less_equal   );
def(Bytecodes::_if_acmpeq           , ubcp|____|clvm|____, atos, vtos, if_acmp             , equal        );
def(Bytecodes::_if_acmpne           , ubcp|____|clvm|____, atos, vtos, if_acmp             , not_equal    );
def(Bytecodes::_goto                , ubcp|disp|clvm|____, vtos, vtos, _goto               ,  _           );
def(Bytecodes::_jsr                 , ubcp|disp|____|____, vtos, vtos, jsr                 ,  _           ); // result is not an oop, so do not transition to atos
def(Bytecodes::_ret                 , ubcp|disp|____|____, vtos, vtos, ret                 ,  _           );
def(Bytecodes::_tableswitch         , ubcp|disp|____|____, itos, vtos, tableswitch         ,  _           );
def(Bytecodes::_lookupswitch        , ubcp|disp|____|____, itos, itos, lookupswitch        ,  _           );
def(Bytecodes::_ireturn             , ____|disp|clvm|____, itos, itos, _return             , itos         );
def(Bytecodes::_lreturn             , ____|disp|clvm|____, ltos, ltos, _return             , ltos         );
def(Bytecodes::_freturn             , ____|disp|clvm|____, ftos, ftos, _return             , ftos         );
def(Bytecodes::_dreturn             , ____|disp|clvm|____, dtos, dtos, _return             , dtos         );
def(Bytecodes::_areturn             , ____|disp|clvm|____, atos, atos, _return             , atos         );
def(Bytecodes::_return              , ____|disp|clvm|____, vtos, vtos, _return             , vtos         );

def(Bytecodes::_ifnull              , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , equal        );
def(Bytecodes::_ifnonnull           , ubcp|____|clvm|____, atos, vtos, if_nullcmp          , not_equal    );
def(Bytecodes::_goto_w              , ubcp|____|clvm|____, vtos, vtos, goto_w              ,  _           );
def(Bytecodes::_jsr_w               , ubcp|____|____|____, vtos, vtos, jsr_w               ,  _           );

The assembly implementation of several typical instructions is introduced below.

1. goto instruction

The generation function of goto bytecode instruction is templatetable:: # u goto(). The generated assembly code is as follows: (add the command - Xint when generating code)    - 20: - profileinterpreter, which eliminates the generation of unnecessary instructions)

// Method * saved in% rcx
0x00007fffe1019df0: mov    -0x18(%rbp),%rcx
// Store the index(2 bytes) after goto in% edx
0x00007fffe1019df4: movswl 0x1(%r13),%edx
0x00007fffe1019df9: bswap  %edx
// Arithmetic shift right instruction
0x00007fffe1019dfb: sar    $0x10,%edx
// A double word symbol is extended and sent to a four word address
0x00007fffe1019dfe: movslq %edx,%rdx
// Add the current bytecode address with the offset saved by rdx to calculate the jump target address
0x00007fffe1019e01: add    %rdx,%r13
// %r13 has become the target jump address. Here is the first bytecode of the jump address loaded into rbx
0x00007fffe1019e04: movzbl 0x0(%r13),%ebx

// continue with the bytecode @ target
// eax: return bci for jsr's, unused otherwise
// ebx: target bytecode
// r13: target bcp
// Start executing the bytecode at the jump address, where the constant address is
// The first address of the TemplateInterpreter::_active_table whose stack top cache status is vtos
0x00007fffe1019e09: movabs $0x7ffff73ba4a0,%r10
0x00007fffe1019e13: jmpq   *(%r10,%rbx,8)

In fact, the goto instruction actually generates more assembly code than the above code, because the goto instruction is a branch instruction, in which some performance statistics will be made to assist in compilation optimization, and if the goto is in a loop, it may also involve the technology of stack replacement. Therefore, we will introduce other functions of the goto instruction in detail when we introduce the corresponding technical points later Some assembly logic.  

2. ifeq, ifne and other instructions

At present, the generation function of ifeq, ifne and other instructions is TemplateTable::if_0cmp(). Ifeq bytecode instructions represent the comparison between the top value of the stack and the zero value. If and only if the value of int type at the top of the stack is 0, the comparison result is true. The corresponding assembly code is as follows:

0x00007fffe10196c7: test   %eax,%eax
// When the stack top cache% eax is not 0, skip to not_taken directly
0x00007fffe10196c9: jne    0x00007fffe10196f6

// Assembly code generated by calling TemplateTable::branch(false,false) function

// Copy the Method * saved in the current stack frame to rcx
0x00007fffe10196cf: mov    -0x18(%rbp),%rcx
// Read the 2-byte data starting from the 1-byte backward offset of the current bytecode position into edx
0x00007fffe10196d3: movswl 0x1(%r13),%edx
// Reverse the byte order of values in% edx
0x00007fffe10196d8: bswap  %edx
// Shift the value in edx by 16 bits to the right. The above two steps are to calculate the offset of the jump branch
0x00007fffe10196da: sar    $0x10,%edx
// Expand the data in edx from 2 bytes to 4 bytes
0x00007fffe10196dd: movslq %edx,%rdx
// Add the current bytecode address with the offset saved by rdx to calculate the jump target address
0x00007fffe10196e0: add    %rdx,%r13
// r13 has become the target jump address. Here is the first bytecode of the jump address loaded into ebx
0x00007fffe10196e3: movzbl 0x0(%r13),%ebx

// Start executing the bytecode at the jump address, where the constant address is
// TemplateInterpreter::_ active_ The first address of table and the top of stack cache state is vtos
0x00007fffe10196e8: movabs $0x7ffff73ba4a0,%r10
0x00007fffe10196f2: jmpq   *(%r10,%rbx,8)

// -- not_taken -- 

Similar instruction implementation logic is also highly similar. If you are interested, you can study it yourself.  

3. lookupswitch, tableswitch and other instructions

The lookupswitch instruction finds the paired branch in the jump table according to the key value and jumps. The specific format is shown in the following figure.

 

This is a variable length instruction and requires all operands to be aligned with 4 bytes, so there may be 0 to 3 bytes immediately after the lookupswitch instruction as blank filling, and the following default, npairs, etc. are represented by 4 bytes. The address calculated from the current method (the first bytecode instruction), that is, a series of 32-bit signed integer values followed by blank filling, It includes the default jump address default, the number of matching coordinates npairs, and the matching coordinates of npairs group. The value of npairs should be greater than or equal to 0. Each set of matching coordinates contains an integer value match and a signed 32-bit offset. All the above 32-bit signed values are calculated in the following way:

(byte1<<24)|(byte2<<24)|(byte3<<24)|byte4

The tableswitch instruction finds the paired branch in the jump table according to the key value and jumps. The specific format is shown in the following figure.

This is a variable length instruction and requires all operands to be aligned with 4 bytes, so there may be 0 to 3 bytes immediately after the lookupswitch instruction as blank filling, and the following default, lowbyte, highbyte, etc. are represented by 4 bytes, which is the address calculated from the current method (the first bytecode instruction), That is, a series of 32-bit signed integer values are filled immediately after the blank, including the default jump address default, the high value high and the low value low, followed by high-low+1 signed 32-bit offset. All the above 32-bit signed values are calculated in the following way:

(byte1<<24)|(byte2<<24)|(byte3<<24)|byte4

The generating function is TemplateTable::tableswitch(). The generated assembly is as follows:

// align r13, aligned by 4 bytes
0x00007fffe1019fa7: lea    0x4(%r13),%rbx
0x00007fffe1019fab: and    $0xfffffffffffffffc,%rbx
// load lo & hi
0x00007fffe1019faf: mov    0x4(%rbx),%ecx
0x00007fffe1019fb2: mov    0x8(%rbx),%edx
0x00007fffe1019fb5: bswap  %ecx
0x00007fffe1019fb7: bswap  %edx

// check against lo & hi
// %ecx stores lowbyte
0x00007fffe1019fb9: cmp    %ecx,%eax
// If it is lower than the low value, jump to default_case
0x00007fffe1019fbb: jl     0x00007fffe1019feb 
// %High byte is stored in edx
0x00007fffe1019fc1: cmp    %edx,%eax
// If it is higher than the high value, jump to default_case
0x00007fffe1019fc3: jg     0x00007fffe1019feb

// lookup dispatch offset
0x00007fffe1019fc9: sub    %ecx,%eax
// %rbx stores the aligned bytecode instruction address, and rax stores the stack top cache value
0x00007fffe1019fcb: mov    0xc(%rbx,%rax,4),%edx
// -- continue_execution --
// continue execution
0x00007fffe1019fcf: bswap  %edx
0x00007fffe1019fd1: movslq %edx,%rdx
0x00007fffe1019fd4: movzbl 0x0(%r13,%rdx,1),%ebx
0x00007fffe1019fda: add    %rdx,%r13

0x00007fffe1019fdd: movabs $0x7ffff73ba4a0,%r10
0x00007fffe1019fe7: jmpq   *(%r10,%rbx,8)

// -- default_case --
// handle default
0x00007fffe1019feb: mov (%rbx),%edx 
// Jump to continue_execution
0x00007fffe1019fed: jmp 0x00007fffe1019fcf

Keywords: Java jvm

Added by deveed on Thu, 25 Nov 2021 06:12:23 +0200