/**
   * Determine whether dataflow should be propagated on given edge.
   *
   * @param edge the edge
   * @return true if dataflow should be propagated on the edge, false otherwise
   */
  private boolean isExceptionEdge(Edge edge) {
    boolean isExceptionEdge = edge.isExceptionEdge();
    if (isExceptionEdge) {
      if (DEBUG) {
        System.out.println("NOT Ignoring " + edge);
      }
      return true; // false
    }
    if (edge.getType() != EdgeTypes.FALL_THROUGH_EDGE) {
      return false;
    }
    InstructionHandle h = edge.getSource().getLastInstruction();
    if (h != null
        && h.getInstruction() instanceof IFNONNULL
        && isNullCheck(h, methodGen.getConstantPool())) {
      return true;
    }

    return false;
  }
  /**
   * Clear deref sets of values if this edge is the non-null branch of an if comparison.
   *
   * @param fact a datflow fact
   * @param edge edge to check
   * @return possibly-modified dataflow fact
   */
  private @CheckForNull ValueNumber findValueKnownNonnullOnBranch(
      UnconditionalValueDerefSet fact, Edge edge) {

    IsNullValueFrame invFrame = invDataflow.getResultFact(edge.getSource());
    if (!invFrame.isValid()) {
      return null;
    }
    IsNullConditionDecision decision = invFrame.getDecision();
    if (decision == null) {
      return null;
    }

    IsNullValue inv = decision.getDecision(edge.getType());
    if (inv == null || !inv.isDefinitelyNotNull()) {
      return null;
    }
    ValueNumber value = decision.getValue();
    if (DEBUG) {
      System.out.println("Value number " + value + " is known nonnull on " + edge);
    }

    return value;
  }
  /**
   * Find out if any VNs in the source block contribute to unconditionally dereferenced VNs in the
   * target block. If so, the VN in the source block is also unconditionally dereferenced, and we
   * must propagate the target VN's dereferences.
   *
   * @param fact a dataflow value
   * @param edge edge to check for merge input values
   * @return possibly-modified dataflow value
   */
  private UnconditionalValueDerefSet propagateDerefSetsToMergeInputValues(
      UnconditionalValueDerefSet fact, Edge edge) {

    ValueNumberFrame blockValueNumberFrame = vnaDataflow.getResultFact(edge.getSource());
    ValueNumberFrame targetValueNumberFrame = vnaDataflow.getStartFact(edge.getTarget());

    UnconditionalValueDerefSet originalFact = fact;
    fact = duplicateFact(fact);

    if (blockValueNumberFrame.isValid() && targetValueNumberFrame.isValid()) {
      int slots = 0;
      if (targetValueNumberFrame.getNumSlots() == blockValueNumberFrame.getNumSlots()) {
        slots = targetValueNumberFrame.getNumSlots();
      } else if (targetValueNumberFrame.getNumLocals() == blockValueNumberFrame.getNumLocals()) {
        slots = targetValueNumberFrame.getNumLocals();
      }

      if (slots > 0) {
        if (DEBUG) {
          System.out.println("** Valid VNA frames for " + edge);
          System.out.println("** Block : " + blockValueNumberFrame);
          System.out.println("** Target: " + targetValueNumberFrame);
        }

        for (int i = 0; i < slots; i++) {
          ValueNumber blockVN = blockValueNumberFrame.getValue(i);
          ValueNumber targetVN = targetValueNumberFrame.getValue(i);
          if (blockVN.equals(targetVN)) {
            continue;
          }
          fact.clearDerefSet(blockVN);
          if (originalFact.isUnconditionallyDereferenced(targetVN)) {
            fact.setDerefSet(blockVN, originalFact.getUnconditionalDerefLocationSet(targetVN));
          }
        } // for all slots

        for (ValueNumber blockVN : blockValueNumberFrame.valueNumbersForLoads()) {
          AvailableLoad load = blockValueNumberFrame.getLoad(blockVN);
          if (load == null) {
            continue;
          }
          ValueNumber[] targetVNs = targetValueNumberFrame.getAvailableLoad(load);
          if (targetVNs != null) {
            for (ValueNumber targetVN : targetVNs) {
              if (targetVN.hasFlag(ValueNumber.PHI_NODE)
                  && fact.isUnconditionallyDereferenced(targetVN)
                  && !fact.isUnconditionallyDereferenced(blockVN)) {
                // Block VN is also dereferenced
                // unconditionally.
                AvailableLoad targetLoad = targetValueNumberFrame.getLoad(targetVN);
                if (!load.equals(targetLoad)) {
                  continue;
                }
                if (DEBUG) {
                  System.out.println(
                      "** Copy vn derefs for " + load + " from " + targetVN + " --> " + blockVN);
                  System.out.println(
                      "** block phi for "
                          + System.identityHashCode(blockValueNumberFrame)
                          + " is "
                          + blockValueNumberFrame.phiNodeForLoads);
                  System.out.println(
                      "** target phi for "
                          + System.identityHashCode(targetValueNumberFrame)
                          + " is "
                          + targetValueNumberFrame.phiNodeForLoads);
                }
                fact.setDerefSet(blockVN, fact.getUnconditionalDerefLocationSet(targetVN));
              }
            }
          }
        }
      }
    }
    if (DEBUG) {
      System.out.println("Target VNF: " + targetValueNumberFrame);
      System.out.println("Block VNF: " + blockValueNumberFrame);
      System.out.println("fact: " + fact);
    }
    fact.cleanDerefSet(null, blockValueNumberFrame);
    return fact;
  }
  public void meetInto(
      UnconditionalValueDerefSet fact,
      Edge edge,
      UnconditionalValueDerefSet result,
      boolean onlyEdge) {
    if (isExceptionEdge(edge) && !onlyEdge) {
      if (DEBUG) {
        System.out.println("Skipping exception edge");
      }
      return;
    }

    ValueNumber knownNonnullOnBranch = null;
    // Edge transfer function
    if (isFactValid(fact)) {
      fact = propagateDerefSetsToMergeInputValues(fact, edge);
      if (invDataflow != null) {
        knownNonnullOnBranch = findValueKnownNonnullOnBranch(fact, edge);
        if (knownNonnullOnBranch != null) {
          fact = duplicateFact(fact);
          fact.clearDerefSet(knownNonnullOnBranch);
        }
      }
    }
    boolean isBackEdge = edge.isBackwardInBytecode();
    Set<Integer> loopExitBranches = ClassContext.getLoopExitBranches(method, methodGen);
    assert loopExitBranches != null;
    boolean sourceIsTopOfLoop = edge.sourceIsTopOfLoop(loopExitBranches);
    if (sourceIsTopOfLoop && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE) {
      isBackEdge = true;
    }
    /*
    if (false && (edge.getType() == EdgeTypes.IFCMP_EDGE || sourceIsTopOfLoop)) {
        System.out.println("Meet into " + edge);
        System.out.println("  foo2: " + sourceIsTopOfLoop);
        System.out.println("  getType: " + edge.getType());
        System.out.println("  Backedge according to bytecode: " + isBackEdge);
        System.out.println("  Fact hashCode: " + System.identityHashCode(result));
        System.out.println("  Initial fact: " + result);
        System.out.println("  Edge fact: " + fact);
    }
     */
    if (result.isTop() || fact.isBottom()) {
      // Make result identical to other fact
      copy(fact, result);
      if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
        result.resultsFromBackEdge = true;
      }
    } else if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
      result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
      result.resultsFromBackEdge = true;
      if (DEBUG) {
        System.out.println(
            "\n Forcing union of " + System.identityHashCode(result) + " due to backedge info");
        System.out.println("  result: " + result);
      }

    } else if (result.isBottom() || fact.isTop()) {
      // No change in result fact
    } else {
      // Dataflow merge
      // (intersection of unconditional deref values)
      if (ASSUME_NONZERO_TRIP_LOOPS && result.resultsFromBackEdge) {
        result.backEdgeUpdateCount++;
        if (result.backEdgeUpdateCount < 10) {
          if (DEBUG) {
            System.out.println(
                "\n Union update of " + System.identityHashCode(result) + " due to backedge info");
          }
          result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
          return;
        }
      }
      result.mergeWith(fact, knownNonnullOnBranch, vnaDataflow.getAnalysis().getFactory());
      if (DEBUG) {
        System.out.println("  updated: " + System.identityHashCode(result));
        System.out.println("  result: " + result);
      }
    }
    if (DEBUG && isBackEdge && edge.getType() == EdgeTypes.IFCMP_EDGE) {
      System.out.println("  result: " + result);
    }
  }