--- old/src/share/vm/ci/ciMethod.hpp 2015-10-30 00:19:36.000000000 +0300 +++ new/src/share/vm/ci/ciMethod.hpp 2015-10-30 00:19:35.000000000 +0300 @@ -250,6 +250,12 @@ ciField* get_field_at_bci( int bci, bool &will_link); ciMethod* get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature); + ciMethod* get_method_at_bci(int bci) { + bool ignored_will_link; + ciSignature* ignored_declared_signature; + return get_method_at_bci(bci, ignored_will_link, &ignored_declared_signature); + } + // Given a certain calling environment, find the monomorphic target // for the call. Return NULL if the call is not monomorphic in // its calling environment. --- old/src/share/vm/classfile/vmSymbols.hpp 2015-10-30 00:19:36.000000000 +0300 +++ new/src/share/vm/classfile/vmSymbols.hpp 2015-10-30 00:19:36.000000000 +0300 @@ -966,6 +966,11 @@ do_name( isCompileConstant_name, "isCompileConstant") \ do_alias( isCompileConstant_signature, object_boolean_signature) \ \ + do_class(sun_hotspot_WhiteBox, "sun/hotspot/WhiteBox") \ + do_intrinsic(_deoptimize, sun_hotspot_WhiteBox, deoptimize_name, deoptimize_signature, F_R) \ + do_name( deoptimize_name, "deoptimize") \ + do_alias( deoptimize_signature, void_method_signature) \ + \ /* unsafe memory references (there are a lot of them...) */ \ do_signature(getObject_signature, "(Ljava/lang/Object;J)Ljava/lang/Object;") \ do_signature(putObject_signature, "(Ljava/lang/Object;JLjava/lang/Object;)V") \ --- old/src/share/vm/code/compiledIC.cpp 2015-10-30 00:19:37.000000000 +0300 +++ new/src/share/vm/code/compiledIC.cpp 2015-10-30 00:19:37.000000000 +0300 @@ -434,7 +434,7 @@ InlineCacheBuffer::create_transition_stub(this, info.cached_metadata(), info.entry()); } else { if (is_optimized()) { - set_ic_destination(info.entry()); + set_ic_destination(info.entry()); } else { set_ic_destination_and_value(info.entry(), info.cached_metadata()); } --- old/src/share/vm/opto/callGenerator.cpp 2015-10-30 00:19:38.000000000 +0300 +++ new/src/share/vm/opto/callGenerator.cpp 2015-10-30 00:19:38.000000000 +0300 @@ -137,6 +137,7 @@ } CallStaticJavaNode *call = new CallStaticJavaNode(kit.C, tf(), target, method(), kit.bci()); + call->set_override_symbolic_info(override_symbolic_info()); _call_node = call; // Save the call node in case we need it later if (!is_static) { // Make an explicit receiver null_check as part of this call. @@ -192,7 +193,10 @@ // the call instruction will have a seemingly deficient out-count. // (The bailout says something misleading about an "infinite loop".) if (kit.gvn().type(receiver)->higher_equal(TypePtr::NULL_PTR)) { - kit.inc_sp(method()->arg_size()); // restore arguments + assert(Bytecodes::is_invoke(kit.java_bc()), "%d: %s", kit.java_bc(), Bytecodes::name(kit.java_bc())); + ciMethod* declared_method = kit.method()->get_method_at_bci(kit.bci()); + int arg_size = declared_method->signature()->arg_size_for_bc(kit.java_bc()); + kit.inc_sp(arg_size); // restore arguments kit.uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none, NULL, "null receiver"); @@ -226,6 +230,7 @@ address target = SharedRuntime::get_resolve_virtual_call_stub(); // Normal inline cache used for call CallDynamicJavaNode *call = new CallDynamicJavaNode(tf(), target, method(), _vtable_index, kit.bci()); + call->set_override_symbolic_info(override_symbolic_info()); kit.set_arguments_for_java_call(call); kit.set_edges_for_java_call(call); Node* ret = kit.set_results_for_java_call(call); @@ -463,8 +468,8 @@ _attempt++; } - if (cg != NULL) { - assert(!cg->is_late_inline() && cg->is_inline(), "we're doing late inlining"); + if (cg != NULL && cg->is_inline()) { + assert(!cg->is_late_inline(), "we're doing late inlining"); _inline_cg = cg; Compile::current()->dec_number_of_mh_late_inlines(); return true; @@ -807,8 +812,15 @@ const int vtable_index = Method::invalid_vtable_index; CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS, NULL, true, true); assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here"); - if (cg != NULL && cg->is_inline()) + if (cg != NULL) { + if (!cg->is_inline()) { + // To be able to issue a static call (and skip a call to MH.invokeBasic adapter), + // additional information about the method being invoked should be attached + // to the call site to make resolution logic work (see SharedRuntime::resolve_static_call_C). + cg->set_override_symbolic_info(true); + } return cg; + } } else { const char* msg = "receiver not constant"; if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg); @@ -829,7 +841,7 @@ const TypeOopPtr* oop_ptr = member_name->bottom_type()->is_oopptr(); ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget(); - // In lamda forms we erase signature types to avoid resolving issues + // In lambda forms we erase signature types to avoid resolving issues // involving class loaders. When we optimize a method handle invoke // to a direct call we must cast the receiver and arguments to its // actual types. @@ -882,10 +894,18 @@ // provide us with a type speculative_receiver_type = (receiver_type != NULL) ? receiver_type->speculative_type() : NULL; } - CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, true, PROB_ALWAYS, speculative_receiver_type, true, true); + CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, /*allow_inline=*/true, PROB_ALWAYS, speculative_receiver_type, true, true); assert(cg == NULL || !cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here"); - if (cg != NULL && cg->is_inline()) + if (cg != NULL) { + if (!cg->is_inline()) { + // To be able to issue a direct call (static, optimized virtual, or virtual) + // and skip a call to MH.linkTo* adapter, additional information about the method + // being invoked should be attached to the call site to make resolution logic work + // (see SharedRuntime::resolve_{static,virtual,opt_virtual}_call_C). + cg->set_override_symbolic_info(true); + } return cg; + } } else { const char* msg = "member_name not constant"; if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg); --- old/src/share/vm/opto/callGenerator.hpp 2015-10-30 00:19:38.000000000 +0300 +++ new/src/share/vm/opto/callGenerator.hpp 2015-10-30 00:19:38.000000000 +0300 @@ -43,13 +43,17 @@ private: ciMethod* _method; // The method being called. + bool _override_symbolic_info; protected: - CallGenerator(ciMethod* method) : _method(method) {} + CallGenerator(ciMethod* method) : _method(method), _override_symbolic_info(false) {} public: // Accessors - ciMethod* method() const { return _method; } + ciMethod* method() const { return _method; } + void set_override_symbolic_info(bool x) { _override_symbolic_info = x; } + bool override_symbolic_info() const { return _override_symbolic_info; } + // is_inline: At least some code implementing the method is copied here. virtual bool is_inline() const { return false; } --- old/src/share/vm/opto/callnode.cpp 2015-10-30 00:19:39.000000000 +0300 +++ new/src/share/vm/opto/callnode.cpp 2015-10-30 00:19:39.000000000 +0300 @@ -959,7 +959,8 @@ uint CallJavaNode::size_of() const { return sizeof(*this); } uint CallJavaNode::cmp( const Node &n ) const { CallJavaNode &call = (CallJavaNode&)n; - return CallNode::cmp(call) && _method == call._method; + return CallNode::cmp(call) && _method == call._method && + _override_symbolic_info == call._override_symbolic_info; } #ifndef PRODUCT void CallJavaNode::dump_spec(outputStream *st) const { --- old/src/share/vm/opto/callnode.hpp 2015-10-30 00:19:39.000000000 +0300 +++ new/src/share/vm/opto/callnode.hpp 2015-10-30 00:19:39.000000000 +0300 @@ -657,25 +657,29 @@ bool _optimized_virtual; bool _method_handle_invoke; - ciMethod* _method; // Method being direct called + bool _override_symbolic_info; // Override symbolic call site info from bytecode + ciMethod* _method; // Method being direct called public: const int _bci; // Byte Code Index of call byte code CallJavaNode(const TypeFunc* tf , address addr, ciMethod* method, int bci) : CallNode(tf, addr, TypePtr::BOTTOM), _method(method), _bci(bci), _optimized_virtual(false), - _method_handle_invoke(false) + _method_handle_invoke(false), + _override_symbolic_info(false) { init_class_id(Class_CallJava); } virtual int Opcode() const; - ciMethod* method() const { return _method; } - void set_method(ciMethod *m) { _method = m; } - void set_optimized_virtual(bool f) { _optimized_virtual = f; } - bool is_optimized_virtual() const { return _optimized_virtual; } - void set_method_handle_invoke(bool f) { _method_handle_invoke = f; } - bool is_method_handle_invoke() const { return _method_handle_invoke; } + ciMethod* method() const { return _method; } + void set_method(ciMethod *m) { _method = m; } + void set_optimized_virtual(bool f) { _optimized_virtual = f; } + bool is_optimized_virtual() const { return _optimized_virtual; } + void set_method_handle_invoke(bool f) { _method_handle_invoke = f; } + bool is_method_handle_invoke() const { return _method_handle_invoke; } + void set_override_symbolic_info(bool f) { _override_symbolic_info = f; } + bool override_symbolic_info() const { return _override_symbolic_info; } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; --- old/src/share/vm/opto/library_call.cpp 2015-10-30 00:19:40.000000000 +0300 +++ new/src/share/vm/opto/library_call.cpp 2015-10-30 00:19:40.000000000 +0300 @@ -305,6 +305,8 @@ bool inline_profileBoolean(); bool inline_isCompileConstant(); + + bool inline_deoptimize(); }; //---------------------------make_vm_intrinsic---------------------------- @@ -711,6 +713,9 @@ case vmIntrinsics::_isCompileConstant: return inline_isCompileConstant(); + case vmIntrinsics::_deoptimize: + return inline_deoptimize(); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -6329,3 +6334,12 @@ set_result(n->is_Con() ? intcon(1) : intcon(0)); return true; } + +bool LibraryCallKit::inline_deoptimize() { + assert(WhiteBoxAPI, ""); + PreserveReexecuteState preexecs(this); + jvms()->set_should_reexecute(false); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_none); + return true; +} --- old/src/share/vm/opto/machnode.cpp 2015-10-30 00:19:41.000000000 +0300 +++ new/src/share/vm/opto/machnode.cpp 2015-10-30 00:19:41.000000000 +0300 @@ -694,7 +694,8 @@ uint MachCallJavaNode::size_of() const { return sizeof(*this); } uint MachCallJavaNode::cmp( const Node &n ) const { MachCallJavaNode &call = (MachCallJavaNode&)n; - return MachCallNode::cmp(call) && _method->equals(call._method); + return MachCallNode::cmp(call) && _method->equals(call._method) && + _override_symbolic_info == call._override_symbolic_info; } #ifndef PRODUCT void MachCallJavaNode::dump_spec(outputStream *st) const { --- old/src/share/vm/opto/machnode.hpp 2015-10-30 00:19:41.000000000 +0300 +++ new/src/share/vm/opto/machnode.hpp 2015-10-30 00:19:41.000000000 +0300 @@ -881,16 +881,28 @@ virtual uint cmp( const Node &n ) const; virtual uint size_of() const; // Size is bigger public: - ciMethod* _method; // Method being direct called - int _bci; // Byte Code index of call byte code - bool _optimized_virtual; // Tells if node is a static call or an optimized virtual - bool _method_handle_invoke; // Tells if the call has to preserve SP - MachCallJavaNode() : MachCallNode() { + ciMethod* _method; // Method being direct called + bool _override_symbolic_info; // Override symbolic call site info from bytecode + int _bci; // Byte Code index of call byte code + bool _optimized_virtual; // Tells if node is a static call or an optimized virtual + bool _method_handle_invoke; // Tells if the call has to preserve SP + MachCallJavaNode() : MachCallNode(), _override_symbolic_info(false) { init_class_id(Class_MachCallJava); } virtual const RegMask &in_RegMask(uint) const; + int resolved_method_index(CodeBuffer &cbuf) const { + if (_override_symbolic_info) { + // Attach corresponding Method* to the call site, so VM can use it during resolution + // instead of querying symbolic info from bytecode. + assert(_method != NULL, "method should be set"); + assert(_method->constant_encoding()->is_method(), "should point to a Method"); + return cbuf.oop_recorder()->find_index(_method->constant_encoding()); + } + return 0; // Use symbolic info from bytecode (resolved_method == NULL). + } + #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif --- old/src/share/vm/opto/matcher.cpp 2015-10-30 00:19:41.000000000 +0300 +++ new/src/share/vm/opto/matcher.cpp 2015-10-30 00:19:41.000000000 +0300 @@ -1197,6 +1197,7 @@ mcall_java->_optimized_virtual = call_java->is_optimized_virtual(); is_method_handle_invoke = call_java->is_method_handle_invoke(); mcall_java->_method_handle_invoke = is_method_handle_invoke; + mcall_java->_override_symbolic_info = call_java->override_symbolic_info(); if (is_method_handle_invoke) { C->set_has_method_handle_invokes(true); } --- old/src/share/vm/prims/whitebox.cpp 2015-10-30 00:19:42.000000000 +0300 +++ new/src/share/vm/prims/whitebox.cpp 2015-10-30 00:19:42.000000000 +0300 @@ -1280,6 +1280,11 @@ return (long) ikh->constants(); WB_END +WB_ENTRY(void, WB_ClearInlineCaches(JNIEnv* env, jobject wb)) + VM_ClearICs clear_ics; + VMThread::execute(&clear_ics); +WB_END + template static bool GetMethodOption(JavaThread* thread, JNIEnv* env, jobject method, jstring name, T* value) { assert(value != NULL, "sanity"); @@ -1604,6 +1609,7 @@ (void*)&WB_GetMethodStringOption}, {CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared }, {CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored }, + {CC"clearInlineCaches", CC"()V", (void*)&WB_ClearInlineCaches }, }; #undef CC --- old/src/share/vm/runtime/sharedRuntime.cpp 2015-10-30 00:19:42.000000000 +0300 +++ new/src/share/vm/runtime/sharedRuntime.cpp 2015-10-30 00:19:42.000000000 +0300 @@ -1076,6 +1076,85 @@ return find_callee_info_helper(thread, vfst, bc, callinfo, THREAD); } +methodHandle SharedRuntime::extract_attached_call_info(JavaThread* thread) { + ResourceMark rm(thread); + RegisterMap cbl_map(thread, false); + frame caller_frame = thread->last_frame().sender(&cbl_map); + CodeBlob* caller_cb = caller_frame.cb(); + guarantee(caller_cb != NULL && caller_cb->is_nmethod(), "must be called from nmethod"); + nmethod* caller_nm = caller_cb->as_nmethod_or_null(); + + //assert(caller_nm is locked)); + + NativeCall* call = nativeCall_before(caller_frame.pc()); + RelocIterator iter(caller_nm, call->instruction_address()); + while (iter.next()) { + if (iter.addr() == call->instruction_address()) { + switch(iter.type()) { + case relocInfo::static_call_type: return iter.static_call_reloc()->method_value(); + case relocInfo::opt_virtual_call_type: return iter.opt_virtual_call_reloc()->method_value(); + case relocInfo::virtual_call_type: return iter.virtual_call_reloc()->method_value(); + default: + fatal("unexpected reloc type: %d", iter.type()); + } + } + } + return NULL; +} + +static Bytecodes::Code compute_bc(vmIntrinsics::ID id) { + switch(id) { + case vmIntrinsics::_linkToVirtual: return Bytecodes::_invokevirtual; + case vmIntrinsics::_linkToInterface: return Bytecodes::_invokeinterface; + case vmIntrinsics::_linkToStatic: return Bytecodes::_invokestatic; + case vmIntrinsics::_linkToSpecial: return Bytecodes::_invokespecial; + case vmIntrinsics::_invokeBasic: return Bytecodes::_invokestatic; + default: + fatal("unexpected id: (%d) %s", (uint)id, vmIntrinsics::name_at(id)); + return Bytecodes::_illegal; + } +} + +static Handle extract_receiver(JavaThread* thread, TRAPS) { + // This register map must be update since we need to find the receiver for + // compiled frames. The receiver might be in a register. + RegisterMap reg_map2(thread); + frame stubFrame = thread->last_frame(); + // Caller-frame is a compiled frame + frame callerFrame = stubFrame.sender(®_map2); + // Retrieve from a compiled argument list + Handle receiver = Handle(THREAD, callerFrame.retrieve_receiver(®_map2)); + if (receiver.is_null()) { + THROW_(vmSymbols::java_lang_NullPointerException(), Handle()); + } + return receiver; +} + +void SharedRuntime::resolve_attached_call_info(JavaThread* thread, methodHandle info, Bytecodes::Code bc, + Handle& receiver, CallInfo& callinfo, TRAPS) { + KlassHandle defc = info->method_holder(); + Symbol* name = info->name(); + Symbol* type = info->signature(); + LinkInfo link_info(defc, name, type, KlassHandle(), /*check_access=*/false); + switch(bc) { + case Bytecodes::_invokevirtual: + LinkResolver::resolve_virtual_call(callinfo, receiver, receiver->klass(), + link_info, /*check_null_and_abstract=*/true, THREAD); + break; + case Bytecodes::_invokeinterface: + LinkResolver::resolve_interface_call(callinfo, receiver, receiver->klass(), + link_info, /*check_null_and_abstract=*/true, THREAD); + break; + case Bytecodes::_invokestatic: + LinkResolver::resolve_static_call(callinfo, link_info, /*initialize_class=*/false, THREAD); + break; + case Bytecodes::_invokespecial: + LinkResolver::resolve_special_call(callinfo, link_info, THREAD); + break; + default: + fatal("bad call"); + } +} // Finds receiver, CallInfo (i.e. receiver method), and calling bytecode // for a call current in progress, i.e., arguments has been pushed on stack @@ -1126,6 +1205,21 @@ assert(receiver.is_null() || receiver->is_oop(), "wrong receiver"); LinkResolver::resolve_invoke(callinfo, receiver, constants, bytecode_index, bc, CHECK_(nullHandle)); + // When VM replaces MH.invokeBasic/linkTo* call with a direct/virtual call, + // it attaches statically resolved method to the call site. + methodHandle call_info; + vmIntrinsics::ID id = callinfo.resolved_method()->intrinsic_id(); + if (MethodHandles::is_signature_polymorphic(id) && id != vmIntrinsics::_invokeGeneric) { + call_info = extract_attached_call_info(thread); + if (!call_info.is_null()) { + bc = compute_bc(id); + // Find receiver for non-static call + if (bc != Bytecodes::_invokestatic) { + receiver = extract_receiver(thread, CHECK_(nullHandle)); + } + resolve_attached_call_info(thread, call_info, bc, receiver, callinfo, CHECK_(nullHandle)); + } + } #ifdef ASSERT // Check that the receiver klass is of the right subtype and that it is initialized for virtual calls if (bc != Bytecodes::_invokestatic && bc != Bytecodes::_invokedynamic && bc != Bytecodes::_invokehandle) { @@ -1133,14 +1227,13 @@ KlassHandle receiver_klass(THREAD, receiver->klass()); Klass* rk = constants->klass_ref_at(bytecode_index, CHECK_(nullHandle)); // klass is already loaded + if (!call_info.is_null()) { + // In case there's resolved method attached, use it's holder during the check. + rk = call_info->method_holder(); + } KlassHandle static_receiver_klass(THREAD, rk); - // Method handle invokes might have been optimized to a direct call - // so don't check for the receiver class. - // FIXME this weakens the assert too much methodHandle callee = callinfo.selected_method(); - assert(receiver_klass->is_subtype_of(static_receiver_klass()) || - callee->is_method_handle_intrinsic() || - callee->is_compiled_lambda_form(), + assert(receiver_klass->is_subtype_of(static_receiver_klass()), "actual receiver must be subclass of static receiver klass"); if (receiver_klass->oop_is_instance()) { if (InstanceKlass::cast(receiver_klass())->is_not_initialized()) { --- old/src/share/vm/runtime/sharedRuntime.hpp 2015-10-30 00:19:43.000000000 +0300 +++ new/src/share/vm/runtime/sharedRuntime.hpp 2015-10-30 00:19:43.000000000 +0300 @@ -342,6 +342,12 @@ vframeStream& vfst, Bytecodes::Code& bc, CallInfo& callinfo, TRAPS); + static methodHandle extract_attached_call_info(JavaThread* thread); + static void resolve_attached_call_info(JavaThread* thread, + methodHandle info, + Bytecodes::Code bc, + Handle& receiver, + CallInfo& callinfo, TRAPS); static address clean_virtual_call_entry(); static address clean_opt_virtual_call_entry(); --- old/src/share/vm/runtime/vm_operations.hpp 2015-10-30 00:19:44.000000000 +0300 +++ new/src/share/vm/runtime/vm_operations.hpp 2015-10-30 00:19:43.000000000 +0300 @@ -30,6 +30,7 @@ #include "oops/oop.hpp" #include "runtime/thread.hpp" #include "utilities/top.hpp" +#include "code/codeCache.hpp" // The following classes are used for operations // initiated by a Java thread but that must @@ -44,6 +45,7 @@ template(ThreadDump) \ template(PrintThreads) \ template(FindDeadlocks) \ + template(ClearICs) \ template(ForceSafepoint) \ template(ForceAsyncSafepoint) \ template(Deoptimize) \ @@ -230,6 +232,13 @@ } }; +class VM_ClearICs: public VM_Operation { + public: + VM_ClearICs() {} + void doit() { CodeCache::clear_inline_caches(); } + VMOp_Type type() const { return VMOp_ClearICs; } +}; + // dummy vm op, evaluated just to force a safepoint class VM_ForceSafepoint: public VM_Operation { public: --- old/test/sanity/MismatchedWhiteBox/WhiteBox.java 2015-10-30 00:19:44.000000000 +0300 +++ new/test/sanity/MismatchedWhiteBox/WhiteBox.java 2015-10-30 00:19:44.000000000 +0300 @@ -29,7 +29,7 @@ * @library /testlibrary * @compile WhiteBox.java * @run main ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-CheckIntrinsics sun.hotspot.WhiteBox */ package sun.hotspot; --- /dev/null 2015-10-30 00:19:44.000000000 +0300 +++ new/test/compiler/jsr292/NonInlinedCall/Agent.java 2015-10-30 00:19:44.000000000 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.io.File; +import java.io.PrintStream; +import java.lang.instrument.Instrumentation; +import java.util.Arrays; + +public class Agent { + public static void main(String[] args) throws Exception { + String jarName = args[0]; + String className = args[1]; + String manifestName = "manifest.mf"; + + System.out.println("Creating "+manifestName); + try (PrintStream out = new PrintStream(new File(manifestName))) { + out.println("Premain-Class: " + className); + out.println("Can-Redefine-Classes: true"); + } + System.out.println("Building "+jarName); + String[] jarArgs = new String[] {"-cfm", jarName, manifestName }; + + System.out.println("Running jar " + Arrays.toString(jarArgs)); + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(jarArgs)) { + throw new Error("jar failed: args=" + Arrays.toString(args)); + } + } +} --- /dev/null 2015-10-30 00:19:45.000000000 +0300 +++ new/test/compiler/jsr292/NonInlinedCall/GCTest.java 2015-10-30 00:19:44.000000000 +0300 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8072008 + * @library /testlibrary /../../test/lib + * @build GCTest NonInlinedReinvoker + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * java.lang.invoke.GCTest + * java.lang.invoke.GCTest$T + * java.lang.invoke.NonInlinedReinvoker + * jdk.test.lib.Asserts + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 + * java.lang.invoke.GCTest + */ +package java.lang.invoke; + +import sun.hotspot.WhiteBox; + +import java.lang.ref.*; +import static jdk.test.lib.Asserts.*; + +public class GCTest { + static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP; + + static class T { + static int f1() { return 0; } + static int f2() { return 1; } + } + + static @Stable MethodHandle mh; + static PhantomReference lform; + + static final ReferenceQueue rq = new ReferenceQueue<>(); + static final WhiteBox WB = WhiteBox.getWhiteBox(); + + @DontInline + static int invokeBasic() { + try { + return (int) mh.invokeBasic(); + } catch (Throwable e) { + throw new Error(e); + } + } + + static void test(int expected) { + for (int i = 0; i < 20_000; i++) { + invokeBasic(); + } + assertEquals(invokeBasic(), expected); + } + + public static void main(String[] args) throws Exception { + mh = NonInlinedReinvoker.make( + LOOKUP.findStatic(T.class, "f1", MethodType.methodType(int.class))); + + // Monitor LambdaForm GC + lform = new PhantomReference<>(mh.form, rq); + + test(0); + WB.clearInlineCaches(); + test(0); + + mh = NonInlinedReinvoker.make( + LOOKUP.findStatic(T.class, "f2", MethodType.methodType(int.class))); + + Reference ref = null; + while (ref == null) { + WB.fullGC(); + try { + ref = rq.remove(1000); + } catch (InterruptedException e) { /*ignore*/ } + } + + test(1); + WB.clearInlineCaches(); + test(1); + + System.out.println("TEST PASSED"); + } +} --- /dev/null 2015-10-30 00:19:45.000000000 +0300 +++ new/test/compiler/jsr292/NonInlinedCall/InvokeTest.java 2015-10-30 00:19:45.000000000 +0300 @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8072008 + * @library /testlibrary /../../test/lib + * @build InvokeTest NonInlinedReinvoker + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * java.lang.invoke.InvokeTest + * java.lang.invoke.InvokeTest$T + * java.lang.invoke.InvokeTest$P1 + * java.lang.invoke.InvokeTest$P2 + * java.lang.invoke.InvokeTest$I + * java.lang.invoke.NonInlinedReinvoker + * jdk.test.lib.Asserts + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 + * java.lang.invoke.InvokeTest + */ +package java.lang.invoke; + +import sun.hotspot.WhiteBox; +import static jdk.test.lib.Asserts.*; + +public class InvokeTest { + static MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP; + + static final MethodHandle virtualMH; // invokevirtual T.f1 + static final MethodHandle staticMH; // invokestatic T.f2 + static final MethodHandle intfMH; // invokeinterface I.f1 + static final MethodHandle specialMH; // invokespecial T.f4 T + static final MethodHandle basicMH; + + static final WhiteBox WB = WhiteBox.getWhiteBox(); + + static volatile boolean doDeopt = false; + + static { + try { + MethodType mtype = MethodType.methodType(Class.class); + + virtualMH = LOOKUP.findVirtual(T.class, "f1", mtype); + staticMH = LOOKUP.findStatic (T.class, "f2", mtype); + intfMH = LOOKUP.findVirtual(I.class, "f3", mtype); + specialMH = LOOKUP.findSpecial(T.class, "f4", mtype, T.class); + basicMH = NonInlinedReinvoker.make(staticMH); + } catch (Exception e) { + throw new Error(e); + } + } + + static class T implements I { + @DontInline public Class f1() { if (doDeopt) WB.deoptimize(); return T.class; } + @DontInline public static Class f2() { if (doDeopt) WB.deoptimize(); return T.class; } + @DontInline private Class f4() { if (doDeopt) WB.deoptimize(); return T.class; } + } + + static class P1 extends T { + @DontInline public Class f1() { if (doDeopt) WB.deoptimize(); return P1.class; } + @DontInline public Class f3() { if (doDeopt) WB.deoptimize(); return P1.class; } + } + + static class P2 extends T { + @DontInline public Class f1() { if (doDeopt) WB.deoptimize(); return P2.class; } + @DontInline public Class f3() { if (doDeopt) WB.deoptimize(); return P2.class; } + } + + static interface I { + @DontInline default Class f3() { if (doDeopt) WB.deoptimize(); return I.class; } + } + + @DontInline + static void linkToVirtual(Object obj, Class extecpted) { + try { + Class cls = (Class)virtualMH.invokeExact((T)obj); + assertEquals(cls, obj.getClass()); + } catch (Throwable e) { + throw new Error(e); + } + } + + @DontInline + static void linkToInterface(Object obj, Class expected) { + try { + Class cls = (Class)intfMH.invokeExact((I)obj); + assertEquals(cls, expected); + } catch (Throwable e) { + throw new Error(e); + } + } + + @DontInline + static void linkToStatic() { + try { + Class cls = (Class)staticMH.invokeExact(); + assertEquals(cls, T.class); + } catch (Throwable e) { + throw new Error(e); + } + } + + @DontInline + static void linkToSpecial(Object obj, Class expected) { + try { + Class cls = (Class)specialMH.invokeExact((T)obj); + assertEquals(cls, expected); + } catch (Throwable e) { + throw new Error(e); + } + } + + @DontInline + static void invokeBasic() { + try { + Class cls = (Class)basicMH.invokeBasic(); + assertEquals(cls, T.class); + } catch (Throwable e) { + throw new Error(e); + } + } + + static void run(Runnable r) { + for (int i = 0; i < 20_000; i++) { + r.run(); + } + + doDeopt = true; + r.run(); + doDeopt = false; + + WB.clearInlineCaches(); + + for (int i = 0; i < 20_000; i++) { + r.run(); + } + + doDeopt = true; + r.run(); + doDeopt = false; + } + + static void testVirtual() { + System.out.println("linkToVirtual"); + + // Monomorphic case (optimized virtual call) + run(() -> linkToVirtual(new T(), T.class)); + + // Megamorphic case (virtual call) + Object[] recv = new Object[] { new T(), new P1(), new P2() }; + run(() -> { + for (Object r : recv) { + linkToVirtual(r, r.getClass()); + }}); + } + + static void testInterface() { + System.out.println("linkToInterface"); + + // Monomorphic case (optimized virtual call) + run(() -> linkToInterface(new T(), I.class)); + + // Megamorphic case (virtual call) + Object[][] recv = new Object[][] {{new T(), I.class}, {new P1(), P1.class}, {new P2(), P2.class}}; + run(() -> { + for (Object[] r : recv) { + linkToInterface(r[0], (Class)r[1]); + }}); + } + + static void testSpecial() { + System.out.println("linkToSpecial"); + // Monomorphic case (optimized virtual call) + run(() -> linkToSpecial(new T(), T.class)); + } + + static void testStatic() { + System.out.println("linkToStatic"); + // static call + run(() -> linkToStatic()); + } + + static void testBasic() { + System.out.println("invokeBasic"); + // static call + run(() -> invokeBasic()); + } + + public static void main(String[] args) { + testVirtual(); + testInterface(); + testSpecial(); + testStatic(); + testBasic(); + } +} --- /dev/null 2015-10-30 00:19:45.000000000 +0300 +++ new/test/compiler/jsr292/NonInlinedCall/NonInlinedReinvoker.java 2015-10-30 00:19:45.000000000 +0300 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015, 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. + */ +package java.lang.invoke; + +class NonInlinedReinvoker extends DelegatingMethodHandle { + private final MethodHandle target; + + private NonInlinedReinvoker(MethodHandle target, LambdaForm lf) { + super(target.type(), lf); + this.target = target; + } + @Override + protected MethodHandle getTarget() { + return target; + } + + @Override + MethodHandle asTypeUncached(MethodType newType) { + return asTypeCache = target.asType(newType); + } + + static MethodHandle make(MethodHandle target) { + LambdaForm lform = DelegatingMethodHandle.makeReinvokerForm( + target, -1, DelegatingMethodHandle.class, "reinvoker.dontInline", + /*forceInline=*/false, DelegatingMethodHandle.NF_getTarget, null); + return new NonInlinedReinvoker(target, lform); + } +} --- /dev/null 2015-10-30 00:19:45.000000000 +0300 +++ new/test/compiler/jsr292/NonInlinedCall/RedefineTest.java 2015-10-30 00:19:45.000000000 +0300 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @bug 8072008 + * @library /testlibrary /../../test/lib + * @build RedefineTest Agent + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * java.lang.invoke.RedefineTest + * Agent + * jdk.test.lib.Asserts + * @run main Agent agent.jar java.lang.invoke.RedefineTest + * @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar + * -XX:+IgnoreUnrecognizedVMOptions + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 + * java.lang.invoke.RedefineTest + */ +package java.lang.invoke; + +import sun.hotspot.WhiteBox; +import sun.misc.Unsafe; + +import jdk.internal.org.objectweb.asm.*; + +import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; + +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class RedefineTest { + static final MethodHandles.Lookup LOOKUP = MethodHandles.Lookup.IMPL_LOOKUP; + static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + static final String NAME = "java/lang/invoke/RedefineTest$T"; + + static Class getClass(int r) { + byte[] classFile = getClassFile(r); + return UNSAFE.defineClass(NAME, classFile, 0, classFile.length, null, null); + } + + /** + * Generates a class of the following shape: + * static class T { + * @DontInline public static int f() { return $r; } + * } + */ + static byte[] getClassFile(int r) { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); + MethodVisitor mv; + cw.visit(52, ACC_PUBLIC | ACC_SUPER, NAME, null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "f", "()I", null, null); + mv.visitAnnotation("Ljava/lang/invoke/DontInline;", true); + mv.visitCode(); + mv.visitLdcInsn(r); + mv.visitInsn(IRETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + + static final MethodHandle mh; + static final Class CLS = getClass(0); + static { + try { + mh = LOOKUP.findStatic(CLS, "f", MethodType.methodType(int.class)); + } catch (Exception e) { + throw new Error(e); + } + } + + static final WhiteBox WB = WhiteBox.getWhiteBox(); + + @DontInline + static int invokeBasic() { + try { + return (int)mh.invokeExact(); + } catch (Throwable e) { + throw new Error(e); + } + } + + static Instrumentation instr; + public static void premain(String args, Instrumentation instr) { + RedefineTest.instr = instr; + } + + + public static void main(String[] args) throws Exception { + for (int i = 0; i < 20_000; i++) { + int r = invokeBasic(); + if (r != 0) { + throw new Error(r + " != 0"); + } + } + // WB.ensureCompiled(); + + redefine(); + + int exp = (instr != null) ? 1 : 0; + + for (int i = 0; i < 20_000; i++) { + if (invokeBasic() != exp) { + throw new Error(); + } + } + + WB.clearInlineCaches(); + + for (int i = 0; i < 20_000; i++) { + if (invokeBasic() != exp) { + throw new Error(); + } + } + + // WB.ensureCompiled(); + } + + static void redefine() { + if (instr == null) { + System.out.println("NOT REDEFINED"); + return; + } + ClassDefinition cd = new ClassDefinition(CLS, getClassFile(1)); + try { + instr.redefineClasses(cd); + } catch (Exception e) { + throw new Error(e); + } + System.out.println("REDEFINED"); + } +}