# HG changeset patch # User goetz # Date 1394452682 -3600 # Node ID fd1b9f02cc91475bfb5e37ccff10d80c749e3fb4 # Parent 31e80afe3fed55066e6d81f5e066b9a4e78162b9 8036976: PPC64: implement the template interpreter Reviewed-by: kvn, coleenp Contributed-by: axel.siebenborn@sap.com, martin.doerr@sap.com diff -r 31e80afe3fed -r fd1b9f02cc91 make/aix/Makefile --- a/make/aix/Makefile Thu Mar 06 10:55:28 2014 -0800 +++ b/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))","") diff -r 31e80afe3fed -r fd1b9f02cc91 make/linux/Makefile --- a/make/linux/Makefile Thu Mar 06 10:55:28 2014 -0800 +++ b/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))","") diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/cppInterpreter_ppc.cpp --- a/src/cpu/ppc/vm/cppInterpreter_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/frame_ppc.cpp --- a/src/cpu/ppc/vm/frame_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 } } diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/frame_ppc.hpp --- a/src/cpu/ppc/vm/frame_ppc.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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. diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/frame_ppc.inline.hpp --- a/src/cpu/ppc/vm/frame_ppc.inline.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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() { diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/interp_masm_ppc_64.cpp --- a/src/cpu/ppc/vm/interp_masm_ppc_64.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/interp_masm_ppc_64.hpp --- a/src/cpu/ppc/vm/interp_masm_ppc_64.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/interpreter_ppc.cpp --- a/src/cpu/ppc/vm/interpreter_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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; diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/interpreter_ppc.hpp --- a/src/cpu/ppc/vm/interpreter_ppc.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp --- a/src/cpu/ppc/vm/javaFrameAnchor_ppc.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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: diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/macroAssembler_ppc.cpp --- a/src/cpu/ppc/vm/macroAssembler_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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); +} diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/macroAssembler_ppc.hpp --- a/src/cpu/ppc/vm/macroAssembler_ppc.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp --- a/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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) { diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/register_ppc.hpp --- a/src/cpu/ppc/vm/register_ppc.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/sharedRuntime_ppc.cpp --- a/src/cpu/ppc/vm/sharedRuntime_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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(); diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/stubGenerator_ppc.cpp --- a/src/cpu/ppc/vm/stubGenerator_ppc.cpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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); diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/templateInterpreterGenerator_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/templateInterpreter_ppc.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/templateInterpreter_ppc.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 + + diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/templateTable_ppc_64.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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::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::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; iprint_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; iprint_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::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::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::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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/cpu/ppc/vm/templateTable_ppc_64.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 diff -r 31e80afe3fed -r fd1b9f02cc91 src/share/vm/interpreter/templateTable.hpp --- a/src/share/vm/interpreter/templateTable.hpp Thu Mar 06 10:55:28 2014 -0800 +++ b/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 */