mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-12 17:03:14 +00:00
8319850: PrintInlining should print which methods are late inlines
Reviewed-by: chagedorn, kvn, jsjolen, dlong
This commit is contained in:
parent
51cce6e612
commit
96fefed37f
@ -284,21 +284,6 @@ void CompileTask::print_impl(outputStream* st, Method* method, int compile_id, i
|
||||
}
|
||||
}
|
||||
|
||||
void CompileTask::print_inline_indent(int inline_level, outputStream* st) {
|
||||
// 1234567
|
||||
st->print(" "); // print timestamp
|
||||
// 1234
|
||||
st->print(" "); // print compilation number
|
||||
// %s!bn
|
||||
st->print(" "); // print method attributes
|
||||
if (TieredCompilation) {
|
||||
st->print(" ");
|
||||
}
|
||||
st->print(" "); // more indent
|
||||
st->print(" "); // initial inlining indent
|
||||
for (int i = 0; i < inline_level; i++) st->print(" ");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// CompileTask::print_compilation
|
||||
void CompileTask::print(outputStream* st, const char* msg, bool short_form, bool cr) {
|
||||
@ -412,45 +397,76 @@ bool CompileTask::check_break_at_flags() {
|
||||
// ------------------------------------------------------------------
|
||||
// CompileTask::print_inlining
|
||||
void CompileTask::print_inlining_inner(outputStream* st, ciMethod* method, int inline_level, int bci, InliningResult result, const char* msg) {
|
||||
print_inlining_header(st, method, inline_level, bci);
|
||||
print_inlining_inner_message(st, result, msg);
|
||||
st->cr();
|
||||
}
|
||||
|
||||
void CompileTask::print_inlining_header(outputStream* st, ciMethod* method, int inline_level, int bci) {
|
||||
// 1234567
|
||||
st->print(" "); // print timestamp
|
||||
st->print(" "); // print timestamp
|
||||
// 1234
|
||||
st->print(" "); // print compilation number
|
||||
st->print(" "); // print compilation number
|
||||
|
||||
// method attributes
|
||||
if (method->is_loaded()) {
|
||||
const char sync_char = method->is_synchronized() ? 's' : ' ';
|
||||
const char sync_char = method->is_synchronized() ? 's' : ' ';
|
||||
const char exception_char = method->has_exception_handlers() ? '!' : ' ';
|
||||
const char monitors_char = method->has_monitor_bytecodes() ? 'm' : ' ';
|
||||
const char monitors_char = method->has_monitor_bytecodes() ? 'm' : ' ';
|
||||
|
||||
// print method attributes
|
||||
st->print(" %c%c%c ", sync_char, exception_char, monitors_char);
|
||||
} else {
|
||||
// %s!bn
|
||||
st->print(" "); // print method attributes
|
||||
st->print(" "); // print method attributes
|
||||
}
|
||||
|
||||
if (TieredCompilation) {
|
||||
st->print(" ");
|
||||
}
|
||||
st->print(" "); // more indent
|
||||
st->print(" "); // initial inlining indent
|
||||
st->print(" "); // more indent
|
||||
st->print(" "); // initial inlining indent
|
||||
|
||||
for (int i = 0; i < inline_level; i++) st->print(" ");
|
||||
for (int i = 0; i < inline_level; i++) {
|
||||
st->print(" ");
|
||||
}
|
||||
|
||||
st->print("@ %d ", bci); // print bci
|
||||
st->print("@ %d ", bci); // print bci
|
||||
print_inline_inner_method_info(st, method);
|
||||
}
|
||||
|
||||
void CompileTask::print_inline_inner_method_info(outputStream* st, ciMethod* method) {
|
||||
method->print_short_name(st);
|
||||
if (method->is_loaded())
|
||||
if (method->is_loaded()) {
|
||||
st->print(" (%d bytes)", method->code_size());
|
||||
else
|
||||
} else {
|
||||
st->print(" (not loaded)");
|
||||
}
|
||||
}
|
||||
|
||||
void CompileTask::print_inline_indent(int inline_level, outputStream* st) {
|
||||
// 1234567
|
||||
st->print(" "); // print timestamp
|
||||
// 1234
|
||||
st->print(" "); // print compilation number
|
||||
// %s!bn
|
||||
st->print(" "); // print method attributes
|
||||
if (TieredCompilation) {
|
||||
st->print(" ");
|
||||
}
|
||||
st->print(" "); // more indent
|
||||
st->print(" "); // initial inlining indent
|
||||
for (int i = 0; i < inline_level; i++) {
|
||||
st->print(" ");
|
||||
}
|
||||
}
|
||||
|
||||
void CompileTask::print_inlining_inner_message(outputStream* st, InliningResult result, const char* msg) {
|
||||
if (msg != nullptr) {
|
||||
st->print(" %s%s", result == InliningResult::SUCCESS ? "" : "failed to inline: ", msg);
|
||||
} else if (result == InliningResult::FAILURE) {
|
||||
st->print(" %s", "failed to inline");
|
||||
}
|
||||
st->cr();
|
||||
}
|
||||
|
||||
void CompileTask::print_ul(const char* msg){
|
||||
|
||||
@ -236,6 +236,9 @@ public:
|
||||
}
|
||||
static void print_ul(const nmethod* nm, const char* msg = nullptr);
|
||||
|
||||
/**
|
||||
* @deprecated Please rely on Compile::inline_printer. Do not directly write inlining information to tty.
|
||||
*/
|
||||
static void print_inline_indent(int inline_level, outputStream* st = tty);
|
||||
|
||||
void print_tty();
|
||||
@ -253,7 +256,11 @@ public:
|
||||
|
||||
bool check_break_at_flags();
|
||||
|
||||
static void print_inlining_header(outputStream* st, ciMethod* method, int inline_level, int bci);
|
||||
static void print_inlining_inner(outputStream* st, ciMethod* method, int inline_level, int bci, InliningResult result, const char* msg = nullptr);
|
||||
static void print_inline_inner_method_info(outputStream* st, ciMethod* method);
|
||||
static void print_inlining_inner_message(outputStream* st, InliningResult result, const char* msg);
|
||||
|
||||
static void print_inlining_tty(ciMethod* method, int inline_level, int bci, InliningResult result, const char* msg = nullptr) {
|
||||
print_inlining_inner(tty, method, inline_level, bci, result, msg);
|
||||
}
|
||||
|
||||
@ -66,6 +66,8 @@ public:
|
||||
TreapNode* _right;
|
||||
|
||||
public:
|
||||
TreapNode(const K& k, uint64_t p) : _priority(p), _key(k), _left(nullptr), _right(nullptr) {}
|
||||
|
||||
TreapNode(const K& k, const V& v, uint64_t p)
|
||||
: _priority(p),
|
||||
_key(k),
|
||||
@ -313,6 +315,30 @@ public:
|
||||
return candidate;
|
||||
}
|
||||
|
||||
struct FindResult {
|
||||
FindResult(TreapNode* node, bool new_node) : node(node), new_node(new_node) {}
|
||||
TreapNode* const node;
|
||||
bool const new_node;
|
||||
};
|
||||
|
||||
// Finds the node for the given k in the tree or inserts a new node with the default constructed value.
|
||||
FindResult find(const K& k) {
|
||||
if (TreapNode* found = find(_root, k)) {
|
||||
return FindResult(found, false);
|
||||
}
|
||||
_node_count++;
|
||||
// Doesn't exist, make node
|
||||
void* node_place = _allocator.allocate(sizeof(TreapNode));
|
||||
uint64_t prio = prng_next();
|
||||
TreapNode* node = new (node_place) TreapNode(k, prio);
|
||||
|
||||
// (LEQ_k, GT_k)
|
||||
node_pair split_up = split(this->_root, k);
|
||||
// merge(merge(LEQ_k, EQ_k), GT_k)
|
||||
this->_root = merge(merge(split_up.left, node), split_up.right);
|
||||
return FindResult(node, true);
|
||||
}
|
||||
|
||||
TreapNode* closest_gt(const K& key) {
|
||||
TreapNode* candidate = nullptr;
|
||||
TreapNode* pos = _root;
|
||||
|
||||
@ -113,7 +113,8 @@ static bool is_unboxing_method(ciMethod* callee_method, Compile* C) {
|
||||
|
||||
// positive filter: should callee be inlined?
|
||||
bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
|
||||
int caller_bci, bool& should_delay, ciCallProfile& profile) {
|
||||
JVMState* caller_jvms, bool& should_delay, ciCallProfile& profile) {
|
||||
int caller_bci = caller_jvms->bci();
|
||||
// Allows targeted inlining
|
||||
if (C->directive()->should_inline(callee_method)) {
|
||||
set_msg("force inline by CompileCommand");
|
||||
@ -143,9 +144,9 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
|
||||
// Check for too many throws (and not too huge)
|
||||
if(callee_method->interpreter_throwout_count() > InlineThrowCount &&
|
||||
size < InlineThrowMaxSize ) {
|
||||
if (C->print_inlining() && Verbose) {
|
||||
CompileTask::print_inline_indent(inline_level());
|
||||
tty->print_cr("Inlined method with many throws (throws=%d):", callee_method->interpreter_throwout_count());
|
||||
if (Verbose) {
|
||||
outputStream* stream = C->inline_printer()->record(callee_method, caller_jvms, InliningResult::SUCCESS);
|
||||
stream->print("Inlined method with many throws (throws=%d):", callee_method->interpreter_throwout_count());
|
||||
}
|
||||
set_msg("many throws");
|
||||
return true;
|
||||
@ -168,11 +169,8 @@ bool InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method,
|
||||
|
||||
max_inline_size = C->freq_inline_size();
|
||||
if (size <= max_inline_size && TraceFrequencyInlining) {
|
||||
CompileTask::print_inline_indent(inline_level());
|
||||
tty->print_cr("Inlined frequent method (freq=%lf):", freq);
|
||||
CompileTask::print_inline_indent(inline_level());
|
||||
callee_method->print();
|
||||
tty->cr();
|
||||
outputStream* stream = C->inline_printer()->record(callee_method, caller_jvms, InliningResult::SUCCESS);
|
||||
stream->print("Inlined frequent method (freq=%lf):", freq);
|
||||
}
|
||||
} else {
|
||||
// Not hot. Check for medium-sized pre-existing nmethod at cold sites.
|
||||
@ -376,7 +374,7 @@ bool InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_method,
|
||||
_forced_inline = false; // Reset
|
||||
|
||||
// 'should_delay' can be overridden during replay compilation
|
||||
if (!should_inline(callee_method, caller_method, caller_bci, should_delay, profile)) {
|
||||
if (!should_inline(callee_method, caller_method, jvms, should_delay, profile)) {
|
||||
return false;
|
||||
}
|
||||
// 'should_delay' can be overridden during replay compilation
|
||||
@ -534,8 +532,9 @@ const char* InlineTree::check_can_parse(ciMethod* callee) {
|
||||
}
|
||||
|
||||
//------------------------------print_inlining---------------------------------
|
||||
void InlineTree::print_inlining(ciMethod* callee_method, int caller_bci,
|
||||
ciMethod* caller_method, bool success) const {
|
||||
void InlineTree::print_inlining(ciMethod* callee_method, JVMState* jvm, bool success) const {
|
||||
int caller_bci = jvm->bci();
|
||||
ciMethod* caller_method = jvm->method();
|
||||
const char* inline_msg = msg();
|
||||
assert(inline_msg != nullptr, "just checking");
|
||||
if (C->log() != nullptr) {
|
||||
@ -545,19 +544,11 @@ void InlineTree::print_inlining(ciMethod* callee_method, int caller_bci,
|
||||
C->log()->inline_fail(inline_msg);
|
||||
}
|
||||
}
|
||||
CompileTask::print_inlining_ul(callee_method, inline_level(),
|
||||
caller_bci, inlining_result_of(success), inline_msg);
|
||||
if (C->print_inlining()) {
|
||||
C->print_inlining(callee_method, inline_level(), caller_bci, inlining_result_of(success), inline_msg);
|
||||
guarantee(callee_method != nullptr, "would crash in CompilerEvent::InlineEvent::post");
|
||||
if (Verbose) {
|
||||
const InlineTree *top = this;
|
||||
while (top->caller_tree() != nullptr) { top = top->caller_tree(); }
|
||||
//tty->print(" bcs: %d+%d invoked: %d", top->count_inline_bcs(), callee_method->code_size(), callee_method->interpreter_invocation_count());
|
||||
}
|
||||
}
|
||||
CompileTask::print_inlining_ul(callee_method, inline_level(), caller_bci, inlining_result_of(success), inline_msg);
|
||||
C->inline_printer()->record(callee_method, jvm, inlining_result_of(success), inline_msg);
|
||||
EventCompilerInlining event;
|
||||
if (event.should_commit()) {
|
||||
guarantee(callee_method != nullptr, "would crash in CompilerEvent::InlineEvent::post");
|
||||
CompilerEvent::InlineEvent::post(event, C->compile_id(), caller_method->get_Method(), callee_method, success, inline_msg, caller_bci);
|
||||
}
|
||||
}
|
||||
@ -582,14 +573,14 @@ bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallPro
|
||||
// Do some initial checks.
|
||||
if (!pass_initial_checks(caller_method, caller_bci, callee_method)) {
|
||||
set_msg("failed initial checks");
|
||||
print_inlining(callee_method, caller_bci, caller_method, false /* !success */);
|
||||
print_inlining(callee_method, jvms, false /* !success */);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do some parse checks.
|
||||
set_msg(check_can_parse(callee_method));
|
||||
if (msg() != nullptr) {
|
||||
print_inlining(callee_method, caller_bci, caller_method, false /* !success */);
|
||||
print_inlining(callee_method, jvms, false /* !success */);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -601,7 +592,7 @@ bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallPro
|
||||
if (msg() == nullptr) {
|
||||
set_msg("inline (hot)");
|
||||
}
|
||||
print_inlining(callee_method, caller_bci, caller_method, true /* success */);
|
||||
print_inlining(callee_method, jvms, true /* success */);
|
||||
InlineTree* callee_tree = build_inline_tree_for_callee(callee_method, jvms, caller_bci);
|
||||
if (should_delay) {
|
||||
// Record late inlining decision in order to dump it for compiler replay
|
||||
@ -613,7 +604,7 @@ bool InlineTree::ok_to_inline(ciMethod* callee_method, JVMState* jvms, ciCallPro
|
||||
if (msg() == nullptr) {
|
||||
set_msg("too cold to inline");
|
||||
}
|
||||
print_inlining(callee_method, caller_bci, caller_method, false /* !success */ );
|
||||
print_inlining(callee_method, jvms, false /* !success */);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -634,8 +625,7 @@ InlineTree *InlineTree::build_inline_tree_for_callee( ciMethod* callee_method, J
|
||||
max_inline_level_adjust += 1; // don't count method handle calls from java.lang.invoke implementation
|
||||
}
|
||||
if (max_inline_level_adjust != 0 && C->print_inlining() && (Verbose || WizardMode)) {
|
||||
CompileTask::print_inline_indent(inline_level());
|
||||
tty->print_cr(" \\-> discounting inline depth");
|
||||
C->inline_printer()->record(callee_method, caller_jvms, InliningResult::SUCCESS, " \\-> discounting inline depth");
|
||||
}
|
||||
if (max_inline_level_adjust != 0 && C->log()) {
|
||||
int id1 = C->log()->identify(caller_jvms->method());
|
||||
|
||||
@ -84,7 +84,6 @@ public:
|
||||
|
||||
JVMState* ParseGenerator::generate(JVMState* jvms) {
|
||||
Compile* C = Compile::current();
|
||||
C->print_inlining_update(this);
|
||||
|
||||
if (is_osr()) {
|
||||
// The JVMS for a OSR has a single argument (see its TypeFunc).
|
||||
@ -143,7 +142,6 @@ protected:
|
||||
|
||||
JVMState* DirectCallGenerator::generate(JVMState* jvms) {
|
||||
GraphKit kit(jvms);
|
||||
kit.C->print_inlining_update(this);
|
||||
bool is_static = method()->is_static();
|
||||
address target = is_static ? SharedRuntime::get_resolve_static_call_stub()
|
||||
: SharedRuntime::get_resolve_opt_virtual_call_stub();
|
||||
@ -218,8 +216,6 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) {
|
||||
GraphKit kit(jvms);
|
||||
Node* receiver = kit.argument(0);
|
||||
|
||||
kit.C->print_inlining_update(this);
|
||||
|
||||
if (kit.C->log() != nullptr) {
|
||||
kit.C->log()->elem("virtual_call bci='%d'", jvms->bci());
|
||||
}
|
||||
@ -353,15 +349,6 @@ class LateInlineCallGenerator : public DirectCallGenerator {
|
||||
return DirectCallGenerator::generate(jvms);
|
||||
}
|
||||
|
||||
virtual void print_inlining_late(InliningResult result, const char* msg) {
|
||||
CallNode* call = call_node();
|
||||
Compile* C = Compile::current();
|
||||
C->print_inlining_assert_ready();
|
||||
C->print_inlining(method(), call->jvms()->depth()-1, call->jvms()->bci(), result, msg);
|
||||
C->print_inlining_move_to(this);
|
||||
C->print_inlining_update_delayed(this);
|
||||
}
|
||||
|
||||
virtual void set_unique_id(jlong id) {
|
||||
_unique_id = id;
|
||||
}
|
||||
@ -431,9 +418,9 @@ bool LateInlineMHCallGenerator::do_late_inline_check(Compile* C, JVMState* jvms)
|
||||
assert(!input_not_const, "sanity"); // shouldn't have been scheduled for inlining in the first place
|
||||
|
||||
if (cg != nullptr) {
|
||||
if (!allow_inline && (C->print_inlining() || C->print_intrinsics())) {
|
||||
C->print_inlining(cg->method(), jvms->depth()-1, call_node()->jvms()->bci(), InliningResult::FAILURE,
|
||||
"late method handle call resolution");
|
||||
if (!allow_inline) {
|
||||
C->inline_printer()->record(cg->method(), call_node()->jvms(), InliningResult::FAILURE,
|
||||
"late method handle call resolution");
|
||||
}
|
||||
assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining");
|
||||
_inline_cg = cg;
|
||||
@ -499,15 +486,6 @@ class LateInlineVirtualCallGenerator : public VirtualCallGenerator {
|
||||
return new_jvms;
|
||||
}
|
||||
|
||||
virtual void print_inlining_late(InliningResult result, const char* msg) {
|
||||
CallNode* call = call_node();
|
||||
Compile* C = Compile::current();
|
||||
C->print_inlining_assert_ready();
|
||||
C->print_inlining(method(), call->jvms()->depth()-1, call->jvms()->bci(), result, msg);
|
||||
C->print_inlining_move_to(this);
|
||||
C->print_inlining_update_delayed(this);
|
||||
}
|
||||
|
||||
virtual void set_unique_id(jlong id) {
|
||||
_unique_id = id;
|
||||
}
|
||||
@ -531,20 +509,16 @@ bool LateInlineVirtualCallGenerator::do_late_inline_check(Compile* C, JVMState*
|
||||
Node* receiver = jvms->map()->argument(jvms, 0);
|
||||
const Type* recv_type = C->initial_gvn()->type(receiver);
|
||||
if (recv_type->maybe_null()) {
|
||||
if (C->print_inlining() || C->print_intrinsics()) {
|
||||
C->print_inlining(method(), jvms->depth()-1, call_node()->jvms()->bci(), InliningResult::FAILURE,
|
||||
"late call devirtualization failed (receiver may be null)");
|
||||
}
|
||||
C->inline_printer()->record(method(), call_node()->jvms(), InliningResult::FAILURE,
|
||||
"late call devirtualization failed (receiver may be null)");
|
||||
return false;
|
||||
}
|
||||
// Even if inlining is not allowed, a virtual call can be strength-reduced to a direct call.
|
||||
bool allow_inline = C->inlining_incrementally();
|
||||
if (!allow_inline && _callee->holder()->is_interface()) {
|
||||
// Don't convert the interface call to a direct call guarded by an interface subtype check.
|
||||
if (C->print_inlining() || C->print_intrinsics()) {
|
||||
C->print_inlining(method(), jvms->depth()-1, call_node()->jvms()->bci(), InliningResult::FAILURE,
|
||||
"late call devirtualization failed (interface call)");
|
||||
}
|
||||
C->inline_printer()->record(method(), call_node()->jvms(), InliningResult::FAILURE,
|
||||
"late call devirtualization failed (interface call)");
|
||||
return false;
|
||||
}
|
||||
CallGenerator* cg = C->call_generator(_callee,
|
||||
@ -557,9 +531,8 @@ bool LateInlineVirtualCallGenerator::do_late_inline_check(Compile* C, JVMState*
|
||||
true /*allow_intrinsics*/);
|
||||
|
||||
if (cg != nullptr) {
|
||||
if (!allow_inline && (C->print_inlining() || C->print_intrinsics())) {
|
||||
C->print_inlining(cg->method(), jvms->depth()-1, call_node()->jvms()->bci(), InliningResult::FAILURE,
|
||||
"late call devirtualization");
|
||||
if (!allow_inline) {
|
||||
C->inline_printer()->record(cg->method(), call_node()->jvms(), InliningResult::FAILURE, "late call devirtualization");
|
||||
}
|
||||
assert(!cg->is_late_inline() || cg->is_mh_late_inline() || AlwaysIncrementalInline || StressIncrementalInlining, "we're doing late inlining");
|
||||
_inline_cg = cg;
|
||||
@ -682,21 +655,13 @@ void CallGenerator::do_late_inline_helper() {
|
||||
map->set_argument(jvms, i1, call->in(TypeFunc::Parms + i1));
|
||||
}
|
||||
|
||||
C->print_inlining_assert_ready();
|
||||
|
||||
C->print_inlining_move_to(this);
|
||||
|
||||
C->log_late_inline(this);
|
||||
|
||||
// JVMState is ready, so time to perform some checks and prepare for inlining attempt.
|
||||
if (!do_late_inline_check(C, jvms)) {
|
||||
map->disconnect_inputs(C);
|
||||
C->print_inlining_update_delayed(this);
|
||||
return;
|
||||
}
|
||||
if (C->print_inlining() && (is_mh_late_inline() || is_virtual_late_inline())) {
|
||||
C->print_inlining_update_delayed(this);
|
||||
}
|
||||
|
||||
// Setup default node notes to be picked up by the inlining
|
||||
Node_Notes* old_nn = C->node_notes_at(call->_idx);
|
||||
@ -711,6 +676,18 @@ void CallGenerator::do_late_inline_helper() {
|
||||
if (new_jvms == nullptr) return; // no change
|
||||
if (C->failing()) return;
|
||||
|
||||
if (is_mh_late_inline()) {
|
||||
C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded (method handle)");
|
||||
} else if (is_string_late_inline()) {
|
||||
C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded (string method)");
|
||||
} else if (is_boxing_late_inline()) {
|
||||
C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded (boxing method)");
|
||||
} else if (is_vector_reboxing_late_inline()) {
|
||||
C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded (vector reboxing method)");
|
||||
} else {
|
||||
C->inline_printer()->record(method(), jvms, InliningResult::SUCCESS, "late inline succeeded");
|
||||
}
|
||||
|
||||
// Capture any exceptional control flow
|
||||
GraphKit kit(new_jvms);
|
||||
|
||||
@ -782,6 +759,8 @@ class LateInlineBoxingCallGenerator : public LateInlineCallGenerator {
|
||||
return new_jvms;
|
||||
}
|
||||
|
||||
virtual bool is_boxing_late_inline() const { return true; }
|
||||
|
||||
virtual CallGenerator* with_call_node(CallNode* call) {
|
||||
LateInlineBoxingCallGenerator* cg = new LateInlineBoxingCallGenerator(method(), _inline_cg);
|
||||
cg->set_call_node(call->as_CallStaticJava());
|
||||
@ -810,6 +789,8 @@ class LateInlineVectorReboxingCallGenerator : public LateInlineCallGenerator {
|
||||
return new_jvms;
|
||||
}
|
||||
|
||||
virtual bool is_vector_reboxing_late_inline() const { return true; }
|
||||
|
||||
virtual CallGenerator* with_call_node(CallNode* call) {
|
||||
LateInlineVectorReboxingCallGenerator* cg = new LateInlineVectorReboxingCallGenerator(method(), _inline_cg);
|
||||
cg->set_call_node(call->as_CallStaticJava());
|
||||
@ -875,7 +856,6 @@ CallGenerator* CallGenerator::for_guarded_call(ciKlass* guarded_receiver,
|
||||
|
||||
JVMState* PredictedCallGenerator::generate(JVMState* jvms) {
|
||||
GraphKit kit(jvms);
|
||||
kit.C->print_inlining_update(this);
|
||||
PhaseGVN& gvn = kit.gvn();
|
||||
// We need an explicit receiver null_check before checking its type.
|
||||
// We share a map with the caller, so his JVMS gets adjusted.
|
||||
@ -1048,8 +1028,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
const int vtable_index = Method::invalid_vtable_index;
|
||||
|
||||
if (!ciMethod::is_consistent_info(callee, target)) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"signatures mismatch");
|
||||
print_inlining_failure(C, callee, jvms, "signatures mismatch");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1062,15 +1041,12 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
} else {
|
||||
assert(receiver->bottom_type() == TypePtr::NULL_PTR, "not a null: %s",
|
||||
Type::str(receiver->bottom_type()));
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"receiver is always null");
|
||||
print_inlining_failure(C, callee, jvms, "receiver is always null");
|
||||
}
|
||||
} else {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"receiver not constant");
|
||||
print_inlining_failure(C, callee, jvms, "receiver not constant");
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case vmIntrinsics::_linkToVirtual:
|
||||
case vmIntrinsics::_linkToStatic:
|
||||
@ -1085,8 +1061,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
ciMethod* target = oop_ptr->const_oop()->as_member_name()->get_vmtarget();
|
||||
|
||||
if (!ciMethod::is_consistent_info(callee, target)) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"signatures mismatch");
|
||||
print_inlining_failure(C, callee, jvms, "signatures mismatch");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1101,8 +1076,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
Node* recv = kit.argument(0);
|
||||
Node* casted_recv = kit.maybe_narrow_object_type(recv, signature->accessing_klass());
|
||||
if (casted_recv->is_top()) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"argument types mismatch");
|
||||
print_inlining_failure(C, callee, jvms, "argument types mismatch");
|
||||
return nullptr; // FIXME: effectively dead; issue a halt node instead
|
||||
} else if (casted_recv != recv) {
|
||||
kit.set_argument(0, casted_recv);
|
||||
@ -1115,8 +1089,7 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
Node* arg = kit.argument(receiver_skip + j);
|
||||
Node* casted_arg = kit.maybe_narrow_object_type(arg, t->as_klass());
|
||||
if (casted_arg->is_top()) {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"argument types mismatch");
|
||||
print_inlining_failure(C, callee, jvms, "argument types mismatch");
|
||||
return nullptr; // FIXME: effectively dead; issue a halt node instead
|
||||
} else if (casted_arg != arg) {
|
||||
kit.set_argument(receiver_skip + j, casted_arg);
|
||||
@ -1154,15 +1127,12 @@ CallGenerator* CallGenerator::for_method_handle_inline(JVMState* jvms, ciMethod*
|
||||
speculative_receiver_type);
|
||||
return cg;
|
||||
} else {
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"member_name not constant");
|
||||
print_inlining_failure(C, callee, jvms, "member_name not constant");
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case vmIntrinsics::_linkToNative:
|
||||
print_inlining_failure(C, callee, jvms->depth() - 1, jvms->bci(),
|
||||
"native call");
|
||||
case vmIntrinsics::_linkToNative:
|
||||
print_inlining_failure(C, callee, jvms, "native call");
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1410,7 +1380,6 @@ CallGenerator::for_uncommon_trap(ciMethod* m,
|
||||
|
||||
JVMState* UncommonTrapCallGenerator::generate(JVMState* jvms) {
|
||||
GraphKit kit(jvms);
|
||||
kit.C->print_inlining_update(this);
|
||||
// Take the trap with arguments pushed on the stack. (Cf. null_check_receiver).
|
||||
// Callsite signature can be different from actual method being called (i.e _linkTo* sites).
|
||||
// Use callsite signature always.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, 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
|
||||
@ -75,6 +75,8 @@ class CallGenerator : public ArenaObj {
|
||||
// same but for method handle calls
|
||||
virtual bool is_mh_late_inline() const { return false; }
|
||||
virtual bool is_string_late_inline() const { return false; }
|
||||
virtual bool is_boxing_late_inline() const { return false; }
|
||||
virtual bool is_vector_reboxing_late_inline() const { return false; }
|
||||
virtual bool is_virtual_late_inline() const { return false; }
|
||||
|
||||
// Replace the call with an inline version of the code
|
||||
@ -171,28 +173,14 @@ class CallGenerator : public ArenaObj {
|
||||
CallGenerator* cg);
|
||||
virtual Node* generate_predicate(JVMState* jvms, int predicate) { return nullptr; };
|
||||
|
||||
virtual void print_inlining_late(InliningResult result, const char* msg) { ShouldNotReachHere(); }
|
||||
|
||||
static void print_inlining(Compile* C, ciMethod* callee, int inline_level, int bci, const char* msg) {
|
||||
print_inlining_impl(C, callee, inline_level, bci, InliningResult::SUCCESS, msg);
|
||||
}
|
||||
|
||||
static void print_inlining_failure(Compile* C, ciMethod* callee, int inline_level, int bci, const char* msg) {
|
||||
print_inlining_impl(C, callee, inline_level, bci, InliningResult::FAILURE, msg);
|
||||
static void print_inlining_failure(Compile* C, ciMethod* callee, JVMState* jvms, const char* msg) {
|
||||
C->inline_printer()->record(callee, jvms, InliningResult::FAILURE, msg);
|
||||
C->log_inline_failure(msg);
|
||||
}
|
||||
|
||||
static bool is_inlined_method_handle_intrinsic(JVMState* jvms, ciMethod* m);
|
||||
static bool is_inlined_method_handle_intrinsic(ciMethod* caller, int bci, ciMethod* m);
|
||||
static bool is_inlined_method_handle_intrinsic(ciMethod* symbolic_info, ciMethod* m);
|
||||
|
||||
private:
|
||||
static void print_inlining_impl(Compile* C, ciMethod* callee, int inline_level, int bci,
|
||||
InliningResult result, const char* msg) {
|
||||
if (C->print_inlining()) {
|
||||
C->print_inlining(callee, inline_level, bci, result, msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -610,77 +610,75 @@ void Compile::print_ideal_ir(const char* phase_name) {
|
||||
// the continuation bci for on stack replacement.
|
||||
|
||||
|
||||
Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
Options options, DirectiveSet* directive)
|
||||
: Phase(Compiler),
|
||||
_compile_id(ci_env->compile_id()),
|
||||
_options(options),
|
||||
_method(target),
|
||||
_entry_bci(osr_bci),
|
||||
_ilt(nullptr),
|
||||
_stub_function(nullptr),
|
||||
_stub_name(nullptr),
|
||||
_stub_entry_point(nullptr),
|
||||
_max_node_limit(MaxNodeLimit),
|
||||
_post_loop_opts_phase(false),
|
||||
_allow_macro_nodes(true),
|
||||
_inlining_progress(false),
|
||||
_inlining_incrementally(false),
|
||||
_do_cleanup(false),
|
||||
_has_reserved_stack_access(target->has_reserved_stack_access()),
|
||||
Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
Options options, DirectiveSet* directive)
|
||||
: Phase(Compiler),
|
||||
_compile_id(ci_env->compile_id()),
|
||||
_options(options),
|
||||
_method(target),
|
||||
_entry_bci(osr_bci),
|
||||
_ilt(nullptr),
|
||||
_stub_function(nullptr),
|
||||
_stub_name(nullptr),
|
||||
_stub_entry_point(nullptr),
|
||||
_max_node_limit(MaxNodeLimit),
|
||||
_post_loop_opts_phase(false),
|
||||
_allow_macro_nodes(true),
|
||||
_inlining_progress(false),
|
||||
_inlining_incrementally(false),
|
||||
_do_cleanup(false),
|
||||
_has_reserved_stack_access(target->has_reserved_stack_access()),
|
||||
#ifndef PRODUCT
|
||||
_igv_idx(0),
|
||||
_trace_opto_output(directive->TraceOptoOutputOption),
|
||||
_igv_idx(0),
|
||||
_trace_opto_output(directive->TraceOptoOutputOption),
|
||||
#endif
|
||||
_has_method_handle_invokes(false),
|
||||
_clinit_barrier_on_entry(false),
|
||||
_stress_seed(0),
|
||||
_comp_arena(mtCompiler),
|
||||
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
|
||||
_env(ci_env),
|
||||
_directive(directive),
|
||||
_log(ci_env->log()),
|
||||
_first_failure_details(nullptr),
|
||||
_intrinsics (comp_arena(), 0, 0, nullptr),
|
||||
_macro_nodes (comp_arena(), 8, 0, nullptr),
|
||||
_parse_predicates (comp_arena(), 8, 0, nullptr),
|
||||
_template_assertion_predicate_opaqs (comp_arena(), 8, 0, nullptr),
|
||||
_expensive_nodes (comp_arena(), 8, 0, nullptr),
|
||||
_for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
|
||||
_unstable_if_traps (comp_arena(), 8, 0, nullptr),
|
||||
_coarsened_locks (comp_arena(), 8, 0, nullptr),
|
||||
_congraph(nullptr),
|
||||
NOT_PRODUCT(_igv_printer(nullptr) COMMA)
|
||||
_unique(0),
|
||||
_dead_node_count(0),
|
||||
_dead_node_list(comp_arena()),
|
||||
_node_arena_one(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena_two(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena(&_node_arena_one),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler),
|
||||
_initial_gvn(nullptr),
|
||||
_igvn_worklist(nullptr),
|
||||
_types(nullptr),
|
||||
_node_hash(nullptr),
|
||||
_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_string_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_boxing_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_vector_reboxing_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_late_inlines_pos(0),
|
||||
_number_of_mh_late_inlines(0),
|
||||
_oom(false),
|
||||
_print_inlining_stream(new (mtCompiler) stringStream()),
|
||||
_print_inlining_list(nullptr),
|
||||
_print_inlining_idx(0),
|
||||
_print_inlining_output(nullptr),
|
||||
_replay_inline_data(nullptr),
|
||||
_java_calls(0),
|
||||
_inner_loops(0),
|
||||
_interpreter_frame_size(0),
|
||||
_output(nullptr)
|
||||
_has_method_handle_invokes(false),
|
||||
_clinit_barrier_on_entry(false),
|
||||
_stress_seed(0),
|
||||
_comp_arena(mtCompiler),
|
||||
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
|
||||
_env(ci_env),
|
||||
_directive(directive),
|
||||
_log(ci_env->log()),
|
||||
_first_failure_details(nullptr),
|
||||
_intrinsics(comp_arena(), 0, 0, nullptr),
|
||||
_macro_nodes(comp_arena(), 8, 0, nullptr),
|
||||
_parse_predicates(comp_arena(), 8, 0, nullptr),
|
||||
_template_assertion_predicate_opaqs(comp_arena(), 8, 0, nullptr),
|
||||
_expensive_nodes(comp_arena(), 8, 0, nullptr),
|
||||
_for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
|
||||
_unstable_if_traps(comp_arena(), 8, 0, nullptr),
|
||||
_coarsened_locks(comp_arena(), 8, 0, nullptr),
|
||||
_congraph(nullptr),
|
||||
NOT_PRODUCT(_igv_printer(nullptr) COMMA)
|
||||
_unique(0),
|
||||
_dead_node_count(0),
|
||||
_dead_node_list(comp_arena()),
|
||||
_node_arena_one(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena_two(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena(&_node_arena_one),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler),
|
||||
_initial_gvn(nullptr),
|
||||
_igvn_worklist(nullptr),
|
||||
_types(nullptr),
|
||||
_node_hash(nullptr),
|
||||
_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_string_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_boxing_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_vector_reboxing_late_inlines(comp_arena(), 2, 0, nullptr),
|
||||
_late_inlines_pos(0),
|
||||
_number_of_mh_late_inlines(0),
|
||||
_oom(false),
|
||||
_replay_inline_data(nullptr),
|
||||
_inline_printer(this),
|
||||
_java_calls(0),
|
||||
_inner_loops(0),
|
||||
_interpreter_frame_size(0),
|
||||
_output(nullptr)
|
||||
#ifndef PRODUCT
|
||||
, _in_dump_cnt(0)
|
||||
,
|
||||
_in_dump_cnt(0)
|
||||
#endif
|
||||
{
|
||||
C = this;
|
||||
@ -743,7 +741,6 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
PhaseGVN gvn;
|
||||
set_initial_gvn(&gvn);
|
||||
|
||||
print_inlining_init();
|
||||
{ // Scope for timing the parser
|
||||
TracePhase tp(_t_parser);
|
||||
|
||||
@ -886,71 +883,68 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
|
||||
//------------------------------Compile----------------------------------------
|
||||
// Compile a runtime stub
|
||||
Compile::Compile( ciEnv* ci_env,
|
||||
TypeFunc_generator generator,
|
||||
address stub_function,
|
||||
const char *stub_name,
|
||||
int is_fancy_jump,
|
||||
bool pass_tls,
|
||||
bool return_pc,
|
||||
DirectiveSet* directive)
|
||||
: Phase(Compiler),
|
||||
_compile_id(0),
|
||||
_options(Options::for_runtime_stub()),
|
||||
_method(nullptr),
|
||||
_entry_bci(InvocationEntryBci),
|
||||
_stub_function(stub_function),
|
||||
_stub_name(stub_name),
|
||||
_stub_entry_point(nullptr),
|
||||
_max_node_limit(MaxNodeLimit),
|
||||
_post_loop_opts_phase(false),
|
||||
_allow_macro_nodes(true),
|
||||
_inlining_progress(false),
|
||||
_inlining_incrementally(false),
|
||||
_has_reserved_stack_access(false),
|
||||
Compile::Compile(ciEnv* ci_env,
|
||||
TypeFunc_generator generator,
|
||||
address stub_function,
|
||||
const char* stub_name,
|
||||
int is_fancy_jump,
|
||||
bool pass_tls,
|
||||
bool return_pc,
|
||||
DirectiveSet* directive)
|
||||
: Phase(Compiler),
|
||||
_compile_id(0),
|
||||
_options(Options::for_runtime_stub()),
|
||||
_method(nullptr),
|
||||
_entry_bci(InvocationEntryBci),
|
||||
_stub_function(stub_function),
|
||||
_stub_name(stub_name),
|
||||
_stub_entry_point(nullptr),
|
||||
_max_node_limit(MaxNodeLimit),
|
||||
_post_loop_opts_phase(false),
|
||||
_allow_macro_nodes(true),
|
||||
_inlining_progress(false),
|
||||
_inlining_incrementally(false),
|
||||
_has_reserved_stack_access(false),
|
||||
#ifndef PRODUCT
|
||||
_igv_idx(0),
|
||||
_trace_opto_output(directive->TraceOptoOutputOption),
|
||||
_igv_idx(0),
|
||||
_trace_opto_output(directive->TraceOptoOutputOption),
|
||||
#endif
|
||||
_has_method_handle_invokes(false),
|
||||
_clinit_barrier_on_entry(false),
|
||||
_stress_seed(0),
|
||||
_comp_arena(mtCompiler),
|
||||
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
|
||||
_env(ci_env),
|
||||
_directive(directive),
|
||||
_log(ci_env->log()),
|
||||
_first_failure_details(nullptr),
|
||||
_for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
|
||||
_congraph(nullptr),
|
||||
NOT_PRODUCT(_igv_printer(nullptr) COMMA)
|
||||
_unique(0),
|
||||
_dead_node_count(0),
|
||||
_dead_node_list(comp_arena()),
|
||||
_node_arena_one(mtCompiler),
|
||||
_node_arena_two(mtCompiler),
|
||||
_node_arena(&_node_arena_one),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler),
|
||||
_initial_gvn(nullptr),
|
||||
_igvn_worklist(nullptr),
|
||||
_types(nullptr),
|
||||
_node_hash(nullptr),
|
||||
_number_of_mh_late_inlines(0),
|
||||
_oom(false),
|
||||
_print_inlining_stream(new (mtCompiler) stringStream()),
|
||||
_print_inlining_list(nullptr),
|
||||
_print_inlining_idx(0),
|
||||
_print_inlining_output(nullptr),
|
||||
_replay_inline_data(nullptr),
|
||||
_java_calls(0),
|
||||
_inner_loops(0),
|
||||
_interpreter_frame_size(0),
|
||||
_output(nullptr),
|
||||
_has_method_handle_invokes(false),
|
||||
_clinit_barrier_on_entry(false),
|
||||
_stress_seed(0),
|
||||
_comp_arena(mtCompiler),
|
||||
_barrier_set_state(BarrierSet::barrier_set()->barrier_set_c2()->create_barrier_state(comp_arena())),
|
||||
_env(ci_env),
|
||||
_directive(directive),
|
||||
_log(ci_env->log()),
|
||||
_first_failure_details(nullptr),
|
||||
_for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
|
||||
_congraph(nullptr),
|
||||
NOT_PRODUCT(_igv_printer(nullptr) COMMA)
|
||||
_unique(0),
|
||||
_dead_node_count(0),
|
||||
_dead_node_list(comp_arena()),
|
||||
_node_arena_one(mtCompiler),
|
||||
_node_arena_two(mtCompiler),
|
||||
_node_arena(&_node_arena_one),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler),
|
||||
_initial_gvn(nullptr),
|
||||
_igvn_worklist(nullptr),
|
||||
_types(nullptr),
|
||||
_node_hash(nullptr),
|
||||
_number_of_mh_late_inlines(0),
|
||||
_oom(false),
|
||||
_replay_inline_data(nullptr),
|
||||
_inline_printer(this),
|
||||
_java_calls(0),
|
||||
_inner_loops(0),
|
||||
_interpreter_frame_size(0),
|
||||
_output(nullptr),
|
||||
#ifndef PRODUCT
|
||||
_in_dump_cnt(0),
|
||||
_in_dump_cnt(0),
|
||||
#endif
|
||||
_allowed_reasons(0) {
|
||||
_allowed_reasons(0) {
|
||||
C = this;
|
||||
|
||||
TraceTime t1(nullptr, &_t_totalCompilation, CITime, false);
|
||||
@ -991,7 +985,6 @@ Compile::Compile( ciEnv* ci_env,
|
||||
}
|
||||
|
||||
Compile::~Compile() {
|
||||
delete _print_inlining_stream;
|
||||
delete _first_failure_details;
|
||||
};
|
||||
|
||||
@ -2112,7 +2105,7 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) {
|
||||
CallGenerator* cg = _late_inlines.at(i);
|
||||
const char* msg = "live nodes > LiveNodeCountInliningCutoff";
|
||||
if (do_print_inlining) {
|
||||
cg->print_inlining_late(InliningResult::FAILURE, msg);
|
||||
inline_printer()->record(cg->method(), cg->call_node()->jvms(), InliningResult::FAILURE, msg);
|
||||
}
|
||||
log_late_inline_failure(cg, msg);
|
||||
}
|
||||
@ -2232,8 +2225,6 @@ void Compile::Optimize() {
|
||||
|
||||
ResourceMark rm;
|
||||
|
||||
print_inlining_reinit();
|
||||
|
||||
NOT_PRODUCT( verify_graph_edges(); )
|
||||
|
||||
print_method(PHASE_AFTER_PARSING, 1);
|
||||
@ -2484,8 +2475,6 @@ void Compile::Optimize() {
|
||||
|
||||
check_no_dead_use();
|
||||
|
||||
process_print_inlining();
|
||||
|
||||
// We will never use the NodeHash table any more. Clear it so that final_graph_reshaping does not have
|
||||
// to remove hashes to unlock nodes for modifications.
|
||||
C->node_hash()->clear();
|
||||
@ -4439,126 +4428,8 @@ Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt*
|
||||
return phase->transform(new ConvI2LNode(value, ltype));
|
||||
}
|
||||
|
||||
// The message about the current inlining is accumulated in
|
||||
// _print_inlining_stream and transferred into the _print_inlining_list
|
||||
// once we know whether inlining succeeds or not. For regular
|
||||
// inlining, messages are appended to the buffer pointed by
|
||||
// _print_inlining_idx in the _print_inlining_list. For late inlining,
|
||||
// a new buffer is added after _print_inlining_idx in the list. This
|
||||
// way we can update the inlining message for late inlining call site
|
||||
// when the inlining is attempted again.
|
||||
void Compile::print_inlining_init() {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
// print_inlining_init is actually called several times.
|
||||
print_inlining_reset();
|
||||
_print_inlining_list = new (comp_arena())GrowableArray<PrintInliningBuffer*>(comp_arena(), 1, 1, new PrintInliningBuffer());
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::print_inlining_reinit() {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
print_inlining_reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::print_inlining_reset() {
|
||||
_print_inlining_stream->reset();
|
||||
}
|
||||
|
||||
void Compile::print_inlining_commit() {
|
||||
assert(print_inlining() || print_intrinsics(), "PrintInlining off?");
|
||||
// Transfer the message from _print_inlining_stream to the current
|
||||
// _print_inlining_list buffer and clear _print_inlining_stream.
|
||||
_print_inlining_list->at(_print_inlining_idx)->ss()->write(_print_inlining_stream->base(), _print_inlining_stream->size());
|
||||
print_inlining_reset();
|
||||
}
|
||||
|
||||
void Compile::print_inlining_push() {
|
||||
// Add new buffer to the _print_inlining_list at current position
|
||||
_print_inlining_idx++;
|
||||
_print_inlining_list->insert_before(_print_inlining_idx, new PrintInliningBuffer());
|
||||
}
|
||||
|
||||
Compile::PrintInliningBuffer* Compile::print_inlining_current() {
|
||||
return _print_inlining_list->at(_print_inlining_idx);
|
||||
}
|
||||
|
||||
void Compile::print_inlining_update(CallGenerator* cg) {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
if (cg->is_late_inline()) {
|
||||
if (print_inlining_current()->cg() != cg &&
|
||||
(print_inlining_current()->cg() != nullptr ||
|
||||
print_inlining_current()->ss()->size() != 0)) {
|
||||
print_inlining_push();
|
||||
}
|
||||
print_inlining_commit();
|
||||
print_inlining_current()->set_cg(cg);
|
||||
} else {
|
||||
if (print_inlining_current()->cg() != nullptr) {
|
||||
print_inlining_push();
|
||||
}
|
||||
print_inlining_commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::print_inlining_move_to(CallGenerator* cg) {
|
||||
// We resume inlining at a late inlining call site. Locate the
|
||||
// corresponding inlining buffer so that we can update it.
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
for (int i = 0; i < _print_inlining_list->length(); i++) {
|
||||
if (_print_inlining_list->at(i)->cg() == cg) {
|
||||
_print_inlining_idx = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::print_inlining_update_delayed(CallGenerator* cg) {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
assert(_print_inlining_stream->size() > 0, "missing inlining msg");
|
||||
assert(print_inlining_current()->cg() == cg, "wrong entry");
|
||||
// replace message with new message
|
||||
_print_inlining_list->at_put(_print_inlining_idx, new PrintInliningBuffer());
|
||||
print_inlining_commit();
|
||||
print_inlining_current()->set_cg(cg);
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::print_inlining_assert_ready() {
|
||||
assert(!_print_inlining || _print_inlining_stream->size() == 0, "losing data");
|
||||
}
|
||||
|
||||
void Compile::process_print_inlining() {
|
||||
assert(_late_inlines.length() == 0, "not drained yet");
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
ResourceMark rm;
|
||||
stringStream ss;
|
||||
assert(_print_inlining_list != nullptr, "process_print_inlining should be called only once.");
|
||||
for (int i = 0; i < _print_inlining_list->length(); i++) {
|
||||
PrintInliningBuffer* pib = _print_inlining_list->at(i);
|
||||
ss.print("%s", pib->ss()->freeze());
|
||||
delete pib;
|
||||
DEBUG_ONLY(_print_inlining_list->at_put(i, nullptr));
|
||||
}
|
||||
// Reset _print_inlining_list, it only contains destructed objects.
|
||||
// It is on the arena, so it will be freed when the arena is reset.
|
||||
_print_inlining_list = nullptr;
|
||||
// _print_inlining_stream won't be used anymore, either.
|
||||
print_inlining_reset();
|
||||
size_t end = ss.size();
|
||||
_print_inlining_output = NEW_ARENA_ARRAY(comp_arena(), char, end+1);
|
||||
strncpy(_print_inlining_output, ss.freeze(), end+1);
|
||||
_print_inlining_output[end] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::dump_print_inlining() {
|
||||
if (_print_inlining_output != nullptr) {
|
||||
tty->print_raw(_print_inlining_output);
|
||||
}
|
||||
inline_printer()->print_on(tty);
|
||||
}
|
||||
|
||||
void Compile::log_late_inline(CallGenerator* cg) {
|
||||
|
||||
@ -46,6 +46,7 @@
|
||||
#include "runtime/vmThread.hpp"
|
||||
#include "utilities/ticks.hpp"
|
||||
#include "utilities/vmEnums.hpp"
|
||||
#include "opto/printinlining.hpp"
|
||||
|
||||
class AbstractLockNode;
|
||||
class AddPNode;
|
||||
@ -472,29 +473,6 @@ private:
|
||||
// "MemLimit" directive was specified and the memory limit was hit during compilation
|
||||
bool _oom;
|
||||
|
||||
// Inlining may not happen in parse order which would make
|
||||
// PrintInlining output confusing. Keep track of PrintInlining
|
||||
// pieces in order.
|
||||
class PrintInliningBuffer : public CHeapObj<mtCompiler> {
|
||||
private:
|
||||
CallGenerator* _cg;
|
||||
stringStream _ss;
|
||||
static const size_t default_stream_buffer_size = 128;
|
||||
|
||||
public:
|
||||
PrintInliningBuffer()
|
||||
: _cg(nullptr), _ss(default_stream_buffer_size) {}
|
||||
|
||||
stringStream* ss() { return &_ss; }
|
||||
CallGenerator* cg() { return _cg; }
|
||||
void set_cg(CallGenerator* cg) { _cg = cg; }
|
||||
};
|
||||
|
||||
stringStream* _print_inlining_stream;
|
||||
GrowableArray<PrintInliningBuffer*>* _print_inlining_list;
|
||||
int _print_inlining_idx;
|
||||
char* _print_inlining_output;
|
||||
|
||||
// Only keep nodes in the expensive node list that need to be optimized
|
||||
void cleanup_expensive_nodes(PhaseIterGVN &igvn);
|
||||
// Use for sorting expensive nodes to bring similar nodes together
|
||||
@ -506,37 +484,17 @@ private:
|
||||
|
||||
void* _replay_inline_data; // Pointer to data loaded from file
|
||||
|
||||
void print_inlining_init();
|
||||
void print_inlining_reinit();
|
||||
void print_inlining_commit();
|
||||
void print_inlining_push();
|
||||
PrintInliningBuffer* print_inlining_current();
|
||||
|
||||
void log_late_inline_failure(CallGenerator* cg, const char* msg);
|
||||
DEBUG_ONLY(bool _exception_backedge;)
|
||||
|
||||
void record_method_not_compilable_oom();
|
||||
|
||||
public:
|
||||
InlinePrinter _inline_printer;
|
||||
|
||||
public:
|
||||
void* barrier_set_state() const { return _barrier_set_state; }
|
||||
|
||||
stringStream* print_inlining_stream() {
|
||||
assert(print_inlining() || print_intrinsics(), "PrintInlining off?");
|
||||
return _print_inlining_stream;
|
||||
}
|
||||
|
||||
void print_inlining_update(CallGenerator* cg);
|
||||
void print_inlining_update_delayed(CallGenerator* cg);
|
||||
void print_inlining_move_to(CallGenerator* cg);
|
||||
void print_inlining_assert_ready();
|
||||
void print_inlining_reset();
|
||||
|
||||
void print_inlining(ciMethod* method, int inline_level, int bci, InliningResult result, const char* msg = nullptr) {
|
||||
stringStream ss;
|
||||
CompileTask::print_inlining_inner(&ss, method, inline_level, bci, result, msg);
|
||||
print_inlining_stream()->print("%s", ss.freeze());
|
||||
}
|
||||
InlinePrinter* inline_printer() { return &_inline_printer; }
|
||||
|
||||
#ifndef PRODUCT
|
||||
IdealGraphPrinter* igv_printer() { return _igv_printer; }
|
||||
@ -1100,7 +1058,6 @@ private:
|
||||
|
||||
void remove_useless_coarsened_locks(Unique_Node_List& useful);
|
||||
|
||||
void process_print_inlining();
|
||||
void dump_print_inlining();
|
||||
|
||||
bool over_inlining_cutoff() const {
|
||||
|
||||
@ -49,33 +49,40 @@
|
||||
#include "jfr/jfr.hpp"
|
||||
#endif
|
||||
|
||||
static void print_trace_type_profile(outputStream* out, int depth, ciKlass* prof_klass, int site_count, int receiver_count) {
|
||||
CompileTask::print_inline_indent(depth, out);
|
||||
static void print_trace_type_profile(outputStream* out, int depth, ciKlass* prof_klass, int site_count, int receiver_count,
|
||||
bool with_deco) {
|
||||
if (with_deco) {
|
||||
CompileTask::print_inline_indent(depth, out);
|
||||
}
|
||||
out->print(" \\-> TypeProfile (%d/%d counts) = ", receiver_count, site_count);
|
||||
prof_klass->name()->print_symbol_on(out);
|
||||
out->cr();
|
||||
if (with_deco) {
|
||||
out->cr();
|
||||
}
|
||||
}
|
||||
|
||||
static void trace_type_profile(Compile* C, ciMethod* method, int depth, int bci, ciMethod* prof_method,
|
||||
ciKlass* prof_klass, int site_count, int receiver_count) {
|
||||
static void trace_type_profile(Compile* C, ciMethod* method, JVMState* jvms,
|
||||
ciMethod* prof_method, ciKlass* prof_klass, int site_count, int receiver_count) {
|
||||
int depth = jvms->depth() - 1;
|
||||
int bci = jvms->bci();
|
||||
if (TraceTypeProfile || C->print_inlining()) {
|
||||
outputStream* out = tty;
|
||||
if (!C->print_inlining()) {
|
||||
if (!PrintOpto && !PrintCompilation) {
|
||||
method->print_short_name();
|
||||
tty->cr();
|
||||
}
|
||||
CompileTask::print_inlining_tty(prof_method, depth, bci, InliningResult::SUCCESS);
|
||||
print_trace_type_profile(tty, depth, prof_klass, site_count, receiver_count, true);
|
||||
} else {
|
||||
out = C->print_inlining_stream();
|
||||
auto stream = C->inline_printer()->record(method, jvms, InliningResult::SUCCESS);
|
||||
print_trace_type_profile(stream, depth, prof_klass, site_count, receiver_count, false);
|
||||
}
|
||||
print_trace_type_profile(out, depth, prof_klass, site_count, receiver_count);
|
||||
}
|
||||
|
||||
LogTarget(Debug, jit, inlining) lt;
|
||||
if (lt.is_enabled()) {
|
||||
LogStream ls(lt);
|
||||
print_trace_type_profile(&ls, depth, prof_klass, site_count, receiver_count);
|
||||
print_trace_type_profile(&ls, depth, prof_klass, site_count, receiver_count, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,17 +301,19 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
if (miss_cg != nullptr) {
|
||||
if (next_hit_cg != nullptr) {
|
||||
assert(speculative_receiver_type == nullptr, "shouldn't end up here if we used speculation");
|
||||
trace_type_profile(C, jvms->method(), jvms->depth() - 1, jvms->bci(), next_receiver_method, profile.receiver(1), site_count, profile.receiver_count(1));
|
||||
trace_type_profile(C, jvms->method(), jvms, next_receiver_method, profile.receiver(1), site_count, profile.receiver_count(1));
|
||||
// We don't need to record dependency on a receiver here and below.
|
||||
// Whenever we inline, the dependency is added by Parse::Parse().
|
||||
miss_cg = CallGenerator::for_predicted_call(profile.receiver(1), miss_cg, next_hit_cg, PROB_MAX);
|
||||
}
|
||||
if (miss_cg != nullptr) {
|
||||
ciKlass* k = speculative_receiver_type != nullptr ? speculative_receiver_type : profile.receiver(0);
|
||||
trace_type_profile(C, jvms->method(), jvms->depth() - 1, jvms->bci(), receiver_method, k, site_count, receiver_count);
|
||||
trace_type_profile(C, jvms->method(), jvms, receiver_method, k, site_count, receiver_count);
|
||||
float hit_prob = speculative_receiver_type != nullptr ? 1.0 : profile.receiver_prob(0);
|
||||
CallGenerator* cg = CallGenerator::for_predicted_call(k, miss_cg, hit_cg, hit_prob);
|
||||
if (cg != nullptr) return cg;
|
||||
if (cg != nullptr) {
|
||||
return cg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -371,9 +380,7 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool
|
||||
// Use a more generic tactic, like a simple call.
|
||||
if (call_does_dispatch) {
|
||||
const char* msg = "virtual call";
|
||||
if (C->print_inlining()) {
|
||||
print_inlining(callee, jvms->depth() - 1, jvms->bci(), InliningResult::FAILURE, msg);
|
||||
}
|
||||
C->inline_printer()->record(callee, jvms, InliningResult::FAILURE, msg);
|
||||
C->log_inline_failure(msg);
|
||||
if (IncrementalInlineVirtual && allow_inline) {
|
||||
return CallGenerator::for_late_inline_virtual(callee, vtable_index, prof_factor); // attempt to inline through virtual call later
|
||||
@ -512,8 +519,6 @@ void Parse::do_call() {
|
||||
// our contribution to it is cleaned up right here.
|
||||
kill_dead_locals();
|
||||
|
||||
C->print_inlining_assert_ready();
|
||||
|
||||
// Set frequently used booleans
|
||||
const bool is_virtual = bc() == Bytecodes::_invokevirtual;
|
||||
const bool is_virtual_or_interface = is_virtual || bc() == Bytecodes::_invokeinterface;
|
||||
|
||||
@ -119,9 +119,7 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) {
|
||||
const char *inline_msg = is_virtual() ? "(intrinsic, virtual)"
|
||||
: "(intrinsic)";
|
||||
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, InliningResult::SUCCESS, inline_msg);
|
||||
if (C->print_intrinsics() || C->print_inlining()) {
|
||||
C->print_inlining(callee, jvms->depth() - 1, bci, InliningResult::SUCCESS, inline_msg);
|
||||
}
|
||||
C->inline_printer()->record(callee, jvms, InliningResult::SUCCESS, inline_msg);
|
||||
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_worked);
|
||||
if (C->log()) {
|
||||
C->log()->elem("intrinsic id='%s'%s nodes='%d'",
|
||||
@ -131,7 +129,6 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) {
|
||||
}
|
||||
// Push the result from the inlined method onto the stack.
|
||||
kit.push_result();
|
||||
C->print_inlining_update(this);
|
||||
return kit.transfer_exceptions_into_jvms();
|
||||
}
|
||||
|
||||
@ -147,9 +144,7 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) {
|
||||
: "failed to inline (intrinsic), method not annotated";
|
||||
}
|
||||
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, InliningResult::FAILURE, msg);
|
||||
if (C->print_intrinsics() || C->print_inlining()) {
|
||||
C->print_inlining(callee, jvms->depth() - 1, bci, InliningResult::FAILURE, msg);
|
||||
}
|
||||
C->inline_printer()->record(callee, jvms, InliningResult::FAILURE, msg);
|
||||
} else {
|
||||
// Root compile
|
||||
ResourceMark rm;
|
||||
@ -164,7 +159,6 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) {
|
||||
}
|
||||
}
|
||||
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed);
|
||||
C->print_inlining_update(this);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -190,9 +184,8 @@ Node* LibraryIntrinsic::generate_predicate(JVMState* jvms, int predicate) {
|
||||
const char *inline_msg = is_virtual() ? "(intrinsic, virtual, predicate)"
|
||||
: "(intrinsic, predicate)";
|
||||
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, InliningResult::SUCCESS, inline_msg);
|
||||
if (C->print_intrinsics() || C->print_inlining()) {
|
||||
C->print_inlining(callee, jvms->depth() - 1, bci, InliningResult::SUCCESS, inline_msg);
|
||||
}
|
||||
C->inline_printer()->record(callee, jvms, InliningResult::SUCCESS, inline_msg);
|
||||
|
||||
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_worked);
|
||||
if (C->log()) {
|
||||
C->log()->elem("predicate_intrinsic id='%s'%s nodes='%d'",
|
||||
@ -208,9 +201,7 @@ Node* LibraryIntrinsic::generate_predicate(JVMState* jvms, int predicate) {
|
||||
// Not a root compile.
|
||||
const char* msg = "failed to generate predicate for intrinsic";
|
||||
CompileTask::print_inlining_ul(kit.callee(), jvms->depth() - 1, bci, InliningResult::FAILURE, msg);
|
||||
if (C->print_intrinsics() || C->print_inlining()) {
|
||||
C->print_inlining(kit.callee(), jvms->depth() - 1, bci, InliningResult::FAILURE, msg);
|
||||
}
|
||||
C->inline_printer()->record(kit.callee(), jvms, InliningResult::FAILURE, msg);
|
||||
} else {
|
||||
// Root compile
|
||||
ResourceMark rm;
|
||||
@ -220,9 +211,7 @@ Node* LibraryIntrinsic::generate_predicate(JVMState* jvms, int predicate) {
|
||||
is_virtual() ? " (virtual)" : "", bci);
|
||||
const char *msg = msg_stream.freeze();
|
||||
log_debug(jit, inlining)("%s", msg);
|
||||
if (C->print_intrinsics() || C->print_inlining()) {
|
||||
C->print_inlining_stream()->print("%s", msg);
|
||||
}
|
||||
C->inline_printer()->record(kit.callee(), jvms, InliningResult::FAILURE, msg);
|
||||
}
|
||||
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed);
|
||||
return nullptr;
|
||||
|
||||
@ -547,10 +547,6 @@ Node *Node::clone() const {
|
||||
if (cg != nullptr) {
|
||||
CallGenerator* cloned_cg = cg->with_call_node(n->as_Call());
|
||||
n->as_Call()->set_generator(cloned_cg);
|
||||
|
||||
C->print_inlining_assert_ready();
|
||||
C->print_inlining_move_to(cg);
|
||||
C->print_inlining_update(cloned_cg);
|
||||
}
|
||||
}
|
||||
if (n->is_SafePoint()) {
|
||||
|
||||
@ -75,7 +75,7 @@ protected:
|
||||
bool& should_delay);
|
||||
bool should_inline(ciMethod* callee_method,
|
||||
ciMethod* caller_method,
|
||||
int caller_bci,
|
||||
JVMState* caller_jvms,
|
||||
bool& should_delay,
|
||||
ciCallProfile& profile);
|
||||
bool should_not_inline(ciMethod* callee_method,
|
||||
@ -87,8 +87,7 @@ protected:
|
||||
ciMethod* caller_method,
|
||||
int caller_bci,
|
||||
ciCallProfile& profile);
|
||||
void print_inlining(ciMethod* callee_method, int caller_bci,
|
||||
ciMethod* caller_method, bool success) const;
|
||||
void print_inlining(ciMethod* callee_method, JVMState* jvm, bool success) const;
|
||||
|
||||
InlineTree* caller_tree() const { return _caller_tree; }
|
||||
InlineTree* callee_at(int bci, ciMethod* m) const;
|
||||
|
||||
109
src/hotspot/share/opto/printinlining.cpp
Normal file
109
src/hotspot/share/opto/printinlining.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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 "opto/printinlining.hpp"
|
||||
#include "opto/callnode.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
|
||||
bool InlinePrinter::is_enabled() const {
|
||||
return C->print_intrinsics() || C->print_inlining();
|
||||
}
|
||||
|
||||
outputStream* InlinePrinter::record(ciMethod* callee, JVMState* state, InliningResult result, const char* msg) {
|
||||
if (!is_enabled()) {
|
||||
return &_nullStream;
|
||||
}
|
||||
outputStream* stream = locate(state, callee)->add(result);
|
||||
if (msg != nullptr) {
|
||||
stream->print("%s", msg);
|
||||
}
|
||||
return stream; // Pointer stays valid, see IPInlineSite::add()
|
||||
}
|
||||
|
||||
void InlinePrinter::print_on(outputStream* tty) const {
|
||||
if (!is_enabled()) {
|
||||
return;
|
||||
}
|
||||
_root.dump(tty, -1);
|
||||
}
|
||||
|
||||
InlinePrinter::IPInlineSite* InlinePrinter::locate(JVMState* state, ciMethod* callee) {
|
||||
auto growableArray = new GrowableArrayCHeap<JVMState*, mtCompiler>(2);
|
||||
|
||||
while (state != nullptr) {
|
||||
growableArray->push(state);
|
||||
state = state->caller();
|
||||
}
|
||||
|
||||
IPInlineSite* site = &_root;
|
||||
for (int i = growableArray->length() - 1; i >= 0; i--) {
|
||||
site = &site->at_bci(growableArray->at(i)->bci(), i == 0 ? callee : nullptr);
|
||||
}
|
||||
|
||||
delete growableArray;
|
||||
|
||||
return site;
|
||||
}
|
||||
|
||||
InlinePrinter::IPInlineSite& InlinePrinter::IPInlineSite::at_bci(int bci, ciMethod* callee) {
|
||||
auto find_result = _children.find(bci);
|
||||
IPInlineSite& child = find_result.node->val();
|
||||
|
||||
if (find_result.new_node) {
|
||||
assert(callee != nullptr, "an inline call is missing in the chain up to the root");
|
||||
child.set_source(callee, bci);
|
||||
} else { // We already saw a call at this site before
|
||||
if (callee != nullptr && callee != child._method) {
|
||||
outputStream* stream = child.add(InliningResult::SUCCESS);
|
||||
stream->print("callee changed to ");
|
||||
CompileTask::print_inline_inner_method_info(stream, callee);
|
||||
}
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
outputStream* InlinePrinter::IPInlineSite::add(InliningResult result) {
|
||||
_attempts.push(IPInlineAttempt(result));
|
||||
return _attempts.last().make_stream();
|
||||
}
|
||||
|
||||
void InlinePrinter::IPInlineSite::dump(outputStream* tty, int level) const {
|
||||
assert(_bci != -999, "trying to dump site without source");
|
||||
|
||||
if (_attempts.is_nonempty()) {
|
||||
CompileTask::print_inlining_header(tty, _method, level, _bci);
|
||||
}
|
||||
for (int i = 0; i < _attempts.length(); i++) {
|
||||
CompileTask::print_inlining_inner_message(tty, _attempts.at(i).result(), _attempts.at(i).stream()->base());
|
||||
}
|
||||
if (_attempts.is_nonempty()) {
|
||||
tty->cr();
|
||||
}
|
||||
|
||||
_children.visit_in_order([=](auto* node) {
|
||||
node->val().dump(tty, level + 1);
|
||||
});
|
||||
}
|
||||
139
src/hotspot/share/opto/printinlining.hpp
Normal file
139
src/hotspot/share/opto/printinlining.hpp
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PRINTINLINING_HPP
|
||||
#define PRINTINLINING_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "nmt/nmtTreap.hpp"
|
||||
|
||||
class JVMState;
|
||||
class ciMethod;
|
||||
class Compile;
|
||||
enum class InliningResult;
|
||||
|
||||
// If not enabled, all method calls are no-ops.
|
||||
class InlinePrinter {
|
||||
private:
|
||||
class IPInlineAttempt {
|
||||
InliningResult _result;
|
||||
stringStream* _stream;
|
||||
|
||||
public:
|
||||
IPInlineAttempt() : _stream(nullptr) {}
|
||||
|
||||
IPInlineAttempt(InliningResult result) : _result(result), _stream(nullptr) {}
|
||||
|
||||
InliningResult result() const { return _result; }
|
||||
|
||||
stringStream* make_stream() {
|
||||
assert(_stream == nullptr, "stream already exists");
|
||||
_stream = new (mtCompiler) stringStream;
|
||||
return _stream;
|
||||
}
|
||||
|
||||
stringStream* stream() const {
|
||||
assert(_stream != nullptr, "stream was not created yet!");
|
||||
return _stream;
|
||||
}
|
||||
|
||||
void deallocate_stream() {
|
||||
delete _stream;
|
||||
_stream = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct Cmp {
|
||||
static int cmp(int a, int b) {
|
||||
return a - b;
|
||||
}
|
||||
};
|
||||
|
||||
class IPInlineSite : public CHeapObj<mtCompiler> {
|
||||
private:
|
||||
ciMethod* _method;
|
||||
int _bci;
|
||||
GrowableArrayCHeap<IPInlineAttempt, mtCompiler> _attempts;
|
||||
TreapCHeap<int, IPInlineSite, Cmp> _children;
|
||||
|
||||
public:
|
||||
IPInlineSite(ciMethod* method, int bci) : _method(method), _bci(bci) {}
|
||||
|
||||
IPInlineSite() : _method(nullptr), _bci(-999) {}
|
||||
|
||||
~IPInlineSite() {
|
||||
// Since GrowableArrayCHeap uses copy semantics to resize itself we
|
||||
// cannot free the stream inside IPInlineAttempt's destructor unfortunately
|
||||
// and have to take care of this here instead.
|
||||
for (int i = 0; i < _attempts.length(); i++) {
|
||||
_attempts.at(i).deallocate_stream();
|
||||
}
|
||||
}
|
||||
|
||||
void set_source(ciMethod* method, int bci) {
|
||||
_method = method;
|
||||
_bci = bci;
|
||||
}
|
||||
|
||||
// Finds the node for an inline attempt that occurred inside this inline.
|
||||
// If this is a new site, provide the callee otherwise null.
|
||||
// Returned reference is valid until any at_bci is called with non-null callee.
|
||||
IPInlineSite& at_bci(int bci, ciMethod* callee);
|
||||
// The returned pointer stays valid until InlinePrinter is destructed.
|
||||
outputStream* add(InliningResult result);
|
||||
|
||||
void dump(outputStream* tty, int level) const;
|
||||
};
|
||||
|
||||
bool is_enabled() const;
|
||||
|
||||
Compile* C;
|
||||
|
||||
// In case print inline is disabled, this null stream is returned from ::record()
|
||||
nullStream _nullStream;
|
||||
|
||||
// Locates the IPInlineSite node that corresponds to this JVM state.
|
||||
// state may be null. In this case, the root node is returned.
|
||||
// If this is a new site, provide the callee otherwise null.
|
||||
// Returned pointer is valid until InlinePrinter is destructed.
|
||||
IPInlineSite* locate(JVMState* state, ciMethod* callee);
|
||||
|
||||
IPInlineSite _root{nullptr, 0};
|
||||
|
||||
public:
|
||||
InlinePrinter(Compile* compile) : C(compile) {}
|
||||
|
||||
// Saves the result of an inline attempt of method at state.
|
||||
// An optional string message with more details that is copied to the stream for this attempt. Pointer is not captured.
|
||||
// Returns an output stream which stores the message associated with this attempt. The buffer stays valid until InlinePrinter is destructed.
|
||||
// You can print arbitrary information to this stream but do not add line breaks, as this will break formatting.
|
||||
outputStream* record(ciMethod* callee, JVMState* state, InliningResult result, const char* msg = nullptr);
|
||||
|
||||
// Prints all collected inlining information to the given output stream.
|
||||
void print_on(outputStream* tty) const;
|
||||
};
|
||||
|
||||
#endif // PRINTINLINING_HPP
|
||||
@ -86,31 +86,31 @@ public abstract class InliningBase extends DumpReplayBase {
|
||||
}
|
||||
|
||||
public boolean isNormalInline() {
|
||||
return reason.equals("inline (hot)");
|
||||
return reason.startsWith("inline (hot)");
|
||||
}
|
||||
|
||||
public boolean isForcedByReplay() {
|
||||
return reason.equals("force inline by ciReplay");
|
||||
return reason.startsWith("force inline by ciReplay");
|
||||
}
|
||||
|
||||
public boolean isDisallowedByReplay() {
|
||||
return reason.equals("failed to inline: disallowed by ciReplay");
|
||||
return reason.startsWith("failed to inline: disallowed by ciReplay");
|
||||
}
|
||||
|
||||
public boolean isUnloadedSignatureClasses() {
|
||||
return reason.equals("failed to inline: unloaded signature classes");
|
||||
return reason.startsWith("failed to inline: unloaded signature classes");
|
||||
}
|
||||
|
||||
public boolean isForcedIncrementalInlineByReplay() {
|
||||
return reason.equals("force (incremental) inline by ciReplay");
|
||||
return reason.startsWith("force (incremental) inline by ciReplay");
|
||||
}
|
||||
|
||||
public boolean isForcedInline() {
|
||||
return reason.equals("force inline by annotation");
|
||||
return reason.startsWith("force inline by annotation");
|
||||
}
|
||||
|
||||
public boolean isTooDeep() {
|
||||
return reason.equals("failed to inline: inlining too deep");
|
||||
return reason.startsWith("failed to inline: inlining too deep");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
101
test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java
Normal file
101
test/hotspot/jtreg/compiler/inlining/LateInlinePrinting.java
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2024, 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.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8319850
|
||||
* @summary PrintInlining should print which methods are late inlines
|
||||
* @modules java.base/jdk.internal.misc
|
||||
* @library /test/lib
|
||||
* @requires vm.flagless
|
||||
*
|
||||
* @run driver compiler.inlining.LateInlinePrinting
|
||||
*/
|
||||
|
||||
package compiler.inlining;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class LateInlinePrinting {
|
||||
public static class TestLateInlining {
|
||||
public static void main(String[] args) {
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
test1();
|
||||
test2();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test1() {
|
||||
test3();
|
||||
testFailInline();
|
||||
testFailInline();
|
||||
test2();
|
||||
}
|
||||
|
||||
private static void test2() {
|
||||
inlined1();
|
||||
inlined2();
|
||||
}
|
||||
|
||||
private static void test3() {}
|
||||
|
||||
private static void testFailInline() {}
|
||||
|
||||
private static void inlined1() {}
|
||||
|
||||
private static void inlined2() {}
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-XX:-TieredCompilation", "-XX:-UseOnStackReplacement", "-XX:-BackgroundCompilation",
|
||||
"-XX:+PrintCompilation",
|
||||
"-XX:CompileCommand=compileonly,compiler.inlining.LateInlinePrinting$TestLateInlining::test1",
|
||||
"-XX:CompileCommand=compileonly,compiler.inlining.LateInlinePrinting$TestLateInlining::test2",
|
||||
"-XX:CompileCommand=quiet", "-XX:+PrintInlining", "-XX:+AlwaysIncrementalInline",
|
||||
"-XX:CompileCommand=dontinline,compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline",
|
||||
TestLateInlining.class.getName()
|
||||
);
|
||||
|
||||
OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
|
||||
analyzer.shouldHaveExitValue(0);
|
||||
|
||||
analyzer.shouldContain("""
|
||||
compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes)
|
||||
@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded
|
||||
@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded
|
||||
""");
|
||||
analyzer.shouldContain("""
|
||||
compiler.inlining.LateInlinePrinting$TestLateInlining::test1 (13 bytes)
|
||||
@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::test3 (1 bytes) inline (hot) late inline succeeded
|
||||
@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand
|
||||
@ 6 compiler.inlining.LateInlinePrinting$TestLateInlining::testFailInline (1 bytes) failed to inline: disallowed by CompileCommand
|
||||
@ 9 compiler.inlining.LateInlinePrinting$TestLateInlining::test2 (7 bytes) inline (hot) late inline succeeded
|
||||
@ 0 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined1 (1 bytes) inline (hot) late inline succeeded
|
||||
@ 3 compiler.inlining.LateInlinePrinting$TestLateInlining::inlined2 (1 bytes) inline (hot) late inline succeeded
|
||||
""");
|
||||
}
|
||||
}
|
||||
@ -45,12 +45,12 @@ public class TestDuplicatedLateInliningOutput {
|
||||
public static void main(String[] args) throws Exception {
|
||||
test(
|
||||
NonConstantReceiverLauncher.class,
|
||||
"@ (\\d+)\\s+java\\.lang\\.invoke\\.LambdaForm\\$DMH\\/0x[0-9a-f]+::invokeStatic \\(\\d+ bytes\\)\\s+force inline by annotation",
|
||||
"@ (\\d+)\\s+java\\.lang\\.invoke\\.MethodHandle::invokeBasic\\(\\)V \\(\\d+ bytes\\)\\s+failed to inline: receiver not constant\\s+callee changed to\\s+java\\.lang\\.invoke\\.LambdaForm\\$DMH\\/0x[0-9a-f]+::invokeStatic \\(\\d+ bytes\\)\\s+force inline by annotation\\s+late inline succeeded \\(method handle\\)",
|
||||
"@ (\\d+)\\s+java\\.lang\\.invoke\\.MethodHandle::invokeBasic\\(\\)V \\(\\d+ bytes\\)\\s+failed to inline: receiver not constant");
|
||||
|
||||
test(
|
||||
VirtualCallLauncher.class,
|
||||
"@ (\\d+)\\s+compiler\\.inlining\\.TestDuplicatedLateInliningOutput\\$VirtualCallLauncher\\$B::lateInlined2 \\(\\d+ bytes\\)\\s+inline \\(hot\\)",
|
||||
"@ (\\d+)\\s+compiler\\.inlining\\.TestDuplicatedLateInliningOutput\\$VirtualCallLauncher\\$A::lateInlined2 \\(\\d+ bytes\\)\\s+failed to inline: virtual call\\s+callee changed to\\s+\\s+compiler\\.inlining\\.TestDuplicatedLateInliningOutput\\$VirtualCallLauncher\\$B::lateInlined2 \\(\\d+ bytes\\)\\s+inline \\(hot\\)\\s+late inline succeeded",
|
||||
"@ (\\d+)\\s+compiler\\.inlining\\.TestDuplicatedLateInliningOutput\\$VirtualCallLauncher\\$A::lateInlined2 \\(\\d+ bytes\\)\\s+failed to inline: virtual call"
|
||||
);
|
||||
}
|
||||
@ -75,7 +75,7 @@ public class TestDuplicatedLateInliningOutput {
|
||||
int index = IntStream.range(0, lines.size())
|
||||
.filter(i -> lines.get(i).trim().matches(pattern1))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new Exception("No inlining found"));
|
||||
.orElseThrow(() -> new Exception("No inlining found" + pattern1));
|
||||
|
||||
if (lines.get(index - 1).trim().matches(pattern2)) {
|
||||
throw new Exception("Both failure and success message found");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user