• C2 Split If


    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:
    image

    After:
    image

    我理解的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,做合并。

    最开始是这样:
    image

    等identical_backtoback_ifs里面bolphi做完,就会创建新的phi节点,如下图89#Phi:
    image

    接着,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, &region_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之前
    image

    调用split_thru_region之后
    image

    到remove_dead_node(new_iff)的时候:
    image

    我理解,这里说的就是,在合并点即35#Region处,他创建了两个新if代替(if(false){}else{} 和 if(true){}else{}),相当于拆分了43#If

  • 相关阅读:
    redis中save和bgsave区别
    go语言标准库
    numpy 学习:数组改变形状、副本和view
    Python 开始:变量、操作符、print()和type()
    numpy 学习:数据类型和空值
    Python 数据类型:布尔类型和None
    numpy 学习:数组的拼接、堆叠和拆分
    numpy 学习:数组的查找
    Databricks 第12篇:Notebook 工作流
    numpy 学习:通用函数(包含数学函数)
  • 原文地址:https://www.cnblogs.com/kelthuzadx/p/15718397.html
Copyright © 2020-2023  润新知