// Copyright 2017 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_INTERPRETER_BYTECODE_NODE_H_
#define V8_INTERPRETER_BYTECODE_NODE_H_

#include <algorithm>

#include "src/globals.h"
#include "src/interpreter/bytecode-source-info.h"
#include "src/interpreter/bytecodes.h"

namespace v8 {
namespace internal {
namespace interpreter {

// A container for a generated bytecode, it's operands, and source information.
class V8_EXPORT_PRIVATE BytecodeNode final {
 public:
  INLINE(BytecodeNode(Bytecode bytecode,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(0),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
  }

  INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(1),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
    SetOperand(0, operand0);
  }

  INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(2),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
    SetOperand(0, operand0);
    SetOperand(1, operand1);
  }

  INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
                      uint32_t operand2,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(3),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
    SetOperand(0, operand0);
    SetOperand(1, operand1);
    SetOperand(2, operand2);
  }

  INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
                      uint32_t operand2, uint32_t operand3,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(4),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
    SetOperand(0, operand0);
    SetOperand(1, operand1);
    SetOperand(2, operand2);
    SetOperand(3, operand3);
  }

  INLINE(BytecodeNode(Bytecode bytecode, uint32_t operand0, uint32_t operand1,
                      uint32_t operand2, uint32_t operand3, uint32_t operand4,
                      BytecodeSourceInfo source_info = BytecodeSourceInfo()))
      : bytecode_(bytecode),
        operand_count_(5),
        operand_scale_(OperandScale::kSingle),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count());
    SetOperand(0, operand0);
    SetOperand(1, operand1);
    SetOperand(2, operand2);
    SetOperand(3, operand3);
    SetOperand(4, operand4);
  }

#define DEFINE_BYTECODE_NODE_CREATOR(Name, ...)                              \
  template <typename... Operands>                                            \
  INLINE(static BytecodeNode Name(BytecodeSourceInfo source_info,            \
                                  Operands... operands)) {                   \
    return Create<Bytecode::k##Name, __VA_ARGS__>(source_info, operands...); \
  }
  BYTECODE_LIST(DEFINE_BYTECODE_NODE_CREATOR)
#undef DEFINE_BYTECODE_NODE_CREATOR

  // Print to stream |os|.
  void Print(std::ostream& os) const;

  Bytecode bytecode() const { return bytecode_; }

  uint32_t operand(int i) const {
    DCHECK_LT(i, operand_count());
    return operands_[i];
  }
  const uint32_t* operands() const { return operands_; }

  void update_operand0(uint32_t operand0) { SetOperand(0, operand0); }

  int operand_count() const { return operand_count_; }
  OperandScale operand_scale() const { return operand_scale_; }

  const BytecodeSourceInfo& source_info() const { return source_info_; }
  void set_source_info(BytecodeSourceInfo source_info) {
    source_info_ = source_info;
  }

  bool operator==(const BytecodeNode& other) const;
  bool operator!=(const BytecodeNode& other) const { return !(*this == other); }

 private:
  template <Bytecode bytecode, AccumulatorUse accumulator_use,
            OperandType... operand_types>
  friend class BytecodeNodeBuilder;

  INLINE(BytecodeNode(Bytecode bytecode, int operand_count,
                      OperandScale operand_scale,
                      BytecodeSourceInfo source_info, uint32_t operand0 = 0,
                      uint32_t operand1 = 0, uint32_t operand2 = 0,
                      uint32_t operand3 = 0, uint32_t operand4 = 0))
      : bytecode_(bytecode),
        operand_count_(operand_count),
        operand_scale_(operand_scale),
        source_info_(source_info) {
    DCHECK_EQ(Bytecodes::NumberOfOperands(bytecode), operand_count);
    operands_[0] = operand0;
    operands_[1] = operand1;
    operands_[2] = operand2;
    operands_[3] = operand3;
    operands_[4] = operand4;
  }

  template <Bytecode bytecode, AccumulatorUse accum_use>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info)) {
    return BytecodeNode(bytecode, 0, OperandScale::kSingle, source_info);
  }

  template <Bytecode bytecode, AccumulatorUse accum_use,
            OperandType operand0_type>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info,
                                    uint32_t operand0)) {
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
    OperandScale scale = OperandScale::kSingle;
    scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
    return BytecodeNode(bytecode, 1, scale, source_info, operand0);
  }

  template <Bytecode bytecode, AccumulatorUse accum_use,
            OperandType operand0_type, OperandType operand1_type>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info,
                                    uint32_t operand0, uint32_t operand1)) {
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
    OperandScale scale = OperandScale::kSingle;
    scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
    scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
    return BytecodeNode(bytecode, 2, scale, source_info, operand0, operand1);
  }

  template <Bytecode bytecode, AccumulatorUse accum_use,
            OperandType operand0_type, OperandType operand1_type,
            OperandType operand2_type>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info,
                                    uint32_t operand0, uint32_t operand1,
                                    uint32_t operand2)) {
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
    OperandScale scale = OperandScale::kSingle;
    scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
    scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
    scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
    return BytecodeNode(bytecode, 3, scale, source_info, operand0, operand1,
                        operand2);
  }

  template <Bytecode bytecode, AccumulatorUse accum_use,
            OperandType operand0_type, OperandType operand1_type,
            OperandType operand2_type, OperandType operand3_type>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info,
                                    uint32_t operand0, uint32_t operand1,
                                    uint32_t operand2, uint32_t operand3)) {
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 3), operand3_type);
    OperandScale scale = OperandScale::kSingle;
    scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
    scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
    scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
    scale = std::max(scale, ScaleForOperand<operand3_type>(operand3));
    return BytecodeNode(bytecode, 4, scale, source_info, operand0, operand1,
                        operand2, operand3);
  }

  template <Bytecode bytecode, AccumulatorUse accum_use,
            OperandType operand0_type, OperandType operand1_type,
            OperandType operand2_type, OperandType operand3_type,
            OperandType operand4_type>
  INLINE(static BytecodeNode Create(BytecodeSourceInfo source_info,
                                    uint32_t operand0, uint32_t operand1,
                                    uint32_t operand2, uint32_t operand3,
                                    uint32_t operand4)) {
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 0), operand0_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 1), operand1_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 2), operand2_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 3), operand3_type);
    DCHECK_EQ(Bytecodes::GetOperandType(bytecode, 4), operand4_type);
    OperandScale scale = OperandScale::kSingle;
    scale = std::max(scale, ScaleForOperand<operand0_type>(operand0));
    scale = std::max(scale, ScaleForOperand<operand1_type>(operand1));
    scale = std::max(scale, ScaleForOperand<operand2_type>(operand2));
    scale = std::max(scale, ScaleForOperand<operand3_type>(operand3));
    scale = std::max(scale, ScaleForOperand<operand4_type>(operand4));
    return BytecodeNode(bytecode, 5, scale, source_info, operand0, operand1,
                        operand2, operand3, operand4);
  }

  template <OperandType operand_type>
  INLINE(static OperandScale ScaleForOperand(uint32_t operand)) {
    if (BytecodeOperands::IsScalableUnsignedByte(operand_type)) {
      return Bytecodes::ScaleForUnsignedOperand(operand);
    } else if (BytecodeOperands::IsScalableSignedByte(operand_type)) {
      return Bytecodes::ScaleForSignedOperand(operand);
    } else {
      return OperandScale::kSingle;
    }
  }

  INLINE(void UpdateScaleForOperand(int operand_index, uint32_t operand)) {
    if (Bytecodes::OperandIsScalableSignedByte(bytecode(), operand_index)) {
      operand_scale_ =
          std::max(operand_scale_, Bytecodes::ScaleForSignedOperand(operand));
    } else if (Bytecodes::OperandIsScalableUnsignedByte(bytecode(),
                                                        operand_index)) {
      operand_scale_ =
          std::max(operand_scale_, Bytecodes::ScaleForUnsignedOperand(operand));
    }
  }

  INLINE(void SetOperand(int operand_index, uint32_t operand)) {
    operands_[operand_index] = operand;
    UpdateScaleForOperand(operand_index, operand);
  }

  Bytecode bytecode_;
  uint32_t operands_[Bytecodes::kMaxOperands];
  int operand_count_;
  OperandScale operand_scale_;
  BytecodeSourceInfo source_info_;
};

V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
                                           const BytecodeNode& node);

}  // namespace interpreter
}  // namespace internal
}  // namespace v8

#endif  // V8_INTERPRETER_BYTECODE_NODE_H_
