Top


定義場所(file name)

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

名前(function name)

int MacroAssembler::biased_locking_enter(Register lock_reg,
                                         Register obj_reg,
                                         Register swap_reg,
                                         Register tmp_reg,
                                         bool swap_reg_contains_mark,
                                         Label& done,
                                         Label* slow_case,
                                         BiasedLockingCounters* counters) {

本体部(body)

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

      assert(UseBiasedLocking, "why call this otherwise?");
      assert(swap_reg == rax, "swap_reg must be rax, for cmpxchg");
      assert_different_registers(lock_reg, obj_reg, swap_reg);

  {- -------------------------------------------
  (1) (プロファイル情報の記録) (See: BiasedLockingCounters)
      ---------------------------------------- -}

      if (PrintBiasedLockingStatistics && counters == NULL)
        counters = BiasedLocking::counters();

  {- -------------------------------------------
  (1) もし tmp_reg (作業中に一時的に使うレジスタっぽい) が指定されてなければ, 
      lock_reg を tmp_reg として使うことにする.
      (このケースを他の場合と区別するため, need_tmp_reg を true にして分かるようにしておく.
      これ以降のこの関数内では, need_tmp_reg が true であれば
      tmp_reg を使う際には一時的に lock_reg を退避復帰する処理が挿入される.)
      ---------------------------------------- -}

      bool need_tmp_reg = false;
      if (tmp_reg == noreg) {
        need_tmp_reg = true;
        tmp_reg = lock_reg;
      } else {
        assert_different_registers(lock_reg, obj_reg, swap_reg, tmp_reg);
      }

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

      assert(markOopDesc::age_shift == markOopDesc::lock_bits + markOopDesc::biased_lock_bits, "biased locking makes assumptions about bit layout");
      Address mark_addr      (obj_reg, oopDesc::mark_offset_in_bytes());
      Address klass_addr     (obj_reg, oopDesc::klass_offset_in_bytes());
      Address saved_mark_addr(lock_reg, 0);

  {- -------------------------------------------
  (1) コード生成:
      「まず, オブジェクトの mark フィールドが biased locking 状態かどうかをチェックする.
        (mark フィールドを markOopDesc::biased_lock_mask_in_place でマスクして(= 下位3bit だけを取り出して), 
        markOopDesc::biased_lock_pattern と同じかどうかを確認)

        biased locking 状態でなければ, cas_label にジャンプして従来の CAS-based scheme へフォールバック」

      (swap_reg_contains_mark の箇所は何をやっている? #TODO)
      ---------------------------------------- -}

      // Biased locking
      // See whether the lock is currently biased toward our thread and
      // whether the epoch is still valid
      // Note that the runtime guarantees sufficient alignment of JavaThread
      // pointers to allow age to be placed into low bits
      // First check to see whether biasing is even enabled for this object
      Label cas_label;
      int null_check_offset = -1;
      if (!swap_reg_contains_mark) {
        null_check_offset = offset();
        movl(swap_reg, mark_addr);
      }
      if (need_tmp_reg) {
        push(tmp_reg);
      }
      movl(tmp_reg, swap_reg);
      andl(tmp_reg, markOopDesc::biased_lock_mask_in_place);
      cmpl(tmp_reg, markOopDesc::biased_lock_pattern);
      if (need_tmp_reg) {
        pop(tmp_reg);
      }
      jcc(Assembler::notEqual, cas_label);

  {- -------------------------------------------
  (1) (以下は, biased されているオブジェクトだと分かった場合の処理)
      ---------------------------------------- -}

  {- -------------------------------------------
  (1) (まず, 自分に biased されているかどうかを調べる.
       自分に biased されていれば, 引数で渡された done ラベルに分岐.
       (多くの場合(全ての場合?#TODO), これでロック処理は終了))
      ---------------------------------------- -}

      // The bias pattern is present in the object's header. Need to check
      // whether the bias owner and the epoch are both still current.
      // Note that because there is no current thread register on x86 we
      // need to store off the mark word we read out of the object to
      // avoid reloading it and needing to recheck invariants below. This
      // store is unfortunate but it makes the overall code shorter and
      // simpler.

    {- -------------------------------------------
  (1.1) ?? #TODO
        ---------------------------------------- -}

      movl(saved_mark_addr, swap_reg);
      if (need_tmp_reg) {
        push(tmp_reg);
      }

    {- -------------------------------------------
  (1.1) カレントスレッドへのポインタを取得
        ---------------------------------------- -}

      get_thread(tmp_reg);

    {- -------------------------------------------
  (1.1) ?? #TODO
        ---------------------------------------- -}

      xorl(swap_reg, tmp_reg);
      if (swap_reg_contains_mark) {
        null_check_offset = offset();
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「ロック対象のオブジェクトのクラスオブジェクトを取得し, そこから prototype header を取得する.
         そして mark フィールドの値を「prototype header の値にカレントスレッドを足し込んだ値」と比較する.
         (これらの値が age フィールドを除いて同じかどうかを確認. 自分に biased されていれば同じになる.)」
        ---------------------------------------- -}

      movl(tmp_reg, klass_addr);
      xorl(swap_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()));
      andl(swap_reg, ~((int) markOopDesc::age_mask_in_place));
      if (need_tmp_reg) {
        pop(tmp_reg);
      }

    {- -------------------------------------------
  (1.1) コード生成: (プロファイル情報の記録) (See: BiasedLockingCounters)
        ---------------------------------------- -}

      if (counters != NULL) {
        cond_inc32(Assembler::zero,
                   ExternalAddress((address)counters->biased_lock_entry_count_addr()));
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「上の比較結果が同じであれば done ラベルに分岐
         (同じでなければこのままフォールスルー)」
        ---------------------------------------- -}

      jcc(Assembler::equal, done);

  {- -------------------------------------------
  (1) (ここまでが, 自分に biased されているかどうかを調べる処理)
      ---------------------------------------- -}

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

      Label try_revoke_bias;
      Label try_rebias;

  {- -------------------------------------------
  (1) (この段階で, mark フィールドは bias locking pattern をしているが
      その他の条件がどれか満たされていない, と分かっている.
      以下, 何がダメなのかを調べていく)
      ---------------------------------------- -}

      // At this point we know that the header has the bias pattern and
      // that we are not the bias owner in the current epoch. We need to
      // figure out more details about the state of the header in order to
      // know what operations can be legally performed on the object's
      // header.

  {- -------------------------------------------
  (1) コード生成:
      「まず bulk revoke されていないかどうかを確認する. 
       (先ほどの確認結果の下位3bitを確認する.
        これは xor をとった結果に対して, markOopDesc::biased_lock_mask_in_place と
        重なる bit が残っているかどうかを調べることで行える.
        残っていたら prototype header がもう biased pattern を持っていないということなので, 
        bulk revoke されたということになる).

       bulk revoke されていた場合には, try_revoke_bias ラベルに分岐する.」
      ---------------------------------------- -}

      // If the low three bits in the xor result aren't clear, that means
      // the prototype header is no longer biased and we have to revoke
      // the bias on this object.
      testl(swap_reg, markOopDesc::biased_lock_mask_in_place);
      jcc(Assembler::notZero, try_revoke_bias);

  {- -------------------------------------------
  (1) コード生成:
      「次に bulk rebias されていないかどうかを確認する.
        (先ほどの確認結果の epoch 部分を確認する.
        これは xor をとった結果に対して, markOopDesc::biased_lock_mask_in_place と
        重なる bit が残っているかどうかを調べることで行える.
        残っていたら prototype header の epoch とこのオブジェクトの mark フィールドの epoch が
        ずれているということなので, bulk rebias されたということになる).

        bulk rebias されていた場合には, このオブジェクトを
        カレントスレッドに rebias するために, try_rebias ラベルに分岐する.」
      ---------------------------------------- -}

      // Biasing is still enabled for this data type. See whether the
      // epoch of the current bias is still valid, meaning that the epoch
      // bits of the mark word are equal to the epoch bits of the
      // prototype header. (Note that the prototype header's epoch bits
      // only change at a safepoint.) If not, attempt to rebias the object
      // toward the current thread. Note that we must be absolutely sure
      // that the current epoch is invalid in order to do this because
      // otherwise the manipulations it performs on the mark word are
      // illegal.
      testl(swap_reg, markOopDesc::epoch_mask_in_place);
      jcc(Assembler::notZero, try_rebias);

  {- -------------------------------------------
  (1) (次は, bias 対象のスレッド(biased locker)をチェックする.

       anonymously biased なら, ここで CAS を使って自分に biased させる.
       自分に biased させるのに成功したら, 引数で渡された done ラベルに分岐する. 
       (多くの場合(全ての場合?#TODO), これでロック処理は終了)

       逆に, 他のスレッドに biased されていた場合には, 
       引数で渡された slow_case ラベルへ分岐する.
       (また, anonymously biased だったが
        一歩遅く他のスレッドに取られてしまった場合も同様に処理する)
       (ただし, 引数で渡された slow_case が NULL である場合には, 
       こちらの場合でも引数で渡された done ラベルに分岐する.
       <= (これは JIT compiled code での biased locking で使われるパス? #TODO)))
      ---------------------------------------- -}

      // The epoch of the current bias is still valid but we know nothing
      // about the owner; it might be set or it might be clear. Try to
      // acquire the bias of the object using an atomic operation. If this
      // fails we will go in to the runtime to revoke the object's bias.
      // Note that we first construct the presumed unbiased header so we
      // don't accidentally blow away another thread's valid bias.

    {- -------------------------------------------
  (1.1) コード生成:
        「まず anonymously biased (= biased locker が空) だと想定して, CAS で自分に bias させてみる」

         (この処理は以下のように行われている.
          owner 部分を削った mark で CAS が成功すれば, 
          owner 部分は空だった(= anonymously biased だった)ということになる

            まず mark フィールドの値から owner 部分を削って anonymously biased と想定した場合の mark を作る.
            これは (markOopDesc::biased_lock_mask_in_place | 
            markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place) との 
            and を取ることで行える.
            次に, そこにカレントスレッドのアドレスを足しこんで新しい mark を作り, CAS で置き換える.)
        ---------------------------------------- -}

      movl(swap_reg, saved_mark_addr);
      andl(swap_reg,
           markOopDesc::biased_lock_mask_in_place | markOopDesc::age_mask_in_place | markOopDesc::epoch_mask_in_place);
      if (need_tmp_reg) {
        push(tmp_reg);
      }
      get_thread(tmp_reg);
      orl(tmp_reg, swap_reg);
      if (os::is_MP()) {
        lock();
      }
      cmpxchgptr(tmp_reg, Address(obj_reg, 0));
      if (need_tmp_reg) {
        pop(tmp_reg);
      }
      // If the biasing toward our thread failed, this means that
      // another thread succeeded in biasing it toward itself and we
      // need to revoke that bias. The revocation will occur in the
      // interpreter runtime in the slow case.

    {- -------------------------------------------
  (1.1) コード生成: (プロファイル情報の記録) (See: BiasedLockingCounters)
        ---------------------------------------- -}

      if (counters != NULL) {
        cond_inc32(Assembler::zero,
                   ExternalAddress((address)counters->anonymously_biased_lock_entry_count_addr()));
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「CAS が成功したり, 引数で渡された slow_case が NULL であれば, done ラベルへと分岐する.
          CAS が失敗し, かつ引数で渡された slow_case が NULL でなければ, slow_case ラベルへ分岐する.」
        ---------------------------------------- -}

      if (slow_case != NULL) {
        jcc(Assembler::notZero, *slow_case);
      }
      jmp(done);

  {- -------------------------------------------
  (1) (ここが try_rebias ラベルの位置)

      (以下は, bulk rebias されていた場合にカレントスレッドへの rebias を試みる処理)
      (このケースに限り, 誰かに bias されているパターンの mark フィールドを, いきなり他のスレッドに rebias できる)

      (ここでの処理は anonymously biased だった場合の処理と同様.
       具体的には以下の通り.

         CAS を使って自分に biased させ, 成功すれば引数で渡された done ラベルに分岐する.

         逆に, CAS に失敗した場合 (= 一歩遅く他のスレッドに取られてしまった場合) には, 
         引数で渡された slow_case ラベルへ分岐する.
         (ただし, 引数で渡された slow_case が NULL である場合には, 
         こちらの場合でも引数で渡された done ラベルに分岐する.
         <= (これは JIT compiled code での biased locking で使われるパス? #TODO)))

      (なおコメントによると, 
       register が足りず age を吹き飛ばしてしまっているので, 誰か修正宜しく, 
       とのこと)
      ---------------------------------------- -}

      bind(try_rebias);
      // At this point we know the epoch has expired, meaning that the
      // current "bias owner", if any, is actually invalid. Under these
      // circumstances _only_, we are allowed to use the current header's
      // value as the comparison value when doing the cas to acquire the
      // bias in the current epoch. In other words, we allow transfer of
      // the bias from one thread to another directly in this situation.
      //
      // FIXME: due to a lack of registers we currently blow away the age
      // bits in this situation. Should attempt to preserve them.

    {- -------------------------------------------
  (1.1) コード生成:
        「ロック対象のオブジェクトのクラスオブジェクトを取得し, そこから prototype header を取得する.
          そして「prototype header の値にカレントスレッドを足し込んだ値(= 自分に biased された場合の mark フィールド)」を作り
          CAS で現在の mark フィールドを置き換える.」
        ---------------------------------------- -}

      if (need_tmp_reg) {
        push(tmp_reg);
      }
      get_thread(tmp_reg);
      movl(swap_reg, klass_addr);
      orl(tmp_reg, Address(swap_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()));
      movl(swap_reg, saved_mark_addr);
      if (os::is_MP()) {
        lock();
      }
      cmpxchgptr(tmp_reg, Address(obj_reg, 0));
      if (need_tmp_reg) {
        pop(tmp_reg);
      }
      // If the biasing toward our thread failed, then another thread
      // succeeded in biasing it toward itself and we need to revoke that
      // bias. The revocation will occur in the runtime in the slow case.

    {- -------------------------------------------
  (1.1) コード生成: (プロファイル情報の記録) (See: BiasedLockingCounters)
        ---------------------------------------- -}

      if (counters != NULL) {
        cond_inc32(Assembler::zero,
                   ExternalAddress((address)counters->rebiased_lock_entry_count_addr()));
      }

    {- -------------------------------------------
  (1.1) コード生成:
        「CAS が成功したり, 引数で渡された slow_case が NULL であれば, done ラベルへと分岐する.
          CAS が失敗し, かつ引数で渡された slow_case が NULL でなければ, slow_case ラベルへ分岐する.」
         (NULL なのは JIT から使われるパス? #TODO)
        ---------------------------------------- -}

      if (slow_case != NULL) {
        jcc(Assembler::notZero, *slow_case);
      }
      jmp(done);

  {- -------------------------------------------
  (1) (ここが try_revoke_bias ラベルの位置)

      (以下は, bulk revoke されていた場合に
       このオブジェクトの mark フィールドを 非 biased locking pattern に置き換える処理.
       置き換えた後は, 引き続き従来の CAS-based scheme の処理を行う)

      (なお, ここでの置き換え時に CAS が失敗しても, 
       それは単に他のスレッドが revoke したというだけなので, 
       そのまま CAS-based locking scheme にフォールスルーしていって問題ない, とのこと)

      (なおコメントによると, 
       register が足りず age を吹き飛ばしてしまっているので, 誰か修正宜しく, 
       とのこと)
      ---------------------------------------- -}

      bind(try_revoke_bias);
      // The prototype mark in the klass doesn't have the bias bit set any
      // more, indicating that objects of this data type are not supposed
      // to be biased any more. We are going to try to reset the mark of
      // this object to the prototype value and fall through to the
      // CAS-based locking scheme. Note that if our CAS fails, it means
      // that another thread raced us for the privilege of revoking the
      // bias of this particular object, so it's okay to continue in the
      // normal locking code.
      //
      // FIXME: due to a lack of registers we currently blow away the age
      // bits in this situation. Should attempt to preserve them.

    {- -------------------------------------------
  (1.1) コード生成:
        「ロック対象のオブジェクトの mark フィールドを, 
          そのクラスオブジェクトの prototype header の値に CAS で置き換える.
          (bulk revoke 後なので prototype header には
          非 biased locking 用 (CAS-based scheme 用) の mark の値が入っている)」

        (なお, 上述の通り CAS の結果は確認する必要はないので, 成功しても失敗してもそのままフォールスルー)
        ---------------------------------------- -}

      movl(swap_reg, saved_mark_addr);
      if (need_tmp_reg) {
        push(tmp_reg);
      }
      movl(tmp_reg, klass_addr);
      movl(tmp_reg, Address(tmp_reg, Klass::prototype_header_offset_in_bytes() + klassOopDesc::klass_part_offset_in_bytes()));
      if (os::is_MP()) {
        lock();
      }
      cmpxchgptr(tmp_reg, Address(obj_reg, 0));
      if (need_tmp_reg) {
        pop(tmp_reg);
      }
      // Fall through to the normal CAS-based lock, because no matter what
      // the result of the above CAS, some thread must have succeeded in
      // removing the bias bit from the object's header.

    {- -------------------------------------------
  (1.1) コード生成: (プロファイル情報の記録) (See: BiasedLockingCounters)
        ---------------------------------------- -}

      if (counters != NULL) {
        cond_inc32(Assembler::zero,
                   ExternalAddress((address)counters->revoked_lock_entry_count_addr()));
      }

  {- -------------------------------------------
  (1) (ここが cas_label ラベルの位置)

      (biased 状態でなければ, ここまで分岐して次の処理にフォールスルーしていく)
      (bulk revoke だった場合(try_revoke_bias ラベルに飛んだ場合)も, ここを通過してこのままフォールスルーしていく)
      ---------------------------------------- -}

      bind(cas_label);

  {- -------------------------------------------
  (1) 以上のコード生成が終わったら, null_check_offset をリターン (これなんだ? #TODO)
      ---------------------------------------- -}

      return null_check_offset;
    }

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