public class DeadCodeEliminationPhase extends Phase {

  public static class Options {
    // @formatter:off
    @Option(help = "Disable optional dead code eliminations", type = OptionType.Debug)
    public static final OptionValue<Boolean> ReduceDCE = new OptionValue<>(true);
    // @formatter:on
  }

  // Metrics
  private static final DebugMetric metricNodesRemoved = Debug.metric("NodesRemoved");

  public enum Optionality {
    Optional,
    Required;
  }

  /**
   * Creates a dead code elimination phase that will be run irrespective of {@link
   * Options#ReduceDCE}.
   */
  public DeadCodeEliminationPhase() {
    this(Optionality.Required);
  }

  /**
   * Creates a dead code elimination phase that will be run only if it is {@linkplain
   * Optionality#Required non-optional} or {@link Options#ReduceDCE} is false.
   */
  public DeadCodeEliminationPhase(Optionality optionality) {
    this.optional = optionality == Optionality.Optional;
  }

  private final boolean optional;

  @Override
  public void run(StructuredGraph graph) {
    if (optional && Options.ReduceDCE.getValue()) {
      return;
    }

    NodeFlood flood = graph.createNodeFlood();
    int totalNodeCount = graph.getNodeCount();
    flood.add(graph.start());
    iterateSuccessorsAndInputs(flood);
    int totalMarkedCount = flood.getTotalMarkedCount();
    if (totalNodeCount == totalMarkedCount) {
      // All nodes are live => nothing more to do.
      return;
    } else {
      // Some nodes are not marked alive and therefore dead => proceed.
      assert totalNodeCount > totalMarkedCount;
    }

    deleteNodes(flood, graph);
  }

  private static void iterateSuccessorsAndInputs(NodeFlood flood) {
    BiConsumer<Node, Node> consumer =
        (n, succOrInput) -> {
          assert succOrInput.isAlive() : "dead successor or input " + succOrInput + " in " + n;
          flood.add(succOrInput);
        };
    for (Node current : flood) {
      if (current instanceof AbstractEndNode) {
        AbstractEndNode end = (AbstractEndNode) current;
        flood.add(end.merge());
      } else {
        current.acceptSuccessors(consumer);
        current.acceptInputs(consumer);
      }
    }
  }

  private static void deleteNodes(NodeFlood flood, StructuredGraph graph) {
    BiConsumer<Node, Node> consumer =
        (n, input) -> {
          if (input.isAlive() && flood.isMarked(input)) {
            input.removeUsage(n);
          }
        };

    for (Node node : graph.getNodes()) {
      if (!flood.isMarked(node)) {
        node.markDeleted();
        node.acceptInputs(consumer);
        metricNodesRemoved.increment();
      }
    }
  }
}
public class GreedyInliningPolicy extends AbstractInliningPolicy {

  private static final DebugMetric metricInliningStoppedByMaxDesiredSize =
      Debug.metric("InliningStoppedByMaxDesiredSize");

  public GreedyInliningPolicy(Map<Invoke, Double> hints) {
    super(hints);
  }

  public boolean continueInlining(StructuredGraph currentGraph) {
    if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) {
      InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize");
      metricInliningStoppedByMaxDesiredSize.increment();
      return false;
    }
    return true;
  }

  @Override
  public boolean isWorthInlining(
      Replacements replacements,
      MethodInvocation invocation,
      int inliningDepth,
      boolean fullyProcessed) {

    final InlineInfo info = invocation.callee();
    final double probability = invocation.probability();
    final double relevance = invocation.relevance();

    if (InlineEverything.getValue()) {
      InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything");
      return true;
    }

    if (isIntrinsic(replacements, info)) {
      InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic");
      return true;
    }

    if (info.shouldInline()) {
      InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining");
      return true;
    }

    double inliningBonus = getInliningBonus(info);
    int nodes = info.determineNodeCount();
    int lowLevelGraphSize = previousLowLevelGraphSize(info);

    if (SmallCompiledLowLevelGraphSize.getValue() > 0
        && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) {
      InliningUtil.logNotInlinedMethod(
          info,
          inliningDepth,
          "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)",
          lowLevelGraphSize,
          relevance,
          probability,
          inliningBonus,
          nodes);
      return false;
    }

    if (nodes < TrivialInliningSize.getValue() * inliningBonus) {
      InliningUtil.logInlinedMethod(
          info,
          inliningDepth,
          fullyProcessed,
          "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)",
          relevance,
          probability,
          inliningBonus,
          nodes);
      return true;
    }

    /*
     * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> will
     * be compiled anyways and it is likely that we are the only caller... might be useful to
     * inline those methods but increases bootstrap time (maybe those methods are also getting
     * queued in the compilation queue concurrently)
     */
    double invokes = determineInvokeProbability(info);
    if (LimitInlinedInvokes.getValue() > 0
        && fullyProcessed
        && invokes > LimitInlinedInvokes.getValue() * inliningBonus) {
      InliningUtil.logNotInlinedMethod(
          info,
          inliningDepth,
          "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)",
          invokes,
          relevance,
          probability,
          inliningBonus,
          nodes);
      return false;
    }

    double maximumNodes =
        computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus));
    if (nodes <= maximumNodes) {
      InliningUtil.logInlinedMethod(
          info,
          inliningDepth,
          fullyProcessed,
          "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)",
          relevance,
          probability,
          inliningBonus,
          nodes,
          maximumNodes);
      return true;
    }

    InliningUtil.logNotInlinedMethod(
        info,
        inliningDepth,
        "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)",
        relevance,
        probability,
        inliningBonus,
        nodes,
        maximumNodes);
    return false;
  }
}