Commit bf4d6b35 authored by Jake Hughes's avatar Jake Hughes Committed by V8 LUCI CQ

[handles] Add direct handles

This introduces a new DirectHandle class with a deliberately similar API
to Handle. It uses an API that uses identical method names for symmetry,
but with an address field containing a direct pointer to JS heap objects
(or SMI).

Direct handles are experimental and can be enabled with the
v8_enable_conservative_stack_scanning gn option. The motivation for them
is described in the design doc [1].

[1]: https://docs.google.com/document/d/1uRGYQM76vk1fc_aDqDH3pm2qhaJtnK2oyzeVng4cS6I/

Bug: v8:13270
Change-Id: I0a6e0581adb5fa3b420efec3ba2b6d609d945c52
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3820483
Commit-Queue: Jake Hughes <jh@jakehughes.uk>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83434}
parent 6037547c
......@@ -136,6 +136,7 @@ Ingvar Stepanyan <me@rreverser.com>
Ioseb Dzmanashvili <ioseb.dzmanashvili@gmail.com>
Isiah Meadows <impinball@gmail.com>
Jaime Bernardo <jaime@janeasystems.com>
Jake Hughes <jh@jakehughes.uk>
James M Snell <jasnell@gmail.com>
James Pike <g00gle@chilon.net>
Jan Krems <jan.krems@gmail.com>
......
......@@ -34,6 +34,13 @@ ASSERT_TRIVIALLY_COPYABLE(HandleBase);
ASSERT_TRIVIALLY_COPYABLE(Handle<Object>);
ASSERT_TRIVIALLY_COPYABLE(MaybeHandle<Object>);
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
ASSERT_TRIVIALLY_COPYABLE(DirectHandle<Object>);
ASSERT_TRIVIALLY_COPYABLE(DirectMaybeHandle<Object>);
#endif // V8_ENABLE_CONSERVATIVE_STACK_SCANNING
#ifdef DEBUG
bool HandleBase::IsDereferenceAllowed() const {
DCHECK_NOT_NULL(location_);
......@@ -78,7 +85,42 @@ bool HandleBase::IsDereferenceAllowed() const {
// TODO(leszeks): Check if the main thread owns this handle.
return true;
}
#endif
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
template <typename T>
bool DirectHandle<T>::IsDereferenceAllowed() const {
DCHECK_NE(obj_, kTaggedNullAddress);
Object object(obj_);
if (object.IsSmi()) return true;
HeapObject heap_object = HeapObject::cast(object);
if (IsReadOnlyHeapObject(heap_object)) return true;
Isolate* isolate = GetIsolateFromWritableObject(heap_object);
if (!AllowHandleDereference::IsAllowed()) return false;
// Allocations in the shared heap may be dereferenced by multiple threads.
if (isolate->is_shared()) return true;
LocalHeap* local_heap = isolate->CurrentLocalHeap();
// Local heap can't access handles when parked
if (!local_heap->IsHandleDereferenceAllowed()) {
StdoutStream{} << "Cannot dereference handle owned by "
<< "non-running local heap\n";
return false;
}
// If LocalHeap::Current() is null, we're on the main thread -- if we were to
// check main thread HandleScopes here, we should additionally check the
// main-thread LocalHeap.
DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
return true;
}
#endif // V8_ENABLE_CONSERVATIVE_STACK_SCANNING
#endif // DEBUG
int HandleScope::NumberOfHandles(Isolate* isolate) {
HandleScopeImplementer* impl = isolate->handle_scope_implementer();
......
......@@ -37,6 +37,8 @@ class SmallOrderedNameDictionary;
class SwissNameDictionary;
class WasmExportedFunctionData;
constexpr Address kTaggedNullAddress = 0x1;
// ----------------------------------------------------------------------------
// Base class for Handle instantiations. Don't use directly.
class HandleBase {
......@@ -371,6 +373,87 @@ struct HandleScopeData final {
}
};
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
// ----------------------------------------------------------------------------
// A DirectHandle provides a reference to an object without an intermediate
// pointer.
//
// A DirectHandle is a simple wrapper around a tagged pointer to a heap object
// or a SMI. Its methods are symmetrical with Handle, so that Handles can be
// easily migrated.
//
// DirectHandles are intended to be used with conservative stack scanning, as
// they do not provide a mechanism for keeping an object alive across a garbage
// collection.
//
// Further motivation is explained in the design doc:
// https://docs.google.com/document/d/1uRGYQM76vk1fc_aDqDH3pm2qhaJtnK2oyzeVng4cS6I/
template <typename T>
class DirectHandle final {
public:
V8_INLINE explicit DirectHandle() : obj_(kTaggedNullAddress) {
// Skip static type check in order to allow DirectHandle<XXX>::null() as
// default parameter values in non-inl header files without requiring full
// definition of type XXX.
}
V8_INLINE bool is_null() const { return obj_ == kTaggedNullAddress; }
V8_INLINE explicit DirectHandle(Address object) : obj_(object) {
// This static type check also fails for forward class declarations.
static_assert(std::is_convertible<T*, Object*>::value,
"static type violation");
}
// Constructor for handling automatic up casting.
// Ex. DirectHandle<JSFunction> can be passed when DirectHandle<Object> is
// expected.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
V8_INLINE DirectHandle(DirectHandle<S> handle) : obj_(handle.obj_) {}
V8_INLINE T operator->() const { return obj_; }
V8_INLINE T operator*() const {
SLOW_DCHECK(IsDereferenceAllowed());
return T::unchecked_cast(Object(obj_));
}
template <typename S>
inline static const DirectHandle<T> cast(DirectHandle<S> that);
// Consider declaring values that contain empty handles as
// MaybeHandle to force validation before being used as handles.
static const DirectHandle<T> null() { return DirectHandle<T>(); }
protected:
#ifdef DEBUG
bool V8_EXPORT_PRIVATE IsDereferenceAllowed() const;
#else
V8_INLINE
bool V8_EXPORT_PRIVATE IsDereferenceAllowed() const { return true; }
#endif // DEBUG
private:
// DirectHandles of different classes are allowed to access each other's
// obj_.
template <typename>
friend class DirectHandle;
// MaybeDirectHandle is allowed to access obj_.
template <typename>
friend class MaybeDirectHandle;
// This is a direct pointer to either a tagged object or SMI. Design overview:
// https://docs.google.com/document/d/1uRGYQM76vk1fc_aDqDH3pm2qhaJtnK2oyzeVng4cS6I/
T obj_;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, DirectHandle<T> handle);
#endif
} // namespace internal
} // namespace v8
......
......@@ -117,6 +117,71 @@ class MaybeObjectHandle {
MaybeHandle<Object> handle_;
};
#ifdef V8_ENABLE_CONSERVATIVE_STACK_SCANNING
template <typename T>
class DirectMaybeHandle final {
public:
V8_INLINE DirectMaybeHandle() = default;
V8_INLINE DirectMaybeHandle(NullMaybeHandleType) {}
// Constructor for handling automatic up casting from DirectHandle.
// Ex. DirectHandle<JSArray> can be passed when DirectMaybeHandle<Object> is
// expected.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
V8_INLINE DirectMaybeHandle(DirectHandle<S> handle)
: location_(handle.location_) {}
// Constructor for handling automatic up casting.
// Ex. DirectMaybeHandle<JSArray> can be passed when DirectHandle<Object> is
// expected.
template <typename S, typename = typename std::enable_if<
std::is_convertible<S*, T*>::value>::type>
V8_INLINE DirectMaybeHandle(DirectMaybeHandle<S> maybe_handle)
: location_(maybe_handle.location_) {}
V8_INLINE DirectMaybeHandle(T object, Isolate* isolate);
V8_INLINE DirectMaybeHandle(T object, LocalHeap* local_heap);
V8_INLINE void Assert() const { DCHECK_NE(location_, kTaggedNullAddress); }
V8_INLINE void Check() const { CHECK_NE(location_, kTaggedNullAddress); }
V8_INLINE DirectHandle<T> ToDirectHandleChecked() const {
Check();
return DirectHandle<T>(location_);
}
// Convert to a DirectHandle with a type that can be upcasted to.
template <typename S>
V8_WARN_UNUSED_RESULT V8_INLINE bool ToDirectHandle(
DirectHandle<S>* out) const {
if (location_ == kTaggedNullAddress) {
*out = DirectHandle<T>::null();
return false;
} else {
*out = DirectHandle<T>(location_);
return true;
}
}
// Returns the raw address where this direct handle is stored.
V8_INLINE Address address() const { return location_; }
bool is_null() const { return location_ == kTaggedNullAddress; }
protected:
Address location_ = kTaggedNullAddress;
// DirectMaybeHandles of different classes are allowed to access each
// other's location_.
template <typename>
friend class DirectMaybeHandle;
};
#endif // V8_ENABLE_CONSERVATIVE_STACK_SCANNING
} // namespace internal
} // namespace v8
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment