%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/turboshaft/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/compiler/turboshaft/index.h |
// 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_INDEX_H_ #define V8_COMPILER_TURBOSHAFT_INDEX_H_ #include <cstddef> #include <type_traits> #include "src/base/logging.h" #include "src/codegen/tnode.h" #include "src/compiler/turboshaft/fast-hash.h" #include "src/compiler/turboshaft/representations.h" #include "src/objects/heap-number.h" #include "src/objects/oddball.h" #include "src/objects/string.h" #include "src/objects/tagged.h" namespace v8::internal::compiler::turboshaft { namespace detail { template <typename T> struct lazy_false : std::false_type {}; } // namespace detail // Operations are stored in possibly muliple sequential storage slots. using OperationStorageSlot = std::aligned_storage_t<8, 8>; // Operations occupy at least 2 slots, therefore we assign one id per two slots. constexpr size_t kSlotsPerId = 2; template <typename T, typename C> class ConstOrV; // `OpIndex` is an offset from the beginning of the operations buffer. // Compared to `Operation*`, it is more memory efficient (32bit) and stable when // the operations buffer is re-allocated. class OpIndex { public: explicit constexpr OpIndex(uint32_t offset) : offset_(offset) { DCHECK(CheckInvariants()); } constexpr OpIndex() : offset_(std::numeric_limits<uint32_t>::max()) {} template <typename T, typename C> OpIndex(const ConstOrV<T, C>&) { // NOLINT(runtime/explicit) static_assert(detail::lazy_false<T>::value, "Cannot initialize OpIndex from ConstOrV<>. Did you forget " "to resolve() it in the assembler?"); } uint32_t id() const { // Operations are stored at an offset that's a multiple of // `sizeof(OperationStorageSlot)`. In addition, an operation occupies at // least `kSlotsPerId` many `OperationSlot`s. Therefore, we can assign id's // by dividing by `kSlotsPerId`. A compact id space is important, because it // makes side-tables smaller. DCHECK(CheckInvariants()); return offset_ / sizeof(OperationStorageSlot) / kSlotsPerId; } uint32_t hash() const { // It can be useful to hash OpIndex::Invalid(), so we have this `hash` // function, which returns the id, but without DCHECKing that Invalid is // valid. DCHECK_IMPLIES(valid(), CheckInvariants()); return offset_ / sizeof(OperationStorageSlot) / kSlotsPerId; } uint32_t offset() const { DCHECK(CheckInvariants()); #ifdef DEBUG return offset_ & kUnmaskGenerationMask; #else return offset_; #endif } constexpr bool valid() const { return *this != Invalid(); } static constexpr OpIndex Invalid() { return OpIndex(); } // Encode a sea-of-nodes node id in the `OpIndex` type. // Only used for node origins that actually point to sea-of-nodes graph nodes. static OpIndex EncodeTurbofanNodeId(uint32_t id) { OpIndex result = OpIndex(id * sizeof(OperationStorageSlot)); result.offset_ += kTurbofanNodeIdFlag; return result; } uint32_t DecodeTurbofanNodeId() const { DCHECK(IsTurbofanNodeId()); return offset_ / sizeof(OperationStorageSlot); } bool IsTurbofanNodeId() const { return offset_ % sizeof(OperationStorageSlot) == kTurbofanNodeIdFlag; } constexpr bool operator==(OpIndex other) const { return offset_ == other.offset_; } constexpr bool operator!=(OpIndex other) const { return offset_ != other.offset_; } constexpr bool operator<(OpIndex other) const { return offset_ < other.offset_; } constexpr bool operator>(OpIndex other) const { return offset_ > other.offset_; } constexpr bool operator<=(OpIndex other) const { return offset_ <= other.offset_; } constexpr bool operator>=(OpIndex other) const { return offset_ >= other.offset_; } #ifdef DEBUG int generation_mod2() const { return (offset_ & kGenerationMask) >> kGenerationMaskShift; } void set_generation_mod2(int generation_mod2) { DCHECK_LE(generation_mod2, 1); offset_ |= generation_mod2 << kGenerationMaskShift; } constexpr bool CheckInvariants() const { DCHECK(valid()); // The second lowest significant bit of the offset is used to store the // graph generation modulo 2. The lowest and 3rd lowest bits should always // be 0 (as long as sizeof(OperationStorageSlot) is 8). static_assert(sizeof(OperationStorageSlot) == 8); return (offset_ & 0b101) == 0; } #endif private: static constexpr uint32_t kGenerationMaskShift = 1; static constexpr uint32_t kGenerationMask = 1 << kGenerationMaskShift; static constexpr uint32_t kUnmaskGenerationMask = ~kGenerationMask; // In DEBUG builds, the offset's second lowest bit contains the graph // generation % 2, so one should keep this in mind when looking at the value // of the offset. uint32_t offset_; static constexpr uint32_t kTurbofanNodeIdFlag = 1; }; std::ostream& operator<<(std::ostream& os, OpIndex idx); // Dummy value for abstract representation classes that don't have a // RegisterRepresentation. struct nullrep_t {}; constexpr nullrep_t nullrep; // Abstract tag classes for V<>. struct Any {}; template <size_t Bits> struct WordWithBits : public Any { static constexpr int bits = Bits; static_assert(Bits == 32 || Bits == 64 || Bits == 128); }; using Word32 = WordWithBits<32>; using Word64 = WordWithBits<64>; using WordPtr = std::conditional_t<Is64(), Word64, Word32>; template <size_t Bits> struct FloatWithBits : public Any { // FloatAny { static constexpr int bits = Bits; static_assert(Bits == 32 || Bits == 64); }; using Float32 = FloatWithBits<32>; using Float64 = FloatWithBits<64>; using Simd128 = WordWithBits<128>; // TODO(nicohartmann@): Replace all uses of `V<Tagged>` by `V<Object>`. using Tagged = Object; struct Compressed : public Any {}; // Traits classes `v_traits<T>` to provide additional T-specific information for // V<T> and ConstOrV<T>. If you need to provide non-default conversion behavior // for a specific type, specialize the corresponding v_traits<>. template <typename T, typename = void> struct v_traits; template <> struct v_traits<Any> { static constexpr bool is_abstract_tag = true; static constexpr auto rep = nullrep; static constexpr bool allows_representation(RegisterRepresentation rep) { return true; } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_same_v<U, Any>> {}; }; template <> struct v_traits<Compressed> { static constexpr bool is_abstract_tag = true; static constexpr auto rep = nullrep; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Compressed(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Compressed>> {}; }; template <> struct v_traits<Word32> { static constexpr bool is_abstract_tag = true; static constexpr WordRepresentation rep = WordRepresentation::Word32(); using constexpr_type = uint32_t; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Word32(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Word32>> {}; }; template <> struct v_traits<Word64> { static constexpr bool is_abstract_tag = true; static constexpr WordRepresentation rep = WordRepresentation::Word64(); using constexpr_type = uint64_t; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Word64(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Word64>> {}; }; template <> struct v_traits<Float32> { static constexpr bool is_abstract_tag = true; static constexpr FloatRepresentation rep = FloatRepresentation::Float32(); using constexpr_type = float; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Float32(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Float32>> {}; }; template <> struct v_traits<Float64> { static constexpr bool is_abstract_tag = true; static constexpr FloatRepresentation rep = FloatRepresentation::Float64(); using constexpr_type = double; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Float64(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Float64>> {}; }; template <> struct v_traits<Simd128> { static constexpr bool is_abstract_tag = true; static constexpr RegisterRepresentation rep = RegisterRepresentation::Simd128(); using constexpr_type = uint8_t[kSimd128Size]; static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Simd128(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_base_of_v<U, Simd128>> {}; }; template <typename T> struct v_traits<T, std::enable_if_t<is_taggable_v<T>>> { static constexpr bool is_abstract_tag = false; static constexpr auto rep = RegisterRepresentation::Tagged(); static constexpr bool allows_representation(RegisterRepresentation rep) { return rep == RegisterRepresentation::Tagged(); } template <typename U> struct implicitly_convertible_to : std::bool_constant<std::is_same_v<U, Any> || is_subtype<T, U>::value> { }; }; template <typename T1, typename T2> struct v_traits<UnionT<T1, T2>, std::enable_if_t<is_taggable_v<UnionT<T1, T2>>>> { static_assert(!v_traits<T1>::is_abstract_tag); static_assert(!v_traits<T2>::is_abstract_tag); static constexpr bool is_abstract_tag = false; static_assert(v_traits<T1>::rep == v_traits<T2>::rep); static constexpr auto rep = v_traits<T1>::rep; static constexpr bool allows_representation(RegisterRepresentation r) { return r == rep; } template <typename U> struct implicitly_convertible_to : std::bool_constant<( v_traits<T1>::template implicitly_convertible_to<U>::value || v_traits<T2>::template implicitly_convertible_to<U>::value)> {}; }; using BooleanOrNullOrUndefined = UnionT<UnionT<Boolean, Null>, Undefined>; using NumberOrString = UnionT<Number, String>; using PlainPrimitive = UnionT<NumberOrString, BooleanOrNullOrUndefined>; // V<> represents an SSA-value that is parameterized with the type of the value. // Types from the `Object` hierarchy can be provided as well as the abstract // representation classes (`Word32`, ...) defined above. // Prefer using V<> instead of a plain OpIndex where possible. template <typename T> class V : public OpIndex { public: using type = T; static constexpr auto rep = v_traits<type>::rep; constexpr V() : OpIndex() {} // V<T> is implicitly constructible from plain OpIndex. template <typename U, typename = std::enable_if_t<std::is_same_v<U, OpIndex>>> V(U index) : OpIndex(index) {} // NOLINT(runtime/explicit) // V<T> is implicitly constructible from V<U> iff // `v_traits<U>::implicitly_convertible_to<T>::value`. This is typically the // case if T == U or T is a subclass of U. Different types may specify // different conversion rules in the corresponding `v_traits` when necessary. template <typename U, typename = std::enable_if_t<v_traits< U>::template implicitly_convertible_to<T>::value>> V(V<U> index) : OpIndex(index) {} // NOLINT(runtime/explicit) template <typename U> static V<T> Cast(V<U> index) { return V<T>(OpIndex{index}); } static V<T> Cast(OpIndex index) { return V<T>(index); } static constexpr bool allows_representation(RegisterRepresentation rep) { return v_traits<T>::allows_representation(rep); } }; // ConstOrV<> is a generalization of V<> that allows constexpr values // (constants) to be passed implicitly. This allows reducers to write things // like // // __ Word32Add(value, 1) // // instead of having to write // // __ Word32Add(value, __ Word32Constant(1)) // // which makes overall code more compact and easier to read. Functions need to // call `resolve` on the assembler in order to convert to V<> (which will then // construct the corresponding ConstantOp if the given ConstOrV<> holds a // constexpr value). // NOTICE: `ConstOrV<T>` can only be used if `v_traits<T>` provides a // `constexpr_type`. template <typename T, typename C = typename v_traits<T>::constexpr_type> class ConstOrV { public: using type = T; using constant_type = C; ConstOrV(constant_type value) // NOLINT(runtime/explicit) : constant_value_(value), value_() {} // ConstOrV<T> is implicitly constructible from plain OpIndex. template <typename U, typename = std::enable_if_t<std::is_same_v<U, OpIndex>>> ConstOrV(U index) // NOLINT(runtime/explicit) : constant_value_(), value_(index) {} // ConstOrV<T> is implicitly constructible from V<U> iff V<T> is // constructible from V<U>. template <typename U, typename = std::enable_if_t<std::is_constructible_v<V<T>, V<U>>>> ConstOrV(V<U> index) // NOLINT(runtime/explicit) : constant_value_(), value_(index) {} bool is_constant() const { return constant_value_.has_value(); } constant_type constant_value() const { DCHECK(is_constant()); return *constant_value_; } V<type> value() const { DCHECK(!is_constant()); return value_; } private: base::Optional<constant_type> constant_value_; V<type> value_; }; template <> struct fast_hash<OpIndex> { V8_INLINE size_t operator()(OpIndex op) const { return op.hash(); } }; V8_INLINE size_t hash_value(OpIndex op) { return base::hash_value(op.hash()); } // `BlockIndex` is the index of a bound block. // A dominating block always has a smaller index. // It corresponds to the ordering of basic blocks in the operations buffer. class BlockIndex { public: explicit constexpr BlockIndex(uint32_t id) : id_(id) {} constexpr BlockIndex() : id_(std::numeric_limits<uint32_t>::max()) {} uint32_t id() const { return id_; } bool valid() const { return *this != Invalid(); } static constexpr BlockIndex Invalid() { return BlockIndex(); } bool operator==(BlockIndex other) const { return id_ == other.id_; } bool operator!=(BlockIndex other) const { return id_ != other.id_; } bool operator<(BlockIndex other) const { return id_ < other.id_; } bool operator>(BlockIndex other) const { return id_ > other.id_; } bool operator<=(BlockIndex other) const { return id_ <= other.id_; } bool operator>=(BlockIndex other) const { return id_ >= other.id_; } private: uint32_t id_; }; template <> struct fast_hash<BlockIndex> { V8_INLINE size_t operator()(BlockIndex op) const { return op.id(); } }; V8_INLINE size_t hash_value(BlockIndex op) { return base::hash_value(op.id()); } std::ostream& operator<<(std::ostream& os, BlockIndex b); } // namespace v8::internal::compiler::turboshaft #endif // V8_COMPILER_TURBOSHAFT_INDEX_H_