| /* | |
| * Copyright (c) 1999, 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 "ci/ciCallProfile.hpp" | |
| #include "ci/ciExceptionHandler.hpp" | |
| #include "ci/ciInstanceKlass.hpp" | |
| #include "ci/ciMethod.hpp" | |
| #include "ci/ciMethodBlocks.hpp" | |
| #include "ci/ciMethodData.hpp" | |
| #include "ci/ciReplay.hpp" | |
| #include "ci/ciStreams.hpp" | |
| #include "ci/ciSymbol.hpp" | |
| #include "ci/ciSymbols.hpp" | |
| #include "ci/ciUtilities.inline.hpp" | |
| #include "compiler/abstractCompiler.hpp" | |
| #include "compiler/compilerDefinitions.inline.hpp" | |
| #include "compiler/compilerOracle.hpp" | |
| #include "compiler/compileTask.hpp" | |
| #include "compiler/methodLiveness.hpp" | |
| #include "interpreter/interpreter.hpp" | |
| #include "interpreter/linkResolver.hpp" | |
| #include "interpreter/oopMapCache.hpp" | |
| #include "logging/log.hpp" | |
| #include "logging/logStream.hpp" | |
| #include "memory/allocation.inline.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "oops/generateOopMap.hpp" | |
| #include "oops/method.inline.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "oops/trainingData.hpp" | |
| #include "prims/methodHandles.hpp" | |
| #include "runtime/deoptimization.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "utilities/bitMap.inline.hpp" | |
| #include "utilities/xmlstream.hpp" | |
| #ifdef COMPILER2 | |
| #include "ci/bcEscapeAnalyzer.hpp" | |
| #include "ci/ciTypeFlow.hpp" | |
| #include "oops/method.hpp" | |
| #endif | |
| // ciMethod | |
| // | |
| // This class represents a Method* in the HotSpot virtual | |
| // machine. | |
| // ------------------------------------------------------------------ | |
| // ciMethod::ciMethod | |
| // | |
| // Loaded method. | |
| ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) : | |
| ciMetadata(h_m()), | |
| _holder(holder) | |
| { | |
| assert(h_m() != nullptr, "no null method"); | |
| assert(_holder->get_instanceKlass() == h_m->method_holder(), ""); | |
| // These fields are always filled in in loaded methods. | |
| _flags = ciFlags(h_m->access_flags()); | |
| // Easy to compute, so fill them in now. | |
| _max_stack = h_m->max_stack(); | |
| _max_locals = h_m->max_locals(); | |
| _code_size = h_m->code_size(); | |
| _handler_count = h_m->exception_table_length(); | |
| _size_of_parameters = h_m->size_of_parameters(); | |
| _uses_monitors = h_m->has_monitor_bytecodes(); | |
| _balanced_monitors = !_uses_monitors || h_m->guaranteed_monitor_matching(); | |
| _is_c1_compilable = !h_m->is_not_c1_compilable(); | |
| _is_c2_compilable = !h_m->is_not_c2_compilable(); | |
| _can_be_parsed = true; | |
| _has_reserved_stack_access = h_m->has_reserved_stack_access(); | |
| _is_overpass = h_m->is_overpass(); | |
| // Lazy fields, filled in on demand. Require allocation. | |
| _code = nullptr; | |
| _exception_handlers = nullptr; | |
| _liveness = nullptr; | |
| _method_blocks = nullptr; | |
| #if defined(COMPILER2) | |
| _flow = nullptr; | |
| _bcea = nullptr; | |
| #endif // COMPILER2 | |
| // Check for blackhole intrinsic and then populate the intrinsic ID. | |
| CompilerOracle::tag_blackhole_if_possible(h_m); | |
| _intrinsic_id = h_m->intrinsic_id(); | |
| ciEnv *env = CURRENT_ENV; | |
| if (env->jvmti_can_hotswap_or_post_breakpoint()) { | |
| // 6328518 check hotswap conditions under the right lock. | |
| bool should_take_Compile_lock = !Compile_lock->owned_by_self(); | |
| ConditionalMutexLocker locker(Compile_lock, should_take_Compile_lock, Mutex::_safepoint_check_flag); | |
| if (Dependencies::check_evol_method(h_m()) != nullptr) { | |
| _is_c1_compilable = false; | |
| _is_c2_compilable = false; | |
| _can_be_parsed = false; | |
| } | |
| } else { | |
| DEBUG_ONLY(CompilerThread::current()->check_possible_safepoint()); | |
| } | |
| if (h_m->method_holder()->is_linked()) { | |
| _can_be_statically_bound = h_m->can_be_statically_bound(); | |
| _can_omit_stack_trace = h_m->can_omit_stack_trace(); | |
| } else { | |
| // Have to use a conservative value in this case. | |
| _can_be_statically_bound = false; | |
| _can_omit_stack_trace = true; | |
| } | |
| // Adjust the definition of this condition to be more useful: | |
| // %%% take these conditions into account in vtable generation | |
| if (!_can_be_statically_bound && h_m->is_private()) | |
| _can_be_statically_bound = true; | |
| if (_can_be_statically_bound && h_m->is_abstract()) | |
| _can_be_statically_bound = false; | |
| // generating _signature may allow GC and therefore move m. | |
| // These fields are always filled in. | |
| _name = env->get_symbol(h_m->name()); | |
| ciSymbol* sig_symbol = env->get_symbol(h_m->signature()); | |
| constantPoolHandle cpool(Thread::current(), h_m->constants()); | |
| _signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol); | |
| _method_data = nullptr; | |
| // Take a snapshot of these values, so they will be commensurate with the MDO. | |
| if (ProfileInterpreter || CompilerConfig::is_c1_profiling()) { | |
| int invcnt = h_m->interpreter_invocation_count(); | |
| // if the value overflowed report it as max int | |
| _interpreter_invocation_count = invcnt < 0 ? max_jint : invcnt ; | |
| _interpreter_throwout_count = h_m->interpreter_throwout_count(); | |
| } else { | |
| _interpreter_invocation_count = 0; | |
| _interpreter_throwout_count = 0; | |
| } | |
| if (_interpreter_invocation_count == 0) | |
| _interpreter_invocation_count = 1; | |
| _inline_instructions_size = -1; | |
| if (ReplayCompiles) { | |
| ciReplay::initialize(this); | |
| } | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::ciMethod | |
| // | |
| // Unloaded method. | |
| ciMethod::ciMethod(ciInstanceKlass* holder, | |
| ciSymbol* name, | |
| ciSymbol* signature, | |
| ciInstanceKlass* accessor) : | |
| ciMetadata((Metadata*)nullptr), | |
| _name( name), | |
| _holder( holder), | |
| _method_data( nullptr), | |
| _method_blocks( nullptr), | |
| _intrinsic_id( vmIntrinsics::_none), | |
| _inline_instructions_size(-1), | |
| _can_be_statically_bound(false), | |
| _can_omit_stack_trace(true), | |
| _liveness( nullptr) | |
| #if defined(COMPILER2) | |
| , | |
| _flow( nullptr), | |
| _bcea( nullptr) | |
| #endif // COMPILER2 | |
| { | |
| // Usually holder and accessor are the same type but in some cases | |
| // the holder has the wrong class loader (e.g. invokedynamic call | |
| // sites) so we pass the accessor. | |
| _signature = new (CURRENT_ENV->arena()) ciSignature(accessor, constantPoolHandle(), signature); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::load_code | |
| // | |
| // Load the bytecodes and exception handler table for this method. | |
| void ciMethod::load_code() { | |
| VM_ENTRY_MARK; | |
| assert(is_loaded(), "only loaded methods have code"); | |
| Method* me = get_Method(); | |
| Arena* arena = CURRENT_THREAD_ENV->arena(); | |
| // Load the bytecodes. | |
| _code = (address)arena->Amalloc(code_size()); | |
| memcpy(_code, me->code_base(), code_size()); | |
| #if INCLUDE_JVMTI | |
| // Revert any breakpoint bytecodes in ci's copy | |
| if (me->number_of_breakpoints() > 0) { | |
| BreakpointInfo* bp = me->method_holder()->breakpoints(); | |
| for (; bp != nullptr; bp = bp->next()) { | |
| if (bp->match(me)) { | |
| code_at_put(bp->bci(), bp->orig_bytecode()); | |
| } | |
| } | |
| } | |
| #endif | |
| // And load the exception table. | |
| ExceptionTable exc_table(me); | |
| // Allocate one extra spot in our list of exceptions. This | |
| // last entry will be used to represent the possibility that | |
| // an exception escapes the method. See ciExceptionHandlerStream | |
| // for details. | |
| _exception_handlers = | |
| (ciExceptionHandler**)arena->Amalloc(sizeof(ciExceptionHandler*) | |
| * (_handler_count + 1)); | |
| if (_handler_count > 0) { | |
| for (int i=0; i<_handler_count; i++) { | |
| _exception_handlers[i] = new (arena) ciExceptionHandler( | |
| holder(), | |
| /* start */ exc_table.start_pc(i), | |
| /* limit */ exc_table.end_pc(i), | |
| /* goto pc */ exc_table.handler_pc(i), | |
| /* cp index */ exc_table.catch_type_index(i)); | |
| } | |
| } | |
| // Put an entry at the end of our list to represent the possibility | |
| // of exceptional exit. | |
| _exception_handlers[_handler_count] = | |
| new (arena) ciExceptionHandler(holder(), 0, code_size(), -1, 0); | |
| if (CIPrintMethodCodes) { | |
| print_codes(); | |
| } | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_linenumber_table | |
| // | |
| // length unknown until decompression | |
| bool ciMethod::has_linenumber_table() const { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| return get_Method()->has_linenumber_table(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::line_number_from_bci | |
| int ciMethod::line_number_from_bci(int bci) const { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| return get_Method()->line_number_from_bci(bci); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::vtable_index | |
| // | |
| // Get the position of this method's entry in the vtable, if any. | |
| int ciMethod::vtable_index() { | |
| check_is_loaded(); | |
| assert(holder()->is_linked(), "must be linked"); | |
| VM_ENTRY_MARK; | |
| return get_Method()->vtable_index(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::uses_balanced_monitors | |
| // | |
| // Does this method use monitors in a strict stack-disciplined manner? | |
| bool ciMethod::has_balanced_monitors() { | |
| check_is_loaded(); | |
| if (_balanced_monitors) return true; | |
| // Analyze the method to see if monitors are used properly. | |
| VM_ENTRY_MARK; | |
| methodHandle method(THREAD, get_Method()); | |
| assert(method->has_monitor_bytecodes(), "should have checked this"); | |
| // Check to see if a previous compilation computed the | |
| // monitor-matching analysis. | |
| if (method->guaranteed_monitor_matching()) { | |
| _balanced_monitors = true; | |
| return true; | |
| } | |
| { | |
| ExceptionMark em(THREAD); | |
| ResourceMark rm(THREAD); | |
| GeneratePairingInfo gpi(method); | |
| if (!gpi.compute_map(THREAD)) { | |
| fatal("Unrecoverable verification or out-of-memory error"); | |
| } | |
| if (!gpi.monitor_safe()) { | |
| return false; | |
| } | |
| method->set_guaranteed_monitor_matching(); | |
| _balanced_monitors = true; | |
| } | |
| return true; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::get_flow_analysis | |
| ciTypeFlow* ciMethod::get_flow_analysis() { | |
| #if defined(COMPILER2) | |
| if (_flow == nullptr) { | |
| ciEnv* env = CURRENT_ENV; | |
| _flow = new (env->arena()) ciTypeFlow(env, this); | |
| _flow->do_flow(); | |
| } | |
| return _flow; | |
| #else // COMPILER2 | |
| ShouldNotReachHere(); | |
| return nullptr; | |
| #endif // COMPILER2 | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::get_osr_flow_analysis | |
| ciTypeFlow* ciMethod::get_osr_flow_analysis(int osr_bci) { | |
| #if defined(COMPILER2) | |
| // OSR entry points are always place after a call bytecode of some sort | |
| assert(osr_bci >= 0, "must supply valid OSR entry point"); | |
| ciEnv* env = CURRENT_ENV; | |
| ciTypeFlow* flow = new (env->arena()) ciTypeFlow(env, this, osr_bci); | |
| flow->do_flow(); | |
| return flow; | |
| #else // COMPILER2 | |
| ShouldNotReachHere(); | |
| return nullptr; | |
| #endif // COMPILER2 | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::raw_liveness_at_bci | |
| // | |
| // Which local variables are live at a specific bci? | |
| MethodLivenessResult ciMethod::raw_liveness_at_bci(int bci) { | |
| check_is_loaded(); | |
| if (_liveness == nullptr) { | |
| // Create the liveness analyzer. | |
| Arena* arena = CURRENT_ENV->arena(); | |
| _liveness = new (arena) MethodLiveness(arena, this); | |
| _liveness->compute_liveness(); | |
| } | |
| return _liveness->get_liveness_at(bci); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::liveness_at_bci | |
| // | |
| // Which local variables are live at a specific bci? When debugging | |
| // will return true for all locals in some cases to improve debug | |
| // information. | |
| MethodLivenessResult ciMethod::liveness_at_bci(int bci) { | |
| if (CURRENT_ENV->should_retain_local_variables() || DeoptimizeALot) { | |
| // Keep all locals live for the user's edification and amusement. | |
| MethodLivenessResult result(_max_locals); | |
| result.set_range(0, _max_locals); | |
| result.set_is_valid(); | |
| return result; | |
| } | |
| return raw_liveness_at_bci(bci); | |
| } | |
| // ciMethod::live_local_oops_at_bci | |
| // | |
| // find all the live oops in the locals array for a particular bci | |
| // Compute what the interpreter believes by using the interpreter | |
| // oopmap generator. This is used as a double check during osr to | |
| // guard against conservative result from MethodLiveness making us | |
| // think a dead oop is live. MethodLiveness is conservative in the | |
| // sense that it may consider locals to be live which cannot be live, | |
| // like in the case where a local could contain an oop or a primitive | |
| // along different paths. In that case the local must be dead when | |
| // those paths merge. Since the interpreter's viewpoint is used when | |
| // gc'ing an interpreter frame we need to use its viewpoint during | |
| // OSR when loading the locals. | |
| ResourceBitMap ciMethod::live_local_oops_at_bci(int bci) { | |
| VM_ENTRY_MARK; | |
| InterpreterOopMap mask; | |
| OopMapCache::compute_one_oop_map(methodHandle(THREAD, get_Method()), bci, &mask); | |
| int mask_size = max_locals(); | |
| ResourceBitMap result(mask_size); | |
| int i; | |
| for (i = 0; i < mask_size ; i++ ) { | |
| if (mask.is_oop(i)) result.set_bit(i); | |
| } | |
| return result; | |
| } | |
| #ifdef COMPILER1 | |
| // ------------------------------------------------------------------ | |
| // ciMethod::bci_block_start | |
| // | |
| // Marks all bcis where a new basic block starts | |
| const BitMap& ciMethod::bci_block_start() { | |
| check_is_loaded(); | |
| if (_liveness == nullptr) { | |
| // Create the liveness analyzer. | |
| Arena* arena = CURRENT_ENV->arena(); | |
| _liveness = new (arena) MethodLiveness(arena, this); | |
| _liveness->compute_liveness(); | |
| } | |
| return _liveness->get_bci_block_start(); | |
| } | |
| #endif // COMPILER1 | |
| // ------------------------------------------------------------------ | |
| // ciMethod::check_overflow | |
| // | |
| // Check whether the profile counter is overflowed and adjust if true. | |
| // For invoke* it will turn negative values into max_jint, | |
| // and for checkcast/aastore/instanceof turn positive values into min_jint. | |
| int ciMethod::check_overflow(int c, Bytecodes::Code code) { | |
| switch (code) { | |
| case Bytecodes::_aastore: // fall-through | |
| case Bytecodes::_checkcast: // fall-through | |
| case Bytecodes::_instanceof: { | |
| if (VM_Version::profile_all_receivers_at_type_check()) { | |
| return (c < 0 ? max_jint : c); // always non-negative | |
| } | |
| return (c > 0 ? min_jint : c); // always non-positive | |
| } | |
| default: { | |
| assert(Bytecodes::is_invoke(code), "%s", Bytecodes::name(code)); | |
| return (c < 0 ? max_jint : c); // always non-negative | |
| } | |
| } | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::call_profile_at_bci | |
| // | |
| // Get the ciCallProfile for the invocation of this method. | |
| // Also reports receiver types for non-call type checks (if TypeProfileCasts). | |
| ciCallProfile ciMethod::call_profile_at_bci(int bci) { | |
| ResourceMark rm; | |
| ciCallProfile result; | |
| if (method_data() != nullptr && method_data()->is_mature()) { | |
| ciProfileData* data = method_data()->bci_to_data(bci); | |
| if (data != nullptr && data->is_CounterData()) { | |
| // Every profiled call site has a counter. | |
| int count = check_overflow(data->as_CounterData()->count(), java_code_at_bci(bci)); | |
| if (!data->is_ReceiverTypeData()) { | |
| result._receiver_count[0] = 0; // that's a definite zero | |
| } else { // ReceiverTypeData is a subclass of CounterData | |
| ciReceiverTypeData* call = (ciReceiverTypeData*)data->as_ReceiverTypeData(); | |
| // In addition, virtual call sites have receiver type information | |
| int receivers_count_total = 0; | |
| int morphism = 0; | |
| // Precompute morphism for the possible fixup | |
| for (uint i = 0; i < call->row_limit(); i++) { | |
| ciKlass* receiver = call->receiver(i); | |
| if (receiver == nullptr) continue; | |
| morphism++; | |
| } | |
| int epsilon = 0; | |
| // For a call, it is assumed that either the type of the receiver(s) | |
| // is recorded or an associated counter is incremented, but not both. With | |
| // tiered compilation, however, both can happen due to the interpreter and | |
| // C1 profiling invocations differently. Address that inconsistency here. | |
| if (morphism == 1 && count > 0) { | |
| epsilon = count; | |
| count = 0; | |
| } | |
| for (uint i = 0; i < call->row_limit(); i++) { | |
| ciKlass* receiver = call->receiver(i); | |
| if (receiver == nullptr) continue; | |
| int rcount = saturated_add(call->receiver_count(i), epsilon); | |
| if (rcount == 0) rcount = 1; // Should be valid value | |
| receivers_count_total = saturated_add(receivers_count_total, rcount); | |
| // Add the receiver to result data. | |
| result.add_receiver(receiver, rcount); | |
| // If we extend profiling to record methods, | |
| // we will set result._method also. | |
| } | |
| // Determine call site's morphism. | |
| // The call site count is 0 with known morphism (only 1 or 2 receivers) | |
| // or < 0 in the case of a type check failure for checkcast, aastore, instanceof. | |
| // The call site count is > 0 in the case of a polymorphic virtual call. | |
| if (morphism > 0 && morphism == result._limit) { | |
| // The morphism <= MorphismLimit. | |
| if ((morphism < ciCallProfile::MorphismLimit) || | |
| (morphism == ciCallProfile::MorphismLimit && count == 0)) { | |
| #ifdef ASSERT | |
| if (count > 0) { | |
| this->print_short_name(tty); | |
| tty->print_cr(" @ bci:%d", bci); | |
| this->print_codes(); | |
| assert(false, "this call site should not be polymorphic"); | |
| } | |
| #endif | |
| result._morphism = morphism; | |
| } | |
| } | |
| // Make the count consistent if this is a call profile. If count is | |
| // zero or less, presume that this is a typecheck profile and | |
| // do nothing. Otherwise, increase count to be the sum of all | |
| // receiver's counts. | |
| if (count >= 0) { | |
| count = saturated_add(count, receivers_count_total); | |
| } | |
| } | |
| result._count = count; | |
| } | |
| } | |
| return result; | |
| } | |
| // ------------------------------------------------------------------ | |
| // Add new receiver and sort data by receiver's profile count. | |
| void ciCallProfile::add_receiver(ciKlass* receiver, int receiver_count) { | |
| // Add new receiver and sort data by receiver's counts when we have space | |
| // for it otherwise replace the less called receiver (less called receiver | |
| // is placed to the last array element which is not used). | |
| // First array's element contains most called receiver. | |
| int i = _limit; | |
| for (; i > 0 && receiver_count > _receiver_count[i-1]; i--) { | |
| _receiver[i] = _receiver[i-1]; | |
| _receiver_count[i] = _receiver_count[i-1]; | |
| } | |
| _receiver[i] = receiver; | |
| _receiver_count[i] = receiver_count; | |
| if (_limit < MorphismLimit) _limit++; | |
| } | |
| void ciMethod::assert_virtual_call_type_ok(int bci) { | |
| assert(java_code_at_bci(bci) == Bytecodes::_invokevirtual || | |
| java_code_at_bci(bci) == Bytecodes::_invokeinterface, "unexpected bytecode %s", Bytecodes::name(java_code_at_bci(bci))); | |
| } | |
| void ciMethod::assert_call_type_ok(int bci) { | |
| assert(java_code_at_bci(bci) == Bytecodes::_invokestatic || | |
| java_code_at_bci(bci) == Bytecodes::_invokespecial || | |
| java_code_at_bci(bci) == Bytecodes::_invokedynamic, "unexpected bytecode %s", Bytecodes::name(java_code_at_bci(bci))); | |
| } | |
| /** | |
| * Check whether profiling provides a type for the argument i to the | |
| * call at bci bci | |
| * | |
| * @param [in]bci bci of the call | |
| * @param [in]i argument number | |
| * @param [out]type profiled type of argument, null if none | |
| * @param [out]ptr_kind whether always null, never null or maybe null | |
| * @return true if profiling exists | |
| * | |
| */ | |
| bool ciMethod::argument_profiled_type(int bci, int i, ciKlass*& type, ProfilePtrKind& ptr_kind) { | |
| if (MethodData::profile_parameters() && method_data() != nullptr && method_data()->is_mature()) { | |
| ciProfileData* data = method_data()->bci_to_data(bci); | |
| if (data != nullptr) { | |
| if (data->is_VirtualCallTypeData()) { | |
| assert_virtual_call_type_ok(bci); | |
| ciVirtualCallTypeData* call = (ciVirtualCallTypeData*)data->as_VirtualCallTypeData(); | |
| if (i >= call->number_of_arguments()) { | |
| return false; | |
| } | |
| type = call->valid_argument_type(i); | |
| ptr_kind = call->argument_ptr_kind(i); | |
| return true; | |
| } else if (data->is_CallTypeData()) { | |
| assert_call_type_ok(bci); | |
| ciCallTypeData* call = (ciCallTypeData*)data->as_CallTypeData(); | |
| if (i >= call->number_of_arguments()) { | |
| return false; | |
| } | |
| type = call->valid_argument_type(i); | |
| ptr_kind = call->argument_ptr_kind(i); | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Check whether profiling provides a type for the return value from | |
| * the call at bci bci | |
| * | |
| * @param [in]bci bci of the call | |
| * @param [out]type profiled type of argument, null if none | |
| * @param [out]ptr_kind whether always null, never null or maybe null | |
| * @return true if profiling exists | |
| * | |
| */ | |
| bool ciMethod::return_profiled_type(int bci, ciKlass*& type, ProfilePtrKind& ptr_kind) { | |
| if (MethodData::profile_return() && method_data() != nullptr && method_data()->is_mature()) { | |
| ciProfileData* data = method_data()->bci_to_data(bci); | |
| if (data != nullptr) { | |
| if (data->is_VirtualCallTypeData()) { | |
| assert_virtual_call_type_ok(bci); | |
| ciVirtualCallTypeData* call = (ciVirtualCallTypeData*)data->as_VirtualCallTypeData(); | |
| if (call->has_return()) { | |
| type = call->valid_return_type(); | |
| ptr_kind = call->return_ptr_kind(); | |
| return true; | |
| } | |
| } else if (data->is_CallTypeData()) { | |
| assert_call_type_ok(bci); | |
| ciCallTypeData* call = (ciCallTypeData*)data->as_CallTypeData(); | |
| if (call->has_return()) { | |
| type = call->valid_return_type(); | |
| ptr_kind = call->return_ptr_kind(); | |
| } | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| /** | |
| * Check whether profiling provides a type for the parameter i | |
| * | |
| * @param [in]i parameter number | |
| * @param [out]type profiled type of parameter, null if none | |
| * @param [out]ptr_kind whether always null, never null or maybe null | |
| * @return true if profiling exists | |
| * | |
| */ | |
| bool ciMethod::parameter_profiled_type(int i, ciKlass*& type, ProfilePtrKind& ptr_kind) { | |
| if (MethodData::profile_parameters() && method_data() != nullptr && method_data()->is_mature()) { | |
| ciParametersTypeData* parameters = method_data()->parameters_type_data(); | |
| if (parameters != nullptr && i < parameters->number_of_parameters()) { | |
| type = parameters->valid_parameter_type(i); | |
| ptr_kind = parameters->parameter_ptr_kind(i); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::find_monomorphic_target | |
| // | |
| // Given a certain calling environment, find the monomorphic target | |
| // for the call. Return null if the call is not monomorphic in | |
| // its calling environment, or if there are only abstract methods. | |
| // The returned method is never abstract. | |
| // Note: If caller uses a non-null result, it must inform dependencies | |
| // via assert_unique_concrete_method or assert_leaf_type. | |
| ciMethod* ciMethod::find_monomorphic_target(ciInstanceKlass* caller, | |
| ciInstanceKlass* callee_holder, | |
| ciInstanceKlass* actual_recv, | |
| bool check_access) { | |
| check_is_loaded(); | |
| if (actual_recv->is_interface()) { | |
| // %%% We cannot trust interface types, yet. See bug 6312651. | |
| return nullptr; | |
| } | |
| ciMethod* root_m = resolve_invoke(caller, actual_recv, check_access, true /* allow_abstract */); | |
| if (root_m == nullptr) { | |
| // Something went wrong looking up the actual receiver method. | |
| return nullptr; | |
| } | |
| // Make certain quick checks even if UseCHA is false. | |
| // Is it private or final? | |
| if (root_m->can_be_statically_bound()) { | |
| assert(!root_m->is_abstract(), "sanity"); | |
| return root_m; | |
| } | |
| if (actual_recv->is_leaf_type() && actual_recv == root_m->holder()) { | |
| // Easy case. There is no other place to put a method, so don't bother | |
| // to go through the VM_ENTRY_MARK and all the rest. | |
| if (root_m->is_abstract()) { | |
| return nullptr; | |
| } | |
| return root_m; | |
| } | |
| // Array methods (clone, hashCode, etc.) are always statically bound. | |
| // If we were to see an array type here, we'd return root_m. | |
| // However, this method processes only ciInstanceKlasses. (See 4962591.) | |
| // The inline_native_clone intrinsic narrows Object to T[] properly, | |
| // so there is no need to do the same job here. | |
| if (!UseCHA) return nullptr; | |
| VM_ENTRY_MARK; | |
| methodHandle target; | |
| { | |
| MutexLocker locker(Compile_lock); | |
| InstanceKlass* context = actual_recv->get_instanceKlass(); | |
| target = methodHandle(THREAD, Dependencies::find_unique_concrete_method(context, | |
| root_m->get_Method(), | |
| callee_holder->get_Klass(), | |
| this->get_Method())); | |
| assert(target() == nullptr || !target()->is_abstract(), "not allowed"); | |
| // %%% Should upgrade this ciMethod API to look for 1 or 2 concrete methods. | |
| } | |
| #ifndef PRODUCT | |
| LogTarget(Debug, dependencies) lt; | |
| if (lt.is_enabled() && target() != nullptr && target() != root_m->get_Method()) { | |
| LogStream ls(<); | |
| ls.print("found a non-root unique target method"); | |
| ls.print_cr(" context = %s", actual_recv->get_Klass()->external_name()); | |
| ls.print(" method = "); | |
| target->print_short_name(&ls); | |
| ls.cr(); | |
| } | |
| #endif //PRODUCT | |
| if (target() == nullptr) { | |
| return nullptr; | |
| } | |
| // Redefinition support. | |
| if (this->is_old() || root_m->is_old() || target->is_old()) { | |
| guarantee(CURRENT_THREAD_ENV->jvmti_state_changed(), "old method not detected"); | |
| return nullptr; | |
| } | |
| if (target() == root_m->get_Method()) { | |
| return root_m; | |
| } | |
| if (!root_m->is_public() && | |
| !root_m->is_protected()) { | |
| // If we are going to reason about inheritance, it's easiest | |
| // if the method in question is public, protected, or private. | |
| // If the answer is not root_m, it is conservatively correct | |
| // to return null, even if the CHA encountered irrelevant | |
| // methods in other packages. | |
| // %%% TO DO: Work out logic for package-private methods | |
| // with the same name but different vtable indexes. | |
| return nullptr; | |
| } | |
| return CURRENT_THREAD_ENV->get_method(target()); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::can_be_statically_bound | |
| // | |
| // Tries to determine whether a method can be statically bound in some context. | |
| bool ciMethod::can_be_statically_bound(ciInstanceKlass* context) const { | |
| return (holder() == context) && can_be_statically_bound(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::can_omit_stack_trace | |
| // | |
| // Tries to determine whether a method can omit stack trace in throw in compiled code. | |
| bool ciMethod::can_omit_stack_trace() const { | |
| if (!StackTraceInThrowable) { | |
| return true; // stack trace is switched off. | |
| } | |
| if (!OmitStackTraceInFastThrow) { | |
| return false; // Have to provide stack trace. | |
| } | |
| return _can_omit_stack_trace; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::resolve_invoke | |
| // | |
| // Given a known receiver klass, find the target for the call. | |
| // Return null if the call has no target or the target is abstract. | |
| ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver, bool check_access, bool allow_abstract) { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| Klass* caller_klass = caller->get_Klass(); | |
| Klass* recv = exact_receiver->get_Klass(); | |
| Klass* resolved = holder()->get_Klass(); | |
| Symbol* h_name = name()->get_symbol(); | |
| Symbol* h_signature = signature()->get_symbol(); | |
| LinkInfo link_info(resolved, h_name, h_signature, caller_klass, | |
| check_access ? LinkInfo::AccessCheck::required : LinkInfo::AccessCheck::skip, | |
| check_access ? LinkInfo::LoaderConstraintCheck::required : LinkInfo::LoaderConstraintCheck::skip); | |
| Method* m = nullptr; | |
| // Only do exact lookup if receiver klass has been linked. Otherwise, | |
| // the vtable has not been setup, and the LinkResolver will fail. | |
| if (recv->is_array_klass() | |
| || | |
| (InstanceKlass::cast(recv)->is_linked() && !exact_receiver->is_interface())) { | |
| if (holder()->is_interface()) { | |
| m = LinkResolver::resolve_interface_call_or_null(recv, link_info); | |
| } else { | |
| m = LinkResolver::resolve_virtual_call_or_null(recv, link_info); | |
| } | |
| } | |
| if (m == nullptr) { | |
| // Return null only if there was a problem with lookup (uninitialized class, etc.) | |
| return nullptr; | |
| } | |
| ciMethod* result = this; | |
| if (m != get_Method()) { | |
| // Redefinition support. | |
| if (this->is_old() || m->is_old()) { | |
| guarantee(CURRENT_THREAD_ENV->jvmti_state_changed(), "old method not detected"); | |
| return nullptr; | |
| } | |
| result = CURRENT_THREAD_ENV->get_method(m); | |
| } | |
| if (result->is_abstract() && !allow_abstract) { | |
| // Don't return abstract methods because they aren't optimizable or interesting. | |
| return nullptr; | |
| } | |
| return result; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::resolve_vtable_index | |
| // | |
| // Given a known receiver klass, find the vtable index for the call. | |
| // Return Method::invalid_vtable_index if the vtable_index is unknown. | |
| int ciMethod::resolve_vtable_index(ciKlass* caller, ciKlass* receiver) { | |
| check_is_loaded(); | |
| int vtable_index = Method::invalid_vtable_index; | |
| // Only do lookup if receiver klass has been linked. Otherwise, | |
| // the vtable has not been setup, and the LinkResolver will fail. | |
| if (!receiver->is_interface() | |
| && (!receiver->is_instance_klass() || | |
| receiver->as_instance_klass()->is_linked())) { | |
| VM_ENTRY_MARK; | |
| Klass* caller_klass = caller->get_Klass(); | |
| Klass* recv = receiver->get_Klass(); | |
| Symbol* h_name = name()->get_symbol(); | |
| Symbol* h_signature = signature()->get_symbol(); | |
| LinkInfo link_info(recv, h_name, h_signature, caller_klass); | |
| vtable_index = LinkResolver::resolve_virtual_vtable_index(recv, link_info); | |
| if (vtable_index == Method::nonvirtual_vtable_index) { | |
| // A statically bound method. Return "no such index". | |
| vtable_index = Method::invalid_vtable_index; | |
| } | |
| } | |
| return vtable_index; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::get_field_at_bci | |
| ciField* ciMethod::get_field_at_bci(int bci, bool &will_link) { | |
| ciBytecodeStream iter(this); | |
| iter.reset_to_bci(bci); | |
| iter.next(); | |
| return iter.get_field(will_link); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::get_method_at_bci | |
| ciMethod* ciMethod::get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature) { | |
| ciBytecodeStream iter(this); | |
| iter.reset_to_bci(bci); | |
| iter.next(); | |
| return iter.get_method(will_link, declared_signature); | |
| } | |
| // ------------------------------------------------------------------ | |
| ciKlass* ciMethod::get_declared_method_holder_at_bci(int bci) { | |
| ciBytecodeStream iter(this); | |
| iter.reset_to_bci(bci); | |
| iter.next(); | |
| return iter.get_declared_method_holder(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // Adjust a CounterData count to be commensurate with | |
| // interpreter_invocation_count. If the MDO exists for | |
| // only 25% of the time the method exists, then the | |
| // counts in the MDO should be scaled by 4X, so that | |
| // they can be usefully and stably compared against the | |
| // invocation counts in methods. | |
| int ciMethod::scale_count(int count, float prof_factor) { | |
| if (count > 0 && method_data() != nullptr) { | |
| int counter_life = method_data()->invocation_count(); | |
| int method_life = interpreter_invocation_count(); | |
| if (method_life < counter_life) { // may happen because of the snapshot timing | |
| method_life = counter_life; | |
| } | |
| if (counter_life > 0) { | |
| double count_d = (double)count * prof_factor * method_life / counter_life + 0.5; | |
| if (count_d >= static_cast<double>(INT_MAX)) { | |
| // Clamp in case of overflowing int range. | |
| count = INT_MAX; | |
| } else { | |
| count = int(count_d); | |
| count = (count > 0) ? count : 1; | |
| } | |
| } else { | |
| count = 1; | |
| } | |
| } | |
| return count; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_special_get_caller_class_method | |
| // | |
| bool ciMethod::is_ignored_by_security_stack_walk() const { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| return get_Method()->is_ignored_by_security_stack_walk(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::needs_clinit_barrier | |
| // | |
| bool ciMethod::needs_clinit_barrier() const { | |
| check_is_loaded(); | |
| return is_static() && !holder()->is_initialized(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // invokedynamic support | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_method_handle_intrinsic | |
| // | |
| // Return true if the method is an instance of the JVM-generated | |
| // signature-polymorphic MethodHandle methods, _invokeBasic, _linkToVirtual, etc. | |
| bool ciMethod::is_method_handle_intrinsic() const { | |
| vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded | |
| return (MethodHandles::is_signature_polymorphic(iid) && | |
| MethodHandles::is_signature_polymorphic_intrinsic(iid)); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_compiled_lambda_form | |
| // | |
| // Return true if the method is a generated MethodHandle adapter. | |
| // These are built by Java code. | |
| bool ciMethod::is_compiled_lambda_form() const { | |
| vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded | |
| return iid == vmIntrinsics::_compiledLambdaForm; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_object_initializer | |
| // | |
| bool ciMethod::is_object_initializer() const { | |
| return name() == ciSymbols::object_initializer_name(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_scoped | |
| // | |
| // Return true for methods annotated with @Scoped | |
| bool ciMethod::is_scoped() const { | |
| return get_Method()->is_scoped(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_member_arg | |
| // | |
| // Return true if the method is a linker intrinsic like _linkToVirtual. | |
| // These are built by the JVM. | |
| bool ciMethod::has_member_arg() const { | |
| vmIntrinsics::ID iid = _intrinsic_id; // do not check if loaded | |
| return (MethodHandles::is_signature_polymorphic(iid) && | |
| MethodHandles::has_member_arg(iid)); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::ensure_method_data | |
| // | |
| // Generate new MethodData* objects at compile time. | |
| // Return true if allocation was successful or no MDO is required. | |
| bool ciMethod::ensure_method_data(const methodHandle& h_m) { | |
| EXCEPTION_CONTEXT; | |
| if (is_native() || is_abstract() || h_m()->is_accessor()) { | |
| return true; | |
| } | |
| if (h_m()->method_data() == nullptr) { | |
| Method::build_profiling_method_data(h_m, THREAD); | |
| if (HAS_PENDING_EXCEPTION) { | |
| CLEAR_PENDING_EXCEPTION; | |
| } | |
| } | |
| if (h_m()->method_data() != nullptr) { | |
| _method_data = CURRENT_ENV->get_method_data(h_m()->method_data()); | |
| return _method_data->load_data(); | |
| } else { | |
| _method_data = CURRENT_ENV->get_empty_methodData(); | |
| return false; | |
| } | |
| } | |
| // public, retroactive version | |
| bool ciMethod::ensure_method_data() { | |
| bool result = true; | |
| if (_method_data == nullptr || _method_data->is_empty()) { | |
| GUARDED_VM_ENTRY({ | |
| methodHandle mh(Thread::current(), get_Method()); | |
| result = ensure_method_data(mh); | |
| }); | |
| } | |
| return result; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::method_data | |
| // | |
| ciMethodData* ciMethod::method_data() { | |
| if (_method_data != nullptr) { | |
| return _method_data; | |
| } | |
| VM_ENTRY_MARK; | |
| ciEnv* env = CURRENT_ENV; | |
| Thread* my_thread = JavaThread::current(); | |
| methodHandle h_m(my_thread, get_Method()); | |
| if (h_m()->method_data() != nullptr) { | |
| _method_data = CURRENT_ENV->get_method_data(h_m()->method_data()); | |
| _method_data->load_data(); | |
| } else { | |
| _method_data = CURRENT_ENV->get_empty_methodData(); | |
| } | |
| return _method_data; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::method_data_or_null | |
| // Returns a pointer to ciMethodData if MDO exists on the VM side, | |
| // null otherwise. | |
| ciMethodData* ciMethod::method_data_or_null() { | |
| ciMethodData *md = method_data(); | |
| if (md->is_empty()) { | |
| return nullptr; | |
| } | |
| return md; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::ensure_method_counters | |
| // | |
| MethodCounters* ciMethod::ensure_method_counters() { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| methodHandle mh(THREAD, get_Method()); | |
| MethodCounters* method_counters = mh->get_method_counters(CHECK_NULL); | |
| return method_counters; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_option | |
| // | |
| bool ciMethod::has_option(CompileCommandEnum option) { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| methodHandle mh(THREAD, get_Method()); | |
| return CompilerOracle::has_option(mh, option); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_option_value | |
| // | |
| bool ciMethod::has_option_value(CompileCommandEnum option, double& value) { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| methodHandle mh(THREAD, get_Method()); | |
| return CompilerOracle::has_option_value(mh, option, value); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::can_be_compiled | |
| // | |
| // Have previous compilations of this method succeeded? | |
| bool ciMethod::can_be_compiled() { | |
| check_is_loaded(); | |
| ciEnv* env = CURRENT_ENV; | |
| if (is_c1_compile(env->comp_level())) { | |
| return _is_c1_compilable; | |
| } | |
| return _is_c2_compilable; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_compiled_code | |
| bool ciMethod::has_compiled_code() { | |
| return inline_instructions_size() > 0; | |
| } | |
| int ciMethod::highest_osr_comp_level() { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| return get_Method()->highest_osr_comp_level(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::code_size_for_inlining | |
| // | |
| // Code size for inlining decisions. This method returns a code | |
| // size of 1 for methods which has the ForceInline annotation. | |
| int ciMethod::code_size_for_inlining() { | |
| check_is_loaded(); | |
| if (get_Method()->force_inline()) { | |
| return 1; | |
| } | |
| return code_size(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::inline_instructions_size | |
| // | |
| // This is a rough metric for "fat" methods, compared before inlining | |
| // with InlineSmallCode. The CodeBlob::code_size accessor includes | |
| // junk like exception handler, stubs, and constant table, which are | |
| // not highly relevant to an inlined method. So we use the more | |
| // specific accessor nmethod::insts_size. | |
| // Also some instructions inside the code are excluded from inline | |
| // heuristic (e.g. post call nop instructions; see InlineSkippedInstructionsCounter) | |
| int ciMethod::inline_instructions_size() { | |
| if (_inline_instructions_size == -1) { | |
| if (TrainingData::have_data()) { | |
| GUARDED_VM_ENTRY( | |
| CompLevel level = static_cast<CompLevel>(CURRENT_ENV->comp_level()); | |
| methodHandle top_level_mh(Thread::current(), CURRENT_ENV->task()->method()); | |
| MethodTrainingData* mtd = MethodTrainingData::find(top_level_mh); | |
| if (mtd != nullptr) { | |
| CompileTrainingData* ctd = mtd->last_toplevel_compile(level); | |
| if (ctd != nullptr) { | |
| methodHandle mh(Thread::current(), get_Method()); | |
| MethodTrainingData* this_mtd = MethodTrainingData::find(mh); | |
| if (this_mtd != nullptr) { | |
| auto r = ctd->ci_records().ciMethod__inline_instructions_size.find(this_mtd); | |
| if (r.is_valid()) { | |
| _inline_instructions_size = r.result(); | |
| } | |
| } | |
| } | |
| } | |
| ); | |
| } | |
| } | |
| if (_inline_instructions_size == -1) { | |
| GUARDED_VM_ENTRY( | |
| nmethod* code = get_Method()->code(); | |
| if (code != nullptr && (code->comp_level() == CompLevel_full_optimization)) { | |
| int isize = code->insts_end() - code->verified_entry_point() - code->skipped_instructions_size(); | |
| _inline_instructions_size = isize > 0 ? isize : 0; | |
| } else { | |
| _inline_instructions_size = 0; | |
| } | |
| if (TrainingData::need_data()) { | |
| CompileTrainingData* ctd = CURRENT_ENV->task()->training_data(); | |
| if (ctd != nullptr) { | |
| methodHandle mh(Thread::current(), get_Method()); | |
| MethodTrainingData* this_mtd = MethodTrainingData::make(mh); | |
| ctd->ci_records().ciMethod__inline_instructions_size.append_if_missing(_inline_instructions_size, this_mtd); | |
| } | |
| } | |
| ); | |
| } | |
| return _inline_instructions_size; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::log_nmethod_identity | |
| void ciMethod::log_nmethod_identity(xmlStream* log) { | |
| GUARDED_VM_ENTRY( | |
| nmethod* code = get_Method()->code(); | |
| if (code != nullptr) { | |
| code->log_identity(log); | |
| } | |
| ) | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_not_reached | |
| bool ciMethod::is_not_reached(int bci) { | |
| check_is_loaded(); | |
| VM_ENTRY_MARK; | |
| return Interpreter::is_not_reached( | |
| methodHandle(THREAD, get_Method()), bci); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::was_never_executed | |
| bool ciMethod::was_executed_more_than(int times) { | |
| VM_ENTRY_MARK; | |
| return get_Method()->was_executed_more_than(times); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::has_unloaded_classes_in_signature | |
| bool ciMethod::has_unloaded_classes_in_signature() { | |
| // ciSignature is resolved against some accessing class and | |
| // signature classes aren't required to be local. As a benefit, | |
| // it makes signature classes visible through loader constraints. | |
| // So, encountering an unloaded class signals it is absent both in | |
| // the callee (local) and caller contexts. | |
| return signature()->has_unloaded_classes(); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_klass_loaded | |
| bool ciMethod::is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const { | |
| VM_ENTRY_MARK; | |
| return get_Method()->is_klass_loaded(refinfo_index, bc, must_be_resolved); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::check_call | |
| bool ciMethod::check_call(int refinfo_index, bool is_static) const { | |
| // This method is used only in C2 from InlineTree::ok_to_inline, | |
| // and is only used under -Xcomp. | |
| // It appears to fail when applied to an invokeinterface call site. | |
| // FIXME: Remove this method and resolve_method_statically; refactor to use the other LinkResolver entry points. | |
| VM_ENTRY_MARK; | |
| { | |
| ExceptionMark em(THREAD); | |
| HandleMark hm(THREAD); | |
| constantPoolHandle pool (THREAD, get_Method()->constants()); | |
| Bytecodes::Code code = (is_static ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual); | |
| Method* spec_method = LinkResolver::resolve_method_statically(code, pool, refinfo_index, THREAD); | |
| if (HAS_PENDING_EXCEPTION) { | |
| CLEAR_PENDING_EXCEPTION; | |
| return false; | |
| } else { | |
| return (spec_method->is_static() == is_static); | |
| } | |
| } | |
| return false; | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::print_codes | |
| // | |
| // Print the bytecodes for this method. | |
| void ciMethod::print_codes_on(outputStream* st) { | |
| check_is_loaded(); | |
| GUARDED_VM_ENTRY(get_Method()->print_codes_on(st);) | |
| } | |
| #define FETCH_FLAG_FROM_VM(flag_accessor) { \ | |
| check_is_loaded(); \ | |
| VM_ENTRY_MARK; \ | |
| return get_Method()->flag_accessor(); \ | |
| } | |
| bool ciMethod::has_loops () const { FETCH_FLAG_FROM_VM(has_loops); } | |
| bool ciMethod::has_jsrs () const { FETCH_FLAG_FROM_VM(has_jsrs); } | |
| bool ciMethod::is_getter () const { FETCH_FLAG_FROM_VM(is_getter); } | |
| bool ciMethod::is_setter () const { FETCH_FLAG_FROM_VM(is_setter); } | |
| bool ciMethod::is_accessor () const { FETCH_FLAG_FROM_VM(is_accessor); } | |
| bool ciMethod::is_empty () const { FETCH_FLAG_FROM_VM(is_empty_method); } | |
| bool ciMethod::is_boxing_method() const { | |
| if (intrinsic_id() != vmIntrinsics::_none && holder()->is_box_klass()) { | |
| switch (intrinsic_id()) { | |
| case vmIntrinsics::_Boolean_valueOf: | |
| case vmIntrinsics::_Byte_valueOf: | |
| case vmIntrinsics::_Character_valueOf: | |
| case vmIntrinsics::_Short_valueOf: | |
| case vmIntrinsics::_Integer_valueOf: | |
| case vmIntrinsics::_Long_valueOf: | |
| case vmIntrinsics::_Float_valueOf: | |
| case vmIntrinsics::_Double_valueOf: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| bool ciMethod::is_unboxing_method() const { | |
| if (intrinsic_id() != vmIntrinsics::_none && holder()->is_box_klass()) { | |
| switch (intrinsic_id()) { | |
| case vmIntrinsics::_booleanValue: | |
| case vmIntrinsics::_byteValue: | |
| case vmIntrinsics::_charValue: | |
| case vmIntrinsics::_shortValue: | |
| case vmIntrinsics::_intValue: | |
| case vmIntrinsics::_longValue: | |
| case vmIntrinsics::_floatValue: | |
| case vmIntrinsics::_doubleValue: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| bool ciMethod::is_vector_method() const { | |
| return (holder() == ciEnv::current()->vector_VectorSupport_klass()) && | |
| (intrinsic_id() != vmIntrinsics::_none); | |
| } | |
| BCEscapeAnalyzer *ciMethod::get_bcea() { | |
| #ifdef COMPILER2 | |
| if (_bcea == nullptr) { | |
| _bcea = new (CURRENT_ENV->arena()) BCEscapeAnalyzer(this, nullptr); | |
| } | |
| return _bcea; | |
| #else // COMPILER2 | |
| ShouldNotReachHere(); | |
| return nullptr; | |
| #endif // COMPILER2 | |
| } | |
| ciMethodBlocks *ciMethod::get_method_blocks() { | |
| if (_method_blocks == nullptr) { | |
| Arena *arena = CURRENT_ENV->arena(); | |
| _method_blocks = new (arena) ciMethodBlocks(arena, this); | |
| } | |
| return _method_blocks; | |
| } | |
| #undef FETCH_FLAG_FROM_VM | |
| void ciMethod::dump_name_as_ascii(outputStream* st, Method* method) { | |
| st->print("%s %s %s", | |
| CURRENT_ENV->replay_name(method->method_holder()), | |
| method->name()->as_quoted_ascii(), | |
| method->signature()->as_quoted_ascii()); | |
| } | |
| void ciMethod::dump_name_as_ascii(outputStream* st) { | |
| Method* method = get_Method(); | |
| dump_name_as_ascii(st, method); | |
| } | |
| void ciMethod::dump_replay_data(outputStream* st) { | |
| ResourceMark rm; | |
| Method* method = get_Method(); | |
| if (MethodHandles::is_signature_polymorphic_method(method)) { | |
| // ignore for now | |
| return; | |
| } | |
| MethodCounters* mcs = method->method_counters(); | |
| st->print("ciMethod "); | |
| dump_name_as_ascii(st); | |
| st->print_cr(" %d %d %d %d %d", | |
| mcs == nullptr ? 0 : mcs->invocation_counter()->raw_counter(), | |
| mcs == nullptr ? 0 : mcs->backedge_counter()->raw_counter(), | |
| interpreter_invocation_count(), | |
| interpreter_throwout_count(), | |
| _inline_instructions_size); | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::print_codes | |
| // | |
| // Print a range of the bytecodes for this method. | |
| void ciMethod::print_codes_on(int from, int to, outputStream* st) { | |
| check_is_loaded(); | |
| GUARDED_VM_ENTRY(get_Method()->print_codes_on(from, to, st);) | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::print_name | |
| // | |
| // Print the name of this method, including signature and some flags. | |
| void ciMethod::print_name(outputStream* st) { | |
| check_is_loaded(); | |
| GUARDED_VM_ENTRY(get_Method()->print_name(st);) | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::print_short_name | |
| // | |
| // Print the name of this method, without signature. | |
| void ciMethod::print_short_name(outputStream* st) { | |
| if (is_loaded()) { | |
| GUARDED_VM_ENTRY(get_Method()->print_short_name(st);); | |
| } else { | |
| // Fall back if method is not loaded. | |
| holder()->print_name_on(st); | |
| st->print("::"); | |
| name()->print_symbol_on(st); | |
| if (WizardMode) | |
| signature()->as_symbol()->print_symbol_on(st); | |
| } | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::print_impl | |
| // | |
| // Implementation of the print method. | |
| void ciMethod::print_impl(outputStream* st) { | |
| ciMetadata::print_impl(st); | |
| st->print(" name="); | |
| name()->print_symbol_on(st); | |
| st->print(" holder="); | |
| holder()->print_name_on(st); | |
| st->print(" signature="); | |
| signature()->as_symbol()->print_symbol_on(st); | |
| if (is_loaded()) { | |
| st->print(" loaded=true"); | |
| st->print(" arg_size=%d", arg_size()); | |
| st->print(" flags="); | |
| flags().print_member_flags(st); | |
| } else { | |
| st->print(" loaded=false"); | |
| } | |
| } | |
| // ------------------------------------------------------------------ | |
| static BasicType erase_to_word_type(BasicType bt) { | |
| if (is_subword_type(bt)) return T_INT; | |
| if (is_reference_type(bt)) return T_OBJECT; | |
| return bt; | |
| } | |
| static bool basic_types_match(ciType* t1, ciType* t2) { | |
| if (t1 == t2) return true; | |
| return erase_to_word_type(t1->basic_type()) == erase_to_word_type(t2->basic_type()); | |
| } | |
| bool ciMethod::is_consistent_info(ciMethod* declared_method, ciMethod* resolved_method) { | |
| bool invoke_through_mh_intrinsic = declared_method->is_method_handle_intrinsic() && | |
| !resolved_method->is_method_handle_intrinsic(); | |
| if (!invoke_through_mh_intrinsic) { | |
| // Method name & descriptor should stay the same. | |
| // Signatures may reference unloaded types and thus they may be not strictly equal. | |
| ciSymbol* declared_signature = declared_method->signature()->as_symbol(); | |
| ciSymbol* resolved_signature = resolved_method->signature()->as_symbol(); | |
| return (declared_method->name()->equals(resolved_method->name())) && | |
| (declared_signature->equals(resolved_signature)); | |
| } | |
| ciMethod* linker = declared_method; | |
| ciMethod* target = resolved_method; | |
| // Linkers have appendix argument which is not passed to callee. | |
| int has_appendix = MethodHandles::has_member_arg(linker->intrinsic_id()) ? 1 : 0; | |
| if (linker->arg_size() != (target->arg_size() + has_appendix)) { | |
| return false; // argument slot count mismatch | |
| } | |
| ciSignature* linker_sig = linker->signature(); | |
| ciSignature* target_sig = target->signature(); | |
| if (linker_sig->count() + (linker->is_static() ? 0 : 1) != | |
| target_sig->count() + (target->is_static() ? 0 : 1) + has_appendix) { | |
| return false; // argument count mismatch | |
| } | |
| int sbase = 0, rbase = 0; | |
| switch (linker->intrinsic_id()) { | |
| case vmIntrinsics::_linkToVirtual: | |
| case vmIntrinsics::_linkToInterface: | |
| case vmIntrinsics::_linkToSpecial: { | |
| if (target->is_static()) { | |
| return false; | |
| } | |
| if (linker_sig->type_at(0)->is_primitive_type()) { | |
| return false; // receiver should be an oop | |
| } | |
| sbase = 1; // skip receiver | |
| break; | |
| } | |
| case vmIntrinsics::_linkToStatic: { | |
| if (!target->is_static()) { | |
| return false; | |
| } | |
| break; | |
| } | |
| case vmIntrinsics::_invokeBasic: { | |
| if (target->is_static()) { | |
| if (target_sig->type_at(0)->is_primitive_type()) { | |
| return false; // receiver should be an oop | |
| } | |
| rbase = 1; // skip receiver | |
| } | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| assert(target_sig->count() - rbase == linker_sig->count() - sbase - has_appendix, "argument count mismatch"); | |
| int arg_count = target_sig->count() - rbase; | |
| for (int i = 0; i < arg_count; i++) { | |
| if (!basic_types_match(linker_sig->type_at(sbase + i), target_sig->type_at(rbase + i))) { | |
| return false; | |
| } | |
| } | |
| // Only check the return type if the symbolic info has non-void return type. | |
| // I.e. the return value of the resolved method can be dropped. | |
| if (!linker->return_type()->is_void() && | |
| !basic_types_match(linker->return_type(), target->return_type())) { | |
| return false; | |
| } | |
| return true; // no mismatch found | |
| } | |
| // ------------------------------------------------------------------ | |
| // ciMethod::is_old | |
| // | |
| // Return true for redefined methods | |
| bool ciMethod::is_old() const { | |
| ASSERT_IN_VM; | |
| return get_Method()->is_old(); | |
| } |