Top


定義場所(file name)

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

名前(function name)

void TemplateInterpreterGenerator::generate_throw_exception() {

本体部(body)

  {- -------------------------------------------
  (1) (ここからが, 例外が rethrow された場合の処理コード.
       ここで生成したコードは Interpreter::rethrow_exception_entry() からアクセスされる.)

      (とは言っても, 処理自体は例外送出時とほとんど同じなので, 
      sp や bcp, locals, r12(heapbase) の復帰を行った後, 
      このまま以下の Interpreter::_throw_exception_entry の処理にフォールスルーするだけ)
      ---------------------------------------- -}

      // Entry point in previous activation (i.e., if the caller was
      // interpreted)
      Interpreter::_rethrow_exception_entry = __ pc();
      // Restore sp to interpreter_frame_last_sp even though we are going
      // to empty the expression stack for the exception processing.
      __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);
      // rax: exception
      // rdx: return address/pc that threw exception
      __ restore_bcp();    // r13 points to call/send
      __ restore_locals();
      __ reinit_heapbase();  // restore r12 as heapbase.

  {- -------------------------------------------
  (1) (ここからが, 例外発生時の例外送出処理コード.
       ここで生成したコードは Interpreter::throw_exception_entry() からアクセスされる.)
      ---------------------------------------- -}

      // Entry point for exceptions thrown within interpreter code
      Interpreter::_throw_exception_entry = __ pc();
      // expression stack is undefined here
      // rax: exception
      // r13: exception bcp

  {- -------------------------------------------
  (1) コード生成: (verify)
      ---------------------------------------- -}

      __ verify_oop(rax);

  {- -------------------------------------------
  (1) コード生成:
      「?? 創出された例外オブジェクトが
       以下で呼び出す InterpreterRuntime::exception_handler_for_exception() から見えるように
       c_rarg1 にコピーしている?? #TODO」
      ---------------------------------------- -}

      __ mov(c_rarg1, rax);

  {- -------------------------------------------
  (1) コード生成:
      「オペランドスタック("expression stack")を空にする.」
      ---------------------------------------- -}

      // expression stack must be empty before entering the VM in case of
      // an exception
      __ empty_expression_stack();

  {- -------------------------------------------
  (1) コード生成:
      「InterpreterRuntime::exception_handler_for_exception() を呼んで
        対応する例外ハンドラのエントリポイント(対応するものがなければ unwind 処理のエントリポイント)を取得し, 
        そのアドレスにジャンプする.

        (なお, 例外ハンドラ内で例外オブジェクトにアクセスできるよう
         ジャンプ前に push_ptr() で例外オブジェクトをオペランドスタックに積んでいる)」
      ---------------------------------------- -}

      // find exception handler address and preserve exception oop
      __ call_VM(rdx,
                 CAST_FROM_FN_PTR(address,
                              InterpreterRuntime::exception_handler_for_exception),
                 c_rarg1);
      // rax: exception handler entry point
      // rdx: preserved exception oop
      // r13: bcp for exception handler
      __ push_ptr(rdx); // push exception which is now the only value on the stack
      __ jmp(rax); // jump to exception handler (may be _remove_activation_entry!)

      // If the exception is not handled in the current frame the frame is
      // removed and the exception is rethrown (i.e. exception
      // continuation is _rethrow_exception).
      //
      // Note: At this point the bci is still the bxi for the instruction
      // which caused the exception and the expression stack is
      // empty. Thus, for any VM calls at this point, GC will find a legal
      // oop map (with empty expression stack).

      // In current activation
      // tos: exception
      // esi: exception bcp

  {- -------------------------------------------
  (1) (ここまでが, 例外発生時の例外送出処理用のコード)
      ---------------------------------------- -}

  {- -------------------------------------------
  (1) (ここからは, JVMTI の PopFrame() 処理用のコード.
       ここで生成したコードは Interpreter::remove_activation_preserving_args_entry() からアクセスされる.)
      ---------------------------------------- -}

      //
      // JVMTI PopFrame support
      //

      Interpreter::_remove_activation_preserving_args_entry = __ pc();

  {- -------------------------------------------
  (1) コード生成:
      「オペランドスタック("expression stack")を空にする.」
      ---------------------------------------- -}

      __ empty_expression_stack();

  {- -------------------------------------------
  (1) コード生成:
      「これ以降に起こった call_VM でまたこの関数に re-enter してしまわないように 
        (= PopFrame については既に作業中であることを示すために), 
        カレントスレッドの JavaThread::_popframe_condition フィールド中の 
        JavaThread::popframe_processing_bit ビットを立てておく.」
      ---------------------------------------- -}

      // Set the popframe_processing bit in pending_popframe_condition
      // indicating that we are currently handling popframe, so that
      // call_VMs that may happen later do not trigger new popframe
      // handling cycles.
      __ movl(rdx, Address(r15_thread, JavaThread::popframe_condition_offset()));
      __ orl(rdx, JavaThread::popframe_processing_bit);
      __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()), rdx);

  {- -------------------------------------------
  (1) コード生成:
      「InterpreterRuntime::interpreter_contains() を呼んで
        PopFrame() 対象になっているフレームの呼び出し元を確認し, その種類に応じて処理を分岐.
        * 呼び出し元が compiled frame の場合:
          このままフォールスルー
        * 呼び出し元が interpreter frame の場合:
          caller_not_deoptimized ラベルまでジャンプ                             」
      ---------------------------------------- -}

      {
        // Check to see whether we are returning to a deoptimized frame.
        // (The PopFrame call ensures that the caller of the popped frame is
        // either interpreted or compiled and deoptimizes it if compiled.)
        // In this case, we can't call dispatch_next() after the frame is
        // popped, but instead must save the incoming arguments and restore
        // them after deoptimization has occurred.
        //
        // Note that we don't compare the return PC against the
        // deoptimization blob's unpack entry because of the presence of
        // adapter frames in C2.
        Label caller_not_deoptimized;
        __ movptr(c_rarg1, Address(rbp, frame::return_addr_offset * wordSize));
        __ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
                                   InterpreterRuntime::interpreter_contains), c_rarg1);
        __ testl(rax, rax);
        __ jcc(Assembler::notZero, caller_not_deoptimized);

  {- -------------------------------------------
  (1) コード生成:
      「(以下が, 呼び出し元が compiled frame の場合の処理)

        この場合は, PopFrame() 対象のフレームが破棄された後, 
        呼び出し元に対する deopt 処理が行われ, それが終わった後で invoke 命令が再実行される.

        (引数については deopt 処理中に再セットが行われ, deopt 終了後には 
         bci が invoke 命令を指した状態で interpreter に引き継がれる.
         See: vframeArrayElement::unpack_on_stack())                          」
      ---------------------------------------- -}

    {- -------------------------------------------
  (1.1) コード生成:
        「invoke を再実行するために必要な引数の情報を
          Deoptimization::popframe_preserve_args() で退避する.」
        ---------------------------------------- -}

        // Compute size of arguments for saving when returning to
        // deoptimized caller
        __ get_method(rax);
        __ load_unsigned_short(rax, Address(rax, in_bytes(methodOopDesc::
                                                    size_of_parameters_offset())));
        __ shll(rax, Interpreter::logStackElementSize);
        __ restore_locals(); // XXX do we need this?
        __ subptr(r14, rax);
        __ addptr(r14, wordSize);
        // Save these arguments
        __ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
                                               Deoptimization::
                                               popframe_preserve_args),
                              r15_thread, rax, r14);

    {- -------------------------------------------
  (1.1) コード生成:
        「フレームを破棄し synchronized method であればロックの解除を行う.」
         (See: InterpreterMacroAssembler::remove_activation())
        ---------------------------------------- -}

        __ remove_activation(vtos, rdx,
                             /* throw_monitor_exception */ false,
                             /* install_monitor_exception */ false,
                             /* notify_jvmdi */ false);

    {- -------------------------------------------
  (1.1) コード生成:
        「deopt 処理中で, 今回の処理対象が「PopFrame() 対象の呼び出し元」であることが把握できるよう, 
          JavaThread::_popframe_condition フィールドの 
          JavaThread::popframe_force_deopt_reexecution_bit ビットが立てておく.」
        ---------------------------------------- -}

        // Inform deoptimization that it is responsible for restoring
        // these arguments
        __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()),
                JavaThread::popframe_force_deopt_reexecution_bit);

    {- -------------------------------------------
  (1.1) コード生成:
        「deopt 処理コードにジャンプ」
        ---------------------------------------- -}

        // Continue in deoptimization handler
        __ jmp(rdx);

  {- -------------------------------------------
  (1) コード生成:
      「(以下が, 呼び出し元が interpreter frame の場合の処理)」
      ---------------------------------------- -}

        __ bind(caller_not_deoptimized);
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「フレームを破棄し, もし synchronized method であればロックの解除も行う.
          (See: InterpreterMacroAssembler::remove_activation())        」
        ---------------------------------------- -}

      __ remove_activation(vtos, rdx, /* rdx result (retaddr) is not used */
                           /* throw_monitor_exception */ false,
                           /* install_monitor_exception */ false,
                           /* notify_jvmdi */ false);

    {- -------------------------------------------
  (1.1) コード生成:
        「InterpreterRuntime::popframe_move_outgoing_args() を呼んで
         invoke の再実行に必要な引数を正しい位置に詰め直しておく.

         (i2c によって outgoing argument が移動させられている可能性があるが, 
         PopFrame() の仕様として再度 invoke が実行された際には
         現在の(= 何か変更が行われていればその変更が反映された)引数が見えなければならず, 
         かつ(他の制約上の都合から) deopt 後の interpreter には
         現在の last_sp の値(オペランドスタック("expression stack")の先頭)と
         実際の stack top が同じであるように見えないといけない.)

         (なお, 呼び出しの前後で last_Java_sp への SP のセット/クリアも行っている)
         (また, 呼び出し後には rsp を last_sp まで復帰させ, 代わりに last_sp の値は 0 でクリアしている) 」
        ---------------------------------------- -}

      // Finish with popframe handling
      // A previous I2C followed by a deoptimization might have moved the
      // outgoing arguments further up the stack. PopFrame expects the
      // mutations to those outgoing arguments to be preserved and other
      // constraints basically require this frame to look exactly as
      // though it had previously invoked an interpreted activation with
      // no space between the top of the expression stack (current
      // last_sp) and the top of stack. Rather than force deopt to
      // maintain this kind of invariant all the time we call a small
      // fixup routine to move the mutated arguments onto the top of our
      // expression stack if necessary.
      __ mov(c_rarg1, rsp);
      __ movptr(c_rarg2, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
      // PC must point into interpreter here
      __ set_last_Java_frame(noreg, rbp, __ pc());
      __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::popframe_move_outgoing_args), r15_thread, c_rarg1, c_rarg2);
      __ reset_last_Java_frame(true, true);
      // Restore the last_sp and null it out
      __ movptr(rsp, Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize));
      __ movptr(Address(rbp, frame::interpreter_frame_last_sp_offset * wordSize), (int32_t)NULL_WORD);

    {- -------------------------------------------
  (1.1) コード生成:
         「?? #TODO」
        ---------------------------------------- -}

      __ restore_bcp();  // XXX do we need this?
      __ restore_locals(); // XXX do we need this?

    {- -------------------------------------------
  (1.1) コード生成: (ただし, ProfileInterpreter オプションが指定されていない場合には行わない)
         「既にインクリメントされてしまっている method data pointer を元に戻しておく」
        ---------------------------------------- -}

      // The method data pointer was incremented already during
      // call profiling. We have to restore the mdp for the current bcp.
      if (ProfileInterpreter) {
        __ set_method_data_pointer_for_bcp();
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「カレントスレッドの JavaThread::_popframe_condition フィールドをリセットする.」
        ---------------------------------------- -}

      // Clear the popframe condition flag
      __ movl(Address(r15_thread, JavaThread::popframe_condition_offset()),
              JavaThread::popframe_inactive);

    {- -------------------------------------------
  (1.1) コード生成:
        「dispatch_next() で再度 invoke 命令を実行させる」
        ---------------------------------------- -}

      __ dispatch_next(vtos);

  {- -------------------------------------------
  (1) (ここまでが, JVMTI の PopFrame() 処理用のコード)
      ---------------------------------------- -}

      // end of PopFrame support

  {- -------------------------------------------
  (1) (ここからは, 例外発生時の unwind 処理用のコード.
      ここで生成したコードは Interpreter::remove_activation_entry() からアクセスされる.)
      ---------------------------------------- -}

      Interpreter::_remove_activation_entry = __ pc();

  {- -------------------------------------------
  (1) コード生成:
      「送出された例外オブジェクトを rax レジスタに入れておく」
      ---------------------------------------- -}

      // preserve exception over this code sequence
      __ pop_ptr(rax);
      __ movptr(Address(r15_thread, JavaThread::vm_result_offset()), rax);

  {- -------------------------------------------
  (1) コード生成:
      「フレームを破棄し, もし synchronized method であればロックの解除も行う.
        (また, (JVMTI のフック点)および(DTrace のフック点)でもある)
        (See: InterpreterMacroAssembler::remove_activation())

        (なお処理の前後で, set_vm_result()/get_vm_result() により
         送出された例外オブジェクトの退避復帰も行っている)                   」
      ---------------------------------------- -}

      // remove the activation (without doing throws on illegalMonitorExceptions)
      __ remove_activation(vtos, rdx, false, true, false);
      // restore exception
      __ movptr(rax, Address(r15_thread, JavaThread::vm_result_offset()));
      __ movptr(Address(r15_thread, JavaThread::vm_result_offset()), (int32_t)NULL_WORD);

  {- -------------------------------------------
  (1) コード生成: (verify)
      ---------------------------------------- -}

      __ verify_oop(rax);

  {- -------------------------------------------
  (1) コード生成:
      「SharedRuntime::exception_handler_for_return_address() を呼んで, 
        呼び出し元のメソッドに応じた unwind 先アドレスを取得する. 

        (なお, 呼び出しの前後で rax(送出された例外) と rdx(リターンアドレス) の退避復帰を行っている)

        (また, unwind 先の例外ハンドラでは
         以下のレジスタがセットアップされていると仮定している模様.
         * rax: exception
         * rdx: return address/pc that threw exception
         * rsp: expression stack of caller
         * rbp: ebp of caller                         )            」
      ---------------------------------------- -}

      // In between activations - previous activation type unknown yet
      // compute continuation point - the continuation point expects the
      // following registers set up:
      //
      // rax: exception
      // rdx: return address/pc that threw exception
      // rsp: expression stack of caller
      // rbp: ebp of caller
      __ push(rax);                                  // save exception
      __ push(rdx);                                  // save return address
      __ super_call_VM_leaf(CAST_FROM_FN_PTR(address,
                              SharedRuntime::exception_handler_for_return_address),
                            r15_thread, rdx);
      __ mov(rbx, rax);                              // save exception handler
      __ pop(rdx);                                   // restore return address
      __ pop(rax);                                   // restore exception

  {- -------------------------------------------
  (1) コード生成:
      「取得した unwind 先にジャンプする.」
      ---------------------------------------- -}

      // Note that an "issuing PC" is actually the next PC after the call
      __ jmp(rbx);                                   // jump to exception
                                                     // handler of caller

  {- -------------------------------------------
  (1) (ここまでが, 例外発生時の unwind 処理用のコード)
      ---------------------------------------- -}

    }

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