Top


定義場所(file name)

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

名前(function name)

void TemplateTable::branch(bool is_jsr, bool is_wide) {

本体部(body)

  {- -------------------------------------------
  (1) 
      ---------------------------------------- -}

      __ get_method(rcx); // rcx holds method

  {- -------------------------------------------
  (1) コード生成: (JIT コンパイラ用のプロファイル情報の取得) (See: [here](no2935fdD.html) for details)
      ---------------------------------------- -}

      __ profile_taken_branch(rax, rbx); // rax holds updated MDP, rbx
                                         // holds bumped taken count

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

      const ByteSize be_offset = methodOopDesc::backedge_counter_offset() +
                                 InvocationCounter::counter_offset();
      const ByteSize inv_offset = methodOopDesc::invocation_counter_offset() +
                                  InvocationCounter::counter_offset();
      const int method_offset = frame::interpreter_frame_method_offset * wordSize;

  {- -------------------------------------------
  (1) コード生成:
      「飛び先のオフセットを取得する」
      ---------------------------------------- -}

      // Load up edx with the branch displacement
      __ movl(rdx, at_bcp(1));
      __ bswapl(rdx);

      if (!is_wide) {
        __ sarl(rdx, 16);
      }
      __ movl2ptr(rdx, rdx);

  {- -------------------------------------------
  (1) コード生成: (jsr 用のコードを生成する場合)
      jsr 用の場合は, 以下のようなコードを出力し, ここでリターン.
      「rbx に次のバイトコードのセットする.
      また, リターンアドレスとして現在値を表す bci 値を算出し, オペランドスタックの先頭に乗せる.
      さらに r13 を飛び先の値に変更する.
      その後, dispatch_only() が生成するコードで, 
      次のバイトコードに対応するテンプレートへとジャンプする」
      ---------------------------------------- -}

      // Handle all the JSR stuff here, then exit.
      // It's much shorter and cleaner than intermingling with the non-JSR
      // normal-branch stuff occurring below.
      if (is_jsr) {
        // Pre-load the next target bytecode into rbx
        __ load_unsigned_byte(rbx, Address(r13, rdx, Address::times_1, 0));

        // compute return address as bci in rax
        __ lea(rax, at_bcp((is_wide ? 5 : 3) -
                            in_bytes(constMethodOopDesc::codes_offset())));
        __ subptr(rax, Address(rcx, methodOopDesc::const_offset()));
        // Adjust the bcp in r13 by the displacement in rdx
        __ addptr(r13, rdx);
        // jsr returns atos that is not an oop
        __ push_i(rax);
        __ dispatch_only(vtos);
        return;
      }

  {- -------------------------------------------
  (1) (以下は, jsr ではない場合のコード生成)
      ---------------------------------------- -}

      // Normal (non-jsr) branch handling

  {- -------------------------------------------
  (1) コード生成:
      「r13 の値を更新しておく (rdx に入っているジャンプ先のオフセット分を足す)」
      ---------------------------------------- -}

      // Adjust the bcp in r13 by the displacement in rdx
      __ addptr(r13, rdx);

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert(UseLoopCounter || !UseOnStackReplacement,
             "on-stack-replacement requires loop counters");

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

      Label backedge_counter_overflow;
      Label profile_method;
      Label dispatch;

  {- -------------------------------------------
  (1) もし UseLoopCounter が指定されていれば,
      以下の処理パスで backward branch 用の処理を行うコードを出力する.
      ---------------------------------------- -}

      if (UseLoopCounter) {

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

        // increment backedge counter for backward branches
        // rax: MDO
        // ebx: MDO bumped taken-count
        // rcx: method
        // rdx: target offset
        // r13: target bcp
        // r14: locals pointer

    {- -------------------------------------------
  (1.1) コード生成:
        「分岐の向きをチェックして, 
          forward branch であれば (backward branch 用の処理は不要なので) Lforward まで進む.」
        ---------------------------------------- -}

        __ testl(rdx, rdx);             // check if forward or backward branch
        __ jcc(Assembler::positive, dispatch); // count only if backward branch

    {- -------------------------------------------
  (1.1) TieredCompilation が指定されている場合, 以下の処理パスでコードを生成する
        ---------------------------------------- -}

        if (TieredCompilation) {

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

          Label no_mdo;
          int increment = InvocationCounter::count_increment;
          int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift;

      {- -------------------------------------------
  (1.1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には生成しない)
          「もし methodDataOopDesc(mdo) が付いていれば, 
            InterpreterMacroAssembler::increment_mask_and_jump() が生成するコードによって
            mdo の _backedge_counter フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
            閾値に達していた場合は, backedge_counter_overflow ラベルにジャンプする.
            (= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).
            そうでなければ, dispatch ラベルまでジャンプする.

            もし methodDataOopDesc が付いていなければ, 
            no_mdo ラベルまでジャンプする」
          ---------------------------------------- -}

          if (ProfileInterpreter) {
            // Are we profiling?
            __ movptr(rbx, Address(rcx, in_bytes(methodOopDesc::method_data_offset())));
            __ testptr(rbx, rbx);
            __ jccb(Assembler::zero, no_mdo);
            // Increment the MDO backedge counter
            const Address mdo_backedge_counter(rbx, in_bytes(methodDataOopDesc::backedge_counter_offset()) +
                                               in_bytes(InvocationCounter::counter_offset()));
            __ increment_mask_and_jump(mdo_backedge_counter, increment, mask,
                                       rax, false, Assembler::zero, &backedge_counter_overflow);
            __ jmp(dispatch);
          }

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

          __ bind(no_mdo);

      {- -------------------------------------------
  (1.1.1) コード生成:
          「InterpreterMacroAssembler::increment_mask_and_jump() が生成するコードによって
            実行中のメソッドに対応する methodOopDesc の 
            _backedge_counter フィールドのカウンタ値をインクリメントし, 閾値チェックを行う.
            閾値に達していた場合は, backedge_counter_overflow ラベルにジャンプする.
            (= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).
            そうでなければ, dispatch ラベルまでジャンプする.」
          ---------------------------------------- -}

          // Increment backedge counter in methodOop
          __ increment_mask_and_jump(Address(rcx, be_offset), increment, mask,
                                     rax, false, Assembler::zero, &backedge_counter_overflow);

    {- -------------------------------------------
  (1.1) TieredCompilation が指定されていない場合, 以下の処理パスでコードを生成する
        ---------------------------------------- -}

        } else {

      {- -------------------------------------------
  (1.1.1) コード生成: (TieredCompilation が指定されていない場合)
          まず, 以下のコードを生成する.
          「backedge_counter のカウント値をインクリメントする.」

          その後, ProfileInterpreter が指定されていれば, 以下のコードを生成する.
          「methodDataOop(mdp) に関するチェックを行う.
            チェック結果に応じて, 以下の3通りに分岐.
            * まだ監視対象になる閾値を超えていない場合:
              dispatch ラベルまで分岐.

            * 監視対象になる閾値を超えているが, まだ mdp が確保されていない場合
              (InterpreterMacroAssembler::test_method_data_pointer() が生成するコードでチェック):
              profile_method ラベルまで分岐 (新しい methodDataOop オブジェクトを methodOop 内にセットする)

            * 既に methodDataOop が作成済みの場合:
              このままフォールスルー 
              (UseOnStackReplacement が指定されていれば, その if 中で生成するコードに突入)

          その後, UseOnStackReplacement が指定されていれば, 以下のコードを生成する.
          「InvocationCounter::InterpreterBackwardBranchLimit と現在の値を比較し, 
           越えてなければ dispatch ラベルまで飛ぶ.
           逆に超えていれば, backedge_counter_overflow ラベルまで飛ぶ
           (= JIT コンパイル処理を開始する. または既に JIT コンパイル済みのコードがあれば, OSR を行ってそのコードに遷移する).

           ただし, ProfileInterpreter オプションが指定されている場合は
           frequency_counter_overflow() がカウンタの値をクリアしないので,
           一度しきい値を越えると(既に JIT compiler が作業中なのに)連続してコンパイル要求を出すことになりかねない.
           そのため, この場合は overflow_frequency という間隔(今は1024)につき1度だけ backedge_counter_overflow に飛ぶよう調整している.
           (それ以外の1023回は, 何もせず dispatch ラベルに飛ぶだけにする)」
          ---------------------------------------- -}

          // increment counter
          __ movl(rax, Address(rcx, be_offset));        // load backedge counter
          __ incrementl(rax, InvocationCounter::count_increment); // increment counter
          __ movl(Address(rcx, be_offset), rax);        // store counter

          __ movl(rax, Address(rcx, inv_offset));    // load invocation counter
          __ andl(rax, InvocationCounter::count_mask_value); // and the status bits
          __ addl(rax, Address(rcx, be_offset));        // add both counters

          if (ProfileInterpreter) {
            // Test to see if we should create a method data oop
            __ cmp32(rax,
                     ExternalAddress((address) &InvocationCounter::InterpreterProfileLimit));
            __ jcc(Assembler::less, dispatch);

            // if no method data exists, go to profile method
            __ test_method_data_pointer(rax, profile_method);

            if (UseOnStackReplacement) {
              // check for overflow against ebx which is the MDO taken count
              __ cmp32(rbx,
                       ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit));
              __ jcc(Assembler::below, dispatch);

              // When ProfileInterpreter is on, the backedge_count comes
              // from the methodDataOop, which value does not get reset on
              // the call to frequency_counter_overflow().  To avoid
              // excessive calls to the overflow routine while the method is
              // being compiled, add a second test to make sure the overflow
              // function is called only once every overflow_frequency.
              const int overflow_frequency = 1024;
              __ andl(rbx, overflow_frequency - 1);
              __ jcc(Assembler::zero, backedge_counter_overflow);

            }
          } else {
            if (UseOnStackReplacement) {
              // check for overflow against eax, which is the sum of the
              // counters
              __ cmp32(rax,
                       ExternalAddress((address) &InvocationCounter::InterpreterBackwardBranchLimit));
              __ jcc(Assembler::aboveEqual, backedge_counter_overflow);

            }
          }
        }

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

        __ bind(dispatch);
      }

  {- -------------------------------------------
  (1) コード生成: 
      「rbx に次のバイトコードのセットし, 
      その後, dispatch_only() が生成するコードで, 
      次のバイトコードに対応するテンプレートへとジャンプする」
      ---------------------------------------- -}

      // Pre-load the next target bytecode into rbx
      __ load_unsigned_byte(rbx, Address(r13, 0));

      // continue with the bytecode @ target
      // eax: return bci for jsr's, unused otherwise
      // ebx: target bytecode
      // r13: target bcp
      __ dispatch_only(vtos);

  {- -------------------------------------------
  (1) もし UseLoopCounter が指定されていれば,
      以下の処理パスで backward branch 用の処理を行うコードを出力する.
      ---------------------------------------- -}

      if (UseLoopCounter) {

    {- -------------------------------------------
  (1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には生成しない)
        「(ここが profile_method ラベルの位置)」
        「InterpreterRuntime::profile_method() を呼び出して,
          新しい methodDataOop オブジェクトを methodOop 内にセットする.
          その後, dispatch ラベルに戻って実行を再開する.」
        ---------------------------------------- -}

        if (ProfileInterpreter) {
          // Out-of-line code to allocate method data oop.
          __ bind(profile_method);
          __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method));
          __ load_unsigned_byte(rbx, Address(r13, 0));  // restore target bytecode
          __ set_method_data_pointer_for_bcp();
          __ jmp(dispatch);
        }

    {- -------------------------------------------
  (1.1) もし UseOnStackReplacement が指定されていれば, 
        以下の処理パスで backward branch 用の処理を行うコードを出力する.
        ---------------------------------------- -}

        if (UseOnStackReplacement) {

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

          // invocation counter overflow
          __ bind(backedge_counter_overflow);

      {- -------------------------------------------
  (1.1.1) コード生成:
          「InterpreterRuntime::frequency_counter_overflow() を呼び出して JIT コンパイル処理を開始する.」
          ---------------------------------------- -}

          __ negptr(rdx);
          __ addptr(rdx, r13); // branch bcp
          // IcoResult frequency_counter_overflow([JavaThread*], address branch_bcp)
          __ call_VM(noreg,
                     CAST_FROM_FN_PTR(address,
                                      InterpreterRuntime::frequency_counter_overflow),
                     rdx);
          __ load_unsigned_byte(rbx, Address(r13, 0));  // restore target bytecode

      {- -------------------------------------------
  (1.1.1) コード生成:
          「もし InterpreterRuntime::frequency_counter_overflow() の返り値が NULL だったら,
            (まだ JIT 生成されたコードがないということなので) dispatch ラベルまで飛ぶ.
            また, nmethod が既に invalidate されていた場合も, dispatch ラベルまで飛ぶ.」
          ---------------------------------------- -}

          // rax: osr nmethod (osr ok) or NULL (osr not possible)
          // ebx: target bytecode
          // rdx: scratch
          // r14: locals pointer
          // r13: bcp
          __ testptr(rax, rax);                        // test result
          __ jcc(Assembler::zero, dispatch);         // no osr if null
          // nmethod may have been invalidated (VM may block upon call_VM return)
          __ movl(rcx, Address(rax, nmethod::entry_bci_offset()));
          __ cmpl(rcx, InvalidOSREntryBci);
          __ jcc(Assembler::equal, dispatch);

      {- -------------------------------------------
  (1.1.1) (ここまで来たら, JIT 生成された nmethod が得られたということになる)
          ---------------------------------------- -}

          // We have the address of an on stack replacement routine in eax
          // We need to prepare to execute the OSR method. First we must
          // migrate the locals and monitors off of the stack.

      {- -------------------------------------------
  (1.1.1) コード生成:
          「取得した mmethod を r13 に待避しておく」
          ---------------------------------------- -}

          __ mov(r13, rax);                             // save the nmethod

      {- -------------------------------------------
  (1.1.1) コード生成:
          「SharedRuntime::OSR_migration_begin() を呼んで, 
            interpreter frame 中の局所変数とモニタを C ヒープ上に取ったバッファに退避する.」
          ---------------------------------------- -}

          call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin));

      {- -------------------------------------------
  (1.1.1) コード生成:
          「SharedRuntime::OSR_migration_begin() が返したバッファを j_rarg0 に退避しておく」
          ---------------------------------------- -}

          // eax is OSR buffer, move it to expected parameter location
          __ mov(j_rarg0, rax);

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

          // We use j_rarg definitions here so that registers don't conflict as parameter
          // registers change across platforms as we are in the midst of a calling
          // sequence to the OSR nmethod and we don't want collision. These are NOT parameters.

          const Register retaddr = j_rarg2;
          const Register sender_sp = j_rarg1;

      {- -------------------------------------------
  (1.1.1) コード生成:
          「interpreter frame を削除する. 
          ついでに, rsp の値が align しているよう調整もしておく.」
          ---------------------------------------- -}

          // pop the interpreter frame
          __ movptr(sender_sp, Address(rbp, frame::interpreter_frame_sender_sp_offset * wordSize)); // get sender sp
          __ leave();                                // remove frame anchor
          __ pop(retaddr);                           // get return address
          __ mov(rsp, sender_sp);                   // set sp to sender sp
          // Ensure compiled code always sees stack at proper alignment
          __ andptr(rsp, -(StackAlignmentInBytes));

      {- -------------------------------------------
  (1.1.1) コード生成:
          「リターンアドレスをプッシュした後, osr コードにジャンプする.」
          ---------------------------------------- -}

          // unlike x86 we need no specialized return from compiled code
          // to the interpreter or the call stub.

          // push the return address
          __ push(retaddr);

          // and begin the OSR nmethod
          __ jmp(Address(r13, nmethod::osr_entry_point_offset()));
        }
      }
    }

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