The entry_pointJava method entry address is generated in the CallStub structure when the JVM calls a Java method using the JavaCalls:: Call method, call_helper, and CallStub. So today I will focus on the template interpreter entry address, about the JIT compiler for the time being. JavaCalls:: Call_help is a method entry address for method via its _from_interpreted_entry field. So where is the method entry address set? The method execution entry address is set when the method is linked.

void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) {// omit address entry_point = method->from_interpreted_entry(); }Copy the code

Method the link

In method. CPP, link_method is the method linking work,

  1. If _i2i_entry already exists, it indicates that the link has been created.
  2. Interpreter::entry_for_method gets the entry address of the compiler
  3. Set_interpreter_entry Sets the values of _from_interpreted_entry,_i2i_entry as the second step to obtain the method entry address.
  4. Make_adapters are adapters for converting between an interpreter and a compiler.
void Method::link_method(methodHandle h_method, TRAPS) { if (_i2i_entry ! = NULL) return; address entry = Interpreter::entry_for_method(h_method); assert(entry ! = NULL, "interpreter entry must be non-null"); // Sets both _i2i_entry and _from_interpreted_entry set_interpreter_entry(entry); // Don't overwrite already registered native entries. if (is_native() && ! has_native_function()) { set_native_function( SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), ! native_bind_event_is_interesting); } (void) make_adapters(h_method, CHECK); }Copy the code

The following is the entry_for_method method of the Interpreter parent class AbstractInterpreter,

  1. Call entry_for_kind to get the method type.
  2. Use the method type to go to _entry_table to obtain the corresponding method entry address.
AbstractInterpreter{ static address entry_for_kind(MethodKind k) { assert(0 <= k && k < number_of_method_entries, "illegal kind"); return _entry_table[k]; } static address entry_for_method(methodHandle m) { return entry_for_kind(method_kind(m)); }}Copy the code

Interpreter generation method _entry_table Entry table

The JVM generates a different entry address for the method entry instruction for each of the following types of methods. Let’s focus first on zerolocal types which are different Java method entries.

 enum MethodKind {
    zerolocals,                                                 // method needs locals initialization
    zerolocals_synchronized,                                    // method needs locals initialization & is synchronized
    native,                                                     // native method
    native_synchronized,                                        // native method & is synchronized
    empty,                                                      // empty method (code: _return)
    accessor,                                                   // accessor method (code: _aload_0, _getfield, _(a|i)return)
    abstract,                                                   // abstract method (throws an AbstractMethodException)
    method_handle_invoke_FIRST,                                 // java.lang.invoke.MethodHandles::invokeExact, etc.
    method_handle_invoke_LAST                                   = (method_handle_invoke_FIRST
                                                                   + (vmIntrinsics::LAST_MH_SIG_POLY
                                                                      - vmIntrinsics::FIRST_MH_SIG_POLY)),
    java_lang_math_sin,                                         // implementation of java.lang.Math.sin   (x)
    java_lang_math_cos,                                         // implementation of java.lang.Math.cos   (x)
    java_lang_math_tan,                                         // implementation of java.lang.Math.tan   (x)
    java_lang_math_abs,                                         // implementation of java.lang.Math.abs   (x)
    java_lang_math_sqrt,                                        // implementation of java.lang.Math.sqrt  (x)
    java_lang_math_log,                                         // implementation of java.lang.Math.log   (x)
    java_lang_math_log10,                                       // implementation of java.lang.Math.log10 (x)
    java_lang_math_pow,                                         // implementation of java.lang.Math.pow   (x,y)
    java_lang_math_exp,                                         // implementation of java.lang.Math.exp   (x)
    java_lang_ref_reference_get,                                // implementation of java.lang.ref.Reference.get()
    java_util_zip_CRC32_update,                                 // implementation of java.util.zip.CRC32.update()
    java_util_zip_CRC32_updateBytes,                            // implementation of java.util.zip.CRC32.updateBytes()
    java_util_zip_CRC32_updateByteBuffer,                       // implementation of java.util.zip.CRC32.updateByteBuffer()
    number_of_method_entries,
    invalid = -1
  };
Copy the code

Method entry table is generated in the macro definition


#define method_entry(kind)                                                                    \
  { CodeletMark cm(_masm, "method entry point (kind = " #kind ")");                    \
    Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind);  \
  }

  // all non-native method kinds
  method_entry(zerolocals)
  method_entry(zerolocals_synchronized)
  method_entry(empty)
  method_entry(accessor)
  method_entry(abstract)
  method_entry(java_lang_math_sin  )
  method_entry(java_lang_math_cos  )
  method_entry(java_lang_math_tan  )
  method_entry(java_lang_math_abs  )
  method_entry(java_lang_math_sqrt )
  method_entry(java_lang_math_log  )
  method_entry(java_lang_math_log10)
  method_entry(java_lang_math_exp  )
  method_entry(java_lang_math_pow  )
  method_entry(java_lang_ref_reference_get)

  initialize_method_handle_entries();
#undef method_entry
Copy the code

The above macro definition calls the parent class’s Generate_method_entry method to generate the method entry.

address AbstractInterpreterGenerator::generate_method_entry(AbstractInterpreter::MethodKind kind) {
  // determine code generation flags
  bool synchronized = false;
  address entry_point = NULL;
  InterpreterGenerator* ig_this = (InterpreterGenerator*)this;

  switch (kind) {
    case Interpreter::zerolocals             :                                                       break;
    case Interpreter::zerolocals_synchronized: synchronized = true;                                  break;
    case Interpreter::native                 : entry_point = ig_this->generate_native_entry(false);  break;
    case Interpreter::native_synchronized    : entry_point = ig_this->generate_native_entry(true);   break;
    case Interpreter::empty                  : entry_point = ig_this->generate_empty_entry();        break;
    case Interpreter::accessor               : entry_point = ig_this->generate_accessor_entry();     break;
    case Interpreter::abstract               : entry_point = ig_this->generate_abstract_entry();     break;

    case Interpreter::java_lang_math_sin     : // fall thru
    case Interpreter::java_lang_math_cos     : // fall thru
    case Interpreter::java_lang_math_tan     : // fall thru
    case Interpreter::java_lang_math_abs     : // fall thru
    case Interpreter::java_lang_math_log     : // fall thru
    case Interpreter::java_lang_math_log10   : // fall thru
    case Interpreter::java_lang_math_sqrt    : // fall thru
    case Interpreter::java_lang_math_pow     : // fall thru
    case Interpreter::java_lang_math_exp     : entry_point = ig_this->generate_math_entry(kind);      break;
    case Interpreter::java_lang_ref_reference_get
                                             : entry_point = ig_this->generate_Reference_get_entry(); break;
    case Interpreter::java_util_zip_CRC32_update
                                             : entry_point = ig_this->generate_CRC32_update_entry();  break;
    case Interpreter::java_util_zip_CRC32_updateBytes
                                             : // fall thru
    case Interpreter::java_util_zip_CRC32_updateByteBuffer
                                             : entry_point = ig_this->generate_CRC32_updateBytes_entry(kind); break;
    default:
      fatal(err_msg("unexpected method kind: %d", kind));
      break;
  }
  if (entry_point) return entry_point;
  return ig_this->generate_normal_entry(synchronized);
}
Copy the code

Below is the body of the Generate_Normal_entry method (this is the generation logic for the 32-bit templateInterpreter platform).

Address InterpreterGenerator: : generate_normal_entry method. (bool synchronized) { bool inc_counter = UseCompiler || CountCompiledCalls; // rbx,: Method* // rsi: sender sp address entry_point = __ pc(); const Address constMethod (rbx, Method::const_offset()); const Address access_flags (rbx, Method::access_flags_offset()); const Address size_of_parameters(rdx, ConstMethod::size_of_parameters_offset()); const Address size_of_locals (rdx, ConstMethod::size_of_locals_offset()); // get parameter size (always needed) __ movptr(rdx, constMethod); __ load_unsigned_short(rcx, size_of_parameters); // rbx,: Method* // rcx: size of parameters // rsi: sender_sp (could differ from sp+wordSize if we were called via c2i ) __ load_unsigned_short(rdx, size_of_locals); // get size of locals in words __ subl(rdx, rcx); // rdx = no. of additional locals // see if we've got enough room on the stack for locals plus overhead. generate_stack_overflow_check(); // get return address __ pop(rax); // compute beginning of parameters (rdi) __ lea(rdi, Address(rsp, rcx, Interpreter::stackElementScale(), -wordSize)); // rdx - # of additional locals // allocate space for locals // explicitly initialize locals { Label exit, loop; __ testl(rdx, rdx); __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0 __ bind(loop); __ push((int32_t)NULL_WORD); // initialize local variables __ decrement(rdx); // until everything initialized __ jcc(Assembler::greater, loop); __ bind(exit); } // initialize fixed part of activation frame generate_fixed_frame(false); // Since at this point in the method invocation the exception handler // would try to exit the monitor of synchronized methods which hasn't // been entered yet, we set the thread local variable // _do_not_unlock_if_synchronized to true. The remove_activation will // check this flag. __ get_thread(rax); const Address do_not_unlock_if_synchronized(rax, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); __ movbool(do_not_unlock_if_synchronized, true); __ profile_parameters_type(rax, rcx, rdx); // increment invocation count & check for overflow Label invocation_counter_overflow; Label profile_method; Label profile_method_continue; if (inc_counter) { generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue); if (ProfileInterpreter) { __ bind(profile_method_continue); } } Label continue_after_compile; __ bind(continue_after_compile); bang_stack_shadow_pages(false); // reset the _do_not_unlock_if_synchronized flag __ get_thread(rax); __ movbool(do_not_unlock_if_synchronized, false); // Start dispatching Java bytecode instructions __ dispatch_next(vTOS); // invocation counter overflow if (inc_counter) { if (ProfileInterpreter) { // We have decided to profile this method in  the interpreter __ bind(profile_method); __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method)); __ set_method_data_pointer_for_bcp(); __ get_method(rbx); __ jmp(profile_method_continue); } // Handle overflow of counter and compile method __ bind(invocation_counter_overflow); generate_counter_overflow(&continue_after_compile); } // jvmti support __ notify_method_entry(); // Dispatch_next (vtos); return entry_point; }Copy the code
  1. __ PC () method, the previous articles have introduced __ it is macro definition, is actually a pointer InterpreterMacroAssembler, call its parent class AbstractAssembler

The PC () method gets the end address of the CodeSection object.

class AbstractAssembler : public ResourceObj { protected: CodeSection* _code_section; address pc() const { return code_section()->end(); }}Copy the code
  1. Rsi is the RSP address of the caller. This line moves the address of a constMethod to the RDX register.
 __ movptr(rdx, constMethod);
Copy the code
  1. The value of the parameter size is then loaded into the RCX register.
__ load_unsigned_short(rcx, size_of_parameters);
Copy the code
  1. Load the size of the local variable table into the RDX register
__ load_unsigned_short(rdx, size_of_locals); // get size of locals in words
Copy the code
  1. Subtract the value of RDX from the value of the RCX register to equal the number of additional local variables in addition to the parameters, and save the remaining values that require additional parameters into the RDX register.
 __ subl(rdx, rcx);  
Copy the code
  1. Check stack overflow
generate_stack_overflow_check();
Copy the code
  1. Save the return address on the stack into the RAX register.
  __ pop(rax);
Copy the code
  1. Offset the address at the top of the stack to the address at the top of the stack (size * the size of an element in the stack 32 bits = 4 bytes), and then move down one machine word (32 bits = 4 bytes) to the top of the stack where the first argument was executed.
__ lea(rdi, Address(rsp, rcx, Interpreter::stackElementScale(), -wordSize));
Copy the code
  1. The following code uses a loop to initialize the local variable table. \
  • First define Label Label exit and loop.
  • Then, calculate and between RDX and myself, and judge that the number of RDX is less than 0, indicating that there is no need for the local variable table, then jump directly to the exit label.
  • If the value of RDX is greater than 0, then the local variable table needs to be initialized and a loop tag inserted here
  • NULL_WORD = 0; NULL_WORD = 32; NULL_WORD = 32;
  • I’m going to subtract 1 from RDX.
  • If the value of RDX is greater than 0, the jump bind(loop) position loops until all local variables are initialized.
 {
    Label exit, loop;
    __ testl(rdx, rdx);
    // do nothing if rdx 
    __ jcc(Assembler::lessEqual, exit); <= 0
    __ bind(loop);
      // initialize local 
    __ push((int32_t)NULL_WORD); 
       // until everything initialized
    __ decrement(rdx);   
    __ jcc(Assembler::greater, loop);
    __ bind(exit);
  }
Copy the code
  1. The next step is to generate the Java method to fix stack frames, which we will explore in detail in a separate article.
  // initialize fixed part of activation frame
  generate_fixed_frame(false);
Copy the code
  1. Move the thread object currently executing the method to the RAX register, get the JavaThread thread’s _DO_NOT_unlock_if_synchronized offset, and set the thread local variable to true. Because the method is synchonized and cannot execute unlock because enter has not yet been executed, this flag is checked during remove_activation.
  __ get_thread(rax);
  const Address do_not_unlock_if_synchronized(rax,     in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
  __ movbool(do_not_unlock_if_synchronized, true);
Copy the code
  1. The next paragraph is the count of method calls, which I won’t go into here.
 __ profile_parameters_type(rax, rcx, rdx);
  // increment invocation count & check for overflow
  Label invocation_counter_overflow;
  Label profile_method;
  Label profile_method_continue;
  if (inc_counter) {
    generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue);
    if (ProfileInterpreter) {
      __ bind(profile_method_continue);
    }
  }
  Label continue_after_compile;
  __ bind(continue_after_compile);

  bang_stack_shadow_pages(false);
Copy the code
  1. Jvmti callback function support. I will not go into details here
  // jvmti support
  __ notify_method_entry();
Copy the code
  1. This is the key entry point for Java to execute bytecode execution instructions. This will be covered in the following article.
  __ dispatch_next(vtos);
Copy the code

Summary this article mainly analyzes the JVM generated Java common method entry entry_point assembly instruction code process, can understand the JVM generated Java stack frame origin parameters are saved in the caller stack frame. The local variables inside the method are initialized to the Java stack frame, and the bytecode instructions in the JVM that define the stack frame’s dynamic link address and operand stack, as well as how to execute the bytecode method, will be analyzed later.