summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/turboshaft/types.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/turboshaft/types.h')
-rw-r--r--deps/v8/src/compiler/turboshaft/types.h919
1 files changed, 919 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/turboshaft/types.h b/deps/v8/src/compiler/turboshaft/types.h
new file mode 100644
index 0000000000..3a6f4d192b
--- /dev/null
+++ b/deps/v8/src/compiler/turboshaft/types.h
@@ -0,0 +1,919 @@
+// Copyright 2022 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_COMPILER_TURBOSHAFT_TYPES_H_
+#define V8_COMPILER_TURBOSHAFT_TYPES_H_
+
+#include <cmath>
+#include <limits>
+
+#include "src/base/container-utils.h"
+#include "src/base/export-template.h"
+#include "src/base/logging.h"
+#include "src/base/small-vector.h"
+#include "src/common/globals.h"
+#include "src/compiler/turboshaft/fast-hash.h"
+#include "src/numbers/conversions.h"
+#include "src/objects/turboshaft-types.h"
+#include "src/utils/ostreams.h"
+#include "src/zone/zone-containers.h"
+
+#ifdef DEBUG
+#define TURBOSHAFT_TRACE_TYPING(...) \
+ do { \
+ if (V8_UNLIKELY(v8_flags.turboshaft_trace_typing)) { \
+ PrintF(__VA_ARGS__); \
+ } \
+ } while (false)
+
+#define TURBOSHAFT_TRACE_TYPING_WITH_COLOR(colorcode, str, ...) \
+ TURBOSHAFT_TRACE_TYPING( \
+ (v8_flags.log_colour ? ("\033[" colorcode "m" str "\033[m") : str), \
+ __VA_ARGS__)
+#define TURBOSHAFT_TRACE_TYPING_OK(str, ...) \
+ TURBOSHAFT_TRACE_TYPING_WITH_COLOR("32", str, __VA_ARGS__)
+#define TURBOSHAFT_TRACE_TYPING_FAIL(str, ...) \
+ TURBOSHAFT_TRACE_TYPING_WITH_COLOR("31", str, __VA_ARGS__)
+#else
+#define TURBOSHAFT_TRACE_TYPING(...) ((void)0)
+#define TURBOSHAFT_TRACE_TYPING_WITH_COLOR(colorcode, str, ...) ((void)0)
+#define TURBOSHAFT_TRACE_TYPING_OK(str, ...) ((void)0)
+#define TURBOSHAFT_TRACE_TYPING_FAIL(str, ...) ((void)0)
+#endif // DEBUG
+
+namespace v8::internal {
+class Factory;
+}
+
+namespace v8::internal::compiler::turboshaft {
+
+namespace detail {
+
+template <typename T>
+inline bool is_unique_and_sorted(const T& container) {
+ if (std::size(container) <= 1) return true;
+ auto cur = std::begin(container);
+ auto next = cur;
+ for (++next; next != std::end(container); ++cur, ++next) {
+ if (!(*cur < *next)) return false;
+ }
+ return true;
+}
+
+template <typename T>
+inline bool is_minus_zero(T value) {
+ return IsMinusZero(value);
+}
+
+template <typename T>
+inline bool is_float_special_value(T value) {
+ return std::isnan(value) || is_minus_zero(value);
+}
+
+template <size_t Bits>
+struct TypeForBits;
+template <>
+struct TypeForBits<32> {
+ using uint_type = uint32_t;
+ using float_type = float;
+ static constexpr float_type nan =
+ std::numeric_limits<float_type>::quiet_NaN();
+};
+template <>
+struct TypeForBits<64> {
+ using uint_type = uint64_t;
+ using float_type = double;
+ static constexpr float_type nan =
+ std::numeric_limits<float_type>::quiet_NaN();
+};
+
+// gcc versions < 9 may produce the following compilation error:
+// > '<anonymous>' is used uninitialized in this function
+// if Payload_Empty is initialized without any data, link to a relevant bug:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86465
+// A workaround is to add a dummy value which is zero initialized by default.
+// More information as well as a sample reproducible code can be found at the
+// comment section of this CL crrev.com/c/4057111
+// TODO(nicohartmann@): Remove dummy once all platforms are using gcc >= 9.
+struct Payload_Empty {
+ uint8_t dummy = 0;
+};
+
+template <typename T>
+struct Payload_Range {
+ T min;
+ T max;
+};
+
+template <typename T>
+struct Payload_InlineSet {
+ T elements[2];
+};
+
+template <typename T>
+struct Payload_OutlineSet {
+ T* array;
+};
+
+} // namespace detail
+
+template <typename T>
+std::enable_if_t<std::is_floating_point<T>::value, T> next_smaller(T v) {
+ DCHECK(!std::isnan(v));
+ DCHECK_LT(-std::numeric_limits<T>::infinity(), v);
+ return std::nextafter(v, -std::numeric_limits<T>::infinity());
+}
+
+template <typename T>
+std::enable_if_t<std::is_floating_point<T>::value, T> next_larger(T v) {
+ DCHECK(!std::isnan(v));
+ DCHECK_LT(v, std::numeric_limits<T>::infinity());
+ return std::nextafter(v, std::numeric_limits<T>::infinity());
+}
+
+template <typename T>
+std::enable_if_t<std::is_integral<T>::value, T> next_smaller(T v) {
+ DCHECK_LT(std::numeric_limits<T>::min(), v);
+ return v - 1;
+}
+
+template <typename T>
+std::enable_if_t<std::is_integral<T>::value, T> next_larger(T v) {
+ DCHECK_LT(v, std::numeric_limits<T>::max());
+ return v + 1;
+}
+
+template <size_t Bits>
+using uint_type = typename detail::TypeForBits<Bits>::uint_type;
+template <size_t Bits>
+using float_type = typename detail::TypeForBits<Bits>::float_type;
+template <size_t Bits>
+constexpr float_type<Bits> nan_v = detail::TypeForBits<Bits>::nan;
+
+template <size_t Bits>
+class WordType;
+template <size_t Bits>
+class FloatType;
+class TupleType;
+
+using Word32Type = WordType<32>;
+using Word64Type = WordType<64>;
+using Float32Type = FloatType<32>;
+using Float64Type = FloatType<64>;
+
+class V8_EXPORT_PRIVATE Type {
+ public:
+ enum class Kind : uint8_t {
+ kInvalid,
+ kNone,
+ kWord32,
+ kWord64,
+ kFloat32,
+ kFloat64,
+ kTuple,
+ kAny,
+ };
+
+ // Some operations cannot express the result precisely in a type, e.g. when an
+ // intersection with a wrapping range may produce to disconnect subranges,
+ // which cannot be represented. {ResolutionMode} allows to specify what the
+ // operation should do when the result cannot be represented precisely.
+ enum class ResolutionMode {
+ // Return Type::Invalid().
+ kPreciseOrInvalid,
+ // Return a safe over approximation.
+ kOverApproximate,
+ // Return the greatest lower bound that can be represented.
+ kGreatestLowerBound,
+ };
+
+ Type() : Type(Kind::kInvalid) {}
+
+ // Type constructors
+ static inline Type Invalid() { return Type(); }
+ static inline Type None() { return Type(Kind::kNone); }
+ static inline Type Any() { return Type(Kind::kAny); }
+
+ // Checks and casts
+ inline Kind kind() const { return kind_; }
+ inline bool IsInvalid() const { return kind_ == Kind::kInvalid; }
+ inline bool IsNone() const { return kind_ == Kind::kNone; }
+ inline bool IsWord32() const { return kind_ == Kind::kWord32; }
+ inline bool IsWord64() const { return kind_ == Kind::kWord64; }
+ inline bool IsFloat32() const { return kind_ == Kind::kFloat32; }
+ inline bool IsFloat64() const { return kind_ == Kind::kFloat64; }
+ inline bool IsTuple() const { return kind_ == Kind::kTuple; }
+ inline bool IsAny() const { return kind_ == Kind::kAny; }
+ template <size_t B>
+ inline bool IsWord() const {
+ if constexpr (B == 32)
+ return IsWord32();
+ else
+ return IsWord64();
+ }
+
+ // Casts
+ inline const Word32Type& AsWord32() const;
+ inline const Word64Type& AsWord64() const;
+ inline const Float32Type& AsFloat32() const;
+ inline const Float64Type& AsFloat64() const;
+ inline const TupleType& AsTuple() const;
+ template <size_t B>
+ inline const auto& AsWord() const {
+ if constexpr (B == 32)
+ return AsWord32();
+ else
+ return AsWord64();
+ }
+
+ // Comparison
+ bool Equals(const Type& other) const;
+ bool IsSubtypeOf(const Type& other) const;
+
+ // Printing
+ void PrintTo(std::ostream& stream) const;
+ void Print() const;
+ std::string ToString() const {
+ std::stringstream stream;
+ PrintTo(stream);
+ return stream.str();
+ }
+
+ // Other functions
+ static Type LeastUpperBound(const Type& lhs, const Type& rhs, Zone* zone);
+ static base::Optional<Type> ParseFromString(const std::string_view& str,
+ Zone* zone);
+ Handle<TurboshaftType> AllocateOnHeap(Factory* factory) const;
+
+ protected:
+ template <typename Payload>
+ Type(Kind kind, uint8_t sub_kind, uint8_t set_size, uint32_t bitfield,
+ uint8_t reserved, const Payload& payload)
+ : kind_(kind),
+ sub_kind_(sub_kind),
+ set_size_(set_size),
+ reserved_(reserved),
+ bitfield_(bitfield) {
+ static_assert(sizeof(Payload) <= sizeof(payload_));
+ memcpy(&payload_[0], &payload, sizeof(Payload));
+ if constexpr (sizeof(Payload) < sizeof(payload_)) {
+ memset(reinterpret_cast<uint8_t*>(&payload_[0]) + sizeof(Payload), 0x00,
+ sizeof(payload_) - sizeof(Payload));
+ }
+ }
+
+ template <typename Payload>
+ const Payload& get_payload() const {
+ static_assert(sizeof(Payload) <= sizeof(payload_));
+ return *reinterpret_cast<const Payload*>(&payload_[0]);
+ }
+
+ union {
+ struct {
+ Kind kind_;
+ uint8_t sub_kind_;
+ uint8_t set_size_;
+ uint8_t reserved_;
+ uint32_t bitfield_;
+ };
+ // {header_} can be used for faster hashing or comparison.
+ uint64_t header_;
+ };
+
+ private:
+ // Access through get_payload<>().
+ uint64_t payload_[2]; // Type specific data
+
+ friend struct fast_hash<Type>;
+ explicit Type(Kind kind) : Type(kind, 0, 0, 0, 0, detail::Payload_Empty{}) {
+ DCHECK(kind == Kind::kInvalid || kind == Kind::kNone || kind == Kind::kAny);
+ }
+};
+static_assert(sizeof(Type) == 24);
+
+template <size_t Bits>
+class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) WordType : public Type {
+ static_assert(Bits == 32 || Bits == 64);
+ friend class Type;
+ static constexpr int kMaxInlineSetSize = 2;
+
+ enum class SubKind : uint8_t {
+ kRange,
+ kSet,
+ };
+
+ public:
+ static constexpr int kMaxSetSize = 8;
+ using word_t = uint_type<Bits>;
+ using value_type = word_t;
+
+ // Constructors
+ static WordType Any() {
+ return Range(0, std::numeric_limits<word_t>::max(), nullptr);
+ }
+ static WordType Range(word_t from, word_t to, Zone* zone) {
+ // Normalize ranges smaller than {kMaxSetSize} to sets.
+ if (to >= from) {
+ // (to - from + 1) <= kMaxSetSize
+ if (to - from <= kMaxSetSize - 1) {
+ // Normalizing non-wrapping ranges to a Set.
+ base::SmallVector<word_t, kMaxSetSize> elements;
+ for (word_t i = from; i < to; ++i) elements.push_back(i);
+ elements.push_back(to);
+ return Set(elements, zone);
+ }
+ } else {
+ // (max - from + 1) + (to + 1) <= kMaxSetSize
+ if ((std::numeric_limits<word_t>::max() - from + to) <= kMaxSetSize - 2) {
+ // Normalizing wrapping ranges to a Set.
+ base::SmallVector<word_t, kMaxSetSize> elements;
+ for (word_t i = from; i < std::numeric_limits<word_t>::max(); ++i) {
+ elements.push_back(i);
+ }
+ elements.push_back(std::numeric_limits<word_t>::max());
+ for (word_t i = 0; i < to; ++i) elements.push_back(i);
+ elements.push_back(to);
+ base::sort(elements);
+ return Set(elements, zone);
+ }
+ }
+ return WordType{SubKind::kRange, 0, Payload_Range{from, to}};
+ }
+ template <size_t N>
+ static WordType Set(const base::SmallVector<word_t, N>& elements,
+ Zone* zone) {
+ return Set(base::Vector<const word_t>{elements.data(), elements.size()},
+ zone);
+ }
+ static WordType Set(const std::vector<word_t>& elements, Zone* zone) {
+ return Set(base::Vector<const word_t>{elements.data(), elements.size()},
+ zone);
+ }
+ static WordType Set(const std::initializer_list<word_t>& elements,
+ Zone* zone) {
+ return Set(base::Vector<const word_t>{elements.begin(), elements.size()},
+ zone);
+ }
+ static WordType Set(const base::Vector<const word_t>& elements, Zone* zone) {
+ DCHECK(detail::is_unique_and_sorted(elements));
+ DCHECK_IMPLIES(elements.size() > kMaxInlineSetSize, zone != nullptr);
+ DCHECK_GT(elements.size(), 0);
+ DCHECK_LE(elements.size(), kMaxSetSize);
+
+ if (elements.size() <= kMaxInlineSetSize) {
+ // Use inline storage.
+ Payload_InlineSet p;
+ DCHECK_LT(0, elements.size());
+ p.elements[0] = elements[0];
+ if (elements.size() > 1) p.elements[1] = elements[1];
+ return WordType{SubKind::kSet, static_cast<uint8_t>(elements.size()), p};
+ } else {
+ // Allocate storage in the zone.
+ Payload_OutlineSet p;
+ p.array = zone->NewArray<word_t>(elements.size());
+ DCHECK_NOT_NULL(p.array);
+ for (size_t i = 0; i < elements.size(); ++i) p.array[i] = elements[i];
+ return WordType{SubKind::kSet, static_cast<uint8_t>(elements.size()), p};
+ }
+ }
+ static WordType Constant(word_t constant) { return Set({constant}, nullptr); }
+
+ // Checks
+ bool is_range() const { return sub_kind() == SubKind::kRange; }
+ bool is_set() const { return sub_kind() == SubKind::kSet; }
+ bool is_any() const { return is_range() && range_to() + 1 == range_from(); }
+ bool is_constant() const {
+ DCHECK_EQ(set_size_ > 0, is_set());
+ return set_size_ == 1;
+ }
+ bool is_wrapping() const { return is_range() && range_from() > range_to(); }
+
+ // Accessors
+ word_t range_from() const {
+ DCHECK(is_range());
+ return get_payload<Payload_Range>().min;
+ }
+ word_t range_to() const {
+ DCHECK(is_range());
+ return get_payload<Payload_Range>().max;
+ }
+ std::pair<word_t, word_t> range() const {
+ DCHECK(is_range());
+ return {range_from(), range_to()};
+ }
+ int set_size() const {
+ DCHECK(is_set());
+ return static_cast<int>(set_size_);
+ }
+ word_t set_element(int index) const {
+ DCHECK(is_set());
+ DCHECK_GE(index, 0);
+ DCHECK_LT(index, set_size());
+ return set_elements()[index];
+ }
+ base::Vector<const word_t> set_elements() const {
+ DCHECK(is_set());
+ if (set_size() <= kMaxInlineSetSize) {
+ return base::Vector<const word_t>(
+ get_payload<Payload_InlineSet>().elements, set_size());
+ } else {
+ return base::Vector<const word_t>(get_payload<Payload_OutlineSet>().array,
+ set_size());
+ }
+ }
+ base::Optional<word_t> try_get_constant() const {
+ if (!is_constant()) return base::nullopt;
+ DCHECK(is_set());
+ DCHECK_EQ(set_size(), 1);
+ return set_element(0);
+ }
+ bool is_constant(word_t value) const {
+ if (auto c = try_get_constant()) return *c == value;
+ return false;
+ }
+ word_t unsigned_min() const {
+ switch (sub_kind()) {
+ case SubKind::kRange:
+ return is_wrapping() ? word_t{0} : range_from();
+ case SubKind::kSet:
+ return set_element(0);
+ }
+ }
+ word_t unsigned_max() const {
+ switch (sub_kind()) {
+ case SubKind::kRange:
+ return is_wrapping() ? std::numeric_limits<word_t>::max() : range_to();
+ case SubKind::kSet:
+ DCHECK_GE(set_size(), 1);
+ return set_element(set_size() - 1);
+ }
+ }
+
+ // Misc
+ bool Contains(word_t value) const;
+ bool Equals(const WordType& other) const;
+ bool IsSubtypeOf(const WordType& other) const;
+ static WordType LeastUpperBound(const WordType& lhs, const WordType& rhs,
+ Zone* zone);
+ static Type Intersect(const WordType& lhs, const WordType& rhs,
+ ResolutionMode resolution_mode, Zone* zone);
+ void PrintTo(std::ostream& stream) const;
+ Handle<TurboshaftType> AllocateOnHeap(Factory* factory) const;
+
+ private:
+ static constexpr Kind KIND = Bits == 32 ? Kind::kWord32 : Kind::kWord64;
+ using Payload_Range = detail::Payload_Range<word_t>;
+ using Payload_InlineSet = detail::Payload_InlineSet<word_t>;
+ using Payload_OutlineSet = detail::Payload_OutlineSet<word_t>;
+
+ SubKind sub_kind() const { return static_cast<SubKind>(sub_kind_); }
+ template <typename Payload>
+ WordType(SubKind sub_kind, uint8_t set_size, const Payload& payload)
+ : Type(KIND, static_cast<uint8_t>(sub_kind), set_size, 0, 0, payload) {}
+};
+
+template <size_t Bits>
+class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FloatType : public Type {
+ static_assert(Bits == 32 || Bits == 64);
+ friend class Type;
+ static constexpr int kMaxInlineSetSize = 2;
+
+ enum class SubKind : uint8_t {
+ kRange,
+ kSet,
+ kOnlySpecialValues,
+ };
+
+ public:
+ static constexpr int kMaxSetSize = 8;
+ using float_t = float_type<Bits>;
+ using value_type = float_t;
+
+ enum Special : uint32_t {
+ kNoSpecialValues = 0x0,
+ kNaN = 0x1,
+ kMinusZero = 0x2,
+ };
+
+ // Constructors
+ static FloatType OnlySpecialValues(uint32_t special_values) {
+ DCHECK_NE(0, special_values);
+ return FloatType{SubKind::kOnlySpecialValues, 0, special_values,
+ Payload_OnlySpecial{}};
+ }
+ static FloatType NaN() {
+ return FloatType{SubKind::kOnlySpecialValues, 0, Special::kNaN,
+ Payload_OnlySpecial{}};
+ }
+ static FloatType MinusZero() {
+ return FloatType{SubKind::kOnlySpecialValues, 0, Special::kMinusZero,
+ Payload_OnlySpecial{}};
+ }
+ static FloatType Any(uint32_t special_values = Special::kNaN |
+ Special::kMinusZero) {
+ return FloatType::Range(-std::numeric_limits<float_t>::infinity(),
+ std::numeric_limits<float_t>::infinity(),
+ special_values, nullptr);
+ }
+ static FloatType Range(float_t min, float_t max, Zone* zone) {
+ return Range(min, max, Special::kNoSpecialValues, zone);
+ }
+ static FloatType Range(float_t min, float_t max, uint32_t special_values,
+ Zone* zone) {
+ special_values |= IdentifyMinusZero(min);
+ special_values |= IdentifyMinusZero(max);
+ DCHECK(!detail::is_float_special_value(min));
+ DCHECK(!detail::is_float_special_value(max));
+ DCHECK_LE(min, max);
+ if (min == max) return Set({min}, zone);
+ return FloatType{SubKind::kRange, 0, special_values,
+ Payload_Range{min, max}};
+ }
+ template <size_t N>
+ static FloatType Set(const base::SmallVector<const float_t, N>& elements,
+ Zone* zone) {
+ return Set(elements, Special::kNoSpecialValues, zone);
+ }
+ template <size_t N>
+ static FloatType Set(const base::SmallVector<float_t, N>& elements,
+ uint32_t special_values, Zone* zone) {
+ return Set(base::Vector<const float_t>{elements.data(), elements.size()},
+ special_values, zone);
+ }
+ static FloatType Set(const std::initializer_list<float_t>& elements,
+ uint32_t special_values, Zone* zone) {
+ return Set(base::Vector<const float_t>{elements.begin(), elements.size()},
+ special_values, zone);
+ }
+ static FloatType Set(const std::vector<float_t>& elements, Zone* zone) {
+ return Set(elements, Special::kNoSpecialValues, zone);
+ }
+ static FloatType Set(const std::vector<float_t>& elements,
+ uint32_t special_values, Zone* zone) {
+ return Set(base::Vector<const float_t>{elements.data(), elements.size()},
+ special_values, zone);
+ }
+ static FloatType Set(const base::Vector<const float_t>& elements,
+ uint32_t special_values, Zone* zone) {
+ DCHECK(detail::is_unique_and_sorted(elements));
+ // NaN should be passed via {special_values} rather than {elements}.
+ DCHECK(base::none_of(elements, [](float_t f) { return std::isnan(f); }));
+ DCHECK_IMPLIES(elements.size() > kMaxInlineSetSize, zone != nullptr);
+ DCHECK_GT(elements.size(), 0);
+ DCHECK_LE(elements.size(), kMaxSetSize);
+
+ if (elements.size() <= kMaxInlineSetSize) {
+ // Use inline storage.
+ Payload_InlineSet p;
+ DCHECK_LT(0, elements.size());
+ p.elements[0] = elements[0];
+ special_values |= IdentifyMinusZero(p.elements[0]);
+ if (elements.size() > 1) {
+ p.elements[1] = elements[1];
+ special_values |= IdentifyMinusZero(p.elements[1]);
+ }
+ return FloatType{SubKind::kSet, static_cast<uint8_t>(elements.size()),
+ special_values, p};
+ } else {
+ // Allocate storage in the zone.
+ Payload_OutlineSet p;
+ p.array = zone->NewArray<float_t>(elements.size());
+ DCHECK_NOT_NULL(p.array);
+ for (size_t i = 0; i < elements.size(); ++i) {
+ p.array[i] = elements[i];
+ special_values |= IdentifyMinusZero(p.array[i]);
+ }
+ return FloatType{SubKind::kSet, static_cast<uint8_t>(elements.size()),
+ special_values, p};
+ }
+ }
+ static FloatType Constant(float_t constant) {
+ return Set({constant}, 0, nullptr);
+ }
+
+ // Checks
+ bool is_only_special_values() const {
+ return sub_kind() == SubKind::kOnlySpecialValues;
+ }
+ bool is_only_nan() const {
+ return is_only_special_values() && (special_values() == Special::kNaN);
+ }
+ bool is_only_minus_zero() const {
+ return is_only_special_values() &&
+ (special_values() == Special::kMinusZero);
+ }
+ bool is_range() const { return sub_kind() == SubKind::kRange; }
+ bool is_set() const { return sub_kind() == SubKind::kSet; }
+ bool is_any() const {
+ return is_range() &&
+ range_min() == -std::numeric_limits<float_t>::infinity() &&
+ range_max() == std::numeric_limits<float_t>::infinity();
+ }
+ bool is_constant() const {
+ DCHECK_EQ(set_size_ > 0, is_set());
+ return set_size_ == 1 && !has_special_values();
+ }
+ uint32_t special_values() const { return bitfield_; }
+ bool has_special_values() const { return special_values() != 0; }
+ bool has_nan() const { return (special_values() & Special::kNaN) != 0; }
+ bool has_minus_zero() const {
+ return (special_values() & Special::kMinusZero) != 0;
+ }
+
+ // Accessors
+ float_t range_min() const {
+ DCHECK(is_range());
+ return get_payload<Payload_Range>().min;
+ }
+ float_t range_max() const {
+ DCHECK(is_range());
+ return get_payload<Payload_Range>().max;
+ }
+ std::pair<float_t, float_t> range() const {
+ DCHECK(is_range());
+ return {range_min(), range_max()};
+ }
+ int set_size() const {
+ DCHECK(is_set());
+ return static_cast<int>(set_size_);
+ }
+ float_t set_element(int index) const {
+ DCHECK(is_set());
+ DCHECK_GE(index, 0);
+ DCHECK_LT(index, set_size());
+ return set_elements()[index];
+ }
+ base::Vector<const float_t> set_elements() const {
+ DCHECK(is_set());
+ if (set_size() <= kMaxInlineSetSize) {
+ return base::Vector<const float_t>(
+ get_payload<Payload_InlineSet>().elements, set_size());
+ } else {
+ return base::Vector<const float_t>(
+ get_payload<Payload_OutlineSet>().array, set_size());
+ }
+ }
+ float_t min() const {
+ switch (sub_kind()) {
+ case SubKind::kOnlySpecialValues:
+ if (has_minus_zero()) return float_t{-0.0};
+ DCHECK(is_only_nan());
+ return nan_v<Bits>;
+ case SubKind::kRange:
+ if (has_minus_zero()) return std::min(float_t{-0.0}, range_min());
+ return range_min();
+ case SubKind::kSet:
+ if (has_minus_zero()) return std::min(float_t{-0.0}, set_element(0));
+ return set_element(0);
+ }
+ }
+ float_t max() const {
+ switch (sub_kind()) {
+ case SubKind::kOnlySpecialValues:
+ if (has_minus_zero()) return float_t{-0.0};
+ DCHECK(is_only_nan());
+ return nan_v<Bits>;
+ case SubKind::kRange:
+ if (has_minus_zero()) return std::max(float_t{-0.0}, range_max());
+ return range_max();
+ case SubKind::kSet:
+ if (has_minus_zero()) {
+ return std::max(float_t{-0.0}, set_element(set_size() - 1));
+ }
+ return set_element(set_size() - 1);
+ }
+ }
+ std::pair<float_t, float_t> minmax() const { return {min(), max()}; }
+ base::Optional<float_t> try_get_constant() const {
+ if (!is_constant()) return base::nullopt;
+ DCHECK(is_set());
+ DCHECK_EQ(set_size(), 1);
+ return set_element(0);
+ }
+ bool is_constant(float_t value) const {
+ if (V8_UNLIKELY(std::isnan(value))) return is_only_nan();
+ if (V8_UNLIKELY(IsMinusZero(value))) return is_only_minus_zero();
+ if (auto c = try_get_constant()) return *c == value;
+ return false;
+ }
+ // Returns the minimium value of a range or set, ignoring any special values
+ // (in contrast to min() above).
+ float_t range_or_set_min() const {
+ switch (sub_kind()) {
+ case SubKind::kOnlySpecialValues:
+ UNREACHABLE();
+ case SubKind::kRange:
+ return range_min();
+ case SubKind::kSet:
+ return set_element(0);
+ }
+ }
+ // Returns the maximum value of a range or set, ignoring any special values
+ // (in contrast to max() above).
+ float_t range_or_set_max() const {
+ switch (sub_kind()) {
+ case SubKind::kOnlySpecialValues:
+ UNREACHABLE();
+ case SubKind::kRange:
+ return range_max();
+ case SubKind::kSet:
+ return set_element(set_size() - 1);
+ }
+ }
+ std::pair<float_t, float_t> range_or_set_minmax() const {
+ return {range_or_set_min(), range_or_set_max()};
+ }
+
+ // Misc
+ bool Contains(float_t value) const;
+ bool Equals(const FloatType& other) const;
+ bool IsSubtypeOf(const FloatType& other) const;
+ static FloatType LeastUpperBound(const FloatType& lhs, const FloatType& rhs,
+ Zone* zone);
+ static Type Intersect(const FloatType& lhs, const FloatType& rhs, Zone* zone);
+ void PrintTo(std::ostream& stream) const;
+ Handle<TurboshaftType> AllocateOnHeap(Factory* factory) const;
+
+ private:
+ // This helper turns a -0 into a 0 in {value} and returns the
+ // Special::kMinusZero flag in that case. Otherwise the {value} is unchanged
+ // and Special::kNoSpecialValues is returned.
+ static uint32_t IdentifyMinusZero(float_t& value) {
+ if (V8_UNLIKELY(detail::is_minus_zero(value))) {
+ value = float_t{0};
+ return Special::kMinusZero;
+ }
+ return Special::kNoSpecialValues;
+ }
+ static FloatType ReplacedSpecialValues(const FloatType& t,
+ uint32_t special_values) {
+ auto result = t;
+ result.bitfield_ = special_values;
+ DCHECK_EQ(result.bitfield_, result.special_values());
+ return result;
+ }
+
+ static constexpr Kind KIND = Bits == 32 ? Kind::kFloat32 : Kind::kFloat64;
+ SubKind sub_kind() const { return static_cast<SubKind>(sub_kind_); }
+ using Payload_Range = detail::Payload_Range<float_t>;
+ using Payload_InlineSet = detail::Payload_InlineSet<float_t>;
+ using Payload_OutlineSet = detail::Payload_OutlineSet<float_t>;
+ using Payload_OnlySpecial = detail::Payload_Empty;
+
+ template <typename Payload>
+ FloatType(SubKind sub_kind, uint8_t set_size, uint32_t special_values,
+ const Payload& payload)
+ : Type(KIND, static_cast<uint8_t>(sub_kind), set_size, special_values, 0,
+ payload) {
+ DCHECK_EQ(special_values & ~(Special::kNaN | Special::kMinusZero), 0);
+ }
+};
+
+class TupleType : public Type {
+ public:
+ static constexpr int kMaxTupleSize = std::numeric_limits<uint8_t>::max();
+
+ // Constructors
+ static TupleType Tuple(const Type& element0, const Type& element1,
+ Zone* zone) {
+ Payload p;
+ p.array = zone->NewArray<Type>(2);
+ DCHECK_NOT_NULL(p.array);
+ p.array[0] = element0;
+ p.array[1] = element1;
+ return TupleType{2, p};
+ }
+
+ static TupleType Tuple(const base::Vector<Type>& elements, Zone* zone) {
+ DCHECK_LE(elements.size(), kMaxTupleSize);
+ Payload p;
+ p.array = zone->NewArray<Type>(elements.size());
+ DCHECK_NOT_NULL(p.array);
+ for (size_t i = 0; i < elements.size(); ++i) {
+ p.array[i] = elements[i];
+ }
+ return TupleType{static_cast<uint8_t>(elements.size()), p};
+ }
+
+ // Accessors
+ int size() const { return static_cast<int>(set_size_); }
+ const Type& element(int index) const {
+ DCHECK_LE(0, index);
+ DCHECK_LT(index, size());
+ return get_payload<Payload>().array[index];
+ }
+ base::Vector<Type> elements() const {
+ return base::Vector<Type>{get_payload<Payload>().array,
+ static_cast<size_t>(size())};
+ }
+
+ // Misc
+ bool Equals(const TupleType& other) const;
+ bool IsSubtypeOf(const TupleType& other) const;
+ static Type LeastUpperBound(const TupleType& lhs, const TupleType& rhs,
+ Zone* zone);
+ void PrintTo(std::ostream& stream) const;
+
+ private:
+ static constexpr Kind KIND = Kind::kTuple;
+ using Payload = detail::Payload_OutlineSet<Type>;
+
+ TupleType(uint8_t tuple_size, const Payload& payload)
+ : Type(KIND, 0, tuple_size, 0, 0, payload) {}
+};
+
+const Word32Type& Type::AsWord32() const {
+ DCHECK(IsWord32());
+ return *static_cast<const Word32Type*>(this);
+}
+
+const Word64Type& Type::AsWord64() const {
+ DCHECK(IsWord64());
+ return *static_cast<const Word64Type*>(this);
+}
+
+const Float32Type& Type::AsFloat32() const {
+ DCHECK(IsFloat32());
+ return *static_cast<const Float32Type*>(this);
+}
+
+const Float64Type& Type::AsFloat64() const {
+ DCHECK(IsFloat64());
+ return *static_cast<const Float64Type*>(this);
+}
+
+const TupleType& Type::AsTuple() const {
+ DCHECK(IsTuple());
+ return *static_cast<const TupleType*>(this);
+}
+
+inline std::ostream& operator<<(std::ostream& stream, Type::Kind kind) {
+ switch (kind) {
+ case Type::Kind::kInvalid:
+ return stream << "Invalid";
+ case Type::Kind::kNone:
+ return stream << "None";
+ case Type::Kind::kWord32:
+ return stream << "Word32";
+ case Type::Kind::kWord64:
+ return stream << "Word64";
+ case Type::Kind::kFloat32:
+ return stream << "Float32";
+ case Type::Kind::kFloat64:
+ return stream << "Float64";
+ case Type::Kind::kTuple:
+ return stream << "Tuple";
+ case Type::Kind::kAny:
+ return stream << "Any";
+ }
+}
+
+inline std::ostream& operator<<(std::ostream& stream, const Type& type) {
+ type.PrintTo(stream);
+ return stream;
+}
+
+inline bool operator==(const Type& lhs, const Type& rhs) {
+ return lhs.Equals(rhs);
+}
+
+template <>
+struct fast_hash<Type> {
+ size_t operator()(const Type& v) const {
+ // TODO(nicohartmann@): Fix fast_hash for outline payload once this is
+ // required.
+ UNREACHABLE();
+ // return fast_hash_combine(v.header_, v.payload_[0], v.payload_[1]);
+ }
+};
+
+// The below exports of the explicitly instantiated template instances produce
+// build errors on v8_linux64_gcc_light_compile_dbg build with
+//
+// error: type attributes ignored after type is already defined
+// [-Werror=attributes] extern template class
+// EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) WordType<32>;
+//
+// No combination of export macros seems to be able to resolve this issue
+// although they seem to work for other classes. A temporary workaround is to
+// disable this warning here locally.
+// TODO(nicohartmann@): Ideally, we would find a better solution than to disable
+// the warning.
+#if V8_CC_GNU
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+#endif // V8_CC_GNU
+
+extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) WordType<32>;
+extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) WordType<64>;
+extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FloatType<32>;
+extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FloatType<64>;
+
+#if V8_CC_GNU
+#pragma GCC diagnostic pop
+#endif // V8_CC_GNU
+
+} // namespace v8::internal::compiler::turboshaft
+
+#endif // V8_COMPILER_TURBOSHAFT_TYPES_H_