// 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/codegen/machine-type.h" #include "src/wasm/wasm-constants.h" namespace v8 { namespace internal { template class Signature; namespace wasm { // Type lattice: For any two types connected by a line, the type at the bottom // is a subtype of the other type. // // AnyRef // / \ // FuncRef ExnRef // \ / // I32 I64 F32 F64 NullRef // \ \ \ \ / // ------------ Bottom enum ValueType : uint8_t { kWasmStmt, kWasmI32, kWasmI64, kWasmF32, kWasmF64, kWasmS128, kWasmAnyRef, kWasmFuncRef, kWasmNullRef, kWasmExnRef, kWasmBottom, }; using FunctionSig = Signature; inline size_t hash_value(ValueType type) { return static_cast(type); } // TODO(clemensb): Compute memtype and size from ValueType once we have c++14 // constexpr support. #define FOREACH_LOAD_TYPE(V) \ V(I32, , Int32, 2) \ V(I32, 8S, Int8, 0) \ V(I32, 8U, Uint8, 0) \ V(I32, 16S, Int16, 1) \ V(I32, 16U, Uint16, 1) \ V(I64, , Int64, 3) \ V(I64, 8S, Int8, 0) \ V(I64, 8U, Uint8, 0) \ V(I64, 16S, Int16, 1) \ V(I64, 16U, Uint16, 1) \ V(I64, 32S, Int32, 2) \ V(I64, 32U, Uint32, 2) \ V(F32, , Float32, 2) \ V(F64, , Float64, 3) \ V(S128, , Simd128, 4) 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 convertion 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) { case kWasmI32: return kI32Load; case kWasmI64: return kI64Load; case kWasmF32: return kF32Load; case kWasmF64: return kF64Load; default: UNREACHABLE(); } } private: const LoadTypeValue val_; static constexpr uint8_t kLoadSizeLog2[] = { #define LOAD_SIZE(_, __, ___, size) size, FOREACH_LOAD_TYPE(LOAD_SIZE) #undef LOAD_SIZE }; static constexpr ValueType kValueType[] = { #define VALUE_TYPE(type, ...) kWasm##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, 2) \ V(I32, 8, Word8, 0) \ V(I32, 16, Word16, 1) \ V(I64, , Word64, 3) \ V(I64, 8, Word8, 0) \ V(I64, 16, Word16, 1) \ V(I64, 32, Word32, 2) \ V(F32, , Float32, 2) \ V(F64, , Float64, 3) \ V(S128, , Simd128, 4) 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) { case kWasmI32: return kI32Store; case kWasmI64: return kI64Store; case kWasmF32: return kF32Store; case kWasmF64: return kF64Store; default: UNREACHABLE(); } } private: const StoreTypeValue val_; static constexpr uint8_t kStoreSizeLog2[] = { #define STORE_SIZE(_, __, ___, size) size, FOREACH_STORE_TYPE(STORE_SIZE) #undef STORE_SIZE }; static constexpr ValueType kValueType[] = { #define VALUE_TYPE(type, ...) kWasm##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 }; }; // A collection of ValueType-related static methods. class V8_EXPORT_PRIVATE ValueTypes { public: static inline bool IsSubType(ValueType actual, ValueType expected) { return (expected == actual) || (expected == kWasmAnyRef && actual == kWasmNullRef) || (expected == kWasmAnyRef && actual == kWasmFuncRef) || (expected == kWasmAnyRef && actual == kWasmExnRef) || (expected == kWasmFuncRef && actual == kWasmNullRef) || // TODO(mstarzinger): For now we treat "nullref" as a sub-type of // "exnref", which is correct but might change. See here: // https://github.com/WebAssembly/exception-handling/issues/55 (expected == kWasmExnRef && actual == kWasmNullRef); } static inline bool IsReferenceType(ValueType type) { return type == kWasmAnyRef || type == kWasmFuncRef || type == kWasmExnRef; } static inline ValueType CommonSubType(ValueType a, ValueType b) { if (a == b) return a; // The only sub type of any value type is {bot}. if (!IsReferenceType(a) || !IsReferenceType(b)) return kWasmBottom; if (IsSubType(a, b)) return a; if (IsSubType(b, a)) return b; // {a} and {b} are not each other's subtype. The biggest sub-type of all // reference types is {kWasmNullRef}. return kWasmNullRef; } static byte MemSize(MachineType type) { return 1 << i::ElementSizeLog2Of(type.representation()); } static int ElementSizeInBytes(ValueType type) { switch (type) { case kWasmI32: case kWasmF32: return 4; case kWasmI64: case kWasmF64: return 8; case kWasmS128: return 16; case kWasmAnyRef: case kWasmFuncRef: case kWasmExnRef: return kSystemPointerSize; default: UNREACHABLE(); } } static int ElementSizeLog2Of(ValueType type) { switch (type) { case kWasmI32: case kWasmF32: return 2; case kWasmI64: case kWasmF64: return 3; case kWasmS128: return 4; case kWasmAnyRef: case kWasmFuncRef: case kWasmExnRef: return kSystemPointerSizeLog2; default: UNREACHABLE(); } } static byte MemSize(ValueType type) { return 1 << ElementSizeLog2Of(type); } static ValueTypeCode ValueTypeCodeFor(ValueType type) { switch (type) { case kWasmI32: return kLocalI32; case kWasmI64: return kLocalI64; case kWasmF32: return kLocalF32; case kWasmF64: return kLocalF64; case kWasmS128: return kLocalS128; case kWasmAnyRef: return kLocalAnyRef; case kWasmFuncRef: return kLocalFuncRef; case kWasmExnRef: return kLocalExnRef; case kWasmStmt: return kLocalVoid; default: UNREACHABLE(); } } static MachineType MachineTypeFor(ValueType type) { switch (type) { case kWasmI32: return MachineType::Int32(); case kWasmI64: return MachineType::Int64(); case kWasmF32: return MachineType::Float32(); case kWasmF64: return MachineType::Float64(); case kWasmAnyRef: case kWasmFuncRef: case kWasmExnRef: return MachineType::TaggedPointer(); case kWasmS128: return MachineType::Simd128(); case kWasmStmt: return MachineType::None(); default: UNREACHABLE(); } } static MachineRepresentation MachineRepresentationFor(ValueType type) { switch (type) { case kWasmI32: return MachineRepresentation::kWord32; case kWasmI64: return MachineRepresentation::kWord64; case kWasmF32: return MachineRepresentation::kFloat32; case kWasmF64: return MachineRepresentation::kFloat64; case kWasmAnyRef: case kWasmFuncRef: case kWasmNullRef: case kWasmExnRef: return MachineRepresentation::kTaggedPointer; case kWasmS128: return MachineRepresentation::kSimd128; case kWasmStmt: return MachineRepresentation::kNone; default: UNREACHABLE(); } } static ValueType ValueTypeFor(MachineType type) { switch (type.representation()) { case MachineRepresentation::kWord8: case MachineRepresentation::kWord16: case MachineRepresentation::kWord32: return kWasmI32; case MachineRepresentation::kWord64: return kWasmI64; case MachineRepresentation::kFloat32: return kWasmF32; case MachineRepresentation::kFloat64: return kWasmF64; case MachineRepresentation::kTaggedPointer: return kWasmAnyRef; case MachineRepresentation::kSimd128: return kWasmS128; default: UNREACHABLE(); } } static char ShortNameOf(ValueType type) { switch (type) { case kWasmI32: return 'i'; case kWasmI64: return 'l'; case kWasmF32: return 'f'; case kWasmF64: return 'd'; case kWasmAnyRef: return 'r'; case kWasmFuncRef: return 'a'; case kWasmS128: return 's'; case kWasmStmt: return 'v'; case kWasmBottom: return '*'; default: return '?'; } } static const char* TypeName(ValueType type) { switch (type) { case kWasmI32: return "i32"; case kWasmI64: return "i64"; case kWasmF32: return "f32"; case kWasmF64: return "f64"; case kWasmAnyRef: return "anyref"; case kWasmFuncRef: return "funcref"; case kWasmNullRef: return "nullref"; case kWasmExnRef: return "exn"; case kWasmS128: return "s128"; case kWasmStmt: return ""; case kWasmBottom: return ""; default: return ""; } } private: DISALLOW_IMPLICIT_CONSTRUCTORS(ValueTypes); }; } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_VALUE_TYPE_H_