[turbofan] Extend Type system BitsetType to 64 bit.

Extend BitsetType of TF's type system from 32 to 64 bit.
At the moment all 32 bits are used, so we can't add any new types.
This CL only adds support for > 32 types to C++. The bitset is also
mirrored in Torque. In the Torque definition, we just expose an
unstructured uint32 for the higher bits of the bitfield, because Toruqe
can't deal with 64 bit types on 32 bit platforms (yet) and we also can't
have multiple 1-bit bitfields within a single class (yet).

Bug: v8:12392, chromium:1262750
Change-Id: If571491443e86e4e47eb88d3f15eca485344d12d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3281922
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Commit-Queue: Patrick Thier <pthier@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77962}
This commit is contained in:
Patrick Thier 2021-11-18 08:02:28 +00:00 committed by V8 LUCI CQ
parent 8414adc677
commit c0756abf29
5 changed files with 122 additions and 90 deletions

View File

@ -119,12 +119,12 @@ Type::bitset Type::BitsetLub() const {
if (IsUnion()) {
// Take the representation from the first element, which is always
// a bitset.
int bitset = AsUnion()->Get(0).BitsetLub();
bitset lub = AsUnion()->Get(0).BitsetLub();
for (int i = 0, n = AsUnion()->Length(); i < n; ++i) {
// Other elements only contribute their semantic part.
bitset |= AsUnion()->Get(i).BitsetLub();
lub |= AsUnion()->Get(i).BitsetLub();
}
return bitset;
return lub;
}
if (IsHeapConstant()) return AsHeapConstant()->Lub();
if (IsOtherNumberConstant()) {
@ -415,7 +415,7 @@ Type::bitset BitsetType::ExpandInternals(Type::bitset bits) {
Type::bitset BitsetType::Lub(double min, double max) {
DisallowGarbageCollection no_gc;
int lub = kNone;
bitset lub = kNone;
const Boundary* mins = Boundaries();
for (size_t i = 1; i < BoundariesSize(); ++i) {
@ -431,7 +431,7 @@ Type::bitset BitsetType::NumberBits(bitset bits) { return bits & kPlainNumber; }
Type::bitset BitsetType::Glb(double min, double max) {
DisallowGarbageCollection no_gc;
int glb = kNone;
bitset glb = kNone;
const Boundary* mins = Boundaries();
// If the range does not touch 0, the bound is empty.
@ -1146,7 +1146,10 @@ std::ostream& operator<<(std::ostream& os, Type type) {
Handle<TurbofanType> Type::AllocateOnHeap(Factory* factory) {
DCHECK(CanBeAsserted());
if (IsBitset()) {
return factory->NewTurbofanBitsetType(AsBitset(), AllocationType::kYoung);
const bitset bits = AsBitset();
uint32_t low = bits & 0xffffffff;
uint32_t high = (bits >> 32) & 0xffffffff;
return factory->NewTurbofanBitsetType(low, high, AllocationType::kYoung);
} else if (IsUnion()) {
const UnionType* union_type = AsUnion();
Handle<TurbofanType> result = union_type->Get(0).AllocateOnHeap(factory);
@ -1171,12 +1174,18 @@ Handle<TurbofanType> Type::AllocateOnHeap(Factory* factory) {
}
}
#define VERIFY_TORQUE_BITSET_AGREEMENT(Name, _) \
#define VERIFY_TORQUE_LOW_BITSET_AGREEMENT(Name, _) \
STATIC_ASSERT(static_cast<uint32_t>(BitsetType::k##Name) == \
static_cast<uint32_t>(TurbofanTypeBits::k##Name));
INTERNAL_BITSET_TYPE_LIST(VERIFY_TORQUE_BITSET_AGREEMENT)
PROPER_ATOMIC_BITSET_TYPE_LIST(VERIFY_TORQUE_BITSET_AGREEMENT)
#undef VERIFY_TORQUE_BITSET_AGREEMENT
static_cast<uint32_t>(TurbofanTypeLowBits::k##Name));
#define VERIFY_TORQUE_HIGH_BITSET_AGREEMENT(Name, _) \
STATIC_ASSERT(static_cast<uint32_t>( \
static_cast<uint64_t>(BitsetType::k##Name) >> 32) == \
static_cast<uint32_t>(TurbofanTypeHighBits::k##Name));
INTERNAL_BITSET_TYPE_LIST(VERIFY_TORQUE_LOW_BITSET_AGREEMENT)
PROPER_ATOMIC_BITSET_TYPE_LOW_LIST(VERIFY_TORQUE_LOW_BITSET_AGREEMENT)
PROPER_ATOMIC_BITSET_TYPE_HIGH_LIST(VERIFY_TORQUE_HIGH_BITSET_AGREEMENT)
#undef VERIFY_TORQUE_HIGH_BITSET_AGREEMENT
#undef VERIFY_TORQUE_LOW_BITSET_AGREEMENT
} // namespace compiler
} // namespace internal

View File

@ -96,45 +96,50 @@ namespace compiler {
// clang-format off
#define INTERNAL_BITSET_TYPE_LIST(V) \
V(OtherUnsigned31, 1u << 1) \
V(OtherUnsigned32, 1u << 2) \
V(OtherSigned32, 1u << 3) \
V(OtherNumber, 1u << 4) \
V(OtherString, 1u << 5) \
V(OtherUnsigned31, uint64_t{1} << 1) \
V(OtherUnsigned32, uint64_t{1} << 2) \
V(OtherSigned32, uint64_t{1} << 3) \
V(OtherNumber, uint64_t{1} << 4) \
V(OtherString, uint64_t{1} << 5) \
#define PROPER_ATOMIC_BITSET_TYPE_LIST(V) \
V(Negative31, 1u << 6) \
V(Null, 1u << 7) \
V(Undefined, 1u << 8) \
V(Boolean, 1u << 9) \
V(Unsigned30, 1u << 10) \
V(MinusZero, 1u << 11) \
V(NaN, 1u << 12) \
V(Symbol, 1u << 13) \
V(InternalizedString, 1u << 14) \
V(OtherCallable, 1u << 15) \
V(OtherObject, 1u << 16) \
V(OtherUndetectable, 1u << 17) \
V(CallableProxy, 1u << 18) \
V(OtherProxy, 1u << 19) \
V(Function, 1u << 20) \
V(BoundFunction, 1u << 21) \
V(Hole, 1u << 22) \
V(OtherInternal, 1u << 23) \
V(ExternalPointer, 1u << 24) \
V(Array, 1u << 25) \
V(UnsignedBigInt63, 1u << 26) \
V(OtherUnsignedBigInt64, 1u << 27) \
V(NegativeBigInt63, 1u << 28) \
V(OtherBigInt, 1u << 29) \
#define PROPER_ATOMIC_BITSET_TYPE_LOW_LIST(V) \
V(Negative31, uint64_t{1} << 6) \
V(Null, uint64_t{1} << 7) \
V(Undefined, uint64_t{1} << 8) \
V(Boolean, uint64_t{1} << 9) \
V(Unsigned30, uint64_t{1} << 10) \
V(MinusZero, uint64_t{1} << 11) \
V(NaN, uint64_t{1} << 12) \
V(Symbol, uint64_t{1} << 13) \
V(InternalizedString, uint64_t{1} << 14) \
V(OtherCallable, uint64_t{1} << 15) \
V(OtherObject, uint64_t{1} << 16) \
V(OtherUndetectable, uint64_t{1} << 17) \
V(CallableProxy, uint64_t{1} << 18) \
V(OtherProxy, uint64_t{1} << 19) \
V(Function, uint64_t{1} << 20) \
V(BoundFunction, uint64_t{1} << 21) \
V(Hole, uint64_t{1} << 22) \
V(OtherInternal, uint64_t{1} << 23) \
V(ExternalPointer, uint64_t{1} << 24) \
V(Array, uint64_t{1} << 25) \
V(UnsignedBigInt63, uint64_t{1} << 26) \
V(OtherUnsignedBigInt64, uint64_t{1} << 27) \
V(NegativeBigInt63, uint64_t{1} << 28) \
V(OtherBigInt, uint64_t{1} << 29) \
/* TODO(v8:10391): Remove this type once all ExternalPointer usages are */ \
/* sandbox-ready. */ \
V(SandboxedExternalPointer, 1u << 30) \
V(CagedPointer, 1u << 31) \
/* sandbox-ready. */ \
V(SandboxedExternalPointer, uint64_t{1} << 30) \
V(CagedPointer, uint64_t{1} << 31)
// For future use.
// Types defined in the high 32 bit are currently only supported in C++.
#define PROPER_ATOMIC_BITSET_TYPE_HIGH_LIST(V)
#define PROPER_BITSET_TYPE_LIST(V) \
V(None, 0u) \
PROPER_ATOMIC_BITSET_TYPE_LIST(V) \
V(None, uint64_t{0}) \
PROPER_ATOMIC_BITSET_TYPE_LOW_LIST(V) \
PROPER_ATOMIC_BITSET_TYPE_HIGH_LIST(V) \
V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | \
kOtherSigned32) \
@ -207,7 +212,7 @@ namespace compiler {
V(NonInternal, kPrimitive | kReceiver) \
V(NonBigInt, kNonBigIntPrimitive | kReceiver) \
V(NonNumber, kBigInt | kUnique | kString | kInternal) \
V(Any, 0xfffffffeu)
V(Any, uint64_t{0xfffffffffffffffe})
// clang-format on
@ -243,9 +248,9 @@ class UnionType;
class V8_EXPORT_PRIVATE BitsetType {
public:
using bitset = uint32_t; // Internal
using bitset = uint64_t; // Internal
enum : uint32_t {
enum : bitset {
#define DECLARE_TYPE(type, value) k##type = (value),
BITSET_TYPE_LIST(DECLARE_TYPE)
#undef DECLARE_TYPE
@ -376,7 +381,7 @@ class V8_EXPORT_PRIVATE Type {
PROPER_BITSET_TYPE_LIST(DEFINE_TYPE_CONSTRUCTOR)
#undef DEFINE_TYPE_CONSTRUCTOR
Type() : payload_(0) {}
Type() : payload_(uint64_t{0}) {}
static Type SignedSmall() { return NewBitset(BitsetType::SignedSmall()); }
static Type UnsignedSmall() { return NewBitset(BitsetType::UnsignedSmall()); }
@ -396,7 +401,7 @@ class V8_EXPORT_PRIVATE Type {
// Predicates.
bool IsNone() const { return payload_ == None().payload_; }
bool IsInvalid() const { return payload_ == 0u; }
bool IsInvalid() const { return payload_ == uint64_t{0}; }
bool Is(Type that) const {
return payload_ == that.payload_ || this->SlowIs(that);
@ -405,7 +410,7 @@ class V8_EXPORT_PRIVATE Type {
bool Equals(Type that) const { return this->Is(that) && that.Is(*this); }
// Inspection.
bool IsBitset() const { return payload_ & 1; }
bool IsBitset() const { return payload_ & uint64_t{1}; }
bool IsRange() const { return IsKind(TypeBase::kRange); }
bool IsHeapConstant() const { return IsKind(TypeBase::kHeapConstant); }
bool IsOtherNumberConstant() const {
@ -469,10 +474,10 @@ class V8_EXPORT_PRIVATE Type {
friend UnionType;
friend size_t hash_value(Type type);
explicit Type(bitset bits) : payload_(bits | 1u) {}
explicit Type(bitset bits) : payload_(bits | uint64_t{1}) {}
Type(TypeBase* type_base) // NOLINT(runtime/explicit)
: payload_(reinterpret_cast<uintptr_t>(type_base)) {}
: payload_(reinterpret_cast<uint64_t>(type_base)) {}
// Internal inspection.
bool IsKind(TypeBase::Kind kind) const {
@ -491,7 +496,7 @@ class V8_EXPORT_PRIVATE Type {
bitset AsBitset() const {
DCHECK(IsBitset());
return static_cast<bitset>(payload_) ^ 1u;
return static_cast<bitset>(payload_) ^ uint64_t { 1 };
}
const UnionType* AsUnion() const;
@ -526,7 +531,7 @@ class V8_EXPORT_PRIVATE Type {
// If LSB is set, the payload is a bitset; if LSB is clear, the payload is
// a pointer to a subtype of the TypeBase class.
uintptr_t payload_;
uint64_t payload_;
};
inline size_t hash_value(Type type) { return type.payload_; }

View File

@ -17,9 +17,14 @@ namespace internal {
#include "torque-generated/src/objects/turbofan-types-tq.inc"
class TurbofanTypeBits {
class TurbofanTypeLowBits {
public:
DEFINE_TORQUE_GENERATED_TURBOFAN_TYPE_BITS()
DEFINE_TORQUE_GENERATED_TURBOFAN_TYPE_LOW_BITS()
};
class TurbofanTypeHighBits {
public:
DEFINE_TORQUE_GENERATED_TURBOFAN_TYPE_HIGH_BITS()
};
} // namespace internal

View File

@ -9,7 +9,10 @@
class TurbofanType extends HeapObject {
}
bitfield struct TurbofanTypeBits extends uint32 {
// TurbofanBitsetType is 64 bit.
// We use two separate 32 bit bitsets in Torque, due to limitted support
// of 64 bit bitsets.
bitfield struct TurbofanTypeLowBits extends uint32 {
_unused_padding_field_1: bool: 1 bit;
other_unsigned31: bool: 1 bit;
other_unsigned32: bool: 1 bit;
@ -44,9 +47,14 @@ bitfield struct TurbofanTypeBits extends uint32 {
caged_pointer: bool: 1 bit;
}
bitfield struct TurbofanTypeHighBits extends uint32 {
_unused_field: bool: 1 bit;
}
@export
class TurbofanBitsetType extends TurbofanType {
bitset: TurbofanTypeBits;
bitset_low: TurbofanTypeLowBits;
bitset_high: TurbofanTypeHighBits;
}
@export
@ -75,81 +83,85 @@ macro IsMinusZero(x: float64): bool {
return x == 0 && 1 / x < 0;
}
macro TestTurbofanBitsetType(value: Object, bitset: TurbofanTypeBits): bool {
macro TestTurbofanBitsetType(
value: Object, bitsetLow: TurbofanTypeLowBits,
_bitsetHigh: TurbofanTypeHighBits): bool {
typeswitch (value) {
case (value: Number): {
const valueF = Convert<float64>(value);
if (IsInteger(value)) {
if (IsMinusZero(valueF)) {
return bitset.minus_zero;
return bitsetLow.minus_zero;
} else if (valueF < Convert<float64>(-0x80000000)) {
return bitset.other_number;
return bitsetLow.other_number;
} else if (valueF < -0x40000000) {
return bitset.other_signed32;
return bitsetLow.other_signed32;
} else if (valueF < 0) {
return bitset.negative31;
return bitsetLow.negative31;
} else if (valueF < Convert<float64>(0x40000000)) {
return bitset.unsigned30;
return bitsetLow.unsigned30;
} else if (valueF < 0x80000000) {
return bitset.other_unsigned31;
return bitsetLow.other_unsigned31;
} else if (valueF <= 0xffffffff) {
return bitset.other_unsigned32;
return bitsetLow.other_unsigned32;
} else {
return bitset.other_number;
return bitsetLow.other_number;
}
} else if (Float64IsNaN(valueF)) {
return bitset.naN;
return bitsetLow.naN;
} else {
return bitset.other_number;
return bitsetLow.other_number;
}
}
case (Null): {
return bitset.null;
return bitsetLow.null;
}
case (Undefined): {
return bitset.undefined;
return bitsetLow.undefined;
}
case (Boolean): {
return bitset.boolean;
return bitsetLow.boolean;
}
case (Symbol): {
return bitset.symbol;
return bitsetLow.symbol;
}
case (s: String): {
if (s.IsNotInternalized()) {
return bitset.other_string;
return bitsetLow.other_string;
} else {
return bitset.internalized_string;
return bitsetLow.internalized_string;
}
}
case (proxy: JSProxy): {
return Is<Callable>(proxy) ? bitset.callable_proxy : bitset.other_proxy;
return Is<Callable>(proxy) ? bitsetLow.callable_proxy :
bitsetLow.other_proxy;
}
case (JSFunction): {
return bitset.function;
return bitsetLow.function;
}
case (JSBoundFunction): {
return bitset.bound_function;
return bitsetLow.bound_function;
}
case (TheHole): {
return bitset.hole;
return bitsetLow.hole;
}
case (JSArray): {
return bitset.array;
return bitsetLow.array;
}
case (BigInt): {
// TODO (tebbi): Distinguish different BigInt types.
return bitset.unsigned_big_int_63 | bitset.other_unsigned_big_int_64 |
bitset.negative_big_int_63 | bitset.other_big_int;
return bitsetLow.unsigned_big_int_63 |
bitsetLow.other_unsigned_big_int_64 | bitsetLow.negative_big_int_63 |
bitsetLow.other_big_int;
}
case (Callable): {
return bitset.other_callable;
return bitsetLow.other_callable;
}
case (object: JSObject): {
if (object.map.IsUndetectable()) {
return bitset.other_undetectable;
return bitsetLow.other_undetectable;
} else {
return bitset.other_object;
return bitsetLow.other_object;
}
}
case (Object): {
@ -162,7 +174,8 @@ builtin TestTurbofanType(implicit context: Context)(
value: Object, expectedType: TurbofanType): Boolean {
typeswitch (expectedType) {
case (t: TurbofanBitsetType): {
return Convert<Boolean>(TestTurbofanBitsetType(value, t.bitset));
return Convert<Boolean>(
TestTurbofanBitsetType(value, t.bitset_low, t.bitset_high));
}
case (t: TurbofanUnionType): {
return Convert<Boolean>(

View File

@ -26,7 +26,7 @@ static bool IsInteger(double x) {
return nearbyint(x) == x && !i::IsMinusZero(x); // Allows for infinities.
}
using bitset = uint32_t;
using bitset = Type::bitset;
struct Tests {
using TypeIterator = Types::TypeVector::iterator;
@ -114,7 +114,7 @@ struct Tests {
CHECK(this->IsBitset(T.Any));
CHECK(bitset(0) == this->AsBitset(T.None));
CHECK(bitset(0xFFFFFFFEu) == this->AsBitset(T.Any));
CHECK(bitset(0xFFFFFFFFFFFFFFFEu) == this->AsBitset(T.Any));
// Union(T1, T2) is bitset for bitsets T1,T2
for (TypeIterator it1 = T.types.begin(); it1 != T.types.end(); ++it1) {