[Inside HotSpot] C1循环不变代码提升优化

如果关闭分层编译,执行GVN优化前会使用ShortLoopOptimizer做一些简单的循环优化,其中就包括循环不变代码提升(Loop Invariant Code Motion,LCM)。LCM是指将循环中不变的值移动到循环外面,消除每次进行计算的必要:

void LoopInvariantCodeMotion::process_block(BlockBegin* block) {
    ...
  // 形参表示位于循环的所有基本块。遍历基本块中的每一条指令
  while (cur != NULL) {
    bool cur_invariant = false;
    // 指令是常量且不能发生trap;指令是算数/逻辑/位运算,操作数都是不变量且不能发生trap
    // 指令读取字段值,字段不是volatile,或者是读取数组中的某个元素,数组是不变量等;
    // 指令获取数组长度,且数组长度是不变量。如果发生上述条件之一,那么该指令是循环不变量
    if (cur->as_Constant() != NULL) {
      cur_invariant = !cur->can_trap();
    } else if (cur->as_ArithmeticOp() != NULL || cur->as_LogicOp() != NULL || cur->as_ShiftOp() != NULL) {
      Op2* op2 = (Op2*)cur;
      cur_invariant = ...;
    } else if (cur->as_LoadField() != NULL) {
      cur_invariant = ...;
    } else if (cur->as_ArrayLength() != NULL) {
      ArrayLength *length = cur->as_ArrayLength();
      cur_invariant = is_invariant(length->array());
    } else if (cur->as_LoadIndexed() != NULL) {
      LoadIndexed *li = (LoadIndexed *)cur->as_LoadIndexed();
      cur_invariant = ...;
    }
    // 如果该指令是循环不变量
    if (cur_invariant) {
      ...
      // 将该指令从循环内部移动到循环前面
      Instruction* next = cur->next();
      Instruction* in = _insertion_point->next();
      _insertion_point = _insertion_point->set_next(cur);
      cur->set_next(in);
      ...
    } else {
      prev = cur;
      cur = cur->next();
    }
  }
}

LCM遍历构成循环的所有基本块,然后遍历基本块的每一条指令,当发现满足要求的循环不变量时,将循环不变量从循环基本块中移除,然后添加到insertion_point所在的基本块,insertion_point即支配循环头的基本块,具体到例子中,如下Java代码:

public static void loopInvariant(){
 class LoopInvariantMotion {
    private static int[] arr = new int[]{1,2,3,4};
    public static void loopInvariant(){
        int s = 0;
        for(int i=0;i<10;i++){
            s += arr.length;  // 循环不变量arr.length
            s += arr[2];       // 循环不变量arr[2]
        }
    }
}

它对应的HIR如下图所示,其中B1和B2构成for循环,B0基本块支配B1基本块:

当发现循环基本块B2中的两个不变量后,C1会将它移到循环外面的B0基本块,该基本块支配循环头基本块B1,如下图所示:

原文地址:https://www.cnblogs.com/kelthuzadx/p/12340658.html