text: // Copyright 2018 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_WASM_VALUE_TYPE_H_
  #define V8_WASM_VALUE_TYPE_H_
  
  #include "src/base/bit-field.h"
  #include "src/codegen/machine-type.h"
  #include "src/wasm/wasm-constants.h"
  
  namespace v8 {
  namespace internal {
  
  template <typename T>
  class Signature;
  
  namespace wasm {
  
  // Type for holding simd values, defined in wasm-value.h.
  class Simd128;
  
  // Type lattice: Given a fixed struct type S, the following lattice
  // defines the subtyping relation among types:
  // For every two types connected by a line, the top type is a
  // (direct) subtype of the bottom type.
  //
  //                            AnyRef
  //                           /      \
  //                          /      EqRef
  //                         /       /   \
  //                  FuncRef  ExnRef  OptRef(S)
  //                         \    |   /        \
  // I32  I64  F32  F64        NullRef        Ref(S)
  //   \    \    \    \           |            /
  //    ---------------------- Bottom ---------
  // Format: kind, log2Size, code, machineType, shortName, typeName
  //
  // Some of these types are from proposals that are not standardized yet:
  // - "ref" types per https://github.com/WebAssembly/function-references
  // - "optref"/"eqref" per https://github.com/WebAssembly/gc
  //
  // TODO(7748): Extend this with struct and function subtyping.
  //             Keep up to date with funcref vs. anyref subtyping.
  #define FOREACH_VALUE_TYPE(V)                                                \
    V(Stmt, -1, Void, None, 'v', "<stmt>")                                     \
    V(I32, 2, I32, Int32, 'i', "i32")                                          \
    V(I64, 3, I64, Int64, 'l', "i64")                                          \
    V(F32, 2, F32, Float32, 'f', "f32")                                        \
    V(F64, 3, F64, Float64, 'd', "f64")                                        \
    V(S128, 4, S128, Simd128, 's', "s128")                                     \
    V(AnyRef, kSystemPointerSizeLog2, AnyRef, TaggedPointer, 'r', "anyref")    \
    V(FuncRef, kSystemPointerSizeLog2, FuncRef, TaggedPointer, 'a', "funcref") \
    V(NullRef, kSystemPointerSizeLog2, NullRef, TaggedPointer, 'n', "nullref") \
    V(ExnRef, kSystemPointerSizeLog2, ExnRef, TaggedPointer, 'e', "exn")       \
    V(Ref, kSystemPointerSizeLog2, Ref, TaggedPointer, '*', "ref")             \
    V(OptRef, kSystemPointerSizeLog2, OptRef, TaggedPointer, 'o', "optref")    \
    V(EqRef, kSystemPointerSizeLog2, EqRef, TaggedPointer, 'q', "eqref")       \
    V(Bottom, -1, Void, None, '*', "<bot>")
  
  class ValueType {
   public:
    enum Kind : uint8_t {
  #define DEF_ENUM(kind, ...) k##kind,
      FOREACH_VALUE_TYPE(DEF_ENUM)
  #undef DEF_ENUM
    };
  
    constexpr bool has_immediate() const {
      return kind() == kRef || kind() == kOptRef;
    }
  
    constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {}
    explicit constexpr ValueType(Kind kind)
        : bit_field_(KindField::encode(kind)) {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK(!has_immediate());
  #endif
    }
    constexpr ValueType(Kind kind, uint32_t ref_index)
        : bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK(has_immediate());
  #endif
    }
  
    constexpr Kind kind() const { return KindField::decode(bit_field_); }
    constexpr uint32_t ref_index() const {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK(has_immediate());
  #endif
      return RefIndexField::decode(bit_field_);
    }
  
    constexpr int element_size_log2() const {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK_NE(kStmt, kind());
      DCHECK_NE(kBottom, kind());
  #endif
  
      constexpr int kElementSizeLog2[] = {
  #define ELEM_SIZE_LOG2(kind, log2Size, ...) log2Size,
          FOREACH_VALUE_TYPE(ELEM_SIZE_LOG2)
  #undef ELEM_SIZE_LOG2
      };
  
      return kElementSizeLog2[kind()];
    }
  
    constexpr int element_size_bytes() const { return 1 << element_size_log2(); }
  
    constexpr bool operator==(ValueType other) const {
      return bit_field_ == other.bit_field_;
    }
    constexpr bool operator!=(ValueType other) const {
      return bit_field_ != other.bit_field_;
    }
  
    // TODO(7748): Extend this with struct and function subtyping.
    //             Keep up to date with funcref vs. anyref subtyping.
    constexpr bool IsSubTypeOf(ValueType other) const {
      return (*this == other) || (other.kind() == kAnyRef && IsReferenceType()) ||
             (kind() == kNullRef && other.kind() != kRef &&
              other.IsReferenceType()) ||
             (other.kind() == kEqRef &&
              (kind() == kExnRef || kind() == kOptRef || kind() == kRef)) ||
             (kind() == kRef && other.kind() == kOptRef &&
              ref_index() == other.ref_index());
    }
  
    constexpr bool IsReferenceType() const {
      return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef ||
             kind() == kExnRef || kind() == kRef || kind() == kOptRef ||
             kind() == kEqRef;
    }
  
    // TODO(7748): Extend this with struct and function subtyping.
    //             Keep up to date with funcref vs. anyref subtyping.
    static ValueType CommonSubType(ValueType a, ValueType b) {
      if (a == b) return a;
      // The only sub type of any value type is {bot}.
      if (!a.IsReferenceType() || !b.IsReferenceType()) {
        return ValueType(kBottom);
      }
      if (a.IsSubTypeOf(b)) return a;
      if (b.IsSubTypeOf(a)) return b;
      // {a} and {b} are not each other's subtype.
      // If one of them is not nullable, their greatest subtype is bottom,
      // otherwise null.
      if (a.kind() == kRef || b.kind() == kRef) return ValueType(kBottom);
      return ValueType(kNullRef);
    }
  
    constexpr ValueTypeCode value_type_code() const {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK_NE(kBottom, kind());
  #endif
  
      constexpr ValueTypeCode kValueTypeCode[] = {
  #define TYPE_CODE(kind, log2Size, code, ...) kLocal##code,
          FOREACH_VALUE_TYPE(TYPE_CODE)
  #undef TYPE_CODE
      };
  
      return kValueTypeCode[kind()];
    }
  
    constexpr MachineType machine_type() const {
  #if V8_HAS_CXX14_CONSTEXPR
      DCHECK_NE(kBottom, kind());
  #endif
  
      constexpr MachineType kMachineType[] = {
  #define MACH_TYPE(kind, log2Size, code, machineType, ...) \
    MachineType::machineType(),
          FOREACH_VALUE_TYPE(MACH_TYPE)
  #undef MACH_TYPE
      };
  
      return kMachineType[kind()];
    }
  
    constexpr MachineRepresentation machine_representation() const {
      return machine_type().representation();
    }
  
    static ValueType For(MachineType type) {
      switch (type.representation()) {
        case MachineRepresentation::kWord8:
        case MachineRepresentation::kWord16:
        case MachineRepresentation::kWord32:
          return ValueType(kI32);
        case MachineRepresentation::kWord64:
          return ValueType(kI64);
        case MachineRepresentation::kFloat32:
          return ValueType(kF32);
        case MachineRepresentation::kFloat64:
          return ValueType(kF64);
        case MachineRepresentation::kTaggedPointer:
          return ValueType(kAnyRef);
        case MachineRepresentation::kSimd128:
          return ValueType(kS128);
        default:
          UNREACHABLE();
      }
    }
  
    constexpr char short_name() const {
      constexpr char kShortName[] = {
  #define SHORT_NAME(kind, log2Size, code, machineType, shortName, ...) shortName,
          FOREACH_VALUE_TYPE(SHORT_NAME)
  #undef SHORT_NAME
      };
  
      return kShortName[kind()];
    }
  
    constexpr const char* type_name() const {
      constexpr const char* kTypeName[] = {
  #define TYPE_NAME(kind, log2Size, code, machineType, shortName, typeName, ...) \
    typeName,
          FOREACH_VALUE_TYPE(TYPE_NAME)
  #undef TYPE_NAME
      };
  
      return kTypeName[kind()];
    }
  
   private:
    using KindField = base::BitField<Kind, 0, 8>;
    using RefIndexField = base::BitField<uint32_t, 8, 24>;
  
    uint32_t bit_field_;
  };
  
  static_assert(sizeof(ValueType) <= kUInt32Size,
                "ValueType is small and can be passed by value");
  
  inline size_t hash_value(ValueType type) {
    return static_cast<size_t>(type.kind());
  }
  
  // Output operator, useful for DCHECKS and others.
  inline std::ostream& operator<<(std::ostream& oss, ValueType type) {
    return oss << type.type_name();
  }
  
  constexpr ValueType kWasmI32 = ValueType(ValueType::kI32);
  constexpr ValueType kWasmI64 = ValueType(ValueType::kI64);
  constexpr ValueType kWasmF32 = ValueType(ValueType::kF32);
  constexpr ValueType kWasmF64 = ValueType(ValueType::kF64);
  constexpr ValueType kWasmAnyRef = ValueType(ValueType::kAnyRef);
  constexpr ValueType kWasmEqRef = ValueType(ValueType::kEqRef);
  constexpr ValueType kWasmExnRef = ValueType(ValueType::kExnRef);
  constexpr ValueType kWasmFuncRef = ValueType(ValueType::kFuncRef);
  constexpr ValueType kWasmNullRef = ValueType(ValueType::kNullRef);
  constexpr ValueType kWasmS128 = ValueType(ValueType::kS128);
  constexpr ValueType kWasmStmt = ValueType(ValueType::kStmt);
  constexpr ValueType kWasmBottom = ValueType(ValueType::kBottom);
  
  #define FOREACH_WASMVALUE_CTYPES(V) \
    V(kI32, int32_t)                  \
    V(kI64, int64_t)                  \
    V(kF32, float)                    \
    V(kF64, double)                   \
    V(kS128, Simd128)
  
  using FunctionSig = Signature<ValueType>;
  
  #define FOREACH_LOAD_TYPE(V) \
    V(I32, , Int32)            \
    V(I32, 8S, Int8)           \
    V(I32, 8U, Uint8)          \
    V(I32, 16S, Int16)         \
    V(I32, 16U, Uint16)        \
    V(I64, , Int64)            \
    V(I64, 8S, Int8)           \
    V(I64, 8U, Uint8)          \
    V(I64, 16S, Int16)         \
    V(I64, 16U, Uint16)        \
    V(I64, 32S, Int32)         \
    V(I64, 32U, Uint32)        \
    V(F32, , Float32)          \
    V(F64, , Float64)          \
    V(S128, , Simd128)
  
  class LoadType {
   public:
    enum LoadTypeValue : uint8_t {
  #define DEF_ENUM(type, suffix, ...) k##type##Load##suffix,
      FOREACH_LOAD_TYPE(DEF_ENUM)
  #undef DEF_ENUM
    };
  
    // Allow implicit conversion of the enum value to this wrapper.
    constexpr LoadType(LoadTypeValue val)  // NOLINT(runtime/explicit)
        : val_(val) {}
  
    constexpr LoadTypeValue value() const { return val_; }
    constexpr unsigned size_log_2() const { return kLoadSizeLog2[val_]; }
    constexpr unsigned size() const { return 1 << size_log_2(); }
    constexpr ValueType value_type() const { return kValueType[val_]; }
    constexpr MachineType mem_type() const { return kMemType[val_]; }
  
    static LoadType ForValueType(ValueType type) {
      switch (type.kind()) {
        case ValueType::kI32:
          return kI32Load;
        case ValueType::kI64:
          return kI64Load;
        case ValueType::kF32:
          return kF32Load;
        case ValueType::kF64:
          return kF64Load;
        case ValueType::kS128:
          return kS128Load;
        default:
          UNREACHABLE();
      }
    }
  
   private:
    const LoadTypeValue val_;
  
    static constexpr uint8_t kLoadSizeLog2[] = {
    // MSVC wants a static_cast here.
  #define LOAD_SIZE(_, __, memtype) \
    static_cast<uint8_t>(           \
        ElementSizeLog2Of(MachineType::memtype().representation())),
        FOREACH_LOAD_TYPE(LOAD_SIZE)
  #undef LOAD_SIZE
    };
  
    static constexpr ValueType kValueType[] = {
  #define VALUE_TYPE(type, ...) ValueType(ValueType::k##type),
        FOREACH_LOAD_TYPE(VALUE_TYPE)
  #undef VALUE_TYPE
    };
  
    static constexpr MachineType kMemType[] = {
  #define MEMTYPE(_, __, memtype) MachineType::memtype(),
        FOREACH_LOAD_TYPE(MEMTYPE)
  #undef MEMTYPE
    };
  };
  
  #define FOREACH_STORE_TYPE(V) \
    V(I32, , Word32)            \
    V(I32, 8, Word8)            \
    V(I32, 16, Word16)          \
    V(I64, , Word64)            \
    V(I64, 8, Word8)            \
    V(I64, 16, Word16)          \
    V(I64, 32, Word32)          \
    V(F32, , Float32)           \
    V(F64, , Float64)           \
    V(S128, , Simd128)
  
  class StoreType {
   public:
    enum StoreTypeValue : uint8_t {
  #define DEF_ENUM(type, suffix, ...) k##type##Store##suffix,
      FOREACH_STORE_TYPE(DEF_ENUM)
  #undef DEF_ENUM
    };
  
    // Allow implicit convertion of the enum value to this wrapper.
    constexpr StoreType(StoreTypeValue val)  // NOLINT(runtime/explicit)
        : val_(val) {}
  
    constexpr StoreTypeValue value() const { return val_; }
    constexpr unsigned size_log_2() const { return kStoreSizeLog2[val_]; }
    constexpr unsigned size() const { return 1 << size_log_2(); }
    constexpr ValueType value_type() const { return kValueType[val_]; }
    constexpr MachineRepresentation mem_rep() const { return kMemRep[val_]; }
  
    static StoreType ForValueType(ValueType type) {
      switch (type.kind()) {
        case ValueType::kI32:
          return kI32Store;
        case ValueType::kI64:
          return kI64Store;
        case ValueType::kF32:
          return kF32Store;
        case ValueType::kF64:
          return kF64Store;
        case ValueType::kS128:
          return kS128Store;
        default:
          UNREACHABLE();
      }
    }
  
   private:
    const StoreTypeValue val_;
  
    static constexpr uint8_t kStoreSizeLog2[] = {
    // MSVC wants a static_cast here.
  #define STORE_SIZE(_, __, memrep) \
    static_cast<uint8_t>(ElementSizeLog2Of(MachineRepresentation::k##memrep)),
        FOREACH_STORE_TYPE(STORE_SIZE)
  #undef STORE_SIZE
    };
  
    static constexpr ValueType kValueType[] = {
  #define VALUE_TYPE(type, ...) ValueType(ValueType::k##type),
        FOREACH_STORE_TYPE(VALUE_TYPE)
  #undef VALUE_TYPE
    };
  
    static constexpr MachineRepresentation kMemRep[] = {
  #define MEMREP(_, __, memrep) MachineRepresentation::k##memrep,
        FOREACH_STORE_TYPE(MEMREP)
  #undef MEMREP
    };
  };
  
  }  // namespace wasm
  }  // namespace internal
  }  // namespace v8
  
  #endif  // V8_WASM_VALUE_TYPE_H_
  