| /* | |
| * Copyright (c) 2002, 2025, Oracle and/or its affiliates. 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 "cds/cdsConfig.hpp" | |
| #include "classfile/javaClasses.hpp" | |
| #include "classfile/vmSymbols.hpp" | |
| #include "gc/shared/collectedHeap.hpp" | |
| #include "gc/shared/threadLocalAllocBuffer.inline.hpp" | |
| #include "gc/shared/tlab_globals.hpp" | |
| #include "interpreter/bytecodeHistogram.hpp" | |
| #include "interpreter/zero/bytecodeInterpreter.inline.hpp" | |
| #include "interpreter/interpreter.hpp" | |
| #include "interpreter/interpreterRuntime.hpp" | |
| #include "jvm_io.h" | |
| #include "logging/log.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "memory/universe.hpp" | |
| #include "oops/constantPool.inline.hpp" | |
| #include "oops/cpCache.inline.hpp" | |
| #include "oops/instanceKlass.inline.hpp" | |
| #include "oops/klass.inline.hpp" | |
| #include "oops/method.inline.hpp" | |
| #include "oops/methodCounters.hpp" | |
| #include "oops/objArrayKlass.hpp" | |
| #include "oops/objArrayOop.inline.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "oops/resolvedFieldEntry.hpp" | |
| #include "oops/resolvedIndyEntry.hpp" | |
| #include "oops/resolvedMethodEntry.hpp" | |
| #include "oops/typeArrayOop.inline.hpp" | |
| #include "prims/jvmtiExport.hpp" | |
| #include "prims/jvmtiThreadState.hpp" | |
| #include "runtime/atomic.hpp" | |
| #include "runtime/basicLock.inline.hpp" | |
| #include "runtime/frame.inline.hpp" | |
| #include "runtime/globals.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/interfaceSupport.inline.hpp" | |
| #include "runtime/orderAccess.hpp" | |
| #include "runtime/sharedRuntime.hpp" | |
| #include "utilities/debug.hpp" | |
| #include "utilities/exceptions.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #include "utilities/macros.hpp" | |
| /* | |
| * USELABELS - If using GCC, then use labels for the opcode dispatching | |
| * rather -then a switch statement. This improves performance because it | |
| * gives us the opportunity to have the instructions that calculate the | |
| * next opcode to jump to be intermixed with the rest of the instructions | |
| * that implement the opcode (see UPDATE_PC_AND_TOS_AND_CONTINUE macro). | |
| */ | |
| #undef USELABELS | |
| #ifdef __GNUC__ | |
| /* | |
| ASSERT signifies debugging. It is much easier to step thru bytecodes if we | |
| don't use the computed goto approach. | |
| */ | |
| #ifndef ASSERT | |
| #define USELABELS | |
| #endif | |
| #endif | |
| #undef CASE | |
| #ifdef USELABELS | |
| #define CASE(opcode) opc ## opcode | |
| #define DEFAULT opc_default | |
| #else | |
| #define CASE(opcode) case Bytecodes:: opcode | |
| #define DEFAULT default | |
| #endif | |
| /* | |
| * PREFETCH_OPCCODE - Some compilers do better if you prefetch the next | |
| * opcode before going back to the top of the while loop, rather then having | |
| * the top of the while loop handle it. This provides a better opportunity | |
| * for instruction scheduling. Some compilers just do this prefetch | |
| * automatically. Some actually end up with worse performance if you | |
| * force the prefetch. Solaris gcc seems to do better, but cc does worse. | |
| */ | |
| #undef PREFETCH_OPCCODE | |
| #define PREFETCH_OPCCODE | |
| JRT_ENTRY(void, at_safepoint(JavaThread* current)) {} | |
| JRT_END | |
| /* | |
| Interpreter safepoint: it is expected that the interpreter will have no live | |
| handles of its own creation live at an interpreter safepoint. Therefore we | |
| run a HandleMarkCleaner and trash all handles allocated in the call chain | |
| since the JavaCalls::call_helper invocation that initiated the chain. | |
| There really shouldn't be any handles remaining to trash but this is cheap | |
| in relation to a safepoint. | |
| */ | |
| #define RETURN_SAFEPOINT \ | |
| if (SafepointMechanism::should_process(THREAD)) { \ | |
| CALL_VM(at_safepoint(THREAD), handle_exception); \ | |
| } | |
| /* | |
| * VM_JAVA_ERROR - Macro for throwing a java exception from | |
| * the interpreter loop. Should really be a CALL_VM but there | |
| * is no entry point to do the transition to vm so we just | |
| * do it by hand here. | |
| */ | |
| #define VM_JAVA_ERROR_NO_JUMP(name, msg) \ | |
| DECACHE_STATE(); \ | |
| SET_LAST_JAVA_FRAME(); \ | |
| { \ | |
| ThreadInVMfromJava trans(THREAD); \ | |
| Exceptions::_throw_msg(THREAD, __FILE__, __LINE__, name, msg); \ | |
| } \ | |
| RESET_LAST_JAVA_FRAME(); \ | |
| CACHE_STATE(); | |
| // Normal throw of a java error. | |
| #define VM_JAVA_ERROR(name, msg) \ | |
| VM_JAVA_ERROR_NO_JUMP(name, msg) \ | |
| goto handle_exception; | |
| #ifdef PRODUCT | |
| #define DO_UPDATE_INSTRUCTION_COUNT(opcode) | |
| #else | |
| #define DO_UPDATE_INSTRUCTION_COUNT(opcode) \ | |
| { \ | |
| if (PrintBytecodeHistogram) { \ | |
| BytecodeHistogram::_counters[(Bytecodes::Code)opcode]++; \ | |
| } \ | |
| if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) { \ | |
| BytecodeCounter::_counter_value++; \ | |
| if (StopInterpreterAt == BytecodeCounter::_counter_value) { \ | |
| os::breakpoint(); \ | |
| } \ | |
| if (TraceBytecodes) { \ | |
| CALL_VM((void)InterpreterRuntime::trace_bytecode(THREAD, 0, \ | |
| topOfStack[Interpreter::expr_index_at(1)], \ | |
| topOfStack[Interpreter::expr_index_at(2)]), \ | |
| handle_exception); \ | |
| } \ | |
| } \ | |
| } | |
| #endif | |
| #undef DEBUGGER_SINGLE_STEP_NOTIFY | |
| #if INCLUDE_JVMTI | |
| /* NOTE: (kbr) This macro must be called AFTER the PC has been | |
| incremented. JvmtiExport::at_single_stepping_point() may cause a | |
| breakpoint opcode to get inserted at the current PC to allow the | |
| debugger to coalesce single-step events. | |
| As a result if we call at_single_stepping_point() we refetch opcode | |
| to get the current opcode. This will override any other prefetching | |
| that might have occurred. | |
| */ | |
| #define DEBUGGER_SINGLE_STEP_NOTIFY() \ | |
| { \ | |
| if (JVMTI_ENABLED && JvmtiExport::should_post_single_step()) { \ | |
| DECACHE_STATE(); \ | |
| SET_LAST_JAVA_FRAME(); \ | |
| ThreadInVMfromJava trans(THREAD); \ | |
| JvmtiExport::at_single_stepping_point(THREAD, \ | |
| istate->method(), \ | |
| pc); \ | |
| RESET_LAST_JAVA_FRAME(); \ | |
| CACHE_STATE(); \ | |
| if (THREAD->has_pending_popframe() && \ | |
| !THREAD->pop_frame_in_process()) { \ | |
| goto handle_Pop_Frame; \ | |
| } \ | |
| if (THREAD->jvmti_thread_state() && \ | |
| THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ | |
| goto handle_Early_Return; \ | |
| } \ | |
| opcode = *pc; \ | |
| } \ | |
| } | |
| #else | |
| #define DEBUGGER_SINGLE_STEP_NOTIFY() | |
| #endif // INCLUDE_JVMTI | |
| /* | |
| * CONTINUE - Macro for executing the next opcode. | |
| */ | |
| #undef CONTINUE | |
| #ifdef USELABELS | |
| // Have to do this dispatch this way in C++ because otherwise gcc complains about crossing an | |
| // initialization (which is is the initialization of the table pointer...) | |
| #define DISPATCH(opcode) goto *(void*)dispatch_table[opcode] | |
| #define CONTINUE { \ | |
| opcode = *pc; \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| DISPATCH(opcode); \ | |
| } | |
| #else | |
| #ifdef PREFETCH_OPCCODE | |
| #define CONTINUE { \ | |
| opcode = *pc; \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| continue; \ | |
| } | |
| #else | |
| #define CONTINUE { \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| continue; \ | |
| } | |
| #endif | |
| #endif | |
| #define UPDATE_PC(opsize) {pc += opsize; } | |
| /* | |
| * UPDATE_PC_AND_TOS - Macro for updating the pc and topOfStack. | |
| */ | |
| #undef UPDATE_PC_AND_TOS | |
| #define UPDATE_PC_AND_TOS(opsize, stack) \ | |
| {pc += opsize; MORE_STACK(stack); } | |
| /* | |
| * UPDATE_PC_AND_TOS_AND_CONTINUE - Macro for updating the pc and topOfStack, | |
| * and executing the next opcode. It's somewhat similar to the combination | |
| * of UPDATE_PC_AND_TOS and CONTINUE, but with some minor optimizations. | |
| */ | |
| #undef UPDATE_PC_AND_TOS_AND_CONTINUE | |
| #ifdef USELABELS | |
| #define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ | |
| pc += opsize; opcode = *pc; MORE_STACK(stack); \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| DISPATCH(opcode); \ | |
| } | |
| #define UPDATE_PC_AND_CONTINUE(opsize) { \ | |
| pc += opsize; opcode = *pc; \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| DISPATCH(opcode); \ | |
| } | |
| #else | |
| #ifdef PREFETCH_OPCCODE | |
| #define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ | |
| pc += opsize; opcode = *pc; MORE_STACK(stack); \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| goto do_continue; \ | |
| } | |
| #define UPDATE_PC_AND_CONTINUE(opsize) { \ | |
| pc += opsize; opcode = *pc; \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| goto do_continue; \ | |
| } | |
| #else | |
| #define UPDATE_PC_AND_TOS_AND_CONTINUE(opsize, stack) { \ | |
| pc += opsize; MORE_STACK(stack); \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| goto do_continue; \ | |
| } | |
| #define UPDATE_PC_AND_CONTINUE(opsize) { \ | |
| pc += opsize; \ | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); \ | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); \ | |
| goto do_continue; \ | |
| } | |
| #endif /* PREFETCH_OPCCODE */ | |
| #endif /* USELABELS */ | |
| // About to call a new method, update the save the adjusted pc and return to frame manager | |
| #define UPDATE_PC_AND_RETURN(opsize) \ | |
| DECACHE_TOS(); \ | |
| istate->set_bcp(pc+opsize); \ | |
| return; | |
| #define REWRITE_AT_PC(val) \ | |
| *pc = val; | |
| #define METHOD istate->method() | |
| #define GET_METHOD_COUNTERS(res) | |
| #define DO_BACKEDGE_CHECKS(skip, branch_pc) | |
| /* | |
| * For those opcodes that need to have a GC point on a backwards branch | |
| */ | |
| /* | |
| * Macros for caching and flushing the interpreter state. Some local | |
| * variables need to be flushed out to the frame before we do certain | |
| * things (like pushing frames or becoming gc safe) and some need to | |
| * be recached later (like after popping a frame). We could use one | |
| * macro to cache or decache everything, but this would be less then | |
| * optimal because we don't always need to cache or decache everything | |
| * because some things we know are already cached or decached. | |
| */ | |
| #undef DECACHE_TOS | |
| #undef CACHE_TOS | |
| #undef CACHE_PREV_TOS | |
| #define DECACHE_TOS() istate->set_stack(topOfStack); | |
| #define CACHE_TOS() topOfStack = (intptr_t *)istate->stack(); | |
| #undef DECACHE_PC | |
| #undef CACHE_PC | |
| #define DECACHE_PC() istate->set_bcp(pc); | |
| #define CACHE_PC() pc = istate->bcp(); | |
| #define CACHE_CP() cp = istate->constants(); | |
| #define CACHE_LOCALS() locals = istate->locals(); | |
| #undef CACHE_FRAME | |
| #define CACHE_FRAME() | |
| // BCI() returns the current bytecode-index. | |
| #undef BCI | |
| #define BCI() ((int)(intptr_t)(pc - (intptr_t)istate->method()->code_base())) | |
| /* | |
| * CHECK_NULL - Macro for throwing a NullPointerException if the object | |
| * passed is a null ref. | |
| * On some architectures/platforms it should be possible to do this implicitly | |
| */ | |
| #undef CHECK_NULL | |
| #define CHECK_NULL(obj_) \ | |
| if ((obj_) == nullptr) { \ | |
| VM_JAVA_ERROR(vmSymbols::java_lang_NullPointerException(), nullptr); \ | |
| } \ | |
| VERIFY_OOP(obj_) | |
| #define VMdoubleConstZero() 0.0 | |
| #define VMdoubleConstOne() 1.0 | |
| #define VMlongConstZero() (max_jlong-max_jlong) | |
| #define VMlongConstOne() ((max_jlong-max_jlong)+1) | |
| /* | |
| * Alignment | |
| */ | |
| #define VMalignWordUp(val) (((uintptr_t)(val) + 3) & ~3) | |
| // Decache the interpreter state that interpreter modifies directly (i.e. GC is indirect mod) | |
| #define DECACHE_STATE() DECACHE_PC(); DECACHE_TOS(); | |
| // Reload interpreter state after calling the VM or a possible GC | |
| #define CACHE_STATE() \ | |
| CACHE_TOS(); \ | |
| CACHE_PC(); \ | |
| CACHE_CP(); \ | |
| CACHE_LOCALS(); | |
| // Call the VM with last java frame only. | |
| #define CALL_VM_NAKED_LJF(func) \ | |
| DECACHE_STATE(); \ | |
| SET_LAST_JAVA_FRAME(); \ | |
| func; \ | |
| RESET_LAST_JAVA_FRAME(); \ | |
| CACHE_STATE(); | |
| // Call the VM. Don't check for pending exceptions. | |
| #define CALL_VM_NOCHECK(func) \ | |
| CALL_VM_NAKED_LJF(func) \ | |
| if (THREAD->has_pending_popframe() && \ | |
| !THREAD->pop_frame_in_process()) { \ | |
| goto handle_Pop_Frame; \ | |
| } \ | |
| if (THREAD->jvmti_thread_state() && \ | |
| THREAD->jvmti_thread_state()->is_earlyret_pending()) { \ | |
| goto handle_Early_Return; \ | |
| } | |
| // Call the VM and check for pending exceptions | |
| #define CALL_VM(func, label) { \ | |
| CALL_VM_NOCHECK(func); \ | |
| if (THREAD->has_pending_exception()) goto label; \ | |
| } | |
| #define MAYBE_POST_FIELD_ACCESS(obj) { \ | |
| if (JVMTI_ENABLED) { \ | |
| int* count_addr; \ | |
| /* Check to see if a field modification watch has been set */ \ | |
| /* before we take the time to call into the VM. */ \ | |
| count_addr = (int*)JvmtiExport::get_field_access_count_addr(); \ | |
| if (*count_addr > 0) { \ | |
| oop target; \ | |
| if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { \ | |
| target = nullptr; \ | |
| } else { \ | |
| target = obj; \ | |
| } \ | |
| CALL_VM(InterpreterRuntime::post_field_access(THREAD, \ | |
| target, entry), \ | |
| handle_exception); \ | |
| } \ | |
| } \ | |
| } | |
| #define MAYBE_POST_FIELD_MODIFICATION(obj) { \ | |
| if (JVMTI_ENABLED) { \ | |
| int* count_addr; \ | |
| /* Check to see if a field modification watch has been set */ \ | |
| /* before we take the time to call into the VM. */ \ | |
| count_addr = (int*)JvmtiExport::get_field_modification_count_addr(); \ | |
| if (*count_addr > 0) { \ | |
| oop target; \ | |
| if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) { \ | |
| target = nullptr; \ | |
| } else { \ | |
| target = obj; \ | |
| } \ | |
| CALL_VM(InterpreterRuntime::post_field_modification(THREAD, \ | |
| target, entry, \ | |
| (jvalue*)STACK_SLOT(-1)), \ | |
| handle_exception); \ | |
| } \ | |
| } \ | |
| } | |
| static inline int fast_get_type(TosState tos) { | |
| switch (tos) { | |
| case ztos: | |
| case btos: return Bytecodes::_fast_bgetfield; | |
| case ctos: return Bytecodes::_fast_cgetfield; | |
| case stos: return Bytecodes::_fast_sgetfield; | |
| case itos: return Bytecodes::_fast_igetfield; | |
| case ltos: return Bytecodes::_fast_lgetfield; | |
| case ftos: return Bytecodes::_fast_fgetfield; | |
| case dtos: return Bytecodes::_fast_dgetfield; | |
| case atos: return Bytecodes::_fast_agetfield; | |
| default: | |
| ShouldNotReachHere(); | |
| return -1; | |
| } | |
| } | |
| static inline int fast_put_type(TosState tos) { | |
| switch (tos) { | |
| case ztos: return Bytecodes::_fast_zputfield; | |
| case btos: return Bytecodes::_fast_bputfield; | |
| case ctos: return Bytecodes::_fast_cputfield; | |
| case stos: return Bytecodes::_fast_sputfield; | |
| case itos: return Bytecodes::_fast_iputfield; | |
| case ltos: return Bytecodes::_fast_lputfield; | |
| case ftos: return Bytecodes::_fast_fputfield; | |
| case dtos: return Bytecodes::_fast_dputfield; | |
| case atos: return Bytecodes::_fast_aputfield; | |
| default: | |
| ShouldNotReachHere(); | |
| return -1; | |
| } | |
| } | |
| /* | |
| * BytecodeInterpreter::run(interpreterState istate) | |
| * | |
| * The real deal. This is where byte codes actually get interpreted. | |
| * Basically it's a big while loop that iterates until we return from | |
| * the method passed in. | |
| */ | |
| // Instantiate variants of the method for future linking. | |
| template void BytecodeInterpreter::run<false, false>(interpreterState istate); | |
| template void BytecodeInterpreter::run<false, true>(interpreterState istate); | |
| template void BytecodeInterpreter::run< true, false>(interpreterState istate); | |
| template void BytecodeInterpreter::run< true, true>(interpreterState istate); | |
| template<bool JVMTI_ENABLED, bool REWRITE_BYTECODES> | |
| void BytecodeInterpreter::run(interpreterState istate) { | |
| intptr_t* topOfStack = (intptr_t *)istate->stack(); /* access with STACK macros */ | |
| address pc = istate->bcp(); | |
| jubyte opcode; | |
| intptr_t* locals = istate->locals(); | |
| ConstantPoolCache* cp = istate->constants(); // method()->constants()->cache() | |
| #ifdef LOTS_OF_REGS | |
| JavaThread* THREAD = istate->thread(); | |
| #else | |
| #undef THREAD | |
| #define THREAD istate->thread() | |
| #endif | |
| #ifdef ASSERT | |
| assert(labs(istate->stack_base() - istate->stack_limit()) == (istate->method()->max_stack() + 1), | |
| "Bad stack limit"); | |
| /* QQQ this should be a stack method so we don't know actual direction */ | |
| assert(topOfStack >= istate->stack_limit() && topOfStack < istate->stack_base(), | |
| "Stack top out of range"); | |
| // Verify linkages. | |
| interpreterState l = istate; | |
| do { | |
| assert(l == l->_self_link, "bad link"); | |
| l = l->_prev_link; | |
| } while (l != nullptr); | |
| // Screwups with stack management usually cause us to overwrite istate | |
| // save a copy so we can verify it. | |
| interpreterState orig = istate; | |
| #endif | |
| #ifdef USELABELS | |
| const static void* const opclabels_data[256] = { | |
| /* 0x00 */ &&opc_nop, &&opc_aconst_null, &&opc_iconst_m1, &&opc_iconst_0, | |
| /* 0x04 */ &&opc_iconst_1, &&opc_iconst_2, &&opc_iconst_3, &&opc_iconst_4, | |
| /* 0x08 */ &&opc_iconst_5, &&opc_lconst_0, &&opc_lconst_1, &&opc_fconst_0, | |
| /* 0x0C */ &&opc_fconst_1, &&opc_fconst_2, &&opc_dconst_0, &&opc_dconst_1, | |
| /* 0x10 */ &&opc_bipush, &&opc_sipush, &&opc_ldc, &&opc_ldc_w, | |
| /* 0x14 */ &&opc_ldc2_w, &&opc_iload, &&opc_lload, &&opc_fload, | |
| /* 0x18 */ &&opc_dload, &&opc_aload, &&opc_iload_0, &&opc_iload_1, | |
| /* 0x1C */ &&opc_iload_2, &&opc_iload_3, &&opc_lload_0, &&opc_lload_1, | |
| /* 0x20 */ &&opc_lload_2, &&opc_lload_3, &&opc_fload_0, &&opc_fload_1, | |
| /* 0x24 */ &&opc_fload_2, &&opc_fload_3, &&opc_dload_0, &&opc_dload_1, | |
| /* 0x28 */ &&opc_dload_2, &&opc_dload_3, &&opc_aload_0, &&opc_aload_1, | |
| /* 0x2C */ &&opc_aload_2, &&opc_aload_3, &&opc_iaload, &&opc_laload, | |
| /* 0x30 */ &&opc_faload, &&opc_daload, &&opc_aaload, &&opc_baload, | |
| /* 0x34 */ &&opc_caload, &&opc_saload, &&opc_istore, &&opc_lstore, | |
| /* 0x38 */ &&opc_fstore, &&opc_dstore, &&opc_astore, &&opc_istore_0, | |
| /* 0x3C */ &&opc_istore_1, &&opc_istore_2, &&opc_istore_3, &&opc_lstore_0, | |
| /* 0x40 */ &&opc_lstore_1, &&opc_lstore_2, &&opc_lstore_3, &&opc_fstore_0, | |
| /* 0x44 */ &&opc_fstore_1, &&opc_fstore_2, &&opc_fstore_3, &&opc_dstore_0, | |
| /* 0x48 */ &&opc_dstore_1, &&opc_dstore_2, &&opc_dstore_3, &&opc_astore_0, | |
| /* 0x4C */ &&opc_astore_1, &&opc_astore_2, &&opc_astore_3, &&opc_iastore, | |
| /* 0x50 */ &&opc_lastore, &&opc_fastore, &&opc_dastore, &&opc_aastore, | |
| /* 0x54 */ &&opc_bastore, &&opc_castore, &&opc_sastore, &&opc_pop, | |
| /* 0x58 */ &&opc_pop2, &&opc_dup, &&opc_dup_x1, &&opc_dup_x2, | |
| /* 0x5C */ &&opc_dup2, &&opc_dup2_x1, &&opc_dup2_x2, &&opc_swap, | |
| /* 0x60 */ &&opc_iadd, &&opc_ladd, &&opc_fadd, &&opc_dadd, | |
| /* 0x64 */ &&opc_isub, &&opc_lsub, &&opc_fsub, &&opc_dsub, | |
| /* 0x68 */ &&opc_imul, &&opc_lmul, &&opc_fmul, &&opc_dmul, | |
| /* 0x6C */ &&opc_idiv, &&opc_ldiv, &&opc_fdiv, &&opc_ddiv, | |
| /* 0x70 */ &&opc_irem, &&opc_lrem, &&opc_frem, &&opc_drem, | |
| /* 0x74 */ &&opc_ineg, &&opc_lneg, &&opc_fneg, &&opc_dneg, | |
| /* 0x78 */ &&opc_ishl, &&opc_lshl, &&opc_ishr, &&opc_lshr, | |
| /* 0x7C */ &&opc_iushr, &&opc_lushr, &&opc_iand, &&opc_land, | |
| /* 0x80 */ &&opc_ior, &&opc_lor, &&opc_ixor, &&opc_lxor, | |
| /* 0x84 */ &&opc_iinc, &&opc_i2l, &&opc_i2f, &&opc_i2d, | |
| /* 0x88 */ &&opc_l2i, &&opc_l2f, &&opc_l2d, &&opc_f2i, | |
| /* 0x8C */ &&opc_f2l, &&opc_f2d, &&opc_d2i, &&opc_d2l, | |
| /* 0x90 */ &&opc_d2f, &&opc_i2b, &&opc_i2c, &&opc_i2s, | |
| /* 0x94 */ &&opc_lcmp, &&opc_fcmpl, &&opc_fcmpg, &&opc_dcmpl, | |
| /* 0x98 */ &&opc_dcmpg, &&opc_ifeq, &&opc_ifne, &&opc_iflt, | |
| /* 0x9C */ &&opc_ifge, &&opc_ifgt, &&opc_ifle, &&opc_if_icmpeq, | |
| /* 0xA0 */ &&opc_if_icmpne, &&opc_if_icmplt, &&opc_if_icmpge, &&opc_if_icmpgt, | |
| /* 0xA4 */ &&opc_if_icmple, &&opc_if_acmpeq, &&opc_if_acmpne, &&opc_goto, | |
| /* 0xA8 */ &&opc_jsr, &&opc_ret, &&opc_tableswitch, &&opc_lookupswitch, | |
| /* 0xAC */ &&opc_ireturn, &&opc_lreturn, &&opc_freturn, &&opc_dreturn, | |
| /* 0xB0 */ &&opc_areturn, &&opc_return, &&opc_getstatic, &&opc_putstatic, | |
| /* 0xB4 */ &&opc_getfield, &&opc_putfield, &&opc_invokevirtual, &&opc_invokespecial, | |
| /* 0xB8 */ &&opc_invokestatic, &&opc_invokeinterface, &&opc_invokedynamic, &&opc_new, | |
| /* 0xBC */ &&opc_newarray, &&opc_anewarray, &&opc_arraylength, &&opc_athrow, | |
| /* 0xC0 */ &&opc_checkcast, &&opc_instanceof, &&opc_monitorenter, &&opc_monitorexit, | |
| /* 0xC4 */ &&opc_wide, &&opc_multianewarray, &&opc_ifnull, &&opc_ifnonnull, | |
| /* 0xC8 */ &&opc_goto_w, &&opc_jsr_w, &&opc_breakpoint, &&opc_fast_agetfield, | |
| /* 0xCC */ &&opc_fast_bgetfield,&&opc_fast_cgetfield, &&opc_fast_dgetfield, &&opc_fast_fgetfield, | |
| /* 0xD0 */ &&opc_fast_igetfield,&&opc_fast_lgetfield, &&opc_fast_sgetfield, &&opc_fast_aputfield, | |
| /* 0xD4 */ &&opc_fast_bputfield,&&opc_fast_zputfield, &&opc_fast_cputfield, &&opc_fast_dputfield, | |
| /* 0xD8 */ &&opc_fast_fputfield,&&opc_fast_iputfield, &&opc_fast_lputfield, &&opc_fast_sputfield, | |
| /* 0xDC */ &&opc_fast_aload_0, &&opc_fast_iaccess_0, &&opc_fast_aaccess_0, &&opc_fast_faccess_0, | |
| /* 0xE0 */ &&opc_fast_iload, &&opc_fast_iload2, &&opc_fast_icaload, &&opc_fast_invokevfinal, | |
| /* 0xE4 */ &&opc_default, &&opc_default, &&opc_fast_aldc, &&opc_fast_aldc_w, | |
| /* 0xE8 */ &&opc_return_register_finalizer, | |
| &&opc_invokehandle, &&opc_nofast_getfield,&&opc_nofast_putfield, | |
| /* 0xEC */ &&opc_nofast_aload_0,&&opc_nofast_iload, &&opc_default, &&opc_default, | |
| /* 0xF0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, | |
| /* 0xF4 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, | |
| /* 0xF8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, | |
| /* 0xFC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default | |
| }; | |
| uintptr_t *dispatch_table = (uintptr_t*)&opclabels_data[0]; | |
| #endif /* USELABELS */ | |
| switch (istate->msg()) { | |
| case initialize: { | |
| ShouldNotCallThis(); | |
| return; | |
| } | |
| case method_entry: { | |
| THREAD->set_do_not_unlock_if_synchronized(true); | |
| // Lock method if synchronized. | |
| if (METHOD->is_synchronized()) { | |
| // oop rcvr = locals[0].j.r; | |
| oop rcvr; | |
| if (METHOD->is_static()) { | |
| rcvr = METHOD->constants()->pool_holder()->java_mirror(); | |
| } else { | |
| rcvr = LOCALS_OBJECT(0); | |
| VERIFY_OOP(rcvr); | |
| } | |
| // The initial monitor is ours for the taking. | |
| BasicObjectLock* mon = &istate->monitor_base()[-1]; | |
| mon->set_obj(rcvr); | |
| bool success = false; | |
| if (LockingMode == LM_LEGACY) { | |
| // Traditional fast locking. | |
| markWord displaced = rcvr->mark().set_unlocked(); | |
| mon->lock()->set_displaced_header(displaced); | |
| success = true; | |
| if (rcvr->cas_set_mark(markWord::from_pointer(mon), displaced) != displaced) { | |
| // Is it simple recursive case? | |
| if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) { | |
| mon->lock()->set_displaced_header(markWord::from_pointer(nullptr)); | |
| } else { | |
| success = false; | |
| } | |
| } | |
| } | |
| if (!success) { | |
| CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception); | |
| } | |
| } | |
| THREAD->set_do_not_unlock_if_synchronized(false); | |
| // Notify jvmti. | |
| // Whenever JVMTI puts a thread in interp_only_mode, method | |
| // entry/exit events are sent for that thread to track stack depth. | |
| if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { | |
| CALL_VM(InterpreterRuntime::post_method_entry(THREAD), | |
| handle_exception); | |
| } | |
| goto run; | |
| } | |
| case popping_frame: { | |
| // returned from a java call to pop the frame, restart the call | |
| // clear the message so we don't confuse ourselves later | |
| assert(THREAD->pop_frame_in_process(), "wrong frame pop state"); | |
| istate->set_msg(no_request); | |
| THREAD->clr_pop_frame_in_process(); | |
| goto run; | |
| } | |
| case method_resume: { | |
| if ((istate->_stack_base - istate->_stack_limit) != istate->method()->max_stack() + 1) { | |
| // resume | |
| os::breakpoint(); | |
| } | |
| // returned from a java call, continue executing. | |
| if (THREAD->has_pending_popframe() && !THREAD->pop_frame_in_process()) { | |
| goto handle_Pop_Frame; | |
| } | |
| if (THREAD->jvmti_thread_state() && | |
| THREAD->jvmti_thread_state()->is_earlyret_pending()) { | |
| goto handle_Early_Return; | |
| } | |
| if (THREAD->has_pending_exception()) goto handle_exception; | |
| // Update the pc by the saved amount of the invoke bytecode size | |
| UPDATE_PC(istate->bcp_advance()); | |
| goto run; | |
| } | |
| case deopt_resume2: { | |
| // Returned from an opcode that will reexecute. Deopt was | |
| // a result of a PopFrame request. | |
| // | |
| goto run; | |
| } | |
| case deopt_resume: { | |
| // Returned from an opcode that has completed. The stack has | |
| // the result all we need to do is skip across the bytecode | |
| // and continue (assuming there is no exception pending) | |
| // | |
| // compute continuation length | |
| // | |
| // Note: it is possible to deopt at a return_register_finalizer opcode | |
| // because this requires entering the vm to do the registering. While the | |
| // opcode is complete we can't advance because there are no more opcodes | |
| // much like trying to deopt at a poll return. In that has we simply | |
| // get out of here | |
| // | |
| if ( Bytecodes::code_at(METHOD, pc) == Bytecodes::_return_register_finalizer) { | |
| // this will do the right thing even if an exception is pending. | |
| goto handle_return; | |
| } | |
| UPDATE_PC(Bytecodes::length_at(METHOD, pc)); | |
| if (THREAD->has_pending_exception()) goto handle_exception; | |
| goto run; | |
| } | |
| case got_monitors: { | |
| // continue locking now that we have a monitor to use | |
| // we expect to find newly allocated monitor at the "top" of the monitor stack. | |
| oop lockee = STACK_OBJECT(-1); | |
| VERIFY_OOP(lockee); | |
| // derefing's lockee ought to provoke implicit null check | |
| // find a free monitor | |
| BasicObjectLock* entry = (BasicObjectLock*) istate->stack_base(); | |
| assert(entry->obj() == nullptr, "Frame manager didn't allocate the monitor"); | |
| entry->set_obj(lockee); | |
| bool success = false; | |
| if (LockingMode == LM_LEGACY) { | |
| // Traditional fast locking. | |
| markWord displaced = lockee->mark().set_unlocked(); | |
| entry->lock()->set_displaced_header(displaced); | |
| success = true; | |
| if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) { | |
| // Is it simple recursive case? | |
| if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) { | |
| entry->lock()->set_displaced_header(markWord::from_pointer(nullptr)); | |
| } else { | |
| success = false; | |
| } | |
| } | |
| } | |
| if (!success) { | |
| CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); | |
| } | |
| UPDATE_PC_AND_TOS(1, -1); | |
| goto run; | |
| } | |
| default: { | |
| fatal("Unexpected message from frame manager"); | |
| } | |
| } | |
| run: | |
| DO_UPDATE_INSTRUCTION_COUNT(*pc) | |
| DEBUGGER_SINGLE_STEP_NOTIFY(); | |
| #ifdef PREFETCH_OPCCODE | |
| opcode = *pc; /* prefetch first opcode */ | |
| #endif | |
| #ifndef USELABELS | |
| while (1) | |
| #endif | |
| { | |
| #ifndef PREFETCH_OPCCODE | |
| opcode = *pc; | |
| #endif | |
| // Seems like this happens twice per opcode. At worst this is only | |
| // need at entry to the loop. | |
| // DEBUGGER_SINGLE_STEP_NOTIFY(); | |
| /* Using this labels avoids double breakpoints when quickening and | |
| * when returning from transition frames. | |
| */ | |
| opcode_switch: | |
| assert(istate == orig, "Corrupted istate"); | |
| /* QQQ Hmm this has knowledge of direction, ought to be a stack method */ | |
| assert(topOfStack >= istate->stack_limit(), "Stack overrun"); | |
| assert(topOfStack < istate->stack_base(), "Stack underrun"); | |
| #ifdef USELABELS | |
| DISPATCH(opcode); | |
| #else | |
| switch (opcode) | |
| #endif | |
| { | |
| CASE(_nop): | |
| UPDATE_PC_AND_CONTINUE(1); | |
| /* Push miscellaneous constants onto the stack. */ | |
| CASE(_aconst_null): | |
| SET_STACK_OBJECT(nullptr, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| #undef OPC_CONST_n | |
| #define OPC_CONST_n(opcode, const_type, value) \ | |
| CASE(opcode): \ | |
| SET_STACK_ ## const_type(value, 0); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| OPC_CONST_n(_iconst_m1, INT, -1); | |
| OPC_CONST_n(_iconst_0, INT, 0); | |
| OPC_CONST_n(_iconst_1, INT, 1); | |
| OPC_CONST_n(_iconst_2, INT, 2); | |
| OPC_CONST_n(_iconst_3, INT, 3); | |
| OPC_CONST_n(_iconst_4, INT, 4); | |
| OPC_CONST_n(_iconst_5, INT, 5); | |
| OPC_CONST_n(_fconst_0, FLOAT, 0.0); | |
| OPC_CONST_n(_fconst_1, FLOAT, 1.0); | |
| OPC_CONST_n(_fconst_2, FLOAT, 2.0); | |
| #undef OPC_CONST2_n | |
| #define OPC_CONST2_n(opcname, value, key, kind) \ | |
| CASE(_##opcname): \ | |
| { \ | |
| SET_STACK_ ## kind(VM##key##Const##value(), 1); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); \ | |
| } | |
| OPC_CONST2_n(dconst_0, Zero, double, DOUBLE); | |
| OPC_CONST2_n(dconst_1, One, double, DOUBLE); | |
| OPC_CONST2_n(lconst_0, Zero, long, LONG); | |
| OPC_CONST2_n(lconst_1, One, long, LONG); | |
| /* Load constant from constant pool: */ | |
| /* Push a 1-byte signed integer value onto the stack. */ | |
| CASE(_bipush): | |
| SET_STACK_INT((jbyte)(pc[1]), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); | |
| /* Push a 2-byte signed integer constant onto the stack. */ | |
| CASE(_sipush): | |
| SET_STACK_INT((int16_t)Bytes::get_Java_u2(pc + 1), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); | |
| /* load from local variable */ | |
| CASE(_aload): | |
| VERIFY_OOP(LOCALS_OBJECT(pc[1])); | |
| SET_STACK_OBJECT(LOCALS_OBJECT(pc[1]), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); | |
| CASE(_iload): | |
| { | |
| if (REWRITE_BYTECODES) { | |
| // Attempt to rewrite iload, iload -> fast_iload2 | |
| // iload, caload -> fast_icaload | |
| // Normal iloads will be rewritten to fast_iload to avoid checking again. | |
| switch (*(pc + 2)) { | |
| case Bytecodes::_fast_iload: | |
| REWRITE_AT_PC(Bytecodes::_fast_iload2); | |
| break; | |
| case Bytecodes::_caload: | |
| REWRITE_AT_PC(Bytecodes::_fast_icaload); | |
| break; | |
| case Bytecodes::_iload: | |
| // Wait until rewritten to _fast_iload. | |
| break; | |
| default: | |
| // Last iload in a (potential) series, don't check again. | |
| REWRITE_AT_PC(Bytecodes::_fast_iload); | |
| } | |
| } | |
| // Normal iload handling. | |
| SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); | |
| } | |
| CASE(_nofast_iload): | |
| { | |
| // Normal, non-rewritable iload handling. | |
| SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); | |
| } | |
| CASE(_fast_iload): | |
| CASE(_fload): | |
| SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1); | |
| CASE(_fast_iload2): | |
| SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0); | |
| SET_STACK_SLOT(LOCALS_SLOT(pc[3]), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 2); | |
| CASE(_lload): | |
| SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(pc[1]), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2); | |
| CASE(_dload): | |
| SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(pc[1]), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2); | |
| #undef OPC_LOAD_n | |
| #define OPC_LOAD_n(num) \ | |
| CASE(_iload_##num): \ | |
| CASE(_fload_##num): \ | |
| SET_STACK_SLOT(LOCALS_SLOT(num), 0); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); \ | |
| \ | |
| CASE(_lload_##num): \ | |
| SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(num), 1); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); \ | |
| CASE(_dload_##num): \ | |
| SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(num), 1); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| OPC_LOAD_n(0); | |
| OPC_LOAD_n(1); | |
| OPC_LOAD_n(2); | |
| OPC_LOAD_n(3); | |
| #undef OPC_ALOAD_n | |
| #define OPC_ALOAD_n(num) \ | |
| CASE(_aload_##num): { \ | |
| oop obj = LOCALS_OBJECT(num); \ | |
| VERIFY_OOP(obj); \ | |
| SET_STACK_OBJECT(obj, 0); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); \ | |
| } | |
| CASE(_aload_0): | |
| { | |
| /* Maybe rewrite if following bytecode is one of the supported _fast_Xgetfield bytecodes. */ | |
| if (REWRITE_BYTECODES) { | |
| switch (*(pc + 1)) { | |
| case Bytecodes::_fast_agetfield: | |
| REWRITE_AT_PC(Bytecodes::_fast_aaccess_0); | |
| break; | |
| case Bytecodes::_fast_fgetfield: | |
| REWRITE_AT_PC(Bytecodes::_fast_faccess_0); | |
| break; | |
| case Bytecodes::_fast_igetfield: | |
| REWRITE_AT_PC(Bytecodes::_fast_iaccess_0); | |
| break; | |
| case Bytecodes::_getfield: | |
| case Bytecodes::_nofast_getfield: { | |
| /* Otherwise, do nothing here, wait until/if it gets rewritten to _fast_Xgetfield. | |
| * Unfortunately, this punishes volatile field access, because it never gets | |
| * rewritten. */ | |
| break; | |
| } | |
| default: | |
| REWRITE_AT_PC(Bytecodes::_fast_aload_0); | |
| break; | |
| } | |
| } | |
| // Normal aload_0 handling. | |
| VERIFY_OOP(LOCALS_OBJECT(0)); | |
| SET_STACK_OBJECT(LOCALS_OBJECT(0), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_nofast_aload_0): | |
| { | |
| // Normal, non-rewritable aload_0 handling. | |
| VERIFY_OOP(LOCALS_OBJECT(0)); | |
| SET_STACK_OBJECT(LOCALS_OBJECT(0), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| OPC_ALOAD_n(1); | |
| OPC_ALOAD_n(2); | |
| OPC_ALOAD_n(3); | |
| /* store to a local variable */ | |
| CASE(_astore): | |
| astore(topOfStack, -1, locals, pc[1]); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, -1); | |
| CASE(_istore): | |
| CASE(_fstore): | |
| SET_LOCALS_SLOT(STACK_SLOT(-1), pc[1]); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, -1); | |
| CASE(_lstore): | |
| SET_LOCALS_LONG(STACK_LONG(-1), pc[1]); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, -2); | |
| CASE(_dstore): | |
| SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), pc[1]); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(2, -2); | |
| CASE(_wide): { | |
| uint16_t reg = Bytes::get_Java_u2(pc + 2); | |
| opcode = pc[1]; | |
| // Wide and it's sub-bytecode are counted as separate instructions. If we | |
| // don't account for this here, the bytecode trace skips the next bytecode. | |
| DO_UPDATE_INSTRUCTION_COUNT(opcode); | |
| switch(opcode) { | |
| case Bytecodes::_aload: | |
| VERIFY_OOP(LOCALS_OBJECT(reg)); | |
| SET_STACK_OBJECT(LOCALS_OBJECT(reg), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); | |
| case Bytecodes::_iload: | |
| case Bytecodes::_fload: | |
| SET_STACK_SLOT(LOCALS_SLOT(reg), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); | |
| case Bytecodes::_lload: | |
| SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(reg), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 2); | |
| case Bytecodes::_dload: | |
| SET_STACK_DOUBLE_FROM_ADDR(LOCALS_LONG_AT(reg), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 2); | |
| case Bytecodes::_astore: | |
| astore(topOfStack, -1, locals, reg); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, -1); | |
| case Bytecodes::_istore: | |
| case Bytecodes::_fstore: | |
| SET_LOCALS_SLOT(STACK_SLOT(-1), reg); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, -1); | |
| case Bytecodes::_lstore: | |
| SET_LOCALS_LONG(STACK_LONG(-1), reg); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, -2); | |
| case Bytecodes::_dstore: | |
| SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), reg); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, -2); | |
| case Bytecodes::_iinc: { | |
| int16_t offset = (int16_t)Bytes::get_Java_u2(pc+4); | |
| // Be nice to see what this generates.... QQQ | |
| SET_LOCALS_INT(LOCALS_INT(reg) + offset, reg); | |
| UPDATE_PC_AND_CONTINUE(6); | |
| } | |
| case Bytecodes::_ret: | |
| pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(reg)); | |
| UPDATE_PC_AND_CONTINUE(0); | |
| default: | |
| VM_JAVA_ERROR(vmSymbols::java_lang_InternalError(), "undefined opcode"); | |
| } | |
| } | |
| #undef OPC_STORE_n | |
| #define OPC_STORE_n(num) \ | |
| CASE(_astore_##num): \ | |
| astore(topOfStack, -1, locals, num); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ | |
| CASE(_istore_##num): \ | |
| CASE(_fstore_##num): \ | |
| SET_LOCALS_SLOT(STACK_SLOT(-1), num); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| OPC_STORE_n(0); | |
| OPC_STORE_n(1); | |
| OPC_STORE_n(2); | |
| OPC_STORE_n(3); | |
| #undef OPC_DSTORE_n | |
| #define OPC_DSTORE_n(num) \ | |
| CASE(_dstore_##num): \ | |
| SET_LOCALS_DOUBLE(STACK_DOUBLE(-1), num); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ | |
| CASE(_lstore_##num): \ | |
| SET_LOCALS_LONG(STACK_LONG(-1), num); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); | |
| OPC_DSTORE_n(0); | |
| OPC_DSTORE_n(1); | |
| OPC_DSTORE_n(2); | |
| OPC_DSTORE_n(3); | |
| /* stack pop, dup, and insert opcodes */ | |
| CASE(_pop): /* Discard the top item on the stack */ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| CASE(_pop2): /* Discard the top 2 items on the stack */ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); | |
| CASE(_dup): /* Duplicate the top item on the stack */ | |
| dup(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| CASE(_dup2): /* Duplicate the top 2 items on the stack */ | |
| dup2(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| CASE(_dup_x1): /* insert top word two down */ | |
| dup_x1(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| CASE(_dup_x2): /* insert top word three down */ | |
| dup_x2(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| CASE(_dup2_x1): /* insert top 2 slots three down */ | |
| dup2_x1(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| CASE(_dup2_x2): /* insert top 2 slots four down */ | |
| dup2_x2(topOfStack); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| CASE(_swap): { /* swap top two elements on the stack */ | |
| swap(topOfStack); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| } | |
| /* Perform various binary integer operations */ | |
| #undef OPC_INT_BINARY | |
| #define OPC_INT_BINARY(opcname, opname, test) \ | |
| CASE(_i##opcname): \ | |
| if (test && (STACK_INT(-1) == 0)) { \ | |
| VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ | |
| "/ by zero"); \ | |
| } \ | |
| SET_STACK_INT(VMint##opname(STACK_INT(-2), \ | |
| STACK_INT(-1)), \ | |
| -2); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ | |
| CASE(_l##opcname): \ | |
| { \ | |
| if (test) { \ | |
| jlong l1 = STACK_LONG(-1); \ | |
| if (VMlongEqz(l1)) { \ | |
| VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \ | |
| "/ by long zero"); \ | |
| } \ | |
| } \ | |
| /* First long at (-1,-2) next long at (-3,-4) */ \ | |
| SET_STACK_LONG(VMlong##opname(STACK_LONG(-3), \ | |
| STACK_LONG(-1)), \ | |
| -3); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ | |
| } | |
| OPC_INT_BINARY(add, Add, 0); | |
| OPC_INT_BINARY(sub, Sub, 0); | |
| OPC_INT_BINARY(mul, Mul, 0); | |
| OPC_INT_BINARY(and, And, 0); | |
| OPC_INT_BINARY(or, Or, 0); | |
| OPC_INT_BINARY(xor, Xor, 0); | |
| OPC_INT_BINARY(div, Div, 1); | |
| OPC_INT_BINARY(rem, Rem, 1); | |
| /* Perform various binary floating number operations */ | |
| /* On some machine/platforms/compilers div zero check can be implicit */ | |
| #undef OPC_FLOAT_BINARY | |
| #define OPC_FLOAT_BINARY(opcname, opname) \ | |
| CASE(_d##opcname): { \ | |
| SET_STACK_DOUBLE(VMdouble##opname(STACK_DOUBLE(-3), \ | |
| STACK_DOUBLE(-1)), \ | |
| -3); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -2); \ | |
| } \ | |
| CASE(_f##opcname): \ | |
| SET_STACK_FLOAT(VMfloat##opname(STACK_FLOAT(-2), \ | |
| STACK_FLOAT(-1)), \ | |
| -2); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| OPC_FLOAT_BINARY(add, Add); | |
| OPC_FLOAT_BINARY(sub, Sub); | |
| OPC_FLOAT_BINARY(mul, Mul); | |
| OPC_FLOAT_BINARY(div, Div); | |
| OPC_FLOAT_BINARY(rem, Rem); | |
| /* Shift operations | |
| * Shift left int and long: ishl, lshl | |
| * Logical shift right int and long w/zero extension: iushr, lushr | |
| * Arithmetic shift right int and long w/sign extension: ishr, lshr | |
| */ | |
| #undef OPC_SHIFT_BINARY | |
| #define OPC_SHIFT_BINARY(opcname, opname) \ | |
| CASE(_i##opcname): \ | |
| SET_STACK_INT(VMint##opname(STACK_INT(-2), \ | |
| STACK_INT(-1)), \ | |
| -2); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ | |
| CASE(_l##opcname): \ | |
| { \ | |
| SET_STACK_LONG(VMlong##opname(STACK_LONG(-2), \ | |
| STACK_INT(-1)), \ | |
| -2); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ | |
| } | |
| OPC_SHIFT_BINARY(shl, Shl); | |
| OPC_SHIFT_BINARY(shr, Shr); | |
| OPC_SHIFT_BINARY(ushr, Ushr); | |
| /* Increment local variable by constant */ | |
| CASE(_iinc): | |
| { | |
| // locals[pc[1]].j.i += (jbyte)(pc[2]); | |
| SET_LOCALS_INT(LOCALS_INT(pc[1]) + (jbyte)(pc[2]), pc[1]); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| /* negate the value on the top of the stack */ | |
| CASE(_ineg): | |
| SET_STACK_INT(VMintNeg(STACK_INT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_fneg): | |
| SET_STACK_FLOAT(VMfloatNeg(STACK_FLOAT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_lneg): | |
| { | |
| SET_STACK_LONG(VMlongNeg(STACK_LONG(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| } | |
| CASE(_dneg): | |
| { | |
| SET_STACK_DOUBLE(VMdoubleNeg(STACK_DOUBLE(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| } | |
| /* Conversion operations */ | |
| CASE(_i2f): /* convert top of stack int to float */ | |
| SET_STACK_FLOAT(VMint2Float(STACK_INT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_i2l): /* convert top of stack int to long */ | |
| { | |
| // this is ugly QQQ | |
| jlong r = VMint2Long(STACK_INT(-1)); | |
| MORE_STACK(-1); // Pop | |
| SET_STACK_LONG(r, 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_i2d): /* convert top of stack int to double */ | |
| { | |
| // this is ugly QQQ (why cast to jlong?? ) | |
| jdouble r = (jlong)STACK_INT(-1); | |
| MORE_STACK(-1); // Pop | |
| SET_STACK_DOUBLE(r, 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_l2i): /* convert top of stack long to int */ | |
| { | |
| jint r = VMlong2Int(STACK_LONG(-1)); | |
| MORE_STACK(-2); // Pop | |
| SET_STACK_INT(r, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_l2f): /* convert top of stack long to float */ | |
| { | |
| jlong r = STACK_LONG(-1); | |
| MORE_STACK(-2); // Pop | |
| SET_STACK_FLOAT(VMlong2Float(r), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_l2d): /* convert top of stack long to double */ | |
| { | |
| jlong r = STACK_LONG(-1); | |
| MORE_STACK(-2); // Pop | |
| SET_STACK_DOUBLE(VMlong2Double(r), 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_f2i): /* Convert top of stack float to int */ | |
| SET_STACK_INT(SharedRuntime::f2i(STACK_FLOAT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_f2l): /* convert top of stack float to long */ | |
| { | |
| jlong r = SharedRuntime::f2l(STACK_FLOAT(-1)); | |
| MORE_STACK(-1); // POP | |
| SET_STACK_LONG(r, 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_f2d): /* convert top of stack float to double */ | |
| { | |
| jfloat f; | |
| jdouble r; | |
| f = STACK_FLOAT(-1); | |
| r = (jdouble) f; | |
| MORE_STACK(-1); // POP | |
| SET_STACK_DOUBLE(r, 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_d2i): /* convert top of stack double to int */ | |
| { | |
| jint r1 = SharedRuntime::d2i(STACK_DOUBLE(-1)); | |
| MORE_STACK(-2); | |
| SET_STACK_INT(r1, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_d2f): /* convert top of stack double to float */ | |
| { | |
| jfloat r1 = VMdouble2Float(STACK_DOUBLE(-1)); | |
| MORE_STACK(-2); | |
| SET_STACK_FLOAT(r1, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_d2l): /* convert top of stack double to long */ | |
| { | |
| jlong r1 = SharedRuntime::d2l(STACK_DOUBLE(-1)); | |
| MORE_STACK(-2); | |
| SET_STACK_LONG(r1, 1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); | |
| } | |
| CASE(_i2b): | |
| SET_STACK_INT(VMint2Byte(STACK_INT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_i2c): | |
| SET_STACK_INT(VMint2Char(STACK_INT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| CASE(_i2s): | |
| SET_STACK_INT(VMint2Short(STACK_INT(-1)), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| /* comparison operators */ | |
| #define COMPARISON_OP(name, comparison) \ | |
| CASE(_if_icmp##name): { \ | |
| int skip = (STACK_INT(-2) comparison STACK_INT(-1)) \ | |
| ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ | |
| address branch_pc = pc; \ | |
| UPDATE_PC_AND_TOS(skip, -2); \ | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); \ | |
| CONTINUE; \ | |
| } \ | |
| CASE(_if##name): { \ | |
| int skip = (STACK_INT(-1) comparison 0) \ | |
| ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ | |
| address branch_pc = pc; \ | |
| UPDATE_PC_AND_TOS(skip, -1); \ | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); \ | |
| CONTINUE; \ | |
| } | |
| #define COMPARISON_OP2(name, comparison) \ | |
| COMPARISON_OP(name, comparison) \ | |
| CASE(_if_acmp##name): { \ | |
| int skip = (STACK_OBJECT(-2) comparison STACK_OBJECT(-1)) \ | |
| ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ | |
| address branch_pc = pc; \ | |
| UPDATE_PC_AND_TOS(skip, -2); \ | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); \ | |
| CONTINUE; \ | |
| } | |
| #define NULL_COMPARISON_NOT_OP(name) \ | |
| CASE(_if##name): { \ | |
| int skip = (!(STACK_OBJECT(-1) == nullptr)) \ | |
| ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ | |
| address branch_pc = pc; \ | |
| UPDATE_PC_AND_TOS(skip, -1); \ | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); \ | |
| CONTINUE; \ | |
| } | |
| #define NULL_COMPARISON_OP(name) \ | |
| CASE(_if##name): { \ | |
| int skip = ((STACK_OBJECT(-1) == nullptr)) \ | |
| ? (int16_t)Bytes::get_Java_u2(pc + 1) : 3; \ | |
| address branch_pc = pc; \ | |
| UPDATE_PC_AND_TOS(skip, -1); \ | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); \ | |
| CONTINUE; \ | |
| } | |
| COMPARISON_OP(lt, <); | |
| COMPARISON_OP(gt, >); | |
| COMPARISON_OP(le, <=); | |
| COMPARISON_OP(ge, >=); | |
| COMPARISON_OP2(eq, ==); /* include ref comparison */ | |
| COMPARISON_OP2(ne, !=); /* include ref comparison */ | |
| NULL_COMPARISON_OP(null); | |
| NULL_COMPARISON_NOT_OP(nonnull); | |
| /* Goto pc at specified offset in switch table. */ | |
| CASE(_tableswitch): { | |
| jint* lpc = (jint*)VMalignWordUp(pc+1); | |
| int32_t key = STACK_INT(-1); | |
| int32_t low = Bytes::get_Java_u4((address)&lpc[1]); | |
| int32_t high = Bytes::get_Java_u4((address)&lpc[2]); | |
| int32_t skip; | |
| key -= low; | |
| if (((uint32_t) key > (uint32_t)(high - low))) { | |
| skip = Bytes::get_Java_u4((address)&lpc[0]); | |
| } else { | |
| skip = Bytes::get_Java_u4((address)&lpc[key + 3]); | |
| } | |
| // Does this really need a full backedge check (osr)? | |
| address branch_pc = pc; | |
| UPDATE_PC_AND_TOS(skip, -1); | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); | |
| CONTINUE; | |
| } | |
| /* Goto pc whose table entry matches specified key. */ | |
| CASE(_lookupswitch): { | |
| jint* lpc = (jint*)VMalignWordUp(pc+1); | |
| int32_t key = STACK_INT(-1); | |
| int32_t skip = Bytes::get_Java_u4((address) lpc); /* default amount */ | |
| int32_t npairs = Bytes::get_Java_u4((address) &lpc[1]); | |
| while (--npairs >= 0) { | |
| lpc += 2; | |
| if (key == (int32_t)Bytes::get_Java_u4((address)lpc)) { | |
| skip = Bytes::get_Java_u4((address)&lpc[1]); | |
| break; | |
| } | |
| } | |
| address branch_pc = pc; | |
| UPDATE_PC_AND_TOS(skip, -1); | |
| DO_BACKEDGE_CHECKS(skip, branch_pc); | |
| CONTINUE; | |
| } | |
| CASE(_fcmpl): | |
| CASE(_fcmpg): | |
| { | |
| SET_STACK_INT(VMfloatCompare(STACK_FLOAT(-2), | |
| STACK_FLOAT(-1), | |
| (opcode == Bytecodes::_fcmpl ? -1 : 1)), | |
| -2); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| } | |
| CASE(_dcmpl): | |
| CASE(_dcmpg): | |
| { | |
| int r = VMdoubleCompare(STACK_DOUBLE(-3), | |
| STACK_DOUBLE(-1), | |
| (opcode == Bytecodes::_dcmpl ? -1 : 1)); | |
| MORE_STACK(-4); // Pop | |
| SET_STACK_INT(r, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_lcmp): | |
| { | |
| int r = VMlongCompare(STACK_LONG(-3), STACK_LONG(-1)); | |
| MORE_STACK(-4); | |
| SET_STACK_INT(r, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| /* Return from a method */ | |
| CASE(_areturn): | |
| CASE(_ireturn): | |
| CASE(_freturn): | |
| CASE(_lreturn): | |
| CASE(_dreturn): | |
| CASE(_return): { | |
| // Allow a safepoint before returning to frame manager. | |
| RETURN_SAFEPOINT; | |
| goto handle_return; | |
| } | |
| CASE(_return_register_finalizer): { | |
| oop rcvr = LOCALS_OBJECT(0); | |
| VERIFY_OOP(rcvr); | |
| if (rcvr->klass()->has_finalizer()) { | |
| CALL_VM(InterpreterRuntime::register_finalizer(THREAD, rcvr), handle_exception); | |
| } | |
| goto handle_return; | |
| } | |
| /* Array access byte-codes */ | |
| #define ARRAY_INDEX_CHECK(arrObj, index) \ | |
| /* Two integers, the additional message, and the null-terminator */ \ | |
| char message[2 * jintAsStringSize + 33]; \ | |
| CHECK_NULL(arrObj); \ | |
| if ((uint32_t)index >= (uint32_t)arrObj->length()) { \ | |
| jio_snprintf(message, sizeof(message), \ | |
| "Index %d out of bounds for length %d", \ | |
| index, arrObj->length()); \ | |
| VM_JAVA_ERROR(vmSymbols::java_lang_ArrayIndexOutOfBoundsException(), \ | |
| message); \ | |
| } | |
| /* Every array access byte-code starts out like this */ | |
| // arrayOopDesc* arrObj = (arrayOopDesc*)STACK_OBJECT(arrayOff); | |
| #define ARRAY_INTRO(arrayOff) \ | |
| arrayOop arrObj = (arrayOop)STACK_OBJECT(arrayOff); \ | |
| jint index = STACK_INT(arrayOff + 1); \ | |
| ARRAY_INDEX_CHECK(arrObj, index) | |
| /* 32-bit loads. These handle conversion from < 32-bit types */ | |
| #define ARRAY_LOADTO32(T, T2, format, stackRes, extra) \ | |
| { \ | |
| ARRAY_INTRO(-2); \ | |
| (void)extra; \ | |
| SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), \ | |
| -2); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); \ | |
| } | |
| /* 64-bit loads */ | |
| #define ARRAY_LOADTO64(T,T2, stackRes, extra) \ | |
| { \ | |
| ARRAY_INTRO(-2); \ | |
| SET_ ## stackRes(*(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)), -1); \ | |
| (void)extra; \ | |
| UPDATE_PC_AND_CONTINUE(1); \ | |
| } | |
| CASE(_iaload): | |
| ARRAY_LOADTO32(T_INT, jint, "%d", STACK_INT, 0); | |
| CASE(_faload): | |
| ARRAY_LOADTO32(T_FLOAT, jfloat, "%f", STACK_FLOAT, 0); | |
| CASE(_aaload): { | |
| ARRAY_INTRO(-2); | |
| SET_STACK_OBJECT(((objArrayOop) arrObj)->obj_at(index), -2); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| } | |
| CASE(_baload): | |
| ARRAY_LOADTO32(T_BYTE, jbyte, "%d", STACK_INT, 0); | |
| CASE(_caload): | |
| ARRAY_LOADTO32(T_CHAR, jchar, "%d", STACK_INT, 0); | |
| CASE(_saload): | |
| ARRAY_LOADTO32(T_SHORT, jshort, "%d", STACK_INT, 0); | |
| CASE(_laload): | |
| ARRAY_LOADTO64(T_LONG, jlong, STACK_LONG, 0); | |
| CASE(_daload): | |
| ARRAY_LOADTO64(T_DOUBLE, jdouble, STACK_DOUBLE, 0); | |
| CASE(_fast_icaload): { | |
| // Custom fast access for iload,caload pair. | |
| arrayOop arrObj = (arrayOop) STACK_OBJECT(-1); | |
| jint index = LOCALS_INT(pc[1]); | |
| ARRAY_INDEX_CHECK(arrObj, index); | |
| SET_STACK_INT(*(jchar *)(((address) arrObj->base(T_CHAR)) + index * sizeof(jchar)), -1); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, 0); | |
| } | |
| /* 32-bit stores. These handle conversion to < 32-bit types */ | |
| #define ARRAY_STOREFROM32(T, T2, format, stackSrc, extra) \ | |
| { \ | |
| ARRAY_INTRO(-3); \ | |
| (void)extra; \ | |
| *(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)) = stackSrc( -1); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); \ | |
| } | |
| /* 64-bit stores */ | |
| #define ARRAY_STOREFROM64(T, T2, stackSrc, extra) \ | |
| { \ | |
| ARRAY_INTRO(-4); \ | |
| (void)extra; \ | |
| *(T2 *)(((address) arrObj->base(T)) + index * sizeof(T2)) = stackSrc( -1); \ | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -4); \ | |
| } | |
| CASE(_iastore): | |
| ARRAY_STOREFROM32(T_INT, jint, "%d", STACK_INT, 0); | |
| CASE(_fastore): | |
| ARRAY_STOREFROM32(T_FLOAT, jfloat, "%f", STACK_FLOAT, 0); | |
| /* | |
| * This one looks different because of the assignability check | |
| */ | |
| CASE(_aastore): { | |
| oop rhsObject = STACK_OBJECT(-1); | |
| VERIFY_OOP(rhsObject); | |
| ARRAY_INTRO( -3); | |
| // arrObj, index are set | |
| if (rhsObject != nullptr) { | |
| /* Check assignability of rhsObject into arrObj */ | |
| Klass* rhsKlass = rhsObject->klass(); // EBX (subclass) | |
| Klass* elemKlass = ObjArrayKlass::cast(arrObj->klass())->element_klass(); // superklass EAX | |
| // | |
| // Check for compatibility. This check must not GC!! | |
| // Seems way more expensive now that we must dispatch | |
| // | |
| if (rhsKlass != elemKlass && !rhsKlass->is_subtype_of(elemKlass)) { // ebx->is... | |
| VM_JAVA_ERROR(vmSymbols::java_lang_ArrayStoreException(), ""); | |
| } | |
| } | |
| ((objArrayOop) arrObj)->obj_at_put(index, rhsObject); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); | |
| } | |
| CASE(_bastore): { | |
| ARRAY_INTRO(-3); | |
| int item = STACK_INT(-1); | |
| // if it is a T_BOOLEAN array, mask the stored value to 0/1 | |
| if (arrObj->klass() == Universe::boolArrayKlass()) { | |
| item &= 1; | |
| } else { | |
| assert(arrObj->klass() == Universe::byteArrayKlass(), | |
| "should be byte array otherwise"); | |
| } | |
| ((typeArrayOop)arrObj)->byte_at_put(index, item); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -3); | |
| } | |
| CASE(_castore): | |
| ARRAY_STOREFROM32(T_CHAR, jchar, "%d", STACK_INT, 0); | |
| CASE(_sastore): | |
| ARRAY_STOREFROM32(T_SHORT, jshort, "%d", STACK_INT, 0); | |
| CASE(_lastore): | |
| ARRAY_STOREFROM64(T_LONG, jlong, STACK_LONG, 0); | |
| CASE(_dastore): | |
| ARRAY_STOREFROM64(T_DOUBLE, jdouble, STACK_DOUBLE, 0); | |
| CASE(_arraylength): | |
| { | |
| arrayOop ary = (arrayOop) STACK_OBJECT(-1); | |
| CHECK_NULL(ary); | |
| SET_STACK_INT(ary->length(), -1); | |
| UPDATE_PC_AND_CONTINUE(1); | |
| } | |
| /* monitorenter and monitorexit for locking/unlocking an object */ | |
| CASE(_monitorenter): { | |
| oop lockee = STACK_OBJECT(-1); | |
| // derefing's lockee ought to provoke implicit null check | |
| CHECK_NULL(lockee); | |
| // find a free monitor or one already allocated for this object | |
| // if we find a matching object then we need a new monitor | |
| // since this is recursive enter | |
| BasicObjectLock* limit = istate->monitor_base(); | |
| BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); | |
| BasicObjectLock* entry = nullptr; | |
| while (most_recent != limit ) { | |
| if (most_recent->obj() == nullptr) entry = most_recent; | |
| else if (most_recent->obj() == lockee) break; | |
| most_recent++; | |
| } | |
| if (entry != nullptr) { | |
| entry->set_obj(lockee); | |
| bool success = false; | |
| if (LockingMode == LM_LEGACY) { | |
| // Traditional fast locking. | |
| markWord displaced = lockee->mark().set_unlocked(); | |
| entry->lock()->set_displaced_header(displaced); | |
| success = true; | |
| if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) { | |
| // Is it simple recursive case? | |
| if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) { | |
| entry->lock()->set_displaced_header(markWord::from_pointer(nullptr)); | |
| } else { | |
| success = false; | |
| } | |
| } | |
| } | |
| if (!success) { | |
| CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception); | |
| } | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| } else { | |
| istate->set_msg(more_monitors); | |
| UPDATE_PC_AND_RETURN(0); // Re-execute | |
| } | |
| } | |
| CASE(_monitorexit): { | |
| oop lockee = STACK_OBJECT(-1); | |
| CHECK_NULL(lockee); | |
| // derefing's lockee ought to provoke implicit null check | |
| // find our monitor slot | |
| BasicObjectLock* limit = istate->monitor_base(); | |
| BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base(); | |
| while (most_recent != limit ) { | |
| if ((most_recent)->obj() == lockee) { | |
| BasicLock* lock = most_recent->lock(); | |
| bool success = false; | |
| if (LockingMode == LM_LEGACY) { | |
| // If it isn't recursive we either must swap old header or call the runtime | |
| most_recent->set_obj(nullptr); | |
| success = true; | |
| markWord header = lock->displaced_header(); | |
| if (header.to_pointer() != nullptr) { | |
| markWord old_header = markWord::encode(lock); | |
| if (lockee->cas_set_mark(header, old_header) != old_header) { | |
| // restore object for the slow case | |
| most_recent->set_obj(lockee); | |
| success = false; | |
| } | |
| } | |
| } | |
| if (!success) { | |
| InterpreterRuntime::monitorexit(most_recent); | |
| } | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1); | |
| } | |
| most_recent++; | |
| } | |
| // Need to throw illegal monitor state exception | |
| CALL_VM(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD), handle_exception); | |
| ShouldNotReachHere(); | |
| } | |
| /* All of the non-quick opcodes. */ | |
| /* -Set clobbersCpIndex true if the quickened opcode clobbers the | |
| * constant pool index in the instruction. | |
| */ | |
| CASE(_getfield): | |
| CASE(_nofast_getfield): | |
| CASE(_getstatic): | |
| { | |
| u2 index; | |
| index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| // QQQ Need to make this as inlined as possible. Probably need to | |
| // split all the bytecode cases out so c++ compiler has a chance | |
| // for constant prop to fold everything possible away. | |
| // Interpreter runtime does not expect "nofast" opcodes, | |
| // prepare the vanilla opcode for it. | |
| Bytecodes::Code code = (Bytecodes::Code)opcode; | |
| if (code == Bytecodes::_nofast_getfield) { | |
| code = Bytecodes::_getfield; | |
| } | |
| if (!entry->is_resolved(code)) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, code), | |
| handle_exception); | |
| entry = cp->resolved_field_entry_at(index); | |
| } | |
| oop obj; | |
| if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { | |
| Klass* k = entry->field_holder(); | |
| obj = k->java_mirror(); | |
| MORE_STACK(1); // Assume single slot push | |
| } else { | |
| obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| // Check if we can rewrite non-volatile _getfield to one of the _fast_Xgetfield. | |
| if (REWRITE_BYTECODES && !entry->is_volatile() && | |
| ((Bytecodes::Code)opcode != Bytecodes::_nofast_getfield)) { | |
| // Rewrite current BC to _fast_Xgetfield. | |
| REWRITE_AT_PC(fast_get_type((TosState)(entry->tos_state()))); | |
| } | |
| } | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| // | |
| // Now store the result on the stack | |
| // | |
| TosState tos_type = (TosState)(entry->tos_state()); | |
| int field_offset = entry->field_offset(); | |
| if (entry->is_volatile()) { | |
| if (support_IRIW_for_not_multiple_copy_atomic_cpu) { | |
| OrderAccess::fence(); | |
| } | |
| switch (tos_type) { | |
| case btos: | |
| case ztos: | |
| SET_STACK_INT(obj->byte_field_acquire(field_offset), -1); | |
| break; | |
| case ctos: | |
| SET_STACK_INT(obj->char_field_acquire(field_offset), -1); | |
| break; | |
| case stos: | |
| SET_STACK_INT(obj->short_field_acquire(field_offset), -1); | |
| break; | |
| case itos: | |
| SET_STACK_INT(obj->int_field_acquire(field_offset), -1); | |
| break; | |
| case ftos: | |
| SET_STACK_FLOAT(obj->float_field_acquire(field_offset), -1); | |
| break; | |
| case ltos: | |
| SET_STACK_LONG(obj->long_field_acquire(field_offset), 0); | |
| MORE_STACK(1); | |
| break; | |
| case dtos: | |
| SET_STACK_DOUBLE(obj->double_field_acquire(field_offset), 0); | |
| MORE_STACK(1); | |
| break; | |
| case atos: { | |
| oop val = obj->obj_field_acquire(field_offset); | |
| VERIFY_OOP(val); | |
| SET_STACK_OBJECT(val, -1); | |
| break; | |
| } | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| } else { | |
| switch (tos_type) { | |
| case btos: | |
| case ztos: | |
| SET_STACK_INT(obj->byte_field(field_offset), -1); | |
| break; | |
| case ctos: | |
| SET_STACK_INT(obj->char_field(field_offset), -1); | |
| break; | |
| case stos: | |
| SET_STACK_INT(obj->short_field(field_offset), -1); | |
| break; | |
| case itos: | |
| SET_STACK_INT(obj->int_field(field_offset), -1); | |
| break; | |
| case ftos: | |
| SET_STACK_FLOAT(obj->float_field(field_offset), -1); | |
| break; | |
| case ltos: | |
| SET_STACK_LONG(obj->long_field(field_offset), 0); | |
| MORE_STACK(1); | |
| break; | |
| case dtos: | |
| SET_STACK_DOUBLE(obj->double_field(field_offset), 0); | |
| MORE_STACK(1); | |
| break; | |
| case atos: { | |
| oop val = obj->obj_field(field_offset); | |
| VERIFY_OOP(val); | |
| SET_STACK_OBJECT(val, -1); | |
| break; | |
| } | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| } | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_putfield): | |
| CASE(_nofast_putfield): | |
| CASE(_putstatic): | |
| { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| // Interpreter runtime does not expect "nofast" opcodes, | |
| // prepare the vanilla opcode for it. | |
| Bytecodes::Code code = (Bytecodes::Code)opcode; | |
| if (code == Bytecodes::_nofast_putfield) { | |
| code = Bytecodes::_putfield; | |
| } | |
| if (!entry->is_resolved(code)) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, code), | |
| handle_exception); | |
| entry = cp->resolved_field_entry_at(index); | |
| } | |
| // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases | |
| // out so c++ compiler has a chance for constant prop to fold everything possible away. | |
| oop obj; | |
| int count; | |
| TosState tos_type = (TosState)(entry->tos_state()); | |
| count = -1; | |
| if (tos_type == ltos || tos_type == dtos) { | |
| --count; | |
| } | |
| if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) { | |
| Klass* k = entry->field_holder(); | |
| obj = k->java_mirror(); | |
| } else { | |
| --count; | |
| obj = STACK_OBJECT(count); | |
| CHECK_NULL(obj); | |
| // Check if we can rewrite non-volatile _putfield to one of the _fast_Xputfield. | |
| if (REWRITE_BYTECODES && !entry->is_volatile() && | |
| ((Bytecodes::Code)opcode != Bytecodes::_nofast_putfield)) { | |
| // Rewrite current BC to _fast_Xputfield. | |
| REWRITE_AT_PC(fast_put_type((TosState)(entry->tos_state()))); | |
| } | |
| } | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| // | |
| // Now store the result | |
| // | |
| int field_offset = entry->field_offset(); | |
| if (entry->is_volatile()) { | |
| switch (tos_type) { | |
| case ztos: | |
| obj->release_byte_field_put(field_offset, (STACK_INT(-1) & 1)); // only store LSB | |
| break; | |
| case btos: | |
| obj->release_byte_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case ctos: | |
| obj->release_char_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case stos: | |
| obj->release_short_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case itos: | |
| obj->release_int_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case ftos: | |
| obj->release_float_field_put(field_offset, STACK_FLOAT(-1)); | |
| break; | |
| case ltos: | |
| obj->release_long_field_put(field_offset, STACK_LONG(-1)); | |
| break; | |
| case dtos: | |
| obj->release_double_field_put(field_offset, STACK_DOUBLE(-1)); | |
| break; | |
| case atos: { | |
| oop val = STACK_OBJECT(-1); | |
| VERIFY_OOP(val); | |
| obj->release_obj_field_put(field_offset, val); | |
| break; | |
| } | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| OrderAccess::storeload(); | |
| } else { | |
| switch (tos_type) { | |
| case ztos: | |
| obj->byte_field_put(field_offset, (STACK_INT(-1) & 1)); // only store LSB | |
| break; | |
| case btos: | |
| obj->byte_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case ctos: | |
| obj->char_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case stos: | |
| obj->short_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case itos: | |
| obj->int_field_put(field_offset, STACK_INT(-1)); | |
| break; | |
| case ftos: | |
| obj->float_field_put(field_offset, STACK_FLOAT(-1)); | |
| break; | |
| case ltos: | |
| obj->long_field_put(field_offset, STACK_LONG(-1)); | |
| break; | |
| case dtos: | |
| obj->double_field_put(field_offset, STACK_DOUBLE(-1)); | |
| break; | |
| case atos: { | |
| oop val = STACK_OBJECT(-1); | |
| VERIFY_OOP(val); | |
| obj->obj_field_put(field_offset, val); | |
| break; | |
| } | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| } | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, count); | |
| } | |
| CASE(_new): { | |
| u2 index = Bytes::get_Java_u2(pc+1); | |
| // Attempt TLAB allocation first. | |
| // | |
| // To do this, we need to make sure: | |
| // - klass is initialized | |
| // - klass can be fastpath allocated (e.g. does not have finalizer) | |
| // - TLAB accepts the allocation | |
| ConstantPool* constants = istate->method()->constants(); | |
| if (UseTLAB && !constants->tag_at(index).is_unresolved_klass()) { | |
| Klass* entry = constants->resolved_klass_at(index); | |
| InstanceKlass* ik = InstanceKlass::cast(entry); | |
| if (ik->is_initialized() && ik->can_be_fastpath_allocated()) { | |
| size_t obj_size = ik->size_helper(); | |
| HeapWord* result = THREAD->tlab().allocate(obj_size); | |
| if (result != nullptr) { | |
| // Initialize object field block. | |
| if (!ZeroTLAB) { | |
| // The TLAB was not pre-zeroed, we need to clear the memory here. | |
| size_t hdr_size = oopDesc::header_size(); | |
| Copy::fill_to_words(result + hdr_size, obj_size - hdr_size, 0); | |
| } | |
| // Initialize header, mirrors MemAllocator. | |
| if (UseCompactObjectHeaders) { | |
| oopDesc::release_set_mark(result, ik->prototype_header()); | |
| } else { | |
| oopDesc::set_mark(result, markWord::prototype()); | |
| if (oopDesc::has_klass_gap()) { | |
| oopDesc::set_klass_gap(result, 0); | |
| } | |
| oopDesc::release_set_klass(result, ik); | |
| } | |
| oop obj = cast_to_oop(result); | |
| // Must prevent reordering of stores for object initialization | |
| // with stores that publish the new object. | |
| OrderAccess::storestore(); | |
| SET_STACK_OBJECT(obj, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); | |
| } | |
| } | |
| } | |
| // Slow case allocation | |
| CALL_VM(InterpreterRuntime::_new(THREAD, METHOD->constants(), index), | |
| handle_exception); | |
| // Must prevent reordering of stores for object initialization | |
| // with stores that publish the new object. | |
| OrderAccess::storestore(); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), 0); | |
| THREAD->set_vm_result_oop(nullptr); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1); | |
| } | |
| CASE(_anewarray): { | |
| u2 index = Bytes::get_Java_u2(pc+1); | |
| jint size = STACK_INT(-1); | |
| CALL_VM(InterpreterRuntime::anewarray(THREAD, METHOD->constants(), index, size), | |
| handle_exception); | |
| // Must prevent reordering of stores for object initialization | |
| // with stores that publish the new object. | |
| OrderAccess::storestore(); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), -1); | |
| THREAD->set_vm_result_oop(nullptr); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_multianewarray): { | |
| jint dims = *(pc+3); | |
| jint size = STACK_INT(-1); | |
| // stack grows down, dimensions are up! | |
| jint *dimarray = | |
| (jint*)&topOfStack[dims * Interpreter::stackElementWords+ | |
| Interpreter::stackElementWords-1]; | |
| //adjust pointer to start of stack element | |
| CALL_VM(InterpreterRuntime::multianewarray(THREAD, dimarray), | |
| handle_exception); | |
| // Must prevent reordering of stores for object initialization | |
| // with stores that publish the new object. | |
| OrderAccess::storestore(); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), -dims); | |
| THREAD->set_vm_result_oop(nullptr); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, -(dims-1)); | |
| } | |
| CASE(_checkcast): | |
| if (STACK_OBJECT(-1) != nullptr) { | |
| VERIFY_OOP(STACK_OBJECT(-1)); | |
| u2 index = Bytes::get_Java_u2(pc+1); | |
| // Constant pool may have actual klass or unresolved klass. If it is | |
| // unresolved we must resolve it. | |
| if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { | |
| CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); | |
| } | |
| Klass* klassOf = (Klass*) METHOD->constants()->resolved_klass_at(index); | |
| Klass* objKlass = STACK_OBJECT(-1)->klass(); // ebx | |
| // | |
| // Check for compatibility. This check must not GC!! | |
| // Seems way more expensive now that we must dispatch. | |
| // | |
| if (objKlass != klassOf && !objKlass->is_subtype_of(klassOf)) { | |
| ResourceMark rm(THREAD); | |
| char* message = SharedRuntime::generate_class_cast_message( | |
| objKlass, klassOf); | |
| VM_JAVA_ERROR(vmSymbols::java_lang_ClassCastException(), message); | |
| } | |
| } | |
| UPDATE_PC_AND_CONTINUE(3); | |
| CASE(_instanceof): | |
| if (STACK_OBJECT(-1) == nullptr) { | |
| SET_STACK_INT(0, -1); | |
| } else { | |
| VERIFY_OOP(STACK_OBJECT(-1)); | |
| u2 index = Bytes::get_Java_u2(pc+1); | |
| // Constant pool may have actual klass or unresolved klass. If it is | |
| // unresolved we must resolve it. | |
| if (METHOD->constants()->tag_at(index).is_unresolved_klass()) { | |
| CALL_VM(InterpreterRuntime::quicken_io_cc(THREAD), handle_exception); | |
| } | |
| Klass* klassOf = (Klass*) METHOD->constants()->resolved_klass_at(index); | |
| Klass* objKlass = STACK_OBJECT(-1)->klass(); | |
| // | |
| // Check for compatibility. This check must not GC!! | |
| // Seems way more expensive now that we must dispatch. | |
| // | |
| if ( objKlass == klassOf || objKlass->is_subtype_of(klassOf)) { | |
| SET_STACK_INT(1, -1); | |
| } else { | |
| SET_STACK_INT(0, -1); | |
| } | |
| } | |
| UPDATE_PC_AND_CONTINUE(3); | |
| CASE(_ldc_w): | |
| CASE(_ldc): | |
| { | |
| u2 index; | |
| bool wide = false; | |
| int incr = 2; // frequent case | |
| if (opcode == Bytecodes::_ldc) { | |
| index = pc[1]; | |
| } else { | |
| index = Bytes::get_Java_u2(pc+1); | |
| incr = 3; | |
| wide = true; | |
| } | |
| ConstantPool* constants = METHOD->constants(); | |
| switch (constants->tag_at(index).value()) { | |
| case JVM_CONSTANT_Integer: | |
| SET_STACK_INT(constants->int_at(index), 0); | |
| break; | |
| case JVM_CONSTANT_Float: | |
| SET_STACK_FLOAT(constants->float_at(index), 0); | |
| break; | |
| case JVM_CONSTANT_String: | |
| { | |
| oop result = constants->resolved_reference_at(index); | |
| if (result == nullptr) { | |
| CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), 0); | |
| THREAD->set_vm_result_oop(nullptr); | |
| } else { | |
| VERIFY_OOP(result); | |
| SET_STACK_OBJECT(result, 0); | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_Class: | |
| VERIFY_OOP(constants->resolved_klass_at(index)->java_mirror()); | |
| SET_STACK_OBJECT(constants->resolved_klass_at(index)->java_mirror(), 0); | |
| break; | |
| case JVM_CONSTANT_UnresolvedClass: | |
| case JVM_CONSTANT_UnresolvedClassInError: | |
| CALL_VM(InterpreterRuntime::ldc(THREAD, wide), handle_exception); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), 0); | |
| THREAD->set_vm_result_oop(nullptr); | |
| break; | |
| case JVM_CONSTANT_Dynamic: | |
| case JVM_CONSTANT_DynamicInError: | |
| { | |
| CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception); | |
| oop result = THREAD->vm_result_oop(); | |
| VERIFY_OOP(result); | |
| jvalue value; | |
| BasicType type = java_lang_boxing_object::get_value(result, &value); | |
| switch (type) { | |
| case T_FLOAT: SET_STACK_FLOAT(value.f, 0); break; | |
| case T_INT: SET_STACK_INT(value.i, 0); break; | |
| case T_SHORT: SET_STACK_INT(value.s, 0); break; | |
| case T_BYTE: SET_STACK_INT(value.b, 0); break; | |
| case T_CHAR: SET_STACK_INT(value.c, 0); break; | |
| case T_BOOLEAN: SET_STACK_INT(value.z, 0); break; | |
| default: ShouldNotReachHere(); | |
| } | |
| break; | |
| } | |
| default: ShouldNotReachHere(); | |
| } | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1); | |
| } | |
| CASE(_ldc2_w): | |
| { | |
| u2 index = Bytes::get_Java_u2(pc+1); | |
| ConstantPool* constants = METHOD->constants(); | |
| switch (constants->tag_at(index).value()) { | |
| case JVM_CONSTANT_Long: | |
| SET_STACK_LONG(constants->long_at(index), 1); | |
| break; | |
| case JVM_CONSTANT_Double: | |
| SET_STACK_DOUBLE(constants->double_at(index), 1); | |
| break; | |
| case JVM_CONSTANT_Dynamic: | |
| case JVM_CONSTANT_DynamicInError: | |
| { | |
| CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception); | |
| oop result = THREAD->vm_result_oop(); | |
| VERIFY_OOP(result); | |
| jvalue value; | |
| BasicType type = java_lang_boxing_object::get_value(result, &value); | |
| switch (type) { | |
| case T_DOUBLE: SET_STACK_DOUBLE(value.d, 1); break; | |
| case T_LONG: SET_STACK_LONG(value.j, 1); break; | |
| default: ShouldNotReachHere(); | |
| } | |
| break; | |
| } | |
| default: ShouldNotReachHere(); | |
| } | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, 2); | |
| } | |
| CASE(_fast_aldc_w): | |
| CASE(_fast_aldc): { | |
| u2 index; | |
| int incr; | |
| if (opcode == Bytecodes::_fast_aldc) { | |
| index = pc[1]; | |
| incr = 2; | |
| } else { | |
| index = Bytes::get_native_u2(pc+1); | |
| incr = 3; | |
| } | |
| // We are resolved if the resolved_references array contains a non-null object (CallSite, etc.) | |
| // This kind of CP cache entry does not need to match the flags byte, because | |
| // there is a 1-1 relation between bytecode type and CP entry type. | |
| ConstantPool* constants = METHOD->constants(); | |
| oop result = constants->resolved_reference_at(index); | |
| if (result == nullptr) { | |
| CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), | |
| handle_exception); | |
| result = THREAD->vm_result_oop(); | |
| } | |
| if (result == Universe::the_null_sentinel()) | |
| result = nullptr; | |
| VERIFY_OOP(result); | |
| SET_STACK_OBJECT(result, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1); | |
| } | |
| CASE(_invokedynamic): { | |
| u4 index = Bytes::get_native_u4(pc+1); | |
| ResolvedIndyEntry* indy_info = cp->resolved_indy_entry_at(index); | |
| if (!indy_info->is_resolved()) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode), | |
| handle_exception); | |
| indy_info = cp->resolved_indy_entry_at(index); // get resolved entry | |
| } | |
| Method* method = indy_info->method(); | |
| if (VerifyOops) method->verify(); | |
| if (indy_info->has_appendix()) { | |
| constantPoolHandle cp(THREAD, METHOD->constants()); | |
| SET_STACK_OBJECT(cp->resolved_reference_from_indy(index), 0); | |
| MORE_STACK(1); | |
| } | |
| istate->set_msg(call_method); | |
| istate->set_callee(method); | |
| istate->set_callee_entry_point(method->from_interpreted_entry()); | |
| istate->set_bcp_advance(5); | |
| UPDATE_PC_AND_RETURN(0); // I'll be back... | |
| } | |
| CASE(_invokehandle): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedMethodEntry* entry = cp->resolved_method_entry_at(index); | |
| if (! entry->is_resolved((Bytecodes::Code) opcode)) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode), | |
| handle_exception); | |
| entry = cp->resolved_method_entry_at(index); | |
| } | |
| Method* method = entry->method(); | |
| if (VerifyOops) method->verify(); | |
| if (entry->has_appendix()) { | |
| constantPoolHandle cp(THREAD, METHOD->constants()); | |
| SET_STACK_OBJECT(cp->cache()->appendix_if_resolved(entry), 0); | |
| MORE_STACK(1); | |
| } | |
| istate->set_msg(call_method); | |
| istate->set_callee(method); | |
| istate->set_callee_entry_point(method->from_interpreted_entry()); | |
| istate->set_bcp_advance(3); | |
| UPDATE_PC_AND_RETURN(0); // I'll be back... | |
| } | |
| CASE(_invokeinterface): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases | |
| // out so c++ compiler has a chance for constant prop to fold everything possible away. | |
| ResolvedMethodEntry* entry = cp->resolved_method_entry_at(index); | |
| if (!entry->is_resolved((Bytecodes::Code)opcode)) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode), | |
| handle_exception); | |
| } | |
| istate->set_msg(call_method); | |
| // Special case of invokeinterface called for virtual method of | |
| // java.lang.Object. See cpCache.cpp for details. | |
| Method* callee = nullptr; | |
| if (entry->is_forced_virtual()) { | |
| CHECK_NULL(STACK_OBJECT(-(entry->number_of_parameters()))); | |
| if (entry->is_vfinal()) { | |
| callee = entry->method(); | |
| } else { | |
| // Get receiver. | |
| int parms = entry->number_of_parameters(); | |
| // Same comments as invokevirtual apply here. | |
| oop rcvr = STACK_OBJECT(-parms); | |
| VERIFY_OOP(rcvr); | |
| Klass* rcvrKlass = rcvr->klass(); | |
| callee = (Method*) rcvrKlass->method_at_vtable(entry->table_index()); | |
| } | |
| } else if (entry->is_vfinal()) { | |
| // private interface method invocations | |
| // | |
| // Ensure receiver class actually implements | |
| // the resolved interface class. The link resolver | |
| // does this, but only for the first time this | |
| // interface is being called. | |
| int parms = entry->number_of_parameters(); | |
| oop rcvr = STACK_OBJECT(-parms); | |
| CHECK_NULL(rcvr); | |
| Klass* recv_klass = rcvr->klass(); | |
| Klass* resolved_klass = entry->interface_klass(); | |
| if (!recv_klass->is_subtype_of(resolved_klass)) { | |
| ResourceMark rm(THREAD); | |
| char buf[200]; | |
| jio_snprintf(buf, sizeof(buf), "Class %s does not implement the requested interface %s", | |
| recv_klass->external_name(), | |
| resolved_klass->external_name()); | |
| VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); | |
| } | |
| callee = entry->method(); | |
| } | |
| if (callee != nullptr) { | |
| istate->set_callee(callee); | |
| istate->set_callee_entry_point(callee->from_interpreted_entry()); | |
| if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { | |
| istate->set_callee_entry_point(callee->interpreter_entry()); | |
| } | |
| istate->set_bcp_advance(5); | |
| UPDATE_PC_AND_RETURN(0); // I'll be back... | |
| } | |
| // this could definitely be cleaned up QQQ | |
| Method *interface_method = entry->method(); | |
| InstanceKlass* iclass = interface_method->method_holder(); | |
| // get receiver | |
| int parms = entry->number_of_parameters(); | |
| oop rcvr = STACK_OBJECT(-parms); | |
| CHECK_NULL(rcvr); | |
| InstanceKlass* int2 = (InstanceKlass*) rcvr->klass(); | |
| // Receiver subtype check against resolved interface klass (REFC). | |
| { | |
| Klass* refc = entry->interface_klass(); | |
| itableOffsetEntry* scan; | |
| for (scan = (itableOffsetEntry*) int2->start_of_itable(); | |
| scan->interface_klass() != nullptr; | |
| scan++) { | |
| if (scan->interface_klass() == refc) { | |
| break; | |
| } | |
| } | |
| // Check that the entry is non-null. A null entry means | |
| // that the receiver class doesn't implement the | |
| // interface, and wasn't the same as when the caller was | |
| // compiled. | |
| if (scan->interface_klass() == nullptr) { | |
| VM_JAVA_ERROR(vmSymbols::java_lang_IncompatibleClassChangeError(), ""); | |
| } | |
| } | |
| itableOffsetEntry* ki = (itableOffsetEntry*) int2->start_of_itable(); | |
| int i; | |
| for ( i = 0 ; i < int2->itable_length() ; i++, ki++ ) { | |
| if (ki->interface_klass() == iclass) break; | |
| } | |
| // If the interface isn't found, this class doesn't implement this | |
| // interface. The link resolver checks this but only for the first | |
| // time this interface is called. | |
| if (i == int2->itable_length()) { | |
| CALL_VM(InterpreterRuntime::throw_IncompatibleClassChangeErrorVerbose(THREAD, rcvr->klass(), iclass), | |
| handle_exception); | |
| } | |
| int mindex = interface_method->itable_index(); | |
| itableMethodEntry* im = ki->first_method_entry(rcvr->klass()); | |
| callee = im[mindex].method(); | |
| if (callee == nullptr) { | |
| CALL_VM(InterpreterRuntime::throw_AbstractMethodErrorVerbose(THREAD, rcvr->klass(), interface_method), | |
| handle_exception); | |
| } | |
| istate->set_callee(callee); | |
| istate->set_callee_entry_point(callee->from_interpreted_entry()); | |
| if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { | |
| istate->set_callee_entry_point(callee->interpreter_entry()); | |
| } | |
| istate->set_bcp_advance(5); | |
| UPDATE_PC_AND_RETURN(0); // I'll be back... | |
| } | |
| CASE(_invokevirtual): | |
| CASE(_invokespecial): | |
| CASE(_invokestatic): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedMethodEntry* entry = cp->resolved_method_entry_at(index); | |
| // QQQ Need to make this as inlined as possible. Probably need to split all the bytecode cases | |
| // out so c++ compiler has a chance for constant prop to fold everything possible away. | |
| if (!entry->is_resolved((Bytecodes::Code)opcode)) { | |
| CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode), | |
| handle_exception); | |
| entry = cp->resolved_method_entry_at(index); | |
| } | |
| istate->set_msg(call_method); | |
| { | |
| Method* callee; | |
| if ((Bytecodes::Code)opcode == Bytecodes::_invokevirtual) { | |
| CHECK_NULL(STACK_OBJECT(-(entry->number_of_parameters()))); | |
| if (entry->is_vfinal()) { | |
| callee = entry->method(); | |
| if (REWRITE_BYTECODES && !CDSConfig::is_using_archive() && !CDSConfig::is_dumping_archive()) { | |
| // Rewrite to _fast_invokevfinal. | |
| REWRITE_AT_PC(Bytecodes::_fast_invokevfinal); | |
| } | |
| } else { | |
| // get receiver | |
| int parms = entry->number_of_parameters(); | |
| // this works but needs a resourcemark and seems to create a vtable on every call: | |
| // Method* callee = rcvr->klass()->vtable()->method_at(cache->f2_as_index()); | |
| // | |
| // this fails with an assert | |
| // InstanceKlass* rcvrKlass = InstanceKlass::cast(STACK_OBJECT(-parms)->klass()); | |
| // but this works | |
| oop rcvr = STACK_OBJECT(-parms); | |
| VERIFY_OOP(rcvr); | |
| Klass* rcvrKlass = rcvr->klass(); | |
| /* | |
| Executing this code in java.lang.String: | |
| public String(char value[]) { | |
| this.count = value.length; | |
| this.value = (char[])value.clone(); | |
| } | |
| a find on rcvr->klass() reports: | |
| {type array char}{type array class} | |
| - klass: {other class} | |
| but using InstanceKlass::cast(STACK_OBJECT(-parms)->klass()) causes in assertion failure | |
| because rcvr->klass()->is_instance_klass() == 0 | |
| However it seems to have a vtable in the right location. Huh? | |
| Because vtables have the same offset for ArrayKlass and InstanceKlass. | |
| */ | |
| callee = (Method*) rcvrKlass->method_at_vtable(entry->table_index()); | |
| } | |
| } else { | |
| if ((Bytecodes::Code)opcode == Bytecodes::_invokespecial) { | |
| CHECK_NULL(STACK_OBJECT(-(entry->number_of_parameters()))); | |
| } | |
| callee = entry->method(); | |
| } | |
| istate->set_callee(callee); | |
| istate->set_callee_entry_point(callee->from_interpreted_entry()); | |
| if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { | |
| istate->set_callee_entry_point(callee->interpreter_entry()); | |
| } | |
| istate->set_bcp_advance(3); | |
| UPDATE_PC_AND_RETURN(0); // I'll be back... | |
| } | |
| } | |
| /* Allocate memory for a new java object. */ | |
| CASE(_newarray): { | |
| BasicType atype = (BasicType) *(pc+1); | |
| jint size = STACK_INT(-1); | |
| CALL_VM(InterpreterRuntime::newarray(THREAD, atype, size), | |
| handle_exception); | |
| // Must prevent reordering of stores for object initialization | |
| // with stores that publish the new object. | |
| OrderAccess::storestore(); | |
| SET_STACK_OBJECT(THREAD->vm_result_oop(), -1); | |
| THREAD->set_vm_result_oop(nullptr); | |
| UPDATE_PC_AND_CONTINUE(2); | |
| } | |
| /* Throw an exception. */ | |
| CASE(_athrow): { | |
| oop except_oop = STACK_OBJECT(-1); | |
| CHECK_NULL(except_oop); | |
| // set pending_exception so we use common code | |
| THREAD->set_pending_exception(except_oop, nullptr, 0); | |
| goto handle_exception; | |
| } | |
| /* goto and jsr. They are exactly the same except jsr pushes | |
| * the address of the next instruction first. | |
| */ | |
| CASE(_jsr): { | |
| /* push bytecode index on stack */ | |
| SET_STACK_ADDR(((address)pc - (intptr_t)(istate->method()->code_base()) + 3), 0); | |
| MORE_STACK(1); | |
| /* FALL THROUGH */ | |
| } | |
| CASE(_goto): | |
| { | |
| int16_t offset = (int16_t)Bytes::get_Java_u2(pc + 1); | |
| address branch_pc = pc; | |
| UPDATE_PC(offset); | |
| DO_BACKEDGE_CHECKS(offset, branch_pc); | |
| CONTINUE; | |
| } | |
| CASE(_jsr_w): { | |
| /* push return address on the stack */ | |
| SET_STACK_ADDR(((address)pc - (intptr_t)(istate->method()->code_base()) + 5), 0); | |
| MORE_STACK(1); | |
| /* FALL THROUGH */ | |
| } | |
| CASE(_goto_w): | |
| { | |
| int32_t offset = Bytes::get_Java_u4(pc + 1); | |
| address branch_pc = pc; | |
| UPDATE_PC(offset); | |
| DO_BACKEDGE_CHECKS(offset, branch_pc); | |
| CONTINUE; | |
| } | |
| /* return from a jsr or jsr_w */ | |
| CASE(_ret): { | |
| pc = istate->method()->code_base() + (intptr_t)(LOCALS_ADDR(pc[1])); | |
| UPDATE_PC_AND_CONTINUE(0); | |
| } | |
| /* debugger breakpoint */ | |
| CASE(_breakpoint): { | |
| Bytecodes::Code original_bytecode; | |
| DECACHE_STATE(); | |
| SET_LAST_JAVA_FRAME(); | |
| original_bytecode = InterpreterRuntime::get_original_bytecode_at(THREAD, | |
| METHOD, pc); | |
| RESET_LAST_JAVA_FRAME(); | |
| CACHE_STATE(); | |
| if (THREAD->has_pending_exception()) goto handle_exception; | |
| CALL_VM(InterpreterRuntime::_breakpoint(THREAD, METHOD, pc), | |
| handle_exception); | |
| opcode = (jubyte)original_bytecode; | |
| goto opcode_switch; | |
| } | |
| CASE(_fast_agetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| VERIFY_OOP(obj->obj_field(field_offset)); | |
| SET_STACK_OBJECT(obj->obj_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_bgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_INT(obj->byte_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_cgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_INT(obj->char_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_dgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_DOUBLE(obj->double_field(field_offset), 0); | |
| MORE_STACK(1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_fgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_FLOAT(obj->float_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_igetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_INT(obj->int_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_lgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_LONG(obj->long_field(field_offset), 0); | |
| MORE_STACK(1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_sgetfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = STACK_OBJECT(-1); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_INT(obj->short_field(field_offset), -1); | |
| UPDATE_PC_AND_CONTINUE(3); | |
| } | |
| CASE(_fast_aputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->obj_field_put(field_offset, STACK_OBJECT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_bputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->byte_field_put(field_offset, STACK_INT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_zputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->byte_field_put(field_offset, (STACK_INT(-1) & 1)); // only store LSB | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_cputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->char_field_put(field_offset, STACK_INT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_dputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-3); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->double_field_put(field_offset, STACK_DOUBLE(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -3); | |
| } | |
| CASE(_fast_fputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->float_field_put(field_offset, STACK_FLOAT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_iputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->int_field_put(field_offset, STACK_INT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_lputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-3); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->long_field_put(field_offset, STACK_LONG(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -3); | |
| } | |
| CASE(_fast_sputfield): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| oop obj = STACK_OBJECT(-2); | |
| CHECK_NULL(obj); | |
| MAYBE_POST_FIELD_MODIFICATION(obj); | |
| int field_offset = entry->field_offset(); | |
| obj->short_field_put(field_offset, STACK_INT(-1)); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(3, -2); | |
| } | |
| CASE(_fast_aload_0): { | |
| oop obj = LOCALS_OBJECT(0); | |
| VERIFY_OOP(obj); | |
| SET_STACK_OBJECT(obj, 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1); | |
| } | |
| CASE(_fast_aaccess_0): { | |
| u2 index = Bytes::get_native_u2(pc+2); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = LOCALS_OBJECT(0); | |
| CHECK_NULL(obj); | |
| VERIFY_OOP(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| VERIFY_OOP(obj->obj_field(field_offset)); | |
| SET_STACK_OBJECT(obj->obj_field(field_offset), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); | |
| } | |
| CASE(_fast_iaccess_0): { | |
| u2 index = Bytes::get_native_u2(pc+2); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = LOCALS_OBJECT(0); | |
| CHECK_NULL(obj); | |
| VERIFY_OOP(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_INT(obj->int_field(field_offset), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); | |
| } | |
| CASE(_fast_faccess_0): { | |
| u2 index = Bytes::get_native_u2(pc+2); | |
| ResolvedFieldEntry* entry = cp->resolved_field_entry_at(index); | |
| int field_offset = entry->field_offset(); | |
| oop obj = LOCALS_OBJECT(0); | |
| CHECK_NULL(obj); | |
| VERIFY_OOP(obj); | |
| MAYBE_POST_FIELD_ACCESS(obj); | |
| SET_STACK_FLOAT(obj->float_field(field_offset), 0); | |
| UPDATE_PC_AND_TOS_AND_CONTINUE(4, 1); | |
| } | |
| CASE(_fast_invokevfinal): { | |
| u2 index = Bytes::get_native_u2(pc+1); | |
| ResolvedMethodEntry* entry = cp->resolved_method_entry_at(index); | |
| assert(entry->is_resolved(Bytecodes::_invokevirtual), "Should be resolved before rewriting"); | |
| istate->set_msg(call_method); | |
| CHECK_NULL(STACK_OBJECT(-(entry->number_of_parameters()))); | |
| Method* callee = entry->method(); | |
| istate->set_callee(callee); | |
| if (JVMTI_ENABLED && THREAD->is_interp_only_mode()) { | |
| istate->set_callee_entry_point(callee->interpreter_entry()); | |
| } else { | |
| istate->set_callee_entry_point(callee->from_interpreted_entry()); | |
| } | |
| istate->set_bcp_advance(3); | |
| UPDATE_PC_AND_RETURN(0); | |
| } | |
| DEFAULT: | |
| fatal("Unimplemented opcode %d = %s", opcode, | |
| Bytecodes::name((Bytecodes::Code)opcode)); | |
| goto finish; | |
| } /* switch(opc) */ | |
| #ifdef USELABELS | |
| check_for_exception: | |
| #endif | |
| { | |
| if (!THREAD->has_pending_exception()) { | |
| CONTINUE; | |
| } | |
| /* We will be gcsafe soon, so flush our state. */ | |
| DECACHE_PC(); | |
| goto handle_exception; | |
| } | |
| do_continue: ; | |
| } /* while (1) interpreter loop */ | |
| // An exception exists in the thread state see whether this activation can handle it | |
| handle_exception: { | |
| HandleMarkCleaner __hmc(THREAD); | |
| Handle except_oop(THREAD, THREAD->pending_exception()); | |
| // Prevent any subsequent HandleMarkCleaner in the VM | |
| // from freeing the except_oop handle. | |
| HandleMark __hm(THREAD); | |
| THREAD->clear_pending_exception(); | |
| assert(except_oop() != nullptr, "No exception to process"); | |
| intptr_t continuation_bci; | |
| // expression stack is emptied | |
| topOfStack = istate->stack_base() - Interpreter::stackElementWords; | |
| CALL_VM(continuation_bci = (intptr_t)InterpreterRuntime::exception_handler_for_exception(THREAD, except_oop()), | |
| handle_exception); | |
| except_oop = Handle(THREAD, THREAD->vm_result_oop()); | |
| THREAD->set_vm_result_oop(nullptr); | |
| if (continuation_bci >= 0) { | |
| // Place exception on top of stack | |
| SET_STACK_OBJECT(except_oop(), 0); | |
| MORE_STACK(1); | |
| pc = METHOD->code_base() + continuation_bci; | |
| if (log_is_enabled(Info, exceptions)) { | |
| ResourceMark rm(THREAD); | |
| stringStream tempst; | |
| tempst.print("interpreter method <%s>\n" | |
| " at bci %d, continuing at %d for thread " INTPTR_FORMAT, | |
| METHOD->print_value_string(), | |
| (int)(istate->bcp() - METHOD->code_base()), | |
| (int)continuation_bci, p2i(THREAD)); | |
| Exceptions::log_exception(except_oop, tempst.as_string()); | |
| } | |
| // for AbortVMOnException flag | |
| Exceptions::debug_check_abort(except_oop); | |
| goto run; | |
| } | |
| if (log_is_enabled(Info, exceptions)) { | |
| ResourceMark rm; | |
| stringStream tempst; | |
| tempst.print("interpreter method <%s>\n" | |
| " at bci %d, unwinding for thread " INTPTR_FORMAT, | |
| METHOD->print_value_string(), | |
| (int)(istate->bcp() - METHOD->code_base()), | |
| p2i(THREAD)); | |
| Exceptions::log_exception(except_oop, tempst.as_string()); | |
| } | |
| // for AbortVMOnException flag | |
| Exceptions::debug_check_abort(except_oop); | |
| // No handler in this activation, unwind and try again | |
| THREAD->set_pending_exception(except_oop(), nullptr, 0); | |
| goto handle_return; | |
| } // handle_exception: | |
| // Return from an interpreter invocation with the result of the interpretation | |
| // on the top of the Java Stack (or a pending exception) | |
| handle_Pop_Frame: { | |
| // We don't really do anything special here except we must be aware | |
| // that we can get here without ever locking the method (if sync). | |
| // Also we skip the notification of the exit. | |
| istate->set_msg(popping_frame); | |
| // Clear pending so while the pop is in process | |
| // we don't start another one if a call_vm is done. | |
| THREAD->clear_popframe_condition(); | |
| // Let interpreter (only) see the we're in the process of popping a frame | |
| THREAD->set_pop_frame_in_process(); | |
| goto handle_return; | |
| } // handle_Pop_Frame | |
| // ForceEarlyReturn ends a method, and returns to the caller with a return value | |
| // given by the invoker of the early return. | |
| handle_Early_Return: { | |
| istate->set_msg(early_return); | |
| // Clear expression stack. | |
| topOfStack = istate->stack_base() - Interpreter::stackElementWords; | |
| JvmtiThreadState *ts = THREAD->jvmti_thread_state(); | |
| // Push the value to be returned. | |
| switch (istate->method()->result_type()) { | |
| case T_BOOLEAN: | |
| case T_SHORT: | |
| case T_BYTE: | |
| case T_CHAR: | |
| case T_INT: | |
| SET_STACK_INT(ts->earlyret_value().i, 0); | |
| MORE_STACK(1); | |
| break; | |
| case T_LONG: | |
| SET_STACK_LONG(ts->earlyret_value().j, 1); | |
| MORE_STACK(2); | |
| break; | |
| case T_FLOAT: | |
| SET_STACK_FLOAT(ts->earlyret_value().f, 0); | |
| MORE_STACK(1); | |
| break; | |
| case T_DOUBLE: | |
| SET_STACK_DOUBLE(ts->earlyret_value().d, 1); | |
| MORE_STACK(2); | |
| break; | |
| case T_ARRAY: | |
| case T_OBJECT: | |
| SET_STACK_OBJECT(ts->earlyret_oop(), 0); | |
| MORE_STACK(1); | |
| break; | |
| default: | |
| ShouldNotReachHere(); | |
| } | |
| ts->clr_earlyret_value(); | |
| ts->set_earlyret_oop(nullptr); | |
| ts->clr_earlyret_pending(); | |
| // Fall through to handle_return. | |
| } // handle_Early_Return | |
| handle_return: { | |
| // A storestore barrier is required to order initialization of | |
| // final fields with publishing the reference to the object that | |
| // holds the field. Without the barrier the value of final fields | |
| // can be observed to change. | |
| OrderAccess::storestore(); | |
| DECACHE_STATE(); | |
| bool suppress_error = istate->msg() == popping_frame || istate->msg() == early_return; | |
| bool suppress_exit_event = THREAD->has_pending_exception() || istate->msg() == popping_frame; | |
| Handle original_exception(THREAD, THREAD->pending_exception()); | |
| Handle illegal_state_oop(THREAD, nullptr); | |
| // We'd like a HandleMark here to prevent any subsequent HandleMarkCleaner | |
| // in any following VM entries from freeing our live handles, but illegal_state_oop | |
| // isn't really allocated yet and so doesn't become live until later and | |
| // in unpredictable places. Instead we must protect the places where we enter the | |
| // VM. It would be much simpler (and safer) if we could allocate a real handle with | |
| // a null oop in it and then overwrite the oop later as needed. This isn't | |
| // unfortunately isn't possible. | |
| if (THREAD->has_pending_exception()) { | |
| THREAD->clear_pending_exception(); | |
| } | |
| // | |
| // As far as we are concerned we have returned. If we have a pending exception | |
| // that will be returned as this invocation's result. However if we get any | |
| // exception(s) while checking monitor state one of those IllegalMonitorStateExceptions | |
| // will be our final result (i.e. monitor exception trumps a pending exception). | |
| // | |
| // If we never locked the method (or really passed the point where we would have), | |
| // there is no need to unlock it (or look for other monitors), since that | |
| // could not have happened. | |
| if (THREAD->do_not_unlock_if_synchronized()) { | |
| // Never locked, reset the flag now because obviously any caller must | |
| // have passed their point of locking for us to have gotten here. | |
| THREAD->set_do_not_unlock_if_synchronized(false); | |
| } else { | |
| // At this point we consider that we have returned. We now check that the | |
| // locks were properly block structured. If we find that they were not | |
| // used properly we will return with an illegal monitor exception. | |
| // The exception is checked by the caller not the callee since this | |
| // checking is considered to be part of the invocation and therefore | |
| // in the callers scope (JVM spec 8.13). | |
| // | |
| // Another weird thing to watch for is if the method was locked | |
| // recursively and then not exited properly. This means we must | |
| // examine all the entries in reverse time(and stack) order and | |
| // unlock as we find them. If we find the method monitor before | |
| // we are at the initial entry then we should throw an exception. | |
| // It is not clear the template based interpreter does this | |
| // correctly | |
| BasicObjectLock* base = istate->monitor_base(); | |
| BasicObjectLock* end = (BasicObjectLock*) istate->stack_base(); | |
| bool method_unlock_needed = METHOD->is_synchronized(); | |
| // We know the initial monitor was used for the method don't check that | |
| // slot in the loop | |
| if (method_unlock_needed) base--; | |
| // Check all the monitors to see they are unlocked. Install exception if found to be locked. | |
| while (end < base) { | |
| oop lockee = end->obj(); | |
| if (lockee != nullptr) { | |
| BasicLock* lock = end->lock(); | |
| bool success = false; | |
| if (LockingMode == LM_LEGACY) { | |
| markWord header = lock->displaced_header(); | |
| end->set_obj(nullptr); | |
| // If it isn't recursive we either must swap old header or call the runtime | |
| success = true; | |
| if (header.to_pointer() != nullptr) { | |
| markWord old_header = markWord::encode(lock); | |
| if (lockee->cas_set_mark(header, old_header) != old_header) { | |
| // restore object for the slow case | |
| end->set_obj(lockee); | |
| success = false; | |
| } | |
| } | |
| } | |
| if (!success) { | |
| InterpreterRuntime::monitorexit(end); | |
| } | |
| // One error is plenty | |
| if (illegal_state_oop() == nullptr && !suppress_error) { | |
| { | |
| // Prevent any HandleMarkCleaner from freeing our live handles | |
| HandleMark __hm(THREAD); | |
| CALL_VM_NOCHECK(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD)); | |
| } | |
| assert(THREAD->has_pending_exception(), "Lost our exception!"); | |
| illegal_state_oop = Handle(THREAD, THREAD->pending_exception()); | |
| THREAD->clear_pending_exception(); | |
| } | |
| } | |
| end++; | |
| } | |
| // Unlock the method if needed | |
| if (method_unlock_needed) { | |
| if (base->obj() == nullptr) { | |
| // The method is already unlocked this is not good. | |
| if (illegal_state_oop() == nullptr && !suppress_error) { | |
| { | |
| // Prevent any HandleMarkCleaner from freeing our live handles | |
| HandleMark __hm(THREAD); | |
| CALL_VM_NOCHECK(InterpreterRuntime::throw_illegal_monitor_state_exception(THREAD)); | |
| } | |
| assert(THREAD->has_pending_exception(), "Lost our exception!"); | |
| illegal_state_oop = Handle(THREAD, THREAD->pending_exception()); | |
| THREAD->clear_pending_exception(); | |
| } | |
| } else { | |
| // | |
| // The initial monitor is always used for the method | |
| // However if that slot is no longer the oop for the method it was unlocked | |
| // and reused by something that wasn't unlocked! | |
| // | |
| // deopt can come in with rcvr dead because c2 knows | |
| // its value is preserved in the monitor. So we can't use locals[0] at all | |
| // and must use first monitor slot. | |
| // | |
| oop rcvr = base->obj(); | |
| if (rcvr == nullptr) { | |
| if (!suppress_error) { | |
| VM_JAVA_ERROR_NO_JUMP(vmSymbols::java_lang_NullPointerException(), ""); | |
| illegal_state_oop = Handle(THREAD, THREAD->pending_exception()); | |
| THREAD->clear_pending_exception(); | |
| } | |
| } else if (LockingMode != LM_LEGACY) { | |
| InterpreterRuntime::monitorexit(base); | |
| if (THREAD->has_pending_exception()) { | |
| if (!suppress_error) illegal_state_oop = Handle(THREAD, THREAD->pending_exception()); | |
| THREAD->clear_pending_exception(); | |
| } | |
| } else { | |
| BasicLock* lock = base->lock(); | |
| markWord header = lock->displaced_header(); | |
| base->set_obj(nullptr); | |
| // If it isn't recursive we either must swap old header or call the runtime | |
| bool dec_monitor_count = true; | |
| if (header.to_pointer() != nullptr) { | |
| markWord old_header = markWord::encode(lock); | |
| if (rcvr->cas_set_mark(header, old_header) != old_header) { | |
| // restore object for the slow case | |
| base->set_obj(rcvr); | |
| dec_monitor_count = false; | |
| InterpreterRuntime::monitorexit(base); | |
| if (THREAD->has_pending_exception()) { | |
| if (!suppress_error) illegal_state_oop = Handle(THREAD, THREAD->pending_exception()); | |
| THREAD->clear_pending_exception(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Clear the do_not_unlock flag now. | |
| THREAD->set_do_not_unlock_if_synchronized(false); | |
| // | |
| // Notify jvmti/jvmdi | |
| // | |
| // NOTE: we do not notify a method_exit if we have a pending exception, | |
| // including an exception we generate for unlocking checks. In the former | |
| // case, JVMDI has already been notified by our call for the exception handler | |
| // and in both cases as far as JVMDI is concerned we have already returned. | |
| // If we notify it again JVMDI will be all confused about how many frames | |
| // are still on the stack (4340444). | |
| // | |
| // NOTE Further! It turns out the JVMTI spec in fact expects to see | |
| // method_exit events whenever we leave an activation unless it was done | |
| // for popframe. This is nothing like jvmdi. However we are passing the | |
| // tests at the moment (apparently because they are jvmdi based) so rather | |
| // than change this code and possibly fail tests we will leave it alone | |
| // (with this note) in anticipation of changing the vm and the tests | |
| // simultaneously. | |
| suppress_exit_event = suppress_exit_event || illegal_state_oop() != nullptr; | |
| // Whenever JVMTI puts a thread in interp_only_mode, method | |
| // entry/exit events are sent for that thread to track stack depth. | |
| if (JVMTI_ENABLED && !suppress_exit_event && THREAD->is_interp_only_mode()) { | |
| // Prevent any HandleMarkCleaner from freeing our live handles | |
| HandleMark __hm(THREAD); | |
| CALL_VM_NOCHECK(InterpreterRuntime::post_method_exit(THREAD)); | |
| } | |
| // | |
| // See if we are returning any exception | |
| // A pending exception that was pending prior to a possible popping frame | |
| // overrides the popping frame. | |
| // | |
| assert(!suppress_error || (suppress_error && illegal_state_oop() == nullptr), "Error was not suppressed"); | |
| if (illegal_state_oop() != nullptr || original_exception() != nullptr) { | |
| // Inform the frame manager we have no result. | |
| istate->set_msg(throwing_exception); | |
| if (illegal_state_oop() != nullptr) | |
| THREAD->set_pending_exception(illegal_state_oop(), nullptr, 0); | |
| else | |
| THREAD->set_pending_exception(original_exception(), nullptr, 0); | |
| UPDATE_PC_AND_RETURN(0); | |
| } | |
| if (istate->msg() == popping_frame) { | |
| // Make it simpler on the assembly code and set the message for the frame pop. | |
| // returns | |
| if (istate->prev() == nullptr) { | |
| // We must be returning to a deoptimized frame (because popframe only happens between | |
| // two interpreted frames). We need to save the current arguments in C heap so that | |
| // the deoptimized frame when it restarts can copy the arguments to its expression | |
| // stack and re-execute the call. We also have to notify deoptimization that this | |
| // has occurred and to pick the preserved args copy them to the deoptimized frame's | |
| // java expression stack. Yuck. | |
| // | |
| THREAD->popframe_preserve_args(in_ByteSize(METHOD->size_of_parameters() * wordSize), | |
| LOCALS_SLOT(METHOD->size_of_parameters() - 1)); | |
| THREAD->set_popframe_condition_bit(JavaThread::popframe_force_deopt_reexecution_bit); | |
| } | |
| } else { | |
| istate->set_msg(return_from_method); | |
| } | |
| // Normal return | |
| // Advance the pc and return to frame manager | |
| UPDATE_PC_AND_RETURN(1); | |
| } /* handle_return: */ | |
| // This is really a fatal error return | |
| finish: | |
| DECACHE_TOS(); | |
| DECACHE_PC(); | |
| return; | |
| } | |
| // This constructor should only be used to construct the object to signal | |
| // interpreter initialization. All other instances should be created by | |
| // the frame manager. | |
| BytecodeInterpreter::BytecodeInterpreter(messages msg) { | |
| if (msg != initialize) ShouldNotReachHere(); | |
| _msg = msg; | |
| _self_link = this; | |
| _prev_link = nullptr; | |
| } | |
| void BytecodeInterpreter::astore(intptr_t* tos, int stack_offset, | |
| intptr_t* locals, int locals_offset) { | |
| intptr_t value = tos[Interpreter::expr_index_at(-stack_offset)]; | |
| locals[Interpreter::local_index_at(-locals_offset)] = value; | |
| } | |
| void BytecodeInterpreter::copy_stack_slot(intptr_t *tos, int from_offset, | |
| int to_offset) { | |
| tos[Interpreter::expr_index_at(-to_offset)] = | |
| (intptr_t)tos[Interpreter::expr_index_at(-from_offset)]; | |
| } | |
| void BytecodeInterpreter::dup(intptr_t *tos) { | |
| copy_stack_slot(tos, -1, 0); | |
| } | |
| void BytecodeInterpreter::dup2(intptr_t *tos) { | |
| copy_stack_slot(tos, -2, 0); | |
| copy_stack_slot(tos, -1, 1); | |
| } | |
| void BytecodeInterpreter::dup_x1(intptr_t *tos) { | |
| /* insert top word two down */ | |
| copy_stack_slot(tos, -1, 0); | |
| copy_stack_slot(tos, -2, -1); | |
| copy_stack_slot(tos, 0, -2); | |
| } | |
| void BytecodeInterpreter::dup_x2(intptr_t *tos) { | |
| /* insert top word three down */ | |
| copy_stack_slot(tos, -1, 0); | |
| copy_stack_slot(tos, -2, -1); | |
| copy_stack_slot(tos, -3, -2); | |
| copy_stack_slot(tos, 0, -3); | |
| } | |
| void BytecodeInterpreter::dup2_x1(intptr_t *tos) { | |
| /* insert top 2 slots three down */ | |
| copy_stack_slot(tos, -1, 1); | |
| copy_stack_slot(tos, -2, 0); | |
| copy_stack_slot(tos, -3, -1); | |
| copy_stack_slot(tos, 1, -2); | |
| copy_stack_slot(tos, 0, -3); | |
| } | |
| void BytecodeInterpreter::dup2_x2(intptr_t *tos) { | |
| /* insert top 2 slots four down */ | |
| copy_stack_slot(tos, -1, 1); | |
| copy_stack_slot(tos, -2, 0); | |
| copy_stack_slot(tos, -3, -1); | |
| copy_stack_slot(tos, -4, -2); | |
| copy_stack_slot(tos, 1, -3); | |
| copy_stack_slot(tos, 0, -4); | |
| } | |
| void BytecodeInterpreter::swap(intptr_t *tos) { | |
| // swap top two elements | |
| intptr_t val = tos[Interpreter::expr_index_at(1)]; | |
| // Copy -2 entry to -1 | |
| copy_stack_slot(tos, -2, -1); | |
| // Store saved -1 entry into -2 | |
| tos[Interpreter::expr_index_at(2)] = val; | |
| } | |
| // -------------------------------------------------------------------------------- | |
| // Non-product code | |
| #ifndef PRODUCT | |
| const char* BytecodeInterpreter::C_msg(BytecodeInterpreter::messages msg) { | |
| switch (msg) { | |
| case BytecodeInterpreter::no_request: return("no_request"); | |
| case BytecodeInterpreter::initialize: return("initialize"); | |
| // status message to C++ interpreter | |
| case BytecodeInterpreter::method_entry: return("method_entry"); | |
| case BytecodeInterpreter::method_resume: return("method_resume"); | |
| case BytecodeInterpreter::got_monitors: return("got_monitors"); | |
| case BytecodeInterpreter::rethrow_exception: return("rethrow_exception"); | |
| // requests to frame manager from C++ interpreter | |
| case BytecodeInterpreter::call_method: return("call_method"); | |
| case BytecodeInterpreter::return_from_method: return("return_from_method"); | |
| case BytecodeInterpreter::more_monitors: return("more_monitors"); | |
| case BytecodeInterpreter::throwing_exception: return("throwing_exception"); | |
| case BytecodeInterpreter::popping_frame: return("popping_frame"); | |
| case BytecodeInterpreter::do_osr: return("do_osr"); | |
| // deopt | |
| case BytecodeInterpreter::deopt_resume: return("deopt_resume"); | |
| case BytecodeInterpreter::deopt_resume2: return("deopt_resume2"); | |
| default: return("BAD MSG"); | |
| } | |
| } | |
| void | |
| BytecodeInterpreter::print() { | |
| tty->print_cr("thread: " INTPTR_FORMAT, (uintptr_t) this->_thread); | |
| tty->print_cr("bcp: " INTPTR_FORMAT, (uintptr_t) this->_bcp); | |
| tty->print_cr("locals: " INTPTR_FORMAT, (uintptr_t) this->_locals); | |
| tty->print_cr("constants: " INTPTR_FORMAT, (uintptr_t) this->_constants); | |
| { | |
| ResourceMark rm; | |
| char *method_name = _method->name_and_sig_as_C_string(); | |
| tty->print_cr("method: " INTPTR_FORMAT "[ %s ]", (uintptr_t) this->_method, method_name); | |
| } | |
| tty->print_cr("stack: " INTPTR_FORMAT, (uintptr_t) this->_stack); | |
| tty->print_cr("msg: %s", C_msg(this->_msg)); | |
| tty->print_cr("result_to_call._callee: " INTPTR_FORMAT, (uintptr_t) this->_result._to_call._callee); | |
| tty->print_cr("result_to_call._callee_entry_point: " INTPTR_FORMAT, (uintptr_t) this->_result._to_call._callee_entry_point); | |
| tty->print_cr("result_to_call._bcp_advance: %d ", this->_result._to_call._bcp_advance); | |
| tty->print_cr("osr._osr_buf: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_buf); | |
| tty->print_cr("osr._osr_entry: " INTPTR_FORMAT, (uintptr_t) this->_result._osr._osr_entry); | |
| tty->print_cr("prev_link: " INTPTR_FORMAT, (uintptr_t) this->_prev_link); | |
| tty->print_cr("native_mirror: " INTPTR_FORMAT, (uintptr_t) p2i(this->_oop_temp)); | |
| tty->print_cr("stack_base: " INTPTR_FORMAT, (uintptr_t) this->_stack_base); | |
| tty->print_cr("stack_limit: " INTPTR_FORMAT, (uintptr_t) this->_stack_limit); | |
| tty->print_cr("monitor_base: " INTPTR_FORMAT, (uintptr_t) this->_monitor_base); | |
| tty->print_cr("self_link: " INTPTR_FORMAT, (uintptr_t) this->_self_link); | |
| } | |
| extern "C" { | |
| void PI(uintptr_t arg) { | |
| ((BytecodeInterpreter*)arg)->print(); | |
| } | |
| } | |
| #endif // PRODUCT |