--- old/src/share/vm/opto/callGenerator.cpp 2014-03-21 09:59:10.621217121 +0100 +++ new/src/share/vm/opto/callGenerator.cpp 2014-03-21 09:59:10.322861079 +0100 @@ -283,6 +283,8 @@ virtual JVMState* generate(JVMState* jvms, Parse* parent_parser) { Compile *C = Compile::current(); + C->log_inline_id((jlong)this); + // Record that this call site should be revisited once the main // parse is finished. if (!is_mh_late_inline()) { @@ -368,6 +370,8 @@ C->print_inlining_move_to(this); + C->log_late_inline(this); + // This check is done here because for_method_handle_inline() method // needs jvms for inlined state. if (!do_late_inline_check(jvms)) { @@ -375,17 +379,6 @@ return; } - CompileLog* log = C->log(); - if (log != NULL) { - log->head("late_inline method='%d'", log->identify(method())); - JVMState* p = jvms; - while (p != NULL) { - log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); - p = p->caller(); - } - log->tail("late_inline"); - } - // Setup default node notes to be picked up by the inlining Node_Notes* old_nn = C->default_node_notes(); if (old_nn != NULL) { @@ -438,11 +431,12 @@ virtual JVMState* generate(JVMState* jvms, Parse* parent_parser) { JVMState* new_jvms = LateInlineCallGenerator::generate(jvms, parent_parser); + Compile* C = Compile::current(); if (_input_not_const) { // inlining won't be possible so no need to enqueue right now. call_node()->set_generator(this); } else { - Compile::current()->add_late_inline(this); + C->add_late_inline(this); } return new_jvms; } @@ -483,6 +477,9 @@ virtual JVMState* generate(JVMState* jvms, Parse* parent_parser) { Compile *C = Compile::current(); + + C->log_inline_id((jlong)this); + C->add_string_late_inline(this); JVMState* new_jvms = DirectCallGenerator::generate(jvms, parent_parser); @@ -505,6 +502,8 @@ virtual JVMState* generate(JVMState* jvms, Parse* parent_parser) { Compile *C = Compile::current(); + C->log_inline_id((jlong)this); + C->add_boxing_late_inline(this); JVMState* new_jvms = DirectCallGenerator::generate(jvms, parent_parser); @@ -786,6 +785,7 @@ } else { const char* msg = "receiver not constant"; if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg); + C->log_inline_failure(msg); } } break; @@ -858,6 +858,7 @@ } else { const char* msg = "member_name not constant"; if (PrintInlining) C->print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg); + C->log_inline_failure(msg); } } break; --- old/src/share/vm/opto/compile.hpp 2014-03-21 09:59:10.622089270 +0100 +++ new/src/share/vm/opto/compile.hpp 2014-03-21 09:59:10.344458650 +0100 @@ -440,6 +440,8 @@ void print_inlining_push(); PrintInliningBuffer& print_inlining_current(); + void log_late_inline_failure(CallGenerator* cg, const char* msg); + public: outputStream* print_inlining_stream() const { @@ -459,6 +461,10 @@ print_inlining_stream()->print(ss.as_string()); } + void log_late_inline(CallGenerator* cg); + void log_inline_id(jlong id); + void log_inline_failure(const char* msg); + void* replay_inline_data() const { return _replay_inline_data; } // Dump inlining replay data to the stream. --- old/src/share/vm/opto/compile.cpp 2014-03-21 09:59:10.700983921 +0100 +++ new/src/share/vm/opto/compile.cpp 2014-03-21 09:59:10.396820317 +0100 @@ -3831,7 +3831,7 @@ void Compile::dump_inlining() { bool do_print_inlining = print_inlining() || print_intrinsics(); - if (do_print_inlining) { + if (do_print_inlining || log() != NULL) { // Print inlining message for candidates that we couldn't inline // for lack of space for (int i = 0; i < _late_inlines.length(); i++) { @@ -3841,6 +3841,7 @@ if (do_print_inlining) { cg->print_inlining_late(msg); } + log_late_inline_failure(cg, msg); } } } @@ -3851,6 +3852,41 @@ } } +void Compile::log_late_inline(CallGenerator* cg) { + if (log() != NULL) { + log()->head("late_inline method='%d' inline_id='" JLONG_FORMAT "'", log()->identify(cg->method()), + (jlong)cg); + JVMState* p = cg->call_node()->jvms(); + while (p != NULL) { + log()->elem("jvms bci='%d' method='%d'", p->bci(), log()->identify(p->method())); + p = p->caller(); + } + log()->tail("late_inline"); + } +} + +void Compile::log_late_inline_failure(CallGenerator* cg, const char* msg) { + log_late_inline(cg); + if (log() != NULL) { + log()->inline_fail(msg); + } +} + +void Compile::log_inline_id(jlong id) { + if (log() != NULL) { + // The LogCompilation tool needs a unique way to identify late + // inline call sites. + log()->elem("inline_id id='" JLONG_FORMAT "'", id); + } +} + +void Compile::log_inline_failure(const char* msg) { + if (C->log() != NULL) { + C->log()->inline_fail(msg); + } +} + + // Dump inlining replay data to the stream. // Don't change thread state and acquire any locks. void Compile::dump_inline_data(outputStream* out) { --- old/src/share/vm/compiler/compileLog.cpp 2014-03-21 09:59:10.731856404 +0100 +++ new/src/share/vm/compiler/compileLog.cpp 2014-03-21 09:59:10.450841436 +0100 @@ -106,7 +106,7 @@ if (mobj->is_klass()) { ciKlass* klass = mobj->as_klass(); begin_elem("klass id='%d'", id); - name(klass->name()); + name(klass); if (!klass->is_loaded()) { print(" unloaded='1'"); } else { @@ -171,6 +171,15 @@ print("'"); } +void CompileLog::name(ciKlass* k) { + print(" name='"); + if (!k->is_loaded()) { + text()->print(k->name()->as_klass_external_name()); + } else { + text()->print(k->external_name()); + } + print("'"); +} // ------------------------------------------------------------------ // CompileLog::clear_identities --- old/src/share/vm/ci/ciKlass.hpp 2014-03-21 09:59:10.719471969 +0100 +++ new/src/share/vm/ci/ciKlass.hpp 2014-03-21 09:59:10.425783410 +0100 @@ -125,6 +125,8 @@ virtual ciKlass* exact_klass() = 0; void print_name_on(outputStream* st); + + const char* external_name() const; }; #endif // SHARE_VM_CI_CIKLASS_HPP --- old/src/share/vm/ci/ciSymbol.cpp 2014-03-21 09:59:10.752694319 +0100 +++ new/src/share/vm/ci/ciSymbol.cpp 2014-03-21 09:59:10.368648323 +0100 @@ -123,6 +123,10 @@ GUARDED_VM_ENTRY(get_symbol()->print_symbol_on(st);) } +const char* ciSymbol::as_klass_external_name() const { + GUARDED_VM_ENTRY(return get_symbol()->as_klass_external_name();); +} + // ------------------------------------------------------------------ // ciSymbol::make_impl // --- old/src/share/vm/compiler/compileLog.hpp 2014-03-21 09:59:10.753088202 +0100 +++ new/src/share/vm/compiler/compileLog.hpp 2014-03-21 09:59:10.476220042 +0100 @@ -28,6 +28,7 @@ #include "utilities/xmlstream.hpp" class ciBaseObject; +class ciKlass; class ciObject; class ciMetadata; class ciSymbol; @@ -72,6 +73,7 @@ void name(ciSymbol* s); // name='s' void name(Symbol* s) { xmlStream::name(s); } + void name(ciKlass* k); // Output an object description, return obj->ident(). int identify(ciBaseObject* obj); --- old/src/share/vm/ci/ciSymbol.hpp 2014-03-21 09:59:10.754212289 +0100 +++ new/src/share/vm/ci/ciSymbol.hpp 2014-03-21 09:59:10.493041054 +0100 @@ -90,6 +90,7 @@ void print_symbol() { print_symbol_on(tty); } + const char* as_klass_external_name() const; // Make a ciSymbol from a C string. // Consider adding to vmSymbols.hpp instead of using this constructor. --- old/src/share/vm/ci/ciKlass.cpp 2014-03-21 09:59:10.818581266 +0100 +++ new/src/share/vm/ci/ciKlass.cpp 2014-03-21 09:59:10.519021735 +0100 @@ -237,3 +237,9 @@ void ciKlass::print_name_on(outputStream* st) { name()->print_symbol_on(st); } + +const char* ciKlass::external_name() const { + GUARDED_VM_ENTRY( + return get_Klass()->external_name(); + ) +} --- old/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java 2014-03-21 09:59:10.896368112 +0100 +++ new/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java 2014-03-21 09:59:10.554077684 +0100 @@ -25,6 +25,7 @@ package com.sun.hotspot.tools.compiler; import java.io.PrintStream; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; @@ -40,6 +41,7 @@ private int endNodes; private int endLiveNodes; private double timeStamp; + private long inlineId; CallSite() { } @@ -94,7 +96,7 @@ public void print(PrintStream stream, int indent) { emit(stream, indent); - String m = getMethod().getHolder().replace('/', '.') + "::" + getMethod().getName(); + String m = getMethod().getHolder() + "::" + getMethod().getName(); if (getReason() == null) { stream.print(" @ " + getBci() + " " + m + " (" + getMethod().getBytes() + " bytes)"); @@ -213,5 +215,46 @@ public double getTimeStamp() { return timeStamp; } + + private boolean matches(CallSite other) { + // Every late inline call site has a unique inline id. If the + // call site we're looking for has one then use it other rely + // on method name and bci. + if (other.inlineId != 0) { + return inlineId == other.inlineId; + } + return method.equals(other.method) && bci == other.bci; + } + + public CallSite findCallSite(ArrayDeque sites) { + // Locate a late inline call site. Multiple chains of + // identical call sites with the same method name/bci are + // possible so we have to try them all until we find the late + // inline call site that has a matching inline id. + CallSite site = sites.pop(); + for (CallSite c : calls) { + if (c.matches(site)) { + if (!sites.isEmpty()) { + CallSite res = c.findCallSite(sites); + if (res != null) { + sites.push(site); + return res; + } + } else { + sites.push(site); + return c; + } + } + } + sites.push(site); + return null; + } + public long getInlineId() { + return inlineId; + } + + public void setInlineId(long inlineId) { + this.inlineId = inlineId; + } } --- old/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java 2014-03-21 09:59:10.953485137 +0100 +++ new/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java 2014-03-21 09:59:10.578517095 +0100 @@ -31,6 +31,7 @@ import java.io.FileReader; import java.io.Reader; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -144,9 +145,12 @@ private Stack scopes = new Stack(); private Compilation compile; private CallSite site; + private CallSite methodHandleSite; private Stack phaseStack = new Stack(); private UncommonTrapEvent currentTrap; - private Stack late_inline_scope; + private Stack lateInlineScope; + private boolean lateInlining; + long parseLong(String l) { try { @@ -330,18 +334,61 @@ } methods.put(id, m); } else if (qname.equals("call")) { - site = new CallSite(bci, method(search(atts, "method"))); + if (methodHandleSite != null) { + methodHandleSite = null; + } + Method m = method(search(atts, "method")); + if (lateInlining && scopes.size() == 0) { + // re-attempting already seen call site (late inlining for MH invokes) + if (m != site.getMethod()) { + if (bci != site.getBci()) { + System.out.println(m + " bci: " + bci); + System.out.println(site.getMethod() + " bci: " + site.getBci()); + throw new InternalError("bci mismatch after late inlining"); + } + site.setMethod(m); + } + } else { + site = new CallSite(bci, m); + } site.setCount(Integer.parseInt(search(atts, "count", "0"))); String receiver = atts.getValue("receiver"); if (receiver != null) { site.setReceiver(type(receiver)); site.setReceiver_count(Integer.parseInt(search(atts, "receiver_count"))); } - scopes.peek().add(site); + int methodHandle = Integer.parseInt(search(atts, "method_handle_intrinsic", "0")); + if (lateInlining && scopes.size() == 0) { + // The call was added before this round of late inlining + } else if (methodHandle == 0) { + scopes.peek().add(site); + } else { + // method handle call site can be followed by another + // call (in case it is inlined). If that happens we + // discard the method handle call site. So we keep + // track of it but don't add it to the list yet. + methodHandleSite = site; + } } else if (qname.equals("regalloc")) { compile.setAttempts(Integer.parseInt(search(atts, "attempts"))); } else if (qname.equals("inline_fail")) { - scopes.peek().last().setReason(search(atts, "reason")); + if (methodHandleSite != null) { + scopes.peek().add(methodHandleSite); + methodHandleSite = null; + } + if (lateInlining && scopes.size() == 0) { + site.setReason(search(atts, "reason")); + lateInlining = false; + } else { + scopes.peek().last().setReason(search(atts, "reason")); + } + } else if (qname.equals("inline_success")) { + if (methodHandleSite != null) { + throw new InternalError("method handle site should have been replaced"); + } + if (lateInlining && scopes.size() == 0) { + site.setReason(null); + } } else if (qname.equals("failure")) { failureReason = search(atts, "reason"); } else if (qname.equals("task_done")) { @@ -371,22 +418,30 @@ // ignore for now } } else if (qname.equals("late_inline")) { - late_inline_scope = new Stack(); + long inlineId = Long.parseLong(search(atts, "inline_id")); + lateInlineScope = new Stack(); site = new CallSite(-999, method(search(atts, "method"))); - late_inline_scope.push(site); + site.setInlineId(inlineId); + lateInlineScope.push(site); } else if (qname.equals("jvms")) { // if (currentTrap != null) { currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci"))); - } else if (late_inline_scope != null) { + } else if (lateInlineScope != null) { bci = Integer.parseInt(search(atts, "bci")); site = new CallSite(bci, method(search(atts, "method"))); - late_inline_scope.push(site); + lateInlineScope.push(site); } else { // Ignore , // , // } + } else if (qname.equals("inline_id")) { + if (methodHandleSite != null) { + throw new InternalError("method handle site should have been replaced"); + } + long id = Long.parseLong(search(atts, "id")); + site.setInlineId(id); } else if (qname.equals("nmethod")) { String id = makeId(atts); NMethod nm = new NMethod(Double.parseDouble(search(atts, "stamp")), @@ -396,8 +451,18 @@ nmethods.put(id, nm); events.add(nm); } else if (qname.equals("parse")) { + if (methodHandleSite != null) { + throw new InternalError("method handle site should have been replaced"); + } Method m = method(search(atts, "method")); - if (scopes.size() == 0) { + if (lateInlining && scopes.size() == 0) { + if (site.getMethod() != m) { + System.out.println(site.getMethod()); + System.out.println(m); + throw new InternalError("Unexpected method mismatch during late inlining"); + } + } + if (scopes.size() == 0 && !lateInlining) { compile.setMethod(m); scopes.push(site); } else { @@ -427,14 +492,19 @@ if (qname.equals("parse")) { indent -= 2; scopes.pop(); + if (scopes.size() == 0) { + lateInlining = false; + } } else if (qname.equals("uncommon_trap")) { currentTrap = null; } else if (qname.equals("late_inline")) { // Populate late inlining info. - - // late_inline scopes are specified in reverse order: + if (scopes.size() != 0) { + throw new InternalError("scopes should be empty for late inline"); + } + // late inline scopes are specified in reverse order: // compiled method should be on top of stack. - CallSite caller = late_inline_scope.pop(); + CallSite caller = lateInlineScope.pop(); Method m = compile.getMethod(); if (m != caller.getMethod()) { System.out.println(m); @@ -444,28 +514,42 @@ // late_inline contains caller+bci info, convert it // to bci+callee info used by LogCompilation. - site = compile.getLateInlineCall(); + CallSite lateInlineSite = compile.getLateInlineCall(); + ArrayDeque thisCallScopes = new ArrayDeque(); do { bci = caller.getBci(); // Next inlined call. - caller = late_inline_scope.pop(); + caller = lateInlineScope.pop(); CallSite callee = new CallSite(bci, caller.getMethod()); - site.add(callee); - site = callee; - } while (!late_inline_scope.empty()); + callee.setInlineId(caller.getInlineId()); + thisCallScopes.addLast(callee); + lateInlineSite.add(callee); + lateInlineSite = callee; + } while (!lateInlineScope.empty()); + site = compile.getCall().findCallSite(thisCallScopes); + if (site == null) { + System.out.println(caller.getMethod() + " bci: " + bci); + throw new InternalError("couldn't find call site"); + } + lateInlining = true; + if (caller.getBci() != -999) { System.out.println(caller.getMethod()); throw new InternalError("broken late_inline info"); } if (site.getMethod() != caller.getMethod()) { - System.out.println(site.getMethod()); - System.out.println(caller.getMethod()); - throw new InternalError("call site and late_inline info don't match"); + if (site.getInlineId() == caller.getInlineId()) { + site.setMethod(caller.getMethod()); + } else { + System.out.println(site.getMethod()); + System.out.println(caller.getMethod()); + throw new InternalError("call site and late_inline info don't match"); + } } // late_inline is followed by parse with scopes.size() == 0, // 'site' will be pushed to scopes. - late_inline_scope = null; + lateInlineScope = null; } else if (qname.equals("task")) { types.clear(); methods.clear(); --- old/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java 2014-03-21 09:59:10.934841951 +0100 +++ new/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java 2014-03-21 09:59:10.606038468 +0100 @@ -51,15 +51,15 @@ String format(int osr_bci) { if (osr_bci >= 0) { - return getHolder().replace('/', '.') + "::" + getName() + " @ " + osr_bci + " (" + getBytes() + " bytes)"; + return getHolder() + "::" + getName() + " @ " + osr_bci + " (" + getBytes() + " bytes)"; } else { - return getHolder().replace('/', '.') + "::" + getName() + " (" + getBytes() + " bytes)"; + return getHolder() + "::" + getName() + " (" + getBytes() + " bytes)"; } } @Override public String toString() { - return getHolder().replace('/', '.') + "::" + getName() + " (" + getBytes() + " bytes)"; + return getHolder() + "::" + getName() + " (" + getBytes() + " bytes)"; } public String getHolder() { @@ -117,4 +117,14 @@ public void setFlags(String flags) { this.flags = flags; } + + @Override + public boolean equals(Object o) { + if (o instanceof Method) { + Method other = (Method)o; + return holder.equals(other.holder) && name.equals(other.name) && + arguments.equals(other.arguments) && returnType.equals(other.returnType); + } + return false; + } } --- old/src/share/vm/opto/doCall.cpp 2014-03-21 09:59:13.308893665 +0100 +++ new/src/share/vm/opto/doCall.cpp 2014-03-21 09:59:13.147595574 +0100 @@ -103,6 +103,9 @@ log->print(" receiver2='%d' receiver2_count='%d'", r2id, profile.receiver_count(1)); } } + if (callee->is_method_handle_intrinsic()) { + log->print(" method_handle_intrinsic='1'"); + } log->end_elem(); } @@ -296,6 +299,7 @@ if (call_does_dispatch) { const char* msg = "virtual call"; if (PrintInlining) print_inlining(callee, jvms->depth() - 1, jvms->bci(), msg); + C->log_inline_failure(msg); return CallGenerator::for_virtual_call(callee, vtable_index); } else { // Class Hierarchy Analysis or Type Profile reveals a unique target, --- old/src/share/vm/utilities/xmlstream.cpp 2014-03-21 09:59:13.331253419 +0100 +++ new/src/share/vm/utilities/xmlstream.cpp 2014-03-21 09:59:13.177885400 +0100 @@ -398,8 +398,7 @@ void xmlStream::method_text(methodHandle method) { assert_if_no_error(inside_attrs(), "printing attributes"); if (method.is_null()) return; - //method->print_short_name(text()); - method->method_holder()->name()->print_symbol_on(text()); + text()->print(method->method_holder()->external_name()); print_raw(" "); // " " is easier for tools to parse than "::" method->name()->print_symbol_on(text()); print_raw(" "); // separator