6 #include "src/builtins/builtins-utils-gen.h" 7 #include "src/code-stub-assembler.h" 8 #include "src/microtask-queue.h" 9 #include "src/objects/js-weak-refs.h" 10 #include "src/objects/microtask-inl.h" 11 #include "src/objects/promise.h" 17 using TNode = compiler::TNode<T>;
44 void LeaveMicrotaskContext();
46 void RunPromiseHook(Runtime::FunctionId
id,
TNode<Context> context,
50 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetDefaultMicrotaskQueue() {
51 auto ref = ExternalReference::default_microtask_queue_address(isolate());
52 return UncheckedCast<IntPtrT>(
53 Load(MachineType::Pointer(), ExternalConstant(ref)));
56 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue(
57 TNode<Context> native_context) {
58 CSA_ASSERT(
this, IsNativeContext(native_context));
59 return LoadObjectField<IntPtrT>(native_context,
60 NativeContext::kMicrotaskQueueOffset);
63 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer(
64 TNode<IntPtrT> microtask_queue) {
65 return UncheckedCast<IntPtrT>(
66 Load(MachineType::IntPtr(), microtask_queue,
67 IntPtrConstant(MicrotaskQueue::kRingBufferOffset)));
70 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueCapacity(
71 TNode<IntPtrT> microtask_queue) {
72 return UncheckedCast<IntPtrT>(
73 Load(MachineType::IntPtr(), microtask_queue,
74 IntPtrConstant(MicrotaskQueue::kCapacityOffset)));
77 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueSize(
78 TNode<IntPtrT> microtask_queue) {
79 return UncheckedCast<IntPtrT>(
80 Load(MachineType::IntPtr(), microtask_queue,
81 IntPtrConstant(MicrotaskQueue::kSizeOffset)));
84 void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueSize(
85 TNode<IntPtrT> microtask_queue, TNode<IntPtrT> new_size) {
86 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
87 IntPtrConstant(MicrotaskQueue::kSizeOffset), new_size);
90 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueStart(
91 TNode<IntPtrT> microtask_queue) {
92 return UncheckedCast<IntPtrT>(
93 Load(MachineType::IntPtr(), microtask_queue,
94 IntPtrConstant(MicrotaskQueue::kStartOffset)));
97 void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueStart(
98 TNode<IntPtrT> microtask_queue, TNode<IntPtrT> new_start) {
99 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
100 IntPtrConstant(MicrotaskQueue::kStartOffset), new_start);
103 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::CalculateRingBufferOffset(
104 TNode<IntPtrT> capacity, TNode<IntPtrT> start, TNode<IntPtrT> index) {
105 return TimesPointerSize(
106 WordAnd(IntPtrAdd(start, index), IntPtrSub(capacity, IntPtrConstant(1))));
109 void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
110 TNode<Context> current_context, TNode<Microtask> microtask) {
111 CSA_ASSERT(
this, TaggedIsNotSmi(microtask));
113 StoreRoot(RootIndex::kCurrentMicrotask, microtask);
114 TNode<Map> microtask_map = LoadMap(microtask);
115 TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
117 VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
118 Label if_exception(
this, Label::kDeferred);
119 Label is_callable(
this), is_callback(
this),
120 is_promise_fulfill_reaction_job(
this),
121 is_promise_reject_reaction_job(
this),
122 is_promise_resolve_thenable_job(
this), is_weak_factory_cleanup_job(
this),
123 is_unreachable(
this, Label::kDeferred), done(
this);
125 int32_t case_values[] = {CALLABLE_TASK_TYPE,
127 PROMISE_FULFILL_REACTION_JOB_TASK_TYPE,
128 PROMISE_REJECT_REACTION_JOB_TASK_TYPE,
129 PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE,
130 WEAK_FACTORY_CLEANUP_JOB_TASK_TYPE};
131 Label* case_labels[] = {&is_callable,
133 &is_promise_fulfill_reaction_job,
134 &is_promise_reject_reaction_job,
135 &is_promise_resolve_thenable_job,
136 &is_weak_factory_cleanup_job};
137 static_assert(arraysize(case_values) == arraysize(case_labels),
"");
138 Switch(microtask_type, &is_unreachable, case_values, case_labels,
139 arraysize(case_labels));
144 TNode<Context> microtask_context =
145 LoadObjectField<Context>(microtask, CallableTask::kContextOffset);
146 TNode<Context> native_context = LoadNativeContext(microtask_context);
148 CSA_ASSERT(
this, IsNativeContext(native_context));
149 EnterMicrotaskContext(native_context);
150 SetCurrentContext(native_context);
152 TNode<JSReceiver> callable =
153 LoadObjectField<JSReceiver>(microtask, CallableTask::kCallableOffset);
154 Node*
const result = CallJS(
155 CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
156 microtask_context, callable, UndefinedConstant());
157 GotoIfException(result, &if_exception, &var_exception);
158 LeaveMicrotaskContext();
159 SetCurrentContext(current_context);
165 Node*
const microtask_callback =
166 LoadObjectField(microtask, CallbackTask::kCallbackOffset);
167 Node*
const microtask_data =
168 LoadObjectField(microtask, CallbackTask::kDataOffset);
181 CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
182 microtask_callback, microtask_data);
183 GotoIfException(result, &if_exception, &var_exception);
187 BIND(&is_promise_resolve_thenable_job);
190 TNode<Context> microtask_context = LoadObjectField<Context>(
191 microtask, PromiseResolveThenableJobTask::kContextOffset);
192 TNode<Context> native_context = LoadNativeContext(microtask_context);
193 CSA_ASSERT(
this, IsNativeContext(native_context));
194 EnterMicrotaskContext(native_context);
195 SetCurrentContext(native_context);
197 Node*
const promise_to_resolve = LoadObjectField(
198 microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
200 LoadObjectField(microtask, PromiseResolveThenableJobTask::kThenOffset);
201 Node*
const thenable = LoadObjectField(
202 microtask, PromiseResolveThenableJobTask::kThenableOffset);
205 CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
206 promise_to_resolve, thenable, then);
207 GotoIfException(result, &if_exception, &var_exception);
208 LeaveMicrotaskContext();
209 SetCurrentContext(current_context);
213 BIND(&is_promise_fulfill_reaction_job);
216 TNode<Context> microtask_context = LoadObjectField<Context>(
217 microtask, PromiseReactionJobTask::kContextOffset);
218 TNode<Context> native_context = LoadNativeContext(microtask_context);
219 CSA_ASSERT(
this, IsNativeContext(native_context));
220 EnterMicrotaskContext(native_context);
221 SetCurrentContext(native_context);
223 Node*
const argument =
224 LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
225 Node*
const handler =
226 LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
227 Node*
const promise_or_capability = LoadObjectField(
228 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
231 RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
232 promise_or_capability);
235 CallBuiltin(Builtins::kPromiseFulfillReactionJob, microtask_context,
236 argument, handler, promise_or_capability);
237 GotoIfException(result, &if_exception, &var_exception);
240 RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
241 promise_or_capability);
243 LeaveMicrotaskContext();
244 SetCurrentContext(current_context);
248 BIND(&is_promise_reject_reaction_job);
251 TNode<Context> microtask_context = LoadObjectField<Context>(
252 microtask, PromiseReactionJobTask::kContextOffset);
253 TNode<Context> native_context = LoadNativeContext(microtask_context);
254 CSA_ASSERT(
this, IsNativeContext(native_context));
255 EnterMicrotaskContext(native_context);
256 SetCurrentContext(native_context);
258 Node*
const argument =
259 LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
260 Node*
const handler =
261 LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
262 Node*
const promise_or_capability = LoadObjectField(
263 microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
266 RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
267 promise_or_capability);
270 CallBuiltin(Builtins::kPromiseRejectReactionJob, microtask_context,
271 argument, handler, promise_or_capability);
272 GotoIfException(result, &if_exception, &var_exception);
275 RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
276 promise_or_capability);
278 LeaveMicrotaskContext();
279 SetCurrentContext(current_context);
283 BIND(&is_weak_factory_cleanup_job);
286 TNode<JSWeakFactory> weak_factory = LoadObjectField<JSWeakFactory>(
287 microtask, WeakFactoryCleanupJobTask::kFactoryOffset);
288 TNode<Context> native_context = LoadObjectField<Context>(
289 weak_factory, JSWeakFactory::kNativeContextOffset);
290 CSA_ASSERT(
this, IsNativeContext(native_context));
291 EnterMicrotaskContext(native_context);
292 SetCurrentContext(native_context);
294 Node*
const result = CallRuntime(Runtime::kWeakFactoryCleanupJob,
295 native_context, weak_factory);
297 GotoIfException(result, &if_exception, &var_exception);
298 LeaveMicrotaskContext();
299 SetCurrentContext(current_context);
303 BIND(&is_unreachable);
309 CallRuntime(Runtime::kReportMessage, current_context,
310 var_exception.value());
311 LeaveMicrotaskContext();
312 SetCurrentContext(current_context);
319 TNode<Context> MicrotaskQueueBuiltinsAssembler::GetCurrentContext() {
320 auto ref = ExternalReference::Create(kContextAddress, isolate());
321 return TNode<Context>::UncheckedCast(
322 Load(MachineType::AnyTagged(), ExternalConstant(ref)));
325 void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
326 TNode<Context> context) {
327 auto ref = ExternalReference::Create(kContextAddress, isolate());
328 StoreNoWriteBarrier(MachineRepresentation::kTagged, ExternalConstant(ref),
332 void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext(
333 TNode<Context> native_context) {
334 CSA_ASSERT(
this, IsNativeContext(native_context));
336 auto ref = ExternalReference::handle_scope_implementer_address(isolate());
337 Node*
const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
339 MachineType::PointerRepresentation(), hsi,
340 IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
341 BitcastTaggedToWord(native_context));
345 auto type = kSizetSize == 8 ? MachineType::Uint64() : MachineType::Uint32();
346 Node* entered_contexts_length = Load(
348 IntPtrConstant(HandleScopeImplementerOffsets::kEnteredContextsCount));
350 auto rep = kSizetSize == 8 ? MachineRepresentation::kWord64
351 : MachineRepresentation::kWord32;
356 HandleScopeImplementerOffsets::kEnteredContextCountDuringMicrotasks),
357 entered_contexts_length);
360 void MicrotaskQueueBuiltinsAssembler::LeaveMicrotaskContext() {
361 auto ref = ExternalReference::handle_scope_implementer_address(isolate());
363 Node*
const hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
365 MachineType::PointerRepresentation(), hsi,
366 IntPtrConstant(HandleScopeImplementerOffsets::kMicrotaskContext),
368 if (kSizetSize == 4) {
370 MachineRepresentation::kWord32, hsi,
371 IntPtrConstant(HandleScopeImplementerOffsets::
372 kEnteredContextCountDuringMicrotasks),
376 MachineRepresentation::kWord64, hsi,
377 IntPtrConstant(HandleScopeImplementerOffsets::
378 kEnteredContextCountDuringMicrotasks),
383 void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
384 Runtime::FunctionId
id, TNode<Context> context,
385 SloppyTNode<HeapObject> promise_or_capability) {
386 Label hook(
this, Label::kDeferred), done_hook(
this);
387 Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), &hook,
392 TNode<HeapObject> promise = Select<HeapObject>(
393 IsPromiseCapability(promise_or_capability),
395 return CAST(LoadObjectField(promise_or_capability,
396 PromiseCapability::kPromiseOffset));
399 [=] {
return promise_or_capability; });
400 GotoIf(IsUndefined(promise), &done_hook);
401 CallRuntime(
id, context, promise);
407 TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
408 TNode<Microtask> microtask =
409 UncheckedCast<Microtask>(Parameter(Descriptor::kMicrotask));
410 TNode<Context> context =
411 UncheckedCast<Context>(Parameter(Descriptor::kContext));
412 TNode<Context> native_context = LoadNativeContext(context);
413 TNode<IntPtrT> microtask_queue = GetMicrotaskQueue(native_context);
415 TNode<IntPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
416 TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
417 TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
418 TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
421 GotoIf(IntPtrEqual(size, capacity), &if_grow);
425 StoreNoWriteBarrier(MachineType::PointerRepresentation(), ring_buffer,
426 CalculateRingBufferOffset(capacity, start, size),
427 BitcastTaggedToWord(microtask));
428 StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
429 IntPtrConstant(MicrotaskQueue::kSizeOffset),
430 IntPtrAdd(size, IntPtrConstant(1)));
431 Return(UndefinedConstant());
438 Node* isolate_constant =
439 ExternalConstant(ExternalReference::isolate_address(isolate()));
441 ExternalConstant(ExternalReference::call_enqueue_microtask_function());
442 CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
443 MachineType::IntPtr(), MachineType::AnyTagged(),
function,
444 isolate_constant, microtask_queue, microtask);
445 Return(UndefinedConstant());
449 TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
451 TNode<Context> current_context = GetCurrentContext();
454 TNode<IntPtrT> microtask_queue = GetDefaultMicrotaskQueue();
456 Label loop(
this), done(
this);
460 TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
463 GotoIf(WordEqual(size, IntPtrConstant(0)), &done);
465 TNode<IntPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
466 TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
467 TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
469 TNode<IntPtrT> offset =
470 CalculateRingBufferOffset(capacity, start, IntPtrConstant(0));
471 TNode<IntPtrT> microtask_pointer =
472 UncheckedCast<IntPtrT>(Load(MachineType::Pointer(), ring_buffer, offset));
473 TNode<Microtask> microtask =
474 UncheckedCast<Microtask>(BitcastWordToTagged(microtask_pointer));
476 TNode<IntPtrT> new_size = IntPtrSub(size, IntPtrConstant(1));
477 TNode<IntPtrT> new_start = WordAnd(IntPtrAdd(start, IntPtrConstant(1)),
478 IntPtrSub(capacity, IntPtrConstant(1)));
482 SetMicrotaskQueueSize(microtask_queue, new_size);
483 SetMicrotaskQueueStart(microtask_queue, new_start);
485 RunSingleMicrotask(current_context, microtask);
491 StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
492 Return(UndefinedConstant());