8036976: PPC64: implement the template interpreter
Reviewed-by: kvn, coleenp
Contributed-by: axel.siebenborn@sap.com, martin.doerr@sap.com
--- a/hotspot/make/aix/Makefile Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/make/aix/Makefile Mon Mar 10 12:58:02 2014 +0100
@@ -70,6 +70,10 @@
FORCE_TIERED=1
endif
endif
+# C1 is not ported on ppc64(le), so we cannot build a tiered VM:
+ifneq (,$(filter $(ARCH),ppc64 pp64le))
+ FORCE_TIERED=0
+endif
ifdef LP64
ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","")
--- a/hotspot/make/linux/Makefile Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/make/linux/Makefile Mon Mar 10 12:58:02 2014 +0100
@@ -66,6 +66,10 @@
FORCE_TIERED=1
endif
endif
+# C1 is not ported on ppc64(le), so we cannot build a tiered VM:
+ifneq (,$(filter $(ARCH),ppc64 pp64le))
+ FORCE_TIERED=0
+endif
ifdef LP64
ifeq ("$(filter $(LP64_ARCH),$(BUILDARCH))","")
--- a/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/cppInterpreter_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1353,9 +1353,9 @@
// notify here, we'll drop it on the floor.
__ notify_method_exit(true/*native method*/,
- ilgl /*illegal state (not used for native methods)*/);
-
-
+ ilgl /*illegal state (not used for native methods)*/,
+ InterpreterMacroAssembler::NotifyJVMTI,
+ false /*check_exceptions*/);
//=============================================================================
// Handle exceptions
--- a/hotspot/src/cpu/ppc/vm/frame_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/frame_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -42,10 +42,6 @@
#include "runtime/vframeArray.hpp"
#endif
-#ifndef CC_INTERP
-#error "CC_INTERP must be defined on PPC64"
-#endif
-
#ifdef ASSERT
void RegisterMap::check_location_valid() {
}
@@ -89,7 +85,10 @@
frame frame::sender_for_interpreter_frame(RegisterMap *map) const {
// Pass callers initial_caller_sp as unextended_sp.
- return frame(sender_sp(), sender_pc(), (intptr_t*)((parent_ijava_frame_abi *)callers_abi())->initial_caller_sp);
+ return frame(sender_sp(), sender_pc(),
+ CC_INTERP_ONLY((intptr_t*)((parent_ijava_frame_abi *)callers_abi())->initial_caller_sp)
+ NOT_CC_INTERP((intptr_t*)get_ijava_state()->sender_sp)
+ );
}
frame frame::sender_for_compiled_frame(RegisterMap *map) const {
@@ -183,6 +182,9 @@
interpreterState istate = get_interpreterState();
address lresult = (address)istate + in_bytes(BytecodeInterpreter::native_lresult_offset());
address fresult = (address)istate + in_bytes(BytecodeInterpreter::native_fresult_offset());
+#else
+ address lresult = (address)&(get_ijava_state()->lresult);
+ address fresult = (address)&(get_ijava_state()->fresult);
#endif
switch (method->result_type()) {
@@ -259,7 +261,21 @@
values.describe(frame_no, (intptr_t*)&(istate->_native_fresult), " native_fresult");
values.describe(frame_no, (intptr_t*)&(istate->_native_lresult), " native_lresult");
#else
- Unimplemented();
+#define DESCRIBE_ADDRESS(name) \
+ values.describe(frame_no, (intptr_t*)&(get_ijava_state()->name), #name);
+
+ DESCRIBE_ADDRESS(method);
+ DESCRIBE_ADDRESS(locals);
+ DESCRIBE_ADDRESS(monitors);
+ DESCRIBE_ADDRESS(cpoolCache);
+ DESCRIBE_ADDRESS(bcp);
+ DESCRIBE_ADDRESS(esp);
+ DESCRIBE_ADDRESS(mdx);
+ DESCRIBE_ADDRESS(top_frame_sp);
+ DESCRIBE_ADDRESS(sender_sp);
+ DESCRIBE_ADDRESS(oop_tmp);
+ DESCRIBE_ADDRESS(lresult);
+ DESCRIBE_ADDRESS(fresult);
#endif
}
}
--- a/hotspot/src/cpu/ppc/vm/frame_ppc.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/frame_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -29,10 +29,6 @@
#include "runtime/synchronizer.hpp"
#include "utilities/top.hpp"
-#ifndef CC_INTERP
-#error "CC_INTERP must be defined on PPC64"
-#endif
-
// C frame layout on PPC-64.
//
// In this figure the stack grows upwards, while memory grows
@@ -197,7 +193,85 @@
#define _spill_nonvolatiles_neg(_component) \
(int)(-frame::spill_nonvolatiles_size + offset_of(frame::spill_nonvolatiles, _component))
- // Frame layout for the Java interpreter on PPC64.
+
+
+#ifndef CC_INTERP
+ // Frame layout for the Java template interpreter on PPC64.
+ //
+ // Diffs to the CC_INTERP are marked with 'X'.
+ //
+ // TOP_IJAVA_FRAME:
+ //
+ // 0 [TOP_IJAVA_FRAME_ABI]
+ // alignment (optional)
+ // [operand stack]
+ // [monitors] (optional)
+ // X[IJAVA_STATE]
+ // note: own locals are located in the caller frame.
+ //
+ // PARENT_IJAVA_FRAME:
+ //
+ // 0 [PARENT_IJAVA_FRAME_ABI]
+ // alignment (optional)
+ // [callee's Java result]
+ // [callee's locals w/o arguments]
+ // [outgoing arguments]
+ // [used part of operand stack w/o arguments]
+ // [monitors] (optional)
+ // X[IJAVA_STATE]
+ //
+
+ struct parent_ijava_frame_abi : abi_minframe {
+ };
+
+ enum {
+ parent_ijava_frame_abi_size = sizeof(parent_ijava_frame_abi)
+ };
+
+#define _parent_ijava_frame_abi(_component) \
+ (offset_of(frame::parent_ijava_frame_abi, _component))
+
+ struct top_ijava_frame_abi : abi_reg_args {
+ };
+
+ enum {
+ top_ijava_frame_abi_size = sizeof(top_ijava_frame_abi)
+ };
+
+#define _top_ijava_frame_abi(_component) \
+ (offset_of(frame::top_ijava_frame_abi, _component))
+
+ struct ijava_state {
+#ifdef ASSERT
+ uint64_t ijava_reserved; // Used for assertion.
+ uint64_t ijava_reserved2; // Inserted for alignment.
+#endif
+ uint64_t method;
+ uint64_t locals;
+ uint64_t monitors;
+ uint64_t cpoolCache;
+ uint64_t bcp;
+ uint64_t esp;
+ uint64_t mdx;
+ uint64_t top_frame_sp; // Maybe define parent_frame_abi and move there.
+ uint64_t sender_sp;
+ // Slots only needed for native calls. Maybe better to move elsewhere.
+ uint64_t oop_tmp;
+ uint64_t lresult;
+ uint64_t fresult;
+ // Aligned to frame::alignment_in_bytes (16).
+ };
+
+ enum {
+ ijava_state_size = sizeof(ijava_state)
+ };
+
+#define _ijava_state_neg(_component) \
+ (int) (-frame::ijava_state_size + offset_of(frame::ijava_state, _component))
+
+#else // CC_INTERP:
+
+ // Frame layout for the Java C++ interpreter on PPC64.
//
// This frame layout provides a C-like frame for every Java frame.
//
@@ -300,6 +374,8 @@
#define _top_ijava_frame_abi(_component) \
(offset_of(frame::top_ijava_frame_abi, _component))
+#endif // CC_INTERP
+
// ENTRY_FRAME
struct entry_frame_locals {
@@ -423,6 +499,14 @@
#ifdef CC_INTERP
// Additional interface for interpreter frames:
inline interpreterState get_interpreterState() const;
+#else
+ inline ijava_state* get_ijava_state() const;
+ // Some convenient register frame setters/getters for deoptimization.
+ inline intptr_t* interpreter_frame_esp() const;
+ inline void interpreter_frame_set_cpcache(ConstantPoolCache* cp);
+ inline void interpreter_frame_set_esp(intptr_t* esp);
+ inline void interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp);
+ inline void interpreter_frame_set_sender_sp(intptr_t* sender_sp);
#endif // CC_INTERP
// Size of a monitor in bytes.
--- a/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -26,10 +26,6 @@
#ifndef CPU_PPC_VM_FRAME_PPC_INLINE_HPP
#define CPU_PPC_VM_FRAME_PPC_INLINE_HPP
-#ifndef CC_INTERP
-#error "CC_INTERP must be defined on PPC64"
-#endif
-
// Inline functions for ppc64 frames:
// Find codeblob and set deopt_state.
@@ -199,6 +195,75 @@
interpreterState istate = get_interpreterState();
return &istate->_constants;
}
+
+#else // !CC_INTERP
+
+// Template Interpreter frame value accessors.
+
+inline frame::ijava_state* frame::get_ijava_state() const {
+ return (ijava_state*) ((uintptr_t)fp() - ijava_state_size);
+}
+
+inline intptr_t** frame::interpreter_frame_locals_addr() const {
+ return (intptr_t**) &(get_ijava_state()->locals);
+}
+inline intptr_t* frame::interpreter_frame_bcx_addr() const {
+ return (intptr_t*) &(get_ijava_state()->bcp);
+}
+inline intptr_t* frame::interpreter_frame_mdx_addr() const {
+ return (intptr_t*) &(get_ijava_state()->mdx);
+}
+// Pointer beyond the "oldest/deepest" BasicObjectLock on stack.
+inline BasicObjectLock* frame::interpreter_frame_monitor_end() const {
+ return (BasicObjectLock *) get_ijava_state()->monitors;
+}
+
+inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const {
+ return (BasicObjectLock *) get_ijava_state();
+}
+
+// SAPJVM ASc 2012-11-21. Return register stack slot addr at which currently interpreted method is found
+inline Method** frame::interpreter_frame_method_addr() const {
+ return (Method**) &(get_ijava_state()->method);
+}
+inline ConstantPoolCache** frame::interpreter_frame_cpoolcache_addr() const {
+ return (ConstantPoolCache**) &(get_ijava_state()->cpoolCache);
+}
+inline ConstantPoolCache** frame::interpreter_frame_cache_addr() const {
+ return (ConstantPoolCache**) &(get_ijava_state()->cpoolCache);
+}
+
+inline oop* frame::interpreter_frame_temp_oop_addr() const {
+ return (oop *) &(get_ijava_state()->oop_tmp);
+}
+inline intptr_t* frame::interpreter_frame_esp() const {
+ return (intptr_t*) get_ijava_state()->esp;
+}
+
+// Convenient setters
+inline void frame::interpreter_frame_set_monitor_end(BasicObjectLock* end) { get_ijava_state()->monitors = (intptr_t) end;}
+inline void frame::interpreter_frame_set_cpcache(ConstantPoolCache* cp) { *frame::interpreter_frame_cpoolcache_addr() = cp; }
+inline void frame::interpreter_frame_set_esp(intptr_t* esp) { get_ijava_state()->esp = (intptr_t) esp; }
+inline void frame::interpreter_frame_set_top_frame_sp(intptr_t* top_frame_sp) { get_ijava_state()->top_frame_sp = (intptr_t) top_frame_sp; }
+inline void frame::interpreter_frame_set_sender_sp(intptr_t* sender_sp) { get_ijava_state()->sender_sp = (intptr_t) sender_sp; }
+
+inline intptr_t* frame::interpreter_frame_expression_stack() const {
+ return (intptr_t*)interpreter_frame_monitor_end() - 1;
+}
+
+inline jint frame::interpreter_frame_expression_stack_direction() {
+ return -1;
+}
+
+// top of expression stack
+inline intptr_t* frame::interpreter_frame_tos_address() const {
+ return ((intptr_t*) get_ijava_state()->esp) + Interpreter::stackElementWords;
+}
+
+inline intptr_t* frame::interpreter_frame_tos_at(jint offset) const {
+ return &interpreter_frame_tos_address()[offset];
+}
+
#endif // CC_INTERP
inline int frame::interpreter_frame_monitor_size() {
--- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -29,6 +29,7 @@
#include "asm/macroAssembler.inline.hpp"
#include "interp_masm_ppc_64.hpp"
#include "interpreter/interpreterRuntime.hpp"
+#include "prims/jvmtiThreadState.hpp"
#ifdef PRODUCT
#define BLOCK_COMMENT(str) // nothing
@@ -45,6 +46,691 @@
MacroAssembler::null_check_throw(a, offset, temp_reg, exception_entry);
}
+void InterpreterMacroAssembler::branch_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();
+ }
+}
+
+#ifndef CC_INTERP
+
+void InterpreterMacroAssembler::dispatch_next(TosState state, int bcp_incr) {
+ 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));
+}
+
+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) {
+ mtctr(R24_dispatch_addr);
+ addi(R14_bcp, R14_bcp, bcp_incr);
+ bctr();
+}
+
+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.
+ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*,
+ Interpreter::remove_activation_preserving_args_entry),
+ relocInfo::none);
+
+ // 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 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 verify) {
+ if (verify) {
+ unimplemented("dispatch_Lbyte_code: verify"); // See Sparc Implementation to implement this
+ }
+
+#ifdef FAST_DISPATCH
+ unimplemented("dispatch_Lbyte_code FAST_DISPATCH");
+#else
+ assert_different_registers(bytecode, R11_scratch1);
+
+ // Calc dispatch table address.
+ load_dispatch_table(R11_scratch1, table);
+
+ sldi(R12_scratch2, bytecode, LogBytesPerWord);
+ ldx(R11_scratch1, R11_scratch1, R12_scratch2);
+
+ // Jump off!
+ mtctr(R11_scratch1);
+ bctr();
+#endif
+}
+
+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) {
+ 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::push_l_pop_d(Register l, FloatRegister d) {
+ std(l, 0, R15_esp);
+ lfd(d, 0, R15_esp);
+}
+
+void InterpreterMacroAssembler::push_d_pop_l(FloatRegister d, Register l) {
+ 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 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 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) {
+ // Read Java big endian format.
+ if (is_signed == Signed) {
+ lha(Rdst, bcp_offset, R14_bcp);
+ } else {
+ lhz(Rdst, bcp_offset, R14_bcp);
+ }
+#if 0
+ assert(Rtmp != Rdst, "need separate temp register");
+ Register Rfirst = Rtmp;
+ lbz(Rfirst, bcp_offset, R14_bcp); // first byte
+ lbz(Rdst, bcp_offset+1, R14_bcp); // second byte
+
+ // Rdst = ((Rfirst<<8) & 0xFF00) | (Rdst &~ 0xFF00)
+ rldimi(/*RA=*/Rdst, /*RS=*/Rfirst, /*sh=*/8, /*mb=*/48);
+ if (is_signed == Signed) {
+ extsh(Rdst, Rdst);
+ }
+#endif
+}
+
+void InterpreterMacroAssembler::get_4_byte_integer_at_bcp(int bcp_offset,
+ Register Rdst,
+ signedOrNot is_signed) {
+ // 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);
+ }
+ }
+}
+
+// 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");
+ if (index_size == sizeof(u2)) {
+ get_2_byte_integer_at_bcp(bcp_offset, Rdst, Unsigned);
+ } else if (index_size == sizeof(u4)) {
+ assert(EnableInvokeDynamic, "giant index used only for JSR 292");
+ get_4_byte_integer_at_bcp(bcp_offset, Rdst, Signed);
+ 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 object from cpool->resolved_references(index).
+void InterpreterMacroAssembler::load_resolved_reference_at_index(Register result, Register index) {
+ assert_different_registers(result, index);
+ 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.
+ Register tmp = index; // reuse
+ sldi(tmp, index, LogBytesPerHeapOop);
+ // Load pointer for resolved_references[] objArray.
+ ld(result, ConstantPool::resolved_references_offset_in_bytes(), result);
+ // JNIHandles::resolve(result)
+ ld(result, 0, result);
+#ifdef ASSERT
+ Label index_ok;
+ lwa(R0, arrayOopDesc::length_offset_in_bytes(), result);
+ sldi(R0, R0, LogBytesPerHeapOop);
+ cmpd(CCR0, tmp, R0);
+ blt(CCR0, index_ok);
+ stop("resolved reference index out of bounds", 0x09256);
+ bind(index_ok);
+#endif
+ // Add in the index.
+ add(result, tmp, result);
+ load_heap_oop(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT), result);
+}
+
+// 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);
+}
+
+void InterpreterMacroAssembler::generate_stack_overflow_check_with_compare_and_throw(Register Rmem_frame_size, Register Rscratch1) {
+ Label done;
+ sub(Rmem_frame_size, R1_SP, Rmem_frame_size);
+ ld(Rscratch1, thread_(stack_overflow_limit));
+ cmpld(CCR0/*is_stack_overflow*/, Rmem_frame_size, Rscratch1);
+ bgt(CCR0/*is_stack_overflow*/, done);
+
+ // Load target address of the runtime stub.
+ assert(StubRoutines::throw_StackOverflowError_entry() != NULL, "generated in wrong order");
+ load_const_optimized(Rscratch1, (StubRoutines::throw_StackOverflowError_entry()), R0);
+ mtctr(Rscratch1);
+ // Restore caller_sp.
+#ifdef ASSERT
+ ld(Rscratch1, 0, R1_SP);
+ ld(R0, 0, R21_sender_SP);
+ cmpd(CCR0, R0, Rscratch1);
+ asm_assert_eq("backlink", 0x547);
+#endif // ASSERT
+ mr(R1_SP, R21_sender_SP);
+ bctr();
+
+ align(32, 12);
+ bind(done);
+}
+
+// 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);
+ 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
+#ifdef ASSERT
+ {
+ Label Lok;
+ ld(R0, _ijava_state_neg(ijava_reserved), Rscratch1);
+ cmpdi(CCR0, R0, 0x5afe);
+ beq(CCR0, Lok);
+ stop("frame corrupted (remove activation)", 0x5afe);
+ bind(Lok);
+ }
+#endif
+ if (return_pc!=noreg) {
+ ld(return_pc, _abi(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
+}
+
+// Remove activation.
+//
+// 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) {
+ 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);
+
+ verify_oop(R17_tos, state);
+ verify_thread();
+
+ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2);
+ mtlr(R0);
+}
+
+#endif // !CC_INTERP
+
// Lock object
//
// Registers alive
@@ -81,7 +767,6 @@
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.
@@ -94,7 +779,6 @@
// 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!).
@@ -147,7 +831,6 @@
BasicLock::displaced_header_offset_in_bytes(), monitor);
b(done);
-
// } else {
// // Slow path.
// InterpreterRuntime::monitorenter(THREAD, monitor);
@@ -158,7 +841,7 @@
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
monitor, /*check_for_exceptions=*/true CC_INTERP_ONLY(&& false));
// }
-
+ align(32, 12);
bind(done);
}
}
@@ -173,13 +856,13 @@
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);
+ monitor, check_for_exceptions CC_INTERP_ONLY(&& false));
} else {
// template code:
//
// if ((displaced_header = monitor->displaced_header()) == NULL) {
- // // Recursive unlock. Mark the monitor unlocked by setting the object field to 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.
@@ -221,7 +904,7 @@
// 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);
+ 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
@@ -261,6 +944,959 @@
}
}
+#ifndef CC_INTERP
+
+// 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", 0x951);
+#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
+}
+
+void InterpreterMacroAssembler::test_invocation_counter_for_mdp(Register invocation_count,
+ Register Rscratch,
+ Label &profile_continue) {
+ assert(ProfileInterpreter, "must be profiling interpreter");
+ // Control will flow to "profile_continue" if the counter is less than the
+ // limit or if we call profile_method().
+ Label done;
+
+ // If no method data exists, and the counter is high enough, make one.
+ int ipl_offs = load_const_optimized(Rscratch, &InvocationCounter::InterpreterProfileLimit, R0, true);
+ lwz(Rscratch, ipl_offs, Rscratch);
+
+ cmpdi(CCR0, R28_mdx, 0);
+ // Test to see if we should create a method data oop.
+ cmpd(CCR1, Rscratch /* InterpreterProfileLimit */, invocation_count);
+ bne(CCR0, done);
+ bge(CCR1, profile_continue);
+
+ // Build it now.
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method));
+ set_method_data_pointer_for_bcp();
+ b(profile_continue);
+
+ align(32, 12);
+ bind(done);
+}
+
+void InterpreterMacroAssembler::test_backedge_count_for_osr(Register backedge_count, Register branch_bcp, Register Rtmp) {
+ assert_different_registers(backedge_count, Rtmp, branch_bcp);
+ assert(UseOnStackReplacement,"Must UseOnStackReplacement to test_backedge_count_for_osr");
+
+ Label did_not_overflow;
+ Label overflow_with_error;
+
+ int ibbl_offs = load_const_optimized(Rtmp, &InvocationCounter::InterpreterBackwardBranchLimit, R0, true);
+ lwz(Rtmp, ibbl_offs, Rtmp);
+ cmpw(CCR0, backedge_count, Rtmp);
+
+ blt(CCR0, did_not_overflow);
+
+ // When ProfileInterpreter is on, the backedge_count comes from the
+ // methodDataOop, which value does not get reset on the call to
+ // frequency_counter_overflow(). To avoid excessive calls to the overflow
+ // routine while the method is being compiled, add a second test to make sure
+ // the overflow function is called only once every overflow_frequency.
+ if (ProfileInterpreter) {
+ const int overflow_frequency = 1024;
+ li(Rtmp, overflow_frequency-1);
+ andr(Rtmp, Rtmp, backedge_count);
+ cmpwi(CCR0, Rtmp, 0);
+ bne(CCR0, did_not_overflow);
+ }
+
+ // Overflow in loop, pass branch bytecode.
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), branch_bcp, true);
+
+ // Was an OSR adapter generated?
+ // O0 = osr nmethod
+ cmpdi(CCR0, R3_RET, 0);
+ beq(CCR0, overflow_with_error);
+
+ // Has the nmethod been invalidated already?
+ lwz(Rtmp, nmethod::entry_bci_offset(), R3_RET);
+ cmpwi(CCR0, Rtmp, InvalidOSREntryBci);
+ beq(CCR0, overflow_with_error);
+
+ // Migrate the interpreter frame off of the stack.
+ // We can use all registers because we will not return to interpreter from this point.
+
+ // Save nmethod.
+ const Register osr_nmethod = R31;
+ mr(osr_nmethod, R3_RET);
+ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R11_scratch1);
+ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), R16_thread);
+ reset_last_Java_frame();
+ // OSR buffer is in ARG1
+
+ // Remove the interpreter frame.
+ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2);
+
+ // Jump to the osr code.
+ ld(R11_scratch1, nmethod::osr_entry_point_offset(), osr_nmethod);
+ mtlr(R0);
+ mtctr(R11_scratch1);
+ bctr();
+
+ align(32, 12);
+ bind(overflow_with_error);
+ bind(did_not_overflow);
+}
+
+// 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 Rcounters, const 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.
+ li(Rscratch, InvocationCounter::count_mask_value);
+ andr(invocation_counter, invocation_counter, Rscratch);
+
+ // 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);
+ }
+}
+
+// Add a InterpMonitorElem to stack (see frame_sparc.hpp).
+void InterpreterMacroAssembler::add_monitor_to_stack(bool stack_is_empty, Register Rtemp1, Register Rtemp2) {
+
+ // Very-local scratch registers.
+ const Register esp = Rtemp1;
+ const Register slot = Rtemp2;
+
+ // Extracted monitor_size.
+ int monitor_size = frame::interpreter_frame_monitor_size_in_bytes();
+ assert(Assembler::is_aligned((unsigned int)monitor_size,
+ (unsigned int)frame::alignment_in_bytes),
+ "size of a monitor must respect alignment of SP");
+
+ resize_frame(-monitor_size, /*temp*/esp); // Allocate space for new monitor
+ std(R1_SP, _ijava_state_neg(top_frame_sp), esp); // esp contains fp
+
+ // Shuffle expression stack down. Recall that stack_base points
+ // just above the new expression stack bottom. Old_tos and new_tos
+ // are used to scan thru the old and new expression stacks.
+ if (!stack_is_empty) {
+ Label copy_slot, copy_slot_finished;
+ const Register n_slots = slot;
+
+ addi(esp, R15_esp, Interpreter::stackElementSize); // Point to first element (pre-pushed stack).
+ subf(n_slots, esp, R26_monitor);
+ srdi_(n_slots, n_slots, LogBytesPerWord); // Compute number of slots to copy.
+ assert(LogBytesPerWord == 3, "conflicts assembler instructions");
+ beq(CCR0, copy_slot_finished); // Nothing to copy.
+
+ mtctr(n_slots);
+
+ // loop
+ bind(copy_slot);
+ ld(slot, 0, esp); // Move expression stack down.
+ std(slot, -monitor_size, esp); // distance = monitor_size
+ addi(esp, esp, BytesPerWord);
+ bdnz(copy_slot);
+
+ bind(copy_slot_finished);
+ }
+
+ addi(R15_esp, R15_esp, -monitor_size);
+ addi(R26_monitor, R26_monitor, -monitor_size);
+
+ // Restart interpreter
+}
+
+// ============================================================================
+// Java locals access
+
+// Load a local variable at index in Rindex into register Rdst_value.
+// Also puts address of local into Rdst_address as a service.
+// Kills:
+// - Rdst_value
+// - Rdst_address
+void InterpreterMacroAssembler::load_local_int(Register Rdst_value, Register Rdst_address, Register Rindex) {
+ sldi(Rdst_address, Rindex, Interpreter::logStackElementSize);
+ subf(Rdst_address, Rdst_address, R18_locals);
+ lwz(Rdst_value, 0, Rdst_address);
+}
+
+// Load a local variable at index in Rindex into register Rdst_value.
+// Also puts address of local into Rdst_address as a service.
+// Kills:
+// - Rdst_value
+// - Rdst_address
+void InterpreterMacroAssembler::load_local_long(Register Rdst_value, Register Rdst_address, Register Rindex) {
+ sldi(Rdst_address, Rindex, Interpreter::logStackElementSize);
+ subf(Rdst_address, Rdst_address, R18_locals);
+ ld(Rdst_value, -8, Rdst_address);
+}
+
+// Load a local variable at index in Rindex into register Rdst_value.
+// Also puts address of local into Rdst_address as a service.
+// Input:
+// - Rindex: slot nr of local variable
+// Kills:
+// - Rdst_value
+// - Rdst_address
+void InterpreterMacroAssembler::load_local_ptr(Register Rdst_value, Register Rdst_address, Register Rindex) {
+ sldi(Rdst_address, Rindex, Interpreter::logStackElementSize);
+ subf(Rdst_address, Rdst_address, R18_locals);
+ ld(Rdst_value, 0, Rdst_address);
+}
+
+// Load a local variable at index in Rindex into register Rdst_value.
+// Also puts address of local into Rdst_address as a service.
+// Kills:
+// - Rdst_value
+// - Rdst_address
+void InterpreterMacroAssembler::load_local_float(FloatRegister Rdst_value, Register Rdst_address, Register Rindex) {
+ sldi(Rdst_address, Rindex, Interpreter::logStackElementSize);
+ subf(Rdst_address, Rdst_address, R18_locals);
+ lfs(Rdst_value, 0, Rdst_address);
+}
+
+// Load a local variable at index in Rindex into register Rdst_value.
+// Also puts address of local into Rdst_address as a service.
+// Kills:
+// - Rdst_value
+// - Rdst_address
+void InterpreterMacroAssembler::load_local_double(FloatRegister Rdst_value, Register Rdst_address, Register Rindex) {
+ sldi(Rdst_address, Rindex, Interpreter::logStackElementSize);
+ subf(Rdst_address, Rdst_address, R18_locals);
+ lfd(Rdst_value, -8, Rdst_address);
+}
+
+// Store an int value at local variable slot Rindex.
+// Kills:
+// - Rindex
+void InterpreterMacroAssembler::store_local_int(Register Rvalue, Register Rindex) {
+ sldi(Rindex, Rindex, Interpreter::logStackElementSize);
+ subf(Rindex, Rindex, R18_locals);
+ stw(Rvalue, 0, Rindex);
+}
+
+// Store a long value at local variable slot Rindex.
+// Kills:
+// - Rindex
+void InterpreterMacroAssembler::store_local_long(Register Rvalue, Register Rindex) {
+ sldi(Rindex, Rindex, Interpreter::logStackElementSize);
+ subf(Rindex, Rindex, R18_locals);
+ std(Rvalue, -8, Rindex);
+}
+
+// Store an oop value at local variable slot Rindex.
+// Kills:
+// - Rindex
+void InterpreterMacroAssembler::store_local_ptr(Register Rvalue, Register Rindex) {
+ sldi(Rindex, Rindex, Interpreter::logStackElementSize);
+ subf(Rindex, Rindex, R18_locals);
+ std(Rvalue, 0, Rindex);
+}
+
+// Store an int value at local variable slot Rindex.
+// Kills:
+// - Rindex
+void InterpreterMacroAssembler::store_local_float(FloatRegister Rvalue, Register Rindex) {
+ sldi(Rindex, Rindex, Interpreter::logStackElementSize);
+ subf(Rindex, Rindex, R18_locals);
+ stfs(Rvalue, 0, Rindex);
+}
+
+// Store an int value at local variable slot Rindex.
+// Kills:
+// - Rindex
+void InterpreterMacroAssembler::store_local_double(FloatRegister Rvalue, Register Rindex) {
+ sldi(Rindex, Rindex, Interpreter::logStackElementSize);
+ subf(Rindex, Rindex, R18_locals);
+ stfd(Rvalue, -8, Rindex);
+}
+
+// Read pending exception from thread and jump to interpreter.
+// Throw exception entry if one if pending. Fall through otherwise.
+void InterpreterMacroAssembler::check_and_forward_exception(Register Rscratch1, Register Rscratch2) {
+ assert_different_registers(Rscratch1, Rscratch2, R3);
+ Register Rexception = Rscratch1;
+ Register Rtmp = Rscratch2;
+ Label Ldone;
+ // Get pending exception oop.
+ ld(Rexception, thread_(pending_exception));
+ cmpdi(CCR0, Rexception, 0);
+ beq(CCR0, Ldone);
+ li(Rtmp, 0);
+ mr_if_needed(R3, Rexception);
+ std(Rtmp, thread_(pending_exception)); // Clear exception in thread
+ if (Interpreter::rethrow_exception_entry() != NULL) {
+ // Already got entry address.
+ load_dispatch_table(Rtmp, (address*)Interpreter::rethrow_exception_entry());
+ } else {
+ // Dynamically load entry address.
+ int simm16_rest = load_const_optimized(Rtmp, &Interpreter::_rethrow_exception_entry, R0, true);
+ ld(Rtmp, simm16_rest, Rtmp);
+ }
+ mtctr(Rtmp);
+ save_interpreter_state(Rtmp);
+ bctr();
+
+ align(32, 12);
+ bind(Ldone);
+}
+
+void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, bool check_exceptions) {
+ save_interpreter_state(R11_scratch1);
+
+ MacroAssembler::call_VM(oop_result, entry_point, false);
+
+ restore_interpreter_state(R11_scratch1, /*bcp_and_mdx_only*/ true);
+
+ check_and_handle_popframe(R11_scratch1);
+ check_and_handle_earlyret(R11_scratch1);
+ // Now check exceptions manually.
+ if (check_exceptions) {
+ check_and_forward_exception(R11_scratch1, R12_scratch2);
+ }
+}
+
+void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions) {
+ // ARG1 is reserved for the thread.
+ mr_if_needed(R4_ARG2, arg_1);
+ call_VM(oop_result, entry_point, check_exceptions);
+}
+
+void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) {
+ // ARG1 is reserved for the thread.
+ mr_if_needed(R4_ARG2, arg_1);
+ assert(arg_2 != R4_ARG2, "smashed argument");
+ mr_if_needed(R5_ARG3, arg_2);
+ call_VM(oop_result, entry_point, check_exceptions);
+}
+
+void InterpreterMacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) {
+ // ARG1 is reserved for the thread.
+ mr_if_needed(R4_ARG2, arg_1);
+ assert(arg_2 != R4_ARG2, "smashed argument");
+ mr_if_needed(R5_ARG3, arg_2);
+ assert(arg_3 != R4_ARG2 && arg_3 != R5_ARG3, "smashed argument");
+ mr_if_needed(R6_ARG4, arg_3);
+ call_VM(oop_result, entry_point, check_exceptions);
+}
+
+void InterpreterMacroAssembler::save_interpreter_state(Register scratch) {
+ ld(scratch, 0, R1_SP);
+ std(R15_esp, _ijava_state_neg(esp), scratch);
+ std(R14_bcp, _ijava_state_neg(bcp), scratch);
+ std(R26_monitor, _ijava_state_neg(monitors), scratch);
+ if (ProfileInterpreter) { std(R28_mdx, _ijava_state_neg(mdx), scratch); }
+ // Other entries should be unchanged.
+}
+
+void InterpreterMacroAssembler::restore_interpreter_state(Register scratch, bool bcp_and_mdx_only) {
+ ld(scratch, 0, R1_SP);
+ ld(R14_bcp, _ijava_state_neg(bcp), scratch); // Changed by VM code (exception).
+ if (ProfileInterpreter) { ld(R28_mdx, _ijava_state_neg(mdx), scratch); } // Changed by VM code.
+ if (!bcp_and_mdx_only) {
+ // Following ones are Metadata.
+ ld(R19_method, _ijava_state_neg(method), scratch);
+ ld(R27_constPoolCache, _ijava_state_neg(cpoolCache), scratch);
+ // Following ones are stack addresses and don't require reload.
+ ld(R15_esp, _ijava_state_neg(esp), scratch);
+ ld(R18_locals, _ijava_state_neg(locals), scratch);
+ ld(R26_monitor, _ijava_state_neg(monitors), scratch);
+ }
+#ifdef ASSERT
+ {
+ Label Lok;
+ subf(R0, R1_SP, scratch);
+ cmpdi(CCR0, R0, frame::abi_reg_args_size + frame::ijava_state_size);
+ bge(CCR0, Lok);
+ stop("frame too small (restore istate)", 0x5432);
+ bind(Lok);
+ }
+ {
+ Label Lok;
+ ld(R0, _ijava_state_neg(ijava_reserved), scratch);
+ cmpdi(CCR0, R0, 0x5afe);
+ beq(CCR0, Lok);
+ stop("frame corrupted (restore istate)", 0x5afe);
+ bind(Lok);
+ }
+#endif
+}
+
+#endif // !CC_INTERP
+
void InterpreterMacroAssembler::get_method_counters(Register method,
Register Rcounters,
Label& skip) {
@@ -321,6 +1957,66 @@
if (state == atos) { MacroAssembler::verify_oop(reg); }
}
+#ifndef CC_INTERP
+// Local helper function for the verify_oop_or_return_address macro.
+static bool verify_return_address(Method* m, int bci) {
+#ifndef PRODUCT
+ address pc = (address)(m->constMethod()) + in_bytes(ConstMethod::codes_offset()) + bci;
+ // Assume it is a valid return address if it is inside m and is preceded by a jsr.
+ if (!m->contains(pc)) return false;
+ address jsr_pc;
+ jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr);
+ if (*jsr_pc == Bytecodes::_jsr && jsr_pc >= m->code_base()) return true;
+ jsr_pc = pc - Bytecodes::length_for(Bytecodes::_jsr_w);
+ if (*jsr_pc == Bytecodes::_jsr_w && jsr_pc >= m->code_base()) return true;
+#endif // PRODUCT
+ return false;
+}
+
+void InterpreterMacroAssembler::verify_FPU(int stack_depth, TosState state) {
+ if (VerifyFPU) {
+ unimplemented("verfiyFPU");
+ }
+}
+
+void InterpreterMacroAssembler::verify_oop_or_return_address(Register reg, Register Rtmp) {
+ if (!VerifyOops) return;
+
+ // The VM documentation for the astore[_wide] bytecode allows
+ // the TOS to be not only an oop but also a return address.
+ Label test;
+ Label skip;
+ // See if it is an address (in the current method):
+
+ const int log2_bytecode_size_limit = 16;
+ srdi_(Rtmp, reg, log2_bytecode_size_limit);
+ bne(CCR0, test);
+
+ address fd = CAST_FROM_FN_PTR(address, verify_return_address);
+ unsigned int nbytes_save = 10*8; // 10 volatile gprs
+
+ save_LR_CR(Rtmp);
+ push_frame_reg_args(nbytes_save, Rtmp);
+ save_volatile_gprs(R1_SP, 112); // except R0
+
+ load_const_optimized(Rtmp, fd, R0);
+ mr_if_needed(R4_ARG2, reg);
+ mr(R3_ARG1, R19_method);
+ call_c(Rtmp); // call C
+
+ restore_volatile_gprs(R1_SP, 112); // except R0
+ pop_frame();
+ restore_LR_CR(Rtmp);
+ b(skip);
+
+ // Perform a more elaborate out-of-line call.
+ // Not an address; verify it:
+ bind(test);
+ verify_oop(reg);
+ bind(skip);
+}
+#endif // !CC_INTERP
+
// Inline assembly for:
//
// if (thread is in interp_only_mode) {
@@ -343,13 +2039,12 @@
cmpwi(CCR0, R0, 0);
beq(CCR0, jvmti_post_done);
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_entry),
- /*check_exceptions=*/false);
+ /*check_exceptions=*/true CC_INTERP_ONLY(&& false));
bind(jvmti_post_done);
}
}
-
// Inline assembly for:
//
// if (thread is in interp_only_mode) {
@@ -365,26 +2060,33 @@
//
// 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) {
+void InterpreterMacroAssembler::notify_method_exit(bool is_native_method, TosState state,
+ NotifyMethodExitMode mode, bool check_exceptions) {
// 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()) {
+ if (mode == NotifyJVMTI && 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);
+ CC_INTERP_ONLY(assert(is_native_method && !check_exceptions, "must not push state"));
+ if (!is_native_method) push(state); // Expose tos to GC.
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_method_exit),
- /*check_exceptions=*/false);
+ /*check_exceptions=*/check_exceptions);
+ if (!is_native_method) pop(state);
align(32, 12);
bind(jvmti_post_done);
}
+
+ // Dtrace support not implemented.
}
+#ifdef CC_INTERP
// 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).
@@ -442,7 +2144,6 @@
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);
@@ -471,7 +2172,6 @@
// 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) {
@@ -481,7 +2181,6 @@
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
--- a/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/interp_masm_ppc_64.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -29,7 +29,7 @@
#include "assembler_ppc.inline.hpp"
#include "interpreter/invocationCounter.hpp"
-// This file specializes the assembler with interpreter-specific macros
+// This file specializes the assembler with interpreter-specific macros.
class InterpreterMacroAssembler: public MacroAssembler {
@@ -39,15 +39,176 @@
void null_check_throw(Register a, int offset, Register temp_reg);
- // Handy address generation macros
+ void branch_to_entry(address entry, Register Rscratch);
+
+ // Handy address generation macros.
#define thread_(field_name) in_bytes(JavaThread::field_name ## _offset()), R16_thread
#define method_(field_name) in_bytes(Method::field_name ## _offset()), R19_method
#ifdef CC_INTERP
#define state_(field_name) in_bytes(byte_offset_of(BytecodeInterpreter, field_name)), R14_state
#define prev_state_(field_name) in_bytes(byte_offset_of(BytecodeInterpreter, field_name)), R15_prev_state
+ void pop (TosState state) {}; // Not needed.
+ void push(TosState state) {}; // Not needed.
#endif
+#ifndef CC_INTERP
+ virtual void check_and_handle_popframe(Register java_thread);
+ virtual void check_and_handle_earlyret(Register java_thread);
+
+ // Base routine for all dispatches.
+ void dispatch_base(TosState state, address* table);
+
+ void load_earlyret_value(TosState state, Register Rscratch1);
+
+ static const Address l_tmp;
+ static const Address d_tmp;
+
+ // dispatch routines
+ void dispatch_next(TosState state, int step = 0);
+ void dispatch_via (TosState state, address* table);
+ void load_dispatch_table(Register dst, address* table);
+ void dispatch_Lbyte_code(TosState state, Register bytecode, address* table, bool verify = false);
+
+ // Called by shared interpreter generator.
+ void dispatch_prolog(TosState state, int step = 0);
+ void dispatch_epilog(TosState state, int step = 0);
+
+ // Super call_VM calls - correspond to MacroAssembler::call_VM(_leaf) calls.
+ void super_call_VM_leaf(Register thread_cache, address entry_point, Register arg_1);
+ void super_call_VM(Register thread_cache, Register oop_result, Register last_java_sp,
+ address entry_point, Register arg_1, Register arg_2, bool check_exception = true);
+
+ // Generate a subtype check: branch to ok_is_subtype if sub_klass is
+ // a subtype of super_klass. Blows registers tmp1, tmp2 and tmp3.
+ void gen_subtype_check(Register sub_klass, Register super_klass,
+ Register tmp1, Register tmp2, Register tmp3, Label &ok_is_subtype);
+
+ // Load object from cpool->resolved_references(index).
+ void load_resolved_reference_at_index(Register result, Register index);
+
+ void generate_stack_overflow_check_with_compare_and_throw(Register Rmem_frame_size, Register Rscratch1);
+ void load_receiver(Register Rparam_count, Register Rrecv_dst);
+
+ // helpers for expression stack
+ void pop_i( Register r = R17_tos);
+ void pop_ptr( Register r = R17_tos);
+ void pop_l( Register r = R17_tos);
+ void pop_f(FloatRegister f = F15_ftos);
+ void pop_d(FloatRegister f = F15_ftos );
+
+ void push_i( Register r = R17_tos);
+ void push_ptr( Register r = R17_tos);
+ void push_l( Register r = R17_tos);
+ void push_f(FloatRegister f = F15_ftos );
+ void push_d(FloatRegister f = F15_ftos);
+
+ void push_2ptrs(Register first, Register second);
+
+ void push_l_pop_d(Register l = R17_tos, FloatRegister d = F15_ftos);
+ void push_d_pop_l(FloatRegister d = F15_ftos, Register l = R17_tos);
+
+ void pop (TosState state); // transition vtos -> state
+ void push(TosState state); // transition state -> vtos
+ void empty_expression_stack(); // Resets both Lesp and SP.
+
+ public:
+ // Load values from bytecode stream:
+
+ enum signedOrNot { Signed, Unsigned };
+ enum setCCOrNot { set_CC, dont_set_CC };
+
+ void get_2_byte_integer_at_bcp(int bcp_offset,
+ Register Rdst,
+ signedOrNot is_signed);
+
+ void get_4_byte_integer_at_bcp(int bcp_offset,
+ Register Rdst,
+ signedOrNot is_signed = Unsigned);
+
+ void get_cache_index_at_bcp(Register Rdst, int bcp_offset, size_t index_size);
+
+ void get_cache_and_index_at_bcp(Register cache, int bcp_offset, size_t index_size = sizeof(u2));
+
+
+ // common code
+
+ void field_offset_at(int n, Register tmp, Register dest, Register base);
+ int field_offset_at(Register object, address bcp, int offset);
+ void fast_iaaccess(int n, address bcp);
+ void fast_iagetfield(address bcp);
+ void fast_iaputfield(address bcp, bool do_store_check);
+
+ void index_check(Register array, Register index, int index_shift, Register tmp, Register res);
+ void index_check_without_pop(Register array, Register index, int index_shift, Register tmp, Register res);
+
+ void get_const(Register Rdst);
+ void get_constant_pool(Register Rdst);
+ void get_constant_pool_cache(Register Rdst);
+ void get_cpool_and_tags(Register Rcpool, Register Rtags);
+ void is_a(Label& L);
+
+ // Java Call Helpers
+ void call_from_interpreter(Register Rtarget_method, Register Rret_addr, Register Rscratch1, Register Rscratch2);
+
+ // --------------------------------------------------
+
+ void unlock_if_synchronized_method(TosState state, bool throw_monitor_exception = true,
+ bool install_monitor_exception = true);
+
+ // Removes the current activation (incl. unlocking of monitors).
+ // Additionally this code is used for earlyReturn in which case we
+ // want to skip throwing an exception and installing an exception.
+ void remove_activation(TosState state,
+ bool throw_monitor_exception = true,
+ bool install_monitor_exception = true);
+ void merge_frames(Register Rtop_frame_sp, Register return_pc, Register Rscratch1, Register Rscratch2); // merge top frames
+
+ void add_monitor_to_stack(bool stack_is_empty, Register Rtemp1, Register Rtemp2);
+
+ // Local variable access helpers
+ void load_local_int(Register Rdst_value, Register Rdst_address, Register Rindex);
+ void load_local_long(Register Rdst_value, Register Rdst_address, Register Rindex);
+ void load_local_ptr(Register Rdst_value, Register Rdst_address, Register Rindex);
+ void load_local_float(FloatRegister Rdst_value, Register Rdst_address, Register Rindex);
+ void load_local_double(FloatRegister Rdst_value, Register Rdst_address, Register Rindex);
+ void store_local_int(Register Rvalue, Register Rindex);
+ void store_local_long(Register Rvalue, Register Rindex);
+ void store_local_ptr(Register Rvalue, Register Rindex);
+ void store_local_float(FloatRegister Rvalue, Register Rindex);
+ void store_local_double(FloatRegister Rvalue, Register Rindex);
+
+ // Call VM for std frames
+ // Special call VM versions that check for exceptions and forward exception
+ // via short cut (not via expensive forward exception stub).
+ void check_and_forward_exception(Register Rscratch1, Register Rscratch2);
+ void call_VM(Register oop_result, address entry_point, bool check_exceptions = true);
+ void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true);
+ void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true);
+ void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true);
+ // Should not be used:
+ void call_VM(Register oop_result, Register last_java_sp, address entry_point, bool check_exceptions = true) {ShouldNotReachHere();}
+ void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions = true) {ShouldNotReachHere();}
+ void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true) {ShouldNotReachHere();}
+ void call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions = true) {ShouldNotReachHere();}
+
+ Address first_local_in_stack();
+
+ enum LoadOrStore { load, store };
+ void static_iload_or_store(int which_local, LoadOrStore direction, Register Rtmp);
+ void static_aload_or_store(int which_local, LoadOrStore direction, Register Rtmp);
+ void static_dload_or_store(int which_local, LoadOrStore direction);
+
+ void save_interpreter_state(Register scratch);
+ void restore_interpreter_state(Register scratch, bool bcp_and_mdx_only = false);
+
+ void increment_backedge_counter(const Register Rcounters, Register Rtmp, Register Rtmp2, Register Rscratch);
+ void test_backedge_count_for_osr(Register backedge_count, Register branch_bcp, Register Rtmp);
+
+ void record_static_call_in_profile(Register Rentry, Register Rtmp);
+ void record_receiver_call_in_profile(Register Rklass, Register Rentry, Register Rtmp);
+#endif // !CC_INTERP
+
void get_method_counters(Register method, Register Rcounters, Label& skip);
void increment_invocation_counter(Register iv_be_count, Register Rtmp1, Register Rtmp2_r0);
@@ -55,12 +216,59 @@
void lock_object (Register lock_reg, Register obj_reg);
void unlock_object(Register lock_reg, bool check_for_exceptions = true);
+#ifndef CC_INTERP
+
+ // Interpreter profiling operations
+ void set_method_data_pointer_for_bcp();
+ void test_method_data_pointer(Label& zero_continue);
+ void verify_method_data_pointer();
+ void test_invocation_counter_for_mdp(Register invocation_count, Register Rscratch, Label &profile_continue);
+
+ void set_mdp_data_at(int constant, Register value);
+
+ void increment_mdp_data_at(int constant, Register counter_addr, Register Rbumped_count, bool decrement = false);
+
+ void increment_mdp_data_at(Register counter_addr, Register Rbumped_count, bool decrement = false);
+ void increment_mdp_data_at(Register reg, int constant, Register scratch, Register Rbumped_count, bool decrement = false);
+
+ void set_mdp_flag_at(int flag_constant, Register scratch);
+ void test_mdp_data_at(int offset, Register value, Label& not_equal_continue, Register test_out);
+
+ void update_mdp_by_offset(int offset_of_disp, Register scratch);
+ void update_mdp_by_offset(Register reg, int offset_of_disp,
+ Register scratch);
+ void update_mdp_by_constant(int constant);
+ void update_mdp_for_ret(TosState state, Register return_bci);
+
+ void profile_taken_branch(Register scratch, Register bumped_count);
+ void profile_not_taken_branch(Register scratch1, Register scratch2);
+ void profile_call(Register scratch1, Register scratch2);
+ void profile_final_call(Register scratch1, Register scratch2);
+ void profile_virtual_call(Register Rreceiver, Register Rscratch1, Register Rscratch2, bool receiver_can_be_null);
+ void profile_typecheck(Register Rklass, Register Rscratch1, Register Rscratch2);
+ void profile_typecheck_failed(Register Rscratch1, Register Rscratch2);
+ void profile_ret(TosState state, Register return_bci, Register scratch1, Register scratch2);
+ void profile_switch_default(Register scratch1, Register scratch2);
+ void profile_switch_case(Register index, Register scratch1,Register scratch2, Register scratch3);
+ void profile_null_seen(Register Rscratch1, Register Rscratch2);
+ void record_klass_in_profile(Register receiver, Register scratch1, Register scratch2, bool is_virtual_call);
+ void record_klass_in_profile_helper(Register receiver, Register scratch1, Register scratch2, int start_row, Label& done, bool is_virtual_call);
+
+#endif // !CC_INTERP
+
// Debugging
void verify_oop(Register reg, TosState state = atos); // only if +VerifyOops && state == atos
+#ifndef CC_INTERP
+ void verify_oop_or_return_address(Register reg, Register rtmp); // for astore
+ void verify_FPU(int stack_depth, TosState state = ftos);
+#endif // !CC_INTERP
- // support for jvmdi/jvmpi
+ typedef enum { NotifyJVMTI, SkipNotifyJVMTI } NotifyMethodExitMode;
+
+ // Support for jvmdi/jvmpi.
void notify_method_entry();
- void notify_method_exit(bool is_native_method, TosState state);
+ void notify_method_exit(bool is_native_method, TosState state,
+ NotifyMethodExitMode mode, bool check_exceptions);
#ifdef CC_INTERP
// Convert the current TOP_IJAVA_FRAME into a PARENT_IJAVA_FRAME
--- a/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/interpreter_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -51,10 +51,6 @@
#include "c1/c1_Runtime1.hpp"
#endif
-#ifndef CC_INTERP
-#error "CC_INTERP must be defined on PPC"
-#endif
-
#define __ _masm->
#ifdef PRODUCT
@@ -147,7 +143,8 @@
#ifdef CC_INTERP
__ ld(R19_method, state_(_method));
#else
- __ unimplemented("slow signature handler 1");
+ __ ld(R19_method, 0, target_sp);
+ __ ld(R19_method, _ijava_state_neg(method), R19_method);
#endif
// Get the result handler.
@@ -157,7 +154,8 @@
#ifdef CC_INTERP
__ ld(R19_method, state_(_method));
#else
- __ unimplemented("slow signature handler 2");
+ __ ld(R19_method, 0, target_sp);
+ __ ld(R19_method, _ijava_state_neg(method), R19_method);
#endif
{
@@ -453,7 +451,7 @@
//
// Registers alive
// R16_thread - JavaThread*
- // R19_method - callee's methodOop (method to be invoked)
+ // R19_method - callee's method (method to be invoked)
// R1_SP - SP prepared such that caller's outgoing args are near top
// LR - return address to caller
//
@@ -491,7 +489,12 @@
// Return to frame manager, it will handle the pending exception.
__ blr();
#else
- Unimplemented();
+ // We don't know our caller, so jump to the general forward exception stub,
+ // which will also pop our full frame off. Satisfy the interface of
+ // SharedRuntime::generate_forward_exception()
+ __ load_const_optimized(R11_scratch1, StubRoutines::forward_exception_entry(), R0);
+ __ mtctr(R11_scratch1);
+ __ bctr();
#endif
return entry;
@@ -500,8 +503,9 @@
// Call an accessor method (assuming it is resolved, otherwise drop into
// vanilla (slow path) entry.
address InterpreterGenerator::generate_accessor_entry(void) {
- if(!UseFastAccessorMethods && (!FLAG_IS_ERGO(UseFastAccessorMethods)))
+ if (!UseFastAccessorMethods && (!FLAG_IS_ERGO(UseFastAccessorMethods))) {
return NULL;
+ }
Label Lslow_path, Lacquire;
@@ -586,10 +590,14 @@
// Load from branch table and dispatch (volatile case: one instruction ahead)
__ sldi(Rflags, Rflags, LogBytesPerWord);
__ cmpwi(CCR6, Rscratch, 1); // volatile?
- __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // volatile ? size of 1 instruction : 0
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // volatile ? size of 1 instruction : 0
+ }
__ ldx(Rbtable, Rbtable, Rflags);
- __ subf(Rbtable, Rscratch, Rbtable); // point to volatile/non-volatile entry point
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ subf(Rbtable, Rscratch, Rbtable); // point to volatile/non-volatile entry point
+ }
__ mtctr(Rbtable);
__ bctr();
@@ -605,7 +613,7 @@
}
assert(all_uninitialized != all_initialized, "consistency"); // either or
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
if (branch_table[vtos] == 0) branch_table[vtos] = __ pc(); // non-volatile_entry point
if (branch_table[dtos] == 0) branch_table[dtos] = __ pc(); // non-volatile_entry point
if (branch_table[ftos] == 0) branch_table[ftos] = __ pc(); // non-volatile_entry point
@@ -614,7 +622,7 @@
if (branch_table[itos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[itos] = __ pc(); // non-volatile_entry point
__ lwax(R3_RET, Rclass_or_obj, Roffset);
__ beq(CCR6, Lacquire);
@@ -623,7 +631,7 @@
if (branch_table[ltos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[ltos] = __ pc(); // non-volatile_entry point
__ ldx(R3_RET, Rclass_or_obj, Roffset);
__ beq(CCR6, Lacquire);
@@ -632,7 +640,7 @@
if (branch_table[btos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[btos] = __ pc(); // non-volatile_entry point
__ lbzx(R3_RET, Rclass_or_obj, Roffset);
__ extsb(R3_RET, R3_RET);
@@ -642,7 +650,7 @@
if (branch_table[ctos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[ctos] = __ pc(); // non-volatile_entry point
__ lhzx(R3_RET, Rclass_or_obj, Roffset);
__ beq(CCR6, Lacquire);
@@ -651,7 +659,7 @@
if (branch_table[stos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[stos] = __ pc(); // non-volatile_entry point
__ lhax(R3_RET, Rclass_or_obj, Roffset);
__ beq(CCR6, Lacquire);
@@ -660,7 +668,7 @@
if (branch_table[atos] == 0) { // generate only once
__ align(32, 28, 28); // align load
- __ sync(); // volatile entry point (one instruction before non-volatile_entry point)
+ __ fence(); // volatile entry point (one instruction before non-volatile_entry point)
branch_table[atos] = __ pc(); // non-volatile_entry point
__ load_heap_oop(R3_RET, (RegisterOrConstant)Roffset, Rclass_or_obj);
__ verify_oop(R3_RET);
@@ -683,10 +691,7 @@
#endif
__ bind(Lslow_path);
- assert(Interpreter::entry_for_kind(Interpreter::zerolocals), "Normal entry must have been generated by now");
- __ load_const_optimized(Rscratch, Interpreter::entry_for_kind(Interpreter::zerolocals), R0);
- __ mtctr(Rscratch);
- __ bctr();
+ __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), Rscratch);
__ flush();
return entry;
@@ -773,10 +778,7 @@
// Generate regular method entry.
__ bind(slow_path);
- assert(Interpreter::entry_for_kind(Interpreter::zerolocals), "Normal entry must have been generated by now");
- __ load_const_optimized(R11_scratch1, Interpreter::entry_for_kind(Interpreter::zerolocals), R0);
- __ mtctr(R11_scratch1);
- __ bctr();
+ __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R11_scratch1);
__ flush();
return entry;
--- a/hotspot/src/cpu/ppc/vm/interpreter_ppc.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/interpreter_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -28,15 +28,23 @@
public:
- // Stack index relative to tos (which points at value)
+ // Stack index relative to tos (which points at value).
static int expr_index_at(int i) {
return stackElementWords * i;
}
- // Already negated by c++ interpreter
+ // Already negated by c++ interpreter.
static int local_index_at(int i) {
assert(i <= 0, "local direction already negated");
return stackElementWords * i;
}
+#ifndef CC_INTERP
+ // The offset in bytes to access a expression stack slot
+ // relative to the esp pointer.
+ static int expr_offset_in_bytes(int slot) {
+ return stackElementSize * slot + wordSize;
+ }
+#endif
+
#endif // CPU_PPC_VM_INTERPRETER_PPC_PP
--- a/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -26,10 +26,6 @@
#ifndef CPU_PPC_VM_JAVAFRAMEANCHOR_PPC_HPP
#define CPU_PPC_VM_JAVAFRAMEANCHOR_PPC_HPP
-#ifndef CC_INTERP
-#error "CC_INTERP must be defined on PPC64"
-#endif
-
public:
// Each arch must define reset, save, restore
// These are used by objects that only care about:
--- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -2412,7 +2412,8 @@
#ifdef CC_INTERP
ld(tmp1/*pc*/, _top_ijava_frame_abi(frame_manager_lr), sp);
#else
- Unimplemented();
+ address entry = pc();
+ load_const_optimized(tmp1, entry);
#endif
set_last_Java_frame(/*sp=*/sp, /*pc=*/tmp1);
@@ -2472,6 +2473,16 @@
}
}
+void MacroAssembler::store_klass_gap(Register dst_oop, Register val) {
+ if (UseCompressedClassPointers) {
+ if (val == noreg) {
+ val = R0;
+ li(val, 0);
+ }
+ stw(val, oopDesc::klass_gap_offset_in_bytes(), dst_oop); // klass gap if compressed
+ }
+}
+
int MacroAssembler::instr_size_for_decode_klass_not_null() {
if (!UseCompressedClassPointers) return 0;
int num_instrs = 1; // shift or move
@@ -3143,3 +3154,15 @@
}
#endif // !PRODUCT
+
+SkipIfEqualZero::SkipIfEqualZero(MacroAssembler* masm, Register temp, const bool* flag_addr) : _masm(masm), _label() {
+ int simm16_offset = masm->load_const_optimized(temp, (address)flag_addr, R0, true);
+ assert(sizeof(bool) == 1, "PowerPC ABI");
+ masm->lbz(temp, simm16_offset, temp);
+ masm->cmpwi(CCR0, temp, 0);
+ masm->beq(CCR0, _label);
+}
+
+SkipIfEqualZero::~SkipIfEqualZero() {
+ _masm->bind(_label);
+}
--- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -566,12 +566,14 @@
// Load heap oop and decompress. Loaded oop may not be null.
inline void load_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1 = noreg);
+ inline void store_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1,
+ /*specify if d must stay uncompressed*/ Register tmp = noreg);
// Null allowed.
inline void load_heap_oop(Register d, RegisterOrConstant offs, Register s1 = noreg);
// Encode/decode heap oop. Oop may not be null, else en/decoding goes wrong.
- inline void encode_heap_oop_not_null(Register d);
+ inline Register encode_heap_oop_not_null(Register d, Register src = noreg);
inline void decode_heap_oop_not_null(Register d);
// Null allowed.
@@ -581,6 +583,7 @@
void load_klass(Register dst, Register src);
void load_klass_with_trap_null_check(Register dst, Register src);
void store_klass(Register dst_oop, Register klass, Register tmp = R0);
+ void store_klass_gap(Register dst_oop, Register val = noreg); // Will store 0 if val not specified.
static int instr_size_for_decode_klass_not_null();
void decode_klass_not_null(Register dst, Register src = noreg);
void encode_klass_not_null(Register dst, Register src = noreg);
@@ -693,4 +696,21 @@
void zap_from_to(Register low, int before, Register high, int after, Register val, Register addr) PRODUCT_RETURN;
};
+// class SkipIfEqualZero:
+//
+// Instantiating this class will result in assembly code being output that will
+// jump around any code emitted between the creation of the instance and it's
+// automatic destruction at the end of a scope block, depending on the value of
+// the flag passed to the constructor, which will be checked at run-time.
+class SkipIfEqualZero : public StackObj {
+ private:
+ MacroAssembler* _masm;
+ Label _label;
+
+ public:
+ // 'Temp' is a temp register that this object can use (and trash).
+ explicit SkipIfEqualZero(MacroAssembler*, Register temp, const bool* flag_addr);
+ ~SkipIfEqualZero();
+};
+
#endif // CPU_PPC_VM_MACROASSEMBLER_PPC_HPP
--- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -321,6 +321,15 @@
}
}
+inline void MacroAssembler::store_heap_oop_not_null(Register d, RegisterOrConstant offs, Register s1, Register tmp) {
+ if (UseCompressedOops) {
+ Register compressedOop = encode_heap_oop_not_null((tmp != noreg) ? tmp : d, d);
+ stw(compressedOop, offs, s1);
+ } else {
+ std(d, offs, s1);
+ }
+}
+
inline void MacroAssembler::load_heap_oop(Register d, RegisterOrConstant offs, Register s1) {
if (UseCompressedOops) {
lwz(d, offs, s1);
@@ -330,13 +339,17 @@
}
}
-inline void MacroAssembler::encode_heap_oop_not_null(Register d) {
+inline Register MacroAssembler::encode_heap_oop_not_null(Register d, Register src) {
+ Register current = (src!=noreg) ? src : d; // Compressed oop is in d if no src provided.
if (Universe::narrow_oop_base() != NULL) {
- sub(d, d, R30);
+ sub(d, current, R30);
+ current = d;
}
if (Universe::narrow_oop_shift() != 0) {
- srdi(d, d, LogMinObjAlignmentInBytes);
+ srdi(d, current, LogMinObjAlignmentInBytes);
+ current = d;
}
+ return current; // Encoded oop is in this register.
}
inline void MacroAssembler::decode_heap_oop_not_null(Register d) {
--- a/hotspot/src/cpu/ppc/vm/register_ppc.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/register_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -579,15 +579,27 @@
// Register declarations to be used in frame manager assembly code.
// Use only non-volatile registers in order to keep values across C-calls.
+#ifdef CC_INTERP
REGISTER_DECLARATION(Register, R14_state, R14); // address of new cInterpreter.
REGISTER_DECLARATION(Register, R15_prev_state, R15); // address of old cInterpreter
+#else // CC_INTERP
+REGISTER_DECLARATION(Register, R14_bcp, R14);
+REGISTER_DECLARATION(Register, R15_esp, R15);
+REGISTER_DECLARATION(FloatRegister, F15_ftos, F15);
+#endif // CC_INTERP
REGISTER_DECLARATION(Register, R16_thread, R16); // address of current thread
REGISTER_DECLARATION(Register, R17_tos, R17); // address of Java tos (prepushed).
REGISTER_DECLARATION(Register, R18_locals, R18); // address of first param slot (receiver).
REGISTER_DECLARATION(Register, R19_method, R19); // address of current method
#ifndef DONT_USE_REGISTER_DEFINES
+#ifdef CC_INTERP
#define R14_state AS_REGISTER(Register, R14)
#define R15_prev_state AS_REGISTER(Register, R15)
+#else // CC_INTERP
+#define R14_bcp AS_REGISTER(Register, R14)
+#define R15_esp AS_REGISTER(Register, R15)
+#define F15_ftos AS_REGISTER(FloatRegister, F15)
+#endif // CC_INTERP
#define R16_thread AS_REGISTER(Register, R16)
#define R17_tos AS_REGISTER(Register, R17)
#define R18_locals AS_REGISTER(Register, R18)
@@ -608,6 +620,14 @@
REGISTER_DECLARATION(Register, R27_tmp7, R27);
REGISTER_DECLARATION(Register, R28_tmp8, R28);
REGISTER_DECLARATION(Register, R29_tmp9, R29);
+#ifndef CC_INTERP
+REGISTER_DECLARATION(Register, R24_dispatch_addr, R24);
+REGISTER_DECLARATION(Register, R25_templateTableBase, R25);
+REGISTER_DECLARATION(Register, R26_monitor, R26);
+REGISTER_DECLARATION(Register, R27_constPoolCache, R27);
+REGISTER_DECLARATION(Register, R28_mdx, R28);
+#endif // CC_INTERP
+
#ifndef DONT_USE_REGISTER_DEFINES
#define R21_tmp1 AS_REGISTER(Register, R21)
#define R22_tmp2 AS_REGISTER(Register, R22)
@@ -618,6 +638,16 @@
#define R27_tmp7 AS_REGISTER(Register, R27)
#define R28_tmp8 AS_REGISTER(Register, R28)
#define R29_tmp9 AS_REGISTER(Register, R29)
+#ifndef CC_INTERP
+// Lmonitors : monitor pointer
+// LcpoolCache: constant pool cache
+// mdx: method data index
+#define R24_dispatch_addr AS_REGISTER(Register, R24)
+#define R25_templateTableBase AS_REGISTER(Register, R25)
+#define R26_monitor AS_REGISTER(Register, R26)
+#define R27_constPoolCache AS_REGISTER(Register, R27)
+#define R28_mdx AS_REGISTER(Register, R28)
+#endif
#define CCR4_is_synced AS_REGISTER(ConditionRegister, CCR4)
#endif
--- a/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/sharedRuntime_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -957,6 +957,9 @@
#ifdef CC_INTERP
const Register tos = R17_tos;
+#else
+ const Register tos = R15_esp;
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1);
#endif
// load TOS
@@ -975,7 +978,7 @@
const BasicType *sig_bt,
const VMRegPair *regs) {
- // Load method's entry-point from methodOop.
+ // Load method's entry-point from method.
__ ld(R12_scratch2, in_bytes(Method::from_compiled_offset()), R19_method);
__ mtctr(R12_scratch2);
@@ -996,7 +999,10 @@
#ifdef CC_INTERP
const Register ld_ptr = R17_tos;
+#else
+ const Register ld_ptr = R15_esp;
#endif
+
const Register value_regs[] = { R22_tmp2, R23_tmp3, R24_tmp4, R25_tmp5, R26_tmp6 };
const int num_value_regs = sizeof(value_regs) / sizeof(Register);
int value_regs_index = 0;
@@ -1087,8 +1093,8 @@
}
}
- BLOCK_COMMENT("Store method oop");
- // Store method oop into thread->callee_target.
+ BLOCK_COMMENT("Store method");
+ // Store method into thread->callee_target.
// We might end up in handle_wrong_method if the callee is
// deoptimized as we race thru here. If that happens we don't want
// to take a safepoint because the caller frame will look
@@ -2617,8 +2623,12 @@
#ifdef CC_INTERP
__ std(R1_SP, _parent_ijava_frame_abi(initial_caller_sp), R1_SP);
#else
- Unimplemented();
+#ifdef ASSERT
+ __ load_const_optimized(pc_reg, 0x5afe);
+ __ std(pc_reg, _ijava_state_neg(ijava_reserved), R1_SP);
#endif
+ __ std(R1_SP, _ijava_state_neg(sender_sp), R1_SP);
+#endif // CC_INTERP
__ addi(number_of_frames_reg, number_of_frames_reg, -1);
__ addi(frame_sizes_reg, frame_sizes_reg, wordSize);
__ addi(pcs_reg, pcs_reg, wordSize);
@@ -2690,7 +2700,15 @@
__ std(R12_scratch2, _abi(lr), R1_SP);
// Initialize initial_caller_sp.
+#ifdef CC_INTERP
__ std(frame_size_reg/*old_sp*/, _parent_ijava_frame_abi(initial_caller_sp), R1_SP);
+#else
+#ifdef ASSERT
+ __ load_const_optimized(pc_reg, 0x5afe);
+ __ std(pc_reg, _ijava_state_neg(ijava_reserved), R1_SP);
+#endif
+ __ std(frame_size_reg, _ijava_state_neg(sender_sp), R1_SP);
+#endif // CC_INTERP
#ifdef ASSERT
// Make sure that there is at least one entry in the array.
@@ -2911,10 +2929,16 @@
// optional c2i, caller of deoptee, ...).
// Initialize R14_state.
+#ifdef CC_INTERP
__ ld(R14_state, 0, R1_SP);
__ addi(R14_state, R14_state, -frame::interpreter_frame_cinterpreterstate_size_in_bytes());
// Also inititialize R15_prev_state.
__ restore_prev_state();
+#else
+ __ restore_interpreter_state(R11_scratch1);
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1);
+#endif // CC_INTERP
+
// Return to the interpreter entry point.
__ blr();
@@ -3033,11 +3057,17 @@
// stack: (top interpreter frame, ..., optional interpreter frame,
// optional c2i, caller of deoptee, ...).
+#ifdef CC_INTERP
// Initialize R14_state, ...
__ ld(R11_scratch1, 0, R1_SP);
__ addi(R14_state, R11_scratch1, -frame::interpreter_frame_cinterpreterstate_size_in_bytes());
// also initialize R15_prev_state.
__ restore_prev_state();
+#else
+ __ restore_interpreter_state(R11_scratch1);
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1);
+#endif // CC_INTERP
+
// Return to the interpreter entry point.
__ blr();
@@ -3115,7 +3145,6 @@
frame_size_in_bytes,
/*restore_ctr=*/true);
-
BLOCK_COMMENT(" Jump to forward_exception_entry.");
// Jump to forward_exception_entry, with the issuing PC in LR
// so it looks like the original nmethod called forward_exception_entry.
@@ -3200,7 +3229,7 @@
RegisterSaver::restore_live_registers_and_pop_frame(masm, frame_size_in_bytes, /*restore_ctr*/ false);
- // Get the returned methodOop.
+ // Get the returned method.
__ get_vm_result_2(R19_method);
__ bctr();
--- a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2012, 2013 SAP AG. All rights reserved.
+ * Copyright 2012, 2014 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
@@ -39,15 +39,10 @@
#include "runtime/stubCodeGenerator.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/top.hpp"
-#ifdef TARGET_OS_FAMILY_aix
-# include "thread_aix.inline.hpp"
-#endif
-#ifdef TARGET_OS_FAMILY_linux
-# include "thread_linux.inline.hpp"
-#endif
#ifdef COMPILER2
#include "opto/runtime.hpp"
#endif
+#include "runtime/thread.inline.hpp"
#define __ _masm->
@@ -221,7 +216,7 @@
{
BLOCK_COMMENT("Call frame manager or native entry.");
// Call frame manager or native entry.
- Register r_new_arg_entry = R14_state;
+ Register r_new_arg_entry = R14; // PPC_state;
assert_different_registers(r_new_arg_entry, r_top_of_arguments_addr,
r_arg_method, r_arg_thread);
@@ -234,7 +229,11 @@
// R16_thread - JavaThread*
// Tos must point to last argument - element_size.
+#ifdef CC_INTERP
const Register tos = R17_tos;
+#else
+ const Register tos = R15_esp;
+#endif
__ addi(tos, r_top_of_arguments_addr, -Interpreter::stackElementSize);
// initialize call_stub locals (step 2)
@@ -248,8 +247,11 @@
assert(tos != r_arg_thread && R19_method != r_arg_thread, "trashed r_arg_thread");
// Set R15_prev_state to 0 for simplifying checks in callee.
+#ifdef CC_INTERP
__ li(R15_prev_state, 0);
-
+#else
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1);
+#endif
// Stack on entry to frame manager / native entry:
//
// F0 [TOP_IJAVA_FRAME_ABI]
@@ -2089,7 +2091,7 @@
guarantee(!UseAESIntrinsics, "not yet implemented.");
}
- // PPC uses stubs for safefetch.
+ // Safefetch stubs.
generate_safefetch("SafeFetch32", sizeof(int), &StubRoutines::_safefetch32_entry,
&StubRoutines::_safefetch32_fault_pc,
&StubRoutines::_safefetch32_continuation_pc);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, 2014 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.
+ *
+ */
+
+#ifndef CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP
+#define CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP
+
+ protected:
+ address generate_normal_entry(bool synchronized);
+ address generate_native_entry(bool synchronized);
+ address generate_math_entry(AbstractInterpreter::MethodKind kind);
+ address generate_empty_entry(void);
+
+ void lock_method(Register Rflags, Register Rscratch1, Register Rscratch2, bool flags_preloaded=false);
+ void unlock_method(bool check_exceptions = true);
+
+ void generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue);
+ void generate_counter_overflow(Label& continue_entry);
+
+ void generate_fixed_frame(bool native_call, Register Rsize_of_parameters, Register Rsize_of_locals);
+ void generate_stack_overflow_check(Register Rframe_size, Register Rscratch1);
+
+#endif // CPU_PPC_VM_TEMPLATEINTERPRETERGENERATOR_PPC_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -0,0 +1,1813 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, 2014 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"
+#ifndef CC_INTERP
+#include "asm/macroAssembler.inline.hpp"
+#include "interpreter/bytecodeHistogram.hpp"
+#include "interpreter/interpreter.hpp"
+#include "interpreter/interpreterGenerator.hpp"
+#include "interpreter/interpreterRuntime.hpp"
+#include "interpreter/templateTable.hpp"
+#include "oops/arrayOop.hpp"
+#include "oops/methodData.hpp"
+#include "oops/method.hpp"
+#include "oops/oop.inline.hpp"
+#include "prims/jvmtiExport.hpp"
+#include "prims/jvmtiThreadState.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/deoptimization.hpp"
+#include "runtime/frame.inline.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "runtime/synchronizer.hpp"
+#include "runtime/timer.hpp"
+#include "runtime/vframeArray.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/macros.hpp"
+
+#undef __
+#define __ _masm->
+
+#ifdef PRODUCT
+#define BLOCK_COMMENT(str) /* nothing */
+#else
+#define BLOCK_COMMENT(str) __ block_comment(str)
+#endif
+
+#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
+
+//-----------------------------------------------------------------------------
+
+// Actually we should never reach here since we do stack overflow checks before pushing any frame.
+address TemplateInterpreterGenerator::generate_StackOverflowError_handler() {
+ address entry = __ pc();
+ __ unimplemented("generate_StackOverflowError_handler");
+ return entry;
+}
+
+address TemplateInterpreterGenerator::generate_ArrayIndexOutOfBounds_handler(const char* name) {
+ address entry = __ pc();
+ __ empty_expression_stack();
+ __ load_const_optimized(R4_ARG2, (address) name);
+ // Index is in R17_tos.
+ __ mr(R5_ARG3, R17_tos);
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ArrayIndexOutOfBoundsException));
+ return entry;
+}
+
+#if 0
+// Call special ClassCastException constructor taking object to cast
+// and target class as arguments.
+address TemplateInterpreterGenerator::generate_ClassCastException_verbose_handler(const char* name) {
+ address entry = __ pc();
+
+ // Target class oop is in register R6_ARG4 by convention!
+
+ // Expression stack must be empty before entering the VM if an
+ // exception happened.
+ __ empty_expression_stack();
+ // Setup parameters.
+ // Thread will be loaded to R3_ARG1.
+ __ load_const_optimized(R4_ARG2, (address) name);
+ __ mr(R5_ARG3, R17_tos);
+ // R6_ARG4 contains specified class.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException_verbose));
+#ifdef ASSERT
+ // Above call must not return here since exception pending.
+ __ should_not_reach_here();
+#endif
+ return entry;
+}
+#endif
+
+address TemplateInterpreterGenerator::generate_ClassCastException_handler() {
+ address entry = __ pc();
+ // Expression stack must be empty before entering the VM if an
+ // exception happened.
+ __ empty_expression_stack();
+
+ // Load exception object.
+ // Thread will be loaded to R3_ARG1.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException), R17_tos);
+#ifdef ASSERT
+ // Above call must not return here since exception pending.
+ __ should_not_reach_here();
+#endif
+ return entry;
+}
+
+address TemplateInterpreterGenerator::generate_exception_handler_common(const char* name, const char* message, bool pass_oop) {
+ address entry = __ pc();
+ //__ untested("generate_exception_handler_common");
+ Register Rexception = R17_tos;
+
+ // Expression stack must be empty before entering the VM if an exception happened.
+ __ empty_expression_stack();
+
+ __ load_const_optimized(R4_ARG2, (address) name, R11_scratch1);
+ if (pass_oop) {
+ __ mr(R5_ARG3, Rexception);
+ __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_klass_exception), false);
+ } else {
+ __ load_const_optimized(R5_ARG3, (address) message, R11_scratch1);
+ __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::create_exception), false);
+ }
+
+ // Throw exception.
+ __ mr(R3_ARG1, Rexception);
+ __ load_const_optimized(R11_scratch1, Interpreter::throw_exception_entry(), R12_scratch2);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ return entry;
+}
+
+address TemplateInterpreterGenerator::generate_continuation_for(TosState state) {
+ address entry = __ pc();
+ __ unimplemented("generate_continuation_for");
+ return entry;
+}
+
+// This entry is returned to when a call returns to the interpreter.
+// When we arrive here, we expect that the callee stack frame is already popped.
+address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, int step, size_t index_size) {
+ address entry = __ pc();
+
+ // Move the value out of the return register back to the TOS cache of current frame.
+ switch (state) {
+ case ltos:
+ case btos:
+ case ctos:
+ case stos:
+ case atos:
+ case itos: __ mr(R17_tos, R3_RET); break; // RET -> TOS cache
+ case ftos:
+ case dtos: __ fmr(F15_ftos, F1_RET); break; // TOS cache -> GR_FRET
+ case vtos: break; // Nothing to do, this was a void return.
+ default : ShouldNotReachHere();
+ }
+
+ __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp.
+ __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1);
+ __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0);
+
+ // Compiled code destroys templateTableBase, reload.
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R12_scratch2);
+
+ const Register cache = R11_scratch1;
+ const Register size = R12_scratch2;
+ __ get_cache_and_index_at_bcp(cache, 1, index_size);
+
+ // Big Endian (get least significant byte of 64 bit value):
+ __ lbz(size, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()) + 7, cache);
+ __ sldi(size, size, Interpreter::logStackElementSize);
+ __ add(R15_esp, R15_esp, size);
+ __ dispatch_next(state, step);
+ return entry;
+}
+
+address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, int step) {
+ address entry = __ pc();
+ // If state != vtos, we're returning from a native method, which put it's result
+ // into the result register. So move the value out of the return register back
+ // to the TOS cache of current frame.
+
+ switch (state) {
+ case ltos:
+ case btos:
+ case ctos:
+ case stos:
+ case atos:
+ case itos: __ mr(R17_tos, R3_RET); break; // GR_RET -> TOS cache
+ case ftos:
+ case dtos: __ fmr(F15_ftos, F1_RET); break; // TOS cache -> GR_FRET
+ case vtos: break; // Nothing to do, this was a void return.
+ default : ShouldNotReachHere();
+ }
+
+ // Load LcpoolCache @@@ should be already set!
+ __ get_constant_pool_cache(R27_constPoolCache);
+
+ // Handle a pending exception, fall through if none.
+ __ check_and_forward_exception(R11_scratch1, R12_scratch2);
+
+ // Start executing bytecodes.
+ __ dispatch_next(state, step);
+
+ return entry;
+}
+
+// A result handler converts the native result into java format.
+// Use the shared code between c++ and template interpreter.
+address TemplateInterpreterGenerator::generate_result_handler_for(BasicType type) {
+ return AbstractInterpreterGenerator::generate_result_handler_for(type);
+}
+
+address TemplateInterpreterGenerator::generate_safept_entry_for(TosState state, address runtime_entry) {
+ address entry = __ pc();
+
+ __ push(state);
+ __ call_VM(noreg, runtime_entry);
+ __ dispatch_via(vtos, Interpreter::_normal_table.table_for(vtos));
+
+ return entry;
+}
+
+// Helpers for commoning out cases in the various type of method entries.
+
+// Increment invocation count & check for overflow.
+//
+// Note: checking for negative value instead of overflow
+// so we have a 'sticky' overflow test.
+//
+void TemplateInterpreterGenerator::generate_counter_incr(Label* overflow, Label* profile_method, Label* profile_method_continue) {
+ // Note: In tiered we increment either counters in method or in MDO depending if we're profiling or not.
+ Register Rscratch1 = R11_scratch1;
+ Register Rscratch2 = R12_scratch2;
+ Register R3_counters = R3_ARG1;
+ Label done;
+
+ if (TieredCompilation) {
+ const int increment = InvocationCounter::count_increment;
+ const int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift;
+ Label no_mdo;
+ if (ProfileInterpreter) {
+ const Register Rmdo = Rscratch1;
+ // If no method data exists, go to profile_continue.
+ __ ld(Rmdo, in_bytes(Method::method_data_offset()), R19_method);
+ __ cmpdi(CCR0, Rmdo, 0);
+ __ beq(CCR0, no_mdo);
+
+ // Increment backedge counter in the MDO.
+ const int mdo_bc_offs = in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
+ __ lwz(Rscratch2, mdo_bc_offs, Rmdo);
+ __ addi(Rscratch2, Rscratch2, increment);
+ __ stw(Rscratch2, mdo_bc_offs, Rmdo);
+ __ load_const_optimized(Rscratch1, mask, R0);
+ __ and_(Rscratch1, Rscratch2, Rscratch1);
+ __ bne(CCR0, done);
+ __ b(*overflow);
+ }
+
+ // Increment counter in MethodCounters*.
+ const int mo_bc_offs = in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
+ __ bind(no_mdo);
+ __ get_method_counters(R19_method, R3_counters, done);
+ __ lwz(Rscratch2, mo_bc_offs, R3_counters);
+ __ addi(Rscratch2, Rscratch2, increment);
+ __ stw(Rscratch2, mo_bc_offs, R3_counters);
+ __ load_const_optimized(Rscratch1, mask, R0);
+ __ and_(Rscratch1, Rscratch2, Rscratch1);
+ __ beq(CCR0, *overflow);
+
+ __ bind(done);
+
+ } else {
+
+ // Update standard invocation counters.
+ Register Rsum_ivc_bec = R4_ARG2;
+ __ get_method_counters(R19_method, R3_counters, done);
+ __ increment_invocation_counter(R3_counters, Rsum_ivc_bec, R12_scratch2);
+ // Increment interpreter invocation counter.
+ if (ProfileInterpreter) { // %%% Merge this into methodDataOop.
+ __ lwz(R12_scratch2, in_bytes(MethodCounters::interpreter_invocation_counter_offset()), R3_counters);
+ __ addi(R12_scratch2, R12_scratch2, 1);
+ __ stw(R12_scratch2, in_bytes(MethodCounters::interpreter_invocation_counter_offset()), R3_counters);
+ }
+ // Check if we must create a method data obj.
+ if (ProfileInterpreter && profile_method != NULL) {
+ const Register profile_limit = Rscratch1;
+ int pl_offs = __ load_const_optimized(profile_limit, &InvocationCounter::InterpreterProfileLimit, R0, true);
+ __ lwz(profile_limit, pl_offs, profile_limit);
+ // Test to see if we should create a method data oop.
+ __ cmpw(CCR0, Rsum_ivc_bec, profile_limit);
+ __ blt(CCR0, *profile_method_continue);
+ // If no method data exists, go to profile_method.
+ __ test_method_data_pointer(*profile_method);
+ }
+ // Finally check for counter overflow.
+ if (overflow) {
+ const Register invocation_limit = Rscratch1;
+ int il_offs = __ load_const_optimized(invocation_limit, &InvocationCounter::InterpreterInvocationLimit, R0, true);
+ __ lwz(invocation_limit, il_offs, invocation_limit);
+ assert(4 == sizeof(InvocationCounter::InterpreterInvocationLimit), "unexpected field size");
+ __ cmpw(CCR0, Rsum_ivc_bec, invocation_limit);
+ __ bge(CCR0, *overflow);
+ }
+
+ __ bind(done);
+ }
+}
+
+// Generate code to initiate compilation on invocation counter overflow.
+void TemplateInterpreterGenerator::generate_counter_overflow(Label& continue_entry) {
+ // Generate code to initiate compilation on the counter overflow.
+
+ // InterpreterRuntime::frequency_counter_overflow takes one arguments,
+ // which indicates if the counter overflow occurs at a backwards branch (NULL bcp)
+ // We pass zero in.
+ // The call returns the address of the verified entry point for the method or NULL
+ // if the compilation did not complete (either went background or bailed out).
+ //
+ // Unlike the C++ interpreter above: Check exceptions!
+ // Assumption: Caller must set the flag "do_not_unlock_if_sychronized" if the monitor of a sync'ed
+ // method has not yet been created. Thus, no unlocking of a non-existing monitor can occur.
+
+ __ li(R4_ARG2, 0);
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), R4_ARG2, true);
+
+ // Returns verified_entry_point or NULL.
+ // We ignore it in any case.
+ __ b(continue_entry);
+}
+
+void TemplateInterpreterGenerator::generate_stack_overflow_check(Register Rmem_frame_size, Register Rscratch1) {
+ assert_different_registers(Rmem_frame_size, Rscratch1);
+ __ generate_stack_overflow_check_with_compare_and_throw(Rmem_frame_size, Rscratch1);
+}
+
+void TemplateInterpreterGenerator::unlock_method(bool check_exceptions) {
+ __ unlock_object(R26_monitor, check_exceptions);
+}
+
+// Lock the current method, interpreter register window must be set up!
+void TemplateInterpreterGenerator::lock_method(Register Rflags, Register Rscratch1, Register Rscratch2, bool flags_preloaded) {
+ const Register Robj_to_lock = Rscratch2;
+
+ {
+ if (!flags_preloaded) {
+ __ lwz(Rflags, method_(access_flags));
+ }
+
+#ifdef ASSERT
+ // Check if methods needs synchronization.
+ {
+ Label Lok;
+ __ testbitdi(CCR0, R0, Rflags, JVM_ACC_SYNCHRONIZED_BIT);
+ __ btrue(CCR0,Lok);
+ __ stop("method doesn't need synchronization");
+ __ bind(Lok);
+ }
+#endif // ASSERT
+ }
+
+ // Get synchronization object to Rscratch2.
+ {
+ const int mirror_offset = in_bytes(Klass::java_mirror_offset());
+ Label Lstatic;
+ Label Ldone;
+
+ __ testbitdi(CCR0, R0, Rflags, JVM_ACC_STATIC_BIT);
+ __ btrue(CCR0, Lstatic);
+
+ // Non-static case: load receiver obj from stack and we're done.
+ __ ld(Robj_to_lock, R18_locals);
+ __ b(Ldone);
+
+ __ bind(Lstatic); // Static case: Lock the java mirror
+ __ ld(Robj_to_lock, in_bytes(Method::const_offset()), R19_method);
+ __ ld(Robj_to_lock, in_bytes(ConstMethod::constants_offset()), Robj_to_lock);
+ __ ld(Robj_to_lock, ConstantPool::pool_holder_offset_in_bytes(), Robj_to_lock);
+ __ ld(Robj_to_lock, mirror_offset, Robj_to_lock);
+
+ __ bind(Ldone);
+ __ verify_oop(Robj_to_lock);
+ }
+
+ // Got the oop to lock => execute!
+ __ add_monitor_to_stack(true, Rscratch1, R0);
+
+ __ std(Robj_to_lock, BasicObjectLock::obj_offset_in_bytes(), R26_monitor);
+ __ lock_object(R26_monitor, Robj_to_lock);
+}
+
+// Generate a fixed interpreter frame for pure interpreter
+// and I2N native transition frames.
+//
+// Before (stack grows downwards):
+//
+// | ... |
+// |------------- |
+// | java arg0 |
+// | ... |
+// | java argn |
+// | | <- R15_esp
+// | |
+// |--------------|
+// | abi_112 |
+// | | <- R1_SP
+// |==============|
+//
+//
+// After:
+//
+// | ... |
+// | java arg0 |<- R18_locals
+// | ... |
+// | java argn |
+// |--------------|
+// | |
+// | java locals |
+// | |
+// |--------------|
+// | abi_48 |
+// |==============|
+// | |
+// | istate |
+// | |
+// |--------------|
+// | monitor |<- R26_monitor
+// |--------------|
+// | |<- R15_esp
+// | expression |
+// | stack |
+// | |
+// |--------------|
+// | |
+// | abi_112 |<- R1_SP
+// |==============|
+//
+// The top most frame needs an abi space of 112 bytes. This space is needed,
+// since we call to c. The c function may spill their arguments to the caller
+// frame. When we call to java, we don't need these spill slots. In order to save
+// space on the stack, we resize the caller. However, java local reside in
+// the caller frame and the frame has to be increased. The frame_size for the
+// current frame was calculated based on max_stack as size for the expression
+// stack. At the call, just a part of the expression stack might be used.
+// We don't want to waste this space and cut the frame back accordingly.
+// The resulting amount for resizing is calculated as follows:
+// resize = (number_of_locals - number_of_arguments) * slot_size
+// + (R1_SP - R15_esp) + 48
+//
+// The size for the callee frame is calculated:
+// framesize = 112 + max_stack + monitor + state_size
+//
+// maxstack: Max number of slots on the expression stack, loaded from the method.
+// monitor: We statically reserve room for one monitor object.
+// state_size: We save the current state of the interpreter to this area.
+//
+void TemplateInterpreterGenerator::generate_fixed_frame(bool native_call, Register Rsize_of_parameters, Register Rsize_of_locals) {
+ Register parent_frame_resize = R6_ARG4, // Frame will grow by this number of bytes.
+ top_frame_size = R7_ARG5,
+ Rconst_method = R8_ARG6;
+
+ assert_different_registers(Rsize_of_parameters, Rsize_of_locals, parent_frame_resize, top_frame_size);
+
+ __ ld(Rconst_method, method_(const));
+ __ lhz(Rsize_of_parameters /* number of params */,
+ in_bytes(ConstMethod::size_of_parameters_offset()), Rconst_method);
+ if (native_call) {
+ // If we're calling a native method, we reserve space for the worst-case signature
+ // handler varargs vector, which is max(Argument::n_register_parameters, parameter_count+2).
+ // We add two slots to the parameter_count, one for the jni
+ // environment and one for a possible native mirror.
+ Label skip_native_calculate_max_stack;
+ __ addi(top_frame_size, Rsize_of_parameters, 2);
+ __ cmpwi(CCR0, top_frame_size, Argument::n_register_parameters);
+ __ bge(CCR0, skip_native_calculate_max_stack);
+ __ li(top_frame_size, Argument::n_register_parameters);
+ __ bind(skip_native_calculate_max_stack);
+ __ sldi(Rsize_of_parameters, Rsize_of_parameters, Interpreter::logStackElementSize);
+ __ sldi(top_frame_size, top_frame_size, Interpreter::logStackElementSize);
+ __ sub(parent_frame_resize, R1_SP, R15_esp); // <0, off by Interpreter::stackElementSize!
+ assert(Rsize_of_locals == noreg, "Rsize_of_locals not initialized"); // Only relevant value is Rsize_of_parameters.
+ } else {
+ __ lhz(Rsize_of_locals /* number of params */, in_bytes(ConstMethod::size_of_locals_offset()), Rconst_method);
+ __ sldi(Rsize_of_parameters, Rsize_of_parameters, Interpreter::logStackElementSize);
+ __ sldi(Rsize_of_locals, Rsize_of_locals, Interpreter::logStackElementSize);
+ __ lhz(top_frame_size, in_bytes(ConstMethod::max_stack_offset()), Rconst_method);
+ __ sub(R11_scratch1, Rsize_of_locals, Rsize_of_parameters); // >=0
+ __ sub(parent_frame_resize, R1_SP, R15_esp); // <0, off by Interpreter::stackElementSize!
+ __ sldi(top_frame_size, top_frame_size, Interpreter::logStackElementSize);
+ __ add(parent_frame_resize, parent_frame_resize, R11_scratch1);
+ }
+
+ // Compute top frame size.
+ __ addi(top_frame_size, top_frame_size, frame::abi_reg_args_size + frame::ijava_state_size);
+
+ // Cut back area between esp and max_stack.
+ __ addi(parent_frame_resize, parent_frame_resize, frame::abi_minframe_size - Interpreter::stackElementSize);
+
+ __ round_to(top_frame_size, frame::alignment_in_bytes);
+ __ round_to(parent_frame_resize, frame::alignment_in_bytes);
+ // parent_frame_resize = (locals-parameters) - (ESP-SP-ABI48) Rounded to frame alignment size.
+ // Enlarge by locals-parameters (not in case of native_call), shrink by ESP-SP-ABI48.
+
+ {
+ // --------------------------------------------------------------------------
+ // Stack overflow check
+
+ Label cont;
+ __ add(R11_scratch1, parent_frame_resize, top_frame_size);
+ generate_stack_overflow_check(R11_scratch1, R12_scratch2);
+ }
+
+ // Set up interpreter state registers.
+
+ __ add(R18_locals, R15_esp, Rsize_of_parameters);
+ __ ld(R27_constPoolCache, in_bytes(ConstMethod::constants_offset()), Rconst_method);
+ __ ld(R27_constPoolCache, ConstantPool::cache_offset_in_bytes(), R27_constPoolCache);
+
+ // Set method data pointer.
+ if (ProfileInterpreter) {
+ Label zero_continue;
+ __ ld(R28_mdx, method_(method_data));
+ __ cmpdi(CCR0, R28_mdx, 0);
+ __ beq(CCR0, zero_continue);
+ __ addi(R28_mdx, R28_mdx, in_bytes(MethodData::data_offset()));
+ __ bind(zero_continue);
+ }
+
+ if (native_call) {
+ __ li(R14_bcp, 0); // Must initialize.
+ } else {
+ __ add(R14_bcp, in_bytes(ConstMethod::codes_offset()), Rconst_method);
+ }
+
+ // Resize parent frame.
+ __ mflr(R12_scratch2);
+ __ neg(parent_frame_resize, parent_frame_resize);
+ __ resize_frame(parent_frame_resize, R11_scratch1);
+ __ std(R12_scratch2, _abi(lr), R1_SP);
+
+ __ addi(R26_monitor, R1_SP, - frame::ijava_state_size);
+ __ addi(R15_esp, R26_monitor, - Interpreter::stackElementSize);
+
+ // Store values.
+ // R15_esp, R14_bcp, R26_monitor, R28_mdx are saved at java calls
+ // in InterpreterMacroAssembler::call_from_interpreter.
+ __ std(R19_method, _ijava_state_neg(method), R1_SP);
+ __ std(R21_sender_SP, _ijava_state_neg(sender_sp), R1_SP);
+ __ std(R27_constPoolCache, _ijava_state_neg(cpoolCache), R1_SP);
+ __ std(R18_locals, _ijava_state_neg(locals), R1_SP);
+
+ // Note: esp, bcp, monitor, mdx live in registers. Hence, the correct version can only
+ // be found in the frame after save_interpreter_state is done. This is always true
+ // for non-top frames. But when a signal occurs, dumping the top frame can go wrong,
+ // because e.g. frame::interpreter_frame_bcp() will not access the correct value
+ // (Enhanced Stack Trace).
+ // The signal handler does not save the interpreter state into the frame.
+ __ li(R0, 0);
+#ifdef ASSERT
+ // Fill remaining slots with constants.
+ __ load_const_optimized(R11_scratch1, 0x5afe);
+ __ load_const_optimized(R12_scratch2, 0xdead);
+#endif
+ // We have to initialize some frame slots for native calls (accessed by GC).
+ if (native_call) {
+ __ std(R26_monitor, _ijava_state_neg(monitors), R1_SP);
+ __ std(R14_bcp, _ijava_state_neg(bcp), R1_SP);
+ if (ProfileInterpreter) { __ std(R28_mdx, _ijava_state_neg(mdx), R1_SP); }
+ }
+#ifdef ASSERT
+ else {
+ __ std(R12_scratch2, _ijava_state_neg(monitors), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(bcp), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(mdx), R1_SP);
+ }
+ __ std(R11_scratch1, _ijava_state_neg(ijava_reserved), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(esp), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(lresult), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(fresult), R1_SP);
+#endif
+ __ subf(R12_scratch2, top_frame_size, R1_SP);
+ __ std(R0, _ijava_state_neg(oop_tmp), R1_SP);
+ __ std(R12_scratch2, _ijava_state_neg(top_frame_sp), R1_SP);
+
+ // Push top frame.
+ __ push_frame(top_frame_size, R11_scratch1);
+}
+
+// End of helpers
+
+// ============================================================================
+// Various method entries
+//
+
+// Empty method, generate a very fast return. We must skip this entry if
+// someone's debugging, indicated by the flag
+// "interp_mode" in the Thread obj.
+// Note: empty methods are generated mostly methods that do assertions, which are
+// disabled in the "java opt build".
+address TemplateInterpreterGenerator::generate_empty_entry(void) {
+ if (!UseFastEmptyMethods) {
+ NOT_PRODUCT(__ should_not_reach_here();)
+ return Interpreter::entry_for_kind(Interpreter::zerolocals);
+ }
+
+ Label Lslow_path;
+ const Register Rjvmti_mode = R11_scratch1;
+ address entry = __ pc();
+
+ __ lwz(Rjvmti_mode, thread_(interp_only_mode));
+ __ cmpwi(CCR0, Rjvmti_mode, 0);
+ __ bne(CCR0, Lslow_path); // jvmti_mode!=0
+
+ // Noone's debuggin: Simply return.
+ // Pop c2i arguments (if any) off when we return.
+#ifdef ASSERT
+ __ ld(R9_ARG7, 0, R1_SP);
+ __ ld(R10_ARG8, 0, R21_sender_SP);
+ __ cmpd(CCR0, R9_ARG7, R10_ARG8);
+ __ asm_assert_eq("backlink", 0x545);
+#endif // ASSERT
+ __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started.
+
+ // And we're done.
+ __ blr();
+
+ __ bind(Lslow_path);
+ __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R11_scratch1);
+ __ flush();
+
+ return entry;
+}
+
+// Support abs and sqrt like in compiler.
+// For others we can use a normal (native) entry.
+
+inline bool math_entry_available(AbstractInterpreter::MethodKind kind) {
+ // Provide math entry with debugging on demand.
+ // Note: Debugging changes which code will get executed:
+ // Debugging or disabled InlineIntrinsics: java method will get interpreted and performs a native call.
+ // Not debugging and enabled InlineIntrinics: processor instruction will get used.
+ // Result might differ slightly due to rounding etc.
+ if (!InlineIntrinsics && (!FLAG_IS_ERGO(InlineIntrinsics))) return false; // Generate a vanilla entry.
+
+ return ((kind==Interpreter::java_lang_math_sqrt && VM_Version::has_fsqrt()) ||
+ (kind==Interpreter::java_lang_math_abs));
+}
+
+address TemplateInterpreterGenerator::generate_math_entry(AbstractInterpreter::MethodKind kind) {
+ if (!math_entry_available(kind)) {
+ NOT_PRODUCT(__ should_not_reach_here();)
+ return Interpreter::entry_for_kind(Interpreter::zerolocals);
+ }
+
+ Label Lslow_path;
+ const Register Rjvmti_mode = R11_scratch1;
+ address entry = __ pc();
+
+ // Provide math entry with debugging on demand.
+ __ lwz(Rjvmti_mode, thread_(interp_only_mode));
+ __ cmpwi(CCR0, Rjvmti_mode, 0);
+ __ bne(CCR0, Lslow_path); // jvmti_mode!=0
+
+ __ lfd(F1_RET, Interpreter::stackElementSize, R15_esp);
+
+ // Pop c2i arguments (if any) off when we return.
+#ifdef ASSERT
+ __ ld(R9_ARG7, 0, R1_SP);
+ __ ld(R10_ARG8, 0, R21_sender_SP);
+ __ cmpd(CCR0, R9_ARG7, R10_ARG8);
+ __ asm_assert_eq("backlink", 0x545);
+#endif // ASSERT
+ __ mr(R1_SP, R21_sender_SP); // Cut the stack back to where the caller started.
+
+ if (kind == Interpreter::java_lang_math_sqrt) {
+ __ fsqrt(F1_RET, F1_RET);
+ } else if (kind == Interpreter::java_lang_math_abs) {
+ __ fabs(F1_RET, F1_RET);
+ } else {
+ ShouldNotReachHere();
+ }
+
+ // And we're done.
+ __ blr();
+
+ // Provide slow path for JVMTI case.
+ __ bind(Lslow_path);
+ __ branch_to_entry(Interpreter::entry_for_kind(Interpreter::zerolocals), R12_scratch2);
+ __ flush();
+
+ return entry;
+}
+
+// Interpreter stub for calling a native method. (asm interpreter)
+// This sets up a somewhat different looking stack for calling the
+// native method than the typical interpreter frame setup.
+//
+// On entry:
+// R19_method - method
+// R16_thread - JavaThread*
+// R15_esp - intptr_t* sender tos
+//
+// abstract stack (grows up)
+// [ IJava (caller of JNI callee) ] <-- ASP
+// ...
+address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
+
+ address entry = __ pc();
+
+ const bool inc_counter = UseCompiler || CountCompiledCalls;
+
+ // -----------------------------------------------------------------------------
+ // Allocate a new frame that represents the native callee (i2n frame).
+ // This is not a full-blown interpreter frame, but in particular, the
+ // following registers are valid after this:
+ // - R19_method
+ // - R18_local (points to start of argumuments to native function)
+ //
+ // abstract stack (grows up)
+ // [ IJava (caller of JNI callee) ] <-- ASP
+ // ...
+
+ const Register signature_handler_fd = R11_scratch1;
+ const Register pending_exception = R0;
+ const Register result_handler_addr = R31;
+ const Register native_method_fd = R11_scratch1;
+ const Register access_flags = R22_tmp2;
+ const Register active_handles = R11_scratch1; // R26_monitor saved to state.
+ const Register sync_state = R12_scratch2;
+ const Register sync_state_addr = sync_state; // Address is dead after use.
+ const Register suspend_flags = R11_scratch1;
+
+ //=============================================================================
+ // Allocate new frame and initialize interpreter state.
+
+ Label exception_return;
+ Label exception_return_sync_check;
+ Label stack_overflow_return;
+
+ // Generate new interpreter state and jump to stack_overflow_return in case of
+ // a stack overflow.
+ //generate_compute_interpreter_state(stack_overflow_return);
+
+ Register size_of_parameters = R22_tmp2;
+
+ generate_fixed_frame(true, size_of_parameters, noreg /* unused */);
+
+ //=============================================================================
+ // Increment invocation counter. On overflow, entry to JNI method
+ // will be compiled.
+ Label invocation_counter_overflow, continue_after_compile;
+ if (inc_counter) {
+ if (synchronized) {
+ // Since at this point in the method invocation the exception handler
+ // would try to exit the monitor of synchronized methods which hasn't
+ // been entered yet, we set the thread local variable
+ // _do_not_unlock_if_synchronized to true. If any exception was thrown by
+ // runtime, exception handling i.e. unlock_if_synchronized_method will
+ // check this thread local flag.
+ // This flag has two effects, one is to force an unwind in the topmost
+ // interpreter frame and not perform an unlock while doing so.
+ __ li(R0, 1);
+ __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
+ }
+ generate_counter_incr(&invocation_counter_overflow, NULL, NULL);
+
+ __ BIND(continue_after_compile);
+ // Reset the _do_not_unlock_if_synchronized flag.
+ if (synchronized) {
+ __ li(R0, 0);
+ __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
+ }
+ }
+
+ // access_flags = method->access_flags();
+ // Load access flags.
+ assert(access_flags->is_nonvolatile(),
+ "access_flags must be in a non-volatile register");
+ // Type check.
+ assert(4 == sizeof(AccessFlags), "unexpected field size");
+ __ lwz(access_flags, method_(access_flags));
+
+ // We don't want to reload R19_method and access_flags after calls
+ // to some helper functions.
+ assert(R19_method->is_nonvolatile(),
+ "R19_method must be a non-volatile register");
+
+ // Check for synchronized methods. Must happen AFTER invocation counter
+ // check, so method is not locked if counter overflows.
+
+ if (synchronized) {
+ lock_method(access_flags, R11_scratch1, R12_scratch2, true);
+
+ // Update monitor in state.
+ __ ld(R11_scratch1, 0, R1_SP);
+ __ std(R26_monitor, _ijava_state_neg(monitors), R11_scratch1);
+ }
+
+ // jvmti/jvmpi support
+ __ notify_method_entry();
+
+ //=============================================================================
+ // Get and call the signature handler.
+
+ __ ld(signature_handler_fd, method_(signature_handler));
+ Label call_signature_handler;
+
+ __ cmpdi(CCR0, signature_handler_fd, 0);
+ __ bne(CCR0, call_signature_handler);
+
+ // Method has never been called. Either generate a specialized
+ // handler or point to the slow one.
+ //
+ // Pass parameter 'false' to avoid exception check in call_VM.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::prepare_native_call), R19_method, false);
+
+ // Check for an exception while looking up the target method. If we
+ // incurred one, bail.
+ __ ld(pending_exception, thread_(pending_exception));
+ __ cmpdi(CCR0, pending_exception, 0);
+ __ bne(CCR0, exception_return_sync_check); // Has pending exception.
+
+ // Reload signature handler, it may have been created/assigned in the meanwhile.
+ __ ld(signature_handler_fd, method_(signature_handler));
+ __ twi_0(signature_handler_fd); // Order wrt. load of klass mirror and entry point (isync is below).
+
+ __ BIND(call_signature_handler);
+
+ // Before we call the signature handler we push a new frame to
+ // protect the interpreter frame volatile registers when we return
+ // from jni but before we can get back to Java.
+
+ // First set the frame anchor while the SP/FP registers are
+ // convenient and the slow signature handler can use this same frame
+ // anchor.
+
+ // We have a TOP_IJAVA_FRAME here, which belongs to us.
+ __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R12_scratch2/*tmp*/);
+
+ // Now the interpreter frame (and its call chain) have been
+ // invalidated and flushed. We are now protected against eager
+ // being enabled in native code. Even if it goes eager the
+ // registers will be reloaded as clean and we will invalidate after
+ // the call so no spurious flush should be possible.
+
+ // Call signature handler and pass locals address.
+ //
+ // Our signature handlers copy required arguments to the C stack
+ // (outgoing C args), R3_ARG1 to R10_ARG8, and FARG1 to FARG13.
+ __ mr(R3_ARG1, R18_locals);
+ __ ld(signature_handler_fd, 0, signature_handler_fd);
+
+ __ call_stub(signature_handler_fd);
+
+ // Remove the register parameter varargs slots we allocated in
+ // compute_interpreter_state. SP+16 ends up pointing to the ABI
+ // outgoing argument area.
+ //
+ // Not needed on PPC64.
+ //__ add(SP, SP, Argument::n_register_parameters*BytesPerWord);
+
+ assert(result_handler_addr->is_nonvolatile(), "result_handler_addr must be in a non-volatile register");
+ // Save across call to native method.
+ __ mr(result_handler_addr, R3_RET);
+
+ __ isync(); // Acquire signature handler before trying to fetch the native entry point and klass mirror.
+
+ // Set up fixed parameters and call the native method.
+ // If the method is static, get mirror into R4_ARG2.
+ {
+ Label method_is_not_static;
+ // Access_flags is non-volatile and still, no need to restore it.
+
+ // Restore access flags.
+ __ testbitdi(CCR0, R0, access_flags, JVM_ACC_STATIC_BIT);
+ __ bfalse(CCR0, method_is_not_static);
+
+ // constants = method->constants();
+ __ ld(R11_scratch1, in_bytes(Method::const_offset()), R19_method);
+ __ ld(R11_scratch1, in_bytes(ConstMethod::constants_offset()), R11_scratch1);
+ // pool_holder = method->constants()->pool_holder();
+ __ ld(R11_scratch1/*pool_holder*/, ConstantPool::pool_holder_offset_in_bytes(),
+ R11_scratch1/*constants*/);
+
+ const int mirror_offset = in_bytes(Klass::java_mirror_offset());
+
+ // mirror = pool_holder->klass_part()->java_mirror();
+ __ ld(R0/*mirror*/, mirror_offset, R11_scratch1/*pool_holder*/);
+ // state->_native_mirror = mirror;
+
+ __ ld(R11_scratch1, 0, R1_SP);
+ __ std(R0/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1);
+ // R4_ARG2 = &state->_oop_temp;
+ __ addi(R4_ARG2, R11_scratch1, _ijava_state_neg(oop_tmp));
+ __ BIND(method_is_not_static);
+ }
+
+ // At this point, arguments have been copied off the stack into
+ // their JNI positions. Oops are boxed in-place on the stack, with
+ // handles copied to arguments. The result handler address is in a
+ // register.
+
+ // Pass JNIEnv address as first parameter.
+ __ addir(R3_ARG1, thread_(jni_environment));
+
+ // Load the native_method entry before we change the thread state.
+ __ ld(native_method_fd, method_(native_function));
+
+ //=============================================================================
+ // Transition from _thread_in_Java to _thread_in_native. As soon as
+ // we make this change the safepoint code needs to be certain that
+ // the last Java frame we established is good. The pc in that frame
+ // just needs to be near here not an actual return address.
+
+ // We use release_store_fence to update values like the thread state, where
+ // we don't want the current thread to continue until all our prior memory
+ // accesses (including the new thread state) are visible to other threads.
+ __ li(R0, _thread_in_native);
+ __ release();
+
+ // TODO PPC port assert(4 == JavaThread::sz_thread_state(), "unexpected field size");
+ __ stw(R0, thread_(thread_state));
+
+ if (UseMembar) {
+ __ fence();
+ }
+
+ //=============================================================================
+ // Call the native method. Argument registers must not have been
+ // overwritten since "__ call_stub(signature_handler);" (except for
+ // ARG1 and ARG2 for static methods).
+ __ call_c(native_method_fd);
+
+ __ li(R0, 0);
+ __ ld(R11_scratch1, 0, R1_SP);
+ __ std(R3_RET, _ijava_state_neg(lresult), R11_scratch1);
+ __ stfd(F1_RET, _ijava_state_neg(fresult), R11_scratch1);
+ __ std(R0/*mirror*/, _ijava_state_neg(oop_tmp), R11_scratch1); // reset
+
+ // Note: C++ interpreter needs the following here:
+ // The frame_manager_lr field, which we use for setting the last
+ // java frame, gets overwritten by the signature handler. Restore
+ // it now.
+ //__ get_PC_trash_LR(R11_scratch1);
+ //__ std(R11_scratch1, _top_ijava_frame_abi(frame_manager_lr), R1_SP);
+
+ // Because of GC R19_method may no longer be valid.
+
+ // Block, if necessary, before resuming in _thread_in_Java state.
+ // In order for GC to work, don't clear the last_Java_sp until after
+ // blocking.
+
+ //=============================================================================
+ // Switch thread to "native transition" state before reading the
+ // synchronization state. This additional state is necessary
+ // because reading and testing the synchronization state is not
+ // atomic w.r.t. GC, as this scenario demonstrates: Java thread A,
+ // in _thread_in_native state, loads _not_synchronized and is
+ // preempted. VM thread changes sync state to synchronizing and
+ // suspends threads for GC. Thread A is resumed to finish this
+ // native method, but doesn't block here since it didn't see any
+ // synchronization in progress, and escapes.
+
+ // We use release_store_fence to update values like the thread state, where
+ // we don't want the current thread to continue until all our prior memory
+ // accesses (including the new thread state) are visible to other threads.
+ __ li(R0/*thread_state*/, _thread_in_native_trans);
+ __ release();
+ __ stw(R0/*thread_state*/, thread_(thread_state));
+ if (UseMembar) {
+ __ fence();
+ }
+ // Write serialization page so that the VM thread can do a pseudo remote
+ // membar. We use the current thread pointer to calculate a thread
+ // specific offset to write to within the page. This minimizes bus
+ // traffic due to cache line collision.
+ else {
+ __ serialize_memory(R16_thread, R11_scratch1, R12_scratch2);
+ }
+
+ // Now before we return to java we must look for a current safepoint
+ // (a new safepoint can not start since we entered native_trans).
+ // We must check here because a current safepoint could be modifying
+ // the callers registers right this moment.
+
+ // Acquire isn't strictly necessary here because of the fence, but
+ // sync_state is declared to be volatile, so we do it anyway
+ // (cmp-br-isync on one path, release (same as acquire on PPC64) on the other path).
+ int sync_state_offs = __ load_const_optimized(sync_state_addr, SafepointSynchronize::address_of_state(), /*temp*/R0, true);
+
+ // TODO PPC port assert(4 == SafepointSynchronize::sz_state(), "unexpected field size");
+ __ lwz(sync_state, sync_state_offs, sync_state_addr);
+
+ // TODO PPC port assert(4 == Thread::sz_suspend_flags(), "unexpected field size");
+ __ lwz(suspend_flags, thread_(suspend_flags));
+
+ Label sync_check_done;
+ Label do_safepoint;
+ // No synchronization in progress nor yet synchronized.
+ __ cmpwi(CCR0, sync_state, SafepointSynchronize::_not_synchronized);
+ // Not suspended.
+ __ cmpwi(CCR1, suspend_flags, 0);
+
+ __ bne(CCR0, do_safepoint);
+ __ beq(CCR1, sync_check_done);
+ __ bind(do_safepoint);
+ __ isync();
+ // Block. We do the call directly and leave the current
+ // last_Java_frame setup undisturbed. We must save any possible
+ // native result across the call. No oop is present.
+
+ __ mr(R3_ARG1, R16_thread);
+ __ call_c(CAST_FROM_FN_PTR(FunctionDescriptor*, JavaThread::check_special_condition_for_native_trans),
+ relocInfo::none);
+
+ __ bind(sync_check_done);
+
+ //=============================================================================
+ // <<<<<< Back in Interpreter Frame >>>>>
+
+ // We are in thread_in_native_trans here and back in the normal
+ // interpreter frame. We don't have to do anything special about
+ // safepoints and we can switch to Java mode anytime we are ready.
+
+ // Note: frame::interpreter_frame_result has a dependency on how the
+ // method result is saved across the call to post_method_exit. For
+ // native methods it assumes that the non-FPU/non-void result is
+ // saved in _native_lresult and a FPU result in _native_fresult. If
+ // this changes then the interpreter_frame_result implementation
+ // will need to be updated too.
+
+ // On PPC64, we have stored the result directly after the native call.
+
+ //=============================================================================
+ // Back in Java
+
+ // We use release_store_fence to update values like the thread state, where
+ // we don't want the current thread to continue until all our prior memory
+ // accesses (including the new thread state) are visible to other threads.
+ __ li(R0/*thread_state*/, _thread_in_Java);
+ __ release();
+ __ stw(R0/*thread_state*/, thread_(thread_state));
+ if (UseMembar) {
+ __ fence();
+ }
+
+ __ reset_last_Java_frame();
+
+ // Jvmdi/jvmpi support. Whether we've got an exception pending or
+ // not, and whether unlocking throws an exception or not, we notify
+ // on native method exit. If we do have an exception, we'll end up
+ // in the caller's context to handle it, so if we don't do the
+ // notify here, we'll drop it on the floor.
+ __ notify_method_exit(true/*native method*/,
+ ilgl /*illegal state (not used for native methods)*/,
+ InterpreterMacroAssembler::NotifyJVMTI,
+ false /*check_exceptions*/);
+
+ //=============================================================================
+ // Handle exceptions
+
+ if (synchronized) {
+ // Don't check for exceptions since we're still in the i2n frame. Do that
+ // manually afterwards.
+ unlock_method(false);
+ }
+
+ // Reset active handles after returning from native.
+ // thread->active_handles()->clear();
+ __ ld(active_handles, thread_(active_handles));
+ // TODO PPC port assert(4 == JNIHandleBlock::top_size_in_bytes(), "unexpected field size");
+ __ li(R0, 0);
+ __ stw(R0, JNIHandleBlock::top_offset_in_bytes(), active_handles);
+
+ Label exception_return_sync_check_already_unlocked;
+ __ ld(R0/*pending_exception*/, thread_(pending_exception));
+ __ cmpdi(CCR0, R0/*pending_exception*/, 0);
+ __ bne(CCR0, exception_return_sync_check_already_unlocked);
+
+ //-----------------------------------------------------------------------------
+ // No exception pending.
+
+ // Move native method result back into proper registers and return.
+ // Invoke result handler (may unbox/promote).
+ __ ld(R11_scratch1, 0, R1_SP);
+ __ ld(R3_RET, _ijava_state_neg(lresult), R11_scratch1);
+ __ lfd(F1_RET, _ijava_state_neg(fresult), R11_scratch1);
+ __ call_stub(result_handler_addr);
+
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2);
+
+ // Must use the return pc which was loaded from the caller's frame
+ // as the VM uses return-pc-patching for deoptimization.
+ __ mtlr(R0);
+ __ blr();
+
+ //-----------------------------------------------------------------------------
+ // An exception is pending. We call into the runtime only if the
+ // caller was not interpreted. If it was interpreted the
+ // interpreter will do the correct thing. If it isn't interpreted
+ // (call stub/compiled code) we will change our return and continue.
+
+ __ BIND(exception_return_sync_check);
+
+ if (synchronized) {
+ // Don't check for exceptions since we're still in the i2n frame. Do that
+ // manually afterwards.
+ unlock_method(false);
+ }
+ __ BIND(exception_return_sync_check_already_unlocked);
+
+ const Register return_pc = R31;
+
+ __ ld(return_pc, 0, R1_SP);
+ __ ld(return_pc, _abi(lr), return_pc);
+
+ // Get the address of the exception handler.
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address),
+ R16_thread,
+ return_pc /* return pc */);
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, noreg, R11_scratch1, R12_scratch2);
+
+ // Load the PC of the the exception handler into LR.
+ __ mtlr(R3_RET);
+
+ // Load exception into R3_ARG1 and clear pending exception in thread.
+ __ ld(R3_ARG1/*exception*/, thread_(pending_exception));
+ __ li(R4_ARG2, 0);
+ __ std(R4_ARG2, thread_(pending_exception));
+
+ // Load the original return pc into R4_ARG2.
+ __ mr(R4_ARG2/*issuing_pc*/, return_pc);
+
+ // Return to exception handler.
+ __ blr();
+
+ //=============================================================================
+ // Counter overflow.
+
+ if (inc_counter) {
+ // Handle invocation counter overflow.
+ __ bind(invocation_counter_overflow);
+
+ generate_counter_overflow(continue_after_compile);
+ }
+
+ return entry;
+}
+
+// Generic interpreted method entry to (asm) interpreter.
+//
+address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
+ bool inc_counter = UseCompiler || CountCompiledCalls;
+ address entry = __ pc();
+ // Generate the code to allocate the interpreter stack frame.
+ Register Rsize_of_parameters = R4_ARG2, // Written by generate_fixed_frame.
+ Rsize_of_locals = R5_ARG3; // Written by generate_fixed_frame.
+
+ generate_fixed_frame(false, Rsize_of_parameters, Rsize_of_locals);
+
+#ifdef FAST_DISPATCH
+ __ unimplemented("Fast dispatch in generate_normal_entry");
+#if 0
+ __ set((intptr_t)Interpreter::dispatch_table(), IdispatchTables);
+ // Set bytecode dispatch table base.
+#endif
+#endif
+
+ // --------------------------------------------------------------------------
+ // Zero out non-parameter locals.
+ // Note: *Always* zero out non-parameter locals as Sparc does. It's not
+ // worth to ask the flag, just do it.
+ Register Rslot_addr = R6_ARG4,
+ Rnum = R7_ARG5;
+ Label Lno_locals, Lzero_loop;
+
+ // Set up the zeroing loop.
+ __ subf(Rnum, Rsize_of_parameters, Rsize_of_locals);
+ __ subf(Rslot_addr, Rsize_of_parameters, R18_locals);
+ __ srdi_(Rnum, Rnum, Interpreter::logStackElementSize);
+ __ beq(CCR0, Lno_locals);
+ __ li(R0, 0);
+ __ mtctr(Rnum);
+
+ // The zero locals loop.
+ __ bind(Lzero_loop);
+ __ std(R0, 0, Rslot_addr);
+ __ addi(Rslot_addr, Rslot_addr, -Interpreter::stackElementSize);
+ __ bdnz(Lzero_loop);
+
+ __ bind(Lno_locals);
+
+ // --------------------------------------------------------------------------
+ // Counter increment and overflow check.
+ Label invocation_counter_overflow,
+ profile_method,
+ profile_method_continue;
+ if (inc_counter || ProfileInterpreter) {
+
+ Register Rdo_not_unlock_if_synchronized_addr = R11_scratch1;
+ if (synchronized) {
+ // Since at this point in the method invocation the exception handler
+ // would try to exit the monitor of synchronized methods which hasn't
+ // been entered yet, we set the thread local variable
+ // _do_not_unlock_if_synchronized to true. If any exception was thrown by
+ // runtime, exception handling i.e. unlock_if_synchronized_method will
+ // check this thread local flag.
+ // This flag has two effects, one is to force an unwind in the topmost
+ // interpreter frame and not perform an unlock while doing so.
+ __ li(R0, 1);
+ __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
+ }
+ // Increment invocation counter and check for overflow.
+ if (inc_counter) {
+ generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue);
+ }
+
+ __ bind(profile_method_continue);
+
+ // Reset the _do_not_unlock_if_synchronized flag.
+ if (synchronized) {
+ __ li(R0, 0);
+ __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread);
+ }
+ }
+
+ // --------------------------------------------------------------------------
+ // Locking of synchronized methods. Must happen AFTER invocation_counter
+ // check and stack overflow check, so method is not locked if overflows.
+ if (synchronized) {
+ lock_method(R3_ARG1, R4_ARG2, R5_ARG3);
+ }
+#ifdef ASSERT
+ else {
+ Label Lok;
+ __ lwz(R0, in_bytes(Method::access_flags_offset()), R19_method);
+ __ andi_(R0, R0, JVM_ACC_SYNCHRONIZED);
+ __ asm_assert_eq("method needs synchronization", 0x8521);
+ __ bind(Lok);
+ }
+#endif // ASSERT
+
+ __ verify_thread();
+
+ // --------------------------------------------------------------------------
+ // JVMTI support
+ __ notify_method_entry();
+
+ // --------------------------------------------------------------------------
+ // Start executing instructions.
+ __ dispatch_next(vtos);
+
+ // --------------------------------------------------------------------------
+ // Out of line counter overflow and MDO creation code.
+ if (ProfileInterpreter) {
+ // We have decided to profile this method in the interpreter.
+ __ bind(profile_method);
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::profile_method));
+ __ set_method_data_pointer_for_bcp();
+ __ b(profile_method_continue);
+ }
+
+ if (inc_counter) {
+ // Handle invocation counter overflow.
+ __ bind(invocation_counter_overflow);
+ generate_counter_overflow(profile_method_continue);
+ }
+ return entry;
+}
+
+// =============================================================================
+// Entry points
+
+address AbstractInterpreterGenerator::generate_method_entry(
+ AbstractInterpreter::MethodKind kind) {
+ // Determine code generation flags.
+ bool synchronized = false;
+ address entry_point = NULL;
+
+ switch (kind) {
+ case Interpreter::zerolocals : break;
+ case Interpreter::zerolocals_synchronized: synchronized = true; break;
+ case Interpreter::native : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(false); break;
+ case Interpreter::native_synchronized : entry_point = ((InterpreterGenerator*) this)->generate_native_entry(true); break;
+ case Interpreter::empty : entry_point = ((InterpreterGenerator*) this)->generate_empty_entry(); break;
+ case Interpreter::accessor : entry_point = ((InterpreterGenerator*) this)->generate_accessor_entry(); break;
+ case Interpreter::abstract : entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break;
+
+ case Interpreter::java_lang_math_sin : // fall thru
+ case Interpreter::java_lang_math_cos : // fall thru
+ case Interpreter::java_lang_math_tan : // fall thru
+ case Interpreter::java_lang_math_abs : // fall thru
+ case Interpreter::java_lang_math_log : // fall thru
+ case Interpreter::java_lang_math_log10 : // fall thru
+ case Interpreter::java_lang_math_sqrt : // fall thru
+ case Interpreter::java_lang_math_pow : // fall thru
+ case Interpreter::java_lang_math_exp : entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break;
+ case Interpreter::java_lang_ref_reference_get
+ : entry_point = ((InterpreterGenerator*)this)->generate_Reference_get_entry(); break;
+ default : ShouldNotReachHere(); break;
+ }
+
+ if (entry_point) {
+ return entry_point;
+ }
+
+ return ((InterpreterGenerator*) this)->generate_normal_entry(synchronized);
+}
+
+// These should never be compiled since the interpreter will prefer
+// the compiled version to the intrinsic version.
+bool AbstractInterpreter::can_be_compiled(methodHandle m) {
+ return !math_entry_available(method_kind(m));
+}
+
+// How much stack a method activation needs in stack slots.
+// We must calc this exactly like in generate_fixed_frame.
+// Note: This returns the conservative size assuming maximum alignment.
+int AbstractInterpreter::size_top_interpreter_activation(Method* method) {
+ const int max_alignment_size = 2;
+ const int abi_scratch = frame::abi_reg_args_size;
+ return method->max_locals() + method->max_stack() + frame::interpreter_frame_monitor_size() + max_alignment_size + abi_scratch;
+}
+
+// Fills a sceletal interpreter frame generated during deoptimizations
+// and returns the frame size in slots.
+//
+// Parameters:
+//
+// interpreter_frame == NULL:
+// Only calculate the size of an interpreter activation, no actual layout.
+// Note: This calculation must exactly parallel the frame setup
+// in TemplateInterpreter::generate_normal_entry. But it does not
+// account for the SP alignment, that might further enhance the
+// frame size, depending on FP.
+//
+// interpreter_frame != NULL:
+// set up the method, locals, and monitors.
+// The frame interpreter_frame, if not NULL, is guaranteed to be the
+// right size, as determined by a previous call to this method.
+// It is also guaranteed to be walkable even though it is in a skeletal state
+//
+// is_top_frame == true:
+// We're processing the *oldest* interpreter frame!
+//
+// pop_frame_extra_args:
+// If this is != 0 we are returning to a deoptimized frame by popping
+// off the callee frame. We want to re-execute the call that called the
+// callee interpreted, but since the return to the interpreter would pop
+// the arguments off advance the esp by dummy popframe_extra_args slots.
+// Popping off those will establish the stack layout as it was before the call.
+//
+int AbstractInterpreter::layout_activation(Method* method,
+ int tempcount,
+ int popframe_extra_args,
+ int moncount,
+ int caller_actual_parameters,
+ int callee_param_count,
+ int callee_locals,
+ frame* caller,
+ frame* interpreter_frame,
+ bool is_top_frame,
+ bool is_bottom_frame) {
+
+ const int max_alignment_space = 2;
+ const int abi_scratch = is_top_frame ? (frame::abi_reg_args_size / Interpreter::stackElementSize) :
+ (frame::abi_minframe_size / Interpreter::stackElementSize) ;
+ const int conservative_framesize_in_slots =
+ method->max_stack() + callee_locals - callee_param_count +
+ (moncount * frame::interpreter_frame_monitor_size()) + max_alignment_space +
+ abi_scratch + frame::ijava_state_size / Interpreter::stackElementSize;
+
+ assert(!is_top_frame || conservative_framesize_in_slots * 8 > frame::abi_reg_args_size + frame::ijava_state_size, "frame too small");
+
+ if (interpreter_frame == NULL) {
+ // Since we don't know the exact alignment, we return the conservative size.
+ return (conservative_framesize_in_slots & -2);
+ } else {
+ // Now we know our caller, calc the exact frame layout and size.
+ intptr_t* locals_base = (caller->is_interpreted_frame()) ?
+ caller->interpreter_frame_esp() + caller_actual_parameters :
+ caller->sp() + method->max_locals() - 1 + (frame::abi_minframe_size / Interpreter::stackElementSize) ;
+
+ intptr_t* monitor_base = caller->sp() - frame::ijava_state_size / Interpreter::stackElementSize ;
+ intptr_t* monitor = monitor_base - (moncount * frame::interpreter_frame_monitor_size());
+ intptr_t* esp_base = monitor - 1;
+ intptr_t* esp = esp_base - tempcount - popframe_extra_args;
+ intptr_t* sp = (intptr_t *) (((intptr_t) (esp_base- callee_locals + callee_param_count - method->max_stack()- abi_scratch)) & -StackAlignmentInBytes);
+ intptr_t* sender_sp = caller->sp() + (frame::abi_minframe_size - frame::abi_reg_args_size) / Interpreter::stackElementSize;
+ intptr_t* top_frame_sp = is_top_frame ? sp : sp + (frame::abi_minframe_size - frame::abi_reg_args_size) / Interpreter::stackElementSize;
+
+ interpreter_frame->interpreter_frame_set_method(method);
+ interpreter_frame->interpreter_frame_set_locals(locals_base);
+ interpreter_frame->interpreter_frame_set_cpcache(method->constants()->cache());
+ interpreter_frame->interpreter_frame_set_esp(esp);
+ interpreter_frame->interpreter_frame_set_monitor_end((BasicObjectLock *)monitor);
+ interpreter_frame->interpreter_frame_set_top_frame_sp(top_frame_sp);
+ if (!is_bottom_frame) {
+ interpreter_frame->interpreter_frame_set_sender_sp(sender_sp);
+ }
+
+ int framesize_in_slots = caller->sp() - sp;
+ assert(!is_top_frame ||framesize_in_slots >= (frame::abi_reg_args_size / Interpreter::stackElementSize) + frame::ijava_state_size / Interpreter::stackElementSize, "frame too small");
+ assert(framesize_in_slots <= conservative_framesize_in_slots, "exact frame size must be smaller than the convervative size!");
+ return framesize_in_slots;
+ }
+}
+
+// =============================================================================
+// Exceptions
+
+void TemplateInterpreterGenerator::generate_throw_exception() {
+ Register Rexception = R17_tos,
+ Rcontinuation = R3_RET;
+
+ // --------------------------------------------------------------------------
+ // Entry point if an method returns with a pending exception (rethrow).
+ Interpreter::_rethrow_exception_entry = __ pc();
+ {
+ __ restore_interpreter_state(R11_scratch1); // Sets R11_scratch1 = fp.
+ __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1);
+ __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0);
+
+ // Compiled code destroys templateTableBase, reload.
+ __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R11_scratch1);
+ }
+
+ // Entry point if a interpreted method throws an exception (throw).
+ Interpreter::_throw_exception_entry = __ pc();
+ {
+ __ mr(Rexception, R3_RET);
+
+ __ verify_thread();
+ __ verify_oop(Rexception);
+
+ // Expression stack must be empty before entering the VM in case of an exception.
+ __ empty_expression_stack();
+ // Find exception handler address and preserve exception oop.
+ // Call C routine to find handler and jump to it.
+ __ call_VM(Rexception, CAST_FROM_FN_PTR(address, InterpreterRuntime::exception_handler_for_exception), Rexception);
+ __ mtctr(Rcontinuation);
+ // Push exception for exception handler bytecodes.
+ __ push_ptr(Rexception);
+
+ // Jump to exception handler (may be remove activation entry!).
+ __ bctr();
+ }
+
+ // If the exception is not handled in the current frame the frame is
+ // removed and the exception is rethrown (i.e. exception
+ // continuation is _rethrow_exception).
+ //
+ // Note: At this point the bci is still the bxi for the instruction
+ // which caused the exception and the expression stack is
+ // empty. Thus, for any VM calls at this point, GC will find a legal
+ // oop map (with empty expression stack).
+
+ // In current activation
+ // tos: exception
+ // bcp: exception bcp
+
+ // --------------------------------------------------------------------------
+ // JVMTI PopFrame support
+
+ Interpreter::_remove_activation_preserving_args_entry = __ pc();
+ {
+ // Set the popframe_processing bit in popframe_condition indicating that we are
+ // currently handling popframe, so that call_VMs that may happen later do not
+ // trigger new popframe handling cycles.
+ __ lwz(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);
+ __ ori(R11_scratch1, R11_scratch1, JavaThread::popframe_processing_bit);
+ __ stw(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);
+
+ // Empty the expression stack, as in normal exception handling.
+ __ empty_expression_stack();
+ __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, /* install_monitor_exception */ false);
+
+ // Check to see whether we are returning to a deoptimized frame.
+ // (The PopFrame call ensures that the caller of the popped frame is
+ // either interpreted or compiled and deoptimizes it if compiled.)
+ // Note that we don't compare the return PC against the
+ // deoptimization blob's unpack entry because of the presence of
+ // adapter frames in C2.
+ Label Lcaller_not_deoptimized;
+ Register return_pc = R3_ARG1;
+ __ ld(return_pc, 0, R1_SP);
+ __ ld(return_pc, _abi(lr), return_pc);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, InterpreterRuntime::interpreter_contains), return_pc);
+ __ cmpdi(CCR0, R3_RET, 0);
+ __ bne(CCR0, Lcaller_not_deoptimized);
+
+ // The deoptimized case.
+ // In this case, we can't call dispatch_next() after the frame is
+ // popped, but instead must save the incoming arguments and restore
+ // them after deoptimization has occurred.
+ __ ld(R4_ARG2, in_bytes(Method::const_offset()), R19_method);
+ __ lhz(R4_ARG2 /* number of params */, in_bytes(ConstMethod::size_of_parameters_offset()), R4_ARG2);
+ __ slwi(R4_ARG2, R4_ARG2, Interpreter::logStackElementSize);
+ __ addi(R5_ARG3, R18_locals, Interpreter::stackElementSize);
+ __ subf(R5_ARG3, R4_ARG2, R5_ARG3);
+ // Save these arguments.
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, Deoptimization::popframe_preserve_args), R16_thread, R4_ARG2, R5_ARG3);
+
+ // Inform deoptimization that it is responsible for restoring these arguments.
+ __ load_const_optimized(R11_scratch1, JavaThread::popframe_force_deopt_reexecution_bit);
+ __ stw(R11_scratch1, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);
+
+ // Return from the current method into the deoptimization blob. Will eventually
+ // end up in the deopt interpeter entry, deoptimization prepared everything that
+ // we will reexecute the call that called us.
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*reload return_pc*/ return_pc, R11_scratch1, R12_scratch2);
+ __ mtlr(return_pc);
+ __ blr();
+
+ // The non-deoptimized case.
+ __ bind(Lcaller_not_deoptimized);
+
+ // Clear the popframe condition flag.
+ __ li(R0, 0);
+ __ stw(R0, in_bytes(JavaThread::popframe_condition_offset()), R16_thread);
+
+ // Get out of the current method and re-execute the call that called us.
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ return_pc, R11_scratch1, R12_scratch2);
+ __ restore_interpreter_state(R11_scratch1);
+ __ ld(R12_scratch2, _ijava_state_neg(top_frame_sp), R11_scratch1);
+ __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0);
+ __ mtlr(return_pc);
+ if (ProfileInterpreter) {
+ __ set_method_data_pointer_for_bcp();
+ }
+ __ dispatch_next(vtos);
+ }
+ // end of JVMTI PopFrame support
+
+ // --------------------------------------------------------------------------
+ // Remove activation exception entry.
+ // This is jumped to if an interpreted method can't handle an exception itself
+ // (we come from the throw/rethrow exception entry above). We're going to call
+ // into the VM to find the exception handler in the caller, pop the current
+ // frame and return the handler we calculated.
+ Interpreter::_remove_activation_entry = __ pc();
+ {
+ __ pop_ptr(Rexception);
+ __ verify_thread();
+ __ verify_oop(Rexception);
+ __ std(Rexception, in_bytes(JavaThread::vm_result_offset()), R16_thread);
+
+ __ unlock_if_synchronized_method(vtos, /* throw_monitor_exception */ false, true);
+ __ notify_method_exit(false, vtos, InterpreterMacroAssembler::SkipNotifyJVMTI, false);
+
+ __ get_vm_result(Rexception);
+
+ // We are done with this activation frame; find out where to go next.
+ // The continuation point will be an exception handler, which expects
+ // the following registers set up:
+ //
+ // RET: exception oop
+ // ARG2: Issuing PC (see generate_exception_blob()), only used if the caller is compiled.
+
+ Register return_pc = R31; // Needs to survive the runtime call.
+ __ ld(return_pc, 0, R1_SP);
+ __ ld(return_pc, _abi(lr), return_pc);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::exception_handler_for_return_address), R16_thread, return_pc);
+
+ // Remove the current activation.
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ noreg, R11_scratch1, R12_scratch2);
+
+ __ mr(R4_ARG2, return_pc);
+ __ mtlr(R3_RET);
+ __ mr(R3_RET, Rexception);
+ __ blr();
+ }
+}
+
+// JVMTI ForceEarlyReturn support.
+// Returns "in the middle" of a method with a "fake" return value.
+address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state) {
+
+ Register Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2;
+
+ address entry = __ pc();
+ __ empty_expression_stack();
+
+ __ load_earlyret_value(state, Rscratch1);
+
+ __ ld(Rscratch1, in_bytes(JavaThread::jvmti_thread_state_offset()), R16_thread);
+ // Clear the earlyret state.
+ __ li(R0, 0);
+ __ stw(R0, in_bytes(JvmtiThreadState::earlyret_state_offset()), Rscratch1);
+
+ __ remove_activation(state, false, false);
+ // Copied from TemplateTable::_return.
+ // Restoration of lr done by remove_activation.
+ switch (state) {
+ case ltos:
+ case btos:
+ case ctos:
+ case stos:
+ case atos:
+ case itos: __ mr(R3_RET, R17_tos); break;
+ case ftos:
+ case dtos: __ fmr(F1_RET, F15_ftos); break;
+ case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need
+ // to get visible before the reference to the object gets stored anywhere.
+ __ membar(Assembler::StoreStore); break;
+ default : ShouldNotReachHere();
+ }
+ __ blr();
+
+ return entry;
+} // end of ForceEarlyReturn support
+
+//-----------------------------------------------------------------------------
+// Helper for vtos entry point generation
+
+void TemplateInterpreterGenerator::set_vtos_entry_points(Template* t,
+ address& bep,
+ address& cep,
+ address& sep,
+ address& aep,
+ address& iep,
+ address& lep,
+ address& fep,
+ address& dep,
+ address& vep) {
+ assert(t->is_valid() && t->tos_in() == vtos, "illegal template");
+ Label L;
+
+ aep = __ pc(); __ push_ptr(); __ b(L);
+ fep = __ pc(); __ push_f(); __ b(L);
+ dep = __ pc(); __ push_d(); __ b(L);
+ lep = __ pc(); __ push_l(); __ b(L);
+ __ align(32, 12, 24); // align L
+ bep = cep = sep =
+ iep = __ pc(); __ push_i();
+ vep = __ pc();
+ __ bind(L);
+ generate_and_dispatch(t);
+}
+
+//-----------------------------------------------------------------------------
+// Generation of individual instructions
+
+// helpers for generate_and_dispatch
+
+InterpreterGenerator::InterpreterGenerator(StubQueue* code)
+ : TemplateInterpreterGenerator(code) {
+ generate_all(); // Down here so it can be "virtual".
+}
+
+//-----------------------------------------------------------------------------
+
+// Non-product code
+#ifndef PRODUCT
+address TemplateInterpreterGenerator::generate_trace_code(TosState state) {
+ //__ flush_bundle();
+ address entry = __ pc();
+
+ char *bname = NULL;
+ uint tsize = 0;
+ switch(state) {
+ case ftos:
+ bname = "trace_code_ftos {";
+ tsize = 2;
+ break;
+ case btos:
+ bname = "trace_code_btos {";
+ tsize = 2;
+ break;
+ case ctos:
+ bname = "trace_code_ctos {";
+ tsize = 2;
+ break;
+ case stos:
+ bname = "trace_code_stos {";
+ tsize = 2;
+ break;
+ case itos:
+ bname = "trace_code_itos {";
+ tsize = 2;
+ break;
+ case ltos:
+ bname = "trace_code_ltos {";
+ tsize = 3;
+ break;
+ case atos:
+ bname = "trace_code_atos {";
+ tsize = 2;
+ break;
+ case vtos:
+ // Note: In case of vtos, the topmost of stack value could be a int or doubl
+ // In case of a double (2 slots) we won't see the 2nd stack value.
+ // Maybe we simply should print the topmost 3 stack slots to cope with the problem.
+ bname = "trace_code_vtos {";
+ tsize = 2;
+
+ break;
+ case dtos:
+ bname = "trace_code_dtos {";
+ tsize = 3;
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ BLOCK_COMMENT(bname);
+
+ // Support short-cut for TraceBytecodesAt.
+ // Don't call into the VM if we don't want to trace to speed up things.
+ Label Lskip_vm_call;
+ if (TraceBytecodesAt > 0 && TraceBytecodesAt < max_intx) {
+ int offs1 = __ load_const_optimized(R11_scratch1, (address) &TraceBytecodesAt, R0, true);
+ int offs2 = __ load_const_optimized(R12_scratch2, (address) &BytecodeCounter::_counter_value, R0, true);
+ __ ld(R11_scratch1, offs1, R11_scratch1);
+ __ lwa(R12_scratch2, offs2, R12_scratch2);
+ __ cmpd(CCR0, R12_scratch2, R11_scratch1);
+ __ blt(CCR0, Lskip_vm_call);
+ }
+
+ __ push(state);
+ // Load 2 topmost expression stack values.
+ __ ld(R6_ARG4, tsize*Interpreter::stackElementSize, R15_esp);
+ __ ld(R5_ARG3, Interpreter::stackElementSize, R15_esp);
+ __ mflr(R31);
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, SharedRuntime::trace_bytecode), /* unused */ R4_ARG2, R5_ARG3, R6_ARG4, false);
+ __ mtlr(R31);
+ __ pop(state);
+
+ if (TraceBytecodesAt > 0 && TraceBytecodesAt < max_intx) {
+ __ bind(Lskip_vm_call);
+ }
+ __ blr();
+ BLOCK_COMMENT("} trace_code");
+ return entry;
+}
+
+void TemplateInterpreterGenerator::count_bytecode() {
+ int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeCounter::_counter_value, R12_scratch2, true);
+ __ lwz(R12_scratch2, offs, R11_scratch1);
+ __ addi(R12_scratch2, R12_scratch2, 1);
+ __ stw(R12_scratch2, offs, R11_scratch1);
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode(Template* t) {
+ int offs = __ load_const_optimized(R11_scratch1, (address) &BytecodeHistogram::_counters[t->bytecode()], R12_scratch2, true);
+ __ lwz(R12_scratch2, offs, R11_scratch1);
+ __ addi(R12_scratch2, R12_scratch2, 1);
+ __ stw(R12_scratch2, offs, R11_scratch1);
+}
+
+void TemplateInterpreterGenerator::histogram_bytecode_pair(Template* t) {
+ const Register addr = R11_scratch1,
+ tmp = R12_scratch2;
+ // Get index, shift out old bytecode, bring in new bytecode, and store it.
+ // _index = (_index >> log2_number_of_codes) |
+ // (bytecode << log2_number_of_codes);
+ int offs1 = __ load_const_optimized(addr, (address)&BytecodePairHistogram::_index, tmp, true);
+ __ lwz(tmp, offs1, addr);
+ __ srwi(tmp, tmp, BytecodePairHistogram::log2_number_of_codes);
+ __ ori(tmp, tmp, ((int) t->bytecode()) << BytecodePairHistogram::log2_number_of_codes);
+ __ stw(tmp, offs1, addr);
+
+ // Bump bucket contents.
+ // _counters[_index] ++;
+ int offs2 = __ load_const_optimized(addr, (address)&BytecodePairHistogram::_counters, R0, true);
+ __ sldi(tmp, tmp, LogBytesPerInt);
+ __ add(addr, tmp, addr);
+ __ lwz(tmp, offs2, addr);
+ __ addi(tmp, tmp, 1);
+ __ stw(tmp, offs2, addr);
+}
+
+void TemplateInterpreterGenerator::trace_bytecode(Template* t) {
+ // Call a little run-time stub to avoid blow-up for each bytecode.
+ // The run-time runtime saves the right registers, depending on
+ // the tosca in-state for the given template.
+
+ assert(Interpreter::trace_code(t->tos_in()) != NULL,
+ "entry must have been generated");
+
+ // Note: we destroy LR here.
+ __ bl(Interpreter::trace_code(t->tos_in()));
+}
+
+void TemplateInterpreterGenerator::stop_interpreter_at() {
+ Label L;
+ int offs1 = __ load_const_optimized(R11_scratch1, (address) &StopInterpreterAt, R0, true);
+ int offs2 = __ load_const_optimized(R12_scratch2, (address) &BytecodeCounter::_counter_value, R0, true);
+ __ ld(R11_scratch1, offs1, R11_scratch1);
+ __ lwa(R12_scratch2, offs2, R12_scratch2);
+ __ cmpd(CCR0, R12_scratch2, R11_scratch1);
+ __ bne(CCR0, L);
+ __ illtrap();
+ __ bind(L);
+}
+
+#endif // !PRODUCT
+#endif // !CC_INTERP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/ppc/vm/templateInterpreter_ppc.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, 2014 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.
+ *
+ */
+
+#ifndef CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP
+#define CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP
+
+ protected:
+
+ // Size of interpreter code. Increase if too small. Interpreter will
+ // fail with a guarantee ("not enough space for interpreter generation");
+ // if too small.
+ // Run with +PrintInterpreter to get the VM to print out the size.
+ // Max size with JVMTI
+
+ const static int InterpreterCodeSize = 210*K;
+
+#endif // CPU_PPC_VM_TEMPLATEINTERPRETER_PPC_HPP
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.cpp Mon Mar 10 12:58:02 2014 +0100
@@ -0,0 +1,4082 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, 2014 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/macroAssembler.inline.hpp"
+#include "interpreter/interpreter.hpp"
+#include "interpreter/interpreterRuntime.hpp"
+#include "interpreter/templateInterpreter.hpp"
+#include "interpreter/templateTable.hpp"
+#include "memory/universe.inline.hpp"
+#include "oops/objArrayKlass.hpp"
+#include "oops/oop.inline.hpp"
+#include "prims/methodHandles.hpp"
+#include "runtime/sharedRuntime.hpp"
+#include "runtime/stubRoutines.hpp"
+#include "runtime/synchronizer.hpp"
+#include "utilities/macros.hpp"
+
+#ifndef CC_INTERP
+
+#undef __
+#define __ _masm->
+
+// ============================================================================
+// Misc helpers
+
+// Do an oop store like *(base + index) = val OR *(base + offset) = val
+// (only one of both variants is possible at the same time).
+// Index can be noreg.
+// Kills:
+// Rbase, Rtmp
+static void do_oop_store(InterpreterMacroAssembler* _masm,
+ Register Rbase,
+ RegisterOrConstant offset,
+ Register Rval, // Noreg means always null.
+ Register Rtmp1,
+ Register Rtmp2,
+ Register Rtmp3,
+ BarrierSet::Name barrier,
+ bool precise,
+ bool check_null) {
+ assert_different_registers(Rtmp1, Rtmp2, Rtmp3, Rval, Rbase);
+
+ switch (barrier) {
+#ifndef SERIALGC
+ case BarrierSet::G1SATBCT:
+ case BarrierSet::G1SATBCTLogging:
+ {
+ // Load and record the previous value.
+ __ g1_write_barrier_pre(Rbase, offset,
+ Rtmp3, /* holder of pre_val ? */
+ Rtmp1, Rtmp2, false /* frame */);
+
+ Label Lnull, Ldone;
+ if (Rval != noreg) {
+ if (check_null) {
+ __ cmpdi(CCR0, Rval, 0);
+ __ beq(CCR0, Lnull);
+ }
+ __ store_heap_oop_not_null(Rval, offset, Rbase, /*Rval must stay uncompressed.*/ Rtmp1);
+ // Mark the card.
+ if (!(offset.is_constant() && offset.as_constant() == 0) && precise) {
+ __ add(Rbase, offset, Rbase);
+ }
+ __ g1_write_barrier_post(Rbase, Rval, Rtmp1, Rtmp2, Rtmp3, /*filtered (fast path)*/ &Ldone);
+ if (check_null) { __ b(Ldone); }
+ }
+
+ if (Rval == noreg || check_null) { // Store null oop.
+ Register Rnull = Rval;
+ __ bind(Lnull);
+ if (Rval == noreg) {
+ Rnull = Rtmp1;
+ __ li(Rnull, 0);
+ }
+ if (UseCompressedOops) {
+ __ stw(Rnull, offset, Rbase);
+ } else {
+ __ std(Rnull, offset, Rbase);
+ }
+ }
+ __ bind(Ldone);
+ }
+ break;
+#endif // SERIALGC
+ case BarrierSet::CardTableModRef:
+ case BarrierSet::CardTableExtension:
+ {
+ Label Lnull, Ldone;
+ if (Rval != noreg) {
+ if (check_null) {
+ __ cmpdi(CCR0, Rval, 0);
+ __ beq(CCR0, Lnull);
+ }
+ __ store_heap_oop_not_null(Rval, offset, Rbase, /*Rval should better stay uncompressed.*/ Rtmp1);
+ // Mark the card.
+ if (!(offset.is_constant() && offset.as_constant() == 0) && precise) {
+ __ add(Rbase, offset, Rbase);
+ }
+ __ card_write_barrier_post(Rbase, Rval, Rtmp1);
+ if (check_null) {
+ __ b(Ldone);
+ }
+ }
+
+ if (Rval == noreg || check_null) { // Store null oop.
+ Register Rnull = Rval;
+ __ bind(Lnull);
+ if (Rval == noreg) {
+ Rnull = Rtmp1;
+ __ li(Rnull, 0);
+ }
+ if (UseCompressedOops) {
+ __ stw(Rnull, offset, Rbase);
+ } else {
+ __ std(Rnull, offset, Rbase);
+ }
+ }
+ __ bind(Ldone);
+ }
+ break;
+ case BarrierSet::ModRef:
+ case BarrierSet::Other:
+ ShouldNotReachHere();
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+}
+
+// ============================================================================
+// Platform-dependent initialization
+
+void TemplateTable::pd_initialize() {
+ // No ppc64 specific initialization.
+}
+
+Address TemplateTable::at_bcp(int offset) {
+ // Not used on ppc.
+ ShouldNotReachHere();
+ return Address();
+}
+
+// Patches the current bytecode (ptr to it located in bcp)
+// in the bytecode stream with a new one.
+void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Register Rtemp, bool load_bc_into_bc_reg /*=true*/, int byte_no) {
+ // With sharing on, may need to test method flag.
+ if (!RewriteBytecodes) return;
+ Label L_patch_done;
+
+ switch (new_bc) {
+ case Bytecodes::_fast_aputfield:
+ case Bytecodes::_fast_bputfield:
+ case Bytecodes::_fast_cputfield:
+ case Bytecodes::_fast_dputfield:
+ case Bytecodes::_fast_fputfield:
+ case Bytecodes::_fast_iputfield:
+ case Bytecodes::_fast_lputfield:
+ case Bytecodes::_fast_sputfield:
+ {
+ // We skip bytecode quickening for putfield instructions when
+ // the put_code written to the constant pool cache is zero.
+ // This is required so that every execution of this instruction
+ // calls out to InterpreterRuntime::resolve_get_put to do
+ // additional, required work.
+ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range");
+ assert(load_bc_into_bc_reg, "we use bc_reg as temp");
+ __ get_cache_and_index_at_bcp(Rtemp /* dst = cache */, 1);
+ // Big Endian: ((*(cache+indices))>>((1+byte_no)*8))&0xFF
+ __ lbz(Rnew_bc, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (1 + byte_no), Rtemp);
+ __ cmpwi(CCR0, Rnew_bc, 0);
+ __ li(Rnew_bc, (unsigned int)(unsigned char)new_bc);
+ __ beq(CCR0, L_patch_done);
+ // __ isync(); // acquire not needed
+ break;
+ }
+
+ default:
+ assert(byte_no == -1, "sanity");
+ if (load_bc_into_bc_reg) {
+ __ li(Rnew_bc, (unsigned int)(unsigned char)new_bc);
+ }
+ }
+
+ if (JvmtiExport::can_post_breakpoint()) {
+ Label L_fast_patch;
+ __ lbz(Rtemp, 0, R14_bcp);
+ __ cmpwi(CCR0, Rtemp, (unsigned int)(unsigned char)Bytecodes::_breakpoint);
+ __ bne(CCR0, L_fast_patch);
+ // Perform the quickening, slowly, in the bowels of the breakpoint table.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::set_original_bytecode_at), R19_method, R14_bcp, Rnew_bc);
+ __ b(L_patch_done);
+ __ bind(L_fast_patch);
+ }
+
+ // Patch bytecode.
+ __ stb(Rnew_bc, 0, R14_bcp);
+
+ __ bind(L_patch_done);
+}
+
+// ============================================================================
+// Individual instructions
+
+void TemplateTable::nop() {
+ transition(vtos, vtos);
+ // Nothing to do.
+}
+
+void TemplateTable::shouldnotreachhere() {
+ transition(vtos, vtos);
+ __ stop("shouldnotreachhere bytecode");
+}
+
+void TemplateTable::aconst_null() {
+ transition(vtos, atos);
+ __ li(R17_tos, 0);
+}
+
+void TemplateTable::iconst(int value) {
+ transition(vtos, itos);
+ assert(value >= -1 && value <= 5, "");
+ __ li(R17_tos, value);
+}
+
+void TemplateTable::lconst(int value) {
+ transition(vtos, ltos);
+ assert(value >= -1 && value <= 5, "");
+ __ li(R17_tos, value);
+}
+
+void TemplateTable::fconst(int value) {
+ transition(vtos, ftos);
+ static float zero = 0.0;
+ static float one = 1.0;
+ static float two = 2.0;
+ switch (value) {
+ default: ShouldNotReachHere();
+ case 0: {
+ int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&zero, R0);
+ __ lfs(F15_ftos, simm16_offset, R11_scratch1);
+ break;
+ }
+ case 1: {
+ int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&one, R0);
+ __ lfs(F15_ftos, simm16_offset, R11_scratch1);
+ break;
+ }
+ case 2: {
+ int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&two, R0);
+ __ lfs(F15_ftos, simm16_offset, R11_scratch1);
+ break;
+ }
+ }
+}
+
+void TemplateTable::dconst(int value) {
+ transition(vtos, dtos);
+ static double zero = 0.0;
+ static double one = 1.0;
+ switch (value) {
+ case 0: {
+ int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&zero, R0);
+ __ lfd(F15_ftos, simm16_offset, R11_scratch1);
+ break;
+ }
+ case 1: {
+ int simm16_offset = __ load_const_optimized(R11_scratch1, (address*)&one, R0);
+ __ lfd(F15_ftos, simm16_offset, R11_scratch1);
+ break;
+ }
+ default: ShouldNotReachHere();
+ }
+}
+
+void TemplateTable::bipush() {
+ transition(vtos, itos);
+ __ lbz(R17_tos, 1, R14_bcp);
+ __ extsb(R17_tos, R17_tos);
+}
+
+void TemplateTable::sipush() {
+ transition(vtos, itos);
+ __ get_2_byte_integer_at_bcp(1, R17_tos, InterpreterMacroAssembler::Signed);
+}
+
+void TemplateTable::ldc(bool wide) {
+ Register Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rcpool = R3_ARG1;
+
+ transition(vtos, vtos);
+ Label notInt, notClass, exit;
+
+ __ get_cpool_and_tags(Rcpool, Rscratch2); // Set Rscratch2 = &tags.
+ if (wide) { // Read index.
+ __ get_2_byte_integer_at_bcp(1, Rscratch1, InterpreterMacroAssembler::Unsigned);
+ } else {
+ __ lbz(Rscratch1, 1, R14_bcp);
+ }
+
+ const int base_offset = ConstantPool::header_size() * wordSize;
+ const int tags_offset = Array<u1>::base_offset_in_bytes();
+
+ // Get type from tags.
+ __ addi(Rscratch2, Rscratch2, tags_offset);
+ __ lbzx(Rscratch2, Rscratch2, Rscratch1);
+
+ __ cmpwi(CCR0, Rscratch2, JVM_CONSTANT_UnresolvedClass); // Unresolved class?
+ __ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_UnresolvedClassInError); // Unresolved class in error state?
+ __ cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
+
+ // Resolved class - need to call vm to get java mirror of the class.
+ __ cmpwi(CCR1, Rscratch2, JVM_CONSTANT_Class);
+ __ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // Neither resolved class nor unresolved case from above?
+ __ beq(CCR0, notClass);
+
+ __ li(R4, wide ? 1 : 0);
+ call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::ldc), R4);
+ __ push(atos);
+ __ b(exit);
+
+ __ align(32, 12);
+ __ bind(notClass);
+ __ addi(Rcpool, Rcpool, base_offset);
+ __ sldi(Rscratch1, Rscratch1, LogBytesPerWord);
+ __ cmpdi(CCR0, Rscratch2, JVM_CONSTANT_Integer);
+ __ bne(CCR0, notInt);
+ __ isync(); // Order load of constant wrt. tags.
+ __ lwax(R17_tos, Rcpool, Rscratch1);
+ __ push(itos);
+ __ b(exit);
+
+ __ align(32, 12);
+ __ bind(notInt);
+#ifdef ASSERT
+ // String and Object are rewritten to fast_aldc
+ __ cmpdi(CCR0, Rscratch2, JVM_CONSTANT_Float);
+ __ asm_assert_eq("unexpected type", 0x8765);
+#endif
+ __ isync(); // Order load of constant wrt. tags.
+ __ lfsx(F15_ftos, Rcpool, Rscratch1);
+ __ push(ftos);
+
+ __ align(32, 12);
+ __ bind(exit);
+}
+
+// Fast path for caching oop constants.
+void TemplateTable::fast_aldc(bool wide) {
+ transition(vtos, atos);
+
+ int index_size = wide ? sizeof(u2) : sizeof(u1);
+ const Register Rscratch = R11_scratch1;
+ Label resolved;
+
+ // We are resolved if the resolved reference cache entry contains a
+ // non-null object (CallSite, etc.)
+ __ get_cache_index_at_bcp(Rscratch, 1, index_size); // Load index.
+ __ load_resolved_reference_at_index(R17_tos, Rscratch);
+ __ cmpdi(CCR0, R17_tos, 0);
+ __ bne(CCR0, resolved);
+ __ load_const_optimized(R3_ARG1, (int)bytecode());
+
+ address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc);
+
+ // First time invocation - must resolve first.
+ __ call_VM(R17_tos, entry, R3_ARG1);
+
+ __ align(32, 12);
+ __ bind(resolved);
+ __ verify_oop(R17_tos);
+}
+
+void TemplateTable::ldc2_w() {
+ transition(vtos, vtos);
+ Label Llong, Lexit;
+
+ Register Rindex = R11_scratch1,
+ Rcpool = R12_scratch2,
+ Rtag = R3_ARG1;
+ __ get_cpool_and_tags(Rcpool, Rtag);
+ __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned);
+
+ const int base_offset = ConstantPool::header_size() * wordSize;
+ const int tags_offset = Array<u1>::base_offset_in_bytes();
+ // Get type from tags.
+ __ addi(Rcpool, Rcpool, base_offset);
+ __ addi(Rtag, Rtag, tags_offset);
+
+ __ lbzx(Rtag, Rtag, Rindex);
+
+ __ sldi(Rindex, Rindex, LogBytesPerWord);
+ __ cmpdi(CCR0, Rtag, JVM_CONSTANT_Double);
+ __ bne(CCR0, Llong);
+ // A double can be placed at word-aligned locations in the constant pool.
+ // Check out Conversions.java for an example.
+ // Also ConstantPool::header_size() is 20, which makes it very difficult
+ // to double-align double on the constant pool. SG, 11/7/97
+ __ isync(); // Order load of constant wrt. tags.
+ __ lfdx(F15_ftos, Rcpool, Rindex);
+ __ push(dtos);
+ __ b(Lexit);
+
+ __ bind(Llong);
+ __ isync(); // Order load of constant wrt. tags.
+ __ ldx(R17_tos, Rcpool, Rindex);
+ __ push(ltos);
+
+ __ bind(Lexit);
+}
+
+// Get the locals index located in the bytecode stream at bcp + offset.
+void TemplateTable::locals_index(Register Rdst, int offset) {
+ __ lbz(Rdst, offset, R14_bcp);
+}
+
+void TemplateTable::iload() {
+ transition(vtos, itos);
+
+ // Get the local value into tos
+ const Register Rindex = R22_tmp2;
+ locals_index(Rindex);
+
+ // Rewrite iload,iload pair into fast_iload2
+ // iload,caload pair into fast_icaload
+ if (RewriteFrequentPairs) {
+ Label Lrewrite, Ldone;
+ Register Rnext_byte = R3_ARG1,
+ Rrewrite_to = R6_ARG4,
+ Rscratch = R11_scratch1;
+
+ // get next byte
+ __ lbz(Rnext_byte, Bytecodes::length_for(Bytecodes::_iload), R14_bcp);
+
+ // if _iload, wait to rewrite to iload2. We only want to rewrite the
+ // last two iloads in a pair. Comparing against fast_iload means that
+ // the next bytecode is neither an iload or a caload, and therefore
+ // an iload pair.
+ __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_iload);
+ __ beq(CCR0, Ldone);
+
+ __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_iload);
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iload2);
+ __ beq(CCR1, Lrewrite);
+
+ __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_caload);
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_icaload);
+ __ beq(CCR0, Lrewrite);
+
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iload);
+
+ __ bind(Lrewrite);
+ patch_bytecode(Bytecodes::_iload, Rrewrite_to, Rscratch, false);
+ __ bind(Ldone);
+ }
+
+ __ load_local_int(R17_tos, Rindex, Rindex);
+}
+
+// Load 2 integers in a row without dispatching
+void TemplateTable::fast_iload2() {
+ transition(vtos, itos);
+
+ __ lbz(R3_ARG1, 1, R14_bcp);
+ __ lbz(R17_tos, Bytecodes::length_for(Bytecodes::_iload) + 1, R14_bcp);
+
+ __ load_local_int(R3_ARG1, R11_scratch1, R3_ARG1);
+ __ load_local_int(R17_tos, R12_scratch2, R17_tos);
+ __ push_i(R3_ARG1);
+}
+
+void TemplateTable::fast_iload() {
+ transition(vtos, itos);
+ // Get the local value into tos
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ load_local_int(R17_tos, Rindex, Rindex);
+}
+
+// Load a local variable type long from locals area to TOS cache register.
+// Local index resides in bytecodestream.
+void TemplateTable::lload() {
+ transition(vtos, ltos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ load_local_long(R17_tos, Rindex, Rindex);
+}
+
+void TemplateTable::fload() {
+ transition(vtos, ftos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ load_local_float(F15_ftos, Rindex, Rindex);
+}
+
+void TemplateTable::dload() {
+ transition(vtos, dtos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ load_local_double(F15_ftos, Rindex, Rindex);
+}
+
+void TemplateTable::aload() {
+ transition(vtos, atos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ load_local_ptr(R17_tos, Rindex, Rindex);
+}
+
+void TemplateTable::locals_index_wide(Register Rdst) {
+ // Offset is 2, not 1, because Lbcp points to wide prefix code.
+ __ get_2_byte_integer_at_bcp(2, Rdst, InterpreterMacroAssembler::Unsigned);
+}
+
+void TemplateTable::wide_iload() {
+ // Get the local value into tos.
+
+ const Register Rindex = R11_scratch1;
+ locals_index_wide(Rindex);
+ __ load_local_int(R17_tos, Rindex, Rindex);
+}
+
+void TemplateTable::wide_lload() {
+ transition(vtos, ltos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index_wide(Rindex);
+ __ load_local_long(R17_tos, Rindex, Rindex);
+}
+
+void TemplateTable::wide_fload() {
+ transition(vtos, ftos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index_wide(Rindex);
+ __ load_local_float(F15_ftos, Rindex, Rindex);
+}
+
+void TemplateTable::wide_dload() {
+ transition(vtos, dtos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index_wide(Rindex);
+ __ load_local_double(F15_ftos, Rindex, Rindex);
+}
+
+void TemplateTable::wide_aload() {
+ transition(vtos, atos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index_wide(Rindex);
+ __ load_local_ptr(R17_tos, Rindex, Rindex);
+}
+
+void TemplateTable::iaload() {
+ transition(itos, itos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerInt, Rtemp, Rload_addr);
+ __ lwa(R17_tos, arrayOopDesc::base_offset_in_bytes(T_INT), Rload_addr);
+}
+
+void TemplateTable::laload() {
+ transition(itos, ltos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerLong, Rtemp, Rload_addr);
+ __ ld(R17_tos, arrayOopDesc::base_offset_in_bytes(T_LONG), Rload_addr);
+}
+
+void TemplateTable::faload() {
+ transition(itos, ftos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerInt, Rtemp, Rload_addr);
+ __ lfs(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_FLOAT), Rload_addr);
+}
+
+void TemplateTable::daload() {
+ transition(itos, dtos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerLong, Rtemp, Rload_addr);
+ __ lfd(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_DOUBLE), Rload_addr);
+}
+
+void TemplateTable::aaload() {
+ transition(itos, atos);
+
+ // tos: index
+ // result tos: array
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, UseCompressedOops ? 2 : LogBytesPerWord, Rtemp, Rload_addr);
+ __ load_heap_oop(R17_tos, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Rload_addr);
+ __ verify_oop(R17_tos);
+ //__ dcbt(R17_tos); // prefetch
+}
+
+void TemplateTable::baload() {
+ transition(itos, itos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, 0, Rtemp, Rload_addr);
+ __ lbz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_BYTE), Rload_addr);
+ __ extsb(R17_tos, R17_tos);
+}
+
+void TemplateTable::caload() {
+ transition(itos, itos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R5_ARG3;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr);
+ __ lhz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rload_addr);
+}
+
+// Iload followed by caload frequent pair.
+void TemplateTable::fast_icaload() {
+ transition(vtos, itos);
+
+ const Register Rload_addr = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rtemp = R11_scratch1;
+
+ locals_index(R17_tos);
+ __ load_local_int(R17_tos, Rtemp, R17_tos);
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr);
+ __ lhz(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rload_addr);
+}
+
+void TemplateTable::saload() {
+ transition(itos, itos);
+
+ const Register Rload_addr = R11_scratch1,
+ Rarray = R12_scratch2,
+ Rtemp = R3_ARG1;
+ __ index_check(Rarray, R17_tos /* index */, LogBytesPerShort, Rtemp, Rload_addr);
+ __ lha(R17_tos, arrayOopDesc::base_offset_in_bytes(T_SHORT), Rload_addr);
+}
+
+void TemplateTable::iload(int n) {
+ transition(vtos, itos);
+
+ __ lwz(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::lload(int n) {
+ transition(vtos, ltos);
+
+ __ ld(R17_tos, Interpreter::local_offset_in_bytes(n + 1), R18_locals);
+}
+
+void TemplateTable::fload(int n) {
+ transition(vtos, ftos);
+
+ __ lfs(F15_ftos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::dload(int n) {
+ transition(vtos, dtos);
+
+ __ lfd(F15_ftos, Interpreter::local_offset_in_bytes(n + 1), R18_locals);
+}
+
+void TemplateTable::aload(int n) {
+ transition(vtos, atos);
+
+ __ ld(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::aload_0() {
+ transition(vtos, atos);
+ // According to bytecode histograms, the pairs:
+ //
+ // _aload_0, _fast_igetfield
+ // _aload_0, _fast_agetfield
+ // _aload_0, _fast_fgetfield
+ //
+ // occur frequently. If RewriteFrequentPairs is set, the (slow)
+ // _aload_0 bytecode checks if the next bytecode is either
+ // _fast_igetfield, _fast_agetfield or _fast_fgetfield and then
+ // rewrites the current bytecode into a pair bytecode; otherwise it
+ // rewrites the current bytecode into _0 that doesn't do
+ // the pair check anymore.
+ //
+ // Note: If the next bytecode is _getfield, the rewrite must be
+ // delayed, otherwise we may miss an opportunity for a pair.
+ //
+ // Also rewrite frequent pairs
+ // aload_0, aload_1
+ // aload_0, iload_1
+ // These bytecodes with a small amount of code are most profitable
+ // to rewrite.
+
+ if (RewriteFrequentPairs) {
+
+ Label Lrewrite, Ldont_rewrite;
+ Register Rnext_byte = R3_ARG1,
+ Rrewrite_to = R6_ARG4,
+ Rscratch = R11_scratch1;
+
+ // Get next byte.
+ __ lbz(Rnext_byte, Bytecodes::length_for(Bytecodes::_aload_0), R14_bcp);
+
+ // If _getfield, wait to rewrite. We only want to rewrite the last two bytecodes in a pair.
+ __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_getfield);
+ __ beq(CCR0, Ldont_rewrite);
+
+ __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_igetfield);
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_iaccess_0);
+ __ beq(CCR1, Lrewrite);
+
+ __ cmpwi(CCR0, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_agetfield);
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_aaccess_0);
+ __ beq(CCR0, Lrewrite);
+
+ __ cmpwi(CCR1, Rnext_byte, (unsigned int)(unsigned char)Bytecodes::_fast_fgetfield);
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_faccess_0);
+ __ beq(CCR1, Lrewrite);
+
+ __ li(Rrewrite_to, (unsigned int)(unsigned char)Bytecodes::_fast_aload_0);
+
+ __ bind(Lrewrite);
+ patch_bytecode(Bytecodes::_aload_0, Rrewrite_to, Rscratch, false);
+ __ bind(Ldont_rewrite);
+ }
+
+ // Do actual aload_0 (must do this after patch_bytecode which might call VM and GC might change oop).
+ aload(0);
+}
+
+void TemplateTable::istore() {
+ transition(itos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ store_local_int(R17_tos, Rindex);
+}
+
+void TemplateTable::lstore() {
+ transition(ltos, vtos);
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ store_local_long(R17_tos, Rindex);
+}
+
+void TemplateTable::fstore() {
+ transition(ftos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ store_local_float(F15_ftos, Rindex);
+}
+
+void TemplateTable::dstore() {
+ transition(dtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ locals_index(Rindex);
+ __ store_local_double(F15_ftos, Rindex);
+}
+
+void TemplateTable::astore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_ptr();
+ __ verify_oop_or_return_address(R17_tos, Rindex);
+ locals_index(Rindex);
+ __ store_local_ptr(R17_tos, Rindex);
+}
+
+void TemplateTable::wide_istore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_i();
+ locals_index_wide(Rindex);
+ __ store_local_int(R17_tos, Rindex);
+}
+
+void TemplateTable::wide_lstore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_l();
+ locals_index_wide(Rindex);
+ __ store_local_long(R17_tos, Rindex);
+}
+
+void TemplateTable::wide_fstore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_f();
+ locals_index_wide(Rindex);
+ __ store_local_float(F15_ftos, Rindex);
+}
+
+void TemplateTable::wide_dstore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_d();
+ locals_index_wide(Rindex);
+ __ store_local_double(F15_ftos, Rindex);
+}
+
+void TemplateTable::wide_astore() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1;
+ __ pop_ptr();
+ __ verify_oop_or_return_address(R17_tos, Rindex);
+ locals_index_wide(Rindex);
+ __ store_local_ptr(R17_tos, Rindex);
+}
+
+void TemplateTable::iastore() {
+ transition(itos, vtos);
+
+ const Register Rindex = R3_ARG1,
+ Rstore_addr = R4_ARG2,
+ Rarray = R5_ARG3,
+ Rtemp = R6_ARG4;
+ __ pop_i(Rindex);
+ __ index_check(Rarray, Rindex, LogBytesPerInt, Rtemp, Rstore_addr);
+ __ stw(R17_tos, arrayOopDesc::base_offset_in_bytes(T_INT), Rstore_addr);
+ }
+
+void TemplateTable::lastore() {
+ transition(ltos, vtos);
+
+ const Register Rindex = R3_ARG1,
+ Rstore_addr = R4_ARG2,
+ Rarray = R5_ARG3,
+ Rtemp = R6_ARG4;
+ __ pop_i(Rindex);
+ __ index_check(Rarray, Rindex, LogBytesPerLong, Rtemp, Rstore_addr);
+ __ std(R17_tos, arrayOopDesc::base_offset_in_bytes(T_LONG), Rstore_addr);
+ }
+
+void TemplateTable::fastore() {
+ transition(ftos, vtos);
+
+ const Register Rindex = R3_ARG1,
+ Rstore_addr = R4_ARG2,
+ Rarray = R5_ARG3,
+ Rtemp = R6_ARG4;
+ __ pop_i(Rindex);
+ __ index_check(Rarray, Rindex, LogBytesPerInt, Rtemp, Rstore_addr);
+ __ stfs(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_FLOAT), Rstore_addr);
+ }
+
+void TemplateTable::dastore() {
+ transition(dtos, vtos);
+
+ const Register Rindex = R3_ARG1,
+ Rstore_addr = R4_ARG2,
+ Rarray = R5_ARG3,
+ Rtemp = R6_ARG4;
+ __ pop_i(Rindex);
+ __ index_check(Rarray, Rindex, LogBytesPerLong, Rtemp, Rstore_addr);
+ __ stfd(F15_ftos, arrayOopDesc::base_offset_in_bytes(T_DOUBLE), Rstore_addr);
+ }
+
+// Pop 3 values from the stack and...
+void TemplateTable::aastore() {
+ transition(vtos, vtos);
+
+ Label Lstore_ok, Lis_null, Ldone;
+ const Register Rindex = R3_ARG1,
+ Rarray = R4_ARG2,
+ Rscratch = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rarray_klass = R5_ARG3,
+ Rarray_element_klass = Rarray_klass,
+ Rvalue_klass = R6_ARG4,
+ Rstore_addr = R31; // Use register which survives VM call.
+
+ __ ld(R17_tos, Interpreter::expr_offset_in_bytes(0), R15_esp); // Get value to store.
+ __ lwz(Rindex, Interpreter::expr_offset_in_bytes(1), R15_esp); // Get index.
+ __ ld(Rarray, Interpreter::expr_offset_in_bytes(2), R15_esp); // Get array.
+
+ __ verify_oop(R17_tos);
+ __ index_check_without_pop(Rarray, Rindex, UseCompressedOops ? 2 : LogBytesPerWord, Rscratch, Rstore_addr);
+ // Rindex is dead!
+ Register Rscratch3 = Rindex;
+
+ // Do array store check - check for NULL value first.
+ __ cmpdi(CCR0, R17_tos, 0);
+ __ beq(CCR0, Lis_null);
+
+ __ load_klass(Rarray_klass, Rarray);
+ __ load_klass(Rvalue_klass, R17_tos);
+
+ // Do fast instanceof cache test.
+ __ ld(Rarray_element_klass, in_bytes(ObjArrayKlass::element_klass_offset()), Rarray_klass);
+
+ // Generate a fast subtype check. Branch to store_ok if no failure. Throw if failure.
+ __ gen_subtype_check(Rvalue_klass /*subklass*/, Rarray_element_klass /*superklass*/, Rscratch, Rscratch2, Rscratch3, Lstore_ok);
+
+ // Fell through: subtype check failed => throw an exception.
+ __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArrayStoreException_entry);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ __ bind(Lis_null);
+ do_oop_store(_masm, Rstore_addr, arrayOopDesc::base_offset_in_bytes(T_OBJECT), noreg /* 0 */,
+ Rscratch, Rscratch2, Rscratch3, _bs->kind(), true /* precise */, false /* check_null */);
+ __ profile_null_seen(Rscratch, Rscratch2);
+ __ b(Ldone);
+
+ // Store is OK.
+ __ bind(Lstore_ok);
+ do_oop_store(_masm, Rstore_addr, arrayOopDesc::base_offset_in_bytes(T_OBJECT), R17_tos /* value */,
+ Rscratch, Rscratch2, Rscratch3, _bs->kind(), true /* precise */, false /* check_null */);
+
+ __ bind(Ldone);
+ // Adjust sp (pops array, index and value).
+ __ addi(R15_esp, R15_esp, 3 * Interpreter::stackElementSize);
+}
+
+void TemplateTable::bastore() {
+ transition(itos, vtos);
+
+ const Register Rindex = R11_scratch1,
+ Rarray = R12_scratch2,
+ Rscratch = R3_ARG1;
+ __ pop_i(Rindex);
+ // tos: val
+ // Rarray: array ptr (popped by index_check)
+ __ index_check(Rarray, Rindex, 0, Rscratch, Rarray);
+ __ stb(R17_tos, arrayOopDesc::base_offset_in_bytes(T_BYTE), Rarray);
+}
+
+void TemplateTable::castore() {
+ transition(itos, vtos);
+
+ const Register Rindex = R11_scratch1,
+ Rarray = R12_scratch2,
+ Rscratch = R3_ARG1;
+ __ pop_i(Rindex);
+ // tos: val
+ // Rarray: array ptr (popped by index_check)
+ __ index_check(Rarray, Rindex, LogBytesPerShort, Rscratch, Rarray);
+ __ sth(R17_tos, arrayOopDesc::base_offset_in_bytes(T_CHAR), Rarray);
+}
+
+void TemplateTable::sastore() {
+ castore();
+}
+
+void TemplateTable::istore(int n) {
+ transition(itos, vtos);
+ __ stw(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::lstore(int n) {
+ transition(ltos, vtos);
+ __ std(R17_tos, Interpreter::local_offset_in_bytes(n + 1), R18_locals);
+}
+
+void TemplateTable::fstore(int n) {
+ transition(ftos, vtos);
+ __ stfs(F15_ftos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::dstore(int n) {
+ transition(dtos, vtos);
+ __ stfd(F15_ftos, Interpreter::local_offset_in_bytes(n + 1), R18_locals);
+}
+
+void TemplateTable::astore(int n) {
+ transition(vtos, vtos);
+
+ __ pop_ptr();
+ __ verify_oop_or_return_address(R17_tos, R11_scratch1);
+ __ std(R17_tos, Interpreter::local_offset_in_bytes(n), R18_locals);
+}
+
+void TemplateTable::pop() {
+ transition(vtos, vtos);
+
+ __ addi(R15_esp, R15_esp, Interpreter::stackElementSize);
+}
+
+void TemplateTable::pop2() {
+ transition(vtos, vtos);
+
+ __ addi(R15_esp, R15_esp, Interpreter::stackElementSize * 2);
+}
+
+void TemplateTable::dup() {
+ transition(vtos, vtos);
+
+ __ ld(R11_scratch1, Interpreter::stackElementSize, R15_esp);
+ __ push_ptr(R11_scratch1);
+}
+
+void TemplateTable::dup_x1() {
+ transition(vtos, vtos);
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2;
+ // stack: ..., a, b
+ __ ld(Rb, Interpreter::stackElementSize, R15_esp);
+ __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Rb, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Ra, Interpreter::stackElementSize, R15_esp);
+ __ push_ptr(Rb);
+ // stack: ..., b, a, b
+}
+
+void TemplateTable::dup_x2() {
+ transition(vtos, vtos);
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2,
+ Rc = R3_ARG1;
+
+ // stack: ..., a, b, c
+ __ ld(Rc, Interpreter::stackElementSize, R15_esp); // load c
+ __ ld(Ra, Interpreter::stackElementSize * 3, R15_esp); // load a
+ __ std(Rc, Interpreter::stackElementSize * 3, R15_esp); // store c in a
+ __ ld(Rb, Interpreter::stackElementSize * 2, R15_esp); // load b
+ // stack: ..., c, b, c
+ __ std(Ra, Interpreter::stackElementSize * 2, R15_esp); // store a in b
+ // stack: ..., c, a, c
+ __ std(Rb, Interpreter::stackElementSize, R15_esp); // store b in c
+ __ push_ptr(Rc); // push c
+ // stack: ..., c, a, b, c
+}
+
+void TemplateTable::dup2() {
+ transition(vtos, vtos);
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2;
+ // stack: ..., a, b
+ __ ld(Rb, Interpreter::stackElementSize, R15_esp);
+ __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp);
+ __ push_2ptrs(Ra, Rb);
+ // stack: ..., a, b, a, b
+}
+
+void TemplateTable::dup2_x1() {
+ transition(vtos, vtos);
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2,
+ Rc = R3_ARG1;
+ // stack: ..., a, b, c
+ __ ld(Rc, Interpreter::stackElementSize, R15_esp);
+ __ ld(Rb, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Rc, Interpreter::stackElementSize * 2, R15_esp);
+ __ ld(Ra, Interpreter::stackElementSize * 3, R15_esp);
+ __ std(Ra, Interpreter::stackElementSize, R15_esp);
+ __ std(Rb, Interpreter::stackElementSize * 3, R15_esp);
+ // stack: ..., b, c, a
+ __ push_2ptrs(Rb, Rc);
+ // stack: ..., b, c, a, b, c
+}
+
+void TemplateTable::dup2_x2() {
+ transition(vtos, vtos);
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2,
+ Rc = R3_ARG1,
+ Rd = R4_ARG2;
+ // stack: ..., a, b, c, d
+ __ ld(Rb, Interpreter::stackElementSize * 3, R15_esp);
+ __ ld(Rd, Interpreter::stackElementSize, R15_esp);
+ __ std(Rb, Interpreter::stackElementSize, R15_esp); // store b in d
+ __ std(Rd, Interpreter::stackElementSize * 3, R15_esp); // store d in b
+ __ ld(Ra, Interpreter::stackElementSize * 4, R15_esp);
+ __ ld(Rc, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Ra, Interpreter::stackElementSize * 2, R15_esp); // store a in c
+ __ std(Rc, Interpreter::stackElementSize * 4, R15_esp); // store c in a
+ // stack: ..., c, d, a, b
+ __ push_2ptrs(Rc, Rd);
+ // stack: ..., c, d, a, b, c, d
+}
+
+void TemplateTable::swap() {
+ transition(vtos, vtos);
+ // stack: ..., a, b
+
+ Register Ra = R11_scratch1,
+ Rb = R12_scratch2;
+ // stack: ..., a, b
+ __ ld(Rb, Interpreter::stackElementSize, R15_esp);
+ __ ld(Ra, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Rb, Interpreter::stackElementSize * 2, R15_esp);
+ __ std(Ra, Interpreter::stackElementSize, R15_esp);
+ // stack: ..., b, a
+}
+
+void TemplateTable::iop2(Operation op) {
+ transition(itos, itos);
+
+ Register Rscratch = R11_scratch1;
+
+ __ pop_i(Rscratch);
+ // tos = number of bits to shift
+ // Rscratch = value to shift
+ switch (op) {
+ case add: __ add(R17_tos, Rscratch, R17_tos); break;
+ case sub: __ sub(R17_tos, Rscratch, R17_tos); break;
+ case mul: __ mullw(R17_tos, Rscratch, R17_tos); break;
+ case _and: __ andr(R17_tos, Rscratch, R17_tos); break;
+ case _or: __ orr(R17_tos, Rscratch, R17_tos); break;
+ case _xor: __ xorr(R17_tos, Rscratch, R17_tos); break;
+ case shl: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ slw(R17_tos, Rscratch, R17_tos); break;
+ case shr: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ sraw(R17_tos, Rscratch, R17_tos); break;
+ case ushr: __ rldicl(R17_tos, R17_tos, 0, 64-5); __ srw(R17_tos, Rscratch, R17_tos); break;
+ default: ShouldNotReachHere();
+ }
+}
+
+void TemplateTable::lop2(Operation op) {
+ transition(ltos, ltos);
+
+ Register Rscratch = R11_scratch1;
+ __ pop_l(Rscratch);
+ switch (op) {
+ case add: __ add(R17_tos, Rscratch, R17_tos); break;
+ case sub: __ sub(R17_tos, Rscratch, R17_tos); break;
+ case _and: __ andr(R17_tos, Rscratch, R17_tos); break;
+ case _or: __ orr(R17_tos, Rscratch, R17_tos); break;
+ case _xor: __ xorr(R17_tos, Rscratch, R17_tos); break;
+ default: ShouldNotReachHere();
+ }
+}
+
+void TemplateTable::idiv() {
+ transition(itos, itos);
+
+ Label Lnormal, Lexception, Ldone;
+ Register Rdividend = R11_scratch1; // Used by irem.
+
+ __ addi(R0, R17_tos, 1);
+ __ cmplwi(CCR0, R0, 2);
+ __ bgt(CCR0, Lnormal); // divisor <-1 or >1
+
+ __ cmpwi(CCR1, R17_tos, 0);
+ __ beq(CCR1, Lexception); // divisor == 0
+
+ __ pop_i(Rdividend);
+ __ mullw(R17_tos, Rdividend, R17_tos); // div by +/-1
+ __ b(Ldone);
+
+ __ bind(Lexception);
+ __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArithmeticException_entry);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ __ align(32, 12);
+ __ bind(Lnormal);
+ __ pop_i(Rdividend);
+ __ divw(R17_tos, Rdividend, R17_tos); // Can't divide minint/-1.
+ __ bind(Ldone);
+}
+
+void TemplateTable::irem() {
+ transition(itos, itos);
+
+ __ mr(R12_scratch2, R17_tos);
+ idiv();
+ __ mullw(R17_tos, R17_tos, R12_scratch2);
+ __ subf(R17_tos, R17_tos, R11_scratch1); // Dividend set by idiv.
+}
+
+void TemplateTable::lmul() {
+ transition(ltos, ltos);
+
+ __ pop_l(R11_scratch1);
+ __ mulld(R17_tos, R11_scratch1, R17_tos);
+}
+
+void TemplateTable::ldiv() {
+ transition(ltos, ltos);
+
+ Label Lnormal, Lexception, Ldone;
+ Register Rdividend = R11_scratch1; // Used by lrem.
+
+ __ addi(R0, R17_tos, 1);
+ __ cmpldi(CCR0, R0, 2);
+ __ bgt(CCR0, Lnormal); // divisor <-1 or >1
+
+ __ cmpdi(CCR1, R17_tos, 0);
+ __ beq(CCR1, Lexception); // divisor == 0
+
+ __ pop_l(Rdividend);
+ __ mulld(R17_tos, Rdividend, R17_tos); // div by +/-1
+ __ b(Ldone);
+
+ __ bind(Lexception);
+ __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ArithmeticException_entry);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ __ align(32, 12);
+ __ bind(Lnormal);
+ __ pop_l(Rdividend);
+ __ divd(R17_tos, Rdividend, R17_tos); // Can't divide minint/-1.
+ __ bind(Ldone);
+}
+
+void TemplateTable::lrem() {
+ transition(ltos, ltos);
+
+ __ mr(R12_scratch2, R17_tos);
+ ldiv();
+ __ mulld(R17_tos, R17_tos, R12_scratch2);
+ __ subf(R17_tos, R17_tos, R11_scratch1); // Dividend set by ldiv.
+}
+
+void TemplateTable::lshl() {
+ transition(itos, ltos);
+
+ __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits.
+ __ pop_l(R11_scratch1);
+ __ sld(R17_tos, R11_scratch1, R17_tos);
+}
+
+void TemplateTable::lshr() {
+ transition(itos, ltos);
+
+ __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits.
+ __ pop_l(R11_scratch1);
+ __ srad(R17_tos, R11_scratch1, R17_tos);
+}
+
+void TemplateTable::lushr() {
+ transition(itos, ltos);
+
+ __ rldicl(R17_tos, R17_tos, 0, 64-6); // Extract least significant bits.
+ __ pop_l(R11_scratch1);
+ __ srd(R17_tos, R11_scratch1, R17_tos);
+}
+
+void TemplateTable::fop2(Operation op) {
+ transition(ftos, ftos);
+
+ switch (op) {
+ case add: __ pop_f(F0_SCRATCH); __ fadds(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case sub: __ pop_f(F0_SCRATCH); __ fsubs(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case mul: __ pop_f(F0_SCRATCH); __ fmuls(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case div: __ pop_f(F0_SCRATCH); __ fdivs(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case rem:
+ __ pop_f(F1_ARG1);
+ __ fmr(F2_ARG2, F15_ftos);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::frem));
+ __ fmr(F15_ftos, F1_RET);
+ break;
+
+ default: ShouldNotReachHere();
+ }
+}
+
+void TemplateTable::dop2(Operation op) {
+ transition(dtos, dtos);
+
+ switch (op) {
+ case add: __ pop_d(F0_SCRATCH); __ fadd(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case sub: __ pop_d(F0_SCRATCH); __ fsub(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case mul: __ pop_d(F0_SCRATCH); __ fmul(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case div: __ pop_d(F0_SCRATCH); __ fdiv(F15_ftos, F0_SCRATCH, F15_ftos); break;
+ case rem:
+ __ pop_d(F1_ARG1);
+ __ fmr(F2_ARG2, F15_ftos);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::drem));
+ __ fmr(F15_ftos, F1_RET);
+ break;
+
+ default: ShouldNotReachHere();
+ }
+}
+
+// Negate the value in the TOS cache.
+void TemplateTable::ineg() {
+ transition(itos, itos);
+
+ __ neg(R17_tos, R17_tos);
+}
+
+// Negate the value in the TOS cache.
+void TemplateTable::lneg() {
+ transition(ltos, ltos);
+
+ __ neg(R17_tos, R17_tos);
+}
+
+void TemplateTable::fneg() {
+ transition(ftos, ftos);
+
+ __ fneg(F15_ftos, F15_ftos);
+}
+
+void TemplateTable::dneg() {
+ transition(dtos, dtos);
+
+ __ fneg(F15_ftos, F15_ftos);
+}
+
+// Increments a local variable in place.
+void TemplateTable::iinc() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R11_scratch1,
+ Rincrement = R0,
+ Rvalue = R12_scratch2;
+
+ locals_index(Rindex); // Load locals index from bytecode stream.
+ __ lbz(Rincrement, 2, R14_bcp); // Load increment from the bytecode stream.
+ __ extsb(Rincrement, Rincrement);
+
+ __ load_local_int(Rvalue, Rindex, Rindex); // Puts address of local into Rindex.
+
+ __ add(Rvalue, Rincrement, Rvalue);
+ __ stw(Rvalue, 0, Rindex);
+}
+
+void TemplateTable::wide_iinc() {
+ transition(vtos, vtos);
+
+ Register Rindex = R11_scratch1,
+ Rlocals_addr = Rindex,
+ Rincr = R12_scratch2;
+ locals_index_wide(Rindex);
+ __ get_2_byte_integer_at_bcp(4, Rincr, InterpreterMacroAssembler::Signed);
+ __ load_local_int(R17_tos, Rlocals_addr, Rindex);
+ __ add(R17_tos, Rincr, R17_tos);
+ __ stw(R17_tos, 0, Rlocals_addr);
+}
+
+void TemplateTable::convert() {
+ // %%%%% Factor this first part accross platforms
+#ifdef ASSERT
+ TosState tos_in = ilgl;
+ TosState tos_out = ilgl;
+ switch (bytecode()) {
+ case Bytecodes::_i2l: // fall through
+ case Bytecodes::_i2f: // fall through
+ case Bytecodes::_i2d: // fall through
+ case Bytecodes::_i2b: // fall through
+ case Bytecodes::_i2c: // fall through
+ case Bytecodes::_i2s: tos_in = itos; break;
+ case Bytecodes::_l2i: // fall through
+ case Bytecodes::_l2f: // fall through
+ case Bytecodes::_l2d: tos_in = ltos; break;
+ case Bytecodes::_f2i: // fall through
+ case Bytecodes::_f2l: // fall through
+ case Bytecodes::_f2d: tos_in = ftos; break;
+ case Bytecodes::_d2i: // fall through
+ case Bytecodes::_d2l: // fall through
+ case Bytecodes::_d2f: tos_in = dtos; break;
+ default : ShouldNotReachHere();
+ }
+ switch (bytecode()) {
+ case Bytecodes::_l2i: // fall through
+ case Bytecodes::_f2i: // fall through
+ case Bytecodes::_d2i: // fall through
+ case Bytecodes::_i2b: // fall through
+ case Bytecodes::_i2c: // fall through
+ case Bytecodes::_i2s: tos_out = itos; break;
+ case Bytecodes::_i2l: // fall through
+ case Bytecodes::_f2l: // fall through
+ case Bytecodes::_d2l: tos_out = ltos; break;
+ case Bytecodes::_i2f: // fall through
+ case Bytecodes::_l2f: // fall through
+ case Bytecodes::_d2f: tos_out = ftos; break;
+ case Bytecodes::_i2d: // fall through
+ case Bytecodes::_l2d: // fall through
+ case Bytecodes::_f2d: tos_out = dtos; break;
+ default : ShouldNotReachHere();
+ }
+ transition(tos_in, tos_out);
+#endif
+
+ // Conversion
+ Label done;
+ switch (bytecode()) {
+ case Bytecodes::_i2l:
+ __ extsw(R17_tos, R17_tos);
+ break;
+
+ case Bytecodes::_l2i:
+ // Nothing to do, we'll continue to work with the lower bits.
+ break;
+
+ case Bytecodes::_i2b:
+ __ extsb(R17_tos, R17_tos);
+ break;
+
+ case Bytecodes::_i2c:
+ __ rldicl(R17_tos, R17_tos, 0, 64-2*8);
+ break;
+
+ case Bytecodes::_i2s:
+ __ extsh(R17_tos, R17_tos);
+ break;
+
+ case Bytecodes::_i2d:
+ __ extsw(R17_tos, R17_tos);
+ case Bytecodes::_l2d:
+ __ push_l_pop_d();
+ __ fcfid(F15_ftos, F15_ftos);
+ break;
+
+ case Bytecodes::_i2f:
+ __ extsw(R17_tos, R17_tos);
+ __ push_l_pop_d();
+ if (VM_Version::has_fcfids()) { // fcfids is >= Power7 only
+ // Comment: alternatively, load with sign extend could be done by lfiwax.
+ __ fcfids(F15_ftos, F15_ftos);
+ } else {
+ __ fcfid(F15_ftos, F15_ftos);
+ __ frsp(F15_ftos, F15_ftos);
+ }
+ break;
+
+ case Bytecodes::_l2f:
+ if (VM_Version::has_fcfids()) { // fcfids is >= Power7 only
+ __ push_l_pop_d();
+ __ fcfids(F15_ftos, F15_ftos);
+ } else {
+ // Avoid rounding problem when result should be 0x3f800001: need fixup code before fcfid+frsp.
+ __ mr(R3_ARG1, R17_tos);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::l2f));
+ __ fmr(F15_ftos, F1_RET);
+ }
+ break;
+
+ case Bytecodes::_f2d:
+ // empty
+ break;
+
+ case Bytecodes::_d2f:
+ __ frsp(F15_ftos, F15_ftos);
+ break;
+
+ case Bytecodes::_d2i:
+ case Bytecodes::_f2i:
+ __ fcmpu(CCR0, F15_ftos, F15_ftos);
+ __ li(R17_tos, 0); // 0 in case of NAN
+ __ bso(CCR0, done);
+ __ fctiwz(F15_ftos, F15_ftos);
+ __ push_d_pop_l();
+ break;
+
+ case Bytecodes::_d2l:
+ case Bytecodes::_f2l:
+ __ fcmpu(CCR0, F15_ftos, F15_ftos);
+ __ li(R17_tos, 0); // 0 in case of NAN
+ __ bso(CCR0, done);
+ __ fctidz(F15_ftos, F15_ftos);
+ __ push_d_pop_l();
+ break;
+
+ default: ShouldNotReachHere();
+ }
+ __ bind(done);
+}
+
+// Long compare
+void TemplateTable::lcmp() {
+ transition(ltos, itos);
+
+ const Register Rscratch = R11_scratch1;
+ __ pop_l(Rscratch); // first operand, deeper in stack
+
+ __ cmpd(CCR0, Rscratch, R17_tos); // compare
+ __ mfcr(R17_tos); // set bit 32..33 as follows: <: 0b10, =: 0b00, >: 0b01
+ __ srwi(Rscratch, R17_tos, 30);
+ __ srawi(R17_tos, R17_tos, 31);
+ __ orr(R17_tos, Rscratch, R17_tos); // set result as follows: <: -1, =: 0, >: 1
+}
+
+// fcmpl/fcmpg and dcmpl/dcmpg bytecodes
+// unordered_result == -1 => fcmpl or dcmpl
+// unordered_result == 1 => fcmpg or dcmpg
+void TemplateTable::float_cmp(bool is_float, int unordered_result) {
+ const FloatRegister Rfirst = F0_SCRATCH,
+ Rsecond = F15_ftos;
+ const Register Rscratch = R11_scratch1;
+
+ if (is_float) {
+ __ pop_f(Rfirst);
+ } else {
+ __ pop_d(Rfirst);
+ }
+
+ Label Lunordered, Ldone;
+ __ fcmpu(CCR0, Rfirst, Rsecond); // compare
+ if (unordered_result) {
+ __ bso(CCR0, Lunordered);
+ }
+ __ mfcr(R17_tos); // set bit 32..33 as follows: <: 0b10, =: 0b00, >: 0b01
+ __ srwi(Rscratch, R17_tos, 30);
+ __ srawi(R17_tos, R17_tos, 31);
+ __ orr(R17_tos, Rscratch, R17_tos); // set result as follows: <: -1, =: 0, >: 1
+ if (unordered_result) {
+ __ b(Ldone);
+ __ bind(Lunordered);
+ __ load_const_optimized(R17_tos, unordered_result);
+ }
+ __ bind(Ldone);
+}
+
+// Branch_conditional which takes TemplateTable::Condition.
+void TemplateTable::branch_conditional(ConditionRegister crx, TemplateTable::Condition cc, Label& L, bool invert) {
+ bool positive = false;
+ Assembler::Condition cond = Assembler::equal;
+ switch (cc) {
+ case TemplateTable::equal: positive = true ; cond = Assembler::equal ; break;
+ case TemplateTable::not_equal: positive = false; cond = Assembler::equal ; break;
+ case TemplateTable::less: positive = true ; cond = Assembler::less ; break;
+ case TemplateTable::less_equal: positive = false; cond = Assembler::greater; break;
+ case TemplateTable::greater: positive = true ; cond = Assembler::greater; break;
+ case TemplateTable::greater_equal: positive = false; cond = Assembler::less ; break;
+ default: ShouldNotReachHere();
+ }
+ int bo = (positive != invert) ? Assembler::bcondCRbiIs1 : Assembler::bcondCRbiIs0;
+ int bi = Assembler::bi0(crx, cond);
+ __ bc(bo, bi, L);
+}
+
+void TemplateTable::branch(bool is_jsr, bool is_wide) {
+
+ // Note: on SPARC, we use InterpreterMacroAssembler::if_cmp also.
+ __ verify_thread();
+
+ const Register Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rscratch3 = R3_ARG1,
+ R4_counters = R4_ARG2,
+ bumped_count = R31,
+ Rdisp = R22_tmp2;
+
+ __ profile_taken_branch(Rscratch1, bumped_count);
+
+ // Get (wide) offset.
+ if (is_wide) {
+ __ get_4_byte_integer_at_bcp(1, Rdisp, InterpreterMacroAssembler::Signed);
+ } else {
+ __ get_2_byte_integer_at_bcp(1, Rdisp, InterpreterMacroAssembler::Signed);
+ }
+
+ // --------------------------------------------------------------------------
+ // Handle all the JSR stuff here, then exit.
+ // It's much shorter and cleaner than intermingling with the
+ // non-JSR normal-branch stuff occurring below.
+ if (is_jsr) {
+ // Compute return address as bci in Otos_i.
+ __ ld(Rscratch1, in_bytes(Method::const_offset()), R19_method);
+ __ addi(Rscratch2, R14_bcp, -in_bytes(ConstMethod::codes_offset()) + (is_wide ? 5 : 3));
+ __ subf(R17_tos, Rscratch1, Rscratch2);
+
+ // Bump bcp to target of JSR.
+ __ add(R14_bcp, Rdisp, R14_bcp);
+ // Push returnAddress for "ret" on stack.
+ __ push_ptr(R17_tos);
+ // And away we go!
+ __ dispatch_next(vtos);
+ return;
+ }
+
+ // --------------------------------------------------------------------------
+ // Normal (non-jsr) branch handling
+
+ const bool increment_invocation_counter_for_backward_branches = UseCompiler && UseLoopCounter;
+ if (increment_invocation_counter_for_backward_branches) {
+ //__ unimplemented("branch invocation counter");
+
+ Label Lforward;
+ __ add(R14_bcp, Rdisp, R14_bcp); // Add to bc addr.
+
+ // Check branch direction.
+ __ cmpdi(CCR0, Rdisp, 0);
+ __ bgt(CCR0, Lforward);
+
+ __ get_method_counters(R19_method, R4_counters, Lforward);
+
+ if (TieredCompilation) {
+ Label Lno_mdo, Loverflow;
+ const int increment = InvocationCounter::count_increment;
+ const int mask = ((1 << Tier0BackedgeNotifyFreqLog) - 1) << InvocationCounter::count_shift;
+ if (ProfileInterpreter) {
+ Register Rmdo = Rscratch1;
+
+ // If no method data exists, go to profile_continue.
+ __ ld(Rmdo, in_bytes(Method::method_data_offset()), R19_method);
+ __ cmpdi(CCR0, Rmdo, 0);
+ __ beq(CCR0, Lno_mdo);
+
+ // Increment backedge counter in the MDO.
+ const int mdo_bc_offs = in_bytes(MethodData::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
+ __ lwz(Rscratch2, mdo_bc_offs, Rmdo);
+ __ load_const_optimized(Rscratch3, mask, R0);
+ __ addi(Rscratch2, Rscratch2, increment);
+ __ stw(Rscratch2, mdo_bc_offs, Rmdo);
+ __ and_(Rscratch3, Rscratch2, Rscratch3);
+ __ bne(CCR0, Lforward);
+ __ b(Loverflow);
+ }
+
+ // If there's no MDO, increment counter in method.
+ const int mo_bc_offs = in_bytes(MethodCounters::backedge_counter_offset()) + in_bytes(InvocationCounter::counter_offset());
+ __ bind(Lno_mdo);
+ __ lwz(Rscratch2, mo_bc_offs, R4_counters);
+ __ load_const_optimized(Rscratch3, mask, R0);
+ __ addi(Rscratch2, Rscratch2, increment);
+ __ stw(Rscratch2, mo_bc_offs, R19_method);
+ __ and_(Rscratch3, Rscratch2, Rscratch3);
+ __ bne(CCR0, Lforward);
+
+ __ bind(Loverflow);
+
+ // Notify point for loop, pass branch bytecode.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::frequency_counter_overflow), R14_bcp, true);
+
+ // Was an OSR adapter generated?
+ // O0 = osr nmethod
+ __ cmpdi(CCR0, R3_RET, 0);
+ __ beq(CCR0, Lforward);
+
+ // Has the nmethod been invalidated already?
+ __ lwz(R0, nmethod::entry_bci_offset(), R3_RET);
+ __ cmpwi(CCR0, R0, InvalidOSREntryBci);
+ __ beq(CCR0, Lforward);
+
+ // Migrate the interpreter frame off of the stack.
+ // We can use all registers because we will not return to interpreter from this point.
+
+ // Save nmethod.
+ const Register osr_nmethod = R31;
+ __ mr(osr_nmethod, R3_RET);
+ __ set_top_ijava_frame_at_SP_as_last_Java_frame(R1_SP, R11_scratch1);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::OSR_migration_begin), R16_thread);
+ __ reset_last_Java_frame();
+ // OSR buffer is in ARG1.
+
+ // Remove the interpreter frame.
+ __ merge_frames(/*top_frame_sp*/ R21_sender_SP, /*return_pc*/ R0, R11_scratch1, R12_scratch2);
+
+ // Jump to the osr code.
+ __ ld(R11_scratch1, nmethod::osr_entry_point_offset(), osr_nmethod);
+ __ mtlr(R0);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ } else {
+
+ const Register invoke_ctr = Rscratch1;
+ // Update Backedge branch separately from invocations.
+ __ increment_backedge_counter(R4_counters, invoke_ctr, Rscratch2, Rscratch3);
+
+ if (ProfileInterpreter) {
+ __ test_invocation_counter_for_mdp(invoke_ctr, Rscratch2, Lforward);
+ if (UseOnStackReplacement) {
+ __ test_backedge_count_for_osr(bumped_count, R14_bcp, Rscratch2);
+ }
+ } else {
+ if (UseOnStackReplacement) {
+ __ test_backedge_count_for_osr(invoke_ctr, R14_bcp, Rscratch2);
+ }
+ }
+ }
+
+ __ bind(Lforward);
+
+ } else {
+ // Bump bytecode pointer by displacement (take the branch).
+ __ add(R14_bcp, Rdisp, R14_bcp); // Add to bc addr.
+ }
+ // Continue with bytecode @ target.
+ // %%%%% Like Intel, could speed things up by moving bytecode fetch to code above,
+ // %%%%% and changing dispatch_next to dispatch_only.
+ __ dispatch_next(vtos);
+}
+
+// Helper function for if_cmp* methods below.
+// Factored out common compare and branch code.
+void TemplateTable::if_cmp_common(Register Rfirst, Register Rsecond, Register Rscratch1, Register Rscratch2, Condition cc, bool is_jint, bool cmp0) {
+ Label Lnot_taken;
+ // Note: The condition code we get is the condition under which we
+ // *fall through*! So we have to inverse the CC here.
+
+ if (is_jint) {
+ if (cmp0) {
+ __ cmpwi(CCR0, Rfirst, 0);
+ } else {
+ __ cmpw(CCR0, Rfirst, Rsecond);
+ }
+ } else {
+ if (cmp0) {
+ __ cmpdi(CCR0, Rfirst, 0);
+ } else {
+ __ cmpd(CCR0, Rfirst, Rsecond);
+ }
+ }
+ branch_conditional(CCR0, cc, Lnot_taken, /*invert*/ true);
+
+ // Conition is false => Jump!
+ branch(false, false);
+
+ // Condition is not true => Continue.
+ __ align(32, 12);
+ __ bind(Lnot_taken);
+ __ profile_not_taken_branch(Rscratch1, Rscratch2);
+}
+
+// Compare integer values with zero and fall through if CC holds, branch away otherwise.
+void TemplateTable::if_0cmp(Condition cc) {
+ transition(itos, vtos);
+
+ if_cmp_common(R17_tos, noreg, R11_scratch1, R12_scratch2, cc, true, true);
+}
+
+// Compare integer values and fall through if CC holds, branch away otherwise.
+//
+// Interface:
+// - Rfirst: First operand (older stack value)
+// - tos: Second operand (younger stack value)
+void TemplateTable::if_icmp(Condition cc) {
+ transition(itos, vtos);
+
+ const Register Rfirst = R0,
+ Rsecond = R17_tos;
+
+ __ pop_i(Rfirst);
+ if_cmp_common(Rfirst, Rsecond, R11_scratch1, R12_scratch2, cc, true, false);
+}
+
+void TemplateTable::if_nullcmp(Condition cc) {
+ transition(atos, vtos);
+
+ if_cmp_common(R17_tos, noreg, R11_scratch1, R12_scratch2, cc, false, true);
+}
+
+void TemplateTable::if_acmp(Condition cc) {
+ transition(atos, vtos);
+
+ const Register Rfirst = R0,
+ Rsecond = R17_tos;
+
+ __ pop_ptr(Rfirst);
+ if_cmp_common(Rfirst, Rsecond, R11_scratch1, R12_scratch2, cc, false, false);
+}
+
+void TemplateTable::ret() {
+ locals_index(R11_scratch1);
+ __ load_local_ptr(R17_tos, R11_scratch1, R11_scratch1);
+
+ __ profile_ret(vtos, R17_tos, R11_scratch1, R12_scratch2);
+
+ __ ld(R11_scratch1, in_bytes(Method::const_offset()), R19_method);
+ __ add(R11_scratch1, R17_tos, R11_scratch1);
+ __ addi(R14_bcp, R11_scratch1, in_bytes(ConstMethod::codes_offset()));
+ __ dispatch_next(vtos);
+}
+
+void TemplateTable::wide_ret() {
+ transition(vtos, vtos);
+
+ const Register Rindex = R3_ARG1,
+ Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2;
+
+ locals_index_wide(Rindex);
+ __ load_local_ptr(R17_tos, R17_tos, Rindex);
+ __ profile_ret(vtos, R17_tos, Rscratch1, R12_scratch2);
+ // Tos now contains the bci, compute the bcp from that.
+ __ ld(Rscratch1, in_bytes(Method::const_offset()), R19_method);
+ __ addi(Rscratch2, R17_tos, in_bytes(ConstMethod::codes_offset()));
+ __ add(R14_bcp, Rscratch1, Rscratch2);
+ __ dispatch_next(vtos);
+}
+
+void TemplateTable::tableswitch() {
+ transition(itos, vtos);
+
+ Label Ldispatch, Ldefault_case;
+ Register Rlow_byte = R3_ARG1,
+ Rindex = Rlow_byte,
+ Rhigh_byte = R4_ARG2,
+ Rdef_offset_addr = R5_ARG3, // is going to contain address of default offset
+ Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Roffset = R6_ARG4;
+
+ // Align bcp.
+ __ addi(Rdef_offset_addr, R14_bcp, BytesPerInt);
+ __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt));
+
+ // Load lo & hi.
+ __ lwz(Rlow_byte, BytesPerInt, Rdef_offset_addr);
+ __ lwz(Rhigh_byte, BytesPerInt * 2, Rdef_offset_addr);
+
+ // Check for default case (=index outside [low,high]).
+ __ cmpw(CCR0, R17_tos, Rlow_byte);
+ __ cmpw(CCR1, R17_tos, Rhigh_byte);
+ __ blt(CCR0, Ldefault_case);
+ __ bgt(CCR1, Ldefault_case);
+
+ // Lookup dispatch offset.
+ __ sub(Rindex, R17_tos, Rlow_byte);
+ __ extsw(Rindex, Rindex);
+ __ profile_switch_case(Rindex, Rhigh_byte /* scratch */, Rscratch1, Rscratch2);
+ __ sldi(Rindex, Rindex, LogBytesPerInt);
+ __ addi(Rindex, Rindex, 3 * BytesPerInt);
+ __ lwax(Roffset, Rdef_offset_addr, Rindex);
+ __ b(Ldispatch);
+
+ __ bind(Ldefault_case);
+ __ profile_switch_default(Rhigh_byte, Rscratch1);
+ __ lwa(Roffset, 0, Rdef_offset_addr);
+
+ __ bind(Ldispatch);
+
+ __ add(R14_bcp, Roffset, R14_bcp);
+ __ dispatch_next(vtos);
+}
+
+void TemplateTable::lookupswitch() {
+ transition(itos, itos);
+ __ stop("lookupswitch bytecode should have been rewritten");
+}
+
+// Table switch using linear search through cases.
+// Bytecode stream format:
+// Bytecode (1) | 4-byte padding | default offset (4) | count (4) | value/offset pair1 (8) | value/offset pair2 (8) | ...
+// Note: Everything is big-endian format here. So on little endian machines, we have to revers offset and count and cmp value.
+void TemplateTable::fast_linearswitch() {
+ transition(itos, vtos);
+
+ Label Lloop_entry, Lsearch_loop, Lfound, Lcontinue_execution, Ldefault_case;
+
+ Register Rcount = R3_ARG1,
+ Rcurrent_pair = R4_ARG2,
+ Rdef_offset_addr = R5_ARG3, // Is going to contain address of default offset.
+ Roffset = R31, // Might need to survive C call.
+ Rvalue = R12_scratch2,
+ Rscratch = R11_scratch1,
+ Rcmp_value = R17_tos;
+
+ // Align bcp.
+ __ addi(Rdef_offset_addr, R14_bcp, BytesPerInt);
+ __ clrrdi(Rdef_offset_addr, Rdef_offset_addr, log2_long((jlong)BytesPerInt));
+
+ // Setup loop counter and limit.
+ __ lwz(Rcount, BytesPerInt, Rdef_offset_addr); // Load count.
+ __ addi(Rcurrent_pair, Rdef_offset_addr, 2 * BytesPerInt); // Rcurrent_pair now points to first pair.
+
+ // Set up search loop.
+ __ cmpwi(CCR0, Rcount, 0);
+ __ beq(CCR0, Ldefault_case);
+
+ __ mtctr(Rcount);
+
+ // linear table search
+ __ bind(Lsearch_loop);
+
+ __ lwz(Rvalue, 0, Rcurrent_pair);
+ __ lwa(Roffset, 1 * BytesPerInt, Rcurrent_pair);
+
+ __ cmpw(CCR0, Rvalue, Rcmp_value);
+ __ beq(CCR0, Lfound);
+
+ __ addi(Rcurrent_pair, Rcurrent_pair, 2 * BytesPerInt);
+ __ bdnz(Lsearch_loop);
+
+ // default case
+ __ bind(Ldefault_case);
+
+ __ lwa(Roffset, 0, Rdef_offset_addr);
+ if (ProfileInterpreter) {
+ __ profile_switch_default(Rdef_offset_addr, Rcount/* scratch */);
+ __ b(Lcontinue_execution);
+ }
+
+ // Entry found, skip Roffset bytecodes and continue.
+ __ bind(Lfound);
+ if (ProfileInterpreter) {
+ // Calc the num of the pair we hit. Careful, Rcurrent_pair points 2 ints
+ // beyond the actual current pair due to the auto update load above!
+ __ sub(Rcurrent_pair, Rcurrent_pair, Rdef_offset_addr);
+ __ addi(Rcurrent_pair, Rcurrent_pair, - 2 * BytesPerInt);
+ __ srdi(Rcurrent_pair, Rcurrent_pair, LogBytesPerInt + 1);
+ __ profile_switch_case(Rcurrent_pair, Rcount /*scratch*/, Rdef_offset_addr/*scratch*/, Rscratch);
+ __ bind(Lcontinue_execution);
+ }
+ __ add(R14_bcp, Roffset, R14_bcp);
+ __ dispatch_next(vtos);
+}
+
+// Table switch using binary search (value/offset pairs are ordered).
+// Bytecode stream format:
+// Bytecode (1) | 4-byte padding | default offset (4) | count (4) | value/offset pair1 (8) | value/offset pair2 (8) | ...
+// Note: Everything is big-endian format here. So on little endian machines, we have to revers offset and count and cmp value.
+void TemplateTable::fast_binaryswitch() {
+
+ transition(itos, vtos);
+ // Implementation using the following core algorithm: (copied from Intel)
+ //
+ // int binary_search(int key, LookupswitchPair* array, int n) {
+ // // Binary search according to "Methodik des Programmierens" by
+ // // Edsger W. Dijkstra and W.H.J. Feijen, Addison Wesley Germany 1985.
+ // int i = 0;
+ // int j = n;
+ // while (i+1 < j) {
+ // // invariant P: 0 <= i < j <= n and (a[i] <= key < a[j] or Q)
+ // // with Q: for all i: 0 <= i < n: key < a[i]
+ // // where a stands for the array and assuming that the (inexisting)
+ // // element a[n] is infinitely big.
+ // int h = (i + j) >> 1;
+ // // i < h < j
+ // if (key < array[h].fast_match()) {
+ // j = h;
+ // } else {
+ // i = h;
+ // }
+ // }
+ // // R: a[i] <= key < a[i+1] or Q
+ // // (i.e., if key is within array, i is the correct index)
+ // return i;
+ // }
+
+ // register allocation
+ const Register Rkey = R17_tos; // already set (tosca)
+ const Register Rarray = R3_ARG1;
+ const Register Ri = R4_ARG2;
+ const Register Rj = R5_ARG3;
+ const Register Rh = R6_ARG4;
+ const Register Rscratch = R11_scratch1;
+
+ const int log_entry_size = 3;
+ const int entry_size = 1 << log_entry_size;
+
+ Label found;
+
+ // Find Array start,
+ __ addi(Rarray, R14_bcp, 3 * BytesPerInt);
+ __ clrrdi(Rarray, Rarray, log2_long((jlong)BytesPerInt));
+
+ // initialize i & j
+ __ li(Ri,0);
+ __ lwz(Rj, -BytesPerInt, Rarray);
+
+ // and start.
+ Label entry;
+ __ b(entry);
+
+ // binary search loop
+ { Label loop;
+ __ bind(loop);
+ // int h = (i + j) >> 1;
+ __ srdi(Rh, Rh, 1);
+ // if (key < array[h].fast_match()) {
+ // j = h;
+ // } else {
+ // i = h;
+ // }
+ __ sldi(Rscratch, Rh, log_entry_size);
+ __ lwzx(Rscratch, Rscratch, Rarray);
+
+ // if (key < current value)
+ // Rh = Rj
+ // else
+ // Rh = Ri
+ Label Lgreater;
+ __ cmpw(CCR0, Rkey, Rscratch);
+ __ bge(CCR0, Lgreater);
+ __ mr(Rj, Rh);
+ __ b(entry);
+ __ bind(Lgreater);
+ __ mr(Ri, Rh);
+
+ // while (i+1 < j)
+ __ bind(entry);
+ __ addi(Rscratch, Ri, 1);
+ __ cmpw(CCR0, Rscratch, Rj);
+ __ add(Rh, Ri, Rj); // start h = i + j >> 1;
+
+ __ blt(CCR0, loop);
+ }
+
+ // End of binary search, result index is i (must check again!).
+ Label default_case;
+ Label continue_execution;
+ if (ProfileInterpreter) {
+ __ mr(Rh, Ri); // Save index in i for profiling.
+ }
+ // Ri = value offset
+ __ sldi(Ri, Ri, log_entry_size);
+ __ add(Ri, Ri, Rarray);
+ __ lwz(Rscratch, 0, Ri);
+
+ Label not_found;
+ // Ri = offset offset
+ __ cmpw(CCR0, Rkey, Rscratch);
+ __ beq(CCR0, not_found);
+ // entry not found -> j = default offset
+ __ lwz(Rj, -2 * BytesPerInt, Rarray);
+ __ b(default_case);
+
+ __ bind(not_found);
+ // entry found -> j = offset
+ __ profile_switch_case(Rh, Rj, Rscratch, Rkey);
+ __ lwz(Rj, BytesPerInt, Ri);
+
+ if (ProfileInterpreter) {
+ __ b(continue_execution);
+ }
+
+ __ bind(default_case); // fall through (if not profiling)
+ __ profile_switch_default(Ri, Rscratch);
+
+ __ bind(continue_execution);
+
+ __ extsw(Rj, Rj);
+ __ add(R14_bcp, Rj, R14_bcp);
+ __ dispatch_next(vtos);
+}
+
+void TemplateTable::_return(TosState state) {
+ transition(state, state);
+ assert(_desc->calls_vm(),
+ "inconsistent calls_vm information"); // call in remove_activation
+
+ if (_desc->bytecode() == Bytecodes::_return_register_finalizer) {
+
+ Register Rscratch = R11_scratch1,
+ Rklass = R12_scratch2,
+ Rklass_flags = Rklass;
+ Label Lskip_register_finalizer;
+
+ // Check if the method has the FINALIZER flag set and call into the VM to finalize in this case.
+ assert(state == vtos, "only valid state");
+ __ ld(R17_tos, 0, R18_locals);
+
+ // Load klass of this obj.
+ __ load_klass(Rklass, R17_tos);
+ __ lwz(Rklass_flags, in_bytes(Klass::access_flags_offset()), Rklass);
+ __ testbitdi(CCR0, R0, Rklass_flags, exact_log2(JVM_ACC_HAS_FINALIZER));
+ __ bfalse(CCR0, Lskip_register_finalizer);
+
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::register_finalizer), R17_tos /* obj */);
+
+ __ align(32, 12);
+ __ bind(Lskip_register_finalizer);
+ }
+
+ // Move the result value into the correct register and remove memory stack frame.
+ __ remove_activation(state, /* throw_monitor_exception */ true);
+ // Restoration of lr done by remove_activation.
+ switch (state) {
+ case ltos:
+ case btos:
+ case ctos:
+ case stos:
+ case atos:
+ case itos: __ mr(R3_RET, R17_tos); break;
+ case ftos:
+ case dtos: __ fmr(F1_RET, F15_ftos); break;
+ case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need
+ // to get visible before the reference to the object gets stored anywhere.
+ __ membar(Assembler::StoreStore); break;
+ default : ShouldNotReachHere();
+ }
+ __ blr();
+}
+
+// ============================================================================
+// Constant pool cache access
+//
+// Memory ordering:
+//
+// Like done in C++ interpreter, we load the fields
+// - _indices
+// - _f12_oop
+// acquired, because these are asked if the cache is already resolved. We don't
+// want to float loads above this check.
+// See also comments in ConstantPoolCacheEntry::bytecode_1(),
+// ConstantPoolCacheEntry::bytecode_2() and ConstantPoolCacheEntry::f1();
+
+// Call into the VM if call site is not yet resolved
+//
+// Input regs:
+// - None, all passed regs are outputs.
+//
+// Returns:
+// - Rcache: The const pool cache entry that contains the resolved result.
+// - Rresult: Either noreg or output for f1/f2.
+//
+// Kills:
+// - Rscratch
+void TemplateTable::resolve_cache_and_index(int byte_no, Register Rcache, Register Rscratch, size_t index_size) {
+
+ __ get_cache_and_index_at_bcp(Rcache, 1, index_size);
+ Label Lresolved, Ldone;
+
+ assert(byte_no == f1_byte || byte_no == f2_byte, "byte_no out of range");
+ // We are resolved if the indices offset contains the current bytecode.
+ // Big Endian:
+ __ lbz(Rscratch, in_bytes(ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::indices_offset()) + 7 - (byte_no + 1), Rcache);
+ // Acquire by cmp-br-isync (see below).
+ __ cmpdi(CCR0, Rscratch, (int)bytecode());
+ __ beq(CCR0, Lresolved);
+
+ address entry = NULL;
+ switch (bytecode()) {
+ case Bytecodes::_getstatic : // fall through
+ case Bytecodes::_putstatic : // fall through
+ case Bytecodes::_getfield : // fall through
+ case Bytecodes::_putfield : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_get_put); break;
+ case Bytecodes::_invokevirtual : // fall through
+ case Bytecodes::_invokespecial : // fall through
+ case Bytecodes::_invokestatic : // fall through
+ case Bytecodes::_invokeinterface: entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invoke); break;
+ case Bytecodes::_invokehandle : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokehandle); break;
+ case Bytecodes::_invokedynamic : entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_invokedynamic); break;
+ default : ShouldNotReachHere(); break;
+ }
+ __ li(R4_ARG2, (int)bytecode());
+ __ call_VM(noreg, entry, R4_ARG2, true);
+
+ // Update registers with resolved info.
+ __ get_cache_and_index_at_bcp(Rcache, 1, index_size);
+ __ b(Ldone);
+
+ __ bind(Lresolved);
+ __ isync(); // Order load wrt. succeeding loads.
+ __ bind(Ldone);
+}
+
+// Load the constant pool cache entry at field accesses into registers.
+// The Rcache and Rindex registers must be set before call.
+// Input:
+// - Rcache, Rindex
+// Output:
+// - Robj, Roffset, Rflags
+void TemplateTable::load_field_cp_cache_entry(Register Robj,
+ Register Rcache,
+ Register Rindex /* unused on PPC64 */,
+ Register Roffset,
+ Register Rflags,
+ bool is_static = false) {
+ assert_different_registers(Rcache, Rflags, Roffset);
+ // assert(Rindex == noreg, "parameter not used on PPC64");
+
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+ __ ld(Rflags, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::flags_offset()), Rcache);
+ __ ld(Roffset, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::f2_offset()), Rcache);
+ if (is_static) {
+ __ ld(Robj, in_bytes(cp_base_offset) + in_bytes(ConstantPoolCacheEntry::f1_offset()), Rcache);
+ __ ld(Robj, in_bytes(Klass::java_mirror_offset()), Robj);
+ // Acquire not needed here. Following access has an address dependency on this value.
+ }
+}
+
+// Load the constant pool cache entry at invokes into registers.
+// Resolve if necessary.
+
+// Input Registers:
+// - None, bcp is used, though
+//
+// Return registers:
+// - Rmethod (f1 field or f2 if invokevirtual)
+// - Ritable_index (f2 field)
+// - Rflags (flags field)
+//
+// Kills:
+// - R21
+//
+void TemplateTable::load_invoke_cp_cache_entry(int byte_no,
+ Register Rmethod,
+ Register Ritable_index,
+ Register Rflags,
+ bool is_invokevirtual,
+ bool is_invokevfinal,
+ bool is_invokedynamic) {
+
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+ // Determine constant pool cache field offsets.
+ assert(is_invokevirtual == (byte_no == f2_byte), "is_invokevirtual flag redundant");
+ const int method_offset = in_bytes(cp_base_offset + (is_invokevirtual ? ConstantPoolCacheEntry::f2_offset() : ConstantPoolCacheEntry::f1_offset()));
+ const int flags_offset = in_bytes(cp_base_offset + ConstantPoolCacheEntry::flags_offset());
+ // Access constant pool cache fields.
+ const int index_offset = in_bytes(cp_base_offset + ConstantPoolCacheEntry::f2_offset());
+
+ Register Rcache = R21_tmp1; // Note: same register as R21_sender_SP.
+
+ if (is_invokevfinal) {
+ assert(Ritable_index == noreg, "register not used");
+ // Already resolved.
+ __ get_cache_and_index_at_bcp(Rcache, 1);
+ } else {
+ resolve_cache_and_index(byte_no, Rcache, R0, is_invokedynamic ? sizeof(u4) : sizeof(u2));
+ }
+
+ __ ld(Rmethod, method_offset, Rcache);
+ __ ld(Rflags, flags_offset, Rcache);
+
+ if (Ritable_index != noreg) {
+ __ ld(Ritable_index, index_offset, Rcache);
+ }
+}
+
+// ============================================================================
+// Field access
+
+// Volatile variables demand their effects be made known to all CPU's
+// in order. Store buffers on most chips allow reads & writes to
+// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode
+// without some kind of memory barrier (i.e., it's not sufficient that
+// the interpreter does not reorder volatile references, the hardware
+// also must not reorder them).
+//
+// According to the new Java Memory Model (JMM):
+// (1) All volatiles are serialized wrt to each other. ALSO reads &
+// writes act as aquire & release, so:
+// (2) A read cannot let unrelated NON-volatile memory refs that
+// happen after the read float up to before the read. It's OK for
+// non-volatile memory refs that happen before the volatile read to
+// float down below it.
+// (3) Similar a volatile write cannot let unrelated NON-volatile
+// memory refs that happen BEFORE the write float down to after the
+// write. It's OK for non-volatile memory refs that happen after the
+// volatile write to float up before it.
+//
+// We only put in barriers around volatile refs (they are expensive),
+// not _between_ memory refs (that would require us to track the
+// flavor of the previous memory refs). Requirements (2) and (3)
+// require some barriers before volatile stores and after volatile
+// loads. These nearly cover requirement (1) but miss the
+// volatile-store-volatile-load case. This final case is placed after
+// volatile-stores although it could just as well go before
+// volatile-loads.
+
+// The registers cache and index expected to be set before call.
+// Correct values of the cache and index registers are preserved.
+// Kills:
+// Rcache (if has_tos)
+// Rscratch
+void TemplateTable::jvmti_post_field_access(Register Rcache, Register Rscratch, bool is_static, bool has_tos) {
+
+ assert_different_registers(Rcache, Rscratch);
+
+ if (JvmtiExport::can_post_field_access()) {
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+ Label Lno_field_access_post;
+
+ // Check if post field access in enabled.
+ int offs = __ load_const_optimized(Rscratch, JvmtiExport::get_field_access_count_addr(), R0, true);
+ __ lwz(Rscratch, offs, Rscratch);
+
+ __ cmpwi(CCR0, Rscratch, 0);
+ __ beq(CCR0, Lno_field_access_post);
+
+ // Post access enabled - do it!
+ __ addi(Rcache, Rcache, in_bytes(cp_base_offset));
+ if (is_static) {
+ __ li(R17_tos, 0);
+ } else {
+ if (has_tos) {
+ // The fast bytecode versions have obj ptr in register.
+ // Thus, save object pointer before call_VM() clobbers it
+ // put object on tos where GC wants it.
+ __ push_ptr(R17_tos);
+ } else {
+ // Load top of stack (do not pop the value off the stack).
+ __ ld(R17_tos, Interpreter::expr_offset_in_bytes(0), R15_esp);
+ }
+ __ verify_oop(R17_tos);
+ }
+ // tos: object pointer or NULL if static
+ // cache: cache entry pointer
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_access), R17_tos, Rcache);
+ if (!is_static && has_tos) {
+ // Restore object pointer.
+ __ pop_ptr(R17_tos);
+ __ verify_oop(R17_tos);
+ } else {
+ // Cache is still needed to get class or obj.
+ __ get_cache_and_index_at_bcp(Rcache, 1);
+ }
+
+ __ align(32, 12);
+ __ bind(Lno_field_access_post);
+ }
+}
+
+// kills R11_scratch1
+void TemplateTable::pop_and_check_object(Register Roop) {
+ Register Rtmp = R11_scratch1;
+
+ assert_different_registers(Rtmp, Roop);
+ __ pop_ptr(Roop);
+ // For field access must check obj.
+ __ null_check_throw(Roop, -1, Rtmp);
+ __ verify_oop(Roop);
+}
+
+// PPC64: implement volatile loads as fence-store-acquire.
+void TemplateTable::getfield_or_static(int byte_no, bool is_static) {
+ transition(vtos, vtos);
+
+ Label Lacquire, Lisync;
+
+ const Register Rcache = R3_ARG1,
+ Rclass_or_obj = R22_tmp2,
+ Roffset = R23_tmp3,
+ Rflags = R31,
+ Rbtable = R5_ARG3,
+ Rbc = R6_ARG4,
+ Rscratch = R12_scratch2;
+
+ static address field_branch_table[number_of_states],
+ static_branch_table[number_of_states];
+
+ address* branch_table = is_static ? static_branch_table : field_branch_table;
+
+ // Get field offset.
+ resolve_cache_and_index(byte_no, Rcache, Rscratch, sizeof(u2));
+
+ // JVMTI support
+ jvmti_post_field_access(Rcache, Rscratch, is_static, false);
+
+ // Load after possible GC.
+ load_field_cp_cache_entry(Rclass_or_obj, Rcache, noreg, Roffset, Rflags, is_static);
+
+ // Load pointer to branch table.
+ __ load_const_optimized(Rbtable, (address)branch_table, Rscratch);
+
+ // Get volatile flag.
+ __ rldicl(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit.
+ // Note: sync is needed before volatile load on PPC64.
+
+ // Check field type.
+ __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+
+#ifdef ASSERT
+ Label LFlagInvalid;
+ __ cmpldi(CCR0, Rflags, number_of_states);
+ __ bge(CCR0, LFlagInvalid);
+#endif
+
+ // Load from branch table and dispatch (volatile case: one instruction ahead).
+ __ sldi(Rflags, Rflags, LogBytesPerWord);
+ __ cmpwi(CCR6, Rscratch, 1); // Volatile?
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // Volatile ? size of 1 instruction : 0.
+ }
+ __ ldx(Rbtable, Rbtable, Rflags);
+
+ // Get the obj from stack.
+ if (!is_static) {
+ pop_and_check_object(Rclass_or_obj); // Kills R11_scratch1.
+ } else {
+ __ verify_oop(Rclass_or_obj);
+ }
+
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ subf(Rbtable, Rscratch, Rbtable); // Point to volatile/non-volatile entry point.
+ }
+ __ mtctr(Rbtable);
+ __ bctr();
+
+#ifdef ASSERT
+ __ bind(LFlagInvalid);
+ __ stop("got invalid flag", 0x654);
+
+ // __ bind(Lvtos);
+ address pc_before_fence = __ pc();
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(__ pc() - pc_before_fence == (ptrdiff_t)BytesPerInstWord, "must be single instruction");
+ assert(branch_table[vtos] == 0, "can't compute twice");
+ branch_table[vtos] = __ pc(); // non-volatile_entry point
+ __ stop("vtos unexpected", 0x655);
+#endif
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Ldtos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[dtos] == 0, "can't compute twice");
+ branch_table[dtos] = __ pc(); // non-volatile_entry point
+ __ lfdx(F15_ftos, Rclass_or_obj, Roffset);
+ __ push(dtos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_dgetfield, Rbc, Rscratch);
+ {
+ Label acquire_double;
+ __ beq(CCR6, acquire_double); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ bind(acquire_double);
+ __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync.
+ __ beq_predict_taken(CCR0, Lisync);
+ __ b(Lisync); // In case of NAN.
+ }
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Lftos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ftos] == 0, "can't compute twice");
+ branch_table[ftos] = __ pc(); // non-volatile_entry point
+ __ lfsx(F15_ftos, Rclass_or_obj, Roffset);
+ __ push(ftos);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_fgetfield, Rbc, Rscratch); }
+ {
+ Label acquire_float;
+ __ beq(CCR6, acquire_float); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ bind(acquire_float);
+ __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync.
+ __ beq_predict_taken(CCR0, Lisync);
+ __ b(Lisync); // In case of NAN.
+ }
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Litos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[itos] == 0, "can't compute twice");
+ branch_table[itos] = __ pc(); // non-volatile_entry point
+ __ lwax(R17_tos, Rclass_or_obj, Roffset);
+ __ push(itos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_igetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Lltos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ltos] == 0, "can't compute twice");
+ branch_table[ltos] = __ pc(); // non-volatile_entry point
+ __ ldx(R17_tos, Rclass_or_obj, Roffset);
+ __ push(ltos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_lgetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Lbtos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[btos] == 0, "can't compute twice");
+ branch_table[btos] = __ pc(); // non-volatile_entry point
+ __ lbzx(R17_tos, Rclass_or_obj, Roffset);
+ __ extsb(R17_tos, R17_tos);
+ __ push(btos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_bgetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Lctos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ctos] == 0, "can't compute twice");
+ branch_table[ctos] = __ pc(); // non-volatile_entry point
+ __ lhzx(R17_tos, Rclass_or_obj, Roffset);
+ __ push(ctos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_cgetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Lstos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[stos] == 0, "can't compute twice");
+ branch_table[stos] = __ pc(); // non-volatile_entry point
+ __ lhax(R17_tos, Rclass_or_obj, Roffset);
+ __ push(stos);
+ if (!is_static) patch_bytecode(Bytecodes::_fast_sgetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align load.
+ // __ bind(Latos);
+ __ fence(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[atos] == 0, "can't compute twice");
+ branch_table[atos] = __ pc(); // non-volatile_entry point
+ __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj);
+ __ verify_oop(R17_tos);
+ __ push(atos);
+ //__ dcbt(R17_tos); // prefetch
+ if (!is_static) patch_bytecode(Bytecodes::_fast_agetfield, Rbc, Rscratch);
+ __ beq(CCR6, Lacquire); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 12);
+ __ bind(Lacquire);
+ __ twi_0(R17_tos);
+ __ bind(Lisync);
+ __ isync(); // acquire
+
+#ifdef ASSERT
+ for (int i = 0; i<number_of_states; ++i) {
+ assert(branch_table[i], "get initialization");
+ //tty->print_cr("get: %s_branch_table[%d] = 0x%llx (opcode 0x%llx)",
+ // is_static ? "static" : "field", i, branch_table[i], *((unsigned int*)branch_table[i]));
+ }
+#endif
+}
+
+void TemplateTable::getfield(int byte_no) {
+ getfield_or_static(byte_no, false);
+}
+
+void TemplateTable::getstatic(int byte_no) {
+ getfield_or_static(byte_no, true);
+}
+
+// The registers cache and index expected to be set before call.
+// The function may destroy various registers, just not the cache and index registers.
+void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rscratch, bool is_static) {
+
+ assert_different_registers(Rcache, Rscratch, R6_ARG4);
+
+ if (JvmtiExport::can_post_field_modification()) {
+ Label Lno_field_mod_post;
+
+ // Check if post field access in enabled.
+ int offs = __ load_const_optimized(Rscratch, JvmtiExport::get_field_modification_count_addr(), R0, true);
+ __ lwz(Rscratch, offs, Rscratch);
+
+ __ cmpwi(CCR0, Rscratch, 0);
+ __ beq(CCR0, Lno_field_mod_post);
+
+ // Do the post
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+ const Register Robj = Rscratch;
+
+ __ addi(Rcache, Rcache, in_bytes(cp_base_offset));
+ if (is_static) {
+ // Life is simple. Null out the object pointer.
+ __ li(Robj, 0);
+ } else {
+ // In case of the fast versions, value lives in registers => put it back on tos.
+ int offs = Interpreter::expr_offset_in_bytes(0);
+ Register base = R15_esp;
+ switch(bytecode()) {
+ case Bytecodes::_fast_aputfield: __ push_ptr(); offs+= Interpreter::stackElementSize; break;
+ case Bytecodes::_fast_iputfield: // Fall through
+ case Bytecodes::_fast_bputfield: // Fall through
+ case Bytecodes::_fast_cputfield: // Fall through
+ case Bytecodes::_fast_sputfield: __ push_i(); offs+= Interpreter::stackElementSize; break;
+ case Bytecodes::_fast_lputfield: __ push_l(); offs+=2*Interpreter::stackElementSize; break;
+ case Bytecodes::_fast_fputfield: __ push_f(); offs+= Interpreter::stackElementSize; break;
+ case Bytecodes::_fast_dputfield: __ push_d(); offs+=2*Interpreter::stackElementSize; break;
+ default: {
+ offs = 0;
+ base = Robj;
+ const Register Rflags = Robj;
+ Label is_one_slot;
+ // Life is harder. The stack holds the value on top, followed by the
+ // object. We don't know the size of the value, though; it could be
+ // one or two words depending on its type. As a result, we must find
+ // the type to determine where the object is.
+ __ ld(Rflags, in_bytes(ConstantPoolCacheEntry::flags_offset()), Rcache); // Big Endian
+ __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+
+ __ cmpwi(CCR0, Rflags, ltos);
+ __ cmpwi(CCR1, Rflags, dtos);
+ __ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(1));
+ __ crnor(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2);
+ __ beq(CCR0, is_one_slot);
+ __ addi(base, R15_esp, Interpreter::expr_offset_in_bytes(2));
+ __ bind(is_one_slot);
+ break;
+ }
+ }
+ __ ld(Robj, offs, base);
+ __ verify_oop(Robj);
+ }
+
+ __ addi(R6_ARG4, R15_esp, Interpreter::expr_offset_in_bytes(0));
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::post_field_modification), Robj, Rcache, R6_ARG4);
+ __ get_cache_and_index_at_bcp(Rcache, 1);
+
+ // In case of the fast versions, value lives in registers => put it back on tos.
+ switch(bytecode()) {
+ case Bytecodes::_fast_aputfield: __ pop_ptr(); break;
+ case Bytecodes::_fast_iputfield: // Fall through
+ case Bytecodes::_fast_bputfield: // Fall through
+ case Bytecodes::_fast_cputfield: // Fall through
+ case Bytecodes::_fast_sputfield: __ pop_i(); break;
+ case Bytecodes::_fast_lputfield: __ pop_l(); break;
+ case Bytecodes::_fast_fputfield: __ pop_f(); break;
+ case Bytecodes::_fast_dputfield: __ pop_d(); break;
+ default: break; // Nothin' to do.
+ }
+
+ __ align(32, 12);
+ __ bind(Lno_field_mod_post);
+ }
+}
+
+// PPC64: implement volatile stores as release-store (return bytecode contains an additional release).
+void TemplateTable::putfield_or_static(int byte_no, bool is_static) {
+ Label Lvolatile;
+
+ const Register Rcache = R5_ARG3, // Do not use ARG1/2 (causes trouble in jvmti_post_field_mod).
+ Rclass_or_obj = R31, // Needs to survive C call.
+ Roffset = R22_tmp2, // Needs to survive C call.
+ Rflags = R3_ARG1,
+ Rbtable = R4_ARG2,
+ Rscratch = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rscratch3 = R6_ARG4,
+ Rbc = Rscratch3;
+ const ConditionRegister CR_is_vol = CCR2; // Non-volatile condition register (survives runtime call in do_oop_store).
+
+ static address field_branch_table[number_of_states],
+ static_branch_table[number_of_states];
+
+ address* branch_table = is_static ? static_branch_table : field_branch_table;
+
+ // Stack (grows up):
+ // value
+ // obj
+
+ // Load the field offset.
+ resolve_cache_and_index(byte_no, Rcache, Rscratch, sizeof(u2));
+ jvmti_post_field_mod(Rcache, Rscratch, is_static);
+ load_field_cp_cache_entry(Rclass_or_obj, Rcache, noreg, Roffset, Rflags, is_static);
+
+ // Load pointer to branch table.
+ __ load_const_optimized(Rbtable, (address)branch_table, Rscratch);
+
+ // Get volatile flag.
+ __ rldicl(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit.
+
+ // Check the field type.
+ __ rldicl(Rflags, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+
+#ifdef ASSERT
+ Label LFlagInvalid;
+ __ cmpldi(CCR0, Rflags, number_of_states);
+ __ bge(CCR0, LFlagInvalid);
+#endif
+
+ // Load from branch table and dispatch (volatile case: one instruction ahead).
+ __ sldi(Rflags, Rflags, LogBytesPerWord);
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { __ cmpwi(CR_is_vol, Rscratch, 1); } // Volatile?
+ __ sldi(Rscratch, Rscratch, exact_log2(BytesPerInstWord)); // Volatile? size of instruction 1 : 0.
+ __ ldx(Rbtable, Rbtable, Rflags);
+
+ __ subf(Rbtable, Rscratch, Rbtable); // Point to volatile/non-volatile entry point.
+ __ mtctr(Rbtable);
+ __ bctr();
+
+#ifdef ASSERT
+ __ bind(LFlagInvalid);
+ __ stop("got invalid flag", 0x656);
+
+ // __ bind(Lvtos);
+ address pc_before_release = __ pc();
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(__ pc() - pc_before_release == (ptrdiff_t)BytesPerInstWord, "must be single instruction");
+ assert(branch_table[vtos] == 0, "can't compute twice");
+ branch_table[vtos] = __ pc(); // non-volatile_entry point
+ __ stop("vtos unexpected", 0x657);
+#endif
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Ldtos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[dtos] == 0, "can't compute twice");
+ branch_table[dtos] = __ pc(); // non-volatile_entry point
+ __ pop(dtos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ stfdx(F15_ftos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_dputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Lftos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ftos] == 0, "can't compute twice");
+ branch_table[ftos] = __ pc(); // non-volatile_entry point
+ __ pop(ftos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ stfsx(F15_ftos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_fputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Litos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[itos] == 0, "can't compute twice");
+ branch_table[itos] = __ pc(); // non-volatile_entry point
+ __ pop(itos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ stwx(R17_tos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_iputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Lltos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ltos] == 0, "can't compute twice");
+ branch_table[ltos] = __ pc(); // non-volatile_entry point
+ __ pop(ltos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ stdx(R17_tos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_lputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Lbtos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[btos] == 0, "can't compute twice");
+ branch_table[btos] = __ pc(); // non-volatile_entry point
+ __ pop(btos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ stbx(R17_tos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_bputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Lctos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[ctos] == 0, "can't compute twice");
+ branch_table[ctos] = __ pc(); // non-volatile_entry point
+ __ pop(ctos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1..
+ __ sthx(R17_tos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_cputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Lstos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[stos] == 0, "can't compute twice");
+ branch_table[stos] = __ pc(); // non-volatile_entry point
+ __ pop(stos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1.
+ __ sthx(R17_tos, Rclass_or_obj, Roffset);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_sputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ }
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 28, 28); // Align pop.
+ // __ bind(Latos);
+ __ release(); // Volatile entry point (one instruction before non-volatile_entry point).
+ assert(branch_table[atos] == 0, "can't compute twice");
+ branch_table[atos] = __ pc(); // non-volatile_entry point
+ __ pop(atos);
+ if (!is_static) { pop_and_check_object(Rclass_or_obj); } // kills R11_scratch1
+ do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, _bs->kind(), false /* precise */, true /* check null */);
+ if (!is_static) { patch_bytecode(Bytecodes::_fast_aputfield, Rbc, Rscratch, true, byte_no); }
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ __ beq(CR_is_vol, Lvolatile); // Volatile?
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 12);
+ __ bind(Lvolatile);
+ __ fence();
+ }
+ // fallthru: __ b(Lexit);
+
+#ifdef ASSERT
+ for (int i = 0; i<number_of_states; ++i) {
+ assert(branch_table[i], "put initialization");
+ //tty->print_cr("put: %s_branch_table[%d] = 0x%llx (opcode 0x%llx)",
+ // is_static ? "static" : "field", i, branch_table[i], *((unsigned int*)branch_table[i]));
+ }
+#endif
+}
+
+void TemplateTable::putfield(int byte_no) {
+ putfield_or_static(byte_no, false);
+}
+
+void TemplateTable::putstatic(int byte_no) {
+ putfield_or_static(byte_no, true);
+}
+
+// See SPARC. On PPC64, we have a different jvmti_post_field_mod which does the job.
+void TemplateTable::jvmti_post_fast_field_mod() {
+ __ should_not_reach_here();
+}
+
+void TemplateTable::fast_storefield(TosState state) {
+ transition(state, vtos);
+
+ const Register Rcache = R5_ARG3, // Do not use ARG1/2 (causes trouble in jvmti_post_field_mod).
+ Rclass_or_obj = R31, // Needs to survive C call.
+ Roffset = R22_tmp2, // Needs to survive C call.
+ Rflags = R3_ARG1,
+ Rscratch = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rscratch3 = R4_ARG2;
+ const ConditionRegister CR_is_vol = CCR2; // Non-volatile condition register (survives runtime call in do_oop_store).
+
+ // Constant pool already resolved => Load flags and offset of field.
+ __ get_cache_and_index_at_bcp(Rcache, 1);
+ jvmti_post_field_mod(Rcache, Rscratch, false /* not static */);
+ load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false);
+
+ // Get the obj and the final store addr.
+ pop_and_check_object(Rclass_or_obj); // Kills R11_scratch1.
+
+ // Get volatile flag.
+ __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit.
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { __ cmpdi(CR_is_vol, Rscratch, 1); }
+ {
+ Label LnotVolatile;
+ __ beq(CCR0, LnotVolatile);
+ __ release();
+ __ align(32, 12);
+ __ bind(LnotVolatile);
+ }
+
+ // Do the store and fencing.
+ switch(bytecode()) {
+ case Bytecodes::_fast_aputfield:
+ // Store into the field.
+ do_oop_store(_masm, Rclass_or_obj, Roffset, R17_tos, Rscratch, Rscratch2, Rscratch3, _bs->kind(), false /* precise */, true /* check null */);
+ break;
+
+ case Bytecodes::_fast_iputfield:
+ __ stwx(R17_tos, Rclass_or_obj, Roffset);
+ break;
+
+ case Bytecodes::_fast_lputfield:
+ __ stdx(R17_tos, Rclass_or_obj, Roffset);
+ break;
+
+ case Bytecodes::_fast_bputfield:
+ __ stbx(R17_tos, Rclass_or_obj, Roffset);
+ break;
+
+ case Bytecodes::_fast_cputfield:
+ case Bytecodes::_fast_sputfield:
+ __ sthx(R17_tos, Rclass_or_obj, Roffset);
+ break;
+
+ case Bytecodes::_fast_fputfield:
+ __ stfsx(F15_ftos, Rclass_or_obj, Roffset);
+ break;
+
+ case Bytecodes::_fast_dputfield:
+ __ stfdx(F15_ftos, Rclass_or_obj, Roffset);
+ break;
+
+ default: ShouldNotReachHere();
+ }
+
+ if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
+ Label LVolatile;
+ __ beq(CR_is_vol, LVolatile);
+ __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode()));
+
+ __ align(32, 12);
+ __ bind(LVolatile);
+ __ fence();
+ }
+}
+
+void TemplateTable::fast_accessfield(TosState state) {
+ transition(atos, state);
+
+ Label LisVolatile;
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+
+ const Register Rcache = R3_ARG1,
+ Rclass_or_obj = R17_tos,
+ Roffset = R22_tmp2,
+ Rflags = R23_tmp3,
+ Rscratch = R12_scratch2;
+
+ // Constant pool already resolved. Get the field offset.
+ __ get_cache_and_index_at_bcp(Rcache, 1);
+ load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false);
+
+ // JVMTI support
+ jvmti_post_field_access(Rcache, Rscratch, false, true);
+
+ // Get the load address.
+ __ null_check_throw(Rclass_or_obj, -1, Rscratch);
+
+ // Get volatile flag.
+ __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit.
+ __ bne(CCR0, LisVolatile);
+
+ switch(bytecode()) {
+ case Bytecodes::_fast_agetfield:
+ {
+ __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj);
+ __ verify_oop(R17_tos);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj);
+ __ verify_oop(R17_tos);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_igetfield:
+ {
+ __ lwax(R17_tos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lwax(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_lgetfield:
+ {
+ __ ldx(R17_tos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ ldx(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_bgetfield:
+ {
+ __ lbzx(R17_tos, Rclass_or_obj, Roffset);
+ __ extsb(R17_tos, R17_tos);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lbzx(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ extsb(R17_tos, R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_cgetfield:
+ {
+ __ lhzx(R17_tos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lhzx(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_sgetfield:
+ {
+ __ lhax(R17_tos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lhax(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_fgetfield:
+ {
+ __ lfsx(F15_ftos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ Label Ldummy;
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lfsx(F15_ftos, Rclass_or_obj, Roffset);
+ __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync.
+ __ bne_predict_not_taken(CCR0, Ldummy);
+ __ bind(Ldummy);
+ __ isync();
+ break;
+ }
+ case Bytecodes::_fast_dgetfield:
+ {
+ __ lfdx(F15_ftos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()));
+
+ __ bind(LisVolatile);
+ Label Ldummy;
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lfdx(F15_ftos, Rclass_or_obj, Roffset);
+ __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync.
+ __ bne_predict_not_taken(CCR0, Ldummy);
+ __ bind(Ldummy);
+ __ isync();
+ break;
+ }
+ default: ShouldNotReachHere();
+ }
+}
+
+void TemplateTable::fast_xaccess(TosState state) {
+ transition(vtos, state);
+
+ Label LisVolatile;
+ ByteSize cp_base_offset = ConstantPoolCache::base_offset();
+ const Register Rcache = R3_ARG1,
+ Rclass_or_obj = R17_tos,
+ Roffset = R22_tmp2,
+ Rflags = R23_tmp3,
+ Rscratch = R12_scratch2;
+
+ __ ld(Rclass_or_obj, 0, R18_locals);
+
+ // Constant pool already resolved. Get the field offset.
+ __ get_cache_and_index_at_bcp(Rcache, 2);
+ load_field_cp_cache_entry(noreg, Rcache, noreg, Roffset, Rflags, false);
+
+ // JVMTI support not needed, since we switch back to single bytecode as soon as debugger attaches.
+
+ // Needed to report exception at the correct bcp.
+ __ addi(R14_bcp, R14_bcp, 1);
+
+ // Get the load address.
+ __ null_check_throw(Rclass_or_obj, -1, Rscratch);
+
+ // Get volatile flag.
+ __ rldicl_(Rscratch, Rflags, 64-ConstantPoolCacheEntry::is_volatile_shift, 63); // Extract volatile bit.
+ __ bne(CCR0, LisVolatile);
+
+ switch(state) {
+ case atos:
+ {
+ __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj);
+ __ verify_oop(R17_tos);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment.
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ load_heap_oop(R17_tos, (RegisterOrConstant)Roffset, Rclass_or_obj);
+ __ verify_oop(R17_tos);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case itos:
+ {
+ __ lwax(R17_tos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment.
+
+ __ bind(LisVolatile);
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lwax(R17_tos, Rclass_or_obj, Roffset);
+ __ twi_0(R17_tos);
+ __ isync();
+ break;
+ }
+ case ftos:
+ {
+ __ lfsx(F15_ftos, Rclass_or_obj, Roffset);
+ __ dispatch_epilog(state, Bytecodes::length_for(bytecode()) - 1); // Undo bcp increment.
+
+ __ bind(LisVolatile);
+ Label Ldummy;
+ if (support_IRIW_for_not_multiple_copy_atomic_cpu) { __ fence(); }
+ __ lfsx(F15_ftos, Rclass_or_obj, Roffset);
+ __ fcmpu(CCR0, F15_ftos, F15_ftos); // Acquire by cmp-br-isync.
+ __ bne_predict_not_taken(CCR0, Ldummy);
+ __ bind(Ldummy);
+ __ isync();
+ break;
+ }
+ default: ShouldNotReachHere();
+ }
+ __ addi(R14_bcp, R14_bcp, -1);
+}
+
+// ============================================================================
+// Calls
+
+// Common code for invoke
+//
+// Input:
+// - byte_no
+//
+// Output:
+// - Rmethod: The method to invoke next.
+// - Rret_addr: The return address to return to.
+// - Rindex: MethodType (invokehandle) or CallSite obj (invokedynamic)
+// - Rrecv: Cache for "this" pointer, might be noreg if static call.
+// - Rflags: Method flags from const pool cache.
+//
+// Kills:
+// - Rscratch1
+//
+void TemplateTable::prepare_invoke(int byte_no,
+ Register Rmethod, // linked method (or i-klass)
+ Register Rret_addr,// return address
+ Register Rindex, // itable index, MethodType, etc.
+ Register Rrecv, // If caller wants to see it.
+ Register Rflags, // If caller wants to test it.
+ Register Rscratch
+ ) {
+ // Determine flags.
+ const Bytecodes::Code code = bytecode();
+ const bool is_invokeinterface = code == Bytecodes::_invokeinterface;
+ const bool is_invokedynamic = code == Bytecodes::_invokedynamic;
+ const bool is_invokehandle = code == Bytecodes::_invokehandle;
+ const bool is_invokevirtual = code == Bytecodes::_invokevirtual;
+ const bool is_invokespecial = code == Bytecodes::_invokespecial;
+ const bool load_receiver = (Rrecv != noreg);
+ assert(load_receiver == (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic), "");
+
+ assert_different_registers(Rmethod, Rindex, Rflags, Rscratch);
+ assert_different_registers(Rmethod, Rrecv, Rflags, Rscratch);
+ assert_different_registers(Rret_addr, Rscratch);
+
+ load_invoke_cp_cache_entry(byte_no, Rmethod, Rindex, Rflags, is_invokevirtual, false, is_invokedynamic);
+
+ // Saving of SP done in call_from_interpreter.
+
+ // Maybe push "appendix" to arguments.
+ if (is_invokedynamic || is_invokehandle) {
+ Label Ldone;
+ __ rldicl_(R0, Rflags, 64-ConstantPoolCacheEntry::has_appendix_shift, 63);
+ __ beq(CCR0, Ldone);
+ // Push "appendix" (MethodType, CallSite, etc.).
+ // This must be done before we get the receiver,
+ // since the parameter_size includes it.
+ __ load_resolved_reference_at_index(Rscratch, Rindex);
+ __ verify_oop(Rscratch);
+ __ push_ptr(Rscratch);
+ __ bind(Ldone);
+ }
+
+ // Load receiver if needed (after appendix is pushed so parameter size is correct).
+ if (load_receiver) {
+ const Register Rparam_count = Rscratch;
+ __ andi(Rparam_count, Rflags, ConstantPoolCacheEntry::parameter_size_mask);
+ __ load_receiver(Rparam_count, Rrecv);
+ __ verify_oop(Rrecv);
+ }
+
+ // Get return address.
+ {
+ Register Rtable_addr = Rscratch;
+ Register Rret_type = Rret_addr;
+ address table_addr = (address) Interpreter::invoke_return_entry_table_for(code);
+
+ // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value.
+ __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+ __ load_dispatch_table(Rtable_addr, (address*)table_addr);
+ __ sldi(Rret_type, Rret_type, LogBytesPerWord);
+ // Get return address.
+ __ ldx(Rret_addr, Rtable_addr, Rret_type);
+ }
+}
+
+// Helper for virtual calls. Load target out of vtable and jump off!
+// Kills all passed registers.
+void TemplateTable::generate_vtable_call(Register Rrecv_klass, Register Rindex, Register Rret, Register Rtemp) {
+
+ assert_different_registers(Rrecv_klass, Rtemp, Rret);
+ const Register Rtarget_method = Rindex;
+
+ // Get target method & entry point.
+ const int base = InstanceKlass::vtable_start_offset() * wordSize;
+ // Calc vtable addr scale the vtable index by 8.
+ __ sldi(Rindex, Rindex, exact_log2(vtableEntry::size() * wordSize));
+ // Load target.
+ __ addi(Rrecv_klass, Rrecv_klass, base + vtableEntry::method_offset_in_bytes());
+ __ ldx(Rtarget_method, Rindex, Rrecv_klass);
+ __ call_from_interpreter(Rtarget_method, Rret, Rrecv_klass /* scratch1 */, Rtemp /* scratch2 */);
+}
+
+// Virtual or final call. Final calls are rewritten on the fly to run through "fast_finalcall" next time.
+void TemplateTable::invokevirtual(int byte_no) {
+ transition(vtos, vtos);
+
+ Register Rtable_addr = R11_scratch1,
+ Rret_type = R12_scratch2,
+ Rret_addr = R5_ARG3,
+ Rflags = R22_tmp2, // Should survive C call.
+ Rrecv = R3_ARG1,
+ Rrecv_klass = Rrecv,
+ Rvtableindex_or_method = R31, // Should survive C call.
+ Rnum_params = R4_ARG2,
+ Rnew_bc = R6_ARG4;
+
+ Label LnotFinal;
+
+ load_invoke_cp_cache_entry(byte_no, Rvtableindex_or_method, noreg, Rflags, /*virtual*/ true, false, false);
+
+ __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift);
+ __ bfalse(CCR0, LnotFinal);
+
+ patch_bytecode(Bytecodes::_fast_invokevfinal, Rnew_bc, R12_scratch2);
+ invokevfinal_helper(Rvtableindex_or_method, Rflags, R11_scratch1, R12_scratch2);
+
+ __ align(32, 12);
+ __ bind(LnotFinal);
+ // Load "this" pointer (receiver).
+ __ rldicl(Rnum_params, Rflags, 64, 48);
+ __ load_receiver(Rnum_params, Rrecv);
+ __ verify_oop(Rrecv);
+
+ // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value.
+ __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+ __ load_dispatch_table(Rtable_addr, Interpreter::invoke_return_entry_table());
+ __ sldi(Rret_type, Rret_type, LogBytesPerWord);
+ __ ldx(Rret_addr, Rret_type, Rtable_addr);
+ __ null_check_throw(Rrecv, oopDesc::klass_offset_in_bytes(), R11_scratch1);
+ __ load_klass(Rrecv_klass, Rrecv);
+ __ verify_klass_ptr(Rrecv_klass);
+ __ profile_virtual_call(Rrecv_klass, R11_scratch1, R12_scratch2, false);
+
+ generate_vtable_call(Rrecv_klass, Rvtableindex_or_method, Rret_addr, R11_scratch1);
+}
+
+void TemplateTable::fast_invokevfinal(int byte_no) {
+ transition(vtos, vtos);
+
+ assert(byte_no == f2_byte, "use this argument");
+ Register Rflags = R22_tmp2,
+ Rmethod = R31;
+ load_invoke_cp_cache_entry(byte_no, Rmethod, noreg, Rflags, /*virtual*/ true, /*is_invokevfinal*/ true, false);
+ invokevfinal_helper(Rmethod, Rflags, R11_scratch1, R12_scratch2);
+}
+
+void TemplateTable::invokevfinal_helper(Register Rmethod, Register Rflags, Register Rscratch1, Register Rscratch2) {
+
+ assert_different_registers(Rmethod, Rflags, Rscratch1, Rscratch2);
+
+ // Load receiver from stack slot.
+ Register Rrecv = Rscratch2;
+ Register Rnum_params = Rrecv;
+
+ __ ld(Rnum_params, in_bytes(Method::const_offset()), Rmethod);
+ __ lhz(Rnum_params /* number of params */, in_bytes(ConstMethod::size_of_parameters_offset()), Rnum_params);
+
+ // Get return address.
+ Register Rtable_addr = Rscratch1,
+ Rret_addr = Rflags,
+ Rret_type = Rret_addr;
+ // Get return type. It's coded into the upper 4 bits of the lower half of the 64 bit value.
+ __ rldicl(Rret_type, Rflags, 64-ConstantPoolCacheEntry::tos_state_shift, 64-ConstantPoolCacheEntry::tos_state_bits);
+ __ load_dispatch_table(Rtable_addr, Interpreter::invoke_return_entry_table());
+ __ sldi(Rret_type, Rret_type, LogBytesPerWord);
+ __ ldx(Rret_addr, Rret_type, Rtable_addr);
+
+ // Load receiver and receiver NULL check.
+ __ load_receiver(Rnum_params, Rrecv);
+ __ null_check_throw(Rrecv, -1, Rscratch1);
+
+ __ profile_final_call(Rrecv, Rscratch1);
+
+ // Do the call.
+ __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1, Rscratch2);
+}
+
+void TemplateTable::invokespecial(int byte_no) {
+ assert(byte_no == f1_byte, "use this argument");
+ transition(vtos, vtos);
+
+ Register Rtable_addr = R3_ARG1,
+ Rret_addr = R4_ARG2,
+ Rflags = R5_ARG3,
+ Rreceiver = R6_ARG4,
+ Rmethod = R31;
+
+ prepare_invoke(byte_no, Rmethod, Rret_addr, noreg, Rreceiver, Rflags, R11_scratch1);
+
+ // Receiver NULL check.
+ __ null_check_throw(Rreceiver, -1, R11_scratch1);
+
+ __ profile_call(R11_scratch1, R12_scratch2);
+ __ call_from_interpreter(Rmethod, Rret_addr, R11_scratch1, R12_scratch2);
+}
+
+void TemplateTable::invokestatic(int byte_no) {
+ assert(byte_no == f1_byte, "use this argument");
+ transition(vtos, vtos);
+
+ Register Rtable_addr = R3_ARG1,
+ Rret_addr = R4_ARG2,
+ Rflags = R5_ARG3;
+
+ prepare_invoke(byte_no, R19_method, Rret_addr, noreg, noreg, Rflags, R11_scratch1);
+
+ __ profile_call(R11_scratch1, R12_scratch2);
+ __ call_from_interpreter(R19_method, Rret_addr, R11_scratch1, R12_scratch2);
+}
+
+void TemplateTable::invokeinterface_object_method(Register Rrecv_klass,
+ Register Rret,
+ Register Rflags,
+ Register Rindex,
+ Register Rtemp1,
+ Register Rtemp2) {
+
+ assert_different_registers(Rindex, Rret, Rrecv_klass, Rflags, Rtemp1, Rtemp2);
+ Label LnotFinal;
+
+ // Check for vfinal.
+ __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift);
+ __ bfalse(CCR0, LnotFinal);
+
+ Register Rscratch = Rflags; // Rflags is dead now.
+
+ // Final call case.
+ __ profile_final_call(Rtemp1, Rscratch);
+ // Do the final call - the index (f2) contains the method.
+ __ call_from_interpreter(Rindex, Rret, Rscratch, Rrecv_klass /* scratch */);
+
+ // Non-final callc case.
+ __ bind(LnotFinal);
+ __ profile_virtual_call(Rrecv_klass, Rtemp1, Rscratch, false);
+ generate_vtable_call(Rrecv_klass, Rindex, Rret, Rscratch);
+}
+
+void TemplateTable::invokeinterface(int byte_no) {
+ assert(byte_no == f1_byte, "use this argument");
+ transition(vtos, vtos);
+
+ const Register Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2,
+ Rscratch3 = R9_ARG7,
+ Rscratch4 = R10_ARG8,
+ Rtable_addr = Rscratch2,
+ Rinterface_klass = R5_ARG3,
+ Rret_type = R8_ARG6,
+ Rret_addr = Rret_type,
+ Rindex = R6_ARG4,
+ Rreceiver = R4_ARG2,
+ Rrecv_klass = Rreceiver,
+ Rflags = R7_ARG5;
+
+ prepare_invoke(byte_no, Rinterface_klass, Rret_addr, Rindex, Rreceiver, Rflags, Rscratch1);
+
+ // Get receiver klass.
+ __ null_check_throw(Rreceiver, oopDesc::klass_offset_in_bytes(), Rscratch3);
+ __ load_klass(Rrecv_klass, Rreceiver);
+
+ // Check corner case object method.
+ Label LobjectMethod;
+
+ __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_forced_virtual_shift);
+ __ btrue(CCR0, LobjectMethod);
+
+ // Fallthrough: The normal invokeinterface case.
+ __ profile_virtual_call(Rrecv_klass, Rscratch1, Rscratch2, false);
+
+ // Find entry point to call.
+ Label Lthrow_icc, Lthrow_ame;
+ // Result will be returned in Rindex.
+ __ mr(Rscratch4, Rrecv_klass);
+ __ mr(Rscratch3, Rindex);
+ __ lookup_interface_method(Rrecv_klass, Rinterface_klass, Rindex, Rindex, Rscratch1, Rscratch2, Lthrow_icc);
+
+ __ cmpdi(CCR0, Rindex, 0);
+ __ beq(CCR0, Lthrow_ame);
+ // Found entry. Jump off!
+ __ call_from_interpreter(Rindex, Rret_addr, Rscratch1, Rscratch2);
+
+ // Vtable entry was NULL => Throw abstract method error.
+ __ bind(Lthrow_ame);
+ __ mr(Rrecv_klass, Rscratch4);
+ __ mr(Rindex, Rscratch3);
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_AbstractMethodError));
+
+ // Interface was not found => Throw incompatible class change error.
+ __ bind(Lthrow_icc);
+ __ mr(Rrecv_klass, Rscratch4);
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
+
+ __ should_not_reach_here();
+
+ // Special case of invokeinterface called for virtual method of
+ // java.lang.Object. See ConstantPoolCacheEntry::set_method() for details:
+ // The invokeinterface was rewritten to a invokevirtual, hence we have
+ // to handle this corner case. This code isn't produced by javac, but could
+ // be produced by another compliant java compiler.
+ __ bind(LobjectMethod);
+ invokeinterface_object_method(Rrecv_klass, Rret_addr, Rflags, Rindex, Rscratch1, Rscratch2);
+}
+
+void TemplateTable::invokedynamic(int byte_no) {
+ transition(vtos, vtos);
+
+ const Register Rret_addr = R3_ARG1,
+ Rflags = R4_ARG2,
+ Rmethod = R22_tmp2,
+ Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2;
+
+ if (!EnableInvokeDynamic) {
+ // We should not encounter this bytecode if !EnableInvokeDynamic.
+ // The verifier will stop it. However, if we get past the verifier,
+ // this will stop the thread in a reasonable way, without crashing the JVM.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_IncompatibleClassChangeError));
+ // The call_VM checks for exception, so we should never return here.
+ __ should_not_reach_here();
+ return;
+ }
+
+ prepare_invoke(byte_no, Rmethod, Rret_addr, Rscratch1, noreg, Rflags, Rscratch2);
+
+ // Profile this call.
+ __ profile_call(Rscratch1, Rscratch2);
+
+ // Off we go. With the new method handles, we don't jump to a method handle
+ // entry any more. Instead, we pushed an "appendix" in prepare invoke, which happens
+ // to be the callsite object the bootstrap method returned. This is passed to a
+ // "link" method which does the dispatch (Most likely just grabs the MH stored
+ // inside the callsite and does an invokehandle).
+ __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */);
+}
+
+void TemplateTable::invokehandle(int byte_no) {
+ transition(vtos, vtos);
+
+ const Register Rret_addr = R3_ARG1,
+ Rflags = R4_ARG2,
+ Rrecv = R5_ARG3,
+ Rmethod = R22_tmp2,
+ Rscratch1 = R11_scratch1,
+ Rscratch2 = R12_scratch2;
+
+ if (!EnableInvokeDynamic) {
+ // Rewriter does not generate this bytecode.
+ __ should_not_reach_here();
+ return;
+ }
+
+ prepare_invoke(byte_no, Rmethod, Rret_addr, Rscratch1, Rrecv, Rflags, Rscratch2);
+ __ verify_method_ptr(Rmethod);
+ __ null_check_throw(Rrecv, -1, Rscratch2);
+
+ __ profile_final_call(Rrecv, Rscratch1);
+
+ // Still no call from handle => We call the method handle interpreter here.
+ __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */);
+}
+
+// =============================================================================
+// Allocation
+
+// Puts allocated obj ref onto the expression stack.
+void TemplateTable::_new() {
+ transition(vtos, atos);
+
+ Label Lslow_case,
+ Ldone,
+ Linitialize_header,
+ Lallocate_shared,
+ Linitialize_object; // Including clearing the fields.
+
+ const Register RallocatedObject = R17_tos,
+ RinstanceKlass = R9_ARG7,
+ Rscratch = R11_scratch1,
+ Roffset = R8_ARG6,
+ Rinstance_size = Roffset,
+ Rcpool = R4_ARG2,
+ Rtags = R3_ARG1,
+ Rindex = R5_ARG3;
+
+ const bool allow_shared_alloc = Universe::heap()->supports_inline_contig_alloc() && !CMSIncrementalMode;
+
+ // --------------------------------------------------------------------------
+ // Check if fast case is possible.
+
+ // Load pointers to const pool and const pool's tags array.
+ __ get_cpool_and_tags(Rcpool, Rtags);
+ // Load index of constant pool entry.
+ __ get_2_byte_integer_at_bcp(1, Rindex, InterpreterMacroAssembler::Unsigned);
+
+ if (UseTLAB) {
+ // Make sure the class we're about to instantiate has been resolved
+ // This is done before loading instanceKlass to be consistent with the order
+ // how Constant Pool is updated (see ConstantPoolCache::klass_at_put).
+ __ addi(Rtags, Rtags, Array<u1>::base_offset_in_bytes());
+ __ lbzx(Rtags, Rindex, Rtags);
+
+ __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class);
+ __ bne(CCR0, Lslow_case);
+
+ // Get instanceKlass (load from Rcpool + sizeof(ConstantPool) + Rindex*BytesPerWord).
+ __ sldi(Roffset, Rindex, LogBytesPerWord);
+ __ addi(Rscratch, Rcpool, sizeof(ConstantPool));
+ __ isync(); // Order load of instance Klass wrt. tags.
+ __ ldx(RinstanceKlass, Roffset, Rscratch);
+
+ // Make sure klass is fully initialized and get instance_size.
+ __ lbz(Rscratch, in_bytes(InstanceKlass::init_state_offset()), RinstanceKlass);
+ __ lwz(Rinstance_size, in_bytes(Klass::layout_helper_offset()), RinstanceKlass);
+
+ __ cmpdi(CCR1, Rscratch, InstanceKlass::fully_initialized);
+ // Make sure klass does not have has_finalizer, or is abstract, or interface or java/lang/Class.
+ __ andi_(R0, Rinstance_size, Klass::_lh_instance_slow_path_bit); // slow path bit equals 0?
+
+ __ crnand(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); // slow path bit set or not fully initialized?
+ __ beq(CCR0, Lslow_case);
+
+ // --------------------------------------------------------------------------
+ // Fast case:
+ // Allocate the instance.
+ // 1) Try to allocate in the TLAB.
+ // 2) If fail, and the TLAB is not full enough to discard, allocate in the shared Eden.
+ // 3) If the above fails (or is not applicable), go to a slow case (creates a new TLAB, etc.).
+
+ Register RoldTopValue = RallocatedObject; // Object will be allocated here if it fits.
+ Register RnewTopValue = R6_ARG4;
+ Register RendValue = R7_ARG5;
+
+ // Check if we can allocate in the TLAB.
+ __ ld(RoldTopValue, in_bytes(JavaThread::tlab_top_offset()), R16_thread);
+ __ ld(RendValue, in_bytes(JavaThread::tlab_end_offset()), R16_thread);
+
+ __ add(RnewTopValue, Rinstance_size, RoldTopValue);
+
+ // If there is enough space, we do not CAS and do not clear.
+ __ cmpld(CCR0, RnewTopValue, RendValue);
+ __ bgt(CCR0, allow_shared_alloc ? Lallocate_shared : Lslow_case);
+
+ __ std(RnewTopValue, in_bytes(JavaThread::tlab_top_offset()), R16_thread);
+
+ if (ZeroTLAB) {
+ // The fields have already been cleared.
+ __ b(Linitialize_header);
+ } else {
+ // Initialize both the header and fields.
+ __ b(Linitialize_object);
+ }
+
+ // Fall through: TLAB was too small.
+ if (allow_shared_alloc) {
+ Register RtlabWasteLimitValue = R10_ARG8;
+ Register RfreeValue = RnewTopValue;
+
+ __ bind(Lallocate_shared);
+ // Check if tlab should be discarded (refill_waste_limit >= free).
+ __ ld(RtlabWasteLimitValue, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), R16_thread);
+ __ subf(RfreeValue, RoldTopValue, RendValue);
+ __ srdi(RfreeValue, RfreeValue, LogHeapWordSize); // in dwords
+ __ cmpld(CCR0, RtlabWasteLimitValue, RfreeValue);
+ __ bge(CCR0, Lslow_case);
+
+ // Increment waste limit to prevent getting stuck on this slow path.
+ __ addi(RtlabWasteLimitValue, RtlabWasteLimitValue, (int)ThreadLocalAllocBuffer::refill_waste_limit_increment());
+ __ std(RtlabWasteLimitValue, in_bytes(JavaThread::tlab_refill_waste_limit_offset()), R16_thread);
+ }
+ // else: No allocation in the shared eden. // fallthru: __ b(Lslow_case);
+ }
+ // else: Always go the slow path.
+
+ // --------------------------------------------------------------------------
+ // slow case
+ __ bind(Lslow_case);
+ call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::_new), Rcpool, Rindex);
+
+ if (UseTLAB) {
+ __ b(Ldone);
+ // --------------------------------------------------------------------------
+ // Init1: Zero out newly allocated memory.
+
+ if (!ZeroTLAB || allow_shared_alloc) {
+ // Clear object fields.
+ __ bind(Linitialize_object);
+
+ // Initialize remaining object fields.
+ Register Rbase = Rtags;
+ __ addi(Rinstance_size, Rinstance_size, 7 - (int)sizeof(oopDesc));
+ __ addi(Rbase, RallocatedObject, sizeof(oopDesc));
+ __ srdi(Rinstance_size, Rinstance_size, 3);
+
+ // Clear out object skipping header. Takes also care of the zero length case.
+ __ clear_memory_doubleword(Rbase, Rinstance_size);
+ // fallthru: __ b(Linitialize_header);
+ }
+
+ // --------------------------------------------------------------------------
+ // Init2: Initialize the header: mark, klass
+ __ bind(Linitialize_header);
+
+ // Init mark.
+ if (UseBiasedLocking) {
+ __ ld(Rscratch, in_bytes(Klass::prototype_header_offset()), RinstanceKlass);
+ } else {
+ __ load_const_optimized(Rscratch, markOopDesc::prototype(), R0);
+ }
+ __ std(Rscratch, oopDesc::mark_offset_in_bytes(), RallocatedObject);
+
+ // Init klass.
+ __ store_klass_gap(RallocatedObject);
+ __ store_klass(RallocatedObject, RinstanceKlass, Rscratch); // klass (last for cms)
+
+ // Check and trigger dtrace event.
+ {
+ SkipIfEqualZero skip_if(_masm, Rscratch, &DTraceAllocProbes);
+ __ push(atos);
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_object_alloc));
+ __ pop(atos);
+ }
+ }
+
+ // continue
+ __ bind(Ldone);
+
+ // Must prevent reordering of stores for object initialization with stores that publish the new object.
+ __ membar(Assembler::StoreStore);
+}
+
+void TemplateTable::newarray() {
+ transition(itos, atos);
+
+ __ lbz(R4, 1, R14_bcp);
+ __ extsw(R5, R17_tos);
+ call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::newarray), R4, R5 /* size */);
+
+ // Must prevent reordering of stores for object initialization with stores that publish the new object.
+ __ membar(Assembler::StoreStore);
+}
+
+void TemplateTable::anewarray() {
+ transition(itos, atos);
+
+ __ get_constant_pool(R4);
+ __ get_2_byte_integer_at_bcp(1, R5, InterpreterMacroAssembler::Unsigned);
+ __ extsw(R6, R17_tos); // size
+ call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::anewarray), R4 /* pool */, R5 /* index */, R6 /* size */);
+
+ // Must prevent reordering of stores for object initialization with stores that publish the new object.
+ __ membar(Assembler::StoreStore);
+}
+
+// Allocate a multi dimensional array
+void TemplateTable::multianewarray() {
+ transition(vtos, atos);
+
+ Register Rptr = R31; // Needs to survive C call.
+
+ // Put ndims * wordSize into frame temp slot
+ __ lbz(Rptr, 3, R14_bcp);
+ __ sldi(Rptr, Rptr, Interpreter::logStackElementSize);
+ // Esp points past last_dim, so set to R4 to first_dim address.
+ __ add(R4, Rptr, R15_esp);
+ call_VM(R17_tos, CAST_FROM_FN_PTR(address, InterpreterRuntime::multianewarray), R4 /* first_size_address */);
+ // Pop all dimensions off the stack.
+ __ add(R15_esp, Rptr, R15_esp);
+
+ // Must prevent reordering of stores for object initialization with stores that publish the new object.
+ __ membar(Assembler::StoreStore);
+}
+
+void TemplateTable::arraylength() {
+ transition(atos, itos);
+
+ Label LnoException;
+ __ verify_oop(R17_tos);
+ __ null_check_throw(R17_tos, arrayOopDesc::length_offset_in_bytes(), R11_scratch1);
+ __ lwa(R17_tos, arrayOopDesc::length_offset_in_bytes(), R17_tos);
+}
+
+// ============================================================================
+// Typechecks
+
+void TemplateTable::checkcast() {
+ transition(atos, atos);
+
+ Label Ldone, Lis_null, Lquicked, Lresolved;
+ Register Roffset = R5_ARG3,
+ RobjKlass = R4_ARG2,
+ RspecifiedKlass = R6_ARG4, // Generate_ClassCastException_verbose_handler will expect this register.
+ Rcpool = R11_scratch1,
+ Rtags = R12_scratch2;
+
+ // Null does not pass.
+ __ cmpdi(CCR0, R17_tos, 0);
+ __ beq(CCR0, Lis_null);
+
+ // Get constant pool tag to find out if the bytecode has already been "quickened".
+ __ get_cpool_and_tags(Rcpool, Rtags);
+
+ __ get_2_byte_integer_at_bcp(1, Roffset, InterpreterMacroAssembler::Unsigned);
+
+ __ addi(Rtags, Rtags, Array<u1>::base_offset_in_bytes());
+ __ lbzx(Rtags, Rtags, Roffset);
+
+ __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class);
+ __ beq(CCR0, Lquicked);
+
+ // Call into the VM to "quicken" instanceof.
+ __ push_ptr(); // for GC
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
+ __ get_vm_result_2(RspecifiedKlass);
+ __ pop_ptr(); // Restore receiver.
+ __ b(Lresolved);
+
+ // Extract target class from constant pool.
+ __ bind(Lquicked);
+ __ sldi(Roffset, Roffset, LogBytesPerWord);
+ __ addi(Rcpool, Rcpool, sizeof(ConstantPool));
+ __ isync(); // Order load of specified Klass wrt. tags.
+ __ ldx(RspecifiedKlass, Rcpool, Roffset);
+
+ // Do the checkcast.
+ __ bind(Lresolved);
+ // Get value klass in RobjKlass.
+ __ load_klass(RobjKlass, R17_tos);
+ // Generate a fast subtype check. Branch to cast_ok if no failure. Return 0 if failure.
+ __ gen_subtype_check(RobjKlass, RspecifiedKlass, /*3 temp regs*/ Roffset, Rcpool, Rtags, /*target if subtype*/ Ldone);
+
+ // Not a subtype; so must throw exception
+ // Target class oop is in register R6_ARG4 == RspecifiedKlass by convention.
+ __ load_dispatch_table(R11_scratch1, (address*)Interpreter::_throw_ClassCastException_entry);
+ __ mtctr(R11_scratch1);
+ __ bctr();
+
+ // Profile the null case.
+ __ align(32, 12);
+ __ bind(Lis_null);
+ __ profile_null_seen(R11_scratch1, Rtags); // Rtags used as scratch.
+
+ __ align(32, 12);
+ __ bind(Ldone);
+}
+
+// Output:
+// - tos == 0: Obj was null or not an instance of class.
+// - tos == 1: Obj was an instance of class.
+void TemplateTable::instanceof() {
+ transition(atos, itos);
+
+ Label Ldone, Lis_null, Lquicked, Lresolved;
+ Register Roffset = R5_ARG3,
+ RobjKlass = R4_ARG2,
+ RspecifiedKlass = R6_ARG4, // Generate_ClassCastException_verbose_handler will expect the value in this register.
+ Rcpool = R11_scratch1,
+ Rtags = R12_scratch2;
+
+ // Null does not pass.
+ __ cmpdi(CCR0, R17_tos, 0);
+ __ beq(CCR0, Lis_null);
+
+ // Get constant pool tag to find out if the bytecode has already been "quickened".
+ __ get_cpool_and_tags(Rcpool, Rtags);
+
+ __ get_2_byte_integer_at_bcp(1, Roffset, InterpreterMacroAssembler::Unsigned);
+
+ __ addi(Rtags, Rtags, Array<u1>::base_offset_in_bytes());
+ __ lbzx(Rtags, Rtags, Roffset);
+
+ __ cmpdi(CCR0, Rtags, JVM_CONSTANT_Class);
+ __ beq(CCR0, Lquicked);
+
+ // Call into the VM to "quicken" instanceof.
+ __ push_ptr(); // for GC
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::quicken_io_cc));
+ __ get_vm_result_2(RspecifiedKlass);
+ __ pop_ptr(); // Restore receiver.
+ __ b(Lresolved);
+
+ // Extract target class from constant pool.
+ __ bind(Lquicked);
+ __ sldi(Roffset, Roffset, LogBytesPerWord);
+ __ addi(Rcpool, Rcpool, sizeof(ConstantPool));
+ __ isync(); // Order load of specified Klass wrt. tags.
+ __ ldx(RspecifiedKlass, Rcpool, Roffset);
+
+ // Do the checkcast.
+ __ bind(Lresolved);
+ // Get value klass in RobjKlass.
+ __ load_klass(RobjKlass, R17_tos);
+ // Generate a fast subtype check. Branch to cast_ok if no failure. Return 0 if failure.
+ __ li(R17_tos, 1);
+ __ gen_subtype_check(RobjKlass, RspecifiedKlass, /*3 temp regs*/ Roffset, Rcpool, Rtags, /*target if subtype*/ Ldone);
+ __ li(R17_tos, 0);
+
+ if (ProfileInterpreter) {
+ __ b(Ldone);
+ }
+
+ // Profile the null case.
+ __ align(32, 12);
+ __ bind(Lis_null);
+ __ profile_null_seen(Rcpool, Rtags); // Rcpool and Rtags used as scratch.
+
+ __ align(32, 12);
+ __ bind(Ldone);
+}
+
+// =============================================================================
+// Breakpoints
+
+void TemplateTable::_breakpoint() {
+ transition(vtos, vtos);
+
+ // Get the unpatched byte code.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::get_original_bytecode_at), R19_method, R14_bcp);
+ __ mr(R31, R3_RET);
+
+ // Post the breakpoint event.
+ __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::_breakpoint), R19_method, R14_bcp);
+
+ // Complete the execution of original bytecode.
+ __ dispatch_Lbyte_code(vtos, R31, Interpreter::normal_table(vtos));
+}
+
+// =============================================================================
+// Exceptions
+
+void TemplateTable::athrow() {
+ transition(atos, vtos);
+
+ // Exception oop is in tos
+ __ verify_oop(R17_tos);
+
+ __ null_check_throw(R17_tos, -1, R11_scratch1);
+
+ // Throw exception interpreter entry expects exception oop to be in R3.
+ __ mr(R3_RET, R17_tos);
+ __ load_dispatch_table(R11_scratch1, (address*)Interpreter::throw_exception_entry());
+ __ mtctr(R11_scratch1);
+ __ bctr();
+}
+
+// =============================================================================
+// Synchronization
+// Searches the basic object lock list on the stack for a free slot
+// and uses it to lock the obect in tos.
+//
+// Recursive locking is enabled by exiting the search if the same
+// object is already found in the list. Thus, a new basic lock obj lock
+// is allocated "higher up" in the stack and thus is found first
+// at next monitor exit.
+void TemplateTable::monitorenter() {
+ transition(atos, vtos);
+
+ __ verify_oop(R17_tos);
+
+ Register Rcurrent_monitor = R11_scratch1,
+ Rcurrent_obj = R12_scratch2,
+ Robj_to_lock = R17_tos,
+ Rscratch1 = R3_ARG1,
+ Rscratch2 = R4_ARG2,
+ Rscratch3 = R5_ARG3,
+ Rcurrent_obj_addr = R6_ARG4;
+
+ // ------------------------------------------------------------------------------
+ // Null pointer exception.
+ __ null_check_throw(Robj_to_lock, -1, R11_scratch1);
+
+ // Try to acquire a lock on the object.
+ // Repeat until succeeded (i.e., until monitorenter returns true).
+
+ // ------------------------------------------------------------------------------
+ // Find a free slot in the monitor block.
+ Label Lfound, Lexit, Lallocate_new;
+ ConditionRegister found_free_slot = CCR0,
+ found_same_obj = CCR1,
+ reached_limit = CCR6;
+ {
+ Label Lloop, Lentry;
+ Register Rlimit = Rcurrent_monitor;
+
+ // Set up search loop - start with topmost monitor.
+ __ add(Rcurrent_obj_addr, BasicObjectLock::obj_offset_in_bytes(), R26_monitor);
+
+ __ ld(Rlimit, 0, R1_SP);
+ __ addi(Rlimit, Rlimit, - (frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes() - BasicObjectLock::obj_offset_in_bytes())); // Monitor base
+
+ // Check if any slot is present => short cut to allocation if not.
+ __ cmpld(reached_limit, Rcurrent_obj_addr, Rlimit);
+ __ bgt(reached_limit, Lallocate_new);
+
+ // Pre-load topmost slot.
+ __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
+ __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize);
+ // The search loop.
+ __ bind(Lloop);
+ // Found free slot?
+ __ cmpdi(found_free_slot, Rcurrent_obj, 0);
+ // Is this entry for same obj? If so, stop the search and take the found
+ // free slot or allocate a new one to enable recursive locking.
+ __ cmpd(found_same_obj, Rcurrent_obj, Robj_to_lock);
+ __ cmpld(reached_limit, Rcurrent_obj_addr, Rlimit);
+ __ beq(found_free_slot, Lexit);
+ __ beq(found_same_obj, Lallocate_new);
+ __ bgt(reached_limit, Lallocate_new);
+ // Check if last allocated BasicLockObj reached.
+ __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
+ __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize);
+ // Next iteration if unchecked BasicObjectLocks exist on the stack.
+ __ b(Lloop);
+ }
+
+ // ------------------------------------------------------------------------------
+ // Check if we found a free slot.
+ __ bind(Lexit);
+
+ __ addi(Rcurrent_monitor, Rcurrent_obj_addr, -(frame::interpreter_frame_monitor_size() * wordSize) - BasicObjectLock::obj_offset_in_bytes());
+ __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, - frame::interpreter_frame_monitor_size() * wordSize);
+ __ b(Lfound);
+
+ // We didn't find a free BasicObjLock => allocate one.
+ __ align(32, 12);
+ __ bind(Lallocate_new);
+ __ add_monitor_to_stack(false, Rscratch1, Rscratch2);
+ __ mr(Rcurrent_monitor, R26_monitor);
+ __ addi(Rcurrent_obj_addr, R26_monitor, BasicObjectLock::obj_offset_in_bytes());
+
+ // ------------------------------------------------------------------------------
+ // We now have a slot to lock.
+ __ bind(Lfound);
+
+ // Increment bcp to point to the next bytecode, so exception handling for async. exceptions work correctly.
+ // The object has already been poped from the stack, so the expression stack looks correct.
+ __ addi(R14_bcp, R14_bcp, 1);
+
+ __ std(Robj_to_lock, 0, Rcurrent_obj_addr);
+ __ lock_object(Rcurrent_monitor, Robj_to_lock);
+
+ // Check if there's enough space on the stack for the monitors after locking.
+ Label Lskip_stack_check;
+ // Optimization: If the monitors stack section is less then a std page size (4K) don't run
+ // the stack check. There should be enough shadow pages to fit that in.
+ __ ld(Rscratch3, 0, R1_SP);
+ __ sub(Rscratch3, Rscratch3, R26_monitor);
+ __ cmpdi(CCR0, Rscratch3, 4*K);
+ __ blt(CCR0, Lskip_stack_check);
+
+ DEBUG_ONLY(__ untested("stack overflow check during monitor enter");)
+ __ li(Rscratch1, 0);
+ __ generate_stack_overflow_check_with_compare_and_throw(Rscratch1, Rscratch2);
+
+ __ align(32, 12);
+ __ bind(Lskip_stack_check);
+
+ // The bcp has already been incremented. Just need to dispatch to next instruction.
+ __ dispatch_next(vtos);
+}
+
+void TemplateTable::monitorexit() {
+ transition(atos, vtos);
+ __ verify_oop(R17_tos);
+
+ Register Rcurrent_monitor = R11_scratch1,
+ Rcurrent_obj = R12_scratch2,
+ Robj_to_lock = R17_tos,
+ Rcurrent_obj_addr = R3_ARG1,
+ Rlimit = R4_ARG2;
+ Label Lfound, Lillegal_monitor_state;
+
+ // Check corner case: unbalanced monitorEnter / Exit.
+ __ ld(Rlimit, 0, R1_SP);
+ __ addi(Rlimit, Rlimit, - (frame::ijava_state_size + frame::interpreter_frame_monitor_size_in_bytes())); // Monitor base
+
+ // Null pointer check.
+ __ null_check_throw(Robj_to_lock, -1, R11_scratch1);
+
+ __ cmpld(CCR0, R26_monitor, Rlimit);
+ __ bgt(CCR0, Lillegal_monitor_state);
+
+ // Find the corresponding slot in the monitors stack section.
+ {
+ Label Lloop;
+
+ // Start with topmost monitor.
+ __ addi(Rcurrent_obj_addr, R26_monitor, BasicObjectLock::obj_offset_in_bytes());
+ __ addi(Rlimit, Rlimit, BasicObjectLock::obj_offset_in_bytes());
+ __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
+ __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize);
+
+ __ bind(Lloop);
+ // Is this entry for same obj?
+ __ cmpd(CCR0, Rcurrent_obj, Robj_to_lock);
+ __ beq(CCR0, Lfound);
+
+ // Check if last allocated BasicLockObj reached.
+
+ __ ld(Rcurrent_obj, 0, Rcurrent_obj_addr);
+ __ cmpld(CCR0, Rcurrent_obj_addr, Rlimit);
+ __ addi(Rcurrent_obj_addr, Rcurrent_obj_addr, frame::interpreter_frame_monitor_size() * wordSize);
+
+ // Next iteration if unchecked BasicObjectLocks exist on the stack.
+ __ ble(CCR0, Lloop);
+ }
+
+ // Fell through without finding the basic obj lock => throw up!
+ __ bind(Lillegal_monitor_state);
+ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_illegal_monitor_state_exception));
+ __ should_not_reach_here();
+
+ __ align(32, 12);
+ __ bind(Lfound);
+ __ addi(Rcurrent_monitor, Rcurrent_obj_addr,
+ -(frame::interpreter_frame_monitor_size() * wordSize) - BasicObjectLock::obj_offset_in_bytes());
+ __ unlock_object(Rcurrent_monitor);
+}
+
+// ============================================================================
+// Wide bytecodes
+
+// Wide instructions. Simply redirects to the wide entry point for that instruction.
+void TemplateTable::wide() {
+ transition(vtos, vtos);
+
+ const Register Rtable = R11_scratch1,
+ Rindex = R12_scratch2,
+ Rtmp = R0;
+
+ __ lbz(Rindex, 1, R14_bcp);
+
+ __ load_dispatch_table(Rtable, Interpreter::_wentry_point);
+
+ __ slwi(Rindex, Rindex, LogBytesPerWord);
+ __ ldx(Rtmp, Rtable, Rindex);
+ __ mtctr(Rtmp);
+ __ bctr();
+ // Note: the bcp increment step is part of the individual wide bytecode implementations.
+}
+#endif // !CC_INTERP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/ppc/vm/templateTable_ppc_64.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013, 2014 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.
+ *
+ */
+
+#ifndef CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP
+#define CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP
+
+ static void prepare_invoke(int byte_no, Register Rmethod, Register Rret_addr, Register Rindex, Register Rrecv, Register Rflags, Register Rscratch);
+ static void invokevfinal_helper(Register Rmethod, Register Rflags, Register Rscratch1, Register Rscratch2);
+ static void generate_vtable_call(Register Rrecv_klass, Register Rindex, Register Rret, Register Rtemp);
+ static void invokeinterface_object_method(Register Rrecv_klass, Register Rret, Register Rflags, Register Rindex, Register Rtemp, Register Rtemp2);
+
+ // Branch_conditional which takes TemplateTable::Condition.
+ static void branch_conditional(ConditionRegister crx, TemplateTable::Condition cc, Label& L, bool invert = false);
+ static void if_cmp_common(Register Rfirst, Register Rsecond, Register Rscratch1, Register Rscratch2, Condition cc, bool is_jint, bool cmp0);
+
+#endif // CPU_PPC_VM_TEMPLATETABLE_PPC_64_HPP
--- a/hotspot/src/share/vm/interpreter/templateTable.hpp Wed Mar 12 11:24:26 2014 -0700
+++ b/hotspot/src/share/vm/interpreter/templateTable.hpp Mon Mar 10 12:58:02 2014 +0100
@@ -376,6 +376,9 @@
#ifdef TARGET_ARCH_MODEL_ppc_32
# include "templateTable_ppc_32.hpp"
#endif
+#ifdef TARGET_ARCH_MODEL_ppc_64
+# include "templateTable_ppc_64.hpp"
+#endif
};
#endif /* !CC_INTERP */