Reapply "[vm] Add graph intrinsics for ThreadLocal hasValue and getValue methods."

This reverts commit 4d7467abd6.

The fix for the failure that caused revert is in patchset 2.

TEST=ci
Change-Id: I9b7ff0dd049062b086ad43c8368c6ede130edf35
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/465781
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
Alexander Aprelev
2025-12-03 06:30:20 -08:00
committed by Commit Queue
parent 9ac53f780c
commit 6b72a9c0bc
13 changed files with 3733 additions and 3615 deletions
+22 -31
View File
@@ -1279,36 +1279,31 @@ DEFINE_NATIVE_ENTRY(Isolate_sendOOB, 0, 2) {
}
static void EnsureThreadLocalsTableExistsAndBigEnough(Thread* thread,
intptr_t index) {
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
if (locals.IsNull()) {
locals = GrowableObjectArray::New();
thread->set_thread_locals(locals);
}
if (index >= locals.Length()) {
locals.Grow(index + 1);
intptr_t old_length = locals.Length();
locals.SetLength(locals.Capacity());
for (intptr_t i = old_length; i < locals.Capacity(); i++) {
locals.SetAt(i, Object::sentinel());
intptr_t id) {
Array& locals = Array::Handle(thread->thread_locals());
if (id >= locals.Length()) {
intptr_t new_length = id + 1;
const Array& new_array =
Array::Handle(Array::Grow(locals, new_length, Heap::kOld));
for (intptr_t i = locals.Length(); i < new_length; i++) {
new_array.SetAt(i, Object::sentinel());
}
thread->set_thread_locals(new_array);
}
}
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_allocateId, 0, 0) {
DEFINE_NATIVE_ENTRY(ThreadLocal_allocateId, 0, 0) {
auto isolate_group = thread->isolate_group();
isolate_group->increment_scoped_thread_locals_count();
intptr_t new_index = isolate_group->scoped_thread_locals_count() - 1;
isolate_group->increment_thread_locals_count();
intptr_t new_index = isolate_group->thread_locals_count() - 1;
EnsureThreadLocalsTableExistsAndBigEnough(thread, new_index);
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
Array& locals = Array::Handle(thread->thread_locals());
locals.SetAt(new_index, Object::sentinel());
return Integer::New(new_index);
}
static void ValidateScopedThreadLocalId(Thread* thread, intptr_t id) {
if (id < 0 || id >= thread->isolate_group()->scoped_thread_locals_count()) {
if (id < 0 || id >= thread->isolate_group()->thread_locals_count()) {
const String& msg = String::Handle(String::New("Invalid local id."));
Exceptions::ThrowStateError(msg);
UNREACHABLE();
@@ -1316,42 +1311,38 @@ static void ValidateScopedThreadLocalId(Thread* thread, intptr_t id) {
EnsureThreadLocalsTableExistsAndBigEnough(thread, id);
}
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_hasValue, 0, 1) {
DEFINE_NATIVE_ENTRY(ThreadLocal_hasValue, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
intptr_t id = id_obj.Value();
ValidateScopedThreadLocalId(thread, id);
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
Array& locals = Array::Handle(thread->thread_locals());
return locals.At(id) == Object::sentinel().ptr() ? Bool::False().ptr()
: Bool::True().ptr();
}
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_getValue, 0, 1) {
DEFINE_NATIVE_ENTRY(ThreadLocal_getValue, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
intptr_t id = id_obj.Value();
ValidateScopedThreadLocalId(thread, id);
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
Array& locals = Array::Handle(thread->thread_locals());
return locals.At(id);
}
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_setValue, 0, 2) {
DEFINE_NATIVE_ENTRY(ThreadLocal_setValue, 0, 2) {
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
intptr_t id = id_obj.Value();
ValidateScopedThreadLocalId(thread, id);
GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(1));
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
Array& locals = Array::Handle(thread->thread_locals());
locals.SetAt(id, value);
return Object::null();
}
DEFINE_NATIVE_ENTRY(ScopedThreadLocal_clearValue, 0, 1) {
DEFINE_NATIVE_ENTRY(ThreadLocal_clearValue, 0, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Integer, id_obj, arguments->NativeArgAt(0));
intptr_t id = id_obj.Value();
ValidateScopedThreadLocalId(thread, id);
GrowableObjectArray& locals =
GrowableObjectArray::Handle(thread->thread_locals());
Array& locals = Array::Handle(thread->thread_locals());
locals.SetAt(id, Object::sentinel());
return Object::null();
}
+5 -5
View File
@@ -281,11 +281,11 @@ namespace dart {
V(Internal_allocateObjectInstructionsEnd, 0) \
V(InvocationMirror_unpackTypeArguments, 2) \
V(NoSuchMethodError_existingMethodSignature, 3) \
V(ScopedThreadLocal_allocateId, 0) \
V(ScopedThreadLocal_clearValue, 1) \
V(ScopedThreadLocal_getValue, 1) \
V(ScopedThreadLocal_hasValue, 1) \
V(ScopedThreadLocal_setValue, 2) \
V(ThreadLocal_allocateId, 0) \
V(ThreadLocal_clearValue, 1) \
V(ThreadLocal_getValue, 1) \
V(ThreadLocal_hasValue, 1) \
V(ThreadLocal_setValue, 2) \
V(Uri_isWindowsPlatform, 0) \
V(UserTag_new, 2) \
V(UserTag_label, 1) \
+1 -1
View File
@@ -10763,7 +10763,7 @@ class GenericCheckBoundInstr : public CheckBoundBaseInstr {
// Phantom checks serve as dependencies inhibiting illegal code motion but
// are removed before code generation. Phantom checks are inserted due to
// unsafe annotations. An early-phaee path-sensitive bounds check removal
// unsafe annotations. An early-phase path-sensitive bounds check removal
// optimization can be implemented by replacing a real check with a phantom
// check.
kPhantom
+2 -1
View File
@@ -233,7 +233,8 @@ class ParsedFunction;
V(ObjectStore, _, record_field_names, Array, VAR) \
V(PersistentHandle, _, ptr, Dynamic, VAR) \
V(Thread, _, current_tag, UserTag, VAR) \
V(Thread, _, default_tag, UserTag, VAR)
V(Thread, _, default_tag, UserTag, VAR) \
V(Thread, _, thread_locals, Array, VAR)
// List of slots that correspond to fields of non-Dart objects containing
// unboxed values in the following format:
+49
View File
@@ -721,5 +721,54 @@ bool GraphIntrinsifier::Build_DoubleFlipSignBit(FlowGraph* flow_graph) {
return true;
}
static Definition* GetThreadLocalValue(FlowGraph* flow_graph,
BlockBuilder* builder) {
Definition* thread = builder->AddDefinition(new LoadThreadInstr());
Definition* array = builder->AddDefinition(
new LoadFieldInstr(new Value(thread),
/*slot=*/Slot::Thread_thread_locals(),
InnerPointerAccess::kNotUntagged, builder->Source(),
/*calls_initializer=*/false, DeoptId::kNone,
compiler::Assembler::kRelaxedNonAtomic));
Definition* index = builder->AddParameter(0);
Definition* safe_index =
PrepareIndexedOp(flow_graph, builder, array, index, Slot::Array_length());
Definition* local = builder->AddDefinition(new LoadIndexedInstr(
new Value(array), new Value(safe_index), /*index_unboxed=*/false,
/*index_scale=*/target::Instance::ElementSizeFor(kArrayCid), kArrayCid,
kAlignedAccess, DeoptId::kNone, builder->Source(),
new CompileType(CompileType::FromAbstractType(
Type::ZoneHandle(Type::ObjectType()), CompileType::kCanBeNull,
CompileType::kCanBeSentinel))));
return local;
}
bool GraphIntrinsifier::Build_ThreadLocalGetValue(FlowGraph* flow_graph) {
GraphEntryInstr* graph_entry = flow_graph->graph_entry();
auto normal_entry = graph_entry->normal_entry();
BlockBuilder builder(flow_graph, normal_entry, /*with_frame=*/false);
Definition* local = GetThreadLocalValue(flow_graph, &builder);
builder.AddReturn(new Value(local));
return true;
}
bool GraphIntrinsifier::Build_ThreadLocalHasValue(FlowGraph* flow_graph) {
GraphEntryInstr* graph_entry = flow_graph->graph_entry();
auto normal_entry = graph_entry->normal_entry();
BlockBuilder builder(flow_graph, normal_entry, /*with_frame=*/false);
Definition* local = GetThreadLocalValue(flow_graph, &builder);
Definition* sentinel = flow_graph->GetConstant(Object::sentinel());
Definition* result = builder.AddDefinition(new StrictCompareInstr(
builder.Source(), Token::kNE_STRICT, new Value(local),
new Value(sentinel), /*needs_number_check=*/false, DeoptId::kNone));
builder.AddReturn(new Value(result));
return true;
}
} // namespace compiler
} // namespace dart
@@ -670,6 +670,8 @@ namespace dart {
V(TypedDataLibrary, _Float64x2, /, Float64x2Div, 0x12925562) \
V(TypedDataLibrary, _Float64x2, -, Float64x2Sub, 0x2f258e89) \
V(TypedDataLibrary, _Float64x2, +, Float64x2Add, 0x09ecc418) \
V(VMLibrary, ThreadLocal, _getValue, ThreadLocalGetValue, 0xad8f22db) \
V(VMLibrary, ThreadLocal, _hasValue, ThreadLocalHasValue, 0xa6d3b876) \
#define RECOGNIZED_LIST(V) \
OTHER_RECOGNIZED_LIST(V) \
+1
View File
@@ -1156,6 +1156,7 @@ class Thread : public AllStatic {
static uword exit_through_ffi();
static word dart_stream_offset();
static word service_extension_stream_offset();
static word thread_locals_offset();
static word predefined_symbols_address_offset();
static word optimize_entry_offset();
static word deoptimize_entry_offset();
File diff suppressed because it is too large Load Diff
@@ -254,6 +254,7 @@
FIELD(Thread, dispatch_table_array_offset) \
FIELD(Thread, double_truncate_round_supported_offset) \
FIELD(Thread, service_extension_stream_offset) \
FIELD(Thread, thread_locals_offset) \
FIELD(Thread, optimize_entry_offset) \
FIELD(Thread, optimize_stub_offset) \
FIELD(Thread, deoptimize_entry_offset) \
+4 -4
View File
@@ -853,9 +853,9 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
GrowableObjectArrayPtr tag_table() const { return tag_table_; }
void set_tag_table(const GrowableObjectArray& value);
intptr_t scoped_thread_locals_count() { return scoped_thread_locals_count_; }
intptr_t increment_scoped_thread_locals_count() {
return scoped_thread_locals_count_.fetch_add(1u, std::memory_order_relaxed);
intptr_t thread_locals_count() { return thread_locals_count_; }
intptr_t increment_thread_locals_count() {
return thread_locals_count_.fetch_add(1u, std::memory_order_relaxed);
}
private:
@@ -1019,7 +1019,7 @@ class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
SafepointRwLock tag_table_lock_;
GrowableObjectArrayPtr tag_table_;
std::atomic<intptr_t> scoped_thread_locals_count_ = 0;
std::atomic<intptr_t> thread_locals_count_ = 0;
};
// When an isolate sends-and-exits this class represent things that it passed
+8 -3
View File
@@ -93,10 +93,10 @@ Thread::Thread(bool is_vm_isolate)
#else
service_extension_stream_(nullptr),
#endif
thread_locals_(Array::null()),
thread_lock_(),
reusable_handles_(),
sticky_error_(Error::null()),
thread_locals_(GrowableObjectArray::null()),
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS)
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT)
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER)
@@ -268,7 +268,7 @@ void Thread::set_default_tag(const UserTag& tag) {
default_tag_ = tag.ptr();
}
void Thread::set_thread_locals(const GrowableObjectArray& thread_locals) {
void Thread::set_thread_locals(const Array& thread_locals) {
thread_locals_ = thread_locals.ptr();
}
@@ -421,6 +421,10 @@ void Thread::EnterIsolate(Isolate* isolate) {
/*bypass_safepoint=*/false);
thread->SetupMutatorState();
thread->SetupDartMutatorState(isolate);
if (!isolate->is_vm_isolate()) {
thread->set_thread_locals(Array::empty_array());
}
}
isolate->scheduled_mutator_thread_ = thread;
@@ -569,6 +573,7 @@ void Thread::EnterIsolateGroupAsMutator(IsolateGroup* isolate_group,
thread->SetStackLimit(OSThread::Current()->overflow_stack_limit());
#endif
thread->set_thread_locals(Array::empty_array());
thread->AssertDartMutatorInvariants();
StackZone zone(thread);
@@ -774,7 +779,7 @@ void Thread::FreeActiveThread(Thread* thread,
thread->ResetStateLocked();
thread->current_tag_ = UserTag::null();
thread->default_tag_ = UserTag::null();
thread->thread_locals_ = GrowableObjectArray::null();
thread->thread_locals_ = Array::null();
thread->AssertEmptyThreadInvariants();
thread_registry->ReturnThreadLocked(thread);
+8 -4
View File
@@ -184,6 +184,7 @@ class Thread;
#define CACHED_NON_VM_STUB_LIST(V) \
V(ObjectPtr, object_null_, Object::null(), nullptr) \
V(SentinelPtr, object_sentinel_, Object::sentinel().ptr(), nullptr) \
V(BoolPtr, bool_true_, Object::bool_true().ptr(), nullptr) \
V(BoolPtr, bool_false_, Object::bool_false().ptr(), nullptr) \
V(ArrayPtr, empty_array_, Object::empty_array().ptr(), nullptr) \
@@ -1391,8 +1392,12 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
static void VisitMutators(MutatorThreadVisitor* visitor);
GrowableObjectArrayPtr thread_locals() const { return thread_locals_; }
void set_thread_locals(const GrowableObjectArray& thread_locals);
ArrayPtr thread_locals() const { return thread_locals_; }
void set_thread_locals(const Array& thread_locals);
static intptr_t thread_locals_offset() {
return OFFSET_OF(Thread, thread_locals_);
}
private:
template <class T>
@@ -1543,6 +1548,7 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
UserTagPtr default_tag_;
TimelineStream* const dart_stream_;
StreamInfo* const service_extension_stream_;
ArrayPtr thread_locals_ = nullptr;
// ---- End accessed from generated code. ----
@@ -1587,8 +1593,6 @@ class Thread : public ThreadState, public IntrusiveDListEntry<Thread> {
return shared_field_table_values_;
}
GrowableObjectArrayPtr thread_locals_ = nullptr;
// Reusable handles support.
#define REUSABLE_HANDLE_FIELDS(object) object* object##_handle_;
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_FIELDS)
+22 -18
View File
@@ -37,19 +37,21 @@ final class ThreadLocal<T> {
_clearValue(_id);
}
@pragma("vm:external-name", "ScopedThreadLocal_allocateId")
@pragma("vm:external-name", "ThreadLocal_allocateId")
external static int _allocateId();
@pragma("vm:external-name", "ScopedThreadLocal_hasValue")
@pragma("vm:recognized", "graph-intrinsic")
@pragma("vm:external-name", "ThreadLocal_hasValue")
external static bool _hasValue(int id);
@pragma("vm:external-name", "ScopedThreadLocal_getValue")
@pragma("vm:recognized", "graph-intrinsic")
@pragma("vm:external-name", "ThreadLocal_getValue")
external static Object? _getValue(int id);
@pragma("vm:external-name", "ScopedThreadLocal_setValue")
@pragma("vm:external-name", "ThreadLocal_setValue")
external static void _setValue(int id, Object? value);
@pragma("vm:external-name", "ScopedThreadLocal_clearValue")
@pragma("vm:external-name", "ThreadLocal_clearValue")
external static void _clearValue(int id);
final int _id;
@@ -65,13 +67,13 @@ final class ScopedThreadLocal<T> {
/// Execute [f] binding this [ScopedThreadLocal] to the given
/// [value] for the duration of the execution.
R runWith<R>(T new_value, R Function(T) f) {
bool had_value = variable.hasValue;
T? previous_value = had_value ? variable.value : null;
variable.value = new_value;
R result = f(new_value);
if (had_value) {
variable.value = previous_value as T;
R runWith<R>(T newValue, R Function(T) f) {
bool hadValue = variable.hasValue;
T? previousValue = hadValue ? variable.value : null;
variable.value = newValue;
R result = f(newValue);
if (hadValue) {
variable.value = previousValue as T;
} else {
variable.clearValue();
}
@@ -81,8 +83,8 @@ final class ScopedThreadLocal<T> {
/// Execute [f] initializing this [ScopedThreadLocal] using default initializer if needed.
/// Throws [StateError] if this [ScopedThreadLocal] does not have an initializer.
R runInitialized<R>(R Function(T) f) {
bool had_value = variable.hasValue;
T? previous_value = had_value ? variable.value : null;
bool hadValue = variable.hasValue;
T? previousValue = hadValue ? variable.value : null;
if (!variable.hasValue) {
if (_initializer == null) {
throw StateError(
@@ -92,8 +94,8 @@ final class ScopedThreadLocal<T> {
variable.value = _initializer!();
}
R result = f(variable.value);
if (had_value) {
variable.value = previous_value as T;
if (hadValue) {
variable.value = previousValue as T;
} else {
variable.clearValue();
}
@@ -126,9 +128,11 @@ final class FinalThreadLocal<T> {
/// Returns the value bound to [FinalThreadLocal].
T get value {
if (!variable.hasValue) {
variable.value = _initializer();
final v = _initializer();
variable.value = v;
return v;
}
return variable.value;
return unsafeCast<T>(ThreadLocal._getValue(variable._id));
}
set value(_) {