view src/cpu/ppc/vm/interp_masm_ppc_64.cpp @ 14726:92aa6797d639

Backed out merge changeset: b51e29501f30 Backed out merge revision to its first parent (8f483e200405)
author Doug Simon <doug.simon@oracle.com>
date Mon, 24 Mar 2014 21:30:43 +0100
parents 58cf34613a72
children
line wrap: on
line source

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2012, 2013 SAP AG. 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/assembler.hpp"
#include "asm/macroAssembler.inline.hpp"
#include "interp_masm_ppc_64.hpp"
#include "interpreter/interpreterRuntime.hpp"

#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) {
#ifdef CC_INTERP
  address exception_entry = StubRoutines::throw_NullPointerException_at_call_entry();
#else
  address exception_entry = Interpreter::throw_NullPointerException_entry();
#endif
  MacroAssembler::null_check_throw(a, offset, temp_reg, exception_entry);
}

// 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, /*check_for_exceptions=*/true CC_INTERP_ONLY(&& false));
  } else {
    // template code:
    //
    // markOop displaced_header = obj->mark().set_unlocked();
    // monitor->lock()->set_displaced_header(displaced_header);
    // if (Atomic::cmpxchg_ptr(/*ex=*/monitor, /*addr*/obj->mark_addr(), /*cmp*/displaced_header) == 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 done;
    Label cas_failed, slow_case;

    assert_different_registers(displaced_header, object_mark_addr, current_header, tmp);


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

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

    if (UseBiasedLocking) {
      biased_locking_enter(CCR0, object, displaced_header, tmp, current_header, done, &slow_case);
    }

    // Set displaced_header to be (markOop of object | UNLOCK_VALUE).
    ori(displaced_header, displaced_header, markOopDesc::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_ptr(/*ex=*/monitor, /*addr*/obj->mark_addr(), /*cmp*/displaced_header) == 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).
    fence(); // TODO: replace by MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq ?
    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);

    // If the compare-and-exchange succeeded, then we found an unlocked
    // object and we have now locked it.
    b(done);
    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 markOop 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,
                         (address) (~(os::vm_page_size()-1) |
                                    markOopDesc::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);
    release();
    std(R0/*==0!*/, BasicObjectLock::lock_offset_in_bytes() +
        BasicLock::displaced_header_offset_in_bytes(), monitor);
    b(done);


    // } 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, /*check_for_exceptions=*/true CC_INTERP_ONLY(&& false));
    // }

    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, bool check_for_exceptions) {
  if (UseHeavyMonitors) {
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit),
            monitor, /*check_for_exceptions=*/false);
  } 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_ptr(displaced_header, obj->mark_addr(), monitor) == monitor) {
    //   // We swapped the unlocked mark in displaced_header into the object's mark word.
    //   monitor->set_obj(NULL);
    // } else {
    //   // Slow path.
    //   InterpreterRuntime::monitorexit(THREAD, 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);

    if (UseBiasedLocking) {
      // The object address from the monitor is in object.
      ld(object, BasicObjectLock::obj_offset_in_bytes(), monitor);
      assert(oopDesc::mark_offset_in_bytes() == 0, "offset of _mark is not 0");
      biased_locking_exit(CCR0, object, displaced_header, free_slot);
    }

    // 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_ptr(displaced_header, obj->mark_addr(), monitor) == 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.
    if (!UseBiasedLocking) 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(THREAD, 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(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorexit),
            monitor, check_for_exceptions CC_INTERP_ONLY(&& false));
    // }

    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);
    bind(done);
  }
}

void InterpreterMacroAssembler::get_method_counters(Register method,
                                                    Register Rcounters,
                                                    Label& skip) {
  BLOCK_COMMENT("Load and ev. allocate counter object {");
  Label has_counters;
  ld(Rcounters, in_bytes(Method::method_counters_offset()), method);
  cmpdi(CCR0, Rcounters, 0);
  bne(CCR0, has_counters);
  call_VM(noreg, CAST_FROM_FN_PTR(address,
                                  InterpreterRuntime::build_method_counters), method, false);
  ld(Rcounters, in_bytes(Method::method_counters_offset()), method);
  cmpdi(CCR0, Rcounters, 0);
  beq(CCR0, skip); // No MethodCounters, OutOfMemory.
  BLOCK_COMMENT("} Load and ev. allocate counter object");

  bind(has_counters);
}

void InterpreterMacroAssembler::increment_invocation_counter(Register Rcounters, Register iv_be_count, Register Rtmp_r0) {
  assert(UseCompiler, "incrementing must be useful");
  Register invocation_count = iv_be_count;
  Register backedge_count   = Rtmp_r0;
  int delta = InvocationCounter::count_increment;

  // Load each counter in a register.
  //  ld(inv_counter, Rtmp);
  //  ld(be_counter, Rtmp2);
  int inv_counter_offset = in_bytes(MethodCounters::invocation_counter_offset() +
                                    InvocationCounter::counter_offset());
  int be_counter_offset  = in_bytes(MethodCounters::backedge_counter_offset() +
                                    InvocationCounter::counter_offset());

  BLOCK_COMMENT("Increment profiling counters {");

  // Load the backedge counter.
  lwz(backedge_count, be_counter_offset, Rcounters); // is unsigned int
  // Mask the backedge counter.
  Register tmp = invocation_count;
  li(tmp, InvocationCounter::count_mask_value);
  andr(backedge_count, tmp, backedge_count); // Cannot use andi, need sign extension of count_mask_value.

  // Load the invocation counter.
  lwz(invocation_count, inv_counter_offset, Rcounters); // is unsigned int
  // Add the delta to the invocation counter and store the result.
  addi(invocation_count, invocation_count, delta);
  // Store value.
  stw(invocation_count, inv_counter_offset, Rcounters);

  // Add invocation counter + backedge counter.
  add(iv_be_count, backedge_count, invocation_count);

  // Note that this macro must leave the backedge_count + invocation_count in
  // register iv_be_count!
  BLOCK_COMMENT("} Increment profiling counters");
}

void InterpreterMacroAssembler::verify_oop(Register reg, TosState state) {
  if (state == atos) { MacroAssembler::verify_oop(reg); }
}

// Inline assembly for:
//
// if (thread is in interp_only_mode) {
//   InterpreterRuntime::post_method_entry();
// }
// if (*jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_ENTRY ) ||
//     *jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_ENTRY2)   ) {
//   SharedRuntime::jvmpi_method_entry(method, receiver);
// }
void InterpreterMacroAssembler::notify_method_entry() {
  // JVMTI
  // Whenever JVMTI puts a thread in interp_only_mode, method
  // entry/exit events are sent for that thread to track stack
  // depth. If it is possible to enter interp_only_mode we add
  // the code to check if the event should be sent.
  if (JvmtiExport::can_post_interpreter_events()) {
    Label jvmti_post_done;

    lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread);
    cmpwi(CCR0, R0, 0);
    beq(CCR0, jvmti_post_done);
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry),
            /*check_exceptions=*/false);

    bind(jvmti_post_done);
  }
}


// Inline assembly for:
//
// if (thread is in interp_only_mode) {
//   // save result
//   InterpreterRuntime::post_method_exit();
//   // restore result
// }
// if (*jvmpi::event_flags_array_at_addr(JVMPI_EVENT_METHOD_EXIT)) {
//   // save result
//   SharedRuntime::jvmpi_method_exit();
//   // restore result
// }
//
// Native methods have their result stored in d_tmp and l_tmp.
// Java methods have their result stored in the expression stack.
void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, TosState state) {
  // JVMTI
  // Whenever JVMTI puts a thread in interp_only_mode, method
  // entry/exit events are sent for that thread to track stack
  // depth. If it is possible to enter interp_only_mode we add
  // the code to check if the event should be sent.
  if (JvmtiExport::can_post_interpreter_events()) {
    Label jvmti_post_done;

    lwz(R0, in_bytes(JavaThread::interp_only_mode_offset()), R16_thread);
    cmpwi(CCR0, R0, 0);
    beq(CCR0, jvmti_post_done);
    call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit),
            /*check_exceptions=*/false);

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

// Convert the current TOP_IJAVA_FRAME into a PARENT_IJAVA_FRAME
// (using parent_frame_resize) and push a new interpreter
// TOP_IJAVA_FRAME (using frame_size).
void InterpreterMacroAssembler::push_interpreter_frame(Register top_frame_size, Register parent_frame_resize,
                                                       Register tmp1, Register tmp2, Register tmp3,
                                                       Register tmp4, Register pc) {
  assert_different_registers(top_frame_size, parent_frame_resize, tmp1, tmp2, tmp3, tmp4);
  ld(tmp1, _top_ijava_frame_abi(frame_manager_lr), R1_SP);
  mr(tmp2/*top_frame_sp*/, R1_SP);
  // Move initial_caller_sp.
  ld(tmp4, _top_ijava_frame_abi(initial_caller_sp), R1_SP);
  neg(parent_frame_resize, parent_frame_resize);
  resize_frame(parent_frame_resize/*-parent_frame_resize*/, tmp3);

  // Set LR in new parent frame.
  std(tmp1, _abi(lr), R1_SP);
  // Set top_frame_sp info for new parent frame.
  std(tmp2, _parent_ijava_frame_abi(top_frame_sp), R1_SP);
  std(tmp4, _parent_ijava_frame_abi(initial_caller_sp), R1_SP);

  // Push new TOP_IJAVA_FRAME.
  push_frame(top_frame_size, tmp2);

  get_PC_trash_LR(tmp3);
  std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP);
  // Used for non-initial callers by unextended_sp().
  std(R1_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP);
}

// Pop the topmost TOP_IJAVA_FRAME and convert the previous
// PARENT_IJAVA_FRAME back into a TOP_IJAVA_FRAME.
void InterpreterMacroAssembler::pop_interpreter_frame(Register tmp1, Register tmp2, Register tmp3, Register tmp4) {
  assert_different_registers(tmp1, tmp2, tmp3, tmp4);

  ld(tmp1/*caller's sp*/, _abi(callers_sp), R1_SP);
  ld(tmp3, _abi(lr), tmp1);

  ld(tmp4, _parent_ijava_frame_abi(initial_caller_sp), tmp1);

  ld(tmp2/*caller's caller's sp*/, _abi(callers_sp), tmp1);
  // Merge top frame.
  std(tmp2, _abi(callers_sp), R1_SP);

  ld(tmp2, _parent_ijava_frame_abi(top_frame_sp), tmp1);

  // Update C stack pointer to caller's top_abi.
  resize_frame_absolute(tmp2/*addr*/, tmp1/*tmp*/, tmp2/*tmp*/);

  // Update LR in top_frame.
  std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP);

  std(tmp4, _top_ijava_frame_abi(initial_caller_sp), R1_SP);

  // Store the top-frame stack-pointer for c2i adapters.
  std(R1_SP, _top_ijava_frame_abi(top_frame_sp), R1_SP);
}

#ifdef CC_INTERP
// Turn state's interpreter frame into the current TOP_IJAVA_FRAME.
void InterpreterMacroAssembler::pop_interpreter_frame_to_state(Register state, Register tmp1, Register tmp2, Register tmp3) {
  assert_different_registers(R14_state, R15_prev_state, tmp1, tmp2, tmp3);

  if (state == R14_state) {
    ld(tmp1/*state's fp*/, state_(_last_Java_fp));
    ld(tmp2/*state's sp*/, state_(_last_Java_sp));
  } else if (state == R15_prev_state) {
    ld(tmp1/*state's fp*/, prev_state_(_last_Java_fp));
    ld(tmp2/*state's sp*/, prev_state_(_last_Java_sp));
  } else {
    ShouldNotReachHere();
  }

  // Merge top frames.
  std(tmp1, _abi(callers_sp), R1_SP);

  // Tmp2 is new SP.
  // Tmp1 is parent's SP.
  resize_frame_absolute(tmp2/*addr*/, tmp1/*tmp*/, tmp2/*tmp*/);

  // Update LR in top_frame.
  // Must be interpreter frame.
  get_PC_trash_LR(tmp3);
  std(tmp3, _top_ijava_frame_abi(frame_manager_lr), R1_SP);
  // Used for non-initial callers by unextended_sp().
  std(R1_SP, _top_ijava_frame_abi(initial_caller_sp), R1_SP);
}
#endif // CC_INTERP

// Set SP to initial caller's sp, but before fix the back chain.
void InterpreterMacroAssembler::resize_frame_to_initial_caller(Register tmp1, Register tmp2) {
  ld(tmp1, _parent_ijava_frame_abi(initial_caller_sp), R1_SP);
  ld(tmp2, _parent_ijava_frame_abi(callers_sp), R1_SP);
  std(tmp2, _parent_ijava_frame_abi(callers_sp), tmp1); // Fix back chain ...
  mr(R1_SP, tmp1); // ... and resize to initial caller.
}

#ifdef CC_INTERP
// Pop the current interpreter state (without popping the correspoding
// frame) and restore R14_state and R15_prev_state accordingly.
// Use prev_state_may_be_0 to indicate whether prev_state may be 0
// in order to generate an extra check before retrieving prev_state_(_prev_link).
void InterpreterMacroAssembler::pop_interpreter_state(bool prev_state_may_be_0)
{
  // Move prev_state to state and restore prev_state from state_(_prev_link).
  Label prev_state_is_0;
  mr(R14_state, R15_prev_state);

  // Don't retrieve /*state==*/prev_state_(_prev_link)
  // if /*state==*/prev_state is 0.
  if (prev_state_may_be_0) {
    cmpdi(CCR0, R15_prev_state, 0);
    beq(CCR0, prev_state_is_0);
  }

  ld(R15_prev_state, /*state==*/prev_state_(_prev_link));
  bind(prev_state_is_0);
}

void InterpreterMacroAssembler::restore_prev_state() {
  // _prev_link is private, but cInterpreter is a friend.
  ld(R15_prev_state, state_(_prev_link));
}
#endif // CC_INTERP