void Parse::jump_switch_ranges(Node* key_val, SwitchRange *lo, SwitchRange *hi, int switch_depth) {
Block* switch_block = block();
if (switch_depth == 0) {
assert(lo->lo() == min_jint, "initial range must exhaust Type::INT");
assert(hi->hi() == max_jint, "initial range must exhaust Type::INT");
#ifdef ASSERT
int successors_counted = 0; // block occurrences in [hi..lo]
int unique_successors = switch_block->num_successors();
for (int i = 0; i < unique_successors; i++) {
Block* target = switch_block->successor_at(i);
int successors_found = 0;
for (SwitchRange* p = lo; p <= hi; p++) {
if (p->dest() == target->start()) successors_found++;
}
assert(successors_found > 0, "successor must be known");
successors_counted += successors_found;
}
assert(successors_counted == (hi-lo)+1, "no unexpected successors");
#endif
jint min_val = min_jint;
jint max_val = max_jint;
const TypeInt* ti = key_val->bottom_type()->isa_int();
if (ti != NULL) {
min_val = ti->_lo;
max_val = ti->_hi;
assert(min_val <= max_val, "invalid int type");
}
while (lo->hi() < min_val) lo++;
if (lo->lo() < min_val) lo->setRange(min_val, lo->hi(), lo->dest(), lo->table_index());
while (hi->lo() > max_val) hi--;
if (hi->hi() > max_val) hi->setRange(hi->lo(), max_val, hi->dest(), hi->table_index());
}
#ifndef PRODUCT
if (switch_depth == 0) {
_max_switch_depth = 0;
_est_switch_depth = log2_intptr((hi-lo+1)-1)+1;
}
#endif
assert(lo <= hi, "must be a non-empty set of ranges");
if (lo == hi) {
jump_if_always_fork(lo->dest(), lo->table_index());
} else {
assert(lo->hi() == (lo+1)->lo()-1, "contiguous ranges");
assert(hi->lo() == (hi-1)->hi()+1, "contiguous ranges");
if (create_jump_tables(key_val, lo, hi)) return;
int nr = hi - lo + 1;
SwitchRange* mid = lo + nr/2;
if (nr > 3 && !mid->is_singleton() && (mid-1)->is_singleton()) mid--;
assert(lo < mid && mid <= hi, "good pivot choice");
assert(nr != 2 || mid == hi, "should pick higher of 2");
assert(nr != 3 || mid == hi-1, "should pick middle of 3");
Node *test_val = _gvn.intcon(mid->lo());
if (mid->is_singleton()) {
IfNode *iff_ne = jump_if_fork_int(key_val, test_val, BoolTest::ne);
jump_if_false_fork(iff_ne, mid->dest(), mid->table_index());
bool eq_test_only = (hi == lo+2 && hi->dest() == lo->dest());
if (eq_test_only) {
assert(mid == hi-1, "");
}
if (mid < hi && !eq_test_only) {
IfNode *iff_le = jump_if_fork_int(key_val, test_val, BoolTest::le);
Node *iftrue = _gvn.transform( new (C) IfTrueNode(iff_le) );
Node *iffalse = _gvn.transform( new (C) IfFalseNode(iff_le) );
{ PreserveJVMState pjvms(this);
set_control(iffalse);
jump_switch_ranges(key_val, mid+1, hi, switch_depth+1);
}
set_control(iftrue);
}
} else {
IfNode *iff_ge = jump_if_fork_int(key_val, test_val, BoolTest::ge);
if (mid == hi) {
jump_if_true_fork(iff_ge, mid->dest(), mid->table_index());
} else {
Node *iftrue = _gvn.transform( new (C) IfTrueNode(iff_ge) );
Node *iffalse = _gvn.transform( new (C) IfFalseNode(iff_ge) );
{ PreserveJVMState pjvms(this);
set_control(iftrue);
jump_switch_ranges(key_val, mid, hi, switch_depth+1);
}
set_control(iffalse);
}
}
jump_switch_ranges(key_val, lo, mid-1, switch_depth+1);
}
if (switch_depth == 0) {
int unique_successors = switch_block->num_successors();
for (int i = 0; i < unique_successors; i++) {
Block* target = switch_block->successor_at(i);
target->next_path_num();
}
}
#ifndef PRODUCT
_max_switch_depth = MAX2(switch_depth, _max_switch_depth);
if (TraceOptoParse && Verbose && WizardMode && switch_depth == 0) {
SwitchRange* r;
int nsing = 0;
for( r = lo; r <= hi; r++ ) {
if( r->is_singleton() ) nsing++;
}
tty->print(">>> ");
_method->print_short_name();
tty->print_cr(" switch decision tree");
tty->print_cr(" %d ranges (%d singletons), max_depth=%d, est_depth=%d",
(int) (hi-lo+1), nsing, _max_switch_depth, _est_switch_depth);
if (_max_switch_depth > _est_switch_depth) {
tty->print_cr("******** BAD SWITCH DEPTH ********");
}
tty->print(" ");
for( r = lo; r <= hi; r++ ) {
r->print();
}
tty->cr();
}
#endif
}
void Parse::modf() {
Node *f2 = pop();
Node *f1 = pop();
Node* c = make_runtime_call(RC_LEAF, OptoRuntime::modf_Type(),
CAST_FROM_FN_PTR(address, SharedRuntime::frem),
"frem", NULL, //no memory effects
f1, f2);
Node* res = _gvn.transform(new (C) ProjNode(c, TypeFunc::Parms + 0));
push(res);
}
void Parse::modd() {
Node *d2 = pop_pair();
Node *d1 = pop_pair();
Node* c = make_runtime_call(RC_LEAF, OptoRuntime::Math_DD_D_Type(),
CAST_FROM_FN_PTR(address, SharedRuntime::drem),
"drem", NULL, //no memory effects
d1, top(), d2, top());
Node* res_d = _gvn.transform(new (C) ProjNode(c, TypeFunc::Parms + 0));
#ifdef ASSERT
Node* res_top = _gvn.transform(new (C) ProjNode(c, TypeFunc::Parms + 1));
assert(res_top == top(), "second value must be top");
#endif
push_pair(res_d);
}
void Parse::l2f() {
Node* f2 = pop();
Node* f1 = pop();
Node* c = make_runtime_call(RC_LEAF, OptoRuntime::l2f_Type(),
CAST_FROM_FN_PTR(address, SharedRuntime::l2f),
"l2f", NULL, //no memory effects
f1, f2);
Node* res = _gvn.transform(new (C) ProjNode(c, TypeFunc::Parms + 0));
push(res);
}
void Parse::do_irem() {
zero_check_int(peek());
if (stopped()) return;
Node* b = pop();
Node* a = pop();
const Type *t = _gvn.type(b);
if (t != Type::TOP) {
const TypeInt *ti = t->is_int();
if (ti->is_con()) {
int divisor = ti->get_con();
if (divisor > 0 &&
(divisor & ~(divisor-1)) == divisor) {
Node *mask = _gvn.intcon((divisor - 1));
Node *zero = _gvn.intcon(0);
IfNode *ifff = jump_if_fork_int(a, zero, BoolTest::lt);
Node *iff = _gvn.transform( new (C) IfFalseNode(ifff) );
Node *ift = _gvn.transform( new (C) IfTrueNode (ifff) );
Node *reg = jump_if_join(ift, iff);
Node *phi = PhiNode::make(reg, NULL, TypeInt::INT);
Node *neg = _gvn.transform( new (C) SubINode(zero, a) );
Node *andn= _gvn.transform( new (C) AndINode(neg, mask) );
Node *negn= _gvn.transform( new (C) SubINode(zero, andn) );
phi->init_req(1, negn);
Node *andx = _gvn.transform( new (C) AndINode(a, mask) );
phi->init_req(2, andx);
push( _gvn.transform(phi) );
return;
}
}
}
push( _gvn.transform( new (C) ModINode(control(),a,b) ) );
}
void Parse::do_jsr() {
assert(bc() == Bytecodes::_jsr || bc() == Bytecodes::_jsr_w, "wrong bytecode");
int return_bci = iter().next_bci();
int jsr_bci = (bc() == Bytecodes::_jsr) ? iter().get_dest() : iter().get_far_dest();
profile_taken_branch(jsr_bci);
Block* target = successor_for_bci(jsr_bci);
const Type* ret_addr = target->peek();
assert(ret_addr->singleton(), "must be a constant (cloned jsr body)");
push(_gvn.makecon(ret_addr));
merge(jsr_bci);
}
void Parse::do_ret() {
assert(block()->num_successors() == 1, "a ret can only go one place now");
Block* target = block()->successor_at(0);
assert(!target->is_ready(), "our arrival must be expected");
profile_ret(target->flow()->start());
int pnum = target->next_path_num();
merge_common(target, pnum);
}
static bool has_injected_profile(BoolTest::mask btest, Node* test, int& taken, int& not_taken) {
if (btest != BoolTest::eq && btest != BoolTest::ne) {
return false;
}
if (test->is_Cmp() &&
test->in(1)->Opcode() == Op_ProfileBoolean) {
ProfileBooleanNode* profile = (ProfileBooleanNode*)test->in(1);
int false_cnt = profile->false_count();
int true_cnt = profile->true_count();
taken = (btest == BoolTest::eq) ? false_cnt : true_cnt;
not_taken = (btest == BoolTest::eq) ? true_cnt : false_cnt;
profile->consume();
return true;
}
return false;
}
float Parse::dynamic_branch_prediction(float &cnt, BoolTest::mask btest, Node* test) {
ResourceMark rm;
cnt = COUNT_UNKNOWN;
int taken = 0;
int not_taken = 0;
bool use_mdo = !has_injected_profile(btest, test, taken, not_taken);
if (use_mdo) {
ciMethodData* methodData = method()->method_data();
if (!methodData->is_mature()) return PROB_UNKNOWN;
ciProfileData* data = methodData->bci_to_data(bci());
if (data == NULL) {
return PROB_UNKNOWN;
}
if (!data->is_JumpData()) return PROB_UNKNOWN;
taken = data->as_JumpData()->taken();
not_taken = 0;
if (data->is_BranchData()) {
not_taken = data->as_BranchData()->not_taken();
}
taken = method()->scale_count(taken);
not_taken = method()->scale_count(not_taken);
}
if (taken < 0 || not_taken < 0 || taken + not_taken < 40) {
if (C->log() != NULL) {
C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d'", iter().get_dest(), taken, not_taken);
}
return PROB_UNKNOWN;
}
float sum = taken + not_taken;
if( block()->count() > 0 )
sum = block()->count();
cnt = sum / FreqCountInvocations;
float prob;
if( !taken )
prob = (0+PROB_MIN) / 2;
else if( !not_taken )
prob = (1+PROB_MAX) / 2;
else { // Compute probability of true path
prob = (float)taken / (float)(taken + not_taken);
if (prob > PROB_MAX) prob = PROB_MAX;
if (prob < PROB_MIN) prob = PROB_MIN;
}
assert((cnt > 0.0f) && (prob > 0.0f),
"Bad frequency assignment in if");
if (C->log() != NULL) {
const char* prob_str = NULL;
if (prob >= PROB_MAX) prob_str = (prob == PROB_MAX) ? "max" : "always";
if (prob <= PROB_MIN) prob_str = (prob == PROB_MIN) ? "min" : "never";
char prob_str_buf[30];
if (prob_str == NULL) {
sprintf(prob_str_buf, "%g", prob);
prob_str = prob_str_buf;
}
C->log()->elem("branch target_bci='%d' taken='%d' not_taken='%d' cnt='%g' prob='%s'",
iter().get_dest(), taken, not_taken, cnt, prob_str);
}
return prob;
}
float Parse::branch_prediction(float& cnt,
BoolTest::mask btest,
int target_bci,
Node* test) {
float prob = dynamic_branch_prediction(cnt, btest, test);
if (prob != PROB_UNKNOWN) return prob;
prob = PROB_FAIR; // Set default value
if (btest == BoolTest::eq) // Exactly equal test?
prob = PROB_STATIC_INFREQUENT; // Assume its relatively infrequent
else if (btest == BoolTest::ne)
prob = PROB_STATIC_FREQUENT; // Assume its relatively frequent
if (target_bci < bci()) {
if (is_osr_parse()) { // Could be a hot OSR'd loop; force deopt
ciMethodData* methodData = method()->method_data();
if (!methodData->is_empty()) {
ciProfileData* data = methodData->bci_to_data(bci());
if (data == NULL ||
(data->as_BranchData()->taken() + data->as_BranchData()->not_taken() == 0)) {
return PROB_UNKNOWN;
}
}
}
prob = PROB_STATIC_FREQUENT; // Likely to take backwards branch
}
assert(prob != PROB_UNKNOWN, "must have some guess at this point");
return prob;
}
bool Parse::seems_never_taken(float prob) const {
return prob < PROB_MIN;
}
bool Parse::seems_stable_comparison() const {
if (C->too_many_traps(method(), bci(), Deoptimization::Reason_unstable_if)) {
return false;
}
return true;
}
inline int Parse::repush_if_args() {
#ifndef PRODUCT
if (PrintOpto && WizardMode) {
tty->print("defending against excessive implicit null exceptions on %s @%d in ",
Bytecodes::name(iter().cur_bc()), iter().cur_bci());
method()->print_name(); tty->cr();
}
#endif
int bc_depth = - Bytecodes::depth(iter().cur_bc());
assert(bc_depth == 1 || bc_depth == 2, "only two kinds of branches");
DEBUG_ONLY(sync_jvms()); // argument(n) requires a synced jvms
assert(argument(0) != NULL, "must exist");
assert(bc_depth == 1 || argument(1) != NULL, "two must exist");
inc_sp(bc_depth);
return bc_depth;
}
void Parse::do_ifnull(BoolTest::mask btest, Node *c) {
int target_bci = iter().get_dest();
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
float cnt;
float prob = branch_prediction(cnt, btest, target_bci, c);
if (prob == PROB_UNKNOWN) {
#ifndef PRODUCT
if (PrintOpto && Verbose)
tty->print_cr("Never-taken edge stops compilation at bci %d",bci());
#endif
repush_if_args(); // to gather stats on loop
profile_taken_branch(target_bci, !ProfileInterpreter);
uncommon_trap(Deoptimization::Reason_unreached,
Deoptimization::Action_reinterpret,
NULL, "cold");
if (C->eliminate_boxing()) {
branch_block->next_path_num();
next_block->next_path_num();
}
return;
}
explicit_null_checks_inserted++;
Node *tst = _gvn.transform( new (C) BoolNode( c, btest ) );
assert(prob > 0.0f,"Bad probability in Parser");
IfNode *iff = create_and_xform_if( control(), tst, prob, cnt );
assert(iff->_prob > 0.0f,"Optimizer made bad probability in parser");
{ PreserveJVMState pjvms(this);
Node* iftrue = _gvn.transform( new (C) IfTrueNode (iff) );
set_control(iftrue);
if (stopped()) { // Path is dead?
explicit_null_checks_elided++;
if (C->eliminate_boxing()) {
branch_block->next_path_num();
}
} else { // Path is live.
profile_taken_branch(target_bci);
adjust_map_after_if(btest, c, prob, branch_block, next_block);
if (!stopped()) {
merge(target_bci);
}
}
}
Node* iffalse = _gvn.transform( new (C) IfFalseNode(iff) );
set_control(iffalse);
if (stopped()) { // Path is dead?
explicit_null_checks_elided++;
if (C->eliminate_boxing()) {
next_block->next_path_num();
}
} else { // Path is live.
profile_not_taken_branch();
adjust_map_after_if(BoolTest(btest).negate(), c, 1.0-prob,
next_block, branch_block);
}
}
void Parse::do_if(BoolTest::mask btest, Node* c) {
int target_bci = iter().get_dest();
Block* branch_block = successor_for_bci(target_bci);
Block* next_block = successor_for_bci(iter().next_bci());
float cnt;
float prob = branch_prediction(cnt, btest, target_bci, c);
float untaken_prob = 1.0 - prob;
if (prob == PROB_UNKNOWN) {
#ifndef PRODUCT
if (PrintOpto && Verbose)
tty->print_cr("Never-taken edge stops compilation at bci %d",bci());
#endif
repush_if_args(); // to gather stats on loop
profile_taken_branch(target_bci, !ProfileInterpreter);
uncommon_trap(Deoptimization::Reason_unreached,
Deoptimization::Action_reinterpret,
NULL, "cold");
if (C->eliminate_boxing()) {
branch_block->next_path_num();
next_block->next_path_num();
}
return;
}
assert(0.0f < prob && prob < 1.0f,"Bad probability in Parser");
bool taken_if_true = true;
if (!BoolTest(btest).is_canonical()) {
btest = BoolTest(btest).negate();
taken_if_true = false;
}
assert(btest != BoolTest::eq, "!= is the only canonical exact test");
Node* tst0 = new (C) BoolNode(c, btest);
Node* tst = _gvn.transform(tst0);
BoolTest::mask taken_btest = BoolTest::illegal;
BoolTest::mask untaken_btest = BoolTest::illegal;
if (tst->is_Bool()) {
if (tst != tst0) {
btest = tst->as_Bool()->_test._test;
if (!BoolTest(btest).is_canonical()) {
tst = _gvn.transform( tst->as_Bool()->negate(&_gvn) );
btest = tst->as_Bool()->_test._test;
assert(BoolTest(btest).is_canonical(), "sanity");
taken_if_true = !taken_if_true;
}
c = tst->in(1);
}
BoolTest::mask neg_btest = BoolTest(btest).negate();
taken_btest = taken_if_true ? btest : neg_btest;
untaken_btest = taken_if_true ? neg_btest : btest;
}
float true_prob = (taken_if_true ? prob : untaken_prob);
IfNode* iff = create_and_map_if(control(), tst, true_prob, cnt);
assert(iff->_prob > 0.0f,"Optimizer made bad probability in parser");
Node* taken_branch = new (C) IfTrueNode(iff);
Node* untaken_branch = new (C) IfFalseNode(iff);
if (!taken_if_true) { // Finish conversion to canonical form
Node* tmp = taken_branch;
taken_branch = untaken_branch;
untaken_branch = tmp;
}
{ PreserveJVMState pjvms(this);
taken_branch = _gvn.transform(taken_branch);
set_control(taken_branch);
if (stopped()) {
if (C->eliminate_boxing()) {
branch_block->next_path_num();
}
} else {
profile_taken_branch(target_bci);
adjust_map_after_if(taken_btest, c, prob, branch_block, next_block);
if (!stopped()) {
merge(target_bci);
}
}
}
untaken_branch = _gvn.transform(untaken_branch);
set_control(untaken_branch);
if (stopped()) {
if (C->eliminate_boxing()) {
next_block->next_path_num();
}
} else {
profile_not_taken_branch();
adjust_map_after_if(untaken_btest, c, untaken_prob,
next_block, branch_block);
}
}
bool Parse::path_is_suitable_for_uncommon_trap(float prob) const {
if (!UseInterpreter) {
return false;
}
return (seems_never_taken(prob) && seems_stable_comparison());
}
void Parse::adjust_map_after_if(BoolTest::mask btest, Node* c, float prob,
Block* path, Block* other_path) {
if (stopped() || !c->is_Cmp() || btest == BoolTest::illegal)
return; // nothing to do
bool is_fallthrough = (path == successor_for_bci(iter().next_bci()));
if (path_is_suitable_for_uncommon_trap(prob)) {
repush_if_args();
uncommon_trap(Deoptimization::Reason_unstable_if,
Deoptimization::Action_reinterpret,
NULL,
(is_fallthrough ? "taken always" : "taken never"));
return;
}
Node* val = c->in(1);
Node* con = c->in(2);
const Type* tcon = _gvn.type(con);
const Type* tval = _gvn.type(val);
bool have_con = tcon->singleton();
if (tval->singleton()) {
if (!have_con) {
con = val;
tcon = tval;
val = c->in(2);
tval = _gvn.type(val);
btest = BoolTest(btest).commute();
have_con = true;
} else {
have_con = false;
}
}
if (!have_con) // remaining adjustments need a con
return;
sharpen_type_after_if(btest, con, tcon, val, tval);
}
static Node* extract_obj_from_klass_load(PhaseGVN* gvn, Node* n) {
Node* ldk;
if (n->is_DecodeNKlass()) {
if (n->in(1)->Opcode() != Op_LoadNKlass) {
return NULL;
} else {
ldk = n->in(1);
}
} else if (n->Opcode() != Op_LoadKlass) {
return NULL;
} else {
ldk = n;
}
assert(ldk != NULL && ldk->is_Load(), "should have found a LoadKlass or LoadNKlass node");
Node* adr = ldk->in(MemNode::Address);
intptr_t off = 0;
Node* obj = AddPNode::Ideal_base_and_offset(adr, gvn, off);
if (obj == NULL || off != oopDesc::klass_offset_in_bytes()) // loading oopDesc::_klass?
return NULL;
const TypePtr* tp = gvn->type(obj)->is_ptr();
if (tp == NULL || !(tp->isa_instptr() || tp->isa_aryptr())) // is obj a Java object ptr?
return NULL;
return obj;
}
void Parse::sharpen_type_after_if(BoolTest::mask btest,
Node* con, const Type* tcon,
Node* val, const Type* tval) {
if (btest == BoolTest::eq && tcon->isa_klassptr()) {
Node* obj = extract_obj_from_klass_load(&_gvn, val);
const TypeOopPtr* con_type = tcon->isa_klassptr()->as_instance_type();
if (obj != NULL && (con_type->isa_instptr() || con_type->isa_aryptr())) {
const Type* obj_type = _gvn.type(obj);
const TypeOopPtr* tboth = obj_type->join_speculative(con_type)->isa_oopptr();
if (tboth != NULL && tboth->klass_is_exact() && tboth != obj_type &&
tboth->higher_equal(obj_type)) {
int obj_in_map = map()->find_edge(obj);
JVMState* jvms = this->jvms();
if (obj_in_map >= 0 &&
(jvms->is_loc(obj_in_map) || jvms->is_stk(obj_in_map))) {
TypeNode* ccast = new (C) CheckCastPPNode(control(), obj, tboth);
const Type* tcc = ccast->as_Type()->type();
assert(tcc != obj_type && tcc->higher_equal_speculative(obj_type), "must improve");
_gvn.set_type_bottom(ccast);
record_for_igvn(ccast);
replace_in_map(obj, ccast);
}
}
}
}
int val_in_map = map()->find_edge(val);
if (val_in_map < 0) return; // replace_in_map would be useless
{
JVMState* jvms = this->jvms();
if (!(jvms->is_loc(val_in_map) ||
jvms->is_stk(val_in_map)))
return; // again, it would be useless
}
assert(tcon->singleton(), "");
ConstraintCastNode* ccast = NULL;
Node* cast = NULL;
switch (btest) {
case BoolTest::eq: // Constant test?
{
const Type* tboth = tcon->join_speculative(tval);
if (tboth == tval) break; // Nothing to gain.
if (tcon->isa_int()) {
ccast = new (C) CastIINode(val, tboth);
} else if (tcon == TypePtr::NULL_PTR) {
ccast = new (C) CastPPNode(val, tboth);
} else {
const TypeF* tf = tcon->isa_float_constant();
const TypeD* td = tcon->isa_double_constant();
if ((!tf || tf->_f != 0.0) &&
(!td || td->_d != 0.0))
cast = con; // Replace non-constant val by con.
}
}
break;
case BoolTest::ne:
if (tcon == TypePtr::NULL_PTR) {
cast = cast_not_null(val, false);
}
break;
default:
break;
}
if (ccast != NULL) {
const Type* tcc = ccast->as_Type()->type();
assert(tcc != tval && tcc->higher_equal_speculative(tval), "must improve");
ccast->set_req(0, control());
_gvn.set_type_bottom(ccast);
record_for_igvn(ccast);
cast = ccast;
}
if (cast != NULL) { // Here's the payoff.
replace_in_map(val, cast);
}
}
Node* Parse::optimize_cmp_with_klass(Node* c) {
if (c->Opcode() == Op_CmpP &&
(c->in(1)->Opcode() == Op_LoadKlass || c->in(1)->Opcode() == Op_DecodeNKlass) &&
c->in(2)->is_Con()) {
Node* load_klass = NULL;
Node* decode = NULL;
if (c->in(1)->Opcode() == Op_DecodeNKlass) {
decode = c->in(1);
load_klass = c->in(1)->in(1);
} else {
load_klass = c->in(1);
}
if (load_klass->in(2)->is_AddP()) {
Node* addp = load_klass->in(2);
Node* obj = addp->in(AddPNode::Address);
const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr();
if (obj_type->speculative_type() != NULL) {
ciKlass* k = obj_type->speculative_type();
inc_sp(2);
obj = maybe_cast_profiled_obj(obj, k);
dec_sp(2);
addp = basic_plus_adr(obj, addp->in(AddPNode::Offset));
load_klass = load_klass->clone();
load_klass->set_req(2, addp);
load_klass = _gvn.transform(load_klass);
if (decode != NULL) {
decode = decode->clone();
decode->set_req(1, load_klass);
load_klass = _gvn.transform(decode);
}
c = c->clone();
c->set_req(1, load_klass);
c = _gvn.transform(c);
}
}
}
return c;
}
void Parse::do_one_bytecode() {
Node *a, *b, *c, *d; // Handy temps
BoolTest::mask btest;
int i;
assert(!has_exceptions(), "bytecode entry state must be clear of throws");
if (C->check_node_count(NodeLimitFudgeFactor * 5,
"out of nodes parsing method")) {
return;
}
#ifdef ASSERT
if (TraceOptoParse) {
tty->print(" @");
dump_bci(bci());
tty->cr();
}
#endif
switch (bc()) {
case Bytecodes::_nop:
break;
case Bytecodes::_lconst_0:
push_pair(longcon(0));
break;
case Bytecodes::_lconst_1:
push_pair(longcon(1));
break;
case Bytecodes::_fconst_0:
push(zerocon(T_FLOAT));
break;
case Bytecodes::_fconst_1:
push(makecon(TypeF::ONE));
break;
case Bytecodes::_fconst_2:
push(makecon(TypeF::make(2.0f)));
break;
case Bytecodes::_dconst_0:
push_pair(zerocon(T_DOUBLE));
break;
case Bytecodes::_dconst_1:
push_pair(makecon(TypeD::ONE));
break;
case Bytecodes::_iconst_m1:push(intcon(-1)); break;
case Bytecodes::_iconst_0: push(intcon( 0)); break;
case Bytecodes::_iconst_1: push(intcon( 1)); break;
case Bytecodes::_iconst_2: push(intcon( 2)); break;
case Bytecodes::_iconst_3: push(intcon( 3)); break;
case Bytecodes::_iconst_4: push(intcon( 4)); break;
case Bytecodes::_iconst_5: push(intcon( 5)); break;
case Bytecodes::_bipush: push(intcon(iter().get_constant_u1())); break;
case Bytecodes::_sipush: push(intcon(iter().get_constant_u2())); break;
case Bytecodes::_aconst_null: push(null()); break;
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
{
ciConstant constant = iter().get_constant();
if (constant.basic_type() == T_OBJECT &&
!constant.as_object()->is_loaded()) {
int index = iter().get_constant_pool_index();
constantTag tag = iter().get_constant_pool_tag(index);
uncommon_trap(Deoptimization::make_trap_request
(Deoptimization::Reason_unloaded,
Deoptimization::Action_reinterpret,
index),
NULL, tag.internal_name());
break;
}
assert(constant.basic_type() != T_OBJECT || constant.as_object()->is_instance(),
"must be java_mirror of klass");
bool pushed = push_constant(constant, true);
guarantee(pushed, "must be possible to push this constant");
}
break;
case Bytecodes::_aload_0:
push( local(0) );
break;
case Bytecodes::_aload_1:
push( local(1) );
break;
case Bytecodes::_aload_2:
push( local(2) );
break;
case Bytecodes::_aload_3:
push( local(3) );
break;
case Bytecodes::_aload:
push( local(iter().get_index()) );
break;
case Bytecodes::_fload_0:
case Bytecodes::_iload_0:
push( local(0) );
break;
case Bytecodes::_fload_1:
case Bytecodes::_iload_1:
push( local(1) );
break;
case Bytecodes::_fload_2:
case Bytecodes::_iload_2:
push( local(2) );
break;
case Bytecodes::_fload_3:
case Bytecodes::_iload_3:
push( local(3) );
break;
case Bytecodes::_fload:
case Bytecodes::_iload:
push( local(iter().get_index()) );
break;
case Bytecodes::_lload_0:
push_pair_local( 0 );
break;
case Bytecodes::_lload_1:
push_pair_local( 1 );
break;
case Bytecodes::_lload_2:
push_pair_local( 2 );
break;
case Bytecodes::_lload_3:
push_pair_local( 3 );
break;
case Bytecodes::_lload:
push_pair_local( iter().get_index() );
break;
case Bytecodes::_dload_0:
push_pair_local(0);
break;
case Bytecodes::_dload_1:
push_pair_local(1);
break;
case Bytecodes::_dload_2:
push_pair_local(2);
break;
case Bytecodes::_dload_3:
push_pair_local(3);
break;
case Bytecodes::_dload:
push_pair_local(iter().get_index());
break;
case Bytecodes::_fstore_0:
case Bytecodes::_istore_0:
case Bytecodes::_astore_0:
set_local( 0, pop() );
break;
case Bytecodes::_fstore_1:
case Bytecodes::_istore_1:
case Bytecodes::_astore_1:
set_local( 1, pop() );
break;
case Bytecodes::_fstore_2:
case Bytecodes::_istore_2:
case Bytecodes::_astore_2:
set_local( 2, pop() );
break;
case Bytecodes::_fstore_3:
case Bytecodes::_istore_3:
case Bytecodes::_astore_3:
set_local( 3, pop() );
break;
case Bytecodes::_fstore:
case Bytecodes::_istore:
case Bytecodes::_astore:
set_local( iter().get_index(), pop() );
break;
case Bytecodes::_lstore_0:
set_pair_local( 0, pop_pair() );
break;
case Bytecodes::_lstore_1:
set_pair_local( 1, pop_pair() );
break;
case Bytecodes::_lstore_2:
set_pair_local( 2, pop_pair() );
break;
case Bytecodes::_lstore_3:
set_pair_local( 3, pop_pair() );
break;
case Bytecodes::_lstore:
set_pair_local( iter().get_index(), pop_pair() );
break;
case Bytecodes::_dstore_0:
set_pair_local( 0, dstore_rounding(pop_pair()) );
break;
case Bytecodes::_dstore_1:
set_pair_local( 1, dstore_rounding(pop_pair()) );
break;
case Bytecodes::_dstore_2:
set_pair_local( 2, dstore_rounding(pop_pair()) );
break;
case Bytecodes::_dstore_3:
set_pair_local( 3, dstore_rounding(pop_pair()) );
break;
case Bytecodes::_dstore:
set_pair_local( iter().get_index(), dstore_rounding(pop_pair()) );
break;
case Bytecodes::_pop: dec_sp(1); break;
case Bytecodes::_pop2: dec_sp(2); break;
case Bytecodes::_swap:
a = pop();
b = pop();
push(a);
push(b);
break;
case Bytecodes::_dup:
a = pop();
push(a);
push(a);
break;
case Bytecodes::_dup_x1:
a = pop();
b = pop();
push( a );
push( b );
push( a );
break;
case Bytecodes::_dup_x2:
a = pop();
b = pop();
c = pop();
push( a );
push( c );
push( b );
push( a );
break;
case Bytecodes::_dup2:
a = pop();
b = pop();
push( b );
push( a );
push( b );
push( a );
break;
case Bytecodes::_dup2_x1:
a = pop();
b = pop();
c = pop();
push( b );
push( a );
push( c );
push( b );
push( a );
break;
case Bytecodes::_dup2_x2:
a = pop();
b = pop();
c = pop();
d = pop();
push( b );
push( a );
push( d );
push( c );
push( b );
push( a );
break;
case Bytecodes::_arraylength: {
Node *ary = null_check(peek(), T_ARRAY);
if (stopped()) return;
a = pop();
push(load_array_length(a));
break;
}
case Bytecodes::_baload: array_load(T_BYTE); break;
case Bytecodes::_caload: array_load(T_CHAR); break;
case Bytecodes::_iaload: array_load(T_INT); break;
case Bytecodes::_saload: array_load(T_SHORT); break;
case Bytecodes::_faload: array_load(T_FLOAT); break;
case Bytecodes::_aaload: array_load(T_OBJECT); break;
case Bytecodes::_laload: {
a = array_addressing(T_LONG, 0);
if (stopped()) return; // guaranteed null or range check
dec_sp(2); // Pop array and index
push_pair(make_load(control(), a, TypeLong::LONG, T_LONG, TypeAryPtr::LONGS, MemNode::unordered));
break;
}
case Bytecodes::_daload: {
a = array_addressing(T_DOUBLE, 0);
if (stopped()) return; // guaranteed null or range check
dec_sp(2); // Pop array and index
push_pair(make_load(control(), a, Type::DOUBLE, T_DOUBLE, TypeAryPtr::DOUBLES, MemNode::unordered));
break;
}
case Bytecodes::_bastore: array_store(T_BYTE); break;
case Bytecodes::_castore: array_store(T_CHAR); break;
case Bytecodes::_iastore: array_store(T_INT); break;
case Bytecodes::_sastore: array_store(T_SHORT); break;
case Bytecodes::_fastore: array_store(T_FLOAT); break;
case Bytecodes::_aastore: {
d = array_addressing(T_OBJECT, 1);
if (stopped()) return; // guaranteed null or range check
array_store_check();
c = pop(); // Oop to store
b = pop(); // index (already used)
a = pop(); // the array itself
const TypeOopPtr* elemtype = _gvn.type(a)->is_aryptr()->elem()->make_oopptr();
const TypeAryPtr* adr_type = TypeAryPtr::OOPS;
Node* store = store_oop_to_array(control(), a, d, adr_type, c, elemtype, T_OBJECT, MemNode::release);
break;
}
case Bytecodes::_lastore: {
a = array_addressing(T_LONG, 2);
if (stopped()) return; // guaranteed null or range check
c = pop_pair();
dec_sp(2); // Pop array and index
store_to_memory(control(), a, c, T_LONG, TypeAryPtr::LONGS, MemNode::unordered);
break;
}
case Bytecodes::_dastore: {
a = array_addressing(T_DOUBLE, 2);
if (stopped()) return; // guaranteed null or range check
c = pop_pair();
dec_sp(2); // Pop array and index
c = dstore_rounding(c);
store_to_memory(control(), a, c, T_DOUBLE, TypeAryPtr::DOUBLES, MemNode::unordered);
break;
}
case Bytecodes::_getfield:
do_getfield();
break;
case Bytecodes::_getstatic:
do_getstatic();
break;
case Bytecodes::_putfield:
do_putfield();
break;
case Bytecodes::_putstatic:
do_putstatic();
break;
case Bytecodes::_irem:
do_irem();
break;
case Bytecodes::_idiv:
zero_check_int(peek());
if (stopped()) return;
b = pop();
a = pop();
push( _gvn.transform( new (C) DivINode(control(),a,b) ) );
break;
case Bytecodes::_imul:
b = pop(); a = pop();
push( _gvn.transform( new (C) MulINode(a,b) ) );
break;
case Bytecodes::_iadd:
b = pop(); a = pop();
push( _gvn.transform( new (C) AddINode(a,b) ) );
break;
case Bytecodes::_ineg:
a = pop();
push( _gvn.transform( new (C) SubINode(_gvn.intcon(0),a)) );
break;
case Bytecodes::_isub:
b = pop(); a = pop();
push( _gvn.transform( new (C) SubINode(a,b) ) );
break;
case Bytecodes::_iand:
b = pop(); a = pop();
push( _gvn.transform( new (C) AndINode(a,b) ) );
break;
case Bytecodes::_ior:
b = pop(); a = pop();
push( _gvn.transform( new (C) OrINode(a,b) ) );
break;
case Bytecodes::_ixor:
b = pop(); a = pop();
push( _gvn.transform( new (C) XorINode(a,b) ) );
break;
case Bytecodes::_ishl:
b = pop(); a = pop();
push( _gvn.transform( new (C) LShiftINode(a,b) ) );
break;
case Bytecodes::_ishr:
b = pop(); a = pop();
push( _gvn.transform( new (C) RShiftINode(a,b) ) );
break;
case Bytecodes::_iushr:
b = pop(); a = pop();
push( _gvn.transform( new (C) URShiftINode(a,b) ) );
break;
case Bytecodes::_fneg:
a = pop();
b = _gvn.transform(new (C) NegFNode (a));
push(b);
break;
case Bytecodes::_fsub:
b = pop();
a = pop();
c = _gvn.transform( new (C) SubFNode(a,b) );
d = precision_rounding(c);
push( d );
break;
case Bytecodes::_fadd:
b = pop();
a = pop();
c = _gvn.transform( new (C) AddFNode(a,b) );
d = precision_rounding(c);
push( d );
break;
case Bytecodes::_fmul:
b = pop();
a = pop();
c = _gvn.transform( new (C) MulFNode(a,b) );
d = precision_rounding(c);
push( d );
break;
case Bytecodes::_fdiv:
b = pop();
a = pop();
c = _gvn.transform( new (C) DivFNode(0,a,b) );
d = precision_rounding(c);
push( d );
break;
case Bytecodes::_frem:
if (Matcher::has_match_rule(Op_ModF)) {
b = pop();
a = pop();
c = _gvn.transform( new (C) ModFNode(0,a,b) );
d = precision_rounding(c);
push( d );
}
else {
modf();
}
break;
case Bytecodes::_fcmpl:
b = pop();
a = pop();
c = _gvn.transform( new (C) CmpF3Node( a, b));
push(c);
break;
case Bytecodes::_fcmpg:
b = pop();
a = pop();
c = _gvn.transform( new (C) CmpF3Node( b, a));
c = _gvn.transform( new (C) SubINode(_gvn.intcon(0),c) );
push(c);
break;
case Bytecodes::_f2i:
a = pop();
push(_gvn.transform(new (C) ConvF2INode(a)));
break;
case Bytecodes::_d2i:
a = pop_pair();
b = _gvn.transform(new (C) ConvD2INode(a));
push( b );
break;
case Bytecodes::_f2d:
a = pop();
b = _gvn.transform( new (C) ConvF2DNode(a));
push_pair( b );
break;
case Bytecodes::_d2f:
a = pop_pair();
b = _gvn.transform( new (C) ConvD2FNode(a));
push( b );
break;
case Bytecodes::_l2f:
if (Matcher::convL2FSupported()) {
a = pop_pair();
b = _gvn.transform( new (C) ConvL2FNode(a));
c = _gvn.transform(b);
push(c);
} else {
l2f();
}
break;
case Bytecodes::_l2d:
a = pop_pair();
b = _gvn.transform( new (C) ConvL2DNode(a));
c = _gvn.transform(b);
push_pair(c);
break;
case Bytecodes::_f2l:
a = pop();
b = _gvn.transform( new (C) ConvF2LNode(a));
push_pair(b);
break;
case Bytecodes::_d2l:
a = pop_pair();
b = _gvn.transform( new (C) ConvD2LNode(a));
push_pair(b);
break;
case Bytecodes::_dsub:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) SubDNode(a,b) );
d = dprecision_rounding(c);
push_pair( d );
break;
case Bytecodes::_dadd:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) AddDNode(a,b) );
d = dprecision_rounding(c);
push_pair( d );
break;
case Bytecodes::_dmul:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) MulDNode(a,b) );
d = dprecision_rounding(c);
push_pair( d );
break;
case Bytecodes::_ddiv:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) DivDNode(0,a,b) );
d = dprecision_rounding(c);
push_pair( d );
break;
case Bytecodes::_dneg:
a = pop_pair();
b = _gvn.transform(new (C) NegDNode (a));
push_pair(b);
break;
case Bytecodes::_drem:
if (Matcher::has_match_rule(Op_ModD)) {
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) ModDNode(0,a,b) );
d = dprecision_rounding(c);
push_pair( d );
}
else {
modd();
}
break;
case Bytecodes::_dcmpl:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) CmpD3Node( a, b));
push(c);
break;
case Bytecodes::_dcmpg:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) CmpD3Node( b, a));
c = _gvn.transform( new (C) SubINode(_gvn.intcon(0),c) );
push(c);
break;
case Bytecodes::_land:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) AndLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lor:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) OrLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lxor:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) XorLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lshl:
b = pop(); // the shift count
a = pop_pair(); // value to be shifted
c = _gvn.transform( new (C) LShiftLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lshr:
b = pop(); // the shift count
a = pop_pair(); // value to be shifted
c = _gvn.transform( new (C) RShiftLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lushr:
b = pop(); // the shift count
a = pop_pair(); // value to be shifted
c = _gvn.transform( new (C) URShiftLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lmul:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) MulLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lrem:
assert(peek(0) == top(), "long word order");
zero_check_long(peek(1));
if (stopped()) return;
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) ModLNode(control(),a,b) );
push_pair(c);
break;
case Bytecodes::_ldiv:
assert(peek(0) == top(), "long word order");
zero_check_long(peek(1));
if (stopped()) return;
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) DivLNode(control(),a,b) );
push_pair(c);
break;
case Bytecodes::_ladd:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) AddLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lsub:
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) SubLNode(a,b) );
push_pair(c);
break;
case Bytecodes::_lcmp:
if( UseLoopSafepoints ) {
switch( iter().next_bc() ) {
case Bytecodes::_ifgt:
case Bytecodes::_iflt:
case Bytecodes::_ifge:
case Bytecodes::_ifle:
case Bytecodes::_ifne:
case Bytecodes::_ifeq:
maybe_add_safepoint(iter().next_get_dest());
}
}
b = pop_pair();
a = pop_pair();
c = _gvn.transform( new (C) CmpL3Node( a, b ));
push(c);
break;
case Bytecodes::_lneg:
a = pop_pair();
b = _gvn.transform( new (C) SubLNode(longcon(0),a));
push_pair(b);
break;
case Bytecodes::_l2i:
a = pop_pair();
push( _gvn.transform( new (C) ConvL2INode(a)));
break;
case Bytecodes::_i2l:
a = pop();
b = _gvn.transform( new (C) ConvI2LNode(a));
push_pair(b);
break;
case Bytecodes::_i2b:
a = pop();
a = _gvn.transform( new (C) LShiftINode(a,_gvn.intcon(24)) );
a = _gvn.transform( new (C) RShiftINode(a,_gvn.intcon(24)) );
push( a );
break;
case Bytecodes::_i2s:
a = pop();
a = _gvn.transform( new (C) LShiftINode(a,_gvn.intcon(16)) );
a = _gvn.transform( new (C) RShiftINode(a,_gvn.intcon(16)) );
push( a );
break;
case Bytecodes::_i2c:
a = pop();
push( _gvn.transform( new (C) AndINode(a,_gvn.intcon(0xFFFF)) ) );
break;
case Bytecodes::_i2f:
a = pop();
b = _gvn.transform( new (C) ConvI2FNode(a) ) ;
c = precision_rounding(b);
push (b);
break;
case Bytecodes::_i2d:
a = pop();
b = _gvn.transform( new (C) ConvI2DNode(a));
push_pair(b);
break;
case Bytecodes::_iinc: // Increment local
i = iter().get_index(); // Get local index
set_local( i, _gvn.transform( new (C) AddINode( _gvn.intcon(iter().get_iinc_con()), local(i) ) ) );
break;
case Bytecodes::_return:
return_current(NULL);
break;
case Bytecodes::_ireturn:
case Bytecodes::_areturn:
case Bytecodes::_freturn:
return_current(pop());
break;
case Bytecodes::_lreturn:
return_current(pop_pair());
break;
case Bytecodes::_dreturn:
return_current(pop_pair());
break;
case Bytecodes::_athrow:
null_check(peek());
if (stopped()) return;
if (BailoutToInterpreterForThrows) {
uncommon_trap(Deoptimization::Reason_unhandled,
Deoptimization::Action_make_not_compilable);
return;
}
if (env()->jvmti_can_post_on_exceptions()) {
uncommon_trap_if_should_post_on_exceptions(Deoptimization::Reason_unhandled, false);
}
add_exception_state(make_exception_state(peek()));
break;
case Bytecodes::_goto: // fall through
case Bytecodes::_goto_w: {
int target_bci = (bc() == Bytecodes::_goto) ? iter().get_dest() : iter().get_far_dest();
maybe_add_safepoint(target_bci);
profile_taken_branch(target_bci);
merge(target_bci);
Block *target_block = block()->successor_for_bci(target_bci);
if (target_block->pred_count() != 1) break;
ciMethodData* methodData = method()->method_data();
if (!methodData->is_mature()) break;
ciProfileData* data = methodData->bci_to_data(bci());
assert( data->is_JumpData(), "" );
int taken = ((ciJumpData*)data)->taken();
taken = method()->scale_count(taken);
target_block->set_count(taken);
break;
}
case Bytecodes::_ifnull: btest = BoolTest::eq; goto handle_if_null;
case Bytecodes::_ifnonnull: btest = BoolTest::ne; goto handle_if_null;
handle_if_null:
maybe_add_safepoint(iter().get_dest());
a = null();
b = pop();
c = _gvn.transform( new (C) CmpPNode(b, a) );
do_ifnull(btest, c);
break;
case Bytecodes::_if_acmpeq: btest = BoolTest::eq; goto handle_if_acmp;
case Bytecodes::_if_acmpne: btest = BoolTest::ne; goto handle_if_acmp;
handle_if_acmp:
maybe_add_safepoint(iter().get_dest());
a = pop();
b = pop();
c = _gvn.transform( new (C) CmpPNode(b, a) );
c = optimize_cmp_with_klass(c);
do_if(btest, c);
break;
case Bytecodes::_ifeq: btest = BoolTest::eq; goto handle_ifxx;
case Bytecodes::_ifne: btest = BoolTest::ne; goto handle_ifxx;
case Bytecodes::_iflt: btest = BoolTest::lt; goto handle_ifxx;
case Bytecodes::_ifle: btest = BoolTest::le; goto handle_ifxx;
case Bytecodes::_ifgt: btest = BoolTest::gt; goto handle_ifxx;
case Bytecodes::_ifge: btest = BoolTest::ge; goto handle_ifxx;
handle_ifxx:
maybe_add_safepoint(iter().get_dest());
a = _gvn.intcon(0);
b = pop();
c = _gvn.transform( new (C) CmpINode(b, a) );
do_if(btest, c);
break;
case Bytecodes::_if_icmpeq: btest = BoolTest::eq; goto handle_if_icmp;
case Bytecodes::_if_icmpne: btest = BoolTest::ne; goto handle_if_icmp;
case Bytecodes::_if_icmplt: btest = BoolTest::lt; goto handle_if_icmp;
case Bytecodes::_if_icmple: btest = BoolTest::le; goto handle_if_icmp;
case Bytecodes::_if_icmpgt: btest = BoolTest::gt; goto handle_if_icmp;
case Bytecodes::_if_icmpge: btest = BoolTest::ge; goto handle_if_icmp;
handle_if_icmp:
maybe_add_safepoint(iter().get_dest());
a = pop();
b = pop();
c = _gvn.transform( new (C) CmpINode( b, a ) );
do_if(btest, c);
break;
case Bytecodes::_tableswitch:
do_tableswitch();
break;
case Bytecodes::_lookupswitch:
do_lookupswitch();
break;
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokespecial:
case Bytecodes::_invokevirtual:
case Bytecodes::_invokeinterface:
do_call();
break;
case Bytecodes::_checkcast:
do_checkcast();
break;
case Bytecodes::_instanceof:
do_instanceof();
break;
case Bytecodes::_anewarray:
do_anewarray();
break;
case Bytecodes::_newarray:
do_newarray((BasicType)iter().get_index());
break;
case Bytecodes::_multianewarray:
do_multianewarray();
break;
case Bytecodes::_new:
do_new();
break;
case Bytecodes::_jsr:
case Bytecodes::_jsr_w:
do_jsr();
break;
case Bytecodes::_ret:
do_ret();
break;
case Bytecodes::_monitorenter:
do_monitor_enter();
break;
case Bytecodes::_monitorexit:
do_monitor_exit();
break;
case Bytecodes::_breakpoint:
C->record_failure("breakpoint in method");
return;
default:
#ifndef PRODUCT
map()->dump(99);
#endif
tty->print("\nUnhandled bytecode %s\n", Bytecodes::name(bc()) );
ShouldNotReachHere();
}
#ifndef PRODUCT
IdealGraphPrinter *printer = IdealGraphPrinter::printer();
if(printer) {
char buffer[256];
sprintf(buffer, "Bytecode %d: %s", bci(), Bytecodes::name(bc()));
bool old = printer->traverse_outs();
printer->set_traverse_outs(true);
printer->print_method(C, buffer, 4);
printer->set_traverse_outs(old);
}
#endif
}
C:\hotspot-69087d08d473\src\share\vm/opto/parse3.cpp
#include "precompiled.hpp"
#include "compiler/compileLog.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/universe.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "opto/addnode.hpp"
#include "opto/memnode.hpp"
#include "opto/parse.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "opto/subnode.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/handles.inline.hpp"
bool Parse::static_field_ok_in_clinit(ciField *field, ciMethod *method) {
assert( field->is_static(), "Only check if field is static");
ciInstanceKlass *field_holder = field->holder();
bool access_OK = false;
if (method->holder()->is_subclass_of(field_holder)) {
if (method->is_static()) {
if (method->name() == ciSymbol::class_initializer_name()) {
access_OK = true;
}
} else {
if (method->name() == ciSymbol::object_initializer_name()) {
access_OK = true;
}
}
}
return access_OK;
}
void Parse::do_field_access(bool is_get, bool is_field) {
bool will_link;
ciField* field = iter().get_field(will_link);
assert(will_link, "getfield: typeflow responsibility");
ciInstanceKlass* field_holder = field->holder();
if (is_field == field->is_static()) {
uncommon_trap(Deoptimization::Reason_unhandled,
Deoptimization::Action_none);
return;
}
if (!is_field && !field_holder->is_initialized()) {
if (!static_field_ok_in_clinit(field, method())) {
uncommon_trap(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret,
NULL, "!static_field_ok_in_clinit");
return;
}
}
if (!is_get && field->is_call_site_target()) {
uncommon_trap(Deoptimization::Reason_unhandled,
Deoptimization::Action_reinterpret,
NULL, "put to call site target field");
return;
}
assert(field->will_link(method()->holder(), bc()), "getfield: typeflow responsibility");
Node* obj;
if (is_field) {
int obj_depth = is_get ? 0 : field->type()->size();
obj = null_check(peek(obj_depth));
if (stopped()) return;
#ifdef ASSERT
const TypeInstPtr *tjp = TypeInstPtr::make(TypePtr::NotNull, iter().get_declared_field_holder());
assert(_gvn.type(obj)->higher_equal(tjp), "cast_up is no longer needed");
#endif
if (is_get) {
(void) pop(); // pop receiver before getting
do_get_xxx(obj, field, is_field);
} else {
do_put_xxx(obj, field, is_field);
(void) pop(); // pop receiver after putting
}
} else {
const TypeInstPtr* tip = TypeInstPtr::make(field_holder->java_mirror());
obj = _gvn.makecon(tip);
if (is_get) {
do_get_xxx(obj, field, is_field);
} else {
do_put_xxx(obj, field, is_field);
}
}
}
void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
if (field->is_constant()) {
const Type* stable_type = NULL;
if (FoldStableValues && field->is_stable()) {
stable_type = Type::get_const_type(field->type());
if (field->type()->is_array_klass()) {
int stable_dimension = field->type()->as_array_klass()->dimension();
stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension);
}
}
if (field->is_static()) {
if (C->eliminate_boxing()) {
ciSymbol* klass_name = field->holder()->name();
if (field->name() == ciSymbol::cache_field_name() &&
field->holder()->uses_default_loader() &&
(klass_name == ciSymbol::java_lang_Character_CharacterCache() ||
klass_name == ciSymbol::java_lang_Byte_ByteCache() ||
klass_name == ciSymbol::java_lang_Short_ShortCache() ||
klass_name == ciSymbol::java_lang_Integer_IntegerCache() ||
klass_name == ciSymbol::java_lang_Long_LongCache())) {
bool require_const = true;
bool autobox_cache = true;
if (push_constant(field->constant_value(), require_const, autobox_cache)) {
return;
}
}
}
if (push_constant(field->constant_value(), false, false, stable_type))
return;
} else {
if (obj->is_Con()) {
const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr();
ciObject* constant_oop = oop_ptr->const_oop();
ciConstant constant = field->constant_value_of(constant_oop);
if (FoldStableValues && field->is_stable() && constant.is_null_or_zero()) {
} else {
if (push_constant(constant, true, false, stable_type))
return;
}
}
}
}
Node* leading_membar = NULL;
ciType* field_klass = field->type();
bool is_vol = field->is_volatile();
int offset = field->offset_in_bytes();
const TypePtr* adr_type = C->alias_type(field)->adr_type();
Node *adr = basic_plus_adr(obj, obj, offset);
BasicType bt = field->layout_type();
const Type *type;
bool must_assert_null = false;
if( bt == T_OBJECT ) {
if (!field->type()->is_loaded()) {
type = TypeInstPtr::BOTTOM;
must_assert_null = true;
} else if (field->is_constant() && field->is_static()) {
ciObject* con = field->constant_value().as_object();
type = TypeOopPtr::make_from_constant(con)->isa_oopptr();
assert(type != NULL, "field singleton type must be consistent");
} else {
type = TypeOopPtr::make_from_klass(field_klass->as_klass());
}
} else {
type = Type::get_const_basic_type(bt);
}
if (support_IRIW_for_not_multiple_copy_atomic_cpu && field->is_volatile()) {
leading_membar = insert_mem_bar(Op_MemBarVolatile); // StoreLoad barrier
}
MemNode::MemOrd mo = is_vol ? MemNode::acquire : MemNode::unordered;
Node* ld = make_load(NULL, adr, type, bt, adr_type, mo, LoadNode::DependsOnlyOnTest, is_vol);
if (type2size[bt] == 1)
push(ld);
else
push_pair(ld);
if (must_assert_null) {
#ifndef PRODUCT
if (PrintOpto && (Verbose || WizardMode)) {
method()->print_name(); tty->print_cr(" asserting nullness of field at bci: %d", bci());
}
#endif
if (C->log() != NULL) {
C->log()->elem("assert_null reason='field' klass='%d'",
C->log()->identify(field->type()));
}
set_bci(iter().next_bci());
null_assert(peek());
set_bci(iter().cur_bci()); // put it back
}
if (field->is_volatile()) {
assert(leading_membar == NULL || support_IRIW_for_not_multiple_copy_atomic_cpu, "no leading membar expected");
Node* mb = insert_mem_bar(Op_MemBarAcquire, ld);
mb->as_MemBar()->set_trailing_load();
}
}
void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
Node* leading_membar = NULL;
bool is_vol = field->is_volatile();
if (is_vol) {
leading_membar = insert_mem_bar(Op_MemBarRelease);
}
int offset = field->offset_in_bytes();
const TypePtr* adr_type = C->alias_type(field)->adr_type();
Node* adr = basic_plus_adr(obj, obj, offset);
BasicType bt = field->layout_type();
Node* val = type2size[bt] == 1 ? pop() : pop_pair();
if (bt == T_DOUBLE) val = dstore_rounding(val);
const MemNode::MemOrd mo =
is_vol ?
MemNode::release :
StoreNode::release_if_reference(bt);
Node* store;
if (bt == T_OBJECT) {
const TypeOopPtr* field_type;
if (!field->type()->is_loaded()) {
field_type = TypeInstPtr::BOTTOM;
} else {
field_type = TypeOopPtr::make_from_klass(field->type()->as_klass());
}
store = store_oop_to_object(control(), obj, adr, adr_type, val, field_type, bt, mo);
} else {
store = store_to_memory(control(), adr, val, bt, adr_type, mo, is_vol);
}
if (is_vol) {
if (!support_IRIW_for_not_multiple_copy_atomic_cpu) {
Node* mb = insert_mem_bar(Op_MemBarVolatile, store); // Use fat membar
MemBarNode::set_store_pair(leading_membar->as_MemBar(), mb->as_MemBar());
}
if (is_field) {
set_wrote_volatile(true);
}
}
if (is_field && (field->is_final() || field->is_stable())) {
set_wrote_final(true);
if (C->eliminate_boxing() &&
adr_type->isa_oopptr() && adr_type->is_oopptr()->is_ptr_to_boxed_value() &&
AllocateNode::Ideal_allocation(obj, &_gvn) != NULL) {
set_alloc_with_final(obj);
}
}
}
bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache, const Type* stable_type) {
const Type* con_type = Type::make_from_constant(constant, require_constant, is_autobox_cache);
switch (constant.basic_type()) {
case T_ARRAY:
case T_OBJECT:
if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr())
con_type = con_type->join_speculative(stable_type);
break;
case T_ILLEGAL:
assert(C->env()->failing(), "otherwise should not see this");
push( zerocon(T_OBJECT) );
return false;
}
if (con_type == NULL)
return false;
push_node(constant.basic_type(), makecon(con_type));
return true;
}
void Parse::do_anewarray() {
bool will_link;
ciKlass* klass = iter().get_klass(will_link);
assert(will_link, "anewarray: typeflow responsibility");
ciObjArrayKlass* array_klass = ciObjArrayKlass::make(klass);
if (!array_klass->is_loaded()) {
uncommon_trap(Deoptimization::Reason_unloaded,
Deoptimization::Action_reinterpret,
array_klass);
return;
}
kill_dead_locals();
const TypeKlassPtr* array_klass_type = TypeKlassPtr::make(array_klass);
Node* count_val = pop();
Node* obj = new_array(makecon(array_klass_type), count_val, 1);
push(obj);
}
void Parse::do_newarray(BasicType elem_type) {
kill_dead_locals();
Node* count_val = pop();
const TypeKlassPtr* array_klass = TypeKlassPtr::make(ciTypeArrayKlass::make(elem_type));
Node* obj = new_array(makecon(array_klass), count_val, 1);
push(obj);
}
Node* Parse::expand_multianewarray(ciArrayKlass* array_klass, Node* *lengths, int ndimensions, int nargs) {
Node* length = lengths[0];
assert(length != NULL, "");
Node* array = new_array(makecon(TypeKlassPtr::make(array_klass)), length, nargs);
if (ndimensions > 1) {
jint length_con = find_int_con(length, -1);
guarantee(length_con >= 0, "non-constant multianewarray");
ciArrayKlass* array_klass_1 = array_klass->as_obj_array_klass()->element_klass()->as_array_klass();
const TypePtr* adr_type = TypeAryPtr::OOPS;
const TypeOopPtr* elemtype = _gvn.type(array)->is_aryptr()->elem()->make_oopptr();
const intptr_t header = arrayOopDesc::base_offset_in_bytes(T_OBJECT);
for (jint i = 0; i < length_con; i++) {
Node* elem = expand_multianewarray(array_klass_1, &lengths[1], ndimensions-1, nargs);
intptr_t offset = header + ((intptr_t)i << LogBytesPerHeapOop);
Node* eaddr = basic_plus_adr(array, offset);
store_oop_to_array(control(), array, eaddr, adr_type, elem, elemtype, T_OBJECT, MemNode::unordered);
}
}
return array;
}
void Parse::do_multianewarray() {
int ndimensions = iter().get_dimensions();
bool will_link;
ciArrayKlass* array_klass = iter().get_klass(will_link)->as_array_klass();
assert(will_link, "multianewarray: typeflow responsibility");
kill_dead_locals();
Node** length = NEW_RESOURCE_ARRAY(Node*, ndimensions + 1);
length[ndimensions] = NULL; // terminating null for make_runtime_call
int j;
for (j = ndimensions-1; j >= 0 ; j--) length[j] = pop();
const jint expand_limit = MIN2((juint)MultiArrayExpandLimit, (juint)100);
jint expand_count = 1; // count of allocations in the expansion
jint expand_fanout = 1; // running total fanout
for (j = 0; j < ndimensions-1; j++) {
jint dim_con = find_int_con(length[j], -1);
expand_fanout *= dim_con;
expand_count += expand_fanout; // count the level-J sub-arrays
if (dim_con <= 0
|| dim_con > expand_limit
|| expand_count > expand_limit) {
expand_count = 0;
break;
}
}
if (ndimensions == 1 || (1 <= expand_count && expand_count <= expand_limit)) {
Node* obj = NULL;
{ PreserveReexecuteState preexecs(this);
inc_sp(ndimensions);
obj = expand_multianewarray(array_klass, &length[0], ndimensions, 0);
} //original reexecute and sp are set back here
push(obj);
return;
}
address fun = NULL;
switch (ndimensions) {
case 1: ShouldNotReachHere(); break;
case 2: fun = OptoRuntime::multianewarray2_Java(); break;
case 3: fun = OptoRuntime::multianewarray3_Java(); break;
case 4: fun = OptoRuntime::multianewarray4_Java(); break;
case 5: fun = OptoRuntime::multianewarray5_Java(); break;
};
Node* c = NULL;
if (fun != NULL) {
c = make_runtime_call(RC_NO_LEAF | RC_NO_IO,
OptoRuntime::multianewarray_Type(ndimensions),
fun, NULL, TypeRawPtr::BOTTOM,
makecon(TypeKlassPtr::make(array_klass)),
length[0], length[1], length[2],
(ndimensions > 2) ? length[3] : NULL,
(ndimensions > 3) ? length[4] : NULL);
} else {
Node* dims = NULL;
{ PreserveReexecuteState preexecs(this);
inc_sp(ndimensions);
Node* dims_array_klass = makecon(TypeKlassPtr::make(ciArrayKlass::make(ciType::make(T_INT))));
dims = new_array(dims_array_klass, intcon(ndimensions), 0);
for (j = 0; j < ndimensions; j++) {
Node *dims_elem = array_element_address(dims, intcon(j), T_INT);
store_to_memory(control(), dims_elem, length[j], T_INT, TypeAryPtr::INTS, MemNode::unordered);
}
}
c = make_runtime_call(RC_NO_LEAF | RC_NO_IO,
OptoRuntime::multianewarrayN_Type(),
OptoRuntime::multianewarrayN_Java(), NULL, TypeRawPtr::BOTTOM,
makecon(TypeKlassPtr::make(array_klass)),
dims);
}
make_slow_call_ex(c, env()->Throwable_klass(), false);
Node* res = _gvn.transform(new (C) ProjNode(c, TypeFunc::Parms));
const Type* type = TypeOopPtr::make_from_klass_raw(array_klass);
type = type->is_ptr()->cast_to_ptr_type(TypePtr::NotNull);
type = type->is_aryptr()->cast_to_exactness(true);
const TypeInt* ltype = _gvn.find_int_type(length[0]);
if (ltype != NULL)
type = type->is_aryptr()->cast_to_size(ltype);
Node* cast = _gvn.transform( new (C) CheckCastPPNode(control(), res, type) );
push(cast);
}
C:\hotspot-69087d08d473\src\share\vm/opto/parseHelper.cpp
#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/compileLog.hpp"
#include "oops/objArrayKlass.hpp"
#include "opto/addnode.hpp"
#include "opto/memnode.hpp"
#include "opto/mulnode.hpp"
#include "opto/parse.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
#include "runtime/sharedRuntime.hpp"
void GraphKit::make_dtrace_method_entry_exit(ciMethod* method, bool is_entry) {
const TypeFunc *call_type = OptoRuntime::dtrace_method_entry_exit_Type();
address call_address = is_entry ? CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_entry) :
CAST_FROM_FN_PTR(address, SharedRuntime::dtrace_method_exit);
const char *call_name = is_entry ? "dtrace_method_entry" : "dtrace_method_exit";
Node* thread = _gvn.transform( new (C) ThreadLocalNode() );
const TypePtr* method_type = TypeMetadataPtr::make(method);
Node *method_node = _gvn.transform( ConNode::make(C, method_type) );
kill_dead_locals();
const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM;
make_runtime_call(RC_LEAF | RC_NARROW_MEM,
call_type, call_address,
call_name, raw_adr_type,
thread, method_node);
}
void Parse::do_checkcast() {
bool will_link;
ciKlass* klass = iter().get_klass(will_link);
Node *obj = peek();
const TypeOopPtr *tp = _gvn.type(obj)->isa_oopptr();
if (!will_link || (tp && tp->klass() && !tp->klass()->is_loaded())) {
if (C->log() != NULL) {
if (!will_link) {
C->log()->elem("assert_null reason='checkcast' klass='%d'",
C->log()->identify(klass));
}
if (tp && tp->klass() && !tp->klass()->is_loaded()) {
C->log()->elem("assert_null reason='checkcast source' klass='%d'",
C->log()->identify(tp->klass()));
}
}
null_assert(obj);
assert( stopped() || _gvn.type(peek())->higher_equal(TypePtr::NULL_PTR), "what's left behind is null" );
if (!stopped()) {
profile_null_checkcast();
}
return;
}
Node *res = gen_checkcast(obj, makecon(TypeKlassPtr::make(klass)) );
pop();
push(res);
}
void Parse::do_instanceof() {
if (stopped()) return;
bool will_link;
ciKlass* klass = iter().get_klass(will_link);
if (!will_link) {
if (C->log() != NULL) {
C->log()->elem("assert_null reason='instanceof' klass='%d'",
C->log()->identify(klass));
}
null_assert(peek());
assert( stopped() || _gvn.type(peek())->higher_equal(TypePtr::NULL_PTR), "what's left behind is null" );
if (!stopped()) {
pop(); // pop the null
push(_gvn.intcon(0)); // push false answer
}
return;
}
Node* res = gen_instanceof(peek(), makecon(TypeKlassPtr::make(klass)), true);
pop();
push(res);
}
void Parse::array_store_check() {
Node *obj = peek(0);
Node *idx = peek(1);
Node *ary = peek(2);
if (_gvn.type(obj) == TypePtr::NULL_PTR) {
return;
}
int klass_offset = oopDesc::klass_offset_in_bytes();
Node* p = basic_plus_adr( ary, ary, klass_offset );
Node* array_klass = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), p, TypeInstPtr::KLASS));
const TypeKlassPtr *tak = _gvn.type(array_klass)->is_klassptr();
bool always_see_exact_class = false;
if (MonomorphicArrayCheck
&& !too_many_traps(Deoptimization::Reason_array_check)
&& !tak->klass_is_exact()
&& tak != TypeKlassPtr::OBJECT) {
always_see_exact_class = true;
const TypeKlassPtr *extak = tak->cast_to_exactness(true)->is_klassptr();
Node* con = makecon(extak);
Node* cmp = _gvn.transform(new (C) CmpPNode( array_klass, con ));
Node* bol = _gvn.transform(new (C) BoolNode( cmp, BoolTest::eq ));
Node* ctrl= control();
{ BuildCutout unless(this, bol, PROB_MAX);
uncommon_trap(Deoptimization::Reason_array_check,
Deoptimization::Action_maybe_recompile,
tak->klass());
}
if (stopped()) { // MUST uncommon-trap?
set_control(ctrl); // Then Don't Do It, just fall into the normal checking
} else { // Cast array klass to exactness:
replace_in_map(array_klass,con);
CompileLog* log = C->log();
if (log != NULL) {
log->elem("cast_up reason='monomorphic_array' from='%d' to='(exact)'",
log->identify(tak->klass()));
}
array_klass = con; // Use cast value moving forward
}
}
int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset());
Node *p2 = basic_plus_adr(array_klass, array_klass, element_klass_offset);
Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, always_see_exact_class ? control() : NULL,
immutable_memory(), p2, tak));
gen_checkcast(obj, a_e_klass);
}
void Parse::emit_guard_for_new(ciInstanceKlass* klass) {
Node* cur_thread = _gvn.transform( new (C) ThreadLocalNode() );
Node* merge = new (C) RegionNode(3);
_gvn.set_type(merge, Type::CONTROL);
Node* kls = makecon(TypeKlassPtr::make(klass));
Node* init_thread_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_thread_offset()));
Node* adr_node = basic_plus_adr(kls, kls, init_thread_offset);
Node* init_thread = make_load(NULL, adr_node, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered);
Node *tst = Bool( CmpP( init_thread, cur_thread), BoolTest::eq);
IfNode* iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN);
set_control(IfTrue(iff));
merge->set_req(1, IfFalse(iff));
Node* init_state_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_state_offset()));
adr_node = basic_plus_adr(kls, kls, init_state_offset);
Node* init_state = make_load(NULL, adr_node, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered);
Node* being_init = _gvn.intcon(InstanceKlass::being_initialized);
tst = Bool( CmpI( init_state, being_init), BoolTest::eq);
iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN);
set_control(IfTrue(iff));
merge->set_req(2, IfFalse(iff));
PreserveJVMState pjvms(this);
record_for_igvn(merge);
set_control(merge);
uncommon_trap(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret,
klass);
}
void Parse::do_new() {
kill_dead_locals();
bool will_link;
ciInstanceKlass* klass = iter().get_klass(will_link)->as_instance_klass();
assert(will_link, "_new: typeflow responsibility");
if (!klass->is_initialized() && !klass->is_being_initialized() ||
klass->is_abstract() || klass->is_interface() ||
klass->name() == ciSymbol::java_lang_Class() ||
iter().is_unresolved_klass()) {
uncommon_trap(Deoptimization::Reason_uninitialized,
Deoptimization::Action_reinterpret,
klass);
return;
}
if (klass->is_being_initialized()) {
emit_guard_for_new(klass);
}
Node* kls = makecon(TypeKlassPtr::make(klass));
Node* obj = new_instance(kls);
push(obj);
if (OptimizeStringConcat &&
(klass == C->env()->StringBuilder_klass() ||
klass == C->env()->StringBuffer_klass())) {
C->set_has_stringbuilder(true);
}
if (C->eliminate_boxing() && klass->is_box_klass()) {
C->set_has_boxed_value(true);
}
}
#ifndef PRODUCT
void Parse::dump_map_adr_mem() const {
tty->print_cr("--- Mapping from address types to memory Nodes ---");
MergeMemNode *mem = map() == NULL ? NULL : (map()->memory()->is_MergeMem() ?
map()->memory()->as_MergeMem() : NULL);
for (uint i = 0; i < (uint)C->num_alias_types(); i++) {
C->alias_type(i)->print_on(tty);
tty->print("\t");
if (mem && i < mem->req() && mem->in(i) && mem->in(i) != mem->empty_memory()) {
mem->in(i)->dump();
} else {
tty->cr();
}
}
}
#endif
void Parse::test_counter_against_threshold(Node* cnt, int limit) {
Node *threshold = makecon(TypeInt::make(limit));
Node *chk = _gvn.transform( new (C) CmpUNode( cnt, threshold) );
BoolTest::mask btest = BoolTest::lt;
Node *tst = _gvn.transform( new (C) BoolNode( chk, btest) );
{ BuildCutout unless(this, tst, PROB_ALWAYS);
uncommon_trap(Deoptimization::Reason_age,
Deoptimization::Action_maybe_recompile);
}
}
void Parse::increment_and_test_invocation_counter(int limit) {
if (!count_invocations()) return;
ciMethod* m = method();
MethodCounters* counters_adr = m->ensure_method_counters();
if (counters_adr == NULL) {
C->record_failure("method counters allocation failed");
return;
}
Node* ctrl = control();
const TypePtr* adr_type = TypeRawPtr::make((address) counters_adr);
Node *counters_node = makecon(adr_type);
Node* adr_iic_node = basic_plus_adr(counters_node, counters_node,
MethodCounters::interpreter_invocation_counter_offset_in_bytes());
Node* cnt = make_load(ctrl, adr_iic_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered);
test_counter_against_threshold(cnt, limit);
Node* incr = _gvn.transform(new (C) AddINode(cnt, _gvn.intcon(1)));
store_to_memory(ctrl, adr_iic_node, incr, T_INT, adr_type, MemNode::unordered);
}
Node* Parse::method_data_addressing(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) {
ByteSize data_offset = MethodData::data_offset();
int cell_offset = md->dp_to_di(data->dp());
int offset = in_bytes(data_offset) + cell_offset + in_bytes(counter_offset);
const TypePtr* adr_type = TypeMetadataPtr::make(md);
Node* mdo = makecon(adr_type);
Node* ptr = basic_plus_adr(mdo, mdo, offset);
if (stride != 0) {
Node* str = _gvn.MakeConX(stride);
Node* scale = _gvn.transform( new (C) MulXNode( idx, str ) );
ptr = _gvn.transform( new (C) AddPNode( mdo, ptr, scale ) );
}
return ptr;
}
void Parse::increment_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, Node* idx, uint stride) {
Node* adr_node = method_data_addressing(md, data, counter_offset, idx, stride);
const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr();
Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered);
Node* incr = _gvn.transform(new (C) AddINode(cnt, _gvn.intcon(DataLayout::counter_increment)));
store_to_memory(NULL, adr_node, incr, T_INT, adr_type, MemNode::unordered);
}
void Parse::test_for_osr_md_counter_at(ciMethodData* md, ciProfileData* data, ByteSize counter_offset, int limit) {
Node* adr_node = method_data_addressing(md, data, counter_offset);
const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr();
Node* cnt = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered);
test_counter_against_threshold(cnt, limit);
}
void Parse::set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_constant) {
Node* adr_node = method_data_addressing(md, data, DataLayout::flags_offset());
const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr();
Node* flags = make_load(NULL, adr_node, TypeInt::BYTE, T_BYTE, adr_type, MemNode::unordered);
Node* incr = _gvn.transform(new (C) OrINode(flags, _gvn.intcon(flag_constant)));
store_to_memory(NULL, adr_node, incr, T_BYTE, adr_type, MemNode::unordered);
}
void Parse::profile_taken_branch(int target_bci, bool force_update) {
int cur_bci = bci();
bool osr_site =
(target_bci <= cur_bci) && count_invocations() && UseOnStackReplacement;
set_bci(target_bci);
if (method_data_update() || force_update) {
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(cur_bci);
assert(data->is_JumpData(), "need JumpData for taken branch");
increment_md_counter_at(md, data, JumpData::taken_offset());
}
#ifndef TIERED
if (method_data_update()) {
ciMethodData* md = method()->method_data();
if (osr_site) {
ciProfileData* data = md->bci_to_data(cur_bci);
int limit = (CompileThreshold
test_for_osr_md_counter_at(md, data, JumpData::taken_offset(), limit);
}
} else {
if (osr_site) {
int limit = (CompileThreshold * OnStackReplacePercentage) / 100;
increment_and_test_invocation_counter(limit);
}
}
#endif // TIERED
set_bci(cur_bci);
}
void Parse::profile_not_taken_branch(bool force_update) {
if (method_data_update() || force_update) {
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_BranchData(), "need BranchData for not taken branch");
increment_md_counter_at(md, data, BranchData::not_taken_offset());
}
}
void Parse::profile_call(Node* receiver) {
if (!method_data_update()) return;
switch (bc()) {
case Bytecodes::_invokevirtual:
case Bytecodes::_invokeinterface:
profile_receiver_type(receiver);
break;
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokespecial:
profile_generic_call();
break;
default: fatal("unexpected call bytecode");
}
}
void Parse::profile_generic_call() {
assert(method_data_update(), "must be generating profile code");
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_CounterData(), "need CounterData for not taken branch");
increment_md_counter_at(md, data, CounterData::count_offset());
}
void Parse::profile_receiver_type(Node* receiver) {
assert(method_data_update(), "must be generating profile code");
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_ReceiverTypeData(), "need ReceiverTypeData here");
if (TypeProfileWidth < 1) {
increment_md_counter_at(md, data, CounterData::count_offset());
return;
}
ciReceiverTypeData* rdata = (ciReceiverTypeData*)data->as_ReceiverTypeData();
Node* method_data = method_data_addressing(md, rdata, in_ByteSize(0));
make_runtime_call(RC_LEAF, OptoRuntime::profile_receiver_type_Type(),
CAST_FROM_FN_PTR(address,
OptoRuntime::profile_receiver_type_C),
"profile_receiver_type_C",
TypePtr::BOTTOM,
method_data, receiver);
}
void Parse::profile_ret(int target_bci) {
if (!method_data_update()) return;
if (TypeProfileWidth < 1) return;
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_RetData(), "need RetData for ret");
ciRetData* ret_data = (ciRetData*)data->as_RetData();
uint row;
bool table_full = true;
for (row = 0; row < ret_data->row_limit(); row++) {
int key = ret_data->bci(row);
table_full &= (key != RetData::no_bci);
if (key == target_bci) break;
}
if (row >= ret_data->row_limit()) {
if (!table_full) {
}
return;
}
increment_md_counter_at(md, data, RetData::bci_count_offset(row));
}
void Parse::profile_null_checkcast() {
if (!method_data_update()) return;
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_BitData(), "need BitData for checkcast");
set_md_flag_at(md, data, BitData::null_seen_byte_constant());
}
void Parse::profile_switch_case(int table_index) {
if (!method_data_update()) return;
ciMethodData* md = method()->method_data();
assert(md != NULL, "expected valid ciMethodData");
ciProfileData* data = md->bci_to_data(bci());
assert(data->is_MultiBranchData(), "need MultiBranchData for switch case");
if (table_index >= 0) {
increment_md_counter_at(md, data, MultiBranchData::case_count_offset(table_index));
} else {
increment_md_counter_at(md, data, MultiBranchData::default_count_offset());
}
}
C:\hotspot-69087d08d473\src\share\vm/opto/phase.cpp
#include "precompiled.hpp"
#include "code/nmethod.hpp"
#include "compiler/compileBroker.hpp"
#include "opto/compile.hpp"
#include "opto/matcher.hpp"
#include "opto/node.hpp"
#include "opto/phase.hpp"
#ifndef PRODUCT
int Phase::_total_bytes_compiled = 0;
elapsedTimer Phase::_t_totalCompilation;
elapsedTimer Phase::_t_methodCompilation;
elapsedTimer Phase::_t_stubCompilation;
#endif
elapsedTimer Phase::_t_parser;
elapsedTimer Phase::_t_optimizer;
elapsedTimer Phase::_t_escapeAnalysis;
elapsedTimer Phase::_t_connectionGraph;
elapsedTimer Phase::_t_idealLoop;
elapsedTimer Phase::_t_ccp;
elapsedTimer Phase::_t_matcher;
elapsedTimer Phase::_t_registerAllocation;
elapsedTimer Phase::_t_output;
#ifndef PRODUCT
elapsedTimer Phase::_t_graphReshaping;
elapsedTimer Phase::_t_scheduler;
elapsedTimer Phase::_t_blockOrdering;
elapsedTimer Phase::_t_macroEliminate;
elapsedTimer Phase::_t_macroExpand;
elapsedTimer Phase::_t_peephole;
elapsedTimer Phase::_t_postalloc_expand;
elapsedTimer Phase::_t_codeGeneration;
elapsedTimer Phase::_t_registerMethod;
elapsedTimer Phase::_t_temporaryTimer1;
elapsedTimer Phase::_t_temporaryTimer2;
elapsedTimer Phase::_t_idealLoopVerify;
elapsedTimer Phase::_t_iterGVN;
elapsedTimer Phase::_t_iterGVN2;
elapsedTimer Phase::_t_incrInline;
elapsedTimer Phase::_t_renumberLive;
elapsedTimer Phase::_t_ctorChaitin;
elapsedTimer Phase::_t_buildIFGphysical;
elapsedTimer Phase::_t_computeLive;
elapsedTimer Phase::_t_regAllocSplit;
elapsedTimer Phase::_t_postAllocCopyRemoval;
elapsedTimer Phase::_t_mergeMultidefs;
elapsedTimer Phase::_t_fixupSpills;
elapsedTimer Phase::_t_instrSched;
elapsedTimer Phase::_t_buildOopMaps;
#endif
Phase::Phase( PhaseNumber pnum ) : _pnum(pnum), C( pnum == Compiler ? NULL : Compile::current()) {
CompileBroker::maybe_block();
}
#ifndef PRODUCT
static const double minimum_reported_time = 0.0001; // seconds
static const double expected_method_compile_coverage = 0.97; // %
static const double minimum_meaningful_method_compile = 2.00; // seconds
void Phase::print_timers() {
tty->print_cr ("Accumulated compiler times:");
tty->print_cr ("---------------------------");
tty->print_cr (" Total compilation: %3.3f sec.", Phase::_t_totalCompilation.seconds());
tty->print (" method compilation : %3.3f sec", Phase::_t_methodCompilation.seconds());
tty->print ("/%d bytes",_total_bytes_compiled);
tty->print_cr (" (%3.0f bytes per sec) ", Phase::_total_bytes_compiled / Phase::_t_methodCompilation.seconds());
tty->print_cr (" stub compilation : %3.3f sec.", Phase::_t_stubCompilation.seconds());
tty->print_cr (" Phases:");
tty->print_cr (" parse : %3.3f sec", Phase::_t_parser.seconds());
tty->print_cr (" optimizer : %3.3f sec", Phase::_t_optimizer.seconds());
if( Verbose || WizardMode ) {
if (DoEscapeAnalysis) {
tty->print_cr (" escape analysis: %3.3f sec", Phase::_t_escapeAnalysis.seconds());
tty->print_cr (" connection graph: %3.3f sec", Phase::_t_connectionGraph.seconds());
tty->print_cr (" macroEliminate : %3.3f sec", Phase::_t_macroEliminate.seconds());
}
tty->print_cr (" iterGVN : %3.3f sec", Phase::_t_iterGVN.seconds());
tty->print_cr (" incrInline : %3.3f sec", Phase::_t_incrInline.seconds());
tty->print_cr (" renumberLive : %3.3f sec", Phase::_t_renumberLive.seconds());
tty->print_cr (" idealLoop : %3.3f sec", Phase::_t_idealLoop.seconds());
tty->print_cr (" idealLoopVerify: %3.3f sec", Phase::_t_idealLoopVerify.seconds());
tty->print_cr (" ccp : %3.3f sec", Phase::_t_ccp.seconds());
tty->print_cr (" iterGVN2 : %3.3f sec", Phase::_t_iterGVN2.seconds());
tty->print_cr (" macroExpand : %3.3f sec", Phase::_t_macroExpand.seconds());
tty->print_cr (" graphReshape : %3.3f sec", Phase::_t_graphReshaping.seconds());
double optimizer_subtotal = Phase::_t_iterGVN.seconds() + Phase::_t_iterGVN2.seconds() + Phase::_t_renumberLive.seconds() +
Phase::_t_escapeAnalysis.seconds() + Phase::_t_macroEliminate.seconds() +
Phase::_t_idealLoop.seconds() + Phase::_t_ccp.seconds() +
Phase::_t_macroExpand.seconds() + Phase::_t_graphReshaping.seconds();
double percent_of_optimizer = ((optimizer_subtotal == 0.0) ? 0.0 : (optimizer_subtotal / Phase::_t_optimizer.seconds() * 100.0));
tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", optimizer_subtotal, percent_of_optimizer);
}
tty->print_cr (" matcher : %3.3f sec", Phase::_t_matcher.seconds());
tty->print_cr (" scheduler : %3.3f sec", Phase::_t_scheduler.seconds());
tty->print_cr (" regalloc : %3.3f sec", Phase::_t_registerAllocation.seconds());
if( Verbose || WizardMode ) {
tty->print_cr (" ctorChaitin : %3.3f sec", Phase::_t_ctorChaitin.seconds());
tty->print_cr (" buildIFG : %3.3f sec", Phase::_t_buildIFGphysical.seconds());
tty->print_cr (" computeLive : %3.3f sec", Phase::_t_computeLive.seconds());
tty->print_cr (" regAllocSplit : %3.3f sec", Phase::_t_regAllocSplit.seconds());
tty->print_cr (" postAllocCopyRemoval: %3.3f sec", Phase::_t_postAllocCopyRemoval.seconds());
tty->print_cr (" mergeMultidefs: %3.3f sec", Phase::_t_mergeMultidefs.seconds());
tty->print_cr (" fixupSpills : %3.3f sec", Phase::_t_fixupSpills.seconds());
double regalloc_subtotal = Phase::_t_ctorChaitin.seconds() +
Phase::_t_buildIFGphysical.seconds() + Phase::_t_computeLive.seconds() +
Phase::_t_regAllocSplit.seconds() + Phase::_t_fixupSpills.seconds() +
Phase::_t_postAllocCopyRemoval.seconds() + Phase::_t_mergeMultidefs.seconds();
double percent_of_regalloc = ((regalloc_subtotal == 0.0) ? 0.0 : (regalloc_subtotal / Phase::_t_registerAllocation.seconds() * 100.0));
tty->print_cr (" subtotal : %3.3f sec, %3.2f %%", regalloc_subtotal, percent_of_regalloc);
}
tty->print_cr (" blockOrdering : %3.3f sec", Phase::_t_blockOrdering.seconds());
tty->print_cr (" peephole : %3.3f sec", Phase::_t_peephole.seconds());
if (Matcher::require_postalloc_expand) {
tty->print_cr (" postalloc_expand: %3.3f sec", Phase::_t_postalloc_expand.seconds());
}
tty->print_cr (" codeGen : %3.3f sec", Phase::_t_codeGeneration.seconds());
tty->print_cr (" install_code : %3.3f sec", Phase::_t_registerMethod.seconds());
tty->print_cr (" -------------- : ----------");
double phase_subtotal = Phase::_t_parser.seconds() +
Phase::_t_optimizer.seconds() + Phase::_t_graphReshaping.seconds() +
Phase::_t_matcher.seconds() + Phase::_t_scheduler.seconds() +
Phase::_t_registerAllocation.seconds() + Phase::_t_blockOrdering.seconds() +
Phase::_t_codeGeneration.seconds() + Phase::_t_registerMethod.seconds();
double percent_of_method_compile = ((phase_subtotal == 0.0) ? 0.0 : phase_subtotal / Phase::_t_methodCompilation.seconds()) * 100.0;
tty->print_cr (" total : %3.3f sec, %3.2f %%", phase_subtotal, percent_of_method_compile);
assert( percent_of_method_compile > expected_method_compile_coverage ||
phase_subtotal < minimum_meaningful_method_compile,
"Must account for method compilation");
if( Phase::_t_temporaryTimer1.seconds() > minimum_reported_time ) {
tty->cr();
tty->print_cr (" temporaryTimer1: %3.3f sec", Phase::_t_temporaryTimer1.seconds());
}
if( Phase::_t_temporaryTimer2.seconds() > minimum_reported_time ) {
tty->cr();
tty->print_cr (" temporaryTimer2: %3.3f sec", Phase::_t_temporaryTimer2.seconds());
}
tty->print_cr (" output : %3.3f sec", Phase::_t_output.seconds());
tty->print_cr (" isched : %3.3f sec", Phase::_t_instrSched.seconds());
tty->print_cr (" bldOopMaps : %3.3f sec", Phase::_t_buildOopMaps.seconds());
}
#endif
C:\hotspot-69087d08d473\src\share\vm/opto/phase.hpp
#ifndef SHARE_VM_OPTO_PHASE_HPP
#define SHARE_VM_OPTO_PHASE_HPP
#include "libadt/port.hpp"
#include "runtime/timer.hpp"
class Compile;
class Phase : public StackObj {
public:
enum PhaseNumber {
Compiler, // Top-level compiler phase
Parser, // Parse bytecodes
Remove_Useless, // Remove useless nodes
Remove_Useless_And_Renumber_Live, // First, remove useless nodes from the graph. Then, renumber live nodes.
Optimistic, // Optimistic analysis phase
GVN, // Pessimistic global value numbering phase
Ins_Select, // Instruction selection phase
CFG, // Build a CFG
BlockLayout, // Linear ordering of blocks
Register_Allocation, // Register allocation, duh
LIVE, // Dragon-book LIVE range problem
StringOpts, // StringBuilder related optimizations
Interference_Graph, // Building the IFG
Coalesce, // Coalescing copies
Ideal_Loop, // Find idealized trip-counted loops
Macro_Expand, // Expand macro nodes
Peephole, // Apply peephole optimizations
last_phase
};
protected:
enum PhaseNumber _pnum; // Phase number (for stat gathering)
#ifndef PRODUCT
static int _total_bytes_compiled;
static elapsedTimer _t_totalCompilation;
static elapsedTimer _t_methodCompilation;
static elapsedTimer _t_stubCompilation;
#endif
static elapsedTimer _t_parser;
static elapsedTimer _t_optimizer;
public:
static elapsedTimer _t_escapeAnalysis;
static elapsedTimer _t_connectionGraph;
protected:
static elapsedTimer _t_idealLoop;
static elapsedTimer _t_ccp;
static elapsedTimer _t_matcher;
static elapsedTimer _t_registerAllocation;
static elapsedTimer _t_output;
#ifndef PRODUCT
static elapsedTimer _t_graphReshaping;
static elapsedTimer _t_scheduler;
static elapsedTimer _t_blockOrdering;
static elapsedTimer _t_macroEliminate;
static elapsedTimer _t_macroExpand;
static elapsedTimer _t_peephole;
static elapsedTimer _t_postalloc_expand;
static elapsedTimer _t_codeGeneration;
static elapsedTimer _t_registerMethod;
static elapsedTimer _t_temporaryTimer1;
static elapsedTimer _t_temporaryTimer2;
static elapsedTimer _t_idealLoopVerify;
static elapsedTimer _t_iterGVN;
static elapsedTimer _t_iterGVN2;
static elapsedTimer _t_incrInline;
static elapsedTimer _t_renumberLive;
static elapsedTimer _t_ctorChaitin;
static elapsedTimer _t_buildIFGphysical;
static elapsedTimer _t_computeLive;
static elapsedTimer _t_regAllocSplit;
static elapsedTimer _t_postAllocCopyRemoval;
static elapsedTimer _t_mergeMultidefs;
static elapsedTimer _t_fixupSpills;
static elapsedTimer _t_instrSched;
static elapsedTimer _t_buildOopMaps;
#endif
public:
Compile * C;
Phase( PhaseNumber pnum );
#ifndef PRODUCT
static void print_timers();
#endif
};
#endif // SHARE_VM_OPTO_PHASE_HPP
C:\hotspot-69087d08d473\src\share\vm/opto/phasetype.hpp
#ifndef SHARE_VM_OPTO_PHASETYPE_HPP
#define SHARE_VM_OPTO_PHASETYPE_HPP
enum CompilerPhaseType {
PHASE_BEFORE_STRINGOPTS,
PHASE_AFTER_STRINGOPTS,
PHASE_BEFORE_REMOVEUSELESS,
PHASE_AFTER_PARSING,
PHASE_ITER_GVN1,
PHASE_PHASEIDEAL_BEFORE_EA,
PHASE_ITER_GVN_AFTER_EA,
PHASE_ITER_GVN_AFTER_ELIMINATION,
PHASE_PHASEIDEALLOOP1,
PHASE_PHASEIDEALLOOP2,
PHASE_PHASEIDEALLOOP3,
PHASE_CPP1,
PHASE_ITER_GVN2,
PHASE_PHASEIDEALLOOP_ITERATIONS,
PHASE_OPTIMIZE_FINISHED,
PHASE_GLOBAL_CODE_MOTION,
PHASE_FINAL_CODE,
PHASE_AFTER_EA,
PHASE_BEFORE_CLOOPS,
PHASE_AFTER_CLOOPS,
PHASE_BEFORE_BEAUTIFY_LOOPS,
PHASE_AFTER_BEAUTIFY_LOOPS,
PHASE_BEFORE_MATCHING,
PHASE_INCREMENTAL_INLINE,
PHASE_INCREMENTAL_BOXING_INLINE,
PHASE_END,
PHASE_FAILURE,
PHASE_NUM_TYPES
};
class CompilerPhaseTypeHelper {
public:
static const char* to_string(CompilerPhaseType cpt) {
switch (cpt) {
case PHASE_BEFORE_STRINGOPTS: return "Before StringOpts";
case PHASE_AFTER_STRINGOPTS: return "After StringOpts";
case PHASE_BEFORE_REMOVEUSELESS: return "Before RemoveUseless";
case PHASE_AFTER_PARSING: return "After Parsing";
case PHASE_ITER_GVN1: return "Iter GVN 1";
case PHASE_PHASEIDEAL_BEFORE_EA: return "PhaseIdealLoop before EA";
case PHASE_ITER_GVN_AFTER_EA: return "Iter GVN after EA";
case PHASE_ITER_GVN_AFTER_ELIMINATION: return "Iter GVN after eliminating allocations and locks";
case PHASE_PHASEIDEALLOOP1: return "PhaseIdealLoop 1";
case PHASE_PHASEIDEALLOOP2: return "PhaseIdealLoop 2";
case PHASE_PHASEIDEALLOOP3: return "PhaseIdealLoop 3";
case PHASE_CPP1: return "PhaseCPP 1";
case PHASE_ITER_GVN2: return "Iter GVN 2";
case PHASE_PHASEIDEALLOOP_ITERATIONS: return "PhaseIdealLoop iterations";
case PHASE_OPTIMIZE_FINISHED: return "Optimize finished";
case PHASE_GLOBAL_CODE_MOTION: return "Global code motion";
case PHASE_FINAL_CODE: return "Final Code";
case PHASE_AFTER_EA: return "After Escape Analysis";
case PHASE_BEFORE_CLOOPS: return "Before CountedLoop";
case PHASE_AFTER_CLOOPS: return "After CountedLoop";
case PHASE_BEFORE_BEAUTIFY_LOOPS: return "Before beautify loops";
case PHASE_AFTER_BEAUTIFY_LOOPS: return "After beautify loops";
case PHASE_BEFORE_MATCHING: return "Before Matching";
case PHASE_INCREMENTAL_INLINE: return "Incremental Inline";
case PHASE_INCREMENTAL_BOXING_INLINE: return "Incremental Boxing Inline";
case PHASE_END: return "End";
case PHASE_FAILURE: return "Failure";
default:
ShouldNotReachHere();
return NULL;
}
}
};
#endif //SHARE_VM_OPTO_PHASETYPE_HPP
C:\hotspot-69087d08d473\src\share\vm/opto/phaseX.cpp
#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/block.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/connode.hpp"
#include "opto/idealGraphPrinter.hpp"
#include "opto/loopnode.hpp"
#include "opto/machnode.hpp"
#include "opto/opcodes.hpp"
#include "opto/phaseX.hpp"
#include "opto/regalloc.hpp"
#include "opto/rootnode.hpp"
#define NODE_HASH_MINIMUM_SIZE 255
NodeHash::NodeHash(uint est_max_size) :
_max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ),
_a(Thread::current()->resource_area()),
_table( NEW_ARENA_ARRAY( _a , Node* , _max ) ), // (Node**)_a->Amalloc(_max * sizeof(Node*)) ),
_inserts(0), _insert_limit( insert_limit() ),
_look_probes(0), _lookup_hits(0), _lookup_misses(0),
_total_insert_probes(0), _total_inserts(0),
_insert_probes(0), _grows(0) {
_sentinel = new (Compile::current()) ProjNode(NULL, TypeFunc::Control);
memset(_table,0,sizeof(Node*)*_max);
}
NodeHash::NodeHash(Arena *arena, uint est_max_size) :
_max( round_up(est_max_size < NODE_HASH_MINIMUM_SIZE ? NODE_HASH_MINIMUM_SIZE : est_max_size) ),
_a(arena),
_table( NEW_ARENA_ARRAY( _a , Node* , _max ) ),
_inserts(0), _insert_limit( insert_limit() ),
_look_probes(0), _lookup_hits(0), _lookup_misses(0),
_delete_probes(0), _delete_hits(0), _delete_misses(0),
_total_insert_probes(0), _total_inserts(0),
_insert_probes(0), _grows(0) {
_sentinel = new (Compile::current()) ProjNode(NULL, TypeFunc::Control);
memset(_table,0,sizeof(Node*)*_max);
}
NodeHash::NodeHash(NodeHash *nh) {
debug_only(_table = (Node**)badAddress); // interact correctly w/ operator=
}
void NodeHash::replace_with(NodeHash *nh) {
debug_only(_table = (Node**)badAddress); // interact correctly w/ operator=
}
Node *NodeHash::hash_find( const Node *n ) {
uint hash = n->hash();
if (hash == Node::NO_HASH) {
debug_only( _lookup_misses++ );
return NULL;
}
uint key = hash & (_max-1);
uint stride = key | 0x01;
debug_only( _look_probes++ );
Node *k = _table[key]; // Get hashed value
if( !k ) { // ?Miss?
debug_only( _lookup_misses++ );
return NULL; // Miss!
}
int op = n->Opcode();
uint req = n->req();
while( 1 ) { // While probing hash table
if( k->req() == req && // Same count of inputs
k->Opcode() == op ) { // Same Opcode
for( uint i=0; i<req; i++ )
if( n->in(i)!=k->in(i)) // Different inputs?
goto collision; // "goto" is a speed hack...
if( n->cmp(*k) ) { // Check for any special bits
debug_only( _lookup_hits++ );
return k; // Hit!
}
}
collision:
debug_only( _look_probes++ );
key = (key + stride/*7*/) & (_max-1); // Stride through table with relative prime
k = _table[key]; // Get hashed value
if( !k ) { // ?Miss?
debug_only( _lookup_misses++ );
return NULL; // Miss!
}
}
ShouldNotReachHere();
return NULL;
}
Node *NodeHash::hash_find_insert( Node *n ) {
uint hash = n->hash();
if (hash == Node::NO_HASH) {
debug_only( _lookup_misses++ );
return NULL;
}
uint key = hash & (_max-1);
uint stride = key | 0x01; // stride must be relatively prime to table siz
uint first_sentinel = 0; // replace a sentinel if seen.
debug_only( _look_probes++ );
Node *k = _table[key]; // Get hashed value
if( !k ) { // ?Miss?
debug_only( _lookup_misses++ );
_table[key] = n; // Insert into table!
debug_only(n->enter_hash_lock()); // Lock down the node while in the table.
check_grow(); // Grow table if insert hit limit
return NULL; // Miss!
}
else if( k == _sentinel ) {
first_sentinel = key; // Can insert here
}
int op = n->Opcode();
uint req = n->req();
while( 1 ) { // While probing hash table
if( k->req() == req && // Same count of inputs
k->Opcode() == op ) { // Same Opcode
for( uint i=0; i<req; i++ )
if( n->in(i)!=k->in(i)) // Different inputs?
goto collision; // "goto" is a speed hack...
if( n->cmp(*k) ) { // Check for any special bits
debug_only( _lookup_hits++ );
return k; // Hit!
}
}
collision:
debug_only( _look_probes++ );
key = (key + stride) & (_max-1); // Stride through table w/ relative prime
k = _table[key]; // Get hashed value
if( !k ) { // ?Miss?
debug_only( _lookup_misses++ );
key = (first_sentinel == 0) ? key : first_sentinel; // ?saw sentinel?
_table[key] = n; // Insert into table!
debug_only(n->enter_hash_lock()); // Lock down the node while in the table.
check_grow(); // Grow table if insert hit limit
return NULL; // Miss!
}
else if( first_sentinel == 0 && k == _sentinel ) {
first_sentinel = key; // Can insert here
}
}
ShouldNotReachHere();
return NULL;
}
void NodeHash::hash_insert( Node *n ) {
uint hash = n->hash();
if (hash == Node::NO_HASH) {
return;
}
check_grow();
uint key = hash & (_max-1);
uint stride = key | 0x01;
while( 1 ) { // While probing hash table
debug_only( _insert_probes++ );
Node *k = _table[key]; // Get hashed value
if( !k || (k == _sentinel) ) break; // Found a slot
assert( k != n, "already inserted" );
key = (key + stride) & (_max-1); // Stride through table w/ relative prime
}
_table[key] = n; // Insert into table!
debug_only(n->enter_hash_lock()); // Lock down the node while in the table.
}
bool NodeHash::hash_delete( const Node *n ) {
Node *k;
uint hash = n->hash();
if (hash == Node::NO_HASH) {
debug_only( _delete_misses++ );
return false;
}
uint key = hash & (_max-1);
uint stride = key | 0x01;
debug_only( uint counter = 0; );
for( ; /* (k != NULL) && (k != _sentinel) */; ) {
debug_only( counter++ );
debug_only( _delete_probes++ );
k = _table[key]; // Get hashed value
if( !k ) { // Miss?
debug_only( _delete_misses++ );
#ifdef ASSERT
if( VerifyOpto ) {
for( uint i=0; i < _max; i++ )
assert( _table[i] != n, "changed edges with rehashing" );
}
#endif
return false; // Miss! Not in chain
}
else if( n == k ) {
debug_only( _delete_hits++ );
_table[key] = _sentinel; // Hit! Label as deleted entry
debug_only(((Node*)n)->exit_hash_lock()); // Unlock the node upon removal from table.
return true;
}
else {
key = (key + stride/*7*/) & (_max-1);
assert( counter <= _insert_limit, "Cycle in hash-table");
}
}
ShouldNotReachHere();
return false;
}
uint NodeHash::round_up( uint x ) {
x += (x>>2); // Add 25% slop
if( x <16 ) return 16; // Small stuff
uint i=16;
while( i < x ) i <<= 1; // Double to fit
return i; // Return hash table size
}
void NodeHash::grow() {
uint old_max = _max;
Node **old_table = _table;
_grows++;
_total_inserts += _inserts;
_total_insert_probes += _insert_probes;
_inserts = 0;
_insert_probes = 0;
_max = _max << 1;
_table = NEW_ARENA_ARRAY( _a , Node* , _max ); // (Node**)_a->Amalloc( _max * sizeof(Node*) );
memset(_table,0,sizeof(Node*)*_max);
_insert_limit = insert_limit();
for( uint i = 0; i < old_max; i++ ) {
Node *m = *old_table++;
if( !m || m == _sentinel ) continue;
debug_only(m->exit_hash_lock()); // Unlock the node upon removal from old table.
hash_insert(m);
}
}
void NodeHash::clear() {
#ifdef ASSERT
for (uint i = 0; i < _max; i++) {
Node* n = _table[i];
if (!n || n == _sentinel) continue;
n->exit_hash_lock();
}
#endif
memset( _table, 0, _max * sizeof(Node*) );
}
void NodeHash::remove_useless_nodes(VectorSet &useful) {
uint max = size();
Node *sentinel_node = sentinel();
for( uint i = 0; i < max; ++i ) {
Node *n = at(i);
if(n != NULL && n != sentinel_node && !useful.test(n->_idx)) {
debug_only(n->exit_hash_lock()); // Unlock the node when removed
_table[i] = sentinel_node; // Replace with placeholder
}
}
}
void NodeHash::check_no_speculative_types() {
#ifdef ASSERT
uint max = size();
Node *sentinel_node = sentinel();
for (uint i = 0; i < max; ++i) {
Node *n = at(i);
if(n != NULL && n != sentinel_node && n->is_Type()) {
TypeNode* tn = n->as_Type();
const Type* t = tn->type();
const Type* t_no_spec = t->remove_speculative();
assert(t == t_no_spec, "dead node in hash table or missed node during speculative cleanup");
}
}
#endif
}
#ifndef PRODUCT
void NodeHash::dump() {
_total_inserts += _inserts;
_total_insert_probes += _insert_probes;
if (PrintCompilation && PrintOptoStatistics && Verbose && (_inserts > 0)) {
if (WizardMode) {
for (uint i=0; i<_max; i++) {
if (_table[i])
tty->print("%d/%d/%d ",i,_table[i]->hash()&(_max-1),_table[i]->_idx);
}
}
tty->print("\nGVN Hash stats: %d grows to %d max_size\n", _grows, _max);
tty->print(" %d/%d (%8.1f%% full)\n", _inserts, _max, (double)_inserts/_max*100.0);
tty->print(" %dp/(%dh+%dm) (%8.2f probes/lookup)\n", _look_probes, _lookup_hits, _lookup_misses, (double)_look_probes/(_lookup_hits+_lookup_misses));
tty->print(" %dp/%di (%8.2f probes/insert)\n", _total_insert_probes, _total_inserts, (double)_total_insert_probes/_total_inserts);
assert((_lookup_misses+_lookup_hits)*4+100 >= _look_probes, "bad hash function");
assert( _inserts+(_inserts>>3) < _max, "table too full" );
assert( _inserts*3+100 >= _insert_probes, "bad hash function" );
}
}
Node *NodeHash::find_index(uint idx) { // For debugging
for( uint i = 0; i < _max; i++ ) {
Node *m = _table[i];
if( !m || m == _sentinel ) continue;
if( m->_idx == (uint)idx ) return m;
}
return NULL;
}
#endif
#ifdef ASSERT
NodeHash::~NodeHash() {
if (_table != (Node**)badAddress) clear();
}
void NodeHash::operator=(const NodeHash& nh) {
if (&nh == this) return;
if (_table != (Node**)badAddress) clear();
memcpy(this, &nh, sizeof(*this));
((NodeHash*)&nh)->_table = (Node**)badAddress;
}
#endif
PhaseRemoveUseless::PhaseRemoveUseless(PhaseGVN *gvn, Unique_Node_List *worklist, PhaseNumber phase_num) : Phase(phase_num),
_useful(Thread::current()->resource_area()) {
if( !UseLoopSafepoints || !OptoRemoveUseless ) return;
C->identify_useful_nodes(_useful);
C->update_dead_node_list(_useful);
gvn->remove_useless_nodes(_useful.member_set());
worklist->remove_useless_nodes(_useful.member_set());
C->remove_useless_nodes(_useful);
}
PhaseRenumberLive::PhaseRenumberLive(PhaseGVN* gvn,
Unique_Node_List* worklist, Unique_Node_List* new_worklist,
PhaseNumber phase_num) :
PhaseRemoveUseless(gvn, worklist, Remove_Useless_And_Renumber_Live) {
assert(RenumberLiveNodes, "RenumberLiveNodes must be set to true for node renumbering to take place");
assert(C->live_nodes() == _useful.size(), "the number of live nodes must match the number of useful nodes");
assert(gvn->nodes_size() == 0, "GVN must not contain any nodes at this point");
uint old_unique_count = C->unique();
uint live_node_count = C->live_nodes();
uint worklist_size = worklist->size();
Type_Array new_type_array(C->comp_arena());
uint current_idx = 0; // The current new node ID. Incremented after every assignment.
for (uint i = 0; i < _useful.size(); i++) {
Node* n = _useful.at(i);
assert(!n->is_Phi() || n->as_Phi()->inst_mem_id() == -1, "should not be linked to data Phi");
const Type* type = gvn->type_or_null(n);
new_type_array.map(current_idx, type);
bool in_worklist = false;
if (worklist->member(n)) {
in_worklist = true;
}
n->set_idx(current_idx); // Update node ID.
if (in_worklist) {
new_worklist->push(n);
}
current_idx++;
}
assert(worklist_size == new_worklist->size(), "the new worklist must have the same size as the original worklist");
assert(live_node_count == current_idx, "all live nodes must be processed");
gvn->replace_types(new_type_array);
C->set_unique(live_node_count);
C->reset_dead_node_list();
}
PhaseTransform::PhaseTransform( PhaseNumber pnum ) : Phase(pnum),
_arena(Thread::current()->resource_area()),
_nodes(_arena),
_types(_arena)
{
init_con_caches();
#ifndef PRODUCT
clear_progress();
clear_transforms();
set_allow_progress(true);
#endif
_types.map(C->unique(), NULL);
}
PhaseTransform::PhaseTransform( Arena *arena, PhaseNumber pnum ) : Phase(pnum),
_arena(arena),
_nodes(arena),
_types(arena)
{
init_con_caches();
#ifndef PRODUCT
clear_progress();
clear_transforms();
set_allow_progress(true);
#endif
_types.map(C->unique(), NULL);
}
PhaseTransform::PhaseTransform( PhaseTransform *pt, PhaseNumber pnum ) : Phase(pnum),
_arena(pt->_arena),
_nodes(pt->_nodes),
_types(pt->_types)
{
init_con_caches();
#ifndef PRODUCT
clear_progress();
clear_transforms();
set_allow_progress(true);
#endif
}
void PhaseTransform::init_con_caches() {
memset(_icons,0,sizeof(_icons));
memset(_lcons,0,sizeof(_lcons));
memset(_zcons,0,sizeof(_zcons));
}
const TypeInt* PhaseTransform::find_int_type(Node* n) {
if (n == NULL) return NULL;
const Type* t = type_or_null(n);
if (t == NULL) return NULL;
return t->isa_int();
}
const TypeLong* PhaseTransform::find_long_type(Node* n) {
if (n == NULL) return NULL;
const Type* t = type_or_null(n);
if (t == NULL) return NULL;
return t->isa_long();
}
#ifndef PRODUCT
void PhaseTransform::dump_old2new_map() const {
_nodes.dump();
}
void PhaseTransform::dump_new( uint nidx ) const {
for( uint i=0; i<_nodes.Size(); i++ )
if( _nodes[i] && _nodes[i]->_idx == nidx ) {
_nodes[i]->dump();
tty->cr();
tty->print_cr("Old index= %d",i);
return;
}
tty->print_cr("Node %d not found in the new indices", nidx);
}
void PhaseTransform::dump_types( ) const {
_types.dump();
}
void PhaseTransform::dump_nodes_and_types(const Node *root, uint depth, bool only_ctrl) {
VectorSet visited(Thread::current()->resource_area());
dump_nodes_and_types_recur( root, depth, only_ctrl, visited );
}
void PhaseTransform::dump_nodes_and_types_recur( const Node *n, uint depth, bool only_ctrl, VectorSet &visited) {
if( !n ) return;
if( depth == 0 ) return;
if( visited.test_set(n->_idx) ) return;
for( uint i=0; i<n->len(); i++ ) {
if( only_ctrl && !(n->is_Region()) && i != TypeFunc::Control ) continue;
dump_nodes_and_types_recur( n->in(i), depth-1, only_ctrl, visited );
}
n->dump();
if (type_or_null(n) != NULL) {
tty->print(" "); type(n)->dump(); tty->cr();
}
}
#endif
PhaseValues::PhaseValues( Arena *arena, uint est_max_size ) : PhaseTransform(arena, GVN), _table(arena, est_max_size) {
NOT_PRODUCT( clear_new_values(); )
}
PhaseValues::PhaseValues( PhaseValues *ptv ) : PhaseTransform( ptv, GVN ),
_table(&ptv->_table) {
NOT_PRODUCT( clear_new_values(); )
}
PhaseValues::PhaseValues( PhaseValues *ptv, const char *dummy ) : PhaseTransform( ptv, GVN ),
_table(ptv->arena(),ptv->_table.size()) {
NOT_PRODUCT( clear_new_values(); )
}
#ifndef PRODUCT
PhaseValues::~PhaseValues() {
_table.dump();
if( PrintCompilation && Verbose && WizardMode ) {
tty->print("\n%sValues: %d nodes ---> %d/%d (%d)",
is_IterGVN() ? "Iter" : " ", C->unique(), made_progress(), made_transforms(), made_new_values());
if( made_transforms() != 0 ) {
tty->print_cr(" ratio %f", made_progress()/(float)made_transforms() );
} else {
tty->cr();
}
}
}
#endif
ConNode* PhaseTransform::makecon(const Type *t) {
assert(t->singleton(), "must be a constant");
assert(!t->empty() || t == Type::TOP, "must not be vacuous range");
switch (t->base()) { // fast paths
case Type::Half:
case Type::Top: return (ConNode*) C->top();
case Type::Int: return intcon( t->is_int()->get_con() );
case Type::Long: return longcon( t->is_long()->get_con() );
}
if (t->is_zero_type())
return zerocon(t->basic_type());
return uncached_makecon(t);
}
ConNode* PhaseValues::uncached_makecon(const Type *t) {
assert(t->singleton(), "must be a constant");
ConNode* x = ConNode::make(C, t);
ConNode* k = (ConNode*)hash_find_insert(x); // Value numbering
if (k == NULL) {
set_type(x, t); // Missed, provide type mapping
GrowableArray<Node_Notes*>* nna = C->node_note_array();
if (nna != NULL) {
Node_Notes* loc = C->locate_node_notes(nna, x->_idx, true);
loc->clear(); // do not put debug info on constants
}
} else {
x->destruct(); // Hit, destroy duplicate constant
x = k; // use existing constant
}
return x;
}
ConINode* PhaseTransform::intcon(int i) {
if (i >= _icon_min && i <= _icon_max) {
ConINode* icon = _icons[i-_icon_min];
if (icon != NULL && icon->in(TypeFunc::Control) != NULL)
return icon;
}
ConINode* icon = (ConINode*) uncached_makecon(TypeInt::make(i));
assert(icon->is_Con(), "");
if (i >= _icon_min && i <= _icon_max)
_icons[i-_icon_min] = icon; // Cache small integers
return icon;
}
ConLNode* PhaseTransform::longcon(jlong l) {
if (l >= _lcon_min && l <= _lcon_max) {
ConLNode* lcon = _lcons[l-_lcon_min];
if (lcon != NULL && lcon->in(TypeFunc::Control) != NULL)
return lcon;
}
ConLNode* lcon = (ConLNode*) uncached_makecon(TypeLong::make(l));
assert(lcon->is_Con(), "");
if (l >= _lcon_min && l <= _lcon_max)
_lcons[l-_lcon_min] = lcon; // Cache small integers
return lcon;
}
ConNode* PhaseTransform::zerocon(BasicType bt) {
assert((uint)bt <= _zcon_max, "domain check");
ConNode* zcon = _zcons[bt];
if (zcon != NULL && zcon->in(TypeFunc::Control) != NULL)
return zcon;
zcon = (ConNode*) uncached_makecon(Type::get_zero_type(bt));
_zcons[bt] = zcon;
return zcon;
}
Node *PhaseGVN::transform( Node *n ) {
return transform_no_reclaim(n);
}
Node *PhaseGVN::transform_no_reclaim( Node *n ) {
NOT_PRODUCT( set_transforms(); )
Node *k = n;
NOT_PRODUCT( uint loop_count = 0; )
while( 1 ) {
Node *i = k->Ideal(this, /*can_reshape=*/false);
if( !i ) break;
assert( i->_idx >= k->_idx, "Idealize should return new nodes, use Identity to return old nodes" );
k = i;
assert(loop_count++ < K, "infinite loop in PhaseGVN::transform");
}
NOT_PRODUCT( if( loop_count != 0 ) { set_progress(); } )
ensure_type_or_null(k);
const Type *t = k->Value(this); // Get runtime Value set
assert(t != NULL, "value sanity");
if (type_or_null(k) != t) {
#ifndef PRODUCT
if (type_or_null(k) == NULL) {
inc_new_values();
set_progress();
}
#endif
set_type(k, t);
k->raise_bottom_type(t);
}
if( t->singleton() && !k->is_Con() ) {
NOT_PRODUCT( set_progress(); )
return makecon(t); // Turn into a constant
}
Node *i = k->Identity(this); // Look for a nearby replacement
if( i != k ) { // Found? Return replacement!
NOT_PRODUCT( set_progress(); )
return i;
}
i = hash_find_insert(k); // Insert if new
if( i && (i != k) ) {
NOT_PRODUCT( set_progress(); )
return i;
}
return k;
}
#ifdef ASSERT
void PhaseGVN::dead_loop_check( Node *n ) {
if (n != NULL && !n->is_dead_loop_safe() && !n->is_CFG()) {
bool no_dead_loop = true;
uint cnt = n->req();
for (uint i = 1; i < cnt && no_dead_loop; i++) {
Node *in = n->in(i);
if (in == n) {
no_dead_loop = false;
} else if (in != NULL && !in->is_dead_loop_safe()) {
uint icnt = in->req();
for (uint j = 1; j < icnt && no_dead_loop; j++) {
if (in->in(j) == n || in->in(j) == in)
no_dead_loop = false;
}
}
}
if (!no_dead_loop) n->dump(3);
assert(no_dead_loop, "dead loop detected");
}
}
#endif
PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ) : PhaseGVN(igvn,dummy), _worklist( ),
_stack(C->live_nodes() >> 1),
_delay_transform(false) {
}
PhaseIterGVN::PhaseIterGVN( PhaseIterGVN *igvn ) : PhaseGVN(igvn),
_worklist( igvn->_worklist ),
_stack( igvn->_stack ),
_delay_transform(igvn->_delay_transform)
{
}
PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn),
_worklist(*C->for_igvn()),
_stack(C->comp_arena(), 32),
_delay_transform(false)
{
uint max;
max = _table.size();
for( uint i = 0; i < max; ++i ) {
Node *n = _table.at(i);
if(n != NULL && n != _table.sentinel() && n->outcnt() == 0) {
if( n->is_top() ) continue;
assert( false, "Parse::remove_useless_nodes missed this node");
hash_delete(n);
}
}
max = _worklist.size();
for( uint j = 0; j < max; j++ ) {
Node *n = _worklist.at(j);
uint uop = n->Opcode();
if( uop == Op_Phi || uop == Op_Region ||
n->is_Type() ||
n->is_Mem() )
add_users_to_worklist(n);
}
}
#ifndef PRODUCT
void PhaseIterGVN::verify_step(Node* n) {
_verify_window[_verify_counter % _verify_window_size] = n;
++_verify_counter;
ResourceMark rm;
ResourceArea *area = Thread::current()->resource_area();
VectorSet old_space(area), new_space(area);
if (C->unique() < 1000 ||
0 == _verify_counter % (C->unique() < 10000 ? 10 : 100)) {
++_verify_full_passes;
Node::verify_recur(C->root(), -1, old_space, new_space);
}
const int verify_depth = 4;
for ( int i = 0; i < _verify_window_size; i++ ) {
Node* n = _verify_window[i];
if ( n == NULL ) continue;
if( n->in(0) == NodeSentinel ) { // xform_idom
_verify_window[i] = n->in(1);
--i; continue;
}
Node::verify_recur(n, verify_depth, old_space, new_space);
}
}
#endif
void PhaseIterGVN::init_worklist( Node *n ) {
if( _worklist.member(n) ) return;
_worklist.push(n);
uint cnt = n->req();
for( uint i =0 ; i < cnt; i++ ) {
Node *m = n->in(i);
if( m ) init_worklist(m);
}
}
void PhaseIterGVN::optimize() {
debug_only(uint num_processed = 0;);
#ifndef PRODUCT
{
_verify_counter = 0;
_verify_full_passes = 0;
for ( int i = 0; i < _verify_window_size; i++ ) {
_verify_window[i] = NULL;
}
}
#endif
#ifdef ASSERT
Node* prev = NULL;
uint rep_cnt = 0;
#endif
uint loop_count = 0;
while( _worklist.size() ) {
if (C->check_node_count(NodeLimitFudgeFactor * 2,
"out of nodes optimizing method")) {
return;
}
Node *n = _worklist.pop();
if (++loop_count >= K * C->live_nodes()) {
debug_only(n->dump(4);)
assert(false, "infinite loop in PhaseIterGVN::optimize");
C->record_method_not_compilable("infinite loop in PhaseIterGVN::optimize");
return;
}
#ifdef ASSERT
if (n == prev) {
if (++rep_cnt > 3) {
n->dump(4);
assert(false, "loop in Ideal transformation");
}
} else {
rep_cnt = 0;
}
prev = n;
#endif
if (TraceIterativeGVN && Verbose) {
tty->print(" Pop ");
NOT_PRODUCT( n->dump(); )
debug_only(if( (num_processed++ % 100) == 0 ) _worklist.print_set();)
}
if (n->outcnt() != 0) {
#ifndef PRODUCT
uint wlsize = _worklist.size();
const Type* oldtype = type_or_null(n);
#endif //PRODUCT
Node *nn = transform_old(n);
#ifndef PRODUCT
if (TraceIterativeGVN) {
const Type* newtype = type_or_null(n);
if (nn != n) {
tty->print("< ");
if (oldtype != newtype && oldtype != NULL) {
oldtype->dump();
}
do { tty->print("\t"); } while (tty->position() < 16);
tty->print("<");
n->dump();
}
if (oldtype != newtype || nn != n) {
if (oldtype == NULL) {
tty->print("* ");
} else if (nn != n) {
tty->print("> ");
} else {
tty->print("= ");
}
if (newtype == NULL) {
tty->print("null");
} else {
newtype->dump();
}
do { tty->print("\t"); } while (tty->position() < 16);
nn->dump();
}
if (Verbose && wlsize < _worklist.size()) {
tty->print(" Push {");
while (wlsize != _worklist.size()) {
Node* pushed = _worklist.at(wlsize++);
tty->print(" %d", pushed->_idx);
}
tty->print_cr(" }");
}
}
if( VerifyIterativeGVN && nn != n ) {
verify_step((Node*) NULL); // ignore n, it might be subsumed
}
#endif
} else if (!n->is_top()) {
remove_dead_node(n);
}
}
#ifndef PRODUCT
C->verify_graph_edges();
if( VerifyOpto && allow_progress() ) {
C->root()->verify();
{ // Check if any progress was missed using IterGVN
ResourceMark rm;
PhaseIterGVN igvn2(this,"Verify"); // Fresh and clean!
igvn2.init_worklist(C->root());
igvn2.set_allow_progress(false);
igvn2.optimize();
igvn2.set_allow_progress(true);
}
}
if ( VerifyIterativeGVN && PrintOpto ) {
if ( _verify_counter == _verify_full_passes )
tty->print_cr("VerifyIterativeGVN: %d transforms and verify passes",
(int) _verify_full_passes);
else
tty->print_cr("VerifyIterativeGVN: %d transforms, %d full verify passes",
(int) _verify_counter, (int) _verify_full_passes);
}
#endif
}
Node* PhaseIterGVN::register_new_node_with_optimizer(Node* n, Node* orig) {
set_type_bottom(n);
_worklist.push(n);
if (orig != NULL) C->copy_node_notes_to(n, orig);
return n;
}
Node *PhaseIterGVN::transform( Node *n ) {
if (_delay_transform) {
register_new_node_with_optimizer(n);
return n;
}
ensure_type_or_null(n);
if (type_or_null(n) == NULL) {
set_type_bottom(n);
}
return transform_old(n);
}
Node *PhaseIterGVN::transform_old( Node *n ) {
#ifndef PRODUCT
debug_only(uint loop_count = 0;);
set_transforms();
#endif
_table.hash_delete(n);
if( VerifyIterativeGVN ) {
assert( !_table.find_index(n->_idx), "found duplicate entry in table");
}
Node *k = n;
DEBUG_ONLY(dead_loop_check(k);)
DEBUG_ONLY(bool is_new = (k->outcnt() == 0);)
Node *i = k->Ideal(this, /*can_reshape=*/true);
assert(i != k || is_new || i->outcnt() > 0, "don't return dead nodes");
#ifndef PRODUCT
if( VerifyIterativeGVN )
verify_step(k);
if( i && VerifyOpto ) {
if( !allow_progress() ) {
if (i->is_Add() && i->outcnt() == 1) {
} else if( i->is_If() && (i->in(0) == NULL) ) {
return i;
} else
set_progress();
} else
set_progress();
}
#endif
while( i ) {
#ifndef PRODUCT
debug_only( if( loop_count >= K ) i->dump(4); )
assert(loop_count < K, "infinite loop in PhaseIterGVN::transform");
debug_only( loop_count++; )
#endif
assert((i->_idx >= k->_idx) || i->is_top(), "Idealize should return new nodes, use Identity to return old nodes");
add_users_to_worklist( k );
if( k != i ) {
subsume_node( k, i );
k = i;
}
DEBUG_ONLY(dead_loop_check(k);)
DEBUG_ONLY(is_new = (k->outcnt() == 0);)
i = k->Ideal(this, /*can_reshape=*/true);
assert(i != k || is_new || i->outcnt() > 0, "don't return dead nodes");
#ifndef PRODUCT
if( VerifyIterativeGVN )
verify_step(k);
if( i && VerifyOpto ) set_progress();
#endif
}
ensure_type_or_null(k);
const Type *t = k->Value(this);
assert(t != NULL, "value sanity");
if (t != type_or_null(k)) {
NOT_PRODUCT( set_progress(); )
NOT_PRODUCT( inc_new_values();)
set_type(k, t);
k->raise_bottom_type(t);
add_users_to_worklist( k );
}
if( t->singleton() && !k->is_Con() ) {
NOT_PRODUCT( set_progress(); )
Node *con = makecon(t); // Make a constant
add_users_to_worklist( k );
subsume_node( k, con ); // Everybody using k now uses con
return con;
}
i = k->Identity(this); // Look for a nearby replacement
if( i != k ) { // Found? Return replacement!
NOT_PRODUCT( set_progress(); )
add_users_to_worklist( k );
subsume_node( k, i ); // Everybody using k now uses i
return i;
}
i = hash_find_insert(k); // Check for pre-existing node
if( i && (i != k) ) {
NOT_PRODUCT( set_progress(); )
add_users_to_worklist( k );
subsume_node( k, i ); // Everybody using k now uses i
return i;
}
return k;
}
const Type* PhaseIterGVN::saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const {
return new_type->narrow(old_type);
}
void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
enum DeleteProgress {
PROCESS_INPUTS,
PROCESS_OUTPUTS
};
assert(_stack.is_empty(), "not empty");
_stack.push(dead, PROCESS_INPUTS);
while (_stack.is_nonempty()) {
dead = _stack.node();
if (dead->Opcode() == Op_SafePoint) {
dead->as_SafePoint()->disconnect_from_root(this);
}
uint progress_state = _stack.index();
assert(dead != C->root(), "killing root, eh?");
assert(!dead->is_top(), "add check for top when pushing");
NOT_PRODUCT( set_progress(); )
if (progress_state == PROCESS_INPUTS) {
_stack.set_index(PROCESS_OUTPUTS);
if (!dead->is_Con()) { // Don't kill cons but uses
bool recurse = false;
_table.hash_delete( dead );
for (uint i = 0; i < dead->req(); i++) {
Node *in = dead->in(i);
if (in != NULL && in != C->top()) { // Points to something?
int nrep = dead->replace_edge(in, NULL); // Kill edges
assert((nrep > 0), "sanity");
if (in->outcnt() == 0) { // Made input go dead?
_stack.push(in, PROCESS_INPUTS); // Recursively remove
recurse = true;
} else if (in->outcnt() == 1 &&
in->has_special_unique_user()) {
_worklist.push(in->unique_out());
} else if (in->outcnt() <= 2 && dead->is_Phi()) {
if (in->Opcode() == Op_Region) {
_worklist.push(in);
} else if (in->is_Store()) {
DUIterator_Fast imax, i = in->fast_outs(imax);
_worklist.push(in->fast_out(i));
i++;
if (in->outcnt() == 2) {
_worklist.push(in->fast_out(i));
i++;
}
assert(!(i < imax), "sanity");
}
}
if (ReduceFieldZeroing && dead->is_Load() && i == MemNode::Memory &&
in->is_Proj() && in->in(0) != NULL && in->in(0)->is_Initialize()) {
for (DUIterator_Fast jmax, j = in->fast_outs(jmax); j < jmax; j++) {
Node *n = in->fast_out(j);
if (n->is_Store()) {
_worklist.push(n);
}
}
}
} // if (in != NULL && in != C->top())
} // for (uint i = 0; i < dead->req(); i++)
if (recurse) {
continue;
}
} // if (!dead->is_Con())
} // if (progress_state == PROCESS_INPUTS)
if (dead->outcnt() > 0) {
_stack.push(dead->raw_out(0), PROCESS_INPUTS);
} else {
_stack.pop();
_worklist.remove(dead);
if (!dead->is_Con()) {
C->record_dead_node(dead->_idx);
}
if (dead->is_macro()) {
C->remove_macro_node(dead);
}
if (dead->is_expensive()) {
C->remove_expensive_node(dead);
}
CastIINode* cast = dead->isa_CastII();
if (cast != NULL && cast->has_range_check()) {
C->remove_range_check_cast(cast);
}
}
} // while (_stack.is_nonempty())
}
void PhaseIterGVN::subsume_node( Node *old, Node *nn ) {
if (old->Opcode() == Op_SafePoint) {
old->as_SafePoint()->disconnect_from_root(this);
}
assert( old != hash_find(old), "should already been removed" );
assert( old != C->top(), "cannot subsume top node");
C->copy_node_notes_to(nn, old);
for (DUIterator_Last imin, i = old->last_outs(imin); i >= imin; ) {
Node* use = old->last_out(i); // for each use...
bool is_in_table = _table.hash_delete( use );
uint num_edges = 0;
for (uint jmax = use->len(), j = 0; j < jmax; j++) {
if (use->in(j) == old) {
use->set_req(j, nn);
++num_edges;
}
}
if( is_in_table ) {
hash_find_insert(use);
}
i -= num_edges; // we deleted 1 or more copies of this edge
}
if (old->is_Phi() && old->as_Phi()->type()->has_memory() && old->in(0) != NULL) {
Node* region = old->in(0);
for (DUIterator_Fast imax, i = region->fast_outs(imax); i < imax; i++) {
PhiNode* phi = region->fast_out(i)->isa_Phi();
if (phi != NULL && phi->inst_mem_id() == (int)old->_idx) {
phi->set_inst_mem_id((int)nn->_idx);
}
}
}
Node *temp = new (C) Node(1);
temp->init_req(0,nn); // Add a use to nn to prevent him from dying
remove_dead_node( old );
temp->del_req(0); // Yank bogus edge
#ifndef PRODUCT
if( VerifyIterativeGVN ) {
for ( int i = 0; i < _verify_window_size; i++ ) {
if ( _verify_window[i] == old )
_verify_window[i] = nn;
}
}
#endif
_worklist.remove(temp); // this can be necessary
temp->destruct(); // reuse the _idx of this little guy
}
void PhaseIterGVN::add_users_to_worklist0( Node *n ) {
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
_worklist.push(n->fast_out(i)); // Push on worklist
}
}
static PhiNode* countedloop_phi_from_cmp(CmpINode* cmp, Node* n) {
for (DUIterator_Fast imax, i = cmp->fast_outs(imax); i < imax; i++) {
Node* bol = cmp->fast_out(i);
for (DUIterator_Fast i2max, i2 = bol->fast_outs(i2max); i2 < i2max; i2++) {
Node* iff = bol->fast_out(i2);
if (iff->is_CountedLoopEnd()) {
CountedLoopEndNode* cle = iff->as_CountedLoopEnd();
if (cle->limit() == n) {
PhiNode* phi = cle->phi();
if (phi != NULL) {
return phi;
}
}
}
}
}
return NULL;
}
void PhaseIterGVN::add_users_to_worklist( Node *n ) {
add_users_to_worklist0(n);
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* use = n->fast_out(i); // Get use
if( use->is_Multi() || // Multi-definer? Push projs on worklist
use->is_Store() ) // Enable store/load same address
add_users_to_worklist0(use);
if (use->is_CallDynamicJava() && n == use->in(TypeFunc::Parms)) {
Node* p = use->as_CallDynamicJava()->proj_out(TypeFunc::Control);
if (p != NULL) {
add_users_to_worklist0(p);
}
}
uint use_op = use->Opcode();
if(use->is_Cmp()) { // Enable CMP/BOOL optimization
add_users_to_worklist(use); // Put Bool on worklist
if (use->outcnt() > 0) {
Node* bol = use->raw_out(0);
if (bol->outcnt() > 0) {
Node* iff = bol->raw_out(0);
if (iff->outcnt() == 2) {
Node* ifproj0 = iff->raw_out(0);
Node* ifproj1 = iff->raw_out(1);
if (ifproj0->outcnt() > 0 && ifproj1->outcnt() > 0) {
Node* region0 = ifproj0->raw_out(0);
Node* region1 = ifproj1->raw_out(0);
if( region0 == region1 )
add_users_to_worklist0(region0);
}
}
}
}
if (use_op == Op_CmpI) {
Node* phi = countedloop_phi_from_cmp((CmpINode*)use, n);
if (phi != NULL) {
_worklist.push(phi);
}
Node* in1 = use->in(1);
for (uint i = 0; i < in1->outcnt(); i++) {
if (in1->raw_out(i)->Opcode() == Op_CastII) {
Node* castii = in1->raw_out(i);
if (castii->in(0) != NULL && castii->in(0)->in(0) != NULL && castii->in(0)->in(0)->is_If()) {
Node* ifnode = castii->in(0)->in(0);
if (ifnode->in(1) != NULL && ifnode->in(1)->is_Bool() && ifnode->in(1)->in(1) == use) {
_worklist.push(castii);
}
}
}
}
}
}
if( use->is_ConstraintCast() || use->is_CheckCastPP() ) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Phi())
_worklist.push(u);
}
}
if( use_op == Op_LShiftI ) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->Opcode() == Op_RShiftI)
_worklist.push(u);
}
}
if (use_op == Op_AddI || use_op == Op_SubI) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Cmp() && (u->Opcode() == Op_CmpU)) {
_worklist.push(u);
}
}
}
if( use_op == Op_AddP ) {
for (DUIterator_Fast i2max, i2 = use->fast_outs(i2max); i2 < i2max; i2++) {
Node* u = use->fast_out(i2);
if (u->is_Mem())
_worklist.push(u);
}
}
if (use_op == Op_Allocate || use_op == Op_AllocateArray) {
InitializeNode* init = use->as_Allocate()->initialization();
if (init != NULL) {
Node* imem = init->proj_out(TypeFunc::Memory);
if (imem != NULL) add_users_to_worklist0(imem);
}
}
if (use_op == Op_Initialize) {
Node* imem = use->as_Initialize()->proj_out(TypeFunc::Memory);
if (imem != NULL) add_users_to_worklist0(imem);
}
}
}
void PhaseIterGVN::remove_speculative_types() {
assert(UseTypeSpeculation, "speculation is off");
for (uint i = 0; i < _types.Size(); i++) {
const Type* t = _types.fast_lookup(i);
if (t != NULL) {
_types.map(i, t->remove_speculative());
}
}
_table.check_no_speculative_types();
}
#ifndef PRODUCT
uint PhaseCCP::_total_invokes = 0;
uint PhaseCCP::_total_constants = 0;
#endif
PhaseCCP::PhaseCCP( PhaseIterGVN *igvn ) : PhaseIterGVN(igvn) {
NOT_PRODUCT( clear_constants(); )
assert( _worklist.size() == 0, "" );
_nodes.clear(); // Clear out from IterGVN
analyze();
}
#ifndef PRODUCT
PhaseCCP::~PhaseCCP() {
inc_invokes();
_total_constants += count_constants();
}
#endif
#ifdef ASSERT
static bool ccp_type_widens(const Type* t, const Type* t0) {
assert(t->meet(t0) == t, "Not monotonic");
switch (t->base() == t0->base() ? t->base() : Type::Top) {
case Type::Int:
assert(t0->isa_int()->_widen <= t->isa_int()->_widen, "widen increases");
break;
case Type::Long:
assert(t0->isa_long()->_widen <= t->isa_long()->_widen, "widen increases");
break;
}
return true;
}
#endif //ASSERT
void PhaseCCP::analyze() {
for (int i = C->unique() - 1; i >= 0; i--) {
_types.map(i,Type::TOP);
}
Unique_Node_List worklist;
worklist.push(C->root());
while( worklist.size() ) {
Node *n = worklist.pop();
const Type *t = n->Value(this);
if (t != type(n)) {
assert(ccp_type_widens(t, type(n)), "ccp type must widen");
#ifndef PRODUCT
if( TracePhaseCCP ) {
t->dump();
do { tty->print("\t"); } while (tty->position() < 16);
n->dump();
}
#endif
set_type(n, t);
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* m = n->fast_out(i); // Get user
if (m->is_Region()) { // New path to Region? Must recheck Phis too
for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) {
Node* p = m->fast_out(i2); // Propagate changes to uses
if (p->bottom_type() != type(p)) { // If not already bottomed out
worklist.push(p); // Propagate change to user
}
}
}
if (m->is_Call()) {
for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) {
Node* p = m->fast_out(i2); // Propagate changes to uses
if (p->is_Proj() && p->as_Proj()->_con == TypeFunc::Control) {
Node* catch_node = p->find_out_with(Op_Catch);
if (catch_node != NULL) {
worklist.push(catch_node);
}
}
}
}
if (m->bottom_type() != type(m)) { // If not already bottomed out
worklist.push(m); // Propagate change to user
}
uint m_op = m->Opcode();
if (m_op == Op_AddI || m_op == Op_SubI) {
for (DUIterator_Fast i2max, i2 = m->fast_outs(i2max); i2 < i2max; i2++) {
Node* p = m->fast_out(i2); // Propagate changes to uses
if (p->Opcode() == Op_CmpU) {
if(p->bottom_type() != type(p)) { // If not already bottomed out
worklist.push(p); // Propagate change to user
}
}
}
}
if (m_op == Op_CmpI) {
PhiNode* phi = countedloop_phi_from_cmp((CmpINode*)m, n);
if (phi != NULL) {
worklist.push(phi);
}
}
}
}
}
}
void PhaseCCP::do_transform() {
C->set_root( transform(C->root())->as_Root() );
assert( C->top(), "missing TOP node" );
assert( C->root(), "missing root" );
}
Node *PhaseCCP::transform( Node *n ) {
Node *new_node = _nodes[n->_idx]; // Check for transformed node
if( new_node != NULL )
return new_node; // Been there, done that, return old answer
new_node = transform_once(n); // Check for constant
_nodes.map( n->_idx, new_node ); // Flag as having been cloned
GrowableArray <Node *> trstack(C->live_nodes() >> 1);
trstack.push(new_node); // Process children of cloned node
while ( trstack.is_nonempty() ) {
Node *clone = trstack.pop();
uint cnt = clone->req();
for( uint i = 0; i < cnt; i++ ) { // For all inputs do
Node *input = clone->in(i);
if( input != NULL ) { // Ignore NULLs
Node *new_input = _nodes[input->_idx]; // Check for cloned input node
if( new_input == NULL ) {
new_input = transform_once(input); // Check for constant
_nodes.map( input->_idx, new_input );// Flag as having been cloned
trstack.push(new_input);
}
assert( new_input == clone->in(i), "insanity check");
}
}
}
return new_node;
}
Node *PhaseCCP::transform_once( Node *n ) {
const Type *t = type(n);
if( t->singleton() ) {
Node *nn = n; // Default is to return the original constant
if( t == Type::TOP ) {
if( C->cached_top_node() == NULL || C->cached_top_node()->in(0) == NULL ) {
C->set_cached_top_node( ConNode::make(C, Type::TOP) );
set_type(C->top(), Type::TOP);
}
nn = C->top();
}
if( !n->is_Con() ) {
if( t != Type::TOP ) {
nn = makecon(t); // ConNode::make(t);
NOT_PRODUCT( inc_constants(); )
} else if( n->is_Region() ) { // Unreachable region
n->set_req(0, NULL); // Cut selfreference
for (DUIterator i = n->outs(); n->has_out(i); i++) {
Node* m = n->out(i);
if( m->is_Phi() ) {
assert(type(m) == Type::TOP, "Unreachable region should not have live phis.");
replace_node(m, nn);
--i; // deleted this phi; rescan starting with next position
}
}
}
replace_node(n,nn); // Update DefUse edges for new constant
}
return nn;
}
if (t != n->bottom_type()) {
hash_delete(n); // changing bottom type may force a rehash
n->raise_bottom_type(t);
_worklist.push(n); // n re-enters the hash table via the worklist
}
Node *nn = n->Ideal_DU_postCCP(this);
switch( n->Opcode() ) {
case Op_FastLock: // Revisit FastLocks for lock coarsening
case Op_If:
case Op_CountedLoopEnd:
case Op_Region:
case Op_Loop:
case Op_CountedLoop:
case Op_Conv2B:
case Op_Opaque1:
case Op_Opaque2:
_worklist.push(n);
break;
default:
break;
}
if( nn ) {
_worklist.push(n);
add_users_to_worklist(n);
return nn;
}
return n;
}
const Type* PhaseCCP::saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const {
const Type* wide_type = new_type->widen(old_type, limit_type);
if (wide_type != new_type) { // did we widen?
new_type = wide_type->filter(limit_type);
}
return new_type;
}
#ifndef PRODUCT
void PhaseCCP::print_statistics() {
tty->print_cr("CCP: %d constants found: %d", _total_invokes, _total_constants);
}
#endif
#ifndef PRODUCT
uint PhasePeephole::_total_peepholes = 0;
#endif
PhasePeephole::PhasePeephole( PhaseRegAlloc *regalloc, PhaseCFG &cfg )
: PhaseTransform(Peephole), _regalloc(regalloc), _cfg(cfg) {
NOT_PRODUCT( clear_peepholes(); )
}
#ifndef PRODUCT
PhasePeephole::~PhasePeephole() {
_total_peepholes += count_peepholes();
}
#endif
Node *PhasePeephole::transform( Node *n ) {
ShouldNotCallThis();
return NULL;
}
void PhasePeephole::do_transform() {
bool method_name_not_printed = true;
for (uint block_number = 1; block_number < _cfg.number_of_blocks(); ++block_number) {
Block* block = _cfg.get_block(block_number);
bool block_not_printed = true;
uint end_index = block->number_of_nodes();
for( uint instruction_index = 1; instruction_index < end_index; ++instruction_index ) {
Node *n = block->get_node(instruction_index);
if( n->is_Mach() ) {
MachNode *m = n->as_Mach();
int deleted_count = 0;
MachNode *m2 = m->peephole( block, instruction_index, _regalloc, deleted_count, C );
if( m2 != NULL ) {
#ifndef PRODUCT
if( PrintOptoPeephole ) {
if( C->method() && method_name_not_printed ) {
C->method()->print_short_name(); tty->cr();
method_name_not_printed = false;
}
if( Verbose && block_not_printed) {
tty->print_cr("in block");
block->dump();
block_not_printed = false;
}
for( int i = (deleted_count - 1); i >= 0; --i ) {
block->get_node(instruction_index-i)->as_Mach()->format(_regalloc); tty->cr();
}
tty->print_cr("replaced with");
m2->format(_regalloc);
tty->print("\n\n");
}
#endif
uint safe_instruction_index = (instruction_index - deleted_count);
for( ; (instruction_index > safe_instruction_index); --instruction_index ) {
block->remove_node( instruction_index );
}
block->insert_node(m2, safe_instruction_index + 1);
end_index = block->number_of_nodes() - 1; // Recompute new block size
NOT_PRODUCT( inc_peepholes(); )
}
}
}
}
}
#ifndef PRODUCT
void PhasePeephole::print_statistics() {
tty->print_cr("Peephole: peephole rules applied: %d", _total_peepholes);
}
#endif
void Node::set_req_X( uint i, Node *n, PhaseIterGVN *igvn ) {
assert( is_not_dead(n), "can not use dead node");
assert( igvn->hash_find(this) != this, "Need to remove from hash before changing edges" );
Node *old = in(i);
set_req(i, n);
if( old ) {
switch (old->outcnt()) {
case 0:
if (!old->is_top())
igvn->_worklist.push( old );
break;
case 1:
if( old->is_Store() || old->has_special_unique_user() )
igvn->add_users_to_worklist( old );
break;
case 2:
if( old->is_Store() )
igvn->add_users_to_worklist( old );
if( old->Opcode() == Op_Region )
igvn->_worklist.push(old);
break;
case 3:
if( old->Opcode() == Op_Region ) {
igvn->_worklist.push(old);
igvn->add_users_to_worklist( old );
}
break;
default:
break;
}
}
}
void Node::replace_by(Node *new_node) {
assert(!is_top(), "top node has no DU info");
for (DUIterator_Last imin, i = last_outs(imin); i >= imin; ) {
Node* use = last_out(i);
uint uses_found = 0;
for (uint j = 0; j < use->len(); j++) {
if (use->in(j) == this) {
if (j < use->req())
use->set_req(j, new_node);
else use->set_prec(j, new_node);
uses_found++;
}
}
i -= uses_found; // we deleted 1 or more copies of this edge
}
}
void Type_Array::grow( uint i ) {
if( !_max ) {
_max = 1;
_types = (const Type**)_a->Amalloc( _max * sizeof(Type*) );
_types[0] = NULL;
}
uint old = _max;
while( i >= _max ) _max <<= 1; // Double to fit
_types = (const Type**)_a->Arealloc( _types, old*sizeof(Type*),_max*sizeof(Type*));
memset( &_types[old], 0, (_max-old)*sizeof(Type*) );
}
#ifndef PRODUCT
void Type_Array::dump() const {
uint max = Size();
for( uint i = 0; i < max; i++ ) {
if( _types[i] != NULL ) {
tty->print(" %d\t== ", i); _types[i]->dump(); tty->cr();
}
}
}
#endif
C:\hotspot-69087d08d473\src\share\vm/opto/phaseX.hpp
#ifndef SHARE_VM_OPTO_PHASEX_HPP
#define SHARE_VM_OPTO_PHASEX_HPP
#include "libadt/dict.hpp"
#include "libadt/vectset.hpp"
#include "memory/resourceArea.hpp"
#include "opto/memnode.hpp"
#include "opto/node.hpp"
#include "opto/phase.hpp"
#include "opto/type.hpp"
class Compile;
class ConINode;
class ConLNode;
class Node;
class Type;
class PhaseTransform;
class PhaseGVN;
class PhaseIterGVN;
class PhaseCCP;
class PhasePeephole;
class PhaseRegAlloc;
class NodeHash : public StackObj {
protected:
Arena *_a; // Arena to allocate in
uint _max; // Size of table (power of 2)
uint _inserts; // For grow and debug, count of hash_inserts
uint _insert_limit; // 'grow' when _inserts reaches _insert_limit
Node **_table; // Hash table of Node pointers
Node *_sentinel; // Replaces deleted entries in hash table
public:
NodeHash(uint est_max_size);
NodeHash(Arena *arena, uint est_max_size);
NodeHash(NodeHash *use_this_state);
#ifdef ASSERT
~NodeHash(); // Unlock all nodes upon destruction of table.
void operator=(const NodeHash&); // Unlock all nodes upon replacement of table.
#endif
Node *hash_find(const Node*);// Find an equivalent version in hash table
Node *hash_find_insert(Node*);// If not in table insert else return found node
void hash_insert(Node*); // Insert into hash table
bool hash_delete(const Node*);// Replace with _sentinel in hash table
void check_grow() {
_inserts++;
if( _inserts == _insert_limit ) { grow(); }
assert( _inserts <= _insert_limit, "hash table overflow");
assert( _inserts < _max, "hash table overflow" );
}
static uint round_up(uint); // Round up to nearest power of 2
void grow(); // Grow _table to next power of 2 and rehash
uint insert_limit() const { return _max - (_max>>2); }
void clear(); // Set all entries to NULL, keep storage.
uint size() const { return _max; }
Node *at(uint table_index) {
assert(table_index < _max, "Must be within table");
return _table[table_index];
}
void remove_useless_nodes(VectorSet &useful); // replace with sentinel
void replace_with(NodeHash* nh);
void check_no_speculative_types(); // Check no speculative part for type nodes in table
Node *sentinel() { return _sentinel; }
#ifndef PRODUCT
Node *find_index(uint idx); // For debugging
void dump(); // For debugging, dump statistics
#endif
uint _grows; // For debugging, count of table grow()s
uint _look_probes; // For debugging, count of hash probes
uint _lookup_hits; // For debugging, count of hash_finds
uint _lookup_misses; // For debugging, count of hash_finds
uint _insert_probes; // For debugging, count of hash probes
uint _delete_probes; // For debugging, count of hash probes for deletes
uint _delete_hits; // For debugging, count of hash probes for deletes
uint _delete_misses; // For debugging, count of hash probes for deletes
uint _total_inserts; // For debugging, total inserts into hash table
uint _total_insert_probes; // For debugging, total probes while inserting
};
class Type_Array : public StackObj {
Arena *_a; // Arena to allocate in
uint _max;
const Type **_types;
void grow( uint i ); // Grow array node to fit
const Type *operator[] ( uint i ) const // Lookup, or NULL for not mapped
{ return (i<_max) ? _types[i] : (Type*)NULL; }
friend class PhaseTransform;
public:
Type_Array(Arena *a) : _a(a), _max(0), _types(0) {}
Type_Array(Type_Array *ta) : _a(ta->_a), _max(ta->_max), _types(ta->_types) { }
const Type *fast_lookup(uint i) const{assert(i<_max,"oob");return _types[i];}
void map( uint i, const Type *n ) { if( i>=_max ) grow(i); _types[i] = n; }
uint Size() const { return _max; }
#ifndef PRODUCT
void dump() const;
#endif
};
class PhaseRemoveUseless : public Phase {
protected:
Unique_Node_List _useful; // Nodes reachable from root
public:
PhaseRemoveUseless(PhaseGVN *gvn, Unique_Node_List *worklist, PhaseNumber phase_num = Remove_Useless);
Unique_Node_List *get_useful() { return &_useful; }
};
class PhaseRenumberLive : public PhaseRemoveUseless {
public:
PhaseRenumberLive(PhaseGVN* gvn,
Unique_Node_List* worklist, Unique_Node_List* new_worklist,
PhaseNumber phase_num = Remove_Useless_And_Renumber_Live);
};
class PhaseTransform : public Phase {
protected:
Arena* _arena;
Node_List _nodes; // Map old node indices to new nodes.
Type_Array _types; // Map old node indices to Types.
enum { _icon_min = -1 * HeapWordSize,
_icon_max = 16 * HeapWordSize,
_lcon_min = _icon_min,
_lcon_max = _icon_max,
_zcon_max = (uint)T_CONFLICT
};
ConINode* _icons[_icon_max - _icon_min + 1]; // cached jint constant nodes
ConLNode* _lcons[_lcon_max - _lcon_min + 1]; // cached jlong constant nodes
ConNode* _zcons[_zcon_max + 1]; // cached is_zero_type nodes
void init_con_caches();
public:
PhaseTransform( PhaseNumber pnum );
PhaseTransform( Arena *arena, PhaseNumber pnum );
PhaseTransform( PhaseTransform *phase, PhaseNumber pnum );
Arena* arena() { return _arena; }
Type_Array& types() { return _types; }
void replace_types(Type_Array new_types) {
_types = new_types;
}
uint nodes_size() {
return _nodes.size();
}
public:
const Type* type(const Node* n) const {
assert(n != NULL, "must not be null");
const Type* t = _types.fast_lookup(n->_idx);
assert(t != NULL, "must set before get");
return t;
}
const Type* type_or_null(const Node* n) const {
return _types.fast_lookup(n->_idx);
}
void set_type(const Node* n, const Type *t) {
assert(t != NULL, "type must not be null");
_types.map(n->_idx, t);
}
void set_type_bottom(const Node* n) {
assert(_types[n->_idx] == NULL, "must set the initial type just once");
_types.map(n->_idx, n->bottom_type());
}
void ensure_type_or_null(const Node* n) {
if (n->_idx >= _types.Size())
_types.map(n->_idx, NULL); // Grow the types array as needed.
}
const TypeInt* find_int_type( Node* n);
const TypeLong* find_long_type(Node* n);
jint find_int_con( Node* n, jint value_if_unknown) {
const TypeInt* t = find_int_type(n);
return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown;
}
jlong find_long_con(Node* n, jlong value_if_unknown) {
const TypeLong* t = find_long_type(n);
return (t != NULL && t->is_con()) ? t->get_con() : value_if_unknown;
}
ConNode* makecon(const Type* t);
virtual ConNode* uncached_makecon(const Type* t) // override in PhaseValues
{ ShouldNotCallThis(); return NULL; }
ConINode* intcon(jint i);
ConLNode* longcon(jlong l);
ConNode* zerocon(BasicType bt);
virtual Node *transform( Node *n ) = 0;
bool eqv(const Node* n1, const Node* n2) const { return n1 == n2; }
virtual const Type* saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const
{ ShouldNotCallThis(); return NULL; }
virtual PhaseIterGVN *is_IterGVN() { return 0; }
#ifndef PRODUCT
void dump_old2new_map() const;
void dump_new( uint new_lidx ) const;
void dump_types() const;
void dump_nodes_and_types(const Node *root, uint depth, bool only_ctrl = true);
void dump_nodes_and_types_recur( const Node *n, uint depth, bool only_ctrl, VectorSet &visited);
uint _count_progress; // For profiling, count transforms that make progress
void set_progress() { ++_count_progress; assert( allow_progress(),"No progress allowed during verification"); }
void clear_progress() { _count_progress = 0; }
uint made_progress() const { return _count_progress; }
uint _count_transforms; // For profiling, count transforms performed
void set_transforms() { ++_count_transforms; }
void clear_transforms() { _count_transforms = 0; }
uint made_transforms() const{ return _count_transforms; }
bool _allow_progress; // progress not allowed during verification pass
void set_allow_progress(bool allow) { _allow_progress = allow; }
bool allow_progress() { return _allow_progress; }
#endif
};
class PhaseValues : public PhaseTransform {
protected:
NodeHash _table; // Hash table for value-numbering
public:
PhaseValues( Arena *arena, uint est_max_size );
PhaseValues( PhaseValues *pt );
PhaseValues( PhaseValues *ptv, const char *dummy );
NOT_PRODUCT( ~PhaseValues(); )
virtual PhaseIterGVN *is_IterGVN() { return 0; }
bool hash_delete(Node *n) { return _table.hash_delete(n); }
void hash_insert(Node *n) { _table.hash_insert(n); }
Node *hash_find_insert(Node *n){ return _table.hash_find_insert(n); }
Node *hash_find(const Node *n) { return _table.hash_find(n); }
void remove_useless_nodes(VectorSet &useful) {
_table.remove_useless_nodes(useful);
init_con_caches();
}
virtual ConNode* uncached_makecon(const Type* t); // override from PhaseTransform
virtual const Type* saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const
{ return new_type; }
#ifndef PRODUCT
uint _count_new_values; // For profiling, count new values produced
void inc_new_values() { ++_count_new_values; }
void clear_new_values() { _count_new_values = 0; }
uint made_new_values() const { return _count_new_values; }
#endif
};
class PhaseGVN : public PhaseValues {
public:
PhaseGVN( Arena *arena, uint est_max_size ) : PhaseValues( arena, est_max_size ) {}
PhaseGVN( PhaseGVN *gvn ) : PhaseValues( gvn ) {}
PhaseGVN( PhaseGVN *gvn, const char *dummy ) : PhaseValues( gvn, dummy ) {}
Node *transform( Node *n );
Node *transform_no_reclaim( Node *n );
void replace_with(PhaseGVN* gvn) {
_table.replace_with(&gvn->_table);
_types = gvn->_types;
}
DEBUG_ONLY(void dead_loop_check(Node *n);)
};
class PhaseIterGVN : public PhaseGVN {
private:
bool _delay_transform; // When true simply register the node when calling transform
virtual Node *transform_old( Node *a_node );
void subsume_node( Node *old, Node *nn );
Node_Stack _stack; // Stack used to avoid recursion
protected:
virtual Node *transform( Node *a_node );
void init_worklist( Node *a_root );
virtual const Type* saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const;
public:
PhaseIterGVN( PhaseIterGVN *igvn ); // Used by CCP constructor
PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser
PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto
virtual PhaseIterGVN *is_IterGVN() { return this; }
Unique_Node_List _worklist; // Iterative worklist
void optimize();
Node* register_new_node_with_optimizer(Node* n, Node* orig = NULL);
void remove_globally_dead_node( Node *dead );
void remove_dead_node( Node *dead ) {
assert(dead->outcnt() == 0 && !dead->is_top(), "node must be dead");
remove_globally_dead_node(dead);
}
void add_users_to_worklist0( Node *n );
void add_users_to_worklist ( Node *n );
void replace_node( Node *old, Node *nn ) {
add_users_to_worklist(old);
hash_delete(old); // Yank from hash before hacking edges
subsume_node(old, nn);
}
void rehash_node_delayed(Node* n) {
hash_delete(n);
_worklist.push(n);
}
void replace_input_of(Node* n, int i, Node* in) {
rehash_node_delayed(n);
n->set_req(i, in);
}
void delete_input_of(Node* n, int i) {
rehash_node_delayed(n);
n->del_req(i);
}
bool delay_transform() const { return _delay_transform; }
void set_delay_transform(bool delay) {
_delay_transform = delay;
}
Node* clone_loop_predicates(Node* old_entry, Node* new_entry, bool clone_limit_check);
ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry,
Deoptimization::DeoptReason reason);
void remove_speculative_types();
void check_no_speculative_types() {
_table.check_no_speculative_types();
}
#ifndef PRODUCT
protected:
julong _verify_counter;
julong _verify_full_passes;
enum { _verify_window_size = 30 };
Node* _verify_window[_verify_window_size];
void verify_step(Node* n);
#endif
};
class PhaseCCP : public PhaseIterGVN {
virtual Node *transform_once( Node *n );
public:
PhaseCCP( PhaseIterGVN *igvn ); // Compute conditional constants
NOT_PRODUCT( ~PhaseCCP(); )
void analyze();
virtual Node *transform( Node *n );
void do_transform();
virtual const Type* saturate(const Type* new_type, const Type* old_type,
const Type* limit_type) const;
#ifndef PRODUCT
static uint _total_invokes; // For profiling, count invocations
void inc_invokes() { ++PhaseCCP::_total_invokes; }
static uint _total_constants; // For profiling, count constants found
uint _count_constants;
void clear_constants() { _count_constants = 0; }
void inc_constants() { ++_count_constants; }
uint count_constants() const { return _count_constants; }
static void print_statistics();
#endif
};
class PhasePeephole : public PhaseTransform {
PhaseRegAlloc *_regalloc;
PhaseCFG &_cfg;
virtual Node *transform( Node *n );
public:
PhasePeephole( PhaseRegAlloc *regalloc, PhaseCFG &cfg );
NOT_PRODUCT( ~PhasePeephole(); )
void do_transform();
#ifndef PRODUCT
static uint _total_peepholes; // For profiling, count peephole rules applied
uint _count_peepholes;
void clear_peepholes() { _count_peepholes = 0; }
void inc_peepholes() { ++_count_peepholes; }
uint count_peepholes() const { return _count_peepholes; }
static void print_statistics();
#endif
};
#endif // SHARE_VM_OPTO_PHASEX_HPP
C:\hotspot-69087d08d473\src\share\vm/opto/postaloc.cpp
#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/chaitin.hpp"
#include "opto/machnode.hpp"
static bool register_contains_value(Node* val, OptoReg::Name reg, int n_regs,
Node_List& value) {
for (int i = 0; i < n_regs; i++) {
OptoReg::Name nreg = OptoReg::add(reg,-i);
if (value[nreg] != val)
return false;
}
return true;
}
bool PhaseChaitin::may_be_copy_of_callee( Node *def ) const {
if (_matcher.number_of_saved_registers() == 0) return false;
const int limit = 60;
int i;
for( i=0; i < limit; i++ ) {
if( def->is_Proj() && def->in(0)->is_Start() &&
_matcher.is_save_on_entry(lrgs(_lrg_map.live_range_id(def)).reg()))
return true; // Direct use of callee-save proj
if( def->is_Copy() ) // Copies carry value through
def = def->in(def->is_Copy());
else if( def->is_Phi() ) // Phis can merge it from any direction
def = def->in(1);
else
break;
guarantee(def != NULL, "must not resurrect dead copy");
}
return i == limit;
}
int PhaseChaitin::yank( Node *old, Block *current_block, Node_List *value, Node_List *regnd ) {
int blk_adjust=0;
Block *oldb = _cfg.get_block_for_node(old);
oldb->find_remove(old);
if (oldb == current_block) {
blk_adjust++;
}
_cfg.unmap_node_from_block(old);
OptoReg::Name old_reg = lrgs(_lrg_map.live_range_id(old)).reg();
if( regnd && (*regnd)[old_reg]==old ) { // Instruction is currently available?
value->map(old_reg,NULL); // Yank from value/regnd maps
regnd->map(old_reg,NULL); // This register's value is now unknown
}
return blk_adjust;
}
#ifdef ASSERT
static bool expected_yanked_node(Node *old, Node *orig_old) {
if (old->is_MachSpillCopy()) {
return true;
} else if (old->is_Con()) {
return true;
} else if (old->is_MachProj()) { // Dead kills projection of Con node
return (old == orig_old);
} else if (old->is_Copy()) { // Dead copy of a callee-save value
return (old == orig_old);
} else if (old->is_MachTemp()) {
return orig_old->is_Con();
} else if (old->is_Phi()) { // Junk phi's
return true;
} else if (old->is_MachConstantBase()) {
return (orig_old->is_Con() && orig_old->is_MachConstant());
}
return false;
}
#endif
int PhaseChaitin::yank_if_dead_recurse(Node *old, Node *orig_old, Block *current_block,
Node_List *value, Node_List *regnd) {
int blk_adjust=0;
if (old->outcnt() == 0 && old != C->top()) {
#ifdef ASSERT
if (!expected_yanked_node(old, orig_old)) {
tty->print_cr("==============================================");
tty->print_cr("orig_old:");
orig_old->dump();
tty->print_cr("old:");
old->dump();
assert(false, "unexpected yanked node");
}
if (old->is_Con())
orig_old = old; // Reset to satisfy expected nodes checks.
#endif
blk_adjust += yank(old, current_block, value, regnd);
for (uint i = 1; i < old->req(); i++) {
Node* n = old->in(i);
if (n != NULL) {
old->set_req(i, NULL);
blk_adjust += yank_if_dead_recurse(n, orig_old, current_block, value, regnd);
}
}
old->disconnect_inputs(NULL, C);
}
return blk_adjust;
}
int PhaseChaitin::use_prior_register( Node *n, uint idx, Node *def, Block *current_block, Node_List &value, Node_List ®nd ) {
if( def == n->in(idx) ) return 0;
if( def->outcnt() == 0 ) return 0;
const LRG &def_lrg = lrgs(_lrg_map.live_range_id(def));
OptoReg::Name def_reg = def_lrg.reg();
const RegMask &use_mask = n->in_RegMask(idx);
bool can_use = ( RegMask::can_represent(def_reg) ? (use_mask.Member(def_reg) != 0)
: (use_mask.is_AllStack() != 0));
if (!RegMask::is_vector(def->ideal_reg())) {
can_use = can_use && !use_mask.is_misaligned_pair() && !def_lrg.mask().is_misaligned_pair();
}
if (!can_use)
return 0;
Node *old = n->in(idx);
if( may_be_copy_of_callee(def) ) {
if( old->outcnt() > 1 ) return 0; // We're the not last user
int idx = old->is_Copy();
assert( idx, "chain of copies being removed" );
Node *old2 = old->in(idx); // Chain of copies
if( old2->outcnt() > 1 ) return 0; // old is not the last user
int idx2 = old2->is_Copy();
if( !idx2 ) return 0; // Not a chain of 2 copies
if( def != old2->in(idx2) ) return 0; // Chain of exactly 2 copies
}
n->set_req(idx,def);
_post_alloc++;
return yank_if_dead(old,current_block,&value,®nd);
}
Node *PhaseChaitin::skip_copies( Node *c ) {
int idx = c->is_Copy();
uint is_oop = lrgs(_lrg_map.live_range_id(c))._is_oop;
while (idx != 0) {
guarantee(c->in(idx) != NULL, "must not resurrect dead copy");
if (lrgs(_lrg_map.live_range_id(c->in(idx)))._is_oop != is_oop) {
break; // casting copy, not the same value
}
c = c->in(idx);
idx = c->is_Copy();
}
return c;
}
int PhaseChaitin::elide_copy( Node *n, int k, Block *current_block, Node_List &value, Node_List ®nd, bool can_change_regs ) {
int blk_adjust = 0;
uint nk_idx = _lrg_map.live_range_id(n->in(k));
OptoReg::Name nk_reg = lrgs(nk_idx).reg();
Node *x = n->in(k);
int idx;
while( (idx=x->is_Copy()) != 0 ) {
Node *copy = x->in(idx);
guarantee(copy != NULL, "must not resurrect dead copy");
if(lrgs(_lrg_map.live_range_id(copy)).reg() != nk_reg) {
break;
}
blk_adjust += use_prior_register(n,k,copy,current_block,value,regnd);
if (n->in(k) != copy) {
break; // Failed for some cutout?
}
x = copy; // Progress, try again
}
if( !can_change_regs )
return blk_adjust; // Only check stupid copies!
if( &value == NULL ) return blk_adjust;
Node *val = skip_copies(n->in(k));
if (val == x) return blk_adjust; // No progress?
int n_regs = RegMask::num_registers(val->ideal_reg());
uint val_idx = _lrg_map.live_range_id(val);
OptoReg::Name val_reg = lrgs(val_idx).reg();
if (register_contains_value(val, val_reg, n_regs, value)) {
blk_adjust += use_prior_register(n,k,regnd[val_reg],current_block,value,regnd);
if( n->in(k) == regnd[val_reg] ) // Success! Quit trying
return blk_adjust;
}
const Type *t = val->is_Con() ? val->bottom_type() : NULL;
for( uint reg = 0; reg < (uint)_max_reg; reg++ ) {
if (reg == (uint)nk_reg) {
bool ignore_self = true;
x = n->in(k);
DUIterator_Fast imax, i = x->fast_outs(imax);
Node* first = x->fast_out(i); i++;
while (i < imax && ignore_self) {
Node* use = x->fast_out(i); i++;
if (use != first) ignore_self = false;
}
if (ignore_self) continue;
}
Node *vv = value[reg];
if (n_regs > 1) { // Doubles and vectors check for aligned-adjacent set
uint last = (n_regs-1); // Looking for the last part of a set
if ((reg&last) != last) continue; // Wrong part of a set
if (!register_contains_value(vv, reg, n_regs, value)) continue; // Different value
}
if( vv == val || // Got a direct hit?
(t && vv && vv->bottom_type() == t && vv->is_Mach() &&
vv->as_Mach()->rule() == val->as_Mach()->rule()) ) { // Or same constant?
assert( !n->is_Phi(), "cannot change registers at a Phi so easily" );
if( OptoReg::is_stack(nk_reg) || // CISC-loading from stack OR
OptoReg::is_reg(reg) || // turning into a register use OR
regnd[reg]->outcnt()==1 ) { // last use of a spill-load turns into a CISC use
blk_adjust += use_prior_register(n,k,regnd[reg],current_block,value,regnd);
if( n->in(k) == regnd[reg] ) // Success! Quit trying
return blk_adjust;
} // End of if not degrading to a stack
} // End of if found value in another register
} // End of scan all machine registers
return blk_adjust;
}
bool PhaseChaitin::eliminate_copy_of_constant(Node* val, Node* n,
Block *current_block,
Node_List& value, Node_List& regnd,
OptoReg::Name nreg, OptoReg::Name nreg2) {
if (value[nreg] != val && val->is_Con() &&
value[nreg] != NULL && value[nreg]->is_Con() &&
(nreg2 == OptoReg::Bad || value[nreg] == value[nreg2]) &&
value[nreg]->bottom_type() == val->bottom_type() &&
value[nreg]->as_Mach()->rule() == val->as_Mach()->rule()) {
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
Node* use = n->fast_out(i);
if (use->is_Proj() && use->outcnt() == 0) {
use->set_req(0, C->top());
yank_if_dead(use, current_block, &value, ®nd);
--i; --imax;
}
}
_post_alloc++;
return true;
}
return false;
}
void PhaseChaitin::merge_multidefs() {
NOT_PRODUCT( Compile::TracePhase t3("mergeMultidefs", &_t_mergeMultidefs, TimeCompiler); )
ResourceMark rm;
RegToDefUseMap reg2defuse(_max_reg, _max_reg, RegDefUse());
for (uint i = 0; i < _cfg.number_of_blocks(); i++) {
Block* block = _cfg.get_block(i);
for (uint j = 1; j < block->number_of_nodes(); j++) {
Node* n = block->get_node(j);
if (n->is_Phi()) continue;
for (uint k = 1; k < n->req(); k++) {
j += possibly_merge_multidef(n, k, block, reg2defuse);
}
uint lrg = _lrg_map.live_range_id(n);
if (lrg > 0 && lrgs(lrg).is_multidef()) {
OptoReg::Name reg = lrgs(lrg).reg();
reg2defuse.at(reg).clear();
}
}
for (int j = 0; j < reg2defuse.length(); j++) {
reg2defuse.at(j).clear();
}
}
}
int PhaseChaitin::possibly_merge_multidef(Node *n, uint k, Block *block, RegToDefUseMap& reg2defuse) {
int blk_adjust = 0;
uint lrg = _lrg_map.live_range_id(n->in(k));
if (lrg > 0 && lrgs(lrg).is_multidef()) {
OptoReg::Name reg = lrgs(lrg).reg();
Node* def = reg2defuse.at(reg).def();
if (def != NULL && lrg == _lrg_map.live_range_id(def) && def != n->in(k)) {
MachMergeNode* merge;
if (def->is_MachMerge()) { // is it already a merge?
merge = def->as_MachMerge();
} else {
merge = new (C) MachMergeNode(def);
uint use_index = block->find_node(reg2defuse.at(reg).first_use());
block->insert_node(merge, use_index++);
_cfg.map_node_to_block(merge, block);
_lrg_map.extend(merge->_idx, lrg);
blk_adjust++;
for (; use_index < block->number_of_nodes(); use_index++) {
Node* use = block->get_node(use_index);
if (use == n) {
break;
}
use->replace_edge(def, merge);
}
}
if (merge->find_edge(n->in(k)) == -1) {
merge->add_req(n->in(k));
}
n->set_req(k, merge);
}
reg2defuse.at(reg).update(n->in(k), n);
}
return blk_adjust;
}
void PhaseChaitin::post_allocate_copy_removal() {
NOT_PRODUCT( Compile::TracePhase t3("postAllocCopyRemoval", &_t_postAllocCopyRemoval, TimeCompiler); )
ResourceMark rm;
Node_List **blk2value = NEW_RESOURCE_ARRAY( Node_List *, _cfg.number_of_blocks() + 1);
memset(blk2value, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1));
Node_List **blk2regnd = NEW_RESOURCE_ARRAY( Node_List *, _cfg.number_of_blocks() + 1);
memset(blk2regnd, 0, sizeof(Node_List*) * (_cfg.number_of_blocks() + 1));
GrowableArray<Node_List*> free_list = GrowableArray<Node_List*>(16);
for (uint i = 0; i < _cfg.number_of_blocks(); i++) {
uint j;
Block* block = _cfg.get_block(i);
uint phi_dex;
for (phi_dex = 1; phi_dex < block->number_of_nodes(); phi_dex++) {
Node* phi = block->get_node(phi_dex);
if (!phi->is_Phi()) {
break;
}
}
bool missing_some_inputs = false;
Block *freed = NULL;
for (j = 1; j < block->num_preds(); j++) {
Block* pb = _cfg.get_block_for_node(block->pred(j));
for (uint k = 1; k < phi_dex; k++) {
elide_copy(block->get_node(k), j, block, *blk2value[pb->_pre_order], *blk2regnd[pb->_pre_order], false);
}
if (blk2value[pb->_pre_order]) { // Have a mapping on this edge?
uint k;
for (k = 0; k < pb->_num_succs; k++) {
Block* pbsucc = pb->_succs[k];
if (!blk2value[pbsucc->_pre_order] && pbsucc != block) {
break; // Found a future user
}
}
if (k >= pb->_num_succs) { // No more uses, free!
freed = pb; // Record last block freed
free_list.push(blk2value[pb->_pre_order]);
free_list.push(blk2regnd[pb->_pre_order]);
}
} else { // This block has unvisited (loopback) inputs
missing_some_inputs = true;
}
}
Node_List ®nd = *(free_list.is_empty() ? new Node_List() : free_list.pop());
Node_List &value = *(free_list.is_empty() ? new Node_List() : free_list.pop());
assert( !freed || blk2value[freed->_pre_order] == &value, "" );
value.map(_max_reg,NULL);
regnd.map(_max_reg,NULL);
blk2value[block->_pre_order] = &value;
blk2regnd[block->_pre_order] = ®nd;
if (missing_some_inputs) {
for (uint k = 0; k < (uint)_max_reg; k++) {
value.map(k,NULL);
regnd.map(k,NULL);
}
} else {
if( !freed ) { // Didn't get a freebie prior block
freed = _cfg.get_block_for_node(block->pred(1));
Node_List &f_value = *blk2value[freed->_pre_order];
Node_List &f_regnd = *blk2regnd[freed->_pre_order];
for( uint k = 0; k < (uint)_max_reg; k++ ) {
value.map(k,f_value[k]);
regnd.map(k,f_regnd[k]);
}
}
for (j = 1; j < block->num_preds(); j++) {
Block* pb = _cfg.get_block_for_node(block->pred(j));
if (pb == freed) {
continue; // Did self already via freelist
}
Node_List &p_regnd = *blk2regnd[pb->_pre_order];
for( uint k = 0; k < (uint)_max_reg; k++ ) {
if( regnd[k] != p_regnd[k] ) { // Conflict on reaching defs?
value.map(k,NULL); // Then no value handy
regnd.map(k,NULL);
}
}
}
}
for (j = 1; j < phi_dex; j++) {
uint k;
Node *phi = block->get_node(j);
uint pidx = _lrg_map.live_range_id(phi);
OptoReg::Name preg = lrgs(_lrg_map.live_range_id(phi)).reg();
Node *u = NULL;
for (k = 1; k < phi->req(); k++) {
Node *x = phi->in(k);
if( phi != x && u != x ) // Found a different input
u = u ? NodeSentinel : x; // Capture unique input, or NodeSentinel for 2nd input
}
if (u != NodeSentinel) { // Junk Phi. Remove
phi->replace_by(u);
j -= yank_if_dead(phi, block, &value, ®nd);
phi_dex--;
continue;
}
if( pidx ) {
value.map(preg,phi);
regnd.map(preg,phi);
int n_regs = RegMask::num_registers(phi->ideal_reg());
for (int l = 1; l < n_regs; l++) {
OptoReg::Name preg_lo = OptoReg::add(preg,-l);
value.map(preg_lo,phi);
regnd.map(preg_lo,phi);
}
}
}
for (j = phi_dex; j < block->number_of_nodes(); j++) {
Node* n = block->get_node(j);
if(n->outcnt() == 0 && // Dead?
n != C->top() && // (ignore TOP, it has no du info)
!n->is_Proj() ) { // fat-proj kills
j -= yank_if_dead(n, block, &value, ®nd);
continue;
}
uint k;
for (k = 1; k < n->req(); k++) {
Node *def = n->in(k); // n->in(k) is a USE; def is the DEF for this USE
guarantee(def != NULL, "no disconnected nodes at this point");
uint useidx = _lrg_map.live_range_id(def); // useidx is the live range index for this USE
if( useidx ) {
OptoReg::Name ureg = lrgs(useidx).reg();
if( !value[ureg] ) {
int idx; // Skip occasional useless copy
while( (idx=def->is_Copy()) != 0 &&
def->in(idx) != NULL && // NULL should not happen
ureg == lrgs(_lrg_map.live_range_id(def->in(idx))).reg())
def = def->in(idx);
Node *valdef = skip_copies(def); // tighten up val through non-useless copies
value.map(ureg,valdef); // record improved reaching-def info
regnd.map(ureg, def);
uint def_ideal_reg = def->ideal_reg();
int n_regs = RegMask::num_registers(def_ideal_reg);
for (int l = 1; l < n_regs; l++) {
OptoReg::Name ureg_lo = OptoReg::add(ureg,-l);
if (!value[ureg_lo] &&
(!RegMask::can_represent(ureg_lo) ||
lrgs(useidx).mask().Member(ureg_lo))) { // Nearly always adjacent
value.map(ureg_lo,valdef); // record improved reaching-def info
regnd.map(ureg_lo, def);
}
}
}
}
}
const uint two_adr = n->is_Mach() ? n->as_Mach()->two_adr() : 0;
for (k = 1; k < n->req(); k++) {
j -= elide_copy(n, k, block, value, regnd, two_adr != k);
}
uint lidx = _lrg_map.live_range_id(n);
if (!lidx) {
continue;
}
OptoReg::Name nreg = lrgs(lidx).reg();
Node *val = skip_copies(n);
if (regnd[nreg] != NULL && regnd[nreg]->outcnt() == 0) {
regnd.map(nreg, NULL);
value.map(nreg, NULL);
}
uint n_ideal_reg = n->ideal_reg();
int n_regs = RegMask::num_registers(n_ideal_reg);
if (n_regs == 1) {
if( value[nreg] != val ) {
if (eliminate_copy_of_constant(val, n, block, value, regnd, nreg, OptoReg::Bad)) {
j -= replace_and_yank_if_dead(n, nreg, block, value, regnd);
} else {
regnd.map(nreg,n);
value.map(nreg,val);
}
} else if( !may_be_copy_of_callee(n) ) {
assert(n->is_Copy(), "");
j -= replace_and_yank_if_dead(n, nreg, block, value, regnd);
}
} else if (RegMask::is_vector(n_ideal_reg)) {
if (!register_contains_value(val, nreg, n_regs, value)) {
regnd.map(nreg,n);
value.map(nreg,val);
for (int l = 1; l < n_regs; l++) {
OptoReg::Name nreg_lo = OptoReg::add(nreg,-l);
regnd.map(nreg_lo, n );
value.map(nreg_lo,val);
}
} else if (n->is_Copy()) {
j -= replace_and_yank_if_dead(n, nreg, block, value, regnd);
}
} else {
OptoReg::Name nreg_lo = OptoReg::add(nreg,-1);
if( RegMask::can_represent(nreg_lo) && // Either a spill slot, or
!lrgs(lidx).mask().Member(nreg_lo) ) { // Nearly always adjacent
RegMask tmp = lrgs(lidx).mask();
tmp.Remove(nreg);
nreg_lo = tmp.find_first_elem();
}
if (value[nreg] != val || value[nreg_lo] != val) {
if (eliminate_copy_of_constant(val, n, block, value, regnd, nreg, nreg_lo)) {
j -= replace_and_yank_if_dead(n, nreg, block, value, regnd);
} else {
regnd.map(nreg , n );
regnd.map(nreg_lo, n );
value.map(nreg ,val);
value.map(nreg_lo,val);
}
} else if (!may_be_copy_of_callee(n)) {
assert(n->is_Copy(), "");
j -= replace_and_yank_if_dead(n, nreg, block, value, regnd);
}
}
if( n_ideal_reg == MachProjNode::fat_proj ) {
RegMask rm = n->out_RegMask();
nreg = rm.find_first_elem();
while( OptoReg::is_valid(nreg)) {
rm.Remove(nreg);
value.map(nreg,n);
regnd.map(nreg,n);
nreg = rm.find_first_elem();
}
}
} // End of for all instructions in the block
} // End for all blocks
}
C:\hotspot-69087d08d473\src\share\vm/opto/regalloc.cpp
#include "precompiled.hpp"
#include "opto/regalloc.hpp"
static const int NodeRegsOverflowSize = 200;
void (*PhaseRegAlloc::_alloc_statistics[MAX_REG_ALLOCATORS])();
int PhaseRegAlloc::_num_allocators = 0;
#ifndef PRODUCT
int PhaseRegAlloc::_total_framesize = 0;
int PhaseRegAlloc::_max_framesize = 0;
#endif
PhaseRegAlloc::PhaseRegAlloc( uint unique, PhaseCFG &cfg,
Matcher &matcher,
void (*pr_stats)() ):
Phase(Register_Allocation), _cfg(cfg), _matcher(matcher),
_node_oops(Thread::current()->resource_area()),
_node_regs(0),
_node_regs_max_index(0),
_framesize(0xdeadbeef)
{
int i;
for (i=0; i < _num_allocators; i++) {
if (_alloc_statistics[i] == pr_stats)
return;
}
assert((_num_allocators + 1) < MAX_REG_ALLOCATORS, "too many register allocators");
_alloc_statistics[_num_allocators++] = pr_stats;
}
int PhaseRegAlloc::reg2offset_unchecked( OptoReg::Name reg ) const {
int slot = (reg < _matcher._new_SP)
? reg - OptoReg::stack0() + _framesize
: reg - _matcher._new_SP;
return slot*VMRegImpl::stack_slot_size;
}
int PhaseRegAlloc::reg2offset( OptoReg::Name reg ) const {
assert( reg < _matcher._old_SP ||
(reg >= OptoReg::add(_matcher._old_SP,C->out_preserve_stack_slots()) &&
reg < _matcher._in_arg_limit) ||
reg >= OptoReg::add(_matcher._new_SP, C->out_preserve_stack_slots()) ||
reg == _matcher.return_addr(),
"register allocated in a preserve area" );
return reg2offset_unchecked( reg );
}
OptoReg::Name PhaseRegAlloc::offset2reg(int stk_offset) const {
int slot = stk_offset / jintSize;
int reg = (slot < (int) _framesize)
? slot + _matcher._new_SP
: OptoReg::stack2reg(slot) - _framesize;
assert(stk_offset == reg2offset((OptoReg::Name) reg),
"offset2reg does not invert properly");
return (OptoReg::Name) reg;
}
void PhaseRegAlloc::set_oop( const Node *n, bool is_an_oop ) {
if( is_an_oop ) {
_node_oops.set(n->_idx);
}
}
bool PhaseRegAlloc::is_oop( const Node *n ) const {
return _node_oops.test(n->_idx) != 0;
}
void PhaseRegAlloc::alloc_node_regs(int size) {
_node_regs_max_index = size + (size >> 1) + NodeRegsOverflowSize;
_node_regs = NEW_RESOURCE_ARRAY( OptoRegPair, _node_regs_max_index );
for( uint i = size; i < _node_regs_max_index; ++i )
_node_regs[i].set_bad();
}
#ifndef PRODUCT
void
PhaseRegAlloc::print_statistics() {
tty->print_cr("Total frameslots = %d, Max frameslots = %d", _total_framesize, _max_framesize);
int i;
for (i=0; i < _num_allocators; i++) {
_alloc_statistics[i]();
}
}
#endif
C:\hotspot-69087d08d473\src\share\vm/opto/regalloc.hpp
#ifndef SHARE_VM_OPTO_REGALLOC_HPP
#define SHARE_VM_OPTO_REGALLOC_HPP
#include "code/vmreg.hpp"
#include "opto/block.hpp"
#include "opto/matcher.hpp"
#include "opto/phase.hpp"
class Node;
class Matcher;
class PhaseCFG;
#define MAX_REG_ALLOCATORS 10
class PhaseRegAlloc : public Phase {
friend class VMStructs;
static void (*_alloc_statistics[MAX_REG_ALLOCATORS])();
static int _num_allocators;
protected:
OptoRegPair *_node_regs;
uint _node_regs_max_index;
VectorSet _node_oops; // Mapping from node indices to oopiness
void alloc_node_regs(int size); // allocate _node_regs table with at least "size" elements
PhaseRegAlloc( uint unique, PhaseCFG &cfg, Matcher &matcher,
void (*pr_stats)());
public:
PhaseCFG &_cfg; // Control flow graph
uint _framesize; // Size of frame in stack-slots. not counting preserve area
OptoReg::Name _max_reg; // Past largest register seen
Matcher &_matcher; // Convert Ideal to MachNodes
uint node_regs_max_index() const { return _node_regs_max_index; }
OptoReg::Name get_reg_first( const Node *n ) const {
debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); );
assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array");
return _node_regs[n->_idx].first();
}
OptoReg::Name get_reg_second( const Node *n ) const {
debug_only( if( n->_idx >= _node_regs_max_index ) n->dump(); );
assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array");
return _node_regs[n->_idx].second();
}
virtual void Register_Allocate() = 0;
virtual void add_reference( const Node *node, const Node *old_node) = 0;
void set_bad( uint idx ) {
assert( idx < _node_regs_max_index, "Exceeded _node_regs array");
_node_regs[idx].set_bad();
}
void set1( uint idx, OptoReg::Name reg ) {
assert( idx < _node_regs_max_index, "Exceeded _node_regs array");
_node_regs[idx].set1(reg);
}
void set2( uint idx, OptoReg::Name reg ) {
assert( idx < _node_regs_max_index, "Exceeded _node_regs array");
_node_regs[idx].set2(reg);
}
void set_pair( uint idx, OptoReg::Name hi, OptoReg::Name lo ) {
assert( idx < _node_regs_max_index, "Exceeded _node_regs array");
_node_regs[idx].set_pair(hi, lo);
}
void set_ptr( uint idx, OptoReg::Name reg ) {
assert( idx < _node_regs_max_index, "Exceeded _node_regs array");
_node_regs[idx].set_ptr(reg);
}
void set_oop( const Node *n, bool );
bool is_oop( const Node *n ) const;
int reg2offset ( OptoReg::Name reg ) const;
int reg2offset_unchecked( OptoReg::Name reg ) const;
OptoReg::Name offset2reg( int stk_offset ) const;
int get_encode(const Node *n) const {
assert( n->_idx < _node_regs_max_index, "Exceeded _node_regs array");
OptoReg::Name first = _node_regs[n->_idx].first();
OptoReg::Name second = _node_regs[n->_idx].second();
assert( !OptoReg::is_valid(second) || second == first+1, "" );
assert(OptoReg::is_reg(first), "out of range");
return Matcher::_regEncode[first];
}
#ifndef PRODUCT
static int _total_framesize;
static int _max_framesize;
virtual void dump_frame() const = 0;
virtual char *dump_register( const Node *n, char *buf ) const = 0;
static void print_statistics();
#endif
};
#endif // SHARE_VM_OPTO_REGALLOC_HPP
C:\hotspot-69087d08d473\src\share\vm/opto/regmask.cpp
#include "precompiled.hpp"
#include "opto/compile.hpp"
#include "opto/regmask.hpp"
#if defined AD_MD_HPP
# include AD_MD_HPP
#elif defined TARGET_ARCH_MODEL_x86_32
# include "adfiles/ad_x86_32.hpp"
#elif defined TARGET_ARCH_MODEL_x86_64
# include "adfiles/ad_x86_64.hpp"
#elif defined TARGET_ARCH_MODEL_aarch64
# include "adfiles/ad_aarch64.hpp"
#elif defined TARGET_ARCH_MODEL_sparc
# include "adfiles/ad_sparc.hpp"
#elif defined TARGET_ARCH_MODEL_zero
# include "adfiles/ad_zero.hpp"
#elif defined TARGET_ARCH_MODEL_ppc_64
# include "adfiles/ad_ppc_64.hpp"
#endif
#define RM_SIZE _RM_SIZE /* a constant private to the class RegMask */
int find_lowest_bit( uint32 mask ) {
int n = 0;
if( (mask & 0xffff) == 0 ) {
mask >>= 16;
n += 16;
}
if( (mask & 0xff) == 0 ) {
mask >>= 8;
n += 8;
}
if( (mask & 0xf) == 0 ) {
mask >>= 4;
n += 4;
}
if( (mask & 0x3) == 0 ) {
mask >>= 2;
n += 2;
}
if( (mask & 0x1) == 0 ) {
mask >>= 1;
n += 1;
}
if( mask == 0 ) {
n = 32;
}
return n;
}
int find_hihghest_bit( uint32 mask ) {
int n = 0;
if( mask > 0xffff ) {
mask >>= 16;
n += 16;
}
if( mask > 0xff ) {
mask >>= 8;
n += 8;
}
if( mask > 0xf ) {
mask >>= 4;
n += 4;
}
if( mask > 0x3 ) {
mask >>= 2;
n += 2;
}
if( mask > 0x1 ) {
mask >>= 1;
n += 1;
}
if( mask == 0 ) {
n = 32;
}
return n;
}
#ifndef PRODUCT
void OptoReg::dump(int r, outputStream *st) {
switch (r) {
case Special: st->print("r---"); break;
case Bad: st->print("rBAD"); break;
default:
if (r < _last_Mach_Reg) st->print("%s", Matcher::regName[r]);
else st->print("rS%d",r);
break;
}
}
#endif
const RegMask RegMask::Empty(
# define BODY(I) 0,
FORALL_BODY
# undef BODY
0
);
bool RegMask::is_vector(uint ireg) {
return (ireg == Op_VecS || ireg == Op_VecD || ireg == Op_VecX || ireg == Op_VecY);
}
int RegMask::num_registers(uint ireg) {
switch(ireg) {
case Op_VecY:
return 8;
case Op_VecX:
return 4;
case Op_VecD:
case Op_RegD:
case Op_RegL:
#ifdef _LP64
case Op_RegP:
#endif
return 2;
}
return 1;
}
OptoReg::Name RegMask::find_first_pair() const {
verify_pairs();
for( int i = 0; i < RM_SIZE; i++ ) {
if( _A[i] ) { // Found some bits
int bit = _A[i] & -_A[i]; // Extract low bit
return OptoReg::Name((i<<_LogWordBits)+find_lowest_bit(bit)+1);
}
}
return OptoReg::Bad;
}
void RegMask::clear_to_pairs() {
for( int i = 0; i < RM_SIZE; i++ ) {
int bits = _A[i];
bits &= ((bits & 0x55555555)<<1); // 1 hi-bit set for each pair
bits |= (bits>>1); // Smear 1 hi-bit into a pair
_A[i] = bits;
}
verify_pairs();
}
void RegMask::smear_to_pairs() {
for( int i = 0; i < RM_SIZE; i++ ) {
int bits = _A[i];
bits |= ((bits & 0x55555555)<<1); // Smear lo bit hi per pair
bits |= ((bits & 0xAAAAAAAA)>>1); // Smear hi bit lo per pair
_A[i] = bits;
}
verify_pairs();
}
bool RegMask::is_aligned_pairs() const {
for( int i = 0; i < RM_SIZE; i++ ) {
int bits = _A[i];
while( bits ) { // Check bits for pairing
int bit = bits & -bits; // Extract low bit
if( (bit & 0x55555555) == 0 ) return false;
bits -= bit; // Remove bit from mask
if( (bits & (bit<<1)) == 0 ) return false;
bits -= (bit<<1); // Remove other halve of pair
}
}
return true;
}
int RegMask::is_bound1() const {
if( is_AllStack() ) return false;
int bit = -1; // Set to hold the one bit allowed
for( int i = 0; i < RM_SIZE; i++ ) {
if( _A[i] ) { // Found some bits
if( bit != -1 ) return false; // Already had bits, so fail
bit = _A[i] & -_A[i]; // Extract 1 bit from mask
if( bit != _A[i] ) return false; // Found many bits, so fail
}
}
return true;
}
int RegMask::is_bound_pair() const {
if( is_AllStack() ) return false;
int bit = -1; // Set to hold the one bit allowed
for( int i = 0; i < RM_SIZE; i++ ) {
if( _A[i] ) { // Found some bits
if( bit != -1 ) return false; // Already had bits, so fail
bit = _A[i] & -(_A[i]); // Extract 1 bit from mask
if( (bit << 1) != 0 ) { // Bit pair stays in same word?
if( (bit | (bit<<1)) != _A[i] )
return false; // Require adjacent bit pair and no more bits
} else { // Else its a split-pair case
if( bit != _A[i] ) return false; // Found many bits, so fail
i++; // Skip iteration forward
if( i >= RM_SIZE || _A[i] != 1 )
return false; // Require 1 lo bit in next word
}
}
}
return true;
}
static int low_bits[3] = { 0x55555555, 0x11111111, 0x01010101 };
OptoReg::Name RegMask::find_first_set(const int size) const {
verify_sets(size);
for (int i = 0; i < RM_SIZE; i++) {
if (_A[i]) { // Found some bits
int bit = _A[i] & -_A[i]; // Extract low bit
return OptoReg::Name((i<<_LogWordBits)+find_lowest_bit(bit)+(size-1));
}
}
return OptoReg::Bad;
}
void RegMask::clear_to_sets(const int size) {
if (size == 1) return;
assert(2 <= size && size <= 8, "update low bits table");
assert(is_power_of_2(size), "sanity");
int low_bits_mask = low_bits[size>>2];
for (int i = 0; i < RM_SIZE; i++) {
int bits = _A[i];
int sets = (bits & low_bits_mask);
for (int j = 1; j < size; j++) {
sets = (bits & (sets<<1)); // filter bits which produce whole sets
}
sets |= (sets>>1); // Smear 1 hi-bit into a set
if (size > 2) {
sets |= (sets>>2); // Smear 2 hi-bits into a set
if (size > 4) {
sets |= (sets>>4); // Smear 4 hi-bits into a set
}
}
_A[i] = sets;
}
verify_sets(size);
}
void RegMask::smear_to_sets(const int size) {
if (size == 1) return;
assert(2 <= size && size <= 8, "update low bits table");
assert(is_power_of_2(size), "sanity");
int low_bits_mask = low_bits[size>>2];
for (int i = 0; i < RM_SIZE; i++) {
int bits = _A[i];
int sets = 0;
for (int j = 0; j < size; j++) {
sets |= (bits & low_bits_mask); // collect partial bits
bits = bits>>1;
}
sets |= (sets<<1); // Smear 1 lo-bit into a set
if (size > 2) {
sets |= (sets<<2); // Smear 2 lo-bits into a set
if (size > 4) {
sets |= (sets<<4); // Smear 4 lo-bits into a set
}
}
_A[i] = sets;
}
verify_sets(size);
}
bool RegMask::is_aligned_sets(const int size) const {
if (size == 1) return true;
assert(2 <= size && size <= 8, "update low bits table");
assert(is_power_of_2(size), "sanity");
int low_bits_mask = low_bits[size>>2];
for (int i = 0; i < RM_SIZE; i++) {
int bits = _A[i];
while (bits) { // Check bits for pairing
int bit = bits & -bits; // Extract low bit
if ((bit & low_bits_mask) == 0) return false;
int hi_bit = bit << (size-1); // high bit
int set = hi_bit + ((hi_bit-1) & ~(bit-1));
if ((bits & set) != set) return false;
bits -= set; // Remove this set
}
}
return true;
}
int RegMask::is_bound_set(const int size) const {
if( is_AllStack() ) return false;
assert(1 <= size && size <= 8, "update low bits table");
int bit = -1; // Set to hold the one bit allowed
for (int i = 0; i < RM_SIZE; i++) {
if (_A[i] ) { // Found some bits
if (bit != -1)
return false; // Already had bits, so fail
bit = _A[i] & -_A[i]; // Extract low bit from mask
int hi_bit = bit << (size-1); // high bit
if (hi_bit != 0) { // Bit set stays in same word?
int set = hi_bit + ((hi_bit-1) & ~(bit-1));
if (set != _A[i])
return false; // Require adjacent bit set and no more bits
} else { // Else its a split-set case
if (((-1) & ~(bit-1)) != _A[i])
return false; // Found many bits, so fail
i++; // Skip iteration forward and check high part
int set = bit>>24;
set = set & -set; // Remove sign extension.
set = (((set << size) - 1) >> 8);
if (i >= RM_SIZE || _A[i] != set)
return false; // Require expected low bits in next word
}
}
}
return true;
}
bool RegMask::is_UP() const {
if( is_AllStack() )
return false;
if( overlap(Matcher::STACK_ONLY_mask) )
return false;
return true;
}
uint RegMask::Size() const {
extern uint8 bitsInByte[256];
uint sum = 0;
for( int i = 0; i < RM_SIZE; i++ )
sum +=
bitsInByte[(_A[i]>>24) & 0xff] +
bitsInByte[(_A[i]>>16) & 0xff] +
bitsInByte[(_A[i]>> 8) & 0xff] +
bitsInByte[ _A[i] & 0xff];
return sum;
}
#ifndef PRODUCT
void RegMask::dump(outputStream *st) const {
st->print("[");
RegMask rm = *this; // Structure copy into local temp
OptoReg::Name start = rm.find_first_elem(); // Get a register
if (OptoReg::is_valid(start)) { // Check for empty mask
rm.Remove(start); // Yank from mask
OptoReg::dump(start, st); // Print register
OptoReg::Name last = start;
while (1) { //
OptoReg::Name reg = rm.find_first_elem(); // Get a register
if (!OptoReg::is_valid(reg))
break; // Empty mask, end loop
rm.Remove(reg); // Yank from mask
if (last+1 == reg) { // See if they are adjacent
last = reg;
} else { // Ending some kind of run
if (start == last) { // 1-register run; no special printing
} else if (start+1 == last) {
st->print(","); // 2-register run; print as "rX,rY"
OptoReg::dump(last, st);
} else { // Multi-register run; print as "rX-rZ"
st->print("-");
OptoReg::dump(last, st);
}
st->print(","); // Seperate start of new run
start = last = reg; // Start a new register run
OptoReg::dump(start, st); // Print register
} // End of if ending a register run or not
} // End of while regmask not empty
if (start == last) { // 1-register run; no special printing
} else if (start+1 == last) {
st->print(","); // 2-register run; print as "rX,rY"
OptoReg::dump(last, st);
} else { // Multi-register run; print as "rX-rZ"
st->print("-");
OptoReg::dump(last, st);
}
if (rm.is_AllStack()) st->print("...");
}
st->print("]");
}
#endif
C:\hotspot-69087d08d473\src\share\vm/opto/regmask.hpp
#ifndef SHARE_VM_OPTO_REGMASK_HPP
#define SHARE_VM_OPTO_REGMASK_HPP
#include "code/vmreg.hpp"
#include "libadt/port.hpp"
#include "opto/optoreg.hpp"
#if defined ADGLOBALS_MD_HPP
# include ADGLOBALS_MD_HPP
#elif defined TARGET_ARCH_MODEL_x86_32
# include "adfiles/adGlobals_x86_32.hpp"
#elif defined TARGET_ARCH_MODEL_x86_64
# include "adfiles/adGlobals_x86_64.hpp"
#elif defined TARGET_ARCH_MODEL_aarch64
# include "adfiles/adGlobals_aarch64.hpp"
#elif defined TARGET_ARCH_MODEL_sparc
# include "adfiles/adGlobals_sparc.hpp"
#elif defined TARGET_ARCH_MODEL_zero
# include "adfiles/adGlobals_zero.hpp"
#elif defined TARGET_ARCH_MODEL_ppc_64
# include "adfiles/adGlobals_ppc_64.hpp"
#endif
int find_lowest_bit( uint32 mask );
int find_hihghest_bit( uint32 mask );
class RegMask VALUE_OBJ_CLASS_SPEC {
union {
double _dummy_force_double_alignment[RM_SIZE>>1];
int _A[RM_SIZE];
};
enum {
_WordBits = BitsPerInt,
_LogWordBits = LogBitsPerInt,
_RM_SIZE = RM_SIZE // local constant, imported, then hidden by #undef
};
public:
enum { CHUNK_SIZE = RM_SIZE*_WordBits };
enum { SlotsPerLong = 2,
SlotsPerVecS = 1,
SlotsPerVecD = 2,
SlotsPerVecX = 4,
SlotsPerVecY = 8 };
RegMask(
# define BODY(I) int a##I,
FORALL_BODY
# undef BODY
int dummy = 0 ) {
# define BODY(I) _A[I] = a##I;
FORALL_BODY
# undef BODY
}
RegMask( RegMask *rm ) {
# define BODY(I) _A[I] = rm->_A[I];
FORALL_BODY
# undef BODY
}
RegMask( ) { Clear(); }
RegMask( OptoReg::Name reg ) { Clear(); Insert(reg); }
int Member( OptoReg::Name reg ) const {
assert( reg < CHUNK_SIZE, "" );
return _A[reg>>_LogWordBits] & (1<<(reg&(_WordBits-1)));
}
int is_AllStack() const { return _A[RM_SIZE-1] >> (_WordBits-1); }
void set_AllStack() { Insert(OptoReg::Name(CHUNK_SIZE-1)); }
int is_NotEmpty( ) const {
int tmp = 0;
# define BODY(I) tmp |= _A[I];
FORALL_BODY
# undef BODY
return tmp;
}
OptoReg::Name find_first_elem() const {
int base, bits;
# define BODY(I) if( (bits = _A[I]) != 0 ) base = I<<_LogWordBits; else
FORALL_BODY
# undef BODY
{ base = OptoReg::Bad; bits = 1<<0; }
return OptoReg::Name(base + find_lowest_bit(bits));
}
OptoReg::Name find_last_elem() const {
int base, bits;
# define BODY(I) if( (bits = _A[RM_SIZE-1-I]) != 0 ) base = (RM_SIZE-1-I)<<_LogWordBits; else
FORALL_BODY
# undef BODY
{ base = OptoReg::Bad; bits = 1<<0; }
return OptoReg::Name(base + find_hihghest_bit(bits));
}
OptoReg::Name find_first_pair() const;
void clear_to_pairs();
void smear_to_pairs();
void verify_pairs() const { assert( is_aligned_pairs(), "mask is not aligned, adjacent pairs" ); }
bool is_aligned_pairs() const;
bool is_misaligned_pair() const { return Size()==2 && !is_aligned_pairs(); }
int is_bound1() const;
int is_bound_pair() const;
int is_bound(uint ireg) const {
if (is_vector(ireg)) {
if (is_bound_set(num_registers(ireg)))
return true;
} else if (is_bound1() || is_bound_pair()) {
return true;
}
return false;
}
OptoReg::Name find_first_set(const int size) const;
void clear_to_sets(const int size);
void smear_to_sets(const int size);
void verify_sets(int size) const { assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); }
bool is_aligned_sets(const int size) const;
bool is_misaligned_set(int size) const { return (int)Size()==size && !is_aligned_sets(size);}
int is_bound_set(const int size) const;
static bool is_vector(uint ireg);
static int num_registers(uint ireg);
int overlap( const RegMask &rm ) const {
return
# define BODY(I) (_A[I] & rm._A[I]) |
FORALL_BODY
# undef BODY
0 ;
}
bool is_UP() const;
void Clear( ) {
# define BODY(I) _A[I] = 0;
FORALL_BODY
# undef BODY
}
void Set_All( ) {
# define BODY(I) _A[I] = -1;
FORALL_BODY
# undef BODY
}
void Insert( OptoReg::Name reg ) {
assert( reg < CHUNK_SIZE, "" );
_A[reg>>_LogWordBits] |= (1<<(reg&(_WordBits-1)));
}
void Remove( OptoReg::Name reg ) {
assert( reg < CHUNK_SIZE, "" );
_A[reg>>_LogWordBits] &= ~(1<<(reg&(_WordBits-1)));
}
void OR( const RegMask &rm ) {
# define BODY(I) this->_A[I] |= rm._A[I];
FORALL_BODY
# undef BODY
}
void AND( const RegMask &rm ) {
# define BODY(I) this->_A[I] &= rm._A[I];
FORALL_BODY
# undef BODY
}
void SUBTRACT( const RegMask &rm ) {
# define BODY(I) _A[I] &= ~rm._A[I];
FORALL_BODY
# undef BODY
}
uint Size() const;
#ifndef PRODUCT
void print() const { dump(); }
void dump(outputStream *st = tty) const; // Print a mask
#endif
static const RegMask Empty; // Common empty mask
static bool can_represent(OptoReg::Name reg) {
return (int)reg < (int)(CHUNK_SIZE-1);
}
static bool can_represent_arg(OptoReg::Name reg) {
return (int)reg < (int)(CHUNK_SIZE-SlotsPerVecY);
}
};
#undef RM_SIZE
#endif // SHARE_VM_OPTO_REGMASK_HPP
C:\hotspot-69087d08d473\src\share\vm/opto/reg_split.cpp
#include "precompiled.hpp"
#include "libadt/vectset.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/addnode.hpp"
#include "opto/c2compiler.hpp"
#include "opto/callnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/chaitin.hpp"
#include "opto/loopnode.hpp"
#include "opto/machnode.hpp"
static const char out_of_nodes[] = "out of nodes during split";
Node *PhaseChaitin::get_spillcopy_wide( Node *def, Node *use, uint uidx ) {
uint ireg = def->ideal_reg();
if( ireg == 0 || ireg == Op_RegFlags ) {
assert(false, "attempted to spill a non-spillable item");
C->record_method_not_compilable("attempted to spill a non-spillable item");
return NULL;
}
if (C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) {
return NULL;
}
const RegMask *i_mask = &def->out_RegMask();
const RegMask *w_mask = C->matcher()->idealreg2spillmask[ireg];
const RegMask *o_mask = use ? &use->in_RegMask(uidx) : w_mask;
const RegMask *w_i_mask = w_mask->overlap( *i_mask ) ? w_mask : i_mask;
const RegMask *w_o_mask;
int num_regs = RegMask::num_registers(ireg);
bool is_vect = RegMask::is_vector(ireg);
if( w_mask->overlap( *o_mask ) && // Overlap AND
((num_regs == 1) // Single use or aligned
|| is_vect // or vector
|| !is_vect && o_mask->is_aligned_pairs()) ) {
assert(!is_vect || o_mask->is_aligned_sets(num_regs), "vectors are aligned");
w_o_mask = w_mask;
} else { // wide ideal mask does not overlap with o_mask
w_o_mask = o_mask; // Must target desired registers
if( !C->matcher()->idealreg2regmask[ireg]->overlap( *o_mask) && o_mask->is_UP() )
w_i_mask = &C->FIRST_STACK_mask();
}
return new (C) MachSpillCopyNode( def, *w_i_mask, *w_o_mask );
}
void PhaseChaitin::insert_proj( Block *b, uint i, Node *spill, uint maxlrg ) {
while( i < b->number_of_nodes() &&
(b->get_node(i)->is_Proj() ||
b->get_node(i)->is_Phi() ) )
i++;
if( b->get_node(i)->is_Catch() ) {
while( 1 ) {
const CatchProjNode *cp = b->get_node(++i)->as_CatchProj();
if( cp->_con == CatchProjNode::fall_through_index )
break;
}
int sidx = i - b->end_idx()-1;
b = b->_succs[sidx]; // Switch to successor block
i = 1; // Right at start of block
}
b->insert_node(spill, i); // Insert node in block
_cfg.map_node_to_block(spill, b); // Update node->block mapping to reflect
if( i <= b->_ihrp_index ) b->_ihrp_index++;
if( i <= b->_fhrp_index ) b->_fhrp_index++;
new_lrg(spill,maxlrg);
}
uint PhaseChaitin::split_DEF( Node *def, Block *b, int loc, uint maxlrg, Node **Reachblock, Node **debug_defs, GrowableArray<uint> splits, int slidx ) {
#ifdef ASSERT
splits.at_put(slidx, splits.at(slidx)+1);
#endif
Node *be = b->end();
if( be->is_MachNullCheck() && be->in(1) == def && def == b->get_node(loc)) {
b = b->_succs[b->get_node(b->end_idx()+1)->Opcode() == Op_IfTrue];
loc = 0; // Just past the Region
}
assert( loc >= 0, "must insert past block head" );
Node *spill = get_spillcopy_wide(def,NULL,0);
if (!spill) {
return 0;
}
insert_proj( b, loc+1, spill, maxlrg++);
Reachblock[slidx] = spill;
debug_defs[slidx] = spill;
return maxlrg;
}
uint PhaseChaitin::split_USE( Node *def, Block *b, Node *use, uint useidx, uint maxlrg, bool def_down, bool cisc_sp, GrowableArray<uint> splits, int slidx ) {
#ifdef ASSERT
splits.at_put(slidx, splits.at(slidx)+1);
#endif
JVMState* jvms = use->jvms();
uint debug_start = jvms ? jvms->debug_start() : 999999;
uint debug_end = jvms ? jvms->debug_end() : 999999;
if (useidx >= debug_start && useidx < debug_end) {
if( def->is_Mach() ) {
if( def_down ) {
use->set_req(useidx, def);
} else {
Block *b = _cfg.get_block_for_node(use);
int bindex = b->find_node(use);
Node *spill = get_spillcopy_wide(def,use,useidx);
if (!spill) {
return 0;
}
insert_proj( b, bindex, spill, maxlrg++ );
use->set_req(useidx,spill);
}
return maxlrg;
} // End special splitting for debug info live range
} // If debug info
if( UseCISCSpill && cisc_sp ) {
int inp = use->cisc_operand();
if( inp != AdlcVMDeps::Not_cisc_spillable )
inp = use->as_Mach()->operand_index(inp);
if( inp == (int)useidx ) {
use->set_req(useidx, def);
#ifndef PRODUCT
if( TraceCISCSpill ) {
tty->print(" set_split: ");
use->dump();
}
#endif
return maxlrg;
}
}
int bindex;
if( use->is_Phi() ) {
b = _cfg.get_block_for_node(b->pred(useidx));
bindex = b->end_idx();
} else {
bindex = b->find_node(use);
}
Node *spill = get_spillcopy_wide( def, use, useidx );
if( !spill ) return 0; // Bailed out
insert_proj( b, bindex, spill, maxlrg++ );
use->set_req(useidx,spill);
return maxlrg;
}
Node* clone_node(Node* def, Block *b, Compile* C) {
if (def->needs_anti_dependence_check()) {
#ifdef ASSERT
if (Verbose) {
tty->print_cr("RA attempts to clone node with anti_dependence:");
def->dump(-1); tty->cr();
tty->print_cr("into block:");
b->dump();
}
#endif
if (C->subsume_loads() == true && !C->failing()) {
C->record_failure(C2Compiler::retry_no_subsuming_loads());
} else {
C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence");
}
return 0;
}
return def->clone();
}
Node *PhaseChaitin::split_Rematerialize( Node *def, Block *b, uint insidx, uint &maxlrg, GrowableArray<uint> splits, int slidx, uint *lrg2reach, Node **Reachblock, bool walkThru ) {
if( def->req() > 1 ) {
for( uint i = 1; i < def->req(); i++ ) {
Node *in = def->in(i);
uint lidx = _lrg_map.live_range_id(in);
if (lidx < _lrg_map.max_lrg_id() && lrgs(lidx).is_singledef()) {
continue;
}
Block *b_def = _cfg.get_block_for_node(def);
int idx_def = b_def->find_node(def);
Node *in_spill = get_spillcopy_wide( in, def, i );
if( !in_spill ) return 0; // Bailed out
insert_proj(b_def,idx_def,in_spill,maxlrg++);
if( b_def == b )
insidx++;
def->set_req(i,in_spill);
}
}
Node *spill = clone_node(def, b, C);
if (spill == NULL || C->check_node_count(NodeLimitFudgeFactor, out_of_nodes)) {
return 0;
}
if( spill->req() > 1 ) {
for( uint i = 1; i < spill->req(); i++ ) {
Node *in = spill->in(i);
uint lidx = _lrg_map.find_id(in);
if (walkThru) {
while (in->is_SpillCopy() && lidx >= _lrg_map.max_lrg_id()) {
in = in->in(1);
lidx = _lrg_map.find_id(in);
}
if (lidx < _lrg_map.max_lrg_id() && lrgs(lidx).is_multidef()) {
in = spill->in(i);
lidx = _lrg_map.find_id(in);
}
}
if (lidx < _lrg_map.max_lrg_id() && lrgs(lidx).reg() >= LRG::SPILL_REG) {
Node *rdef = Reachblock[lrg2reach[lidx]];
if (rdef) {
spill->set_req(i, rdef);
}
}
}
}
assert( spill->out_RegMask().is_UP(), "rematerialize to a reg" );
set_was_spilled(spill);
if( _spilled_once.test(def->_idx) )
set_was_spilled(spill);
insert_proj( b, insidx, spill, maxlrg++ );
#ifdef ASSERT
splits.at_put(slidx, splits.at(slidx)+1);
#endif
uint i = insidx+1;
int found_projs = clone_projs( b, i, def, spill, maxlrg);
if (found_projs > 0) {
if (i <= b->_ihrp_index) {
b->_ihrp_index += found_projs;
}
if (i <= b->_fhrp_index) {
b->_fhrp_index += found_projs;
}
}
return spill;
}
bool PhaseChaitin::is_high_pressure( Block *b, LRG *lrg, uint insidx ) {
if( lrg->_was_spilled1 ) return true;
bool is_float_or_vector = lrg->_is_float || lrg->_is_vector;
uint hrp_idx = is_float_or_vector ? b->_fhrp_index : b->_ihrp_index;
if( insidx < hrp_idx ) return false;
int block_pres = is_float_or_vector ? b->_freg_pressure : b->_reg_pressure;
int bound_pres = is_float_or_vector ? FLOATPRESSURE : INTPRESSURE;
int lrg_pres = (lrg->get_invalid_mask_size() > lrg->num_regs())
? (lrg->get_invalid_mask_size() >> (lrg->num_regs()-1)) : bound_pres;
return block_pres >= lrg_pres;
}
bool PhaseChaitin::prompt_use( Block *b, uint lidx ) {
if (lrgs(lidx)._was_spilled2) {
return false;
}
for( uint i = 1; i <= b->end_idx(); i++ ) {
Node *n = b->get_node(i);
if (n->is_Phi()) {
continue;
}
for (uint j = 1; j < n->req(); j++) {
if (_lrg_map.find_id(n->in(j)) == lidx) {
return true; // Found 1st use!
}
}
if (n->out_RegMask().is_NotEmpty()) {
return false;
}
}
return false;
}
uint PhaseChaitin::Split(uint maxlrg, ResourceArea* split_arena) {
NOT_PRODUCT( Compile::TracePhase t3("regAllocSplit", &_t_regAllocSplit, TimeCompiler); )
ResourceMark rm(split_arena);
uint bidx, pidx, slidx, insidx, inpidx, twoidx;
uint non_phi = 1, spill_cnt = 0;
Node *n1, *n2, *n3;
Node_List *defs,*phis;
bool *UPblock;
bool u1, u2, u3;
Block *b, *pred;
PhiNode *phi;
GrowableArray<uint> lidxs(split_arena, maxlrg, 0, 0);
GrowableArray<uint> splits(split_arena, maxlrg, 0, 0);
#define NEW_SPLIT_ARRAY(type, size)\
(type*) split_arena->allocate_bytes((size) * sizeof(type))
uint *lrg2reach = NEW_SPLIT_ARRAY(uint, maxlrg);
defs = new Node_List();
phis = new Node_List();
for (bidx = 1; bidx < maxlrg; bidx++) {
if (lrgs(bidx).alive() && lrgs(bidx).reg() >= LRG::SPILL_REG) {
assert(!lrgs(bidx).mask().is_AllStack(),"AllStack should color");
lrg2reach[bidx] = spill_cnt;
spill_cnt++;
lidxs.append(bidx);
#ifdef ASSERT
splits.append(0);
#endif
#ifndef PRODUCT
if( PrintOpto && WizardMode && lrgs(bidx)._was_spilled1 )
tty->print_cr("Warning, 2nd spill of L%d",bidx);
#endif
}
}
Node ***Reaches = NEW_SPLIT_ARRAY( Node**, _cfg.number_of_blocks() + 1);
bool **UP = NEW_SPLIT_ARRAY( bool*, _cfg.number_of_blocks() + 1);
Node **debug_defs = NEW_SPLIT_ARRAY( Node*, spill_cnt );
VectorSet **UP_entry= NEW_SPLIT_ARRAY( VectorSet*, spill_cnt );
for (bidx = 0; bidx < _cfg.number_of_blocks() + 1; bidx++) {
Reaches[bidx] = NEW_SPLIT_ARRAY( Node*, spill_cnt );
UP[bidx] = NEW_SPLIT_ARRAY( bool, spill_cnt );
Node **Reachblock = Reaches[bidx];
bool *UPblock = UP[bidx];
for( slidx = 0; slidx < spill_cnt; slidx++ ) {
UPblock[slidx] = true; // Assume they start in registers
Reachblock[slidx] = NULL; // Assume that no def is present
}
}
#undef NEW_SPLIT_ARRAY
for( slidx = 0; slidx < spill_cnt; slidx++ )
UP_entry[slidx] = new VectorSet(split_arena);
for( bidx = 0; bidx < _cfg.number_of_blocks(); bidx++ ) {
if (C->check_node_count(spill_cnt, out_of_nodes)) {
return 0;
}
b = _cfg.get_block(bidx);
Node** Reachblock = Reaches[b->_pre_order];
UPblock = UP[b->_pre_order];
non_phi = 1;
for( slidx = 0; slidx < spill_cnt; slidx++ ) {
uint lidx = lidxs.at(slidx);
if( lrgs(lidx).is_singledef() &&
lrgs(lidx)._def->rematerialize() ) {
Reachblock[slidx] = lrgs(lidx)._def;
UPblock[slidx] = true;
Block *pred1 = _cfg.get_block_for_node(b->pred(1));
continue;
}
bool needs_phi = false;
bool needs_split = false;
bool has_phi = false;
n1 = b->pred(1);
pred = _cfg.get_block_for_node(n1);
pidx = pred->_pre_order;
Node **Ltmp = Reaches[pidx];
bool *Utmp = UP[pidx];
n1 = Ltmp[slidx];
u1 = Utmp[slidx];
n3 = n1;
u3 = u1;
for( inpidx = 2; inpidx < b->num_preds(); inpidx++ ) {
n2 = b->pred(inpidx);
pred = _cfg.get_block_for_node(n2);
pidx = pred->_pre_order;
Ltmp = Reaches[pidx];
Utmp = UP[pidx];
n2 = Ltmp[slidx];
u2 = Utmp[slidx];
if( n1 != n2 ) {
needs_phi = true;
}
if( n1 && n2 && (u1 != u2) ) {
needs_split = true;
}
n1 = n2;
u1 = u2;
if( (n3 == NULL) && (n2 != NULL) ){
n3 = n2;
u3 = u2;
}
} // End for all potential Phi inputs
for( insidx = 1; insidx <= b->end_idx(); insidx++ ) {
n1 = b->get_node(insidx);
phi = n1->is_Phi() ? n1->as_Phi() : NULL;
if( phi == NULL ) {
non_phi = insidx;
break;
}
if (_lrg_map.find_id(n1) == lidxs.at(slidx)) {
needs_phi = false;
has_phi = true;
Reachblock[slidx] = phi;
break;
} // end if found correct phi
} // end for all phi's
if( needs_phi || has_phi ) {
if( needs_phi ) {
assert(n3,"No non-NULL reaching DEF for a Phi");
phi = new (C) PhiNode(b->head(), n3->bottom_type());
Reachblock[slidx] = phi;
insert_proj(b, insidx++, phi, maxlrg++);
non_phi++;
_lrg_map.map(phi->_idx, lidx);
assert(_lrg_map.find_id(phi) == lidx, "Bad update on Union-Find mapping");
} // end if not found correct phi
assert(phi != NULL,"Must have a Phi Node here");
phis->push(phi);
UPblock[slidx] = true; // Assume new DEF is UP
if( is_high_pressure( b, &lrgs(lidx), b->end_idx()) && !prompt_use(b,lidx) )
UPblock[slidx] = false;
if( !needs_split && !u3 )
UPblock[slidx] = false;
} // end if phi is needed
else {
n1 = b->pred(1);
pred = _cfg.get_block_for_node(n1);
pidx = pred->_pre_order;
Node **Ltmp = Reaches[pidx];
bool *Utmp = UP[pidx];
Reachblock[slidx] = Ltmp[slidx];
UPblock[slidx] = Utmp[slidx];
} // end else no Phi is needed
} // end for all spilling live ranges
#ifndef PRODUCT
if(trace_spilling()) {
tty->print("/`\nBlock %d: ", b->_pre_order);
tty->print("Reaching Definitions after Phi handling\n");
for( uint x = 0; x < spill_cnt; x++ ) {
tty->print("Spill Idx %d: UP %d: Node\n",x,UPblock[x]);
if( Reachblock[x] )
Reachblock[x]->dump();
else
tty->print("Undefined\n");
}
}
#endif
for( insidx = 0; insidx < spill_cnt; insidx++ ) {
debug_defs[insidx] = (UPblock[insidx]) ? NULL : Reachblock[insidx];
if( UPblock[insidx] ) // Memoize UP decision at block start
UP_entry[insidx]->set( b->_pre_order );
}
for( insidx = 1; insidx <= b->end_idx(); insidx++ ) {
Node *n = b->get_node(insidx);
uint defidx = _lrg_map.find_id(n);
uint cnt = n->req();
if (n->is_Phi()) {
if (defidx < _lrg_map.max_lrg_id()) {
if( lrgs(defidx).reg() < LRG::SPILL_REG ) {
uint i;
Node *u = NULL;
for( i = 1; i < cnt; i++ ) {
if( n->in(i) != u && n->in(i) != n ) {
if( u != NULL ) // If it's the 2nd, bail out
break;
u = n->in(i); // Else record it
}
}
assert( u, "at least 1 valid input expected" );
if (i >= cnt) { // Found one unique input
assert(_lrg_map.find_id(n) == _lrg_map.find_id(u), "should be the same lrg");
n->replace_by(u); // Then replace with unique input
n->disconnect_inputs(NULL, C);
b->remove_node(insidx);
insidx--;
b->_ihrp_index--;
b->_fhrp_index--;
}
}
}
continue;
}
assert( insidx > b->_ihrp_index ||
(b->_reg_pressure < (uint)INTPRESSURE) ||
b->_ihrp_index > 4000000 ||
b->_ihrp_index >= b->end_idx() ||
!b->get_node(b->_ihrp_index)->is_Proj(), "" );
assert( insidx > b->_fhrp_index ||
(b->_freg_pressure < (uint)FLOATPRESSURE) ||
b->_fhrp_index > 4000000 ||
b->_fhrp_index >= b->end_idx() ||
!b->get_node(b->_fhrp_index)->is_Proj(), "" );
if( (insidx == b->_ihrp_index) || (insidx == b->_fhrp_index) ) {
for( slidx = 0; slidx < spill_cnt; slidx++ ) {
n1 = Reachblock[slidx];
if( n1 == NULL ) continue;
uint lidx = lidxs.at(slidx);
if( UPblock[slidx] ) {
if( is_high_pressure( b, &lrgs(lidx), insidx ) &&
!n1->rematerialize() ) {
if( debug_defs[slidx] != NULL ) {
Reachblock[slidx] = debug_defs[slidx];
}
else {
int insert_point = insidx-1;
while( insert_point > 0 ) {
Node *n = b->get_node(insert_point);
if (n->is_Phi()) {
break;
}
if (_lrg_map.live_range_id(n) == lidx) {
break;
}
uint i;
for( i = 1; i < n->req(); i++ ) {
if (_lrg_map.live_range_id(n->in(i)) == lidx) {
break;
}
}
if (i < n->req()) {
break;
}
insert_point--;
}
uint orig_eidx = b->end_idx();
maxlrg = split_DEF( n1, b, insert_point, maxlrg, Reachblock, debug_defs, splits, slidx);
if (!maxlrg) {
return 0;
}
if (b->end_idx() > orig_eidx) {
insidx++;
}
}
UPblock[slidx] = false;
#ifndef PRODUCT
if( trace_spilling() ) {
tty->print("\nNew Split DOWN DEF of Spill Idx ");
tty->print("%d, UP %d:\n",slidx,false);
n1->dump();
}
#endif
}
} // end if LRG is UP
} // end for all spilling live ranges
assert( b->get_node(insidx) == n, "got insidx set incorrectly" );
} // end if crossing HRP Boundry
if (defidx >= _lrg_map.max_lrg_id()) {
continue;
}
LRG &deflrg = lrgs(defidx);
uint copyidx = n->is_Copy();
if (copyidx && defidx == _lrg_map.live_range_id(n->in(copyidx))) {
n->replace_by( n->in(copyidx) );
n->set_req( copyidx, NULL );
b->remove_node(insidx--);
b->_ihrp_index--; // Adjust the point where we go hi-pressure
b->_fhrp_index--;
continue;
}
ssssssss59
最新推荐文章于 2024-08-01 15:05:06 发布