public void updateKillGenSet(String methodName, QuadrupletStmt stmt, BlockDataFlowState bFlow) {
    if (stmt == null) return;

    BitSet out = bFlow.getOut();
    BitSet gen = bFlow.getGen();
    BitSet kill = bFlow.getKill();

    for (Name name : this.uniqueGlobals.get(methodName)) {
      int i = this.uniqueGlobals.get(methodName).indexOf(name);

      boolean resetName = false;

      if (name.isArray()) {
        Name myName = name;

        do {
          ArrayName array = (ArrayName) myName;
          if (array.getIndex().equals(stmt.getDestination())) { // Index being reassigned, KILL!
            resetName = true;
          }

          myName = array.getIndex();

        } while (myName.isArray());

        if (stmt.getDestination().isArray()) {
          ArrayName dest = (ArrayName) stmt.getDestination();
          ArrayName arrName = (ArrayName) name;
          if (dest.getIndex().getClass().equals(ConstantName.class)
              && !arrName.getIndex().getClass().equals(ConstantName.class)) {
            if (arrName.getId().equals(dest.getId())) {
              resetName = true;
            }
          }
        }
      }

      if (resetName) {
        if (out.get(i)) {
          kill.set(i);
        }

        gen.clear(i);
      }

      if (name.equals(stmt.getDestination())) {
        gen.set(i);
      }
    }
  }
  // For each use of a Name, see all the reaching definitions for that Name
  // If there exists only ONE reaching assignment definition that assigns to Name,
  // replace Name with definition's LHS
  private Name copyPropagateOnArg(Name arg, BlockDataFlowState bFlow) {
    if (arg != null) {
      HashMap<Name, HashSet<QuadrupletStmt>> nameToStmtsThatAssignIt =
          assignmentDefGenerator.getNameToQStmtsThatAssignIt();
      BitSet in = bFlow.getIn();
      HashSet<QuadrupletStmt> stmtsAssigningName;
      QuadrupletStmt reachingAssignmentStmt = null;
      int numReachingDefs;

      stmtsAssigningName = nameToStmtsThatAssignIt.get(arg);
      numReachingDefs = 0;
      if (stmtsAssigningName != null) {
        for (QuadrupletStmt qs : stmtsAssigningName) {
          if (in.get(qs.getMyId())) {
            numReachingDefs++;
            reachingAssignmentStmt = qs;
          }
        }
        if (numReachingDefs == 1) {
          // There is exactly one assignment statement reaching this block
          // so we can perform copy propagation
          // Return the name which we should replace the arg with
          return reachingAssignmentStmt.getArg1();
        }
      }
      // Check if Name is ArrayName and try to optimize index
      if (arg.getClass().equals(ArrayName.class)) {
        Name arrIndex = ((ArrayName) arg).getIndex();
        Name propagatedName = copyPropagateOnArg(arrIndex, bFlow);
        if (propagatedName != null) {
          ((ArrayName) arg).setIndex(propagatedName);
        }
      }
    }
    return null;
  }
  private void optimize(CFGBlock block) {
    BlockDataFlowState bFlow = assignmentDefGenerator.getBlockAssignReachingDefs().get(block);
    QuadrupletStmt qStmt;
    PopStmt popStmt;
    PushStmt pushStmt;
    CmpStmt cStmt;
    Name newArg1, newArg2, dest;

    for (LIRStatement stmt : block.getStatements()) {
      // Reset kill set
      bFlow.getKill().clear();
      if (stmt.getClass().equals(CallStmt.class)) {
        CallStmt callStmt = (CallStmt) stmt;
        if (callStmt.getMethodLabel().equals(ProgramFlattener.exceptionHandlerLabel)) continue;

        // Update BlockDataFlowState kill set
        assignmentDefGenerator.invalidateFunctionCall(bFlow);
        // Update BlockDataFlowState in set by using updated kill set
        bFlow.getIn().xor(bFlow.getKill());

      } else if (stmt.getClass().equals(QuadrupletStmt.class)) {
        qStmt = (QuadrupletStmt) stmt;
        newArg1 = copyPropagateOnArg(qStmt.getArg1(), bFlow);
        if (newArg1 != null) {
          qStmt.setArg1(newArg1);
        }
        newArg2 = copyPropagateOnArg(qStmt.getArg2(), bFlow);
        if (newArg2 != null) {
          qStmt.setArg2(newArg2);
        }
        dest = qStmt.getDestination();
        if (dest != null) {
          // Check if dest is ArrayName and try to optimize index
          if (dest.getClass().equals(ArrayName.class)) {
            Name arrIndex = ((ArrayName) dest).getIndex();
            Name propagatedName = copyPropagateOnArg(arrIndex, bFlow);
            if (propagatedName != null) ((ArrayName) dest).setIndex(propagatedName);
          }
        }

        // Update BlockDataFlowState kill set
        assignmentDefGenerator.updateKillGenSet(dest, bFlow);
        // Update BlockDataFlowState in set by using updated kill set
        bFlow.getIn().xor(bFlow.getKill());

        // Optimize PopStmt
      } else if (stmt.getClass().equals(PopStmt.class)) {
        popStmt = (PopStmt) stmt;
        newArg1 = copyPropagateOnArg(popStmt.getName(), bFlow);
        if (newArg1 != null) {
          popStmt.setName(newArg1);
        }

        // Optimize PushStmt
      } else if (stmt.getClass().equals(PushStmt.class)) {
        pushStmt = (PushStmt) stmt;
        newArg1 = copyPropagateOnArg(pushStmt.getName(), bFlow);
        if (newArg1 != null) {
          pushStmt.setName(newArg1);
        }

        // Optimize CmpStmt
      } else if (stmt.getClass().equals(CmpStmt.class)) {
        cStmt = (CmpStmt) stmt;
        newArg1 = copyPropagateOnArg(cStmt.getArg1(), bFlow);
        if (newArg1 != null) {
          cStmt.setArg1(newArg1);
        }
        newArg2 = copyPropagateOnArg(cStmt.getArg2(), bFlow);
        if (newArg2 != null) {
          cStmt.setArg2(newArg2);
        }
      }
    }
  }