Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  interp_masm_ppc_64.cpp   Sprache: C

 
/*
 * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2012, 2022 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */



#include "precompiled.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "interp_masm_ppc.hpp"
#include "interpreter/interpreterRuntime.hpp"
#include "oops/methodData.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/safepointMechanism.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/macros.hpp"
#include "utilities/powerOfTwo.hpp"

// Implementation of InterpreterMacroAssembler.

// This file specializes the assembler with interpreter-specific macros.

#ifdef PRODUCT
#define BLOCK_COMMENT(str) // nothing
#else
#define BLOCK_COMMENT(str) block_comment(str)
#endif

void InterpreterMacroAssembler::null_check_throw(Register a, int offset, Register temp_reg) {
  address exception_entry = Interpreter::throw_NullPointerException_entry();
  MacroAssembler::null_check_throw(a, offset, temp_reg, exception_entry);
}

void InterpreterMacroAssembler::jump_to_entry(address entry, Register Rscratch) {
  assert(entry, "Entry must have been generated by now");
  if (is_within_range_of_b(entry, pc())) {
    b(entry);
  } else {
    load_const_optimized(Rscratch, entry, R0);
    mtctr(Rscratch);
    bctr();
  }
}

void InterpreterMacroAssembler::dispatch_next(TosState state, int bcp_incr, bool generate_poll) {
  Register bytecode = R12_scratch2;
  if (bcp_incr != 0) {
    lbzu(bytecode, bcp_incr, R14_bcp);
  } else {
    lbz(bytecode, 0, R14_bcp);
  }

  dispatch_Lbyte_code(state, bytecode, Interpreter::dispatch_table(state), generate_poll);
}

void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) {
  // Load current bytecode.
  Register bytecode = R12_scratch2;
  lbz(bytecode, 0, R14_bcp);
  dispatch_Lbyte_code(state, bytecode, table);
}

// Dispatch code executed in the prolog of a bytecode which does not do it's
// own dispatch. The dispatch address is computed and placed in R24_dispatch_addr.
void InterpreterMacroAssembler::dispatch_prolog(TosState state, int bcp_incr) {
  Register bytecode = R12_scratch2;
  lbz(bytecode, bcp_incr, R14_bcp);

  load_dispatch_table(R24_dispatch_addr, Interpreter::dispatch_table(state));

  sldi(bytecode, bytecode, LogBytesPerWord);
  ldx(R24_dispatch_addr, R24_dispatch_addr, bytecode);
}

// Dispatch code executed in the epilog of a bytecode which does not do it's
// own dispatch. The dispatch address in R24_dispatch_addr is used for the
// dispatch.
void InterpreterMacroAssembler::dispatch_epilog(TosState state, int bcp_incr) {
  if (bcp_incr) { addi(R14_bcp, R14_bcp, bcp_incr); }
  mtctr(R24_dispatch_addr);
  bcctr(bcondAlways, 0, bhintbhBCCTRisNotPredictable);
}

void InterpreterMacroAssembler::check_and_handle_popframe(Register scratch_reg) {
  assert(scratch_reg != R0, "can't use R0 as scratch_reg here");
  if (JvmtiExport::can_pop_frame()) {
    Label L;

    // Check the "pending popframe condition" flag in the current thread.
    lwz(scratch_reg, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);

    // Initiate popframe handling only if it is not already being
    // processed. If the flag has the popframe_processing bit set, it
    // means that this code is called *during* popframe handling - we
    // don't want to reenter.
    andi_(R0, scratch_reg, JavaThread::popframe_pending_bit);
    beq(CCR0, L);

    andi_(R0, scratch_reg, JavaThread::popframe_processing_bit);
    bne(CCR0, L);

    // Call the Interpreter::remove_activation_preserving_args_entry()
    // func to get the address of the same-named entrypoint in the
    // generated interpreter code.
#if defined(ABI_ELFv2)
    call_c(CAST_FROM_FN_PTR(address,
                            Interpreter::remove_activation_preserving_args_entry),
           relocInfo::none);
#else
    call_c(CAST_FROM_FN_PTR(FunctionDescriptor*,
                            Interpreter::remove_activation_preserving_args_entry),
           relocInfo::none);
#endif

    // Jump to Interpreter::_remove_activation_preserving_args_entry.
    mtctr(R3_RET);
    bctr();

    align(32, 12);
    bind(L);
  }
}

void InterpreterMacroAssembler::check_and_handle_earlyret(Register scratch_reg) {
  const Register Rthr_state_addr = scratch_reg;
  if (JvmtiExport::can_force_early_return()) {
    Label Lno_early_ret;
    ld(Rthr_state_addr, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread);
    cmpdi(CCR0, Rthr_state_addr, 0);
    beq(CCR0, Lno_early_ret);

    lwz(R0, in_bytes(JvmtiThreadState::earlyret_state_offset()), Rthr_state_addr);
    cmpwi(CCR0, R0, JvmtiThreadState::earlyret_pending);
    bne(CCR0, Lno_early_ret);

    // Jump to Interpreter::_earlyret_entry.
    lwz(R3_ARG1, in_bytes(JvmtiThreadState::earlyret_tos_offset()), Rthr_state_addr);
    call_VM_leaf(CAST_FROM_FN_PTR(address, Interpreter::remove_activation_early_entry));
    mtlr(R3_RET);
    blr();

    align(32, 12);
    bind(Lno_early_ret);
  }
}

void InterpreterMacroAssembler::load_earlyret_value(TosState state, Register Rscratch1) {
  const Register RjvmtiState = Rscratch1;
  const Register Rscratch2   = R0;

  ld(RjvmtiState, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread);
  li(Rscratch2, 0);

  switch (state) {
    case atos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState);
               std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_oop_offset()), RjvmtiState);
               break;
    case ltos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState);
               break;
    case btos: // fall through
    case ztos: // fall through
    case ctos: // fall through
    case stos: // fall through
    case itos: lwz(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState);
               break;
    case ftos: lfs(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState);
               break;
    case dtos: lfd(F15_ftos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState);
               break;
    case vtos: break;
    default  : ShouldNotReachHere();
  }

  // Clean up tos value in the jvmti thread state.
  std(Rscratch2, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState);
  // Set tos state field to illegal value.
  li(Rscratch2, ilgl);
  stw(Rscratch2, in_bytes(JvmtiThreadState::earlyret_tos_offset()), RjvmtiState);
}

// Common code to dispatch and dispatch_only.
// Dispatch value in Lbyte_code and increment Lbcp.

void InterpreterMacroAssembler::load_dispatch_table(Register dst, address* table) {
  address table_base = (address)Interpreter::dispatch_table((TosState)0);
  intptr_t table_offs = (intptr_t)table - (intptr_t)table_base;
  if (is_simm16(table_offs)) {
    addi(dst, R25_templateTableBase, (int)table_offs);
  } else {
    load_const_optimized(dst, table, R0);
  }
}

void InterpreterMacroAssembler::dispatch_Lbyte_code(TosState state, Register bytecode,
                                                    address* table, bool generate_poll) {
  assert_different_registers(bytecode, R11_scratch1);

  // Calc dispatch table address.
  load_dispatch_table(R11_scratch1, table);

  if (generate_poll) {
    address *sfpt_tbl = Interpreter::safept_table(state);
    if (table != sfpt_tbl) {
      Label dispatch;
      ld(R0, in_bytes(JavaThread::polling_word_offset()), R16_thread);
      // Armed page has poll_bit set, if poll bit is cleared just continue.
      andi_(R0, R0, SafepointMechanism::poll_bit());
      beq(CCR0, dispatch);
      load_dispatch_table(R11_scratch1, sfpt_tbl);
      align(32, 16);
      bind(dispatch);
    }
  }

  sldi(R12_scratch2, bytecode, LogBytesPerWord);
  ldx(R11_scratch1, R11_scratch1, R12_scratch2);

  // Jump off!
  mtctr(R11_scratch1);
  bcctr(bcondAlways, 0, bhintbhBCCTRisNotPredictable);
}

void InterpreterMacroAssembler::load_receiver(Register Rparam_count, Register Rrecv_dst) {
  sldi(Rrecv_dst, Rparam_count, Interpreter::logStackElementSize);
  ldx(Rrecv_dst, Rrecv_dst, R15_esp);
}

// helpers for expression stack

void InterpreterMacroAssembler::pop_i(Register r) {
  lwzu(r, Interpreter::stackElementSize, R15_esp);
}

void InterpreterMacroAssembler::pop_ptr(Register r) {
  ldu(r, Interpreter::stackElementSize, R15_esp);
}

void InterpreterMacroAssembler::pop_l(Register r) {
  ld(r, Interpreter::stackElementSize, R15_esp);
  addi(R15_esp, R15_esp, 2 * Interpreter::stackElementSize);
}

void InterpreterMacroAssembler::pop_f(FloatRegister f) {
  lfsu(f, Interpreter::stackElementSize, R15_esp);
}

void InterpreterMacroAssembler::pop_d(FloatRegister f) {
  lfd(f, Interpreter::stackElementSize, R15_esp);
  addi(R15_esp, R15_esp, 2 * Interpreter::stackElementSize);
}

void InterpreterMacroAssembler::push_i(Register r) {
  stw(r, 0, R15_esp);
  addi(R15_esp, R15_esp, - Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::push_ptr(Register r) {
  std(r, 0, R15_esp);
  addi(R15_esp, R15_esp, - Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::push_l(Register r) {
  // Clear unused slot.
  load_const_optimized(R0, 0L);
  std(R0, 0, R15_esp);
  std(r, - Interpreter::stackElementSize, R15_esp);
  addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::push_f(FloatRegister f) {
  stfs(f, 0, R15_esp);
  addi(R15_esp, R15_esp, - Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::push_d(FloatRegister f)   {
  stfd(f, - Interpreter::stackElementSize, R15_esp);
  addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::push_2ptrs(Register first, Register second) {
  std(first, 0, R15_esp);
  std(second, -Interpreter::stackElementSize, R15_esp);
  addi(R15_esp, R15_esp, - 2 * Interpreter::stackElementSize );
}

void InterpreterMacroAssembler::move_l_to_d(Register l, FloatRegister d) {
  if (VM_Version::has_mtfprd()) {
    mtfprd(d, l);
  } else {
    std(l, 0, R15_esp);
    lfd(d, 0, R15_esp);
  }
}

void InterpreterMacroAssembler::move_d_to_l(FloatRegister d, Register l) {
  if (VM_Version::has_mtfprd()) {
    mffprd(l, d);
  } else {
    stfd(d, 0, R15_esp);
    ld(l, 0, R15_esp);
  }
}

void InterpreterMacroAssembler::push(TosState state) {
  switch (state) {
    case atos: push_ptr();                break;
    case btos:
    case ztos:
    case ctos:
    case stos:
    case itos: push_i();                  break;
    case ltos: push_l();                  break;
    case ftos: push_f();                  break;
    case dtos: push_d();                  break;
    case vtos: /* nothing to do */        break;
    default  : ShouldNotReachHere();
  }
}

void InterpreterMacroAssembler::pop(TosState state) {
  switch (state) {
    case atos: pop_ptr();            break;
    case btos:
    case ztos:
    case ctos:
    case stos:
    case itos: pop_i();              break;
    case ltos: pop_l();              break;
    case ftos: pop_f();              break;
    case dtos: pop_d();              break;
    case vtos: /* nothing to do */   break;
    default  : ShouldNotReachHere();
  }
  verify_oop(R17_tos, state);
}

void InterpreterMacroAssembler::empty_expression_stack() {
  addi(R15_esp, R26_monitor, - Interpreter::stackElementSize);
}

void InterpreterMacroAssembler::get_2_byte_integer_at_bcp(int         bcp_offset,
                                                          Register    Rdst,
                                                          signedOrNot is_signed) {
#if defined(VM_LITTLE_ENDIAN)
  if (bcp_offset) {
    load_const_optimized(Rdst, bcp_offset);
    lhbrx(Rdst, R14_bcp, Rdst);
  } else {
    lhbrx(Rdst, R14_bcp);
  }
  if (is_signed == Signed) {
    extsh(Rdst, Rdst);
  }
#else
  // Read Java big endian format.
  if (is_signed == Signed) {
    lha(Rdst, bcp_offset, R14_bcp);
  } else {
    lhz(Rdst, bcp_offset, R14_bcp);
  }
#endif
}

void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int         bcp_offset,
                                                          Register    Rdst,
                                                          signedOrNot is_signed) {
#if defined(VM_LITTLE_ENDIAN)
  if (bcp_offset) {
    load_const_optimized(Rdst, bcp_offset);
    lwbrx(Rdst, R14_bcp, Rdst);
  } else {
    lwbrx(Rdst, R14_bcp);
  }
  if (is_signed == Signed) {
    extsw(Rdst, Rdst);
  }
#else
  // Read Java big endian format.
  if (bcp_offset & 3) { // Offset unaligned?
    load_const_optimized(Rdst, bcp_offset);
    if (is_signed == Signed) {
      lwax(Rdst, R14_bcp, Rdst);
    } else {
      lwzx(Rdst, R14_bcp, Rdst);
    }
  } else {
    if (is_signed == Signed) {
      lwa(Rdst, bcp_offset, R14_bcp);
    } else {
      lwz(Rdst, bcp_offset, R14_bcp);
    }
  }
#endif
}


// Load the constant pool cache index from the bytecode stream.
//
// Kills / writes:
//   - Rdst, Rscratch
void InterpreterMacroAssembler::get_cache_index_at_bcp(Register Rdst, int bcp_offset,
                                                       size_t index_size) {
  assert(bcp_offset > 0, "bcp is still pointing to start of bytecode");
  // Cache index is always in the native format, courtesy of Rewriter.
  if (index_size == sizeof(u2)) {
    lhz(Rdst, bcp_offset, R14_bcp);
  } else if (index_size == sizeof(u4)) {
    if (bcp_offset & 3) {
      load_const_optimized(Rdst, bcp_offset);
      lwax(Rdst, R14_bcp, Rdst);
    } else {
      lwa(Rdst, bcp_offset, R14_bcp);
    }
    assert(ConstantPool::decode_invokedynamic_index(~123) == 123, "else change next line");
    nand(Rdst, Rdst, Rdst); // convert to plain index
  } else if (index_size == sizeof(u1)) {
    lbz(Rdst, bcp_offset, R14_bcp);
  } else {
    ShouldNotReachHere();
  }
  // Rdst now contains cp cache index.
}

void InterpreterMacroAssembler::get_cache_and_index_at_bcp(Register cache, int bcp_offset,
                                                           size_t index_size) {
  get_cache_index_at_bcp(cache, bcp_offset, index_size);
  sldi(cache, cache, exact_log2(in_words(ConstantPoolCacheEntry::size()) * BytesPerWord));
  add(cache, R27_constPoolCache, cache);
}

// Load 4-byte signed or unsigned integer in Java format (that is, big-endian format)
// from (Rsrc)+offset.
void InterpreterMacroAssembler::get_u4(Register Rdst, Register Rsrc, int offset,
                                       signedOrNot is_signed) {
#if defined(VM_LITTLE_ENDIAN)
  if (offset) {
    load_const_optimized(Rdst, offset);
    lwbrx(Rdst, Rdst, Rsrc);
  } else {
    lwbrx(Rdst, Rsrc);
  }
  if (is_signed == Signed) {
    extsw(Rdst, Rdst);
  }
#else
  if (is_signed == Signed) {
    lwa(Rdst, offset, Rsrc);
  } else {
    lwz(Rdst, offset, Rsrc);
  }
#endif
}

// Load object from cpool->resolved_references(index).
// Kills:
//   - index
void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index,
                                                                 Register tmp1, Register tmp2,
                                                                 Label *L_handle_null) {
  assert_different_registers(result, index, tmp1, tmp2);
  assert(index->is_nonvolatile(), "needs to survive C-call in resolve_oop_handle");
  get_constant_pool(result);

  // Convert from field index to resolved_references() index and from
  // word index to byte offset. Since this is a java object, it can be compressed.
  sldi(index, index, LogBytesPerHeapOop);
  // Load pointer for resolved_references[] objArray.
  ld(result, ConstantPool::cache_offset_in_bytes(), result);
  ld(result, ConstantPoolCache::resolved_references_offset_in_bytes(), result);
  resolve_oop_handle(result, tmp1, tmp2, MacroAssembler::PRESERVATION_NONE);
#ifdef ASSERT
  Label index_ok;
  lwa(R0, arrayOopDesc::length_offset_in_bytes(), result);
  sldi(R0, R0, LogBytesPerHeapOop);
  cmpd(CCR0, index, R0);
  blt(CCR0, index_ok);
  stop("resolved reference index out of bounds");
  bind(index_ok);
#endif
  // Add in the index.
  add(result, index, result);
  load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), result,
                tmp1, tmp2,
                MacroAssembler::PRESERVATION_NONE,
                0, L_handle_null);
}

// load cpool->resolved_klass_at(index)
void InterpreterMacroAssembler::load_resolved_klass_at_offset(Register Rcpool, Register Roffset, Register Rklass) {
  // int value = *(Rcpool->int_at_addr(which));
  // int resolved_klass_index = extract_low_short_from_int(value);
  add(Roffset, Rcpool, Roffset);
#if defined(VM_LITTLE_ENDIAN)
  lhz(Roffset, sizeof(ConstantPool), Roffset);     // Roffset = resolved_klass_index
#else
  lhz(Roffset, sizeof(ConstantPool) + 2, Roffset); // Roffset = resolved_klass_index
#endif

  ld(Rklass, ConstantPool::resolved_klasses_offset_in_bytes(), Rcpool); // Rklass = Rcpool->_resolved_klasses

  sldi(Roffset, Roffset, LogBytesPerWord);
  addi(Roffset, Roffset, Array<Klass*>::base_offset_in_bytes());
  isync(); // Order load of instance Klass wrt. tags.
  ldx(Rklass, Rklass, Roffset);
}

void InterpreterMacroAssembler::load_resolved_method_at_index(int byte_no,
                                                              Register cache,
                                                              Register method) {
  const int method_offset = in_bytes(
    ConstantPoolCache::base_offset() +
      ((byte_no == TemplateTable::f2_byte)
       ? ConstantPoolCacheEntry::f2_offset()
       : ConstantPoolCacheEntry::f1_offset()));

  ld(method, method_offset, cache); // get f1 Method*
}

// Generate a subtype check: branch to ok_is_subtype if sub_klass is
// a subtype of super_klass. Blows registers Rsub_klass, tmp1, tmp2.
void InterpreterMacroAssembler::gen_subtype_check(Register Rsub_klass, Register Rsuper_klass, Register Rtmp1,
                                                  Register Rtmp2, Register Rtmp3, Label &ok_is_subtype) {
  // Profile the not-null value's klass.
  profile_typecheck(Rsub_klass, Rtmp1, Rtmp2);
  check_klass_subtype(Rsub_klass, Rsuper_klass, Rtmp1, Rtmp2, ok_is_subtype);
  profile_typecheck_failed(Rtmp1, Rtmp2);
}

// Separate these two to allow for delay slot in middle.
// These are used to do a test and full jump to exception-throwing code.

// Check that index is in range for array, then shift index by index_shift,
// and put arrayOop + shifted_index into res.
// Note: res is still shy of address by array offset into object.

void InterpreterMacroAssembler::index_check_without_pop(Register Rarray, Register Rindex,
                                                        int index_shift, Register Rtmp, Register Rres) {
  // Check that index is in range for array, then shift index by index_shift,
  // and put arrayOop + shifted_index into res.
  // Note: res is still shy of address by array offset into object.
  // Kills:
  //   - Rindex
  // Writes:
  //   - Rres: Address that corresponds to the array index if check was successful.
  verify_oop(Rarray);
  const Register Rlength   = R0;
  const Register RsxtIndex = Rtmp;
  Label LisNull, LnotOOR;

  // Array nullcheck
  if (!ImplicitNullChecks) {
    cmpdi(CCR0, Rarray, 0);
    beq(CCR0, LisNull);
  } else {
    null_check_throw(Rarray, arrayOopDesc::length_offset_in_bytes(), /*temp*/RsxtIndex);
  }

  // Rindex might contain garbage in upper bits (remember that we don't sign extend
  // during integer arithmetic operations). So kill them and put value into same register
  // where ArrayIndexOutOfBounds would expect the index in.
  rldicl(RsxtIndex, Rindex, 0, 32); // zero extend 32 bit -> 64 bit

  // Index check
  lwz(Rlength, arrayOopDesc::length_offset_in_bytes(), Rarray);
  cmplw(CCR0, Rindex, Rlength);
  sldi(RsxtIndex, RsxtIndex, index_shift);
  blt(CCR0, LnotOOR);
  // Index should be in R17_tos, array should be in R4_ARG2.
  mr_if_needed(R17_tos, Rindex);
  mr_if_needed(R4_ARG2, Rarray);
  load_dispatch_table(Rtmp, (address*)Interpreter::_throw_ArrayIndexOutOfBoundsException_entry);
  mtctr(Rtmp);
  bctr();

  if (!ImplicitNullChecks) {
    bind(LisNull);
    load_dispatch_table(Rtmp, (address*)Interpreter::_throw_NullPointerException_entry);
    mtctr(Rtmp);
    bctr();
  }

  align(32, 16);
  bind(LnotOOR);

  // Calc address
  add(Rres, RsxtIndex, Rarray);
}

void InterpreterMacroAssembler::index_check(Register array, Register index,
                                            int index_shift, Register tmp, Register res) {
  // pop array
  pop_ptr(array);

  // check array
  index_check_without_pop(array, index, index_shift, tmp, res);
}

void InterpreterMacroAssembler::get_const(Register Rdst) {
  ld(Rdst, in_bytes(Method::const_offset()), R19_method);
}

void InterpreterMacroAssembler::get_constant_pool(Register Rdst) {
  get_const(Rdst);
  ld(Rdst, in_bytes(ConstMethod::constants_offset()), Rdst);
}

void InterpreterMacroAssembler::get_constant_pool_cache(Register Rdst) {
  get_constant_pool(Rdst);
  ld(Rdst, ConstantPool::cache_offset_in_bytes(), Rdst);
}

void InterpreterMacroAssembler::get_cpool_and_tags(Register Rcpool, Register Rtags) {
  get_constant_pool(Rcpool);
  ld(Rtags, ConstantPool::tags_offset_in_bytes(), Rcpool);
}

// Unlock if synchronized method.
//
// Unlock the receiver if this is a synchronized method.
// Unlock any Java monitors from synchronized blocks.
//
// If there are locked Java monitors
//   If throw_monitor_exception
//     throws IllegalMonitorStateException
//   Else if install_monitor_exception
//     installs IllegalMonitorStateException
//   Else
//     no error processing
void InterpreterMacroAssembler::unlock_if_synchronized_method(TosState state,
                                                              bool throw_monitor_exception,
                                                              bool install_monitor_exception) {
  Label Lunlocked, Lno_unlock;
  {
    Register Rdo_not_unlock_flag = R11_scratch1;
    Register Raccess_flags       = R12_scratch2;

    // Check if synchronized method or unlocking prevented by
    // JavaThread::do_not_unlock_if_synchronized flag.
    lbz(Rdo_not_unlock_flag, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
    lwz(Raccess_flags, in_bytes(Method::access_flags_offset()), R19_method);
    li(R0, 0);
    stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); // reset flag

    push(state);

    // Skip if we don't have to unlock.
    rldicl_(R0, Raccess_flags, 64-JVM_ACC_SYNCHRONIZED_BIT, 63); // Extract bit and compare to 0.
    beq(CCR0, Lunlocked);

    cmpwi(CCR0, Rdo_not_unlock_flag, 0);
    bne(CCR0, Lno_unlock);
  }

  // Unlock
  {
    Register Rmonitor_base = R11_scratch1;

    Label Lunlock;
    // If it's still locked, everything is ok, unlock it.
    ld(Rmonitor_base, 0, R1_SP);
    addi(Rmonitor_base, Rmonitor_base,
         -(frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes())); // Monitor base

    ld(R0, BasicObjectLock::obj_offset_in_bytes(), Rmonitor_base);
    cmpdi(CCR0, R0, 0);
    bne(CCR0, Lunlock);

    // If it's already unlocked, throw exception.
    if (throw_monitor_exception) {
      call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception));
      should_not_reach_here();
    } else {
      if (install_monitor_exception) {
        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception));
        b(Lunlocked);
      }
    }

    bind(Lunlock);
    unlock_object(Rmonitor_base);
  }

  // Check that all other monitors are unlocked. Throw IllegelMonitorState exception if not.
  bind(Lunlocked);
  {
    Label Lexception, Lrestart;
    Register Rcurrent_obj_addr = R11_scratch1;
    const int delta = frame::interpreter_frame_monitor_size_in_bytes();
    assert((delta & LongAlignmentMask) == 0, "sizeof BasicObjectLock must be even number of doublewords");

    bind(Lrestart);
    // Set up search loop: Calc num of iterations.
    {
      Register Riterations = R12_scratch2;
      Register Rmonitor_base = Rcurrent_obj_addr;
      ld(Rmonitor_base, 0, R1_SP);
      addi(Rmonitor_base, Rmonitor_base, - frame::ijava_state_size);  // Monitor base

      subf_(Riterations, R26_monitor, Rmonitor_base);
      ble(CCR0, Lno_unlock);

      addi(Rcurrent_obj_addr, Rmonitor_base,
           BasicObjectLock::obj_offset_in_bytes() - frame::interpreter_frame_monitor_size_in_bytes());
      // Check if any monitor is on stack, bail out if not
      srdi(Riterations, Riterations, exact_log2(delta));
      mtctr(Riterations);
    }

    // The search loop: Look for locked monitors.
    {
      const Register Rcurrent_obj = R0;
      Label Lloop;

      ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
      addi(Rcurrent_obj_addr, Rcurrent_obj_addr, -delta);
      bind(Lloop);

      // Check if current entry is used.
      cmpdi(CCR0, Rcurrent_obj, 0);
      bne(CCR0, Lexception);
      // Preload next iteration's compare value.
      ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
      addi(Rcurrent_obj_addr, Rcurrent_obj_addr, -delta);
      bdnz(Lloop);
    }
    // Fell through: Everything's unlocked => finish.
    b(Lno_unlock);

    // An object is still locked => need to throw exception.
    bind(Lexception);
    if (throw_monitor_exception) {
      call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception));
      should_not_reach_here();
    } else {
      // Stack unrolling. Unlock object and if requested, install illegal_monitor_exception.
      // Unlock does not block, so don't have to worry about the frame.
      Register Rmonitor_addr = R11_scratch1;
      addi(Rmonitor_addr, Rcurrent_obj_addr, -BasicObjectLock::obj_offset_in_bytes() + delta);
      unlock_object(Rmonitor_addr);
      if (install_monitor_exception) {
        call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::new_illegal_monitor_state_exception));
      }
      b(Lrestart);
    }
  }

  align(32, 12);
  bind(Lno_unlock);
  pop(state);
}

// Support function for remove_activation & Co.
void InterpreterMacroAssembler::merge_frames(Register Rsender_sp, Register return_pc,
                                             Register Rscratch1, Register Rscratch2) {
  // Pop interpreter frame.
  ld(Rscratch1, 0, R1_SP); // *SP
  ld(Rsender_sp, _ijava_state_neg(sender_sp), Rscratch1); // top_frame_sp
  ld(Rscratch2, 0, Rscratch1); // **SP
  if (return_pc!=noreg) {
    ld(return_pc, _abi0(lr), Rscratch1); // LR
  }

  // Merge top frames.
  subf(Rscratch1, R1_SP, Rsender_sp); // top_frame_sp - SP
  stdux(Rscratch2, R1_SP, Rscratch1); // atomically set *(SP = top_frame_sp) = **SP
}

void InterpreterMacroAssembler::narrow(Register result) {
  Register ret_type = R11_scratch1;
  ld(R11_scratch1, in_bytes(Method::const_offset()), R19_method);
  lbz(ret_type, in_bytes(ConstMethod::result_type_offset()), R11_scratch1);

  Label notBool, notByte, notChar, done;

  // common case first
  cmpwi(CCR0, ret_type, T_INT);
  beq(CCR0, done);

  cmpwi(CCR0, ret_type, T_BOOLEAN);
  bne(CCR0, notBool);
  andi(result, result, 0x1);
  b(done);

  bind(notBool);
  cmpwi(CCR0, ret_type, T_BYTE);
  bne(CCR0, notByte);
  extsb(result, result);
  b(done);

  bind(notByte);
  cmpwi(CCR0, ret_type, T_CHAR);
  bne(CCR0, notChar);
  andi(result, result, 0xffff);
  b(done);

  bind(notChar);
  // cmpwi(CCR0, ret_type, T_SHORT);  // all that's left
  // bne(CCR0, done);
  extsh(result, result);

  // Nothing to do for T_INT
  bind(done);
}

// Remove activation.
//
// Apply stack watermark barrier.
// Unlock the receiver if this is a synchronized method.
// Unlock any Java monitors from synchronized blocks.
// Remove the activation from the stack.
//
// If there are locked Java monitors
//    If throw_monitor_exception
//       throws IllegalMonitorStateException
//    Else if install_monitor_exception
//       installs IllegalMonitorStateException
//    Else
//       no error processing
void InterpreterMacroAssembler::remove_activation(TosState state,
                                                  bool throw_monitor_exception,
                                                  bool install_monitor_exception) {
  BLOCK_COMMENT("remove_activation {");

  // The below poll is for the stack watermark barrier. It allows fixing up frames lazily,
  // that would normally not be safe to use. Such bad returns into unsafe territory of
  // the stack, will call InterpreterRuntime::at_unwind.
  Label slow_path;
  Label fast_path;
  safepoint_poll(slow_path, R11_scratch1, true /* at_return */, false /* in_nmethod */);
  b(fast_path);
  bind(slow_path);
  push(state);
  set_last_Java_frame(R1_SP, noreg);
  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::at_unwind), R16_thread);
  reset_last_Java_frame();
  pop(state);
  align(32);
  bind(fast_path);

  unlock_if_synchronized_method(state, throw_monitor_exception, install_monitor_exception);

  // Save result (push state before jvmti call and pop it afterwards) and notify jvmti.
  notify_method_exit(false, state, NotifyJVMTI, true);

  BLOCK_COMMENT("reserved_stack_check:");
  if (StackReservedPages > 0) {
    // Test if reserved zone needs to be enabled.
    Label no_reserved_zone_enabling;

    // Compare frame pointers. There is no good stack pointer, as with stack
    // frame compression we can get different SPs when we do calls. A subsequent
    // call could have a smaller SP, so that this compare succeeds for an
    // inner call of the method annotated with ReservedStack.
    ld_ptr(R0, JavaThread::reserved_stack_activation_offset(), R16_thread);
    ld_ptr(R11_scratch1, _abi0(callers_sp), R1_SP); // Load frame pointer.
    cmpld(CCR0, R11_scratch1, R0);
    blt_predict_taken(CCR0, no_reserved_zone_enabling);

    // Enable reserved zone again, throw stack overflow exception.
    call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::enable_stack_reserved_zone), R16_thread);
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_delayed_StackOverflowError));

    should_not_reach_here();

    bind(no_reserved_zone_enabling);
  }

  verify_oop(R17_tos, state);
  verify_thread();

  merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2);
  mtlr(R0);
  pop_cont_fastpath();
  BLOCK_COMMENT("} remove_activation");
}

// Lock object
//
// Registers alive
//   monitor - Address of the BasicObjectLock to be used for locking,
//             which must be initialized with the object to lock.
//   object  - Address of the object to be locked.
//
void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
  if (UseHeavyMonitors) {
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
  } else {
    // template code:
    //
    // markWord displaced_header = obj->mark().set_unlocked();
    // monitor->lock()->set_displaced_header(displaced_header);
    // if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) {
    //   // We stored the monitor address into the object's mark word.
    // } else if (THREAD->is_lock_owned((address)displaced_header))
    //   // Simple recursive case.
    //   monitor->lock()->set_displaced_header(NULL);
    // } else {
    //   // Slow path.
    //   InterpreterRuntime::monitorenter(THREAD, monitor);
    // }

    const Register displaced_header = R7_ARG5;
    const Register object_mark_addr = R8_ARG6;
    const Register current_header   = R9_ARG7;
    const Register tmp              = R10_ARG8;

    Label count_locking, done;
    Label cas_failed, slow_case;

    assert_different_registers(displaced_header, object_mark_addr, current_header, tmp);

    // markWord displaced_header = obj->mark().set_unlocked();

    // Load markWord from object into displaced_header.
    ld(displaced_header, oopDesc::mark_offset_in_bytes(), object);

    if (DiagnoseSyncOnValueBasedClasses != 0) {
      load_klass(tmp, object);
      lwz(tmp, in_bytes(Klass::access_flags_offset()), tmp);
      testbitdi(CCR0, R0, tmp, exact_log2(JVM_ACC_IS_VALUE_BASED_CLASS));
      bne(CCR0, slow_case);
    }

    // Set displaced_header to be (markWord of object | UNLOCK_VALUE).
    ori(displaced_header, displaced_header, markWord::unlocked_value);

    // monitor->lock()->set_displaced_header(displaced_header);

    // Initialize the box (Must happen before we update the object mark!).
    std(displaced_header, BasicObjectLock::lock_offset_in_bytes() +
        BasicLock::displaced_header_offset_in_bytes(), monitor);

    // if (Atomic::cmpxchg(/*addr*/obj->mark_addr(), /*cmp*/displaced_header, /*ex=*/monitor) == displaced_header) {

    // Store stack address of the BasicObjectLock (this is monitor) into object.
    addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes());

    // Must fence, otherwise, preceding store(s) may float below cmpxchg.
    // CmpxchgX sets CCR0 to cmpX(current, displaced).
    cmpxchgd(/*flag=*/CCR0,
             /*current_value=*/current_header,
             /*compare_value=*/displaced_header, /*exchange_value=*/monitor,
             /*where=*/object_mark_addr,
             MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
             MacroAssembler::cmpxchgx_hint_acquire_lock(),
             noreg,
             &cas_failed,
             /*check without membar and ldarx first*/true);

    // If the compare-and-exchange succeeded, then we found an unlocked
    // object and we have now locked it.
    b(count_locking);
    bind(cas_failed);

    // } else if (THREAD->is_lock_owned((address)displaced_header))
    //   // Simple recursive case.
    //   monitor->lock()->set_displaced_header(NULL);

    // We did not see an unlocked object so try the fast recursive case.

    // Check if owner is self by comparing the value in the markWord of object
    // (current_header) with the stack pointer.
    sub(current_header, current_header, R1_SP);

    assert(os::vm_page_size() > 0xfff, "page size too small - change the constant");
    load_const_optimized(tmp, ~(os::vm_page_size()-1) | markWord::lock_mask_in_place);

    and_(R0/*==0?*/, current_header, tmp);
    // If condition is true we are done and hence we can store 0 in the displaced
    // header indicating it is a recursive lock.
    bne(CCR0, slow_case);
    std(R0/*==0!*/, BasicObjectLock::lock_offset_in_bytes() +
        BasicLock::displaced_header_offset_in_bytes(), monitor);
    b(count_locking);

    // } else {
    //   // Slow path.
    //   InterpreterRuntime::monitorenter(THREAD, monitor);

    // None of the above fast optimizations worked so we have to get into the
    // slow case of monitor enter.
    bind(slow_case);
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
    b(done);
    // }
    align(32, 12);
    bind(count_locking);
    inc_held_monitor_count(current_header /*tmp*/);
    bind(done);
  }
}

// Unlocks an object. Used in monitorexit bytecode and remove_activation.
//
// Registers alive
//   monitor - Address of the BasicObjectLock to be used for locking,
//             which must be initialized with the object to lock.
//
// Throw IllegalMonitorException if object is not locked by current thread.
void InterpreterMacroAssembler::unlock_object(Register monitor) {
  if (UseHeavyMonitors) {
    call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor);
  } else {

    // template code:
    //
    // if ((displaced_header = monitor->displaced_header()) == NULL) {
    //   // Recursive unlock. Mark the monitor unlocked by setting the object field to NULL.
    //   monitor->set_obj(NULL);
    // } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) {
    //   // We swapped the unlocked mark in displaced_header into the object's mark word.
    //   monitor->set_obj(NULL);
    // } else {
    //   // Slow path.
    //   InterpreterRuntime::monitorexit(monitor);
    // }

    const Register object           = R7_ARG5;
    const Register displaced_header = R8_ARG6;
    const Register object_mark_addr = R9_ARG7;
    const Register current_header   = R10_ARG8;

    Label free_slot;
    Label slow_case;

    assert_different_registers(object, displaced_header, object_mark_addr, current_header);

    // Test first if we are in the fast recursive case.
    ld(displaced_header, BasicObjectLock::lock_offset_in_bytes() +
           BasicLock::displaced_header_offset_in_bytes(), monitor);

    // If the displaced header is zero, we have a recursive unlock.
    cmpdi(CCR0, displaced_header, 0);
    beq(CCR0, free_slot); // recursive unlock

    // } else if (Atomic::cmpxchg(obj->mark_addr(), monitor, displaced_header) == monitor) {
    //   // We swapped the unlocked mark in displaced_header into the object's mark word.
    //   monitor->set_obj(NULL);

    // If we still have a lightweight lock, unlock the object and be done.

    // The object address from the monitor is in object.
    ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor);
    addi(object_mark_addr, object, oopDesc::mark_offset_in_bytes());

    // We have the displaced header in displaced_header. If the lock is still
    // lightweight, it will contain the monitor address and we'll store the
    // displaced header back into the object's mark word.
    // CmpxchgX sets CCR0 to cmpX(current, monitor).
    cmpxchgd(/*flag=*/CCR0,
             /*current_value=*/current_header,
             /*compare_value=*/monitor, /*exchange_value=*/displaced_header,
             /*where=*/object_mark_addr,
             MacroAssembler::MemBarRel,
             MacroAssembler::cmpxchgx_hint_release_lock(),
             noreg,
             &slow_case);
    b(free_slot);

    // } else {
    //   // Slow path.
    //   InterpreterRuntime::monitorexit(monitor);

    // The lock has been converted into a heavy lock and hence
    // we need to get into the slow case.
    bind(slow_case);
    call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit), monitor);
    // }

    Label done;
    b(done); // Monitor register may be overwritten! Runtime has already freed the slot.

    // Exchange worked, do monitor->set_obj(NULL);
    align(32, 12);
    bind(free_slot);
    li(R0, 0);
    std(R0, BasicObjectLock::obj_offset_in_bytes(), monitor);
    dec_held_monitor_count(current_header /*tmp*/);
    bind(done);
  }
}

// Load compiled (i2c) or interpreter entry when calling from interpreted and
// do the call. Centralized so that all interpreter calls will do the same actions.
// If jvmti single stepping is on for a thread we must not call compiled code.
//
// Input:
//   - Rtarget_method: method to call
//   - Rret_addr:      return address
//   - 2 scratch regs
//
void InterpreterMacroAssembler::call_from_interpreter(Register Rtarget_method, Register Rret_addr,
                                                      Register Rscratch1, Register Rscratch2) {
  assert_different_registers(Rscratch1, Rscratch2, Rtarget_method, Rret_addr);
  // Assume we want to go compiled if available.
  const Register Rtarget_addr = Rscratch1;
  const Register Rinterp_only = Rscratch2;

  ld(Rtarget_addr, in_bytes(Method::from_interpreted_offset()), Rtarget_method);

  if (JvmtiExport::can_post_interpreter_events()) {
    lwz(Rinterp_only, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread);

    // JVMTI events, such as single-stepping, are implemented partly by avoiding running
    // compiled code in threads for which the event is enabled. Check here for
    // interp_only_mode if these events CAN be enabled.
    Label done;
    verify_thread();
    cmpwi(CCR0, Rinterp_only, 0);
    beq(CCR0, done);
    ld(Rtarget_addr, in_bytes(Method::interpreter_entry_offset()), Rtarget_method);
    align(32, 12);
    bind(done);
  }

#ifdef ASSERT
  {
    Label Lok;
    cmpdi(CCR0, Rtarget_addr, 0);
    bne(CCR0, Lok);
    stop("null entry point");
    bind(Lok);
  }
#endif // ASSERT

  mr(R21_sender_SP, R1_SP);

  // Calc a precise SP for the call. The SP value we calculated in
  // generate_fixed_frame() is based on the max_stack() value, so we would waste stack space
  // if esp is not max. Also, the i2c adapter extends the stack space without restoring
  // our pre-calced value, so repeating calls via i2c would result in stack overflow.
  // Since esp already points to an empty slot, we just have to sub 1 additional slot
  // to meet the abi scratch requirements.
  // The max_stack pointer will get restored by means of the GR_Lmax_stack local in
  // the return entry of the interpreter.
  addi(Rscratch2, R15_esp, Interpreter::stackElementSize - frame::abi_reg_args_size);
  clrrdi(Rscratch2, Rscratch2, exact_log2(frame::alignment_in_bytes)); // round towards smaller address
  resize_frame_absolute(Rscratch2, Rscratch2, R0);

  mr_if_needed(R19_method, Rtarget_method);
  mtctr(Rtarget_addr);
  mtlr(Rret_addr);

  save_interpreter_state(Rscratch2);
#ifdef ASSERT
  ld(Rscratch1, _ijava_state_neg(top_frame_sp), Rscratch2); // Rscratch2 contains fp
  cmpd(CCR0, R21_sender_SP, Rscratch1);
  asm_assert_eq("top_frame_sp incorrect");
#endif

  bctr();
}

// Set the method data pointer for the current bcp.
void InterpreterMacroAssembler::set_method_data_pointer_for_bcp() {
  assert(ProfileInterpreter, "must be profiling interpreter");
  Label get_continue;
  ld(R28_mdx, in_bytes(Method::method_data_offset()), R19_method);
  test_method_data_pointer(get_continue);
  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::bcp_to_di), R19_method, R14_bcp);

  addi(R28_mdx, R28_mdx, in_bytes(MethodData::data_offset()));
  add(R28_mdx, R28_mdx, R3_RET);
  bind(get_continue);
}

// Test ImethodDataPtr. If it is null, continue at the specified label.
void InterpreterMacroAssembler::test_method_data_pointer(Label& zero_continue) {
  assert(ProfileInterpreter, "must be profiling interpreter");
  cmpdi(CCR0, R28_mdx, 0);
  beq(CCR0, zero_continue);
}

void InterpreterMacroAssembler::verify_method_data_pointer() {
  assert(ProfileInterpreter, "must be profiling interpreter");
#ifdef ASSERT
  Label verify_continue;
  test_method_data_pointer(verify_continue);

  // If the mdp is valid, it will point to a DataLayout header which is
  // consistent with the bcp. The converse is highly probable also.
  lhz(R11_scratch1, in_bytes(DataLayout::bci_offset()), R28_mdx);
  ld(R12_scratch2, in_bytes(Method::const_offset()), R19_method);
  addi(R11_scratch1, R11_scratch1, in_bytes(ConstMethod::codes_offset()));
  add(R11_scratch1, R12_scratch2, R12_scratch2);
  cmpd(CCR0, R11_scratch1, R14_bcp);
  beq(CCR0, verify_continue);

  call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::verify_mdp ), R19_method, R14_bcp, R28_mdx);

  bind(verify_continue);
#endif
}

// Store a value at some constant offset from the method data pointer.
void InterpreterMacroAssembler::set_mdp_data_at(int constant, Register value) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  std(value, constant, R28_mdx);
}

// Increment the value at some constant offset from the method data pointer.
void InterpreterMacroAssembler::increment_mdp_data_at(int constant,
                                                      Register counter_addr,
                                                      Register Rbumped_count,
                                                      bool decrement) {
  // Locate the counter at a fixed offset from the mdp:
  addi(counter_addr, R28_mdx, constant);
  increment_mdp_data_at(counter_addr, Rbumped_count, decrement);
}

// Increment the value at some non-fixed (reg + constant) offset from
// the method data pointer.
void InterpreterMacroAssembler::increment_mdp_data_at(Register reg,
                                                      int constant,
                                                      Register scratch,
                                                      Register Rbumped_count,
                                                      bool decrement) {
  // Add the constant to reg to get the offset.
  add(scratch, R28_mdx, reg);
  // Then calculate the counter address.
  addi(scratch, scratch, constant);
  increment_mdp_data_at(scratch, Rbumped_count, decrement);
}

void InterpreterMacroAssembler::increment_mdp_data_at(Register counter_addr,
                                                      Register Rbumped_count,
                                                      bool decrement) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  // Load the counter.
  ld(Rbumped_count, 0, counter_addr);

  if (decrement) {
    // Decrement the register. Set condition codes.
    addi(Rbumped_count, Rbumped_count, - DataLayout::counter_increment);
    // Store the decremented counter, if it is still negative.
    std(Rbumped_count, 0, counter_addr);
    // Note: add/sub overflow check are not ported, since 64 bit
    // calculation should never overflow.
  } else {
    // Increment the register. Set carry flag.
    addi(Rbumped_count, Rbumped_count, DataLayout::counter_increment);
    // Store the incremented counter.
    std(Rbumped_count, 0, counter_addr);
  }
}

// Set a flag value at the current method data pointer position.
void InterpreterMacroAssembler::set_mdp_flag_at(int flag_constant,
                                                Register scratch) {
  assert(ProfileInterpreter, "must be profiling interpreter");
  // Load the data header.
  lbz(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx);
  // Set the flag.
  ori(scratch, scratch, flag_constant);
  // Store the modified header.
  stb(scratch, in_bytes(DataLayout::flags_offset()), R28_mdx);
}

// Test the location at some offset from the method data pointer.
// If it is not equal to value, branch to the not_equal_continue Label.
void InterpreterMacroAssembler::test_mdp_data_at(int offset,
                                                 Register value,
                                                 Label& not_equal_continue,
                                                 Register test_out) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  ld(test_out, offset, R28_mdx);
  cmpd(CCR0,  value, test_out);
  bne(CCR0, not_equal_continue);
}

// Update the method data pointer by the displacement located at some fixed
// offset from the method data pointer.
void InterpreterMacroAssembler::update_mdp_by_offset(int offset_of_disp,
                                                     Register scratch) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  ld(scratch, offset_of_disp, R28_mdx);
  add(R28_mdx, scratch, R28_mdx);
}

// Update the method data pointer by the displacement located at the
// offset (reg + offset_of_disp).
void InterpreterMacroAssembler::update_mdp_by_offset(Register reg,
                                                     int offset_of_disp,
                                                     Register scratch) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  add(scratch, reg, R28_mdx);
  ld(scratch, offset_of_disp, scratch);
  add(R28_mdx, scratch, R28_mdx);
}

// Update the method data pointer by a simple constant displacement.
void InterpreterMacroAssembler::update_mdp_by_constant(int constant) {
  assert(ProfileInterpreter, "must be profiling interpreter");
  addi(R28_mdx, R28_mdx, constant);
}

// Update the method data pointer for a _ret bytecode whose target
// was not among our cached targets.
void InterpreterMacroAssembler::update_mdp_for_ret(TosState state,
                                                   Register return_bci) {
  assert(ProfileInterpreter, "must be profiling interpreter");

  push(state);
  assert(return_bci->is_nonvolatile(), "need to protect return_bci");
  call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::update_mdp_for_ret), return_bci);
  pop(state);
}

// Increments the backedge counter.
// Returns backedge counter + invocation counter in Rdst.
void InterpreterMacroAssembler::increment_backedge_counter(const Register Rcountersconst Register Rdst,
                                                           const Register Rtmp1, Register Rscratch) {
  assert(UseCompiler, "incrementing must be useful");
  assert_different_registers(Rdst, Rtmp1);
  const Register invocation_counter = Rtmp1;
  const Register counter = Rdst;
  // TODO: PPC port: assert(4 == InvocationCounter::sz_counter(), "unexpected field size.");

  // Load backedge counter.
  lwz(counter, in_bytes(MethodCounters::backedge_counter_offset()) +
               in_bytes(InvocationCounter::counter_offset()), Rcounters);
  // Load invocation counter.
  lwz(invocation_counter, in_bytes(MethodCounters::invocation_counter_offset()) +
                          in_bytes(InvocationCounter::counter_offset()), Rcounters);

  // Add the delta to the backedge counter.
  addi(counter, counter, InvocationCounter::count_increment);

  // Mask the invocation counter.
  andi(invocation_counter, invocation_counter, InvocationCounter::count_mask_value);

  // Store new counter value.
  stw(counter, in_bytes(MethodCounters::backedge_counter_offset()) +
               in_bytes(InvocationCounter::counter_offset()), Rcounters);
  // Return invocation counter + backedge counter.
  add(counter, counter, invocation_counter);
}

// Count a taken branch in the bytecodes.
void InterpreterMacroAssembler::profile_taken_branch(Register scratch, Register bumped_count) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // We are taking a branch. Increment the taken count.
    increment_mdp_data_at(in_bytes(JumpData::taken_offset()), scratch, bumped_count);

    // The method data pointer needs to be updated to reflect the new target.
    update_mdp_by_offset(in_bytes(JumpData::displacement_offset()), scratch);
    bind (profile_continue);
  }
}

// Count a not-taken branch in the bytecodes.
void InterpreterMacroAssembler::profile_not_taken_branch(Register scratch1, Register scratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // We are taking a branch. Increment the not taken count.
    increment_mdp_data_at(in_bytes(BranchData::not_taken_offset()), scratch1, scratch2);

    // The method data pointer needs to be updated to correspond to the
    // next bytecode.
    update_mdp_by_constant(in_bytes(BranchData::branch_data_size()));
    bind (profile_continue);
  }
}

// Count a non-virtual call in the bytecodes.
void InterpreterMacroAssembler::profile_call(Register scratch1, Register scratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // We are making a call. Increment the count.
    increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);

    // The method data pointer needs to be updated to reflect the new target.
    update_mdp_by_constant(in_bytes(CounterData::counter_data_size()));
    bind (profile_continue);
  }
}

// Count a final call in the bytecodes.
void InterpreterMacroAssembler::profile_final_call(Register scratch1, Register scratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // We are making a call. Increment the count.
    increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);

    // The method data pointer needs to be updated to reflect the new target.
    update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
    bind (profile_continue);
  }
}

// Count a virtual call in the bytecodes.
void InterpreterMacroAssembler::profile_virtual_call(Register Rreceiver,
                                                     Register Rscratch1,
                                                     Register Rscratch2,
                                                     bool receiver_can_be_null) {
  if (!ProfileInterpreter) { return; }
  Label profile_continue;

  // If no method data exists, go to profile_continue.
  test_method_data_pointer(profile_continue);

  Label skip_receiver_profile;
  if (receiver_can_be_null) {
    Label not_null;
    cmpdi(CCR0, Rreceiver, 0);
    bne(CCR0, not_null);
    // We are making a call. Increment the count for null receiver.
    increment_mdp_data_at(in_bytes(CounterData::count_offset()), Rscratch1, Rscratch2);
    b(skip_receiver_profile);
    bind(not_null);
  }

  // Record the receiver type.
  record_klass_in_profile(Rreceiver, Rscratch1, Rscratch2, true);
  bind(skip_receiver_profile);

  // The method data pointer needs to be updated to reflect the new target.
  update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
  bind (profile_continue);
}

void InterpreterMacroAssembler::profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    int mdp_delta = in_bytes(BitData::bit_data_size());
    if (TypeProfileCasts) {
      mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());

      // Record the object type.
      record_klass_in_profile(Rklass, Rscratch1, Rscratch2, false);
    }

    // The method data pointer needs to be updated.
    update_mdp_by_constant(mdp_delta);

    bind (profile_continue);
  }
}

void InterpreterMacroAssembler::profile_typecheck_failed(Register Rscratch1, Register Rscratch2) {
  if (ProfileInterpreter && TypeProfileCasts) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    int count_offset = in_bytes(CounterData::count_offset());
    // Back up the address, since we have already bumped the mdp.
    count_offset -= in_bytes(VirtualCallData::virtual_call_data_size());

    // *Decrement* the counter. We expect to see zero or small negatives.
    increment_mdp_data_at(count_offset, Rscratch1, Rscratch2, true);

    bind (profile_continue);
  }
}

// Count a ret in the bytecodes.
void InterpreterMacroAssembler::profile_ret(TosState state, Register return_bci,
                                            Register scratch1, Register scratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;
    uint row;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // Update the total ret count.
    increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2 );

    for (row = 0; row < RetData::row_limit(); row++) {
      Label next_test;

      // See if return_bci is equal to bci[n]:
      test_mdp_data_at(in_bytes(RetData::bci_offset(row)), return_bci, next_test, scratch1);

      // return_bci is equal to bci[n]. Increment the count.
      increment_mdp_data_at(in_bytes(RetData::bci_count_offset(row)), scratch1, scratch2);

      // The method data pointer needs to be updated to reflect the new target.
      update_mdp_by_offset(in_bytes(RetData::bci_displacement_offset(row)), scratch1);
      b(profile_continue);
      bind(next_test);
    }

    update_mdp_for_ret(state, return_bci);

    bind (profile_continue);
  }
}

// Count the default case of a switch construct.
void InterpreterMacroAssembler::profile_switch_default(Register scratch1,  Register scratch2) {
  if (ProfileInterpreter) {
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // Update the default case count
    increment_mdp_data_at(in_bytes(MultiBranchData::default_count_offset()),
                          scratch1, scratch2);

    // The method data pointer needs to be updated.
    update_mdp_by_offset(in_bytes(MultiBranchData::default_displacement_offset()),
                         scratch1);

    bind (profile_continue);
  }
}

// Count the index'th case of a switch construct.
void InterpreterMacroAssembler::profile_switch_case(Register index,
                                                    Register scratch1,
                                                    Register scratch2,
                                                    Register scratch3) {
  if (ProfileInterpreter) {
    assert_different_registers(index, scratch1, scratch2, scratch3);
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    // Build the base (index * per_case_size_in_bytes()) + case_array_offset_in_bytes().
    li(scratch3, in_bytes(MultiBranchData::case_array_offset()));

    assert (in_bytes(MultiBranchData::per_case_size()) == 16, "so that shladd works");
    sldi(scratch1, index, exact_log2(in_bytes(MultiBranchData::per_case_size())));
    add(scratch1, scratch1, scratch3);

    // Update the case count.
    increment_mdp_data_at(scratch1, in_bytes(MultiBranchData::relative_count_offset()), scratch2, scratch3);

    // The method data pointer needs to be updated.
    update_mdp_by_offset(scratch1, in_bytes(MultiBranchData::relative_displacement_offset()), scratch2);

    bind (profile_continue);
  }
}

void InterpreterMacroAssembler::profile_null_seen(Register Rscratch1, Register Rscratch2) {
  if (ProfileInterpreter) {
    assert_different_registers(Rscratch1, Rscratch2);
    Label profile_continue;

    // If no method data exists, go to profile_continue.
    test_method_data_pointer(profile_continue);

    set_mdp_flag_at(BitData::null_seen_byte_constant(), Rscratch1);

    // The method data pointer needs to be updated.
    int mdp_delta = in_bytes(BitData::bit_data_size());
    if (TypeProfileCasts) {
      mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
    }
    update_mdp_by_constant(mdp_delta);

    bind (profile_continue);
  }
}

void InterpreterMacroAssembler::record_klass_in_profile(Register Rreceiver,
                                                        Register Rscratch1, Register Rscratch2,
                                                        bool is_virtual_call) {
  assert(ProfileInterpreter, "must be profiling");
  assert_different_registers(Rreceiver, Rscratch1, Rscratch2);

  Label done;
  record_klass_in_profile_helper(Rreceiver, Rscratch1, Rscratch2, 0, done, is_virtual_call);
  bind (done);
}

void InterpreterMacroAssembler::record_klass_in_profile_helper(
                                        Register receiver, Register scratch1, Register scratch2,
                                        int start_row, Label& done, bool is_virtual_call) {
  if (TypeProfileWidth == 0) {
    if (is_virtual_call) {
      increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
    }
    return;
  }

  int last_row = VirtualCallData::row_limit() - 1;
  assert(start_row <= last_row, "must be work left to do");
  // Test this row for both the receiver and for null.
  // Take any of three different outcomes:
  //   1. found receiver => increment count and goto done
  //   2. found null => keep looking for case 1, maybe allocate this cell
  //   3. found something else => keep looking for cases 1 and 2
  // Case 3 is handled by a recursive call.
  for (int row = start_row; row <= last_row; row++) {
    Label next_test;
    bool test_for_null_also = (row == start_row);

    // See if the receiver is receiver[n].
    int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row));
    test_mdp_data_at(recvr_offset, receiver, next_test, scratch1);
    // delayed()->tst(scratch);

    // The receiver is receiver[n]. Increment count[n].
    int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row));
    increment_mdp_data_at(count_offset, scratch1, scratch2);
    b(done);
    bind(next_test);

    if (test_for_null_also) {
      Label found_null;
      // Failed the equality check on receiver[n]... Test for null.
      if (start_row == last_row) {
        // The only thing left to do is handle the null case.
        if (is_virtual_call) {
          // Scratch1 contains test_out from test_mdp_data_at.
          cmpdi(CCR0, scratch1, 0);
          beq(CCR0, found_null);
          // Receiver did not match any saved receiver and there is no empty row for it.
          // Increment total counter to indicate polymorphic case.
          increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch1, scratch2);
          b(done);
          bind(found_null);
        } else {
          cmpdi(CCR0, scratch1, 0);
          bne(CCR0, done);
        }
        break;
      }
      // Since null is rare, make it be the branch-taken case.
      cmpdi(CCR0, scratch1, 0);
      beq(CCR0, found_null);

      // Put all the "Case 3" tests here.
      record_klass_in_profile_helper(receiver, scratch1, scratch2, start_row + 1, done, is_virtual_call);

      // Found a null. Keep searching for a matching receiver,
      // but remember that this is an empty (unused) slot.
      bind(found_null);
    }
  }

  // In the fall-through case, we found no matching receiver, but we
  // observed the receiver[start_row] is NULL.

  // Fill in the receiver field and increment the count.
  int recvr_offset = in_bytes(VirtualCallData::receiver_offset(start_row));
  set_mdp_data_at(recvr_offset, receiver);
  int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
  li(scratch1, DataLayout::counter_increment);
  set_mdp_data_at(count_offset, scratch1);
  if (start_row > 0) {
    b(done);
  }
}

// Argument and return type profilig.
// kills: tmp, tmp2, R0, CR0, CR1
void InterpreterMacroAssembler::profile_obj_type(Register obj, Register mdo_addr_base,
                                                 RegisterOrConstant mdo_addr_offs,
                                                 Register tmp, Register tmp2) {
  Label do_nothing, do_update;

  // tmp2 = obj is allowed
  assert_different_registers(obj, mdo_addr_base, tmp, R0);
  assert_different_registers(tmp2, mdo_addr_base, tmp, R0);
  const Register klass = tmp2;

  verify_oop(obj);

  ld(tmp, mdo_addr_offs, mdo_addr_base);

  // Set null_seen if obj is 0.
  cmpdi(CCR0, obj, 0);
  ori(R0, tmp, TypeEntries::null_seen);
  beq(CCR0, do_update);

  load_klass(klass, obj);

  clrrdi(R0, tmp, exact_log2(-TypeEntries::type_klass_mask));
  // Basically same as andi(R0, tmp, TypeEntries::type_klass_mask);
  cmpd(CCR1, R0, klass);
  // Klass seen before, nothing to do (regardless of unknown bit).
  //beq(CCR1, do_nothing);

  andi_(R0, klass, TypeEntries::type_unknown);
  // Already unknown. Nothing to do anymore.
  //bne(CCR0, do_nothing);
  crorc(CCR0, Assembler::equal, CCR1, Assembler::equal); // cr0 eq = cr1 eq or cr0 ne
  beq(CCR0, do_nothing);

  clrrdi_(R0, tmp, exact_log2(-TypeEntries::type_mask));
  orr(R0, klass, tmp); // Combine klass and null_seen bit (only used if (tmp & type_mask)==0).
  beq(CCR0, do_update); // First time here. Set profile type.

  // Different than before. Cannot keep accurate profile.
  ori(R0, tmp, TypeEntries::type_unknown);

  bind(do_update);
  // update profile
  std(R0, mdo_addr_offs, mdo_addr_base);

  align(32, 12);
  bind(do_nothing);
}

void InterpreterMacroAssembler::profile_arguments_type(Register callee,
                                                       Register tmp1, Register tmp2,
                                                       bool is_virtual) {
  if (!ProfileInterpreter) {
    return;
  }

  assert_different_registers(callee, tmp1, tmp2, R28_mdx);

  if (MethodData::profile_arguments() || MethodData::profile_return()) {
    Label profile_continue;

    test_method_data_pointer(profile_continue);

    int off_to_start = is_virtual ?
      in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size());

    lbz(tmp1, in_bytes(DataLayout::tag_offset()) - off_to_start, R28_mdx);
    cmpwi(CCR0, tmp1, is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag);
    bne(CCR0, profile_continue);

--> --------------------

--> maximum size reached

--> --------------------

100%


¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge