// Copyright 2014 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_MACHINE_TYPE_H_
#define V8_MACHINE_TYPE_H_

#include <iosfwd>

#include "src/base/bits.h"
#include "src/globals.h"
#include "src/signature.h"
#include "src/zone.h"

namespace v8 {
namespace internal {

enum class MachineRepresentation : uint8_t {
  kNone,
  kBit,
  kWord8,
  kWord16,
  kWord32,
  kWord64,
  kFloat32,
  kFloat64,
  kTagged
};

enum class MachineSemantic : uint8_t {
  kNone,
  kBool,
  kInt32,
  kUint32,
  kInt64,
  kUint64,
  kNumber,
  kAny
};

class MachineType {
 public:
  MachineType()
      : representation_(MachineRepresentation::kNone),
        semantic_(MachineSemantic::kNone) {}
  MachineType(MachineRepresentation representation, MachineSemantic semantic)
      : representation_(representation), semantic_(semantic) {}

  bool operator==(MachineType other) const {
    return representation() == other.representation() &&
           semantic() == other.semantic();
  }

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


  MachineRepresentation representation() const { return representation_; }
  MachineSemantic semantic() const { return semantic_; }

  bool IsSigned() {
    return semantic() == MachineSemantic::kInt32 ||
           semantic() == MachineSemantic::kInt64;
  }
  bool IsUnsigned() {
    return semantic() == MachineSemantic::kUint32 ||
           semantic() == MachineSemantic::kUint64;
  }

  static MachineRepresentation PointerRepresentation() {
    return (kPointerSize == 4) ? MachineRepresentation::kWord32
                               : MachineRepresentation::kWord64;
  }
  static MachineType Pointer() {
    return MachineType(PointerRepresentation(), MachineSemantic::kNone);
  }
  static MachineType IntPtr() {
    return (kPointerSize == 4) ? Int32() : Int64();
  }
  static MachineType Float32() {
    return MachineType(MachineRepresentation::kFloat32,
                       MachineSemantic::kNumber);
  }
  static MachineType Float64() {
    return MachineType(MachineRepresentation::kFloat64,
                       MachineSemantic::kNumber);
  }
  static MachineType Int8() {
    return MachineType(MachineRepresentation::kWord8, MachineSemantic::kInt32);
  }
  static MachineType Uint8() {
    return MachineType(MachineRepresentation::kWord8, MachineSemantic::kUint32);
  }
  static MachineType Int16() {
    return MachineType(MachineRepresentation::kWord16, MachineSemantic::kInt32);
  }
  static MachineType Uint16() {
    return MachineType(MachineRepresentation::kWord16,
                       MachineSemantic::kUint32);
  }
  static MachineType Int32() {
    return MachineType(MachineRepresentation::kWord32, MachineSemantic::kInt32);
  }
  static MachineType Uint32() {
    return MachineType(MachineRepresentation::kWord32,
                       MachineSemantic::kUint32);
  }
  static MachineType Int64() {
    return MachineType(MachineRepresentation::kWord64, MachineSemantic::kInt64);
  }
  static MachineType Uint64() {
    return MachineType(MachineRepresentation::kWord64,
                       MachineSemantic::kUint64);
  }
  static MachineType AnyTagged() {
    return MachineType(MachineRepresentation::kTagged, MachineSemantic::kAny);
  }
  static MachineType Bool() {
    return MachineType(MachineRepresentation::kBit, MachineSemantic::kBool);
  }
  static MachineType TaggedBool() {
    return MachineType(MachineRepresentation::kTagged, MachineSemantic::kBool);
  }
  static MachineType None() {
    return MachineType(MachineRepresentation::kNone, MachineSemantic::kNone);
  }

  // These naked representations should eventually go away.
  static MachineType RepWord8() {
    return MachineType(MachineRepresentation::kWord8, MachineSemantic::kNone);
  }
  static MachineType RepWord16() {
    return MachineType(MachineRepresentation::kWord16, MachineSemantic::kNone);
  }
  static MachineType RepWord32() {
    return MachineType(MachineRepresentation::kWord32, MachineSemantic::kNone);
  }
  static MachineType RepWord64() {
    return MachineType(MachineRepresentation::kWord64, MachineSemantic::kNone);
  }
  static MachineType RepFloat32() {
    return MachineType(MachineRepresentation::kFloat32, MachineSemantic::kNone);
  }
  static MachineType RepFloat64() {
    return MachineType(MachineRepresentation::kFloat64, MachineSemantic::kNone);
  }
  static MachineType RepTagged() {
    return MachineType(MachineRepresentation::kTagged, MachineSemantic::kNone);
  }
  static MachineType RepBit() {
    return MachineType(MachineRepresentation::kBit, MachineSemantic::kNone);
  }

 private:
  MachineRepresentation representation_;
  MachineSemantic semantic_;
};

V8_INLINE size_t hash_value(MachineRepresentation rep) {
  return static_cast<size_t>(rep);
}

V8_INLINE size_t hash_value(MachineType type) {
  return static_cast<size_t>(type.representation()) +
         static_cast<size_t>(type.semantic()) * 16;
}

std::ostream& operator<<(std::ostream& os, MachineRepresentation rep);
std::ostream& operator<<(std::ostream& os, MachineSemantic type);
std::ostream& operator<<(std::ostream& os, MachineType type);

inline bool IsFloatingPoint(MachineRepresentation rep) {
  return rep == MachineRepresentation::kFloat32 ||
         rep == MachineRepresentation::kFloat64;
}

// Gets the log2 of the element size in bytes of the machine type.
inline int ElementSizeLog2Of(MachineRepresentation rep) {
  switch (rep) {
    case MachineRepresentation::kBit:
    case MachineRepresentation::kWord8:
      return 0;
    case MachineRepresentation::kWord16:
      return 1;
    case MachineRepresentation::kWord32:
    case MachineRepresentation::kFloat32:
      return 2;
    case MachineRepresentation::kWord64:
    case MachineRepresentation::kFloat64:
      return 3;
    case MachineRepresentation::kTagged:
      return kPointerSizeLog2;
    default:
      break;
  }
  UNREACHABLE();
  return -1;
}

typedef Signature<MachineType> MachineSignature;

}  // namespace internal
}  // namespace v8

#endif  // V8_MACHINE_TYPE_H_
