Stacktrace
libjvm.so!PhaseIdealLoop::split_if_with_blocks(PhaseIdealLoop * const this, VectorSet & visited, Node_Stack & nstack) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/loopopts.cpp:1649)
libjvm.so!PhaseIdealLoop::build_and_optimize(PhaseIdealLoop * const this, LoopOptsMode mode) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/loopnode.cpp:4011)
libjvm.so!PhaseIdealLoop::PhaseIdealLoop(PhaseIdealLoop * const this, PhaseIterGVN & igvn, LoopOptsMode mode) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/loopnode.hpp:1068)
libjvm.so!PhaseIdealLoop::optimize(PhaseIterGVN & igvn, LoopOptsMode mode) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/loopnode.hpp:1146)
libjvm.so!Compile::optimize_loops(Compile * const this, PhaseIterGVN & igvn, LoopOptsMode mode) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/compile.cpp:2006)
libjvm.so!Compile::Optimize(Compile * const this) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/compile.cpp:2229)
libjvm.so!Compile::Compile(Compile * const this, ciEnv * ci_env, ciMethod * target, int osr_bci, bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing, bool install_code, DirectiveSet * directive) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/compile.cpp:771)
libjvm.so!C2Compiler::compile_method(C2Compiler * const this, ciEnv * env, ciMethod * target, int entry_bci, bool install_code, DirectiveSet * directive) (/home/qingfeng.yy/jdktip/src/hotspot/share/opto/c2compiler.cpp:103)
libjvm.so!CompileBroker::invoke_compiler_on_method(CompileTask * task) (/home/qingfeng.yy/jdktip/src/hotspot/share/compiler/compileBroker.cpp:2312)
libjvm.so!CompileBroker::compiler_thread_loop() (/home/qingfeng.yy/jdktip/src/hotspot/share/compiler/compileBroker.cpp:1985)
libjvm.so!CompilerThread::thread_entry(JavaThread * thread, JavaThread * __the_thread__) (/home/qingfeng.yy/jdktip/src/hotspot/share/compiler/compilerThread.cpp:59)
libjvm.so!JavaThread::thread_main_inner(JavaThread * const this) (/home/qingfeng.yy/jdktip/src/hotspot/share/runtime/thread.cpp:1310)
libjvm.so!JavaThread::run(JavaThread * const this) (/home/qingfeng.yy/jdktip/src/hotspot/share/runtime/thread.cpp:1293)
libjvm.so!Thread::call_run(Thread * const this) (/home/qingfeng.yy/jdktip/src/hotspot/share/runtime/thread.cpp:394)
libjvm.so!thread_native_entry(Thread * thread) (/home/qingfeng.yy/jdktip/src/hotspot/os/linux/os_linux.cpp:719)
libpthread.so.0!start_thread (Unknown Source:0)
libc.so.6!clone (Unknown Source:0)
Java
public class SIF {
static int f1(Object a) { return a.hashCode();}
static int f2(Object a) { return a.hashCode();}
static int test(Object a1, Object a2, boolean flag1) {
boolean flag2;
int f = 0;
Object a = null;
if (flag1) {
flag2 = true;
a = a1;
} else {
flag2 = false;
a = a2;
}
if (flag2) {
f = f1(a);
} else {
f = f2(a);
}
return f;
}
public static void main(String[] args) {
Object a = new Object();
for (int i = 0; i < 20_000; i++) {
test(a, a, (i % 2) == 0);
}
}
}
Ideal Graph
Before:
After:
我理解的split if有点像字面意思的反操作,就是吧两个if,合并成一个,就像这样:
// before
if (flag1 == 0) {
phi = a2
} else {
phi = a1
}
if (flag1 == 0){
f1(phi);
} else {
f2(phi);
}
// after
phi = cmove(a1,a2);
if (flag1==0){
f1(phi);
}else {
f2(phi);
}
void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
// Cloning Cmp through Phi's involves the split-if transform.
// FastLock is not used by an If
if (n->is_Cmp() && !n->is_FastLock()) {
Node *n_ctrl = get_ctrl(n);
// Determine if the Node has inputs from some local Phi.
// Returns the block to clone thru.
Node *n_blk = has_local_phi_input(n);
if (n_blk != n_ctrl) {
return;
}
if (!can_split_if(n_ctrl)) {
return;
}
if (n->outcnt() != 1) {
return; // Multiple bool's from 1 compare?
}
Node *bol = n->unique_out();
assert(bol->is_Bool(), "expect a bool here");
if (bol->outcnt() != 1) {
return;// Multiple branches from 1 compare?
}
Node *iff = bol->unique_out();
// Check some safety conditions
if (iff->is_If()) { // Classic split-if?
if (iff->in(0) != n_ctrl) {
return; // Compare must be in same blk as if
}
} else if (iff->is_CMove()) { // Trying to split-up a CMOVE
// Can't split CMove with different control edge.
if (iff->in(0) != NULL && iff->in(0) != n_ctrl ) {
return;
}
if (get_ctrl(iff->in(2)) == n_ctrl ||
get_ctrl(iff->in(3)) == n_ctrl) {
return; // Inputs not yet split-up
}
if (get_loop(n_ctrl) != get_loop(get_ctrl(iff))) {
return; // Loop-invar test gates loop-varying CMOVE
}
} else {
return; // some other kind of node, such as an Allocate
}
// When is split-if profitable? Every 'win' on means some control flow
// goes dead, so it's almost always a win.
int policy = 0;
// Split compare 'n' through the merge point if it is profitable
Node *phi = split_thru_phi( n, n_ctrl, policy);
if (!phi) {
return;
}
// Found a Phi to split thru!
// Replace 'n' with the new phi
_igvn.replace_node(n, phi);
// Now split the bool up thru the phi
Node *bolphi = split_thru_phi(bol, n_ctrl, -1);
guarantee(bolphi != NULL, "null boolean phi node");
_igvn.replace_node(bol, bolphi);
assert(iff->in(1) == bolphi, "");
if (bolphi->Value(&_igvn)->singleton()) {
return;
}
// Conditional-move? Must split up now
if (!iff->is_If()) {
Node *cmovphi = split_thru_phi(iff, n_ctrl, -1);
_igvn.replace_node(iff, cmovphi);
return;
}
// Now split the IF
do_split_if(iff);
return;
}
// Two identical ifs back to back can be merged
if (identical_backtoback_ifs(n) && can_split_if(n->in(0))) {
Node *n_ctrl = n->in(0);
PhiNode* bolphi = PhiNode::make_blank(n_ctrl, n->in(1));
IfNode* dom_if = idom(n_ctrl)->as_If();
Node* proj_true = dom_if->proj_out(1);
Node* proj_false = dom_if->proj_out(0);
Node* con_true = _igvn.makecon(TypeInt::ONE);
Node* con_false = _igvn.makecon(TypeInt::ZERO);
for (uint i = 1; i < n_ctrl->req(); i++) {
if (is_dominator(proj_true, n_ctrl->in(i))) {
bolphi->init_req(i, con_true);
} else {
assert(is_dominator(proj_false, n_ctrl->in(i)), "bad if");
bolphi->init_req(i, con_false);
}
}
register_new_node(bolphi, n_ctrl);
_igvn.replace_input_of(n, 1, bolphi);
// Now split the IF
do_split_if(n);
return;
}
// Check for an IF ready to split; one that has its
// condition codes input coming from a Phi at the block start.
int n_op = n->Opcode();
// Check for an IF being dominated by another IF same test
if (n_op == Op_If ||
n_op == Op_RangeCheck) {
Node *bol = n->in(1);
uint max = bol->outcnt();
// Check for same test used more than once?
if (max > 1 && bol->is_Bool()) {
// Search up IDOMs to see if this IF is dominated.
Node *cutoff = get_ctrl(bol);
// Now search up IDOMs till cutoff, looking for a dominating test
Node *prevdom = n;
Node *dom = idom(prevdom);
while (dom != cutoff) {
if (dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom) {
// It's invalid to move control dependent data nodes in the inner
// strip-mined loop, because:
// 1) break validation of LoopNode::verify_strip_mined()
// 2) move code with side-effect in strip-mined loop
// Move to the exit of outer strip-mined loop in that case.
Node* out_le = is_inner_of_stripmined_loop(dom);
if (out_le != NULL) {
prevdom = out_le;
}
// Replace the dominated test with an obvious true or false.
// Place it on the IGVN worklist for later cleanup.
C->set_major_progress();
dominated_by(prevdom, n, false, true);
#ifndef PRODUCT
if( VerifyLoopOptimizations ) verify();
#endif
return;
}
prevdom = dom;
dom = idom(prevdom);
}
}
}
try_sink_out_of_loop(n);
try_move_store_after_loop(n);
// Check for Opaque2's who's loop has disappeared - who's input is in the
// same loop nest as their output. Remove 'em, they are no longer useful.
if( n_op == Op_Opaque2 &&
n->in(1) != NULL &&
get_loop(get_ctrl(n)) == get_loop(get_ctrl(n->in(1))) ) {
_igvn.replace_node( n, n->in(1) );
}
}
很多情况下会做split if。这里选一种,即identical_backtoidentical_backtoback_ifs,也就是紧挨着的两个if,做合并。
最开始是这样:
等identical_backtoback_ifs里面bolphi做完,就会创建新的phi节点,如下图89#Phi:
接着,do_split_if做实际merge工作
//------------------------------do_split_if------------------------------------
// Found an If getting its condition-code input from a Phi in the same block.
// Split thru the Region.
void PhaseIdealLoop::do_split_if( Node *iff ) {
if (PrintOpto && VerifyLoopOptimizations) {
tty->print_cr("Split-if");
}
if (TraceLoopOpts) {
tty->print_cr("SplitIf");
}
C->set_major_progress();
Node *region = iff->in(0);
Node *region_dom = idom(region);
// We are going to clone this test (and the control flow with it) up through
// the incoming merge point. We need to empty the current basic block.
// Clone any instructions which must be in this block up through the merge
// point.
DUIterator i, j;
bool progress = true;
while (progress) {
progress = false;
for (i = region->outs(); region->has_out(i); i++) {
Node* n = region->out(i);
if( n == region ) continue;
// The IF to be split is OK.
if( n == iff ) continue;
if( !n->is_Phi() ) { // Found pinned memory op or such
if (split_up(n, region, iff)) {
i = region->refresh_out_pos(i);
progress = true;
}
continue;
}
assert( n->in(0) == region, "" );
// Recursively split up all users of a Phi
for (j = n->outs(); n->has_out(j); j++) {
Node* m = n->out(j);
// If m is dead, throw it away, and declare progress
if (_nodes[m->_idx] == NULL) {
_igvn.remove_dead_node(m);
// fall through
}
else if (m != iff && split_up(m, region, iff)) {
// fall through
} else {
continue;
}
// Something unpredictable changed.
// Tell the iterators to refresh themselves, and rerun the loop.
i = region->refresh_out_pos(i);
j = region->refresh_out_pos(j);
progress = true;
}
}
}
// Now we have no instructions in the block containing the IF.
// Split the IF.
Node *new_iff = split_thru_region( iff, region );
// Replace both uses of 'new_iff' with Regions merging True/False
// paths. This makes 'new_iff' go dead.
Node *old_false = NULL, *old_true = NULL;
Node *new_false = NULL, *new_true = NULL;
for (DUIterator_Last j2min, j2 = iff->last_outs(j2min); j2 >= j2min; --j2) {
Node *ifp = iff->last_out(j2);
assert( ifp->Opcode() == Op_IfFalse || ifp->Opcode() == Op_IfTrue, "" );
ifp->set_req(0, new_iff);
Node *ifpx = split_thru_region( ifp, region );
// Replace 'If' projection of a Region with a Region of
// 'If' projections.
ifpx->set_req(0, ifpx); // A TRUE RegionNode
// Setup dominator info
set_idom(ifpx, region_dom, dom_depth(region_dom) + 1);
// Check for splitting loop tails
if( get_loop(iff)->tail() == ifp )
get_loop(iff)->_tail = ifpx;
// Replace in the graph with lazy-update mechanism
new_iff->set_req(0, new_iff); // hook self so it does not go dead
lazy_replace(ifp, ifpx);
new_iff->set_req(0, region);
// Record bits for later xforms
if( ifp->Opcode() == Op_IfFalse ) {
old_false = ifp;
new_false = ifpx;
} else {
old_true = ifp;
new_true = ifpx;
}
}
_igvn.remove_dead_node(new_iff);
// Lazy replace IDOM info with the region's dominator
lazy_replace(iff, region_dom);
lazy_update(region, region_dom); // idom must be update before handle_uses
region->set_req(0, NULL); // Break the self-cycle. Required for lazy_update to work on region
// Now make the original merge point go dead, by handling all its uses.
small_cache region_cache;
// Preload some control flow in region-cache
region_cache.lru_insert( new_false, new_false );
region_cache.lru_insert( new_true , new_true );
// Now handle all uses of the splitting block
for (DUIterator k = region->outs(); region->has_out(k); k++) {
Node* phi = region->out(k);
if (!phi->in(0)) { // Dead phi? Remove it
_igvn.remove_dead_node(phi);
} else if (phi == region) { // Found the self-reference
continue; // No roll-back of DUIterator
} else if (phi->is_Phi()) { // Expected common case: Phi hanging off of Region
assert(phi->in(0) == region, "Inconsistent graph");
// Need a per-def cache. Phi represents a def, so make a cache
small_cache phi_cache;
// Inspect all Phi uses to make the Phi go dead
for (DUIterator_Last lmin, l = phi->last_outs(lmin); l >= lmin; --l) {
Node* use = phi->last_out(l);
// Compute the new DEF for this USE. New DEF depends on the path
// taken from the original DEF to the USE. The new DEF may be some
// collection of PHI's merging values from different paths. The Phis
// inserted depend only on the location of the USE. We use a
// 2-element cache to handle multiple uses from the same block.
handle_use(use, phi, &phi_cache, region_dom, new_false, new_true, old_false, old_true);
} // End of while phi has uses
// Remove the dead Phi
_igvn.remove_dead_node( phi );
} else {
assert(phi->in(0) == region, "Inconsistent graph");
// Random memory op guarded by Region. Compute new DEF for USE.
handle_use(phi, region, ®ion_cache, region_dom, new_false, new_true, old_false, old_true);
}
// Every path above deletes a use of the region, except for the region
// self-cycle (which is needed by handle_use calling find_use_block
// calling get_ctrl calling get_ctrl_no_update looking for dead
// regions). So roll back the DUIterator innards.
--k;
} // End of while merge point has phis
_igvn.remove_dead_node(region);
#ifndef PRODUCT
if( VerifyLoopOptimizations ) verify();
#endif
}
split_thru_region( iff, region )困惑了我很久,其中iff是43#If,region是35#Region
调用split_thru_region之前
调用split_thru_region之后
到remove_dead_node(new_iff)的时候:
我理解,这里说的就是,在合并点即35#Region处,他创建了两个新if代替(if(false){}else{} 和 if(true){}else{}),相当于拆分了43#If