#ifndef V8_WASM_VALUE_TYPE_H_
#define V8_WASM_VALUE_TYPE_H_
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif
#include <optional>
#include "src/base/bit-field.h"
#include "src/base/template-utils.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/signature.h"
#include "src/wasm/wasm-constants.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-limits.h"
namespace v8 {
namespace internal {
class Simd128;
class Zone;
namespace wasm {
#define FOREACH_NUMERIC_VALUE_TYPE(V) \
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', "v128") \
V(I8, 0, I8, Int8, 'b', "i8") \
V(I16, 1, I16, Int16, 'h', "i16") \
V(F16, 1, F16, Float16, 'p', "f16")
#define FOREACH_VALUE_TYPE(V) \
V(Void, -1, Void, None, 'v', "<void>") \
FOREACH_NUMERIC_VALUE_TYPE(V) \
V(Ref, kTaggedSizeLog2, Ref, AnyTagged, 'r', "ref") \
V(RefNull, kTaggedSizeLog2, RefNull, AnyTagged, 'n', "ref null") \
V(Top, -1, Void, None, '\\', "<top>") \
V(Bottom, -1, Void, None, '*', "<bot>")
constexpr int kMaxValueTypeSize = 16;
struct TypeIndex {
uint32_t index;
static constexpr uint32_t kInvalid = ~0u;
constexpr bool valid() const { return index != kInvalid; }
size_t hash_value() const { return index; }
};
struct ModuleTypeIndex : public TypeIndex {
inline static constexpr ModuleTypeIndex Invalid();
bool operator==(ModuleTypeIndex other) const { return index == other.index; }
auto operator<=>(ModuleTypeIndex other) const {
return index <=> other.index;
}
};
ASSERT_TRIVIALLY_COPYABLE(ModuleTypeIndex);
constexpr ModuleTypeIndex ModuleTypeIndex::Invalid() {
return ModuleTypeIndex{ModuleTypeIndex::kInvalid};
}
constexpr ModuleTypeIndex kNoSuperType = ModuleTypeIndex::Invalid();
constexpr ModuleTypeIndex kNoType = ModuleTypeIndex::Invalid();
struct CanonicalTypeIndex : public TypeIndex {
inline static constexpr CanonicalTypeIndex Invalid();
bool operator==(CanonicalTypeIndex other) const {
return index == other.index;
}
auto operator<=>(CanonicalTypeIndex other) const {
return index <=> other.index;
}
};
ASSERT_TRIVIALLY_COPYABLE(CanonicalTypeIndex);
constexpr CanonicalTypeIndex CanonicalTypeIndex::Invalid() {
return CanonicalTypeIndex{CanonicalTypeIndex::kInvalid};
}
inline std::ostream& operator<<(std::ostream& oss, TypeIndex index) {
return oss << index.index;
}
enum Nullability : bool { kNonNullable = false, kNullable = true };
enum Exactness : bool { kAnySubtype, kExact };
static constexpr bool kNotShared = false;
enum ValueKind : uint8_t {
#define DEF_ENUM(kind, ...) k##kind,
FOREACH_VALUE_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
namespace value_type_impl {
using IsRefField = base::BitField<bool, 0, 1>;
using HasIndexOrSentinelField = IsRefField::Next<bool, 1>;
}
#define FUNC value_type_impl::RefTypeKindField::encode(RefTypeKind::kFunction)
#define STRUCT value_type_impl::RefTypeKindField::encode(RefTypeKind::kStruct)
#define ARRAY value_type_impl::RefTypeKindField::encode(RefTypeKind::kArray)
#define CONT value_type_impl::RefTypeKindField::encode(RefTypeKind::kCont)
#define REF value_type_impl::IsRefField::encode(true)
#define SENTINEL value_type_impl::HasIndexOrSentinelField::encode(true)
#define FOREACH_NONE_TYPE(V) \
V(NoCont, NoCont, REF | CONT, "nocont") \
V(NoExn, NoExn, REF, "noexn") \
V(NoExtern, NoExtern, REF, "noextern") \
V(NoFunc, NoFunc, REF | FUNC, "nofunc") \
V(None, None, REF, "none")
#define FOREACH_TOP_TYPE(V) \
V(Func, FuncRef, REF | FUNC, "func") \
V(Any, AnyRef, REF, "any") \
V(Extern, ExternRef, REF, "extern") \
V(Exn, ExnRef, REF, "exn") \
\
V(Cont, ContRef, REF | CONT, "cont")
#define FOREACH_ABSTRACT_TYPE(V) \
FOREACH_NONE_TYPE(V) \
FOREACH_TOP_TYPE(V) \
\
V(Eq, EqRef, REF, "eq") \
V(I31, I31Ref, REF, "i31") \
V(Struct, StructRef, REF | STRUCT, "struct") \
V(Array, ArrayRef, REF | ARRAY, "array") \
\
V(String, StringRef, REF, "string") \
V(StringViewWtf8, StringViewWtf8, REF, "stringview_wtf8") \
V(StringViewWtf16, StringViewWtf16, REF, "stringview_wtf16") \
V(StringViewIter, StringViewIter, REF, "stringview_iter")
#define FOREACH_INTERNAL_TYPE(V) \
V(Void, Void, SENTINEL, "<void>") \
V(Top, Void, SENTINEL, "<top>") \
V(Bottom, Void, SENTINEL, "<bot>") \
V(ExternString, Void, REF, "<extern_string>")
#define FOREACH_GENERIC_TYPE(V) \
FOREACH_INTERNAL_TYPE(V) \
FOREACH_ABSTRACT_TYPE(V)
enum class TypeKind : uint8_t {
kNumeric = 0b00,
kSentinel = SENTINEL,
kAbstractRef = REF,
kIndexedRef = REF | SENTINEL,
};
enum class RefTypeKind : uint8_t {
kOther = 0,
kFunction = 1,
kStruct = 2,
kArray = 3,
kCont = 4,
kLastValue = kCont,
};
namespace value_type_impl {
using TypeKindField = base::BitField<TypeKind, 0, 2>;
static_assert(TypeKindField::kMask ==
(IsRefField::kMask | HasIndexOrSentinelField::kMask));
using IsNullableField = TypeKindField::Next<Nullability, 1>;
using IsExactField = IsNullableField::Next<Exactness, 1>;
using IsSharedField = IsExactField::Next<bool, 1>;
using RefTypeKindField = IsSharedField::Next<RefTypeKind, 3>;
static_assert(RefTypeKindField::is_valid(RefTypeKind::kLastValue));
using PayloadField = RefTypeKindField::Next<uint32_t, 20>;
using ReservedField = PayloadField::Next<uint32_t, 4>;
static_assert(ReservedField::kShift + ReservedField::kSize == 32);
static constexpr uint32_t kGenericKindMask =
PayloadField::kMask | RefTypeKindField::kMask | TypeKindField::kMask;
static constexpr uint32_t kNumericKindMask =
kGenericKindMask | IsSharedField::kMask;
static constexpr uint32_t kNumericExtraBits = IsSharedField::encode(true);
#define COUNT(...) +1
static constexpr uint32_t kNumberOfGenericKinds = 0 FOREACH_GENERIC_TYPE(COUNT);
static constexpr uint32_t kNumberOfNumericKinds =
0 FOREACH_NUMERIC_VALUE_TYPE(COUNT);
#undef COUNT
static constexpr uint32_t kNumberOfStandardTypes =
kNumberOfGenericKinds + kNumberOfNumericKinds;
}
enum class StandardType : uint8_t {
#define DEF_ENUM(kind, ...) k##kind,
FOREACH_GENERIC_TYPE(DEF_ENUM) FOREACH_NUMERIC_VALUE_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
enum class GenericKind : uint32_t {
#define DEF_ENUM(kind, code, extra_bits, ...) \
k##kind = value_type_impl::PayloadField::encode( \
static_cast<uint8_t>(StandardType::k##kind)) + \
(extra_bits),
FOREACH_GENERIC_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
enum class NumericKind : uint32_t {
#define DEF_ENUM(kind, ...) \
k##kind = value_type_impl::PayloadField::encode( \
static_cast<uint8_t>(StandardType::k##kind)) + \
value_type_impl::kNumericExtraBits,
FOREACH_NUMERIC_VALUE_TYPE(DEF_ENUM)
#undef DEF_ENUM
};
#undef FUNC
#undef STRUCT
#undef ARRAY
#undef CONT
#undef REF
#undef SENTINEL
namespace value_type_impl {
constexpr uint32_t ToZeroBasedIndex(NumericKind kind) {
static_assert(static_cast<uint32_t>(StandardType::kI32) >=
kNumberOfGenericKinds);
uint32_t raw = PayloadField::decode(static_cast<uint32_t>(kind));
DCHECK_GE(raw, kNumberOfGenericKinds);
static_assert(kNumberOfNumericKinds == 8);
return (raw - kNumberOfGenericKinds) & 0x7;
}
}
constexpr bool IsNullKind(GenericKind kind) {
switch (kind) {
#define NULLTYPE(name, ...) \
case GenericKind::k##name: \
return true;
FOREACH_NONE_TYPE(NULLTYPE)
#undef NULLTYPE
default:
break;
}
return false;
}
class ValueTypeBase {
public:
static const int kNumIndexBits = value_type_impl::PayloadField::kSize;
static const int kLastUsedBit = value_type_impl::PayloadField::kLastUsedBit;
static const uint32_t kIsRefBit = value_type_impl::IsRefField::kMask;
static const uint32_t kIsNullableBit =
value_type_impl::IsNullableField::kMask;
static const uint32_t kIsSharedBit = value_type_impl::IsSharedField::kMask;
static const uint32_t kHasIndexBit =
value_type_impl::HasIndexOrSentinelField::kMask;
static const uint32_t kRefKindBits = value_type_impl::RefTypeKindField::kMask;
static const uint32_t kRefKindShift =
value_type_impl::RefTypeKindField::kShift;
static const uint32_t kIndexBits = value_type_impl::PayloadField::kMask;
static const uint32_t kIndexShift = value_type_impl::PayloadField::kShift;
constexpr ValueTypeBase()
: ValueTypeBase(GenericKind::kVoid, kNonNullable, false) {}
void Populate(bool shared, RefTypeKind kind) {
uint32_t bits = value_type_impl::IsSharedField::update(bit_field_, shared);
bit_field_ = value_type_impl::RefTypeKindField::update(bits, kind);
}
constexpr TypeKind type_kind() const {
return value_type_impl::TypeKindField::decode(bit_field_);
}
constexpr bool has_index() const {
return type_kind() == TypeKind::kIndexedRef;
}
constexpr bool is_sentinel() const {
return type_kind() == TypeKind::kSentinel;
}
constexpr bool is_numeric() const {
return type_kind() == TypeKind::kNumeric;
}
constexpr bool is_abstract_ref() const {
return type_kind() == TypeKind::kAbstractRef;
}
constexpr bool is_ref() const {
return value_type_impl::IsRefField::decode(bit_field_);
}
constexpr bool is_generic() const {
TypeKind kind = type_kind();
return kind == TypeKind::kAbstractRef || kind == TypeKind::kSentinel;
}
constexpr Nullability nullability() const {
DCHECK(is_ref());
return value_type_impl::IsNullableField::decode(bit_field_);
}
constexpr bool is_nullable() const {
return is_ref() && static_cast<bool>(nullability());
}
constexpr bool is_non_nullable() const {
return is_ref() && !static_cast<bool>(nullability());
}
constexpr Exactness exactness() const {
return value_type_impl::IsExactField::decode(bit_field_);
}
constexpr bool is_exact() const { return exactness() == Exactness::kExact; }
constexpr bool is_shared() const {
return value_type_impl::IsSharedField::decode(bit_field_);
}
constexpr RefTypeKind ref_type_kind() const {
return value_type_impl::RefTypeKindField::decode(bit_field_);
}
constexpr StandardType standard_type() const {
DCHECK(!has_index());
return static_cast<StandardType>(
value_type_impl::PayloadField::decode(bit_field_));
}
constexpr NumericKind numeric_kind() const {
DCHECK(is_numeric());
return static_cast<NumericKind>(bit_field_ &
value_type_impl::kNumericKindMask);
}
constexpr GenericKind generic_kind() const {
DCHECK(is_generic());
return static_cast<GenericKind>(bit_field_ &
value_type_impl::kGenericKindMask);
}
constexpr bool is_bottom() const {
uint32_t bits = bit_field_ & value_type_impl::kGenericKindMask;
return bits == static_cast<uint32_t>(GenericKind::kBottom);
}
constexpr bool is_top() const {
uint32_t bits = bit_field_ & value_type_impl::kGenericKindMask;
return bits == static_cast<uint32_t>(GenericKind::kTop);
}
constexpr bool is_void() const {
uint32_t bits = bit_field_ & value_type_impl::kGenericKindMask;
return bits == static_cast<uint32_t>(GenericKind::kVoid);
}
constexpr bool is_string_view() const {
uint32_t bits = bit_field_ & value_type_impl::kGenericKindMask;
return bits == static_cast<uint32_t>(GenericKind::kStringViewWtf8) ||
bits == static_cast<uint32_t>(GenericKind::kStringViewWtf16) ||
bits == static_cast<uint32_t>(GenericKind::kStringViewIter);
}
constexpr bool is_packed() const {
return bit_field_ == static_cast<uint32_t>(NumericKind::kI8) ||
bit_field_ == static_cast<uint32_t>(NumericKind::kI16) ||
bit_field_ == static_cast<uint32_t>(NumericKind::kF16);
}
constexpr bool is_reference_to(GenericKind type) const {
return is_abstract_ref() && generic_kind() == type;
}
constexpr bool is_defaultable() const {
return is_numeric() || is_nullable();
}
constexpr bool is_uninhabited() const {
if (is_bottom()) return true;
if (is_defaultable()) return false;
if (has_index()) return false;
return IsNullKind(generic_kind());
}
constexpr bool use_wasm_null() const {
DCHECK(is_ref());
if (has_index()) return true;
GenericKind ref = generic_kind();
if (ref == GenericKind::kExtern) return false;
if (ref == GenericKind::kExternString) return false;
if (ref == GenericKind::kNoExtern) return false;
return true;
}
constexpr bool is_none_type() const {
DCHECK(!is_numeric());
if (!is_abstract_ref()) return false;
return IsNullKind(generic_kind());
}
constexpr int value_kind_size_log2() const {
DCHECK(!is_sentinel());
if (is_ref()) return kTaggedSizeLog2;
constexpr uint8_t kValueKindSizeLog2[] = {
#define VALUE_KIND_SIZE_LOG2(kind, log2Size, ...) log2Size,
FOREACH_NUMERIC_VALUE_TYPE(VALUE_KIND_SIZE_LOG2)
#undef VALUE_KIND_SIZE_LOG2
};
uint32_t index = value_type_impl::ToZeroBasedIndex(numeric_kind());
return kValueKindSizeLog2[index];
}
constexpr int value_kind_size() const {
DCHECK(!is_sentinel());
if (is_ref()) return kTaggedSize;
constexpr uint8_t kValueKindSize[] = {
#define ELEM_SIZE_LOG2(kind, log2Size, ...) (1 << log2Size),
FOREACH_NUMERIC_VALUE_TYPE(ELEM_SIZE_LOG2)
#undef ELEM_SIZE_LOG2
};
uint32_t index = value_type_impl::ToZeroBasedIndex(numeric_kind());
return kValueKindSize[index];
}
constexpr int value_kind_full_size() const {
if (is_ref()) {
return kSystemPointerSize;
}
return value_kind_size();
}
constexpr MachineType machine_type() const {
if (is_sentinel()) return MachineType::None();
if (is_ref()) return MachineType::AnyTagged();
constexpr MachineType kMachineType[] = {
#define MACH_TYPE(kind, log2Size, code, machineType, ...) \
MachineType::machineType(),
FOREACH_NUMERIC_VALUE_TYPE(MACH_TYPE)
#undef MACH_TYPE
};
uint32_t index = value_type_impl::ToZeroBasedIndex(numeric_kind());
return kMachineType[index];
}
constexpr MachineRepresentation machine_representation() const {
return machine_type().representation();
}
ValueTypeCode value_type_code() const {
if (is_numeric()) return value_type_code_numeric();
if (is_ref() && encoding_needs_heap_type()) {
return is_nullable() ? kRefNullCode : kRefCode;
}
DCHECK(!has_index());
return value_type_code_generic();
}
constexpr bool encoding_needs_heap_type() const {
if (has_index()) return true;
if (!is_abstract_ref()) return false;
return !is_nullable() || is_shared();
}
constexpr bool encoding_needs_shared() const {
return is_abstract_ref() && is_shared();
}
constexpr bool encoding_needs_exact() const { return is_exact(); }
V8_EXPORT_PRIVATE ValueTypeCode value_type_code_numeric() const;
V8_EXPORT_PRIVATE ValueTypeCode value_type_code_generic() const;
constexpr char short_name() const {
if (is_ref()) {
return is_nullable() ? 'n' : 'r';
}
if (is_sentinel()) {
if (is_void()) return 'v';
if (is_top()) return '\\';
if (is_bottom()) return '*';
}
constexpr const char kNumericShortName[] = {
#define SHORT_NAME(kind, log2, code, mtype, shortName, ...) shortName,
FOREACH_NUMERIC_VALUE_TYPE(SHORT_NAME)
#undef SHORT_NAME
};
uint32_t index = value_type_impl::ToZeroBasedIndex(numeric_kind());
return kNumericShortName[index];
}
V8_EXPORT_PRIVATE std::string generic_heaptype_name() const;
V8_EXPORT_PRIVATE std::string name() const;
constexpr bool is_valid() const {
static_assert(static_cast<int>(StandardType::kI32) >
static_cast<int>(StandardType::kAny));
uint32_t payload = value_type_impl::PayloadField::decode(bit_field_);
if (is_numeric()) {
if ((bit_field_ & ~value_type_impl::PayloadField::kMask) !=
value_type_impl::kNumericExtraBits) {
return false;
}
return payload >= value_type_impl::kNumberOfGenericKinds &&
payload < value_type_impl::kNumberOfStandardTypes;
}
if (!has_index()) {
if (payload >= value_type_impl::kNumberOfGenericKinds) return false;
if (is_exact()) return false;
}
return value_type_impl::ReservedField::decode(bit_field_) == 0;
}
constexpr uint32_t raw_bit_field() const { return bit_field_; }
size_t hash_value() const { return bit_field_; }
constexpr bool is_reference() const { return is_ref(); }
constexpr bool is_object_reference() const { return is_ref(); }
static constexpr ValueTypeBase Primitive(ValueKind kind) {
switch (kind) {
case kI32:
return ValueTypeBase(NumericKind::kI32);
case kI64:
return ValueTypeBase(NumericKind::kI64);
case kF32:
return ValueTypeBase(NumericKind::kF32);
case kF64:
return ValueTypeBase(NumericKind::kF64);
case kS128:
return ValueTypeBase(NumericKind::kS128);
case kI8:
return ValueTypeBase(NumericKind::kI8);
case kI16:
return ValueTypeBase(NumericKind::kI16);
case kF16:
return ValueTypeBase(NumericKind::kF16);
case kVoid:
return ValueTypeBase(GenericKind::kVoid, kNonNullable, false);
case kRef:
case kRefNull:
case kTop:
case kBottom:
UNREACHABLE();
}
SBXCHECK(false);
}
constexpr ValueKind kind() const {
if (is_numeric()) {
switch (numeric_kind()) {
case NumericKind::kI32:
return kI32;
case NumericKind::kI64:
return kI64;
case NumericKind::kF32:
return kF32;
case NumericKind::kF64:
return kF64;
case NumericKind::kS128:
return kS128;
case NumericKind::kI8:
return kI8;
case NumericKind::kI16:
return kI16;
case NumericKind::kF16:
return kF16;
}
UNREACHABLE();
}
if (is_top()) return kTop;
if (is_bottom()) return kBottom;
if (is_void()) return kVoid;
return is_nullable() ? kRefNull : kRef;
}
protected:
friend class CanonicalValueType;
friend class HeapType;
friend class IndependentValueType;
friend class ValueType;
explicit constexpr ValueTypeBase(uint32_t bit_field) : bit_field_(bit_field) {
DCHECK(is_valid());
}
explicit constexpr ValueTypeBase(NumericKind kind)
: bit_field_(static_cast<uint32_t>(kind)) {
DCHECK(is_numeric());
}
explicit constexpr ValueTypeBase(GenericKind kind, Nullability nullable,
bool is_shared)
: bit_field_(static_cast<uint32_t>(kind) |
value_type_impl::IsNullableField::encode(nullable) |
value_type_impl::IsSharedField::encode(is_shared)) {
DCHECK(is_generic());
}
explicit constexpr ValueTypeBase(TypeIndex index, Nullability nullable,
Exactness exact, bool shared,
RefTypeKind ref_type_kind)
: bit_field_(
value_type_impl::TypeKindField::encode(TypeKind::kIndexedRef) |
value_type_impl::IsNullableField::encode(nullable) |
value_type_impl::IsExactField::encode(exact) |
value_type_impl::IsSharedField::encode(shared) |
value_type_impl::RefTypeKindField::encode(ref_type_kind) |
value_type_impl::PayloadField::encode(index.index)) {
CHECK(value_type_impl::PayloadField::is_valid(index.index));
}
constexpr TypeIndex raw_index() const {
DCHECK(has_index());
return TypeIndex(value_type_impl::PayloadField::decode(bit_field_));
}
uint32_t bit_field_;
};
ASSERT_TRIVIALLY_COPYABLE(ValueTypeBase);
class HeapType : public ValueTypeBase {
public:
static constexpr HeapType Generic(GenericKind kind, bool shared) {
return HeapType{ValueTypeBase(kind, kNullable, shared)};
}
static constexpr HeapType Index(ModuleTypeIndex index, bool shared,
RefTypeKind kind,
Exactness exact = Exactness::kAnySubtype) {
return HeapType{ValueTypeBase(index, kNullable, exact, shared, kind)};
}
static constexpr HeapType FromBits(uint32_t bits) {
HeapType type{ValueTypeBase(bits)};
DCHECK(!type.is_numeric());
return type;
}
static constexpr HeapType from_code(uint8_t code, bool is_shared) {
constexpr uint8_t kFirst = ValueTypeCode::kFirstHeapTypeCode;
constexpr uint8_t kLast = ValueTypeCode::kLastHeapTypeCode;
if (code < kFirst || code > kLast) {
return Generic(GenericKind::kBottom, false);
}
constexpr size_t kNumCases = kLast - kFirst + 1;
constexpr std::array<GenericKind, kNumCases> kLookupTable =
base::make_array<kNumCases>([](size_t i) {
switch (i) {
#define CASE(name, code, ...) \
case (k##code##Code - kFirst): \
static_assert(k##code##Code >= kFirst && k##code##Code <= kLast); \
return GenericKind::k##name;
FOREACH_ABSTRACT_TYPE(CASE)
#undef CASE
default:
return GenericKind::kBottom;
}
});
return Generic(kLookupTable[code - kFirst], is_shared);
}
constexpr HeapType AsExact(Exactness exact = Exactness::kExact) const {
DCHECK(exact == Exactness::kAnySubtype || has_index());
return HeapType{ValueTypeBase(
value_type_impl::IsExactField::update(raw_bit_field(), exact))};
}
int32_t code() const {
if (has_index()) return static_cast<int32_t>(ref_index().index);
return 0xFFFF'FF80 | value_type_code_generic();
}
constexpr ModuleTypeIndex ref_index() const {
return ModuleTypeIndex{raw_index()};
}
constexpr bool operator==(HeapType other) const {
constexpr uint32_t kMask = ~value_type_impl::IsRefField::kMask &
~value_type_impl::IsNullableField::kMask;
return (bit_field_ & kMask) == (other.bit_field_ & kMask);
}
std::string name() const {
if (has_index()) {
if (is_exact()) return "exact " + std::to_string(raw_index().index);
return std::to_string(raw_index().index);
}
return generic_heaptype_name();
}
constexpr bool is_index() const { return has_index(); }
private:
constexpr bool is_nullable() const;
constexpr bool is_non_nullable() const;
constexpr Nullability nullability() const;
};
class CanonicalValueType;
class ValueType : public ValueTypeBase {
public:
static constexpr ValueType Primitive(NumericKind kind) {
return ValueType{ValueTypeBase(kind)};
}
static constexpr ValueType Generic(GenericKind kind, Nullability nullable,
bool shared) {
return ValueType{ValueTypeBase(kind, nullable, shared)};
}
static constexpr ValueType Ref(ModuleTypeIndex index, bool shared,
RefTypeKind kind) {
return ValueType{ValueTypeBase(index, kNonNullable, Exactness::kAnySubtype,
shared, kind)};
}
static constexpr ValueType Ref(HeapType type) {
return ValueType{type}.AsNonNull();
}
static constexpr ValueType RefNull(ModuleTypeIndex index, bool shared,
RefTypeKind kind) {
return ValueType{
ValueTypeBase(index, kNullable, Exactness::kAnySubtype, shared, kind)};
}
static constexpr ValueType RefNull(HeapType type) {
return ValueType{type}.AsNullable(kNullable);
}
static constexpr ValueType RefMaybeNull(ModuleTypeIndex index,
Nullability nullable, bool shared,
RefTypeKind kind) {
return ValueType{
ValueTypeBase(index, nullable, Exactness::kAnySubtype, shared, kind)};
}
static constexpr ValueType RefMaybeNull(HeapType type, Nullability nullable) {
return ValueType{type}.AsNullable(nullable);
}
static constexpr ValueType FromRawBitField(uint32_t bits) {
return ValueType{ValueTypeBase(bits)};
}
constexpr ValueType AsNonNull() const { return AsNullable(kNonNullable); }
constexpr ValueType AsNullable(Nullability nullable = kNullable) const {
DCHECK(!is_numeric());
return ValueType{ValueTypeBase(
value_type_impl::IsNullableField::update(raw_bit_field(), nullable))};
}
constexpr ValueType AsExact(Exactness exact = Exactness::kExact) const {
DCHECK(exact == Exactness::kAnySubtype || has_index());
return ValueType{ValueTypeBase(
value_type_impl::IsExactField::update(raw_bit_field(), exact))};
}
constexpr ValueType AsExactIfIndexed(Exactness exact) const {
if (!has_index()) return *this;
return AsExact(exact);
}
ValueType AsExactIfEnabled(WasmEnabledFeatures enabled,
Exactness exact = Exactness::kExact) const {
if V8_LIKELY (!enabled.has_custom_descriptors()) return *this;
return AsExact(exact);
}
constexpr ValueType AsNonShared() const {
if (!is_ref()) return *this;
return ValueType{ValueTypeBase(
value_type_impl::IsSharedField::update(raw_bit_field(), false))};
}
constexpr ValueType Unpacked() const {
if (bit_field_ == static_cast<uint32_t>(NumericKind::kI8) ||
bit_field_ == static_cast<uint32_t>(NumericKind::kI16)) {
return Primitive(NumericKind::kI32);
}
if (bit_field_ == static_cast<uint32_t>(NumericKind::kF16)) {
return Primitive(NumericKind::kF32);
}
return *this;
}
constexpr CanonicalValueType Canonicalize(CanonicalTypeIndex index) const;
static constexpr ValueType Primitive(ValueKind kind) {
return ValueType{ValueTypeBase::Primitive(kind)};
}
static ValueType For(MachineType type) {
switch (type.representation()) {
case MachineRepresentation::kWord8:
case MachineRepresentation::kWord16:
case MachineRepresentation::kWord32:
return Primitive(NumericKind::kI32);
case MachineRepresentation::kWord64:
return Primitive(NumericKind::kI64);
case MachineRepresentation::kFloat32:
return Primitive(NumericKind::kF32);
case MachineRepresentation::kFloat64:
return Primitive(NumericKind::kF64);
case MachineRepresentation::kTaggedPointer:
return Generic(GenericKind::kAny, kNullable, kNotShared);
case MachineRepresentation::kSimd128:
return Primitive(NumericKind::kS128);
default:
UNREACHABLE();
}
}
constexpr bool operator==(ValueType other) const {
return bit_field_ == other.bit_field_;
}
constexpr HeapType heap_type() const {
DCHECK(!is_numeric());
return HeapType{*this};
}
constexpr ModuleTypeIndex ref_index() const {
return ModuleTypeIndex{raw_index()};
}
};
ASSERT_TRIVIALLY_COPYABLE(ValueType);
class CanonicalValueType : public ValueTypeBase {
public:
static constexpr CanonicalValueType Primitive(NumericKind kind) {
return CanonicalValueType{ValueTypeBase(kind)};
}
static constexpr CanonicalValueType Ref(CanonicalTypeIndex index, bool shared,
RefTypeKind kind) {
return CanonicalValueType{ValueTypeBase(
index, kNonNullable, Exactness::kAnySubtype, shared, kind)};
}
static constexpr CanonicalValueType RefNull(CanonicalTypeIndex index,
bool shared, RefTypeKind kind) {
return CanonicalValueType{
ValueTypeBase(index, kNullable, Exactness::kAnySubtype, shared, kind)};
}
static constexpr CanonicalValueType RefMaybeNull(CanonicalValueType type,
Nullability nullable) {
return CanonicalValueType{
ValueTypeBase(value_type_impl::IsNullableField::update(
type.raw_bit_field(), nullable))};
}
static constexpr CanonicalValueType FromRawBitField(uint32_t bits) {
return CanonicalValueType{ValueTypeBase(bits)};
}
constexpr CanonicalValueType AsExact(
Exactness exact = Exactness::kExact) const {
DCHECK(exact == Exactness::kAnySubtype || has_index());
return CanonicalValueType{ValueTypeBase(
value_type_impl::IsExactField::update(raw_bit_field(), exact))};
}
CanonicalValueType AsExactIfEnabled(
WasmEnabledFeatures enabled, Exactness exact = Exactness::kExact) const {
if V8_LIKELY (!enabled.has_custom_descriptors()) return *this;
return AsExact(exact);
}
constexpr CanonicalTypeIndex ref_index() const {
return CanonicalTypeIndex{raw_index()};
}
constexpr bool operator==(CanonicalValueType other) const {
return bit_field_ == other.bit_field_;
}
constexpr uint32_t all_bits_without_index() const {
static_assert(std::is_same_v<uint32_t, decltype(bit_field_)>);
return bit_field_ & ~kIndexBits;
}
constexpr bool is_equal_except_index(CanonicalValueType other) const {
return all_bits_without_index() == other.all_bits_without_index();
}
constexpr bool IsFunctionType() const {
return ref_type_kind() == RefTypeKind::kFunction;
}
};
ASSERT_TRIVIALLY_COPYABLE(CanonicalValueType);
constexpr CanonicalValueType ValueType::Canonicalize(
CanonicalTypeIndex index) const {
DCHECK(has_index());
return CanonicalValueType{ValueTypeBase(
value_type_impl::PayloadField::update(raw_bit_field(), index.index))};
}
class IndependentValueType : public ValueTypeBase {
public:
explicit constexpr IndependentValueType(NumericKind kind)
: ValueTypeBase(kind) {}
constexpr operator ValueType() const { return ValueType{*this}; }
constexpr operator CanonicalValueType() const {
return CanonicalValueType{*this};
}
constexpr bool operator==(ValueType b) const {
return raw_bit_field() == b.raw_bit_field();
}
constexpr bool operator==(CanonicalValueType b) const {
return raw_bit_field() == b.raw_bit_field();
}
protected:
explicit constexpr IndependentValueType(GenericKind kind,
Nullability nullable, bool shared)
: ValueTypeBase(kind, nullable, shared) {}
};
class IndependentHeapType : public IndependentValueType {
public:
explicit constexpr IndependentHeapType(GenericKind kind,
Nullability nullable = kNullable,
bool shared = false)
: IndependentValueType(kind, nullable, shared) {}
constexpr IndependentHeapType AsNonNull() const {
return IndependentHeapType(generic_kind(), kNonNullable, is_shared());
}
constexpr HeapType heap_type() const { return HeapType{*this}; }
constexpr operator HeapType() const { return HeapType{*this}; }
};
constexpr bool is_reference(ValueKind kind) {
return kind == kRef || kind == kRefNull;
}
constexpr bool is_object_reference(ValueKind kind) {
return kind == kRef || kind == kRefNull;
}
constexpr int value_kind_size_log2(ValueKind kind) {
constexpr int8_t kValueKindSizeLog2[] = {
#define VALUE_KIND_SIZE_LOG2(kind, log2Size, ...) log2Size,
FOREACH_VALUE_TYPE(VALUE_KIND_SIZE_LOG2)
#undef VALUE_KIND_SIZE_LOG2
};
int size_log_2 = kValueKindSizeLog2[kind];
DCHECK_LE(0, size_log_2);
return size_log_2;
}
constexpr int value_kind_size(ValueKind kind) {
constexpr int8_t kElementSize[] = {
#define ELEM_SIZE_LOG2(kind, log2Size, ...) \
log2Size == -1 ? -1 : (1 << std::max(0, log2Size)),
FOREACH_VALUE_TYPE(ELEM_SIZE_LOG2)
#undef ELEM_SIZE_LOG2
};
int size = kElementSize[kind];
DCHECK_LT(0, size);
return size;
}
constexpr int value_kind_full_size(ValueKind kind) {
if (is_reference(kind)) {
return kSystemPointerSize;
}
return value_kind_size(kind);
}
constexpr const char* name(ValueKind kind) {
constexpr const char* kKindName[] = {
#define KIND_NAME(kind, log2Size, code, machineType, shortName, kindName, ...) \
kindName,
FOREACH_VALUE_TYPE(KIND_NAME)
#undef TYPE_NAME
};
return kKindName[kind];
}
inline std::ostream& operator<<(std::ostream& oss, ValueKind kind) {
return oss << name(kind);
}
constexpr MachineType machine_type(ValueKind kind) {
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];
}
constexpr bool is_packed(ValueKind kind) {
return kind == kI8 || kind == kI16 || kind == kF16;
}
constexpr ValueKind unpacked(ValueKind kind) {
return is_packed(kind) ? (kind == kF16 ? kF32 : kI32) : kind;
}
static_assert(sizeof(ValueTypeBase) <= kUInt32Size,
"ValueType is small and can be passed by value");
static_assert(ValueTypeBase::kLastUsedBit < kSmiValueSize,
"ValueType has space to be encoded in a Smi");
inline std::ostream& operator<<(std::ostream& oss, ValueType type) {
return oss << type.name() << " (raw=0x" << std::hex << type.raw_bit_field()
<< ")" << std::dec;
}
constexpr IndependentValueType kWasmI32{NumericKind::kI32};
constexpr IndependentValueType kWasmI64{NumericKind::kI64};
constexpr IndependentValueType kWasmF32{NumericKind::kF32};
constexpr IndependentValueType kWasmF64{NumericKind::kF64};
constexpr IndependentValueType kWasmS128{NumericKind::kS128};
constexpr IndependentValueType kWasmI8{NumericKind::kI8};
constexpr IndependentValueType kWasmI16{NumericKind::kI16};
constexpr IndependentValueType kWasmF16{NumericKind::kF16};
constexpr IndependentHeapType kWasmVoid{GenericKind::kVoid, kNonNullable};
constexpr IndependentHeapType kWasmTop{GenericKind::kTop};
constexpr IndependentHeapType kWasmBottom{GenericKind::kBottom, kNonNullable};
constexpr IndependentHeapType kWasmFuncRef{GenericKind::kFunc};
constexpr IndependentHeapType kWasmAnyRef{GenericKind::kAny};
constexpr IndependentHeapType kWasmSharedAnyRef{GenericKind::kAny, kNullable,
true};
constexpr IndependentHeapType kWasmExternRef{GenericKind::kExtern};
constexpr IndependentHeapType kWasmRefExtern{GenericKind::kExtern,
kNonNullable};
constexpr IndependentHeapType kWasmSharedExternRef{GenericKind::kExtern,
kNullable, true};
constexpr IndependentHeapType kWasmExnRef{GenericKind::kExn};
constexpr IndependentHeapType kWasmSharedExnRef{GenericKind::kExn, kNullable,
true};
constexpr IndependentHeapType kWasmEqRef{GenericKind::kEq};
constexpr IndependentHeapType kWasmI31Ref{GenericKind::kI31};
constexpr IndependentHeapType kWasmRefI31{GenericKind::kI31, kNonNullable};
constexpr IndependentHeapType kWasmStructRef{GenericKind::kStruct};
constexpr IndependentHeapType kWasmArrayRef{GenericKind::kArray};
constexpr IndependentHeapType kWasmStringRef{GenericKind::kString};
constexpr IndependentHeapType kWasmRefString{GenericKind::kString,
kNonNullable};
constexpr IndependentHeapType kWasmRefNullExternString{
GenericKind::kExternString};
constexpr IndependentHeapType kWasmRefExternString{GenericKind::kExternString,
kNonNullable};
constexpr IndependentHeapType kWasmStringViewWtf8{GenericKind::kStringViewWtf8,
kNonNullable};
constexpr IndependentHeapType kWasmStringViewWtf16{
GenericKind::kStringViewWtf16, kNonNullable};
constexpr IndependentHeapType kWasmStringViewIter{GenericKind::kStringViewIter,
kNonNullable};
constexpr IndependentHeapType kWasmNullRef{GenericKind::kNone};
constexpr IndependentHeapType kWasmRefNone{GenericKind::kNone, kNonNullable};
constexpr IndependentHeapType kWasmNullExternRef{GenericKind::kNoExtern};
constexpr IndependentHeapType kWasmNullExnRef{GenericKind::kNoExn};
constexpr IndependentHeapType kWasmNullFuncRef{GenericKind::kNoFunc};
constexpr IndependentHeapType kWasmContRef{GenericKind::kCont};
constexpr IndependentHeapType kWasmNullContRef{GenericKind::kNoCont};
#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>;
class CanonicalSig : public Signature<CanonicalValueType> {
public:
CanonicalSig(size_t return_count, size_t parameter_count,
const CanonicalValueType* reps)
: Signature<CanonicalValueType>(return_count, parameter_count, reps) {}
class Builder : public SignatureBuilder<CanonicalSig, CanonicalValueType> {
public:
Builder(Zone* zone, size_t return_count, size_t parameter_count)
: SignatureBuilder<CanonicalSig, CanonicalValueType>(zone, return_count,
parameter_count) {}
const CanonicalSig* Get(CanonicalTypeIndex index) const;
};
uint64_t signature_hash() const { return signature_hash_; }
CanonicalTypeIndex index() const { return index_; }
private:
uint64_t signature_hash_;
CanonicalTypeIndex index_;
};
V8_EXPORT_PRIVATE bool EquivalentNumericSig(const CanonicalSig* a,
const FunctionSig* b);
#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, F16, Float16) \
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
};
constexpr LoadType(LoadTypeValue val) : val_(val) {}
constexpr LoadTypeValue value() const { return val_; }
constexpr uint8_t size_log_2() const { return kLoadSizeLog2[val_]; }
constexpr uint8_t size() const { return kLoadSize[val_]; }
constexpr ValueType value_type() const { return kValueType[val_]; }
constexpr MachineType mem_type() const { return kMemType[val_]; }
static LoadType ForValueKind(ValueKind kind, bool is_signed = false) {
switch (kind) {
case kI32:
return kI32Load;
case kI64:
return kI64Load;
case kF32:
return kF32Load;
case kF64:
return kF64Load;
case kS128:
return kS128Load;
case kI8:
return is_signed ? kI32Load8S : kI32Load8U;
case kI16:
return is_signed ? kI32Load16S : kI32Load16U;
case kF16:
return kF32LoadF16;
default:
UNREACHABLE();
}
}
private:
LoadTypeValue val_;
static constexpr uint8_t kLoadSize[] = {
#define LOAD_SIZE(_, __, memtype) \
static_cast<uint8_t>( \
ElementSizeInBytes(MachineType::memtype().representation())),
FOREACH_LOAD_TYPE(LOAD_SIZE)
#undef LOAD_SIZE
};
static constexpr uint8_t kLoadSizeLog2[] = {
#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::Primitive(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, F16, Float16) \
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
};
constexpr StoreType(StoreTypeValue val) : 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 ForValueKind(ValueKind kind) {
switch (kind) {
case kI32:
return kI32Store;
case kI64:
return kI64Store;
case kF32:
return kF32Store;
case kF64:
return kF64Store;
case kS128:
return kS128Store;
case kI8:
return kI32Store8;
case kI16:
return kI32Store16;
case kF16:
return kF32StoreF16;
default:
UNREACHABLE();
}
}
private:
StoreTypeValue val_;
static constexpr uint8_t kStoreSizeLog2[] = {
#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::Primitive(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
};
};
std::optional<wasm::ValueKind> WasmReturnTypeFromSignature(
const CanonicalSig* wasm_signature);
V8_EXPORT_PRIVATE const wasm::FunctionSig* GetI32Sig(
Zone* zone, const wasm::FunctionSig* sig);
}
}
}
#endif