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:
committed by
Commit Queue
parent
9ac53f780c
commit
6b72a9c0bc
+22
-31
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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(_) {
|
||||
|
||||
Reference in New Issue
Block a user