mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8360389: Support printing from C2 compiled code
Reviewed-by: kvn, thartmann, mhaessig
This commit is contained in:
parent
e783c524c1
commit
07549f3e15
@ -5378,3 +5378,158 @@ Node* Compile::narrow_value(BasicType bt, Node* value, const Type* type, PhaseGV
|
||||
void Compile::record_method_not_compilable_oom() {
|
||||
record_method_not_compilable(CompilationMemoryStatistic::failure_reason_memlimit());
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Collects all the control inputs from nodes on the worklist and from their data dependencies
|
||||
static void find_candidate_control_inputs(Unique_Node_List& worklist, Unique_Node_List& candidates) {
|
||||
// Follow non-control edges until we reach CFG nodes
|
||||
for (uint i = 0; i < worklist.size(); i++) {
|
||||
const Node* n = worklist.at(i);
|
||||
for (uint j = 0; j < n->req(); j++) {
|
||||
Node* in = n->in(j);
|
||||
if (in == nullptr || in->is_Root()) {
|
||||
continue;
|
||||
}
|
||||
if (in->is_CFG()) {
|
||||
if (in->is_Call()) {
|
||||
// The return value of a call is only available if the call did not result in an exception
|
||||
Node* control_proj_use = in->as_Call()->proj_out(TypeFunc::Control)->unique_out();
|
||||
if (control_proj_use->is_Catch()) {
|
||||
Node* fall_through = control_proj_use->as_Catch()->proj_out(CatchProjNode::fall_through_index);
|
||||
candidates.push(fall_through);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (in->is_Multi()) {
|
||||
// We got here by following data inputs so we should only have one control use
|
||||
// (no IfNode, etc)
|
||||
assert(!n->is_MultiBranch(), "unexpected node type: %s", n->Name());
|
||||
candidates.push(in->as_Multi()->proj_out(TypeFunc::Control));
|
||||
} else {
|
||||
candidates.push(in);
|
||||
}
|
||||
} else {
|
||||
worklist.push(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the candidate node that is a descendant to all the other candidates
|
||||
static Node* pick_control(Unique_Node_List& candidates) {
|
||||
Unique_Node_List worklist;
|
||||
worklist.copy(candidates);
|
||||
|
||||
// Traverse backwards through the CFG
|
||||
for (uint i = 0; i < worklist.size(); i++) {
|
||||
const Node* n = worklist.at(i);
|
||||
if (n->is_Root()) {
|
||||
continue;
|
||||
}
|
||||
for (uint j = 0; j < n->req(); j++) {
|
||||
// Skip backedge of loops to avoid cycles
|
||||
if (n->is_Loop() && j == LoopNode::LoopBackControl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Node* pred = n->in(j);
|
||||
if (pred != nullptr && pred != n && pred->is_CFG()) {
|
||||
worklist.push(pred);
|
||||
// if pred is an ancestor of n, then pred is an ancestor to at least one candidate
|
||||
candidates.remove(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(candidates.size() == 1, "unexpected control flow");
|
||||
return candidates.at(0);
|
||||
}
|
||||
|
||||
// Initialize a parameter input for a debug print call, using a placeholder for jlong and jdouble
|
||||
static void debug_print_init_parm(Node* call, Node* parm, Node* half, int* pos) {
|
||||
call->init_req((*pos)++, parm);
|
||||
const BasicType bt = parm->bottom_type()->basic_type();
|
||||
if (bt == T_LONG || bt == T_DOUBLE) {
|
||||
call->init_req((*pos)++, half);
|
||||
}
|
||||
}
|
||||
|
||||
Node* Compile::make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn,
|
||||
Node* parm0, Node* parm1,
|
||||
Node* parm2, Node* parm3,
|
||||
Node* parm4, Node* parm5,
|
||||
Node* parm6) const {
|
||||
Node* str_node = gvn->transform(new ConPNode(TypeRawPtr::make(((address) str))));
|
||||
const TypeFunc* type = OptoRuntime::debug_print_Type(parm0, parm1, parm2, parm3, parm4, parm5, parm6);
|
||||
Node* call = new CallLeafNode(type, call_addr, "debug_print", TypeRawPtr::BOTTOM);
|
||||
|
||||
// find the most suitable control input
|
||||
Unique_Node_List worklist, candidates;
|
||||
if (parm0 != nullptr) { worklist.push(parm0);
|
||||
if (parm1 != nullptr) { worklist.push(parm1);
|
||||
if (parm2 != nullptr) { worklist.push(parm2);
|
||||
if (parm3 != nullptr) { worklist.push(parm3);
|
||||
if (parm4 != nullptr) { worklist.push(parm4);
|
||||
if (parm5 != nullptr) { worklist.push(parm5);
|
||||
if (parm6 != nullptr) { worklist.push(parm6);
|
||||
/* close each nested if ===> */ } } } } } } }
|
||||
find_candidate_control_inputs(worklist, candidates);
|
||||
Node* control = nullptr;
|
||||
if (candidates.size() == 0) {
|
||||
control = C->start()->proj_out(TypeFunc::Control);
|
||||
} else {
|
||||
control = pick_control(candidates);
|
||||
}
|
||||
|
||||
// find all the previous users of the control we picked
|
||||
GrowableArray<Node*> users_of_control;
|
||||
for (DUIterator_Fast kmax, i = control->fast_outs(kmax); i < kmax; i++) {
|
||||
Node* use = control->fast_out(i);
|
||||
if (use->is_CFG() && use != control) {
|
||||
users_of_control.push(use);
|
||||
}
|
||||
}
|
||||
|
||||
// we do not actually care about IO and memory as it uses neither
|
||||
call->init_req(TypeFunc::Control, control);
|
||||
call->init_req(TypeFunc::I_O, top());
|
||||
call->init_req(TypeFunc::Memory, top());
|
||||
call->init_req(TypeFunc::FramePtr, C->start()->proj_out(TypeFunc::FramePtr));
|
||||
call->init_req(TypeFunc::ReturnAdr, top());
|
||||
|
||||
int pos = TypeFunc::Parms;
|
||||
call->init_req(pos++, str_node);
|
||||
if (parm0 != nullptr) { debug_print_init_parm(call, parm0, top(), &pos);
|
||||
if (parm1 != nullptr) { debug_print_init_parm(call, parm1, top(), &pos);
|
||||
if (parm2 != nullptr) { debug_print_init_parm(call, parm2, top(), &pos);
|
||||
if (parm3 != nullptr) { debug_print_init_parm(call, parm3, top(), &pos);
|
||||
if (parm4 != nullptr) { debug_print_init_parm(call, parm4, top(), &pos);
|
||||
if (parm5 != nullptr) { debug_print_init_parm(call, parm5, top(), &pos);
|
||||
if (parm6 != nullptr) { debug_print_init_parm(call, parm6, top(), &pos);
|
||||
/* close each nested if ===> */ } } } } } } }
|
||||
assert(call->in(call->req()-1) != nullptr, "must initialize all parms");
|
||||
|
||||
call = gvn->transform(call);
|
||||
Node* call_control_proj = gvn->transform(new ProjNode(call, TypeFunc::Control));
|
||||
|
||||
// rewire previous users to have the new call as control instead
|
||||
PhaseIterGVN* igvn = gvn->is_IterGVN();
|
||||
for (int i = 0; i < users_of_control.length(); i++) {
|
||||
Node* use = users_of_control.at(i);
|
||||
for (uint j = 0; j < use->req(); j++) {
|
||||
if (use->in(j) == control) {
|
||||
if (igvn != nullptr) {
|
||||
igvn->replace_input_of(use, j, call_control_proj);
|
||||
} else {
|
||||
gvn->hash_delete(use);
|
||||
use->set_req(j, call_control_proj);
|
||||
gvn->hash_insert(use);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return call;
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
|
||||
@ -1316,6 +1316,28 @@ public:
|
||||
BasicType out_bt, BasicType in_bt);
|
||||
|
||||
static Node* narrow_value(BasicType bt, Node* value, const Type* type, PhaseGVN* phase, bool transform_res);
|
||||
|
||||
#ifndef PRODUCT
|
||||
private:
|
||||
// getting rid of the template makes things easier
|
||||
Node* make_debug_print_call(const char* str, address call_addr, PhaseGVN* gvn,
|
||||
Node* parm0 = nullptr, Node* parm1 = nullptr,
|
||||
Node* parm2 = nullptr, Node* parm3 = nullptr,
|
||||
Node* parm4 = nullptr, Node* parm5 = nullptr,
|
||||
Node* parm6 = nullptr) const;
|
||||
|
||||
public:
|
||||
// Creates a CallLeafNode for a runtime call that prints a static string and the values of the
|
||||
// nodes passed as arguments.
|
||||
// This function also takes care of doing the necessary wiring, including finding a suitable control
|
||||
// based on the nodes that need to be printed. Note that passing nodes that have incompatible controls
|
||||
// is undefined behavior.
|
||||
template <typename... TT, typename... NN>
|
||||
Node* make_debug_print(const char* str, PhaseGVN* gvn, NN... in) {
|
||||
address call_addr = CAST_FROM_FN_PTR(address, SharedRuntime::debug_print<TT...>);
|
||||
return make_debug_print_call(str, call_addr, gvn, in...);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // SHARE_OPTO_COMPILE_HPP
|
||||
|
||||
@ -1780,6 +1780,62 @@ static const TypeFunc* make_osr_end_Type() {
|
||||
return TypeFunc::make(domain, range);
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
static void debug_print_convert_type(const Type** fields, int* argp, Node *parm) {
|
||||
const BasicType bt = parm->bottom_type()->basic_type();
|
||||
fields[(*argp)++] = Type::get_const_basic_type(bt);
|
||||
if (bt == T_LONG || bt == T_DOUBLE) {
|
||||
fields[(*argp)++] = Type::HALF;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_arg_cnt(const Node* parm, int* arg_cnt) {
|
||||
(*arg_cnt)++;
|
||||
const BasicType bt = parm->bottom_type()->basic_type();
|
||||
if (bt == T_LONG || bt == T_DOUBLE) {
|
||||
(*arg_cnt)++;
|
||||
}
|
||||
}
|
||||
|
||||
const TypeFunc* OptoRuntime::debug_print_Type(Node* parm0, Node* parm1,
|
||||
Node* parm2, Node* parm3,
|
||||
Node* parm4, Node* parm5,
|
||||
Node* parm6) {
|
||||
int argcnt = 1;
|
||||
if (parm0 != nullptr) { update_arg_cnt(parm0, &argcnt);
|
||||
if (parm1 != nullptr) { update_arg_cnt(parm1, &argcnt);
|
||||
if (parm2 != nullptr) { update_arg_cnt(parm2, &argcnt);
|
||||
if (parm3 != nullptr) { update_arg_cnt(parm3, &argcnt);
|
||||
if (parm4 != nullptr) { update_arg_cnt(parm4, &argcnt);
|
||||
if (parm5 != nullptr) { update_arg_cnt(parm5, &argcnt);
|
||||
if (parm6 != nullptr) { update_arg_cnt(parm6, &argcnt);
|
||||
/* close each nested if ===> */ } } } } } } }
|
||||
|
||||
// create input type (domain)
|
||||
const Type** fields = TypeTuple::fields(argcnt);
|
||||
int argp = TypeFunc::Parms;
|
||||
fields[argp++] = TypePtr::NOTNULL; // static string pointer
|
||||
|
||||
if (parm0 != nullptr) { debug_print_convert_type(fields, &argp, parm0);
|
||||
if (parm1 != nullptr) { debug_print_convert_type(fields, &argp, parm1);
|
||||
if (parm2 != nullptr) { debug_print_convert_type(fields, &argp, parm2);
|
||||
if (parm3 != nullptr) { debug_print_convert_type(fields, &argp, parm3);
|
||||
if (parm4 != nullptr) { debug_print_convert_type(fields, &argp, parm4);
|
||||
if (parm5 != nullptr) { debug_print_convert_type(fields, &argp, parm5);
|
||||
if (parm6 != nullptr) { debug_print_convert_type(fields, &argp, parm6);
|
||||
/* close each nested if ===> */ } } } } } } }
|
||||
|
||||
assert(argp == TypeFunc::Parms+argcnt, "correct decoding");
|
||||
const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields);
|
||||
|
||||
// no result type needed
|
||||
fields = TypeTuple::fields(1);
|
||||
fields[TypeFunc::Parms+0] = nullptr; // void
|
||||
const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields);
|
||||
return TypeFunc::make(domain, range);
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// register policy
|
||||
|
||||
|
||||
@ -737,6 +737,16 @@ private:
|
||||
return _dtrace_object_alloc_Type;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Signature for runtime calls in debug printing nodes, which depends on which nodes are actually passed
|
||||
// Note: we do not allow more than 7 node arguments as GraphKit::make_runtime_call only allows 8, and we need
|
||||
// one for the static string
|
||||
static const TypeFunc* debug_print_Type(Node* parm0 = nullptr, Node* parm1 = nullptr,
|
||||
Node* parm2 = nullptr, Node* parm3 = nullptr,
|
||||
Node* parm4 = nullptr, Node* parm5 = nullptr,
|
||||
Node* parm6 = nullptr);
|
||||
#endif // PRODUCT
|
||||
|
||||
private:
|
||||
static NamedCounter * volatile _named_counters;
|
||||
|
||||
|
||||
@ -264,6 +264,46 @@ void SharedRuntime::print_ic_miss_histogram() {
|
||||
tty->print_cr("Total IC misses: %7d", tot_misses);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef COMPILER2
|
||||
// Runtime methods for printf-style debug nodes (same printing format as fieldDescriptor::print_on_for)
|
||||
void SharedRuntime::debug_print_value(jboolean x) {
|
||||
tty->print_cr("boolean %d", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jbyte x) {
|
||||
tty->print_cr("byte %d", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jshort x) {
|
||||
tty->print_cr("short %d", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jchar x) {
|
||||
tty->print_cr("char %c %d", isprint(x) ? x : ' ', x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jint x) {
|
||||
tty->print_cr("int %d", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jlong x) {
|
||||
tty->print_cr("long " JLONG_FORMAT, x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jfloat x) {
|
||||
tty->print_cr("float %f", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(jdouble x) {
|
||||
tty->print_cr("double %lf", x);
|
||||
}
|
||||
|
||||
void SharedRuntime::debug_print_value(oopDesc* x) {
|
||||
x->print();
|
||||
}
|
||||
#endif // COMPILER2
|
||||
|
||||
#endif // PRODUCT
|
||||
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "memory/allStatic.hpp"
|
||||
#include "memory/metaspaceClosure.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "runtime/safepointVerifiers.hpp"
|
||||
#include "runtime/stubInfo.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
|
||||
@ -635,6 +636,39 @@ class SharedRuntime: AllStatic {
|
||||
static void print_call_statistics(uint64_t comp_total);
|
||||
static void print_ic_miss_histogram();
|
||||
|
||||
#ifdef COMPILER2
|
||||
// Runtime methods for printf-style debug nodes
|
||||
static void debug_print_value(jboolean x);
|
||||
static void debug_print_value(jbyte x);
|
||||
static void debug_print_value(jshort x);
|
||||
static void debug_print_value(jchar x);
|
||||
static void debug_print_value(jint x);
|
||||
static void debug_print_value(jlong x);
|
||||
static void debug_print_value(jfloat x);
|
||||
static void debug_print_value(jdouble x);
|
||||
static void debug_print_value(oopDesc* x);
|
||||
|
||||
template <typename T, typename... Rest>
|
||||
static void debug_print_rec(T arg, Rest... args) {
|
||||
debug_print_value(arg);
|
||||
debug_print_rec(args...);
|
||||
}
|
||||
|
||||
static void debug_print_rec() {}
|
||||
|
||||
// template is required here as we need to know the exact signature at compile-time
|
||||
template <typename... TT>
|
||||
static void debug_print(const char *str, TT... args) {
|
||||
// these three lines are the manual expansion of JRT_LEAF ... JRT_END, does not work well with templates
|
||||
DEBUG_ONLY(NoHandleMark __hm;)
|
||||
os::verify_stack_alignment();
|
||||
DEBUG_ONLY(NoSafepointVerifier __nsv;)
|
||||
|
||||
tty->print_cr("%s", str);
|
||||
debug_print_rec(args...);
|
||||
}
|
||||
#endif // COMPILER2
|
||||
|
||||
#endif // PRODUCT
|
||||
|
||||
static void print_statistics() PRODUCT_RETURN;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user