Top


定義場所(file name)

hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp

説明(description)

//
// Generic interpreted method entry to (asm) interpreter
//

名前(function name)

address InterpreterGenerator::generate_normal_entry(bool synchronized) {

本体部(body)

  {- -------------------------------------------
  (1) (変数宣言など)
      ---------------------------------------- -}

      // determine code generation flags
      bool inc_counter  = UseCompiler || CountCompiledCalls;

  {- -------------------------------------------
  (1) (生成したコードが実行される時点では, レジスタには以下の値が入っているはず)
      ---------------------------------------- -}

      // ebx: methodOop
      // r13: sender sp

  {- -------------------------------------------
  (1) (ここからが生成するコードの始まり)
      ---------------------------------------- -}

      address entry_point = __ pc();

  {- -------------------------------------------
  (1) (変数宣言など)
      ---------------------------------------- -}

      const Address size_of_parameters(rbx,
                                       methodOopDesc::size_of_parameters_offset());
      const Address size_of_locals(rbx, methodOopDesc::size_of_locals_offset());
      const Address invocation_counter(rbx,
                                       methodOopDesc::invocation_counter_offset() +
                                       InvocationCounter::counter_offset());
      const Address access_flags(rbx, methodOopDesc::access_flags_offset());

  {- -------------------------------------------
  (1) (まず, 必要な局所変数領域の大きさを計算し, その分の領域を確保する処理を行う. 
       この際, 必要であれば呼び出し元の SP をずらす処理を行う)

      (局所変数領域は引数を含むので, 呼び出し元のフレーム内に確保する必要がある.
       詳細はフレーム構造を参照.)
      ---------------------------------------- -}

    {- -------------------------------------------
  (1.1) コード生成:
        「引数の数を取得する」
        ---------------------------------------- -}

      // get parameter size (always needed)
      __ load_unsigned_short(rcx, size_of_parameters);

    {- -------------------------------------------
  (1.1) (この時点では, レジスタには以下の値が入っているはず)
        ---------------------------------------- -}

      // rbx: methodOop
      // rcx: size of parameters
      // r13: sender_sp (could differ from sp+wordSize if we were called via c2i )

    {- -------------------------------------------
  (1.1) コード生成:
        「引数分を引いた局所変数領域の大きさを計算しておく.
          結果は rdx に格納」
        ---------------------------------------- -}

      __ load_unsigned_short(rdx, size_of_locals); // get size of locals in words
      __ subl(rdx, rcx); // rdx = no. of additional locals

      // YYY
    //   __ incrementl(rdx);
    //   __ andl(rdx, -2);

    {- -------------------------------------------
  (1.1) コード生成:
        「InterpreterGenerator::generate_stack_overflow_check() が生成するコードにより, 
         stack overflow のチェックを行う.
         もしスタックに空きがなければ, StackOverflowError.」
        ---------------------------------------- -}

      // see if we've got enough room on the stack for locals plus overhead.
      generate_stack_overflow_check();

    {- -------------------------------------------
  (1.1) コード生成:
        「リターンアドレスを rax にロードしておく」
        ---------------------------------------- -}

      // get return address
      __ pop(rax);

    {- -------------------------------------------
  (1.1) コード生成: 
        「r14 の値を修正しておく」

        (r14 は, 最初の引数を指すべき.
         このため, rsp を引数分だけ増加させた値にしておく.)
        ---------------------------------------- -}

      // compute beginning of parameters (r14)
      __ lea(r14, Address(rsp, rcx, Address::times_8, -wordSize));

    {- -------------------------------------------
  (1.1) コード生成: 
        「(引数を除いた)局所変数の分だけ, 呼び出し元の SP の値をずらしておく.」

        (コードとしては, push() を連発するだけ.
         以下の loop ラベルで作られたループを局所変数の個数分だけ繰り返す.
         0 を push するので, ついでに局所変数領域の 0 クリアも兼ねる)
        ---------------------------------------- -}

      // 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((int) NULL_WORD); // initialize local variables
        __ decrementl(rdx); // until everything initialized
        __ jcc(Assembler::greater, loop);
        __ bind(exit);
      }

  {- -------------------------------------------
  (1) (ここまでが, 局所変数領域の大きさの計算処理, 及び局所変数領域の確保処理)
      ---------------------------------------- -}

  {- -------------------------------------------
  (1) コード生成: (ただし, JIT コンパイルのためにメソッドの呼び出し回数を数える必要がある場合のみ生成する. 
                 より具体的には, UseCompiler オプションまたは CountCompiledCalls オプションが指定されている場合のみ生成)
      「_invocation_counter フィールドのアドレスを rcx レジスタに入れておく」

        (これは, 後で呼び出す InterpreterGenerator::generate_counter_incr() の生成コードが
        rcx に _invocation_counter フィールドのアドレスが入っていることを想定しているため.
        (See: InterpreterGenerator::generate_counter_incr()))
      ---------------------------------------- -}

      // (pre-)fetch invocation count
      if (inc_counter) {
        __ movl(rcx, invocation_counter);
      }

  {- -------------------------------------------
  (1) コード生成:
      「TemplateInterpreterGenerator::generate_fixed_frame() が生成するコードで
        スタック上に新しいフレームを確保する」
      ---------------------------------------- -}

      // initialize fixed part of activation frame
      generate_fixed_frame(false);

  {- -------------------------------------------
  (1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
      コード生成: (デバッグ用の処理)
      ---------------------------------------- -}

      // make sure method is not native & not abstract
    #ifdef ASSERT
      __ movl(rax, access_flags);
      {
        Label L;
        __ testl(rax, JVM_ACC_NATIVE);
        __ jcc(Assembler::zero, L);
        __ stop("tried to execute native method as non-native");
        __ bind(L);
      }
      {
        Label L;
        __ testl(rax, JVM_ACC_ABSTRACT);
        __ jcc(Assembler::zero, L);
        __ stop("tried to execute abstract method in interpreter");
        __ bind(L);
      }
    #endif

  {- -------------------------------------------
  (1) コード生成: 
      「カレントスレッドに対応する JavaThread の
        JavaThread::do_not_unlock_if_synchronized フィールドを, true にしておく」

      (synchronized メソッドの場合でも, lock_method() を呼ぶのはこの後なので, 
       この時点ではまだ BasicObjectLock を確保していない. 

       が, stack banging によって StackOverflowError が出ると, 例外処理で stack の巻き戻しが起こる可能性があり, 
       何もしないと (synchronized メソッドなので) unlock_if_synchronized_method() で解放処理が行われてしまう.

       そこで, thread の do_not_unlock_if_synchronized ビットを立てておく.
       これを立てておくと unlock_if_synchronized_method() 時に, このメソッドは見逃されるようになる.
       See: [here](no3059F5A.html) for details)
      ---------------------------------------- -}

      // 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.

      const Address do_not_unlock_if_synchronized(r15_thread,
            in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
      __ movbool(do_not_unlock_if_synchronized, true);

  {- -------------------------------------------
  (1) コード生成: (ただし, JIT コンパイルのためにメソッドの呼び出し回数を数える必要がある場合のみ生成する. 
                 より具体的には, UseCompiler オプションまたは CountCompiledCalls オプションが指定されている場合のみ生成)
      「InterpreterGenerator::generate_counter_incr() が生成するコードで, 
        invocation counter 値の増加処理と値のチェック処理を行う.

        値に応じて, 以下の3通りに分岐.
        * JIT コンパイル対象になる閾値を超えている場合:
          invocation_counter_overflow ラベルまでジャンプ
        * 監視対象になる閾値を超えているが, まだ mdp が確保されていない場合:
          profile_method ラベルまでジャンプ
        * それ以外の場合:
          このまま以下の処理にフォールスルー」

        (See: no3718SNC)
      ---------------------------------------- -}

      // 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);
        }
      }

  {- -------------------------------------------
  (1) コード生成:
      「(ここが continue_after_compile ラベルの位置)」
      ---------------------------------------- -}

      Label continue_after_compile;
      __ bind(continue_after_compile);

  {- -------------------------------------------
  (1) コード生成:
      「AbstractInterpreterGenerator::bang_stack_shadow_pages() が生成するコードで
        stack overflow をチェックする. (stack banging コード)」
       (See: [here](no3059d9W.html) for details)
      ---------------------------------------- -}

      // check for synchronized interpreted methods
      bang_stack_shadow_pages(false);

  {- -------------------------------------------
  (1) コード生成:
      「stack banging が終わったので, do_not_unlock_if_synchronized ビットを元に戻す」
      ---------------------------------------- -}

      // reset the _do_not_unlock_if_synchronized flag
      __ movbool(do_not_unlock_if_synchronized, false);

  {- -------------------------------------------
  (1) コード生成: (引数で synchronized method だと指定されている場合にのみ生成)
      「InterpreterGenerator::lock_method() が生成するコードで, ロックを取得する」
      (See: [here](no9662EYy.html) for details)
      ---------------------------------------- -}

      // check for synchronized methods
      // Must happen AFTER invocation_counter check and stack overflow check,
      // so method is not locked if overflows.
      if (synchronized) {
        // Allocate monitor and lock method
        lock_method();

  {- -------------------------------------------
  (1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
      コード生成: (デバッグ用の処理) (引数で synchronized method だと指定されていない場合にのみ生成)
      (本当に synchronized でないことを確認するコードを生成している)
      ---------------------------------------- -}

      } else {
        // no synchronization necessary
    #ifdef ASSERT
        {
          Label L;
          __ movl(rax, access_flags);
          __ testl(rax, JVM_ACC_SYNCHRONIZED);
          __ jcc(Assembler::zero, L);
          __ stop("method needs synchronization");
          __ bind(L);
        }
    #endif
      }

  {- -------------------------------------------
  (1) (以降で, 実際にメソッド内の処理を開始)
      ---------------------------------------- -}

      // start execution

  {- -------------------------------------------
  (1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
      コード生成: (デバッグ用の処理)
      ---------------------------------------- -}

    #ifdef ASSERT
      {
        Label L;
         const Address monitor_block_top (rbp,
                     frame::interpreter_frame_monitor_block_top_offset * wordSize);
        __ movptr(rax, monitor_block_top);
        __ cmpptr(rax, rsp);
        __ jcc(Assembler::equal, L);
        __ stop("broken stack frame setup in interpreter");
        __ bind(L);
      }
    #endif

  {- -------------------------------------------
  (1) コード生成: (JVMTI のフック点)
      ---------------------------------------- -}

      // jvmti support
      __ notify_method_entry();

  {- -------------------------------------------
  (1) コード生成:
      「dispatch_next() が生成するコードで, 
       メソッドの最初のバイトコードに対応するテンプレートへとジャンプする」
      ---------------------------------------- -}

      __ dispatch_next(vtos);

  {- -------------------------------------------
  (1) (以下は, invocation counter の値が閾値を超えていた際の処理パス)
      ---------------------------------------- -}

      // invocation counter overflow

  {- -------------------------------------------
  (1) コード生成: (ただし, profile_method ラベルによる飛び先については
                 JIT コンパイルのためにメソッドの呼び出し回数を数える必要がある場合のみ生成する. 
                 より具体的には, UseCompiler オプションまたは CountCompiledCalls オプションが指定されている場合のみ生成)
      「それぞれのラベル箇所では以下の処理が行われる.
        * invocation_counter_overflow ラベルによる飛び先
          InterpreterGenerator::generate_counter_overflow() が生成するコードにより, 
          JIT コンパイラを呼び出す (See: ...#TODO)
          その後, continue_after_compile ラベルに戻って実行を再開する.
        * profile_method ラベルによる飛び先
          InterpreterRuntime::profile_method() を呼び出して,
          新しい methodDataOop オブジェクトを methodOop 内にセットする.
          その後, profile_method_continue ラベルに戻って実行を再開する.」
      ---------------------------------------- -}

      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);
      }

  {- -------------------------------------------
  (1) 以上で生成したコードの先頭アドレスをリターン.
      ---------------------------------------- -}

      return entry_point;
    }

This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.