dart-sdk/runtime/vm/tagged_pointer.h
Daco Harkes 483179c934 [vm] Recognize non-const Set in the VM
`_CompactLinkedHashSet` now extends `_HashVMBase`. The class hierarchy
is organized as mixins similar to LinkedHashMap to accomodate for the
other Sets not extending `_HashVMBase`.

Also, rearranges some code so that introducing ImmutableHashMap and
ImmutableHashSet is easier.
1) snapshot.h and snapshot.cc now have a MapReadFrom, MapWriteTo,
   SetReadFrom, and SetWriteTo to facilitate code sharing between
   mutable and immutable implementations similar to ArrayReadFrom and
   ArrayWriteTo.
2) Macros for CLASS_LIST_MAPS and CLASS_LIST_SETS to facilitate
   treating mutable and immutable implementations with the same handle.
   Also similar to Array.

Clustered snapshots for HashMaps is currently dead code. This CL makes
it explicit by marking these as unreachable. Immutable maps and sets
will end up in the clustered snapshot in follow up CLs.

Bug: https://github.com/dart-lang/sdk/issues/36077
Bug: https://github.com/dart-lang/sdk/issues/45908

TEST=runtime/vm/object_test.cc
TEST=tests/**_test.dart on many bots

Change-Id: If3cc5ebb3138535aeb0d5e06d9da3d1c9fb2deb2
Cq-Include-Trybots: luci.dart.try:analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,dart-sdk-linux-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-linux-debug-x64c-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-debug-simarm64c-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-linux-release-simarm_x64-try,vm-kernel-precomp-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/206222
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
2021-07-12 09:36:19 +00:00

432 lines
20 KiB
C++

// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_TAGGED_POINTER_H_
#define RUNTIME_VM_TAGGED_POINTER_H_
#include <type_traits>
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/class_id.h"
#include "vm/pointer_tagging.h"
namespace dart {
class IsolateGroup;
class UntaggedObject;
#define OBJECT_POINTER_CORE_FUNCTIONS(type, ptr) \
type* operator->() { return this; } \
const type* operator->() const { return this; } \
bool IsWellFormed() const { \
const uword value = ptr; \
return (value & kSmiTagMask) == 0 || \
Utils::IsAligned(value - kHeapObjectTag, kWordSize); \
} \
bool IsHeapObject() const { \
ASSERT(IsWellFormed()); \
const uword value = ptr; \
return (value & kSmiTagMask) == kHeapObjectTag; \
} \
/* Assumes this is a heap object. */ \
bool IsNewObject() const { \
ASSERT(IsHeapObject()); \
const uword addr = ptr; \
return (addr & kNewObjectAlignmentOffset) == kNewObjectAlignmentOffset; \
} \
bool IsNewObjectMayBeSmi() const { \
static const uword kNewObjectBits = \
(kNewObjectAlignmentOffset | kHeapObjectTag); \
const uword addr = ptr; \
return (addr & kObjectAlignmentMask) == kNewObjectBits; \
} \
/* Assumes this is a heap object. */ \
bool IsOldObject() const { \
ASSERT(IsHeapObject()); \
const uword addr = ptr; \
return (addr & kNewObjectAlignmentOffset) == kOldObjectAlignmentOffset; \
} \
\
/* Like !IsHeapObject() || IsOldObject() but compiles to a single branch. */ \
bool IsSmiOrOldObject() const { \
ASSERT(IsWellFormed()); \
static const uword kNewObjectBits = \
(kNewObjectAlignmentOffset | kHeapObjectTag); \
const uword addr = ptr; \
return (addr & kObjectAlignmentMask) != kNewObjectBits; \
} \
\
/* Like !IsHeapObject() || IsNewObject() but compiles to a single branch. */ \
bool IsSmiOrNewObject() const { \
ASSERT(IsWellFormed()); \
static const uword kOldObjectBits = \
(kOldObjectAlignmentOffset | kHeapObjectTag); \
const uword addr = ptr; \
return (addr & kObjectAlignmentMask) != kOldObjectBits; \
} \
\
bool operator==(const type& other) { return ptr == other.ptr; } \
bool operator!=(const type& other) { return ptr != other.ptr; } \
constexpr bool operator==(const type& other) const { \
return ptr == other.ptr; \
} \
constexpr bool operator!=(const type& other) const { \
return ptr != other.ptr; \
}
class ObjectPtr {
public:
OBJECT_POINTER_CORE_FUNCTIONS(ObjectPtr, tagged_pointer_)
UntaggedObject* untag() const {
return reinterpret_cast<UntaggedObject*>(untagged_pointer());
}
#define DEFINE_IS_CID(clazz) \
bool Is##clazz() const { return ((GetClassId() == k##clazz##Cid)); }
CLASS_LIST(DEFINE_IS_CID)
#undef DEFINE_IS_CID
#define DEFINE_IS_CID(clazz) \
bool IsTypedData##clazz() const { \
return ((GetClassId() == kTypedData##clazz##Cid)); \
} \
bool IsTypedDataView##clazz() const { \
return ((GetClassId() == kTypedData##clazz##ViewCid)); \
} \
bool IsExternalTypedData##clazz() const { \
return ((GetClassId() == kExternalTypedData##clazz##Cid)); \
}
CLASS_LIST_TYPED_DATA(DEFINE_IS_CID)
#undef DEFINE_IS_CID
#define DEFINE_IS_CID(clazz) \
bool IsFfi##clazz() const { return ((GetClassId() == kFfi##clazz##Cid)); }
CLASS_LIST_FFI(DEFINE_IS_CID)
#undef DEFINE_IS_CID
bool IsStringInstance() const { return IsStringClassId(GetClassId()); }
bool IsRawNull() const { return GetClassId() == kNullCid; }
bool IsDartInstance() const {
return (!IsHeapObject() || (GetClassId() >= kInstanceCid));
}
bool IsFreeListElement() const {
return ((GetClassId() == kFreeListElement));
}
bool IsForwardingCorpse() const {
return ((GetClassId() == kForwardingCorpse));
}
bool IsPseudoObject() const {
return IsFreeListElement() || IsForwardingCorpse();
}
intptr_t GetClassId() const;
intptr_t GetClassIdMayBeSmi() const {
return IsHeapObject() ? GetClassId() : static_cast<intptr_t>(kSmiCid);
}
void Validate(IsolateGroup* isolate_group) const;
bool operator==(const std::nullptr_t& other) { return tagged_pointer_ == 0; }
bool operator!=(const std::nullptr_t& other) { return tagged_pointer_ != 0; }
constexpr bool operator==(const std::nullptr_t& other) const {
return tagged_pointer_ == 0;
}
constexpr bool operator!=(const std::nullptr_t& other) const {
return tagged_pointer_ != 0;
}
// Use explicit null comparisons instead.
operator bool() const = delete;
// The underlying types of int32_t/int64_t and intptr_t are sometimes
// different and sometimes the same, depending on the platform. With
// only a conversion operator for intptr_t, on 64-bit Mac a static_cast
// to int64_t fails because it tries conversion to bool (!) rather than
// intptr_t. So we exhaustive define all the valid conversions based on
// the underlying types.
#if INT_MAX == INTPTR_MAX
explicit operator int() const { // NOLINT
return static_cast<int>(tagged_pointer_); // NOLINT
}
#endif
#if LONG_MAX == INTPTR_MAX
explicit operator long() const { // NOLINT
return static_cast<long>(tagged_pointer_); // NOLINT
}
#endif
#if LLONG_MAX == INTPTR_MAX
explicit operator long long() const { // NOLINT
return static_cast<long long>(tagged_pointer_); // NOLINT
}
#endif
#if UINT_MAX == UINTPTR_MAX
explicit operator unsigned int() const { // NOLINT
return static_cast<unsigned int>(tagged_pointer_); // NOLINT
}
#endif
#if ULONG_MAX == UINTPTR_MAX
explicit operator unsigned long() const { // NOLINT
return static_cast<unsigned long>(tagged_pointer_); // NOLINT
}
#endif
#if ULLONG_MAX == UINTPTR_MAX
explicit operator unsigned long long() const { // NOLINT
return static_cast<unsigned long long>(tagged_pointer_); // NOLINT
}
#endif
// Must be trivially copyable for std::atomic.
ObjectPtr& operator=(const ObjectPtr& other) = default;
constexpr ObjectPtr(const ObjectPtr& other) = default;
ObjectPtr() : tagged_pointer_(0) {}
explicit constexpr ObjectPtr(uword tagged) : tagged_pointer_(tagged) {}
explicit constexpr ObjectPtr(intptr_t tagged) : tagged_pointer_(tagged) {}
constexpr ObjectPtr(std::nullptr_t) : tagged_pointer_(0) {} // NOLINT
explicit ObjectPtr(UntaggedObject* heap_object)
: tagged_pointer_(reinterpret_cast<uword>(heap_object) + kHeapObjectTag) {
}
ObjectPtr Decompress(uword heap_base) const { return *this; }
ObjectPtr DecompressSmi() const { return *this; }
uword heap_base() const {
// TODO(rmacnak): Why does Windows have trouble linking GetClassId used
// here?
#if !defined(DART_HOST_OS_WINDOWS)
ASSERT(IsHeapObject());
ASSERT(!IsInstructions());
ASSERT(!IsInstructionsSection());
#endif
return tagged_pointer_ & kHeapBaseMask;
}
protected:
uword untagged_pointer() const {
ASSERT(IsHeapObject());
return tagged_pointer_ - kHeapObjectTag;
}
uword tagged_pointer_;
};
// Needed by the printing in the EXPECT macros.
#if defined(DEBUG) || defined(TESTING)
inline std::ostream& operator<<(std::ostream& os, const ObjectPtr& obj) {
os << reinterpret_cast<void*>(static_cast<uword>(obj));
return os;
}
#endif
template <typename T, typename Enable = void>
struct is_uncompressed_ptr : std::false_type {};
template <typename T>
struct is_uncompressed_ptr<
T,
typename std::enable_if<std::is_base_of<ObjectPtr, T>::value, void>::type>
: std::true_type {};
template <typename T, typename Enable = void>
struct is_compressed_ptr : std::false_type {};
template <typename T, typename Enable = void>
struct base_ptr_type {
using type =
typename std::enable_if<is_uncompressed_ptr<T>::value, ObjectPtr>::type;
};
#if !defined(DART_COMPRESSED_POINTERS)
typedef ObjectPtr CompressedObjectPtr;
#define DEFINE_COMPRESSED_POINTER(klass, base) \
typedef klass##Ptr Compressed##klass##Ptr;
#else
class CompressedObjectPtr {
public:
OBJECT_POINTER_CORE_FUNCTIONS(CompressedObjectPtr, compressed_pointer_)
explicit CompressedObjectPtr(ObjectPtr uncompressed)
: compressed_pointer_(
static_cast<uint32_t>(static_cast<uword>(uncompressed))) {}
explicit constexpr CompressedObjectPtr(uword tagged)
: compressed_pointer_(static_cast<uint32_t>(tagged)) {}
ObjectPtr Decompress(uword heap_base) const {
if ((compressed_pointer_ & kSmiTagMask) != kHeapObjectTag) {
// TODO(liama): Make all native code robust to junk in the upper 32-bits
// of SMIs, then remove this special casing.
return DecompressSmi();
}
return static_cast<ObjectPtr>(static_cast<uword>(compressed_pointer_) +
heap_base);
}
ObjectPtr DecompressSmi() const {
ASSERT((compressed_pointer_ & kSmiTagMask) != kHeapObjectTag);
return static_cast<ObjectPtr>(static_cast<uword>(compressed_pointer_));
}
const ObjectPtr& operator=(const ObjectPtr& other) {
compressed_pointer_ = static_cast<uint32_t>(static_cast<uword>(other));
return other;
}
protected:
uint32_t compressed_pointer_;
};
template <typename T>
struct is_compressed_ptr<
T,
typename std::enable_if<std::is_base_of<CompressedObjectPtr, T>::value,
void>::type> : std::true_type {};
template <typename T>
struct base_ptr_type<
T,
typename std::enable_if<std::is_base_of<CompressedObjectPtr, T>::value,
void>::type> {
using type = CompressedObjectPtr;
};
#define DEFINE_COMPRESSED_POINTER(klass, base) \
class Compressed##klass##Ptr : public Compressed##base##Ptr { \
public: \
explicit Compressed##klass##Ptr(klass##Ptr uncompressed) \
: Compressed##base##Ptr(uncompressed) {} \
const klass##Ptr& operator=(const klass##Ptr& other) { \
compressed_pointer_ = static_cast<uint32_t>(static_cast<uword>(other)); \
return other; \
} \
klass##Ptr Decompress(uword heap_base) const { \
return klass##Ptr(CompressedObjectPtr::Decompress(heap_base)); \
} \
};
#endif
#define DEFINE_TAGGED_POINTER(klass, base) \
class Untagged##klass; \
class klass##Ptr : public base##Ptr { \
public: \
klass##Ptr* operator->() { return this; } \
const klass##Ptr* operator->() const { return this; } \
Untagged##klass* untag() { \
return reinterpret_cast<Untagged##klass*>(untagged_pointer()); \
} \
/* TODO: Return const pointer */ \
Untagged##klass* untag() const { \
return reinterpret_cast<Untagged##klass*>(untagged_pointer()); \
} \
klass##Ptr& operator=(const klass##Ptr& other) = default; \
constexpr klass##Ptr(const klass##Ptr& other) = default; \
explicit constexpr klass##Ptr(const ObjectPtr& other) \
: base##Ptr(other) {} \
klass##Ptr() : base##Ptr() {} \
explicit constexpr klass##Ptr(uword tagged) : base##Ptr(tagged) {} \
explicit constexpr klass##Ptr(intptr_t tagged) : base##Ptr(tagged) {} \
constexpr klass##Ptr(std::nullptr_t) : base##Ptr(nullptr) {} /* NOLINT */ \
explicit klass##Ptr(const UntaggedObject* untagged) \
: base##Ptr(reinterpret_cast<uword>(untagged) + kHeapObjectTag) {} \
klass##Ptr Decompress(uword heap_base) const { return *this; } \
}; \
DEFINE_COMPRESSED_POINTER(klass, base)
DEFINE_TAGGED_POINTER(Class, Object)
DEFINE_TAGGED_POINTER(PatchClass, Object)
DEFINE_TAGGED_POINTER(Function, Object)
DEFINE_TAGGED_POINTER(ClosureData, Object)
DEFINE_TAGGED_POINTER(FfiTrampolineData, Object)
DEFINE_TAGGED_POINTER(Field, Object)
DEFINE_TAGGED_POINTER(Script, Object)
DEFINE_TAGGED_POINTER(Library, Object)
DEFINE_TAGGED_POINTER(Namespace, Object)
DEFINE_TAGGED_POINTER(KernelProgramInfo, Object)
DEFINE_TAGGED_POINTER(WeakSerializationReference, Object)
DEFINE_TAGGED_POINTER(Code, Object)
DEFINE_TAGGED_POINTER(ObjectPool, Object)
DEFINE_TAGGED_POINTER(Instructions, Object)
DEFINE_TAGGED_POINTER(InstructionsSection, Object)
DEFINE_TAGGED_POINTER(InstructionsTable, Object)
DEFINE_TAGGED_POINTER(PcDescriptors, Object)
DEFINE_TAGGED_POINTER(CodeSourceMap, Object)
DEFINE_TAGGED_POINTER(CompressedStackMaps, Object)
DEFINE_TAGGED_POINTER(LocalVarDescriptors, Object)
DEFINE_TAGGED_POINTER(ExceptionHandlers, Object)
DEFINE_TAGGED_POINTER(Context, Object)
DEFINE_TAGGED_POINTER(ContextScope, Object)
DEFINE_TAGGED_POINTER(Sentinel, Object)
DEFINE_TAGGED_POINTER(SingleTargetCache, Object)
DEFINE_TAGGED_POINTER(UnlinkedCall, Object)
DEFINE_TAGGED_POINTER(MonomorphicSmiableCall, Object)
DEFINE_TAGGED_POINTER(CallSiteData, Object)
DEFINE_TAGGED_POINTER(ICData, CallSiteData)
DEFINE_TAGGED_POINTER(MegamorphicCache, CallSiteData)
DEFINE_TAGGED_POINTER(SubtypeTestCache, Object)
DEFINE_TAGGED_POINTER(LoadingUnit, Object)
DEFINE_TAGGED_POINTER(Error, Object)
DEFINE_TAGGED_POINTER(ApiError, Error)
DEFINE_TAGGED_POINTER(LanguageError, Error)
DEFINE_TAGGED_POINTER(UnhandledException, Error)
DEFINE_TAGGED_POINTER(UnwindError, Error)
DEFINE_TAGGED_POINTER(Instance, Object)
DEFINE_TAGGED_POINTER(LibraryPrefix, Instance)
DEFINE_TAGGED_POINTER(TypeArguments, Instance)
DEFINE_TAGGED_POINTER(TypeParameters, Object)
DEFINE_TAGGED_POINTER(AbstractType, Instance)
DEFINE_TAGGED_POINTER(Type, AbstractType)
DEFINE_TAGGED_POINTER(FunctionType, AbstractType)
DEFINE_TAGGED_POINTER(TypeRef, AbstractType)
DEFINE_TAGGED_POINTER(TypeParameter, AbstractType)
DEFINE_TAGGED_POINTER(Closure, Instance)
DEFINE_TAGGED_POINTER(Number, Instance)
DEFINE_TAGGED_POINTER(Integer, Number)
DEFINE_TAGGED_POINTER(Smi, Integer)
DEFINE_TAGGED_POINTER(Mint, Integer)
DEFINE_TAGGED_POINTER(Double, Number)
DEFINE_TAGGED_POINTER(String, Instance)
DEFINE_TAGGED_POINTER(OneByteString, String)
DEFINE_TAGGED_POINTER(TwoByteString, String)
DEFINE_TAGGED_POINTER(PointerBase, Instance)
DEFINE_TAGGED_POINTER(TypedDataBase, PointerBase)
DEFINE_TAGGED_POINTER(TypedData, TypedDataBase)
DEFINE_TAGGED_POINTER(TypedDataView, TypedDataBase)
DEFINE_TAGGED_POINTER(ExternalOneByteString, String)
DEFINE_TAGGED_POINTER(ExternalTwoByteString, String)
DEFINE_TAGGED_POINTER(Bool, Instance)
DEFINE_TAGGED_POINTER(Array, Instance)
DEFINE_TAGGED_POINTER(ImmutableArray, Array)
DEFINE_TAGGED_POINTER(GrowableObjectArray, Instance)
DEFINE_TAGGED_POINTER(LinkedHashBase, Instance)
DEFINE_TAGGED_POINTER(LinkedHashMap, LinkedHashBase)
DEFINE_TAGGED_POINTER(LinkedHashSet, LinkedHashBase)
DEFINE_TAGGED_POINTER(Float32x4, Instance)
DEFINE_TAGGED_POINTER(Int32x4, Instance)
DEFINE_TAGGED_POINTER(Float64x2, Instance)
DEFINE_TAGGED_POINTER(ExternalTypedData, TypedDataBase)
DEFINE_TAGGED_POINTER(Pointer, PointerBase)
DEFINE_TAGGED_POINTER(DynamicLibrary, Instance)
DEFINE_TAGGED_POINTER(Capability, Instance)
DEFINE_TAGGED_POINTER(SendPort, Instance)
DEFINE_TAGGED_POINTER(ReceivePort, Instance)
DEFINE_TAGGED_POINTER(TransferableTypedData, Instance)
DEFINE_TAGGED_POINTER(StackTrace, Instance)
DEFINE_TAGGED_POINTER(RegExp, Instance)
DEFINE_TAGGED_POINTER(WeakProperty, Instance)
DEFINE_TAGGED_POINTER(MirrorReference, Instance)
DEFINE_TAGGED_POINTER(UserTag, Instance)
DEFINE_TAGGED_POINTER(FutureOr, Instance)
#undef DEFINE_TAGGED_POINTER
inline intptr_t RawSmiValue(const SmiPtr raw_value) {
#if !defined(DART_COMPRESSED_POINTERS)
const intptr_t value = static_cast<intptr_t>(raw_value);
#else
const intptr_t value = static_cast<intptr_t>(static_cast<int32_t>(
static_cast<uint32_t>(static_cast<uintptr_t>(raw_value))));
#endif
ASSERT((value & kSmiTagMask) == kSmiTag);
return (value >> kSmiTagShift);
}
} // namespace dart
#endif // RUNTIME_VM_TAGGED_POINTER_H_