// Copyright (c) 2019, 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_BACKEND_FLOW_GRAPH_CHECKER_H_
#define RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_CHECKER_H_

#include "platform/globals.h"

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

#if defined(DEBUG)

#include "vm/compiler/backend/flow_graph.h"
#include "vm/compiler/backend/il.h"

namespace dart {

// Class responsible for performing sanity checks on the flow graph.
// The intended use is running the checks after each compiler pass
// in debug mode in order to detect graph inconsistencies as soon
// as possible. This way, culprit passes are more easily identified.
//
// All important assumptions on the flow graph structure that can be
// verified in reasonable time should be made explicit in this pass
// so that we no longer rely on asserts that are dispersed throughout
// the passes or, worse, unwritten assumptions once agreed upon but
// so easily forgotten. Since the graph checker runs only in debug
// mode, it is acceptable to perform slightly elaborate tests.
class FlowGraphChecker : public FlowGraphVisitor {
 public:
  // Constructs graph checker. The checker uses some custom-made
  // visitation to perform additional checks, and uses the
  // FlowGraphVisitor structure for anything else.
  explicit FlowGraphChecker(FlowGraph* flow_graph)
      : FlowGraphVisitor(flow_graph->preorder()),
        flow_graph_(flow_graph),
        inline_id_to_function_(
            flow_graph->inlining_info().inline_id_to_function),
        script_(Script::Handle(flow_graph_->zone())),
        current_block_(nullptr) {}

  // Performs a sanity check on the flow graph.
  void Check(const char* pass_name);

 private:
  // Custom-made visitors.
  void VisitBlocks() override;
  void VisitInstructions(BlockEntryInstr* block);
  void VisitInstruction(Instruction* instruction);
  void VisitDefinition(Definition* def);
  void VisitUseDef(Instruction* instruction,
                   Value* use,
                   intptr_t index,
                   bool is_env);
  void VisitDefUse(Definition* def, Value* use, Value* prev, bool is_env);

  // Instruction visitors.
  void VisitConstant(ConstantInstr* constant) override;
  void VisitPhi(PhiInstr* phi) override;
  void VisitGoto(GotoInstr* jmp) override;
  void VisitIndirectGoto(IndirectGotoInstr* jmp) override;
  void VisitBranch(BranchInstr* branch) override;
  void VisitRedefinition(RedefinitionInstr* def) override;
  void AssertArgumentsInEnv(Definition* call);
  void VisitClosureCall(ClosureCallInstr* call) override;
  void VisitStaticCall(StaticCallInstr* call) override;
  void VisitInstanceCall(InstanceCallInstr* call) override;
  void VisitPolymorphicInstanceCall(
      PolymorphicInstanceCallInstr* call) override;

  FlowGraph* const flow_graph_;
  const GrowableArray<const Function*>& inline_id_to_function_;
  Script& script_;
  BlockEntryInstr* current_block_;
};

}  // namespace dart

#endif  // defined(DEBUG)

#endif  // RUNTIME_VM_COMPILER_BACKEND_FLOW_GRAPH_CHECKER_H_
