// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_COMPILER_FRONTEND_FLOW_GRAPH_BUILDER_H_
#define RUNTIME_VM_COMPILER_FRONTEND_FLOW_GRAPH_BUILDER_H_

#if defined(DART_PRECOMPILED_RUNTIME)
#error "AOT runtime should not use compiler sources (including header files)"
#endif  // defined(DART_PRECOMPILED_RUNTIME)

#include "platform/assert.h"
#include "platform/globals.h"
#include "vm/allocation.h"
#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"
#include "vm/growable_array.h"
#include "vm/raw_object.h"

namespace dart {

// A class to collect the exits from an inlined function during graph
// construction so they can be plugged into the caller's flow graph.
class InlineExitCollector : public ZoneObject {
 public:
  InlineExitCollector(FlowGraph* caller_graph, Definition* call)
      : caller_graph_(caller_graph), call_(call), exits_(4) {}

  void AddExit(DartReturnInstr* exit);

  void Union(const InlineExitCollector* other);

  // Before replacing a call with a graph, the outer environment needs to be
  // attached to each instruction in the callee graph and the caller graph
  // needs to have its block and instruction ID state updated.
  // Additionally we need to remove all unreachable exits from the list of
  // collected exits.
  void PrepareGraphs(FlowGraph* callee_graph);

  // Inline a graph at a call site.
  //
  // Assumes the callee is in SSA with a correct dominator tree and use
  // lists.
  //
  // After inlining the caller graph will have correctly adjusted the use
  // lists.  The block orders will need to be recomputed.
  void ReplaceCall(BlockEntryInstr* callee_entry);

 private:
  struct Data {
    BlockEntryInstr* exit_block;
    DartReturnInstr* exit_return;
  };

  BlockEntryInstr* ExitBlockAt(intptr_t i) const {
    ASSERT(exits_[i].exit_block != nullptr);
    return exits_[i].exit_block;
  }

  Instruction* LastInstructionAt(intptr_t i) const {
    return ReturnAt(i)->previous();
  }

  Value* ValueAt(intptr_t i) const { return ReturnAt(i)->value(); }

  DartReturnInstr* ReturnAt(intptr_t i) const { return exits_[i].exit_return; }

  static int LowestBlockIdFirst(const Data* a, const Data* b);
  void SortExits();
  void RemoveUnreachableExits(FlowGraph* callee_graph);

  Definition* JoinReturns(BlockEntryInstr** exit_block,
                          Instruction** last_instruction,
                          intptr_t try_index);

  Zone* zone() const { return caller_graph_->zone(); }

  FlowGraph* caller_graph_;
  Definition* call_;
  GrowableArray<Data> exits_;
};

bool SimpleInstanceOfType(const AbstractType& type);
uword FindDoubleConstant(double value);

}  // namespace dart

#endif  // RUNTIME_VM_COMPILER_FRONTEND_FLOW_GRAPH_BUILDER_H_
