// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_TORQUE_TORQUE_CODE_GENERATOR_H_
#define V8_TORQUE_TORQUE_CODE_GENERATOR_H_

#include <iostream>

#include "src/torque/cfg.h"
#include "src/torque/declarable.h"

namespace v8 {
namespace internal {
namespace torque {

class TorqueCodeGenerator {
 public:
  TorqueCodeGenerator(const ControlFlowGraph& cfg, std::ostream& out)
      : cfg_(cfg),
        out_(&out),
        out_decls_(&out),
        previous_position_(SourcePosition::Invalid()) {}

 protected:
  const ControlFlowGraph& cfg_;
  std::ostream* out_;
  std::ostream* out_decls_;
  size_t fresh_id_ = 0;
  SourcePosition previous_position_;
  std::map<DefinitionLocation, std::string> location_map_;

  std::string DefinitionToVariable(const DefinitionLocation& location) {
    if (location.IsPhi()) {
      std::stringstream stream;
      stream << "phi_bb" << location.GetPhiBlock()->id() << "_"
             << location.GetPhiIndex();
      return stream.str();
    } else if (location.IsParameter()) {
      auto it = location_map_.find(location);
      DCHECK_NE(it, location_map_.end());
      return it->second;
    } else {
      DCHECK(location.IsInstruction());
      auto it = location_map_.find(location);
      if (it == location_map_.end()) {
        it = location_map_.insert(std::make_pair(location, FreshNodeName()))
                 .first;
      }
      return it->second;
    }
  }

  void SetDefinitionVariable(const DefinitionLocation& definition,
                             const std::string& str) {
    DCHECK_EQ(location_map_.find(definition), location_map_.end());
    location_map_.insert(std::make_pair(definition, str));
  }

  std::ostream& out() { return *out_; }
  std::ostream& decls() { return *out_decls_; }

  static bool IsEmptyInstruction(const Instruction& instruction);
  virtual void EmitSourcePosition(SourcePosition pos,
                                  bool always_emit = false) = 0;

  std::string FreshNodeName() { return "tmp" + std::to_string(fresh_id_++); }
  std::string FreshCatchName() { return "catch" + std::to_string(fresh_id_++); }
  std::string FreshLabelName() { return "label" + std::to_string(fresh_id_++); }
  std::string BlockName(const Block* block) {
    return "block" + std::to_string(block->id());
  }

  void EmitInstruction(const Instruction& instruction,
                       Stack<std::string>* stack);

  template <typename T>
  void EmitIRAnnotation(const T& instruction, Stack<std::string>* stack) {
    out() << "    // " << instruction
          << ", starting stack size: " << stack->Size() << "\n";
  }

#define EMIT_INSTRUCTION_DECLARATION(T) \
  void EmitInstruction(const T& instruction, Stack<std::string>* stack);
  TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(EMIT_INSTRUCTION_DECLARATION)
#undef EMIT_INSTRUCTION_DECLARATION

#define EMIT_INSTRUCTION_DECLARATION(T)              \
  virtual void EmitInstruction(const T& instruction, \
                               Stack<std::string>* stack) = 0;
  TORQUE_BACKEND_DEPENDENT_INSTRUCTION_LIST(EMIT_INSTRUCTION_DECLARATION)
#undef EMIT_INSTRUCTION_DECLARATION
};

}  // namespace torque
}  // namespace internal
}  // namespace v8

#endif  // V8_TORQUE_TORQUE_CODE_GENERATOR_H_