| // 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 |
| // / | \ |
| // 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 eqref, 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)) { |
| DCHECK(!has_immediate()); |
| } |
| constexpr ValueType(Kind kind, uint32_t ref_index) |
| : bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) { |
| DCHECK(has_immediate()); |
| } |
| |
| 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 eqref, struct and function subtyping. |
| // Keep up to date with funcref vs. anyref subtyping. |
| bool IsSubTypeOf(ValueType other) const { |
| return (*this == other) || |
| (kind() == kNullRef && |
| (other.kind() == kAnyRef || other.kind() == kFuncRef || |
| other.kind() == kExnRef || other.kind() == kOptRef)) || |
| (other.kind() == kAnyRef && |
| (kind() == kFuncRef || kind() == kExnRef || kind() == kOptRef || |
| kind() == kRef)) || |
| (kind() == kRef && other.kind() == kOptRef && |
| ref_index() == other.ref_index()); |
| } |
| |
| bool IsReferenceType() const { |
| return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef || |
| kind() == kExnRef || kind() == kRef || kind() == kOptRef || |
| kind() == kEqRef; |
| } |
| |
| // TODO(7748): Extend this with eqref, 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. |
| return (a.kind() == kRef || b.kind() == kRef) ? ValueType(kBottom) |
| : ValueType(kNullRef); |
| } |
| |
| ValueTypeCode value_type_code() const { |
| DCHECK_NE(kBottom, kind()); |
| |
| constexpr ValueTypeCode kValueTypeCode[] = { |
| #define TYPE_CODE(kind, log2Size, code, ...) kLocal##code, |
| FOREACH_VALUE_TYPE(TYPE_CODE) |
| #undef TYPE_CODE |
| }; |
| |
| return kValueTypeCode[kind()]; |
| } |
| |
| MachineType machine_type() const { |
| DCHECK_NE(kBottom, kind()); |
| |
| constexpr MachineType kMachineType[] = { |
| #define MACH_TYPE(kind, log2Size, code, machineType, ...) \ |
| MachineType::machineType(), |
| FOREACH_VALUE_TYPE(MACH_TYPE) |
| #undef MACH_TYPE |
| }; |
| |
| return kMachineType[kind()]; |
| } |
| |
| MachineRepresentation machine_representation() { |
| 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_ |