Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/api.h"
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"
12 : #include "src/objects/smi-inl.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : template <typename T>
18 : using TNode = compiler::TNode<T>;
19 :
20 112 : class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler {
21 : public:
22 112 : explicit MicrotaskQueueBuiltinsAssembler(compiler::CodeAssemblerState* state)
23 112 : : CodeStubAssembler(state) {}
24 :
25 : TNode<RawPtrT> GetMicrotaskQueue(TNode<Context> context);
26 : TNode<RawPtrT> GetMicrotaskRingBuffer(TNode<RawPtrT> microtask_queue);
27 : TNode<IntPtrT> GetMicrotaskQueueCapacity(TNode<RawPtrT> microtask_queue);
28 : TNode<IntPtrT> GetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue);
29 : void SetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue,
30 : TNode<IntPtrT> new_size);
31 : TNode<IntPtrT> GetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue);
32 : void SetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue,
33 : TNode<IntPtrT> new_start);
34 : TNode<IntPtrT> CalculateRingBufferOffset(TNode<IntPtrT> capacity,
35 : TNode<IntPtrT> start,
36 : TNode<IntPtrT> index);
37 :
38 : void PrepareForContext(TNode<Context> microtask_context);
39 : void RunSingleMicrotask(TNode<Context> current_context,
40 : TNode<Microtask> microtask);
41 : void IncrementFinishedMicrotaskCount(TNode<RawPtrT> microtask_queue);
42 :
43 : TNode<Context> GetCurrentContext();
44 : void SetCurrentContext(TNode<Context> context);
45 :
46 : TNode<IntPtrT> GetEnteredContextCount();
47 : void EnterMicrotaskContext(TNode<Context> native_context);
48 : void RewindEnteredContext(TNode<IntPtrT> saved_entered_context_count);
49 :
50 : void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context,
51 : SloppyTNode<HeapObject> promise_or_capability);
52 : };
53 :
54 56 : TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue(
55 : TNode<Context> native_context) {
56 : CSA_ASSERT(this, IsNativeContext(native_context));
57 : return LoadObjectField<RawPtrT>(native_context,
58 56 : NativeContext::kMicrotaskQueueOffset);
59 : }
60 :
61 112 : TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer(
62 : TNode<RawPtrT> microtask_queue) {
63 : return UncheckedCast<RawPtrT>(
64 : Load(MachineType::Pointer(), microtask_queue,
65 112 : IntPtrConstant(MicrotaskQueue::kRingBufferOffset)));
66 : }
67 :
68 112 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueCapacity(
69 : TNode<RawPtrT> microtask_queue) {
70 : return UncheckedCast<IntPtrT>(
71 : Load(MachineType::IntPtr(), microtask_queue,
72 112 : IntPtrConstant(MicrotaskQueue::kCapacityOffset)));
73 : }
74 :
75 112 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueSize(
76 : TNode<RawPtrT> microtask_queue) {
77 : return UncheckedCast<IntPtrT>(
78 : Load(MachineType::IntPtr(), microtask_queue,
79 112 : IntPtrConstant(MicrotaskQueue::kSizeOffset)));
80 : }
81 :
82 56 : void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueSize(
83 : TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_size) {
84 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
85 56 : IntPtrConstant(MicrotaskQueue::kSizeOffset), new_size);
86 56 : }
87 :
88 112 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueStart(
89 : TNode<RawPtrT> microtask_queue) {
90 : return UncheckedCast<IntPtrT>(
91 : Load(MachineType::IntPtr(), microtask_queue,
92 112 : IntPtrConstant(MicrotaskQueue::kStartOffset)));
93 : }
94 :
95 56 : void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueStart(
96 : TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_start) {
97 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
98 56 : IntPtrConstant(MicrotaskQueue::kStartOffset), new_start);
99 56 : }
100 :
101 112 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::CalculateRingBufferOffset(
102 : TNode<IntPtrT> capacity, TNode<IntPtrT> start, TNode<IntPtrT> index) {
103 : return TimesSystemPointerSize(
104 112 : WordAnd(IntPtrAdd(start, index), IntPtrSub(capacity, IntPtrConstant(1))));
105 : }
106 :
107 280 : void MicrotaskQueueBuiltinsAssembler::PrepareForContext(
108 : TNode<Context> native_context) {
109 : CSA_ASSERT(this, IsNativeContext(native_context));
110 280 : EnterMicrotaskContext(native_context);
111 280 : SetCurrentContext(native_context);
112 280 : }
113 :
114 56 : void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
115 : TNode<Context> current_context, TNode<Microtask> microtask) {
116 : CSA_ASSERT(this, TaggedIsNotSmi(microtask));
117 :
118 56 : StoreRoot(RootIndex::kCurrentMicrotask, microtask);
119 56 : TNode<IntPtrT> saved_entered_context_count = GetEnteredContextCount();
120 56 : TNode<Map> microtask_map = LoadMap(microtask);
121 56 : TNode<Int32T> microtask_type = LoadMapInstanceType(microtask_map);
122 :
123 56 : VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
124 112 : Label if_exception(this, Label::kDeferred);
125 112 : Label is_callable(this), is_callback(this),
126 112 : is_promise_fulfill_reaction_job(this),
127 112 : is_promise_reject_reaction_job(this),
128 112 : is_promise_resolve_thenable_job(this),
129 112 : is_finalization_group_cleanup_job(this),
130 112 : is_unreachable(this, Label::kDeferred), done(this);
131 :
132 : int32_t case_values[] = {CALLABLE_TASK_TYPE,
133 : CALLBACK_TASK_TYPE,
134 : PROMISE_FULFILL_REACTION_JOB_TASK_TYPE,
135 : PROMISE_REJECT_REACTION_JOB_TASK_TYPE,
136 : PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE,
137 56 : FINALIZATION_GROUP_CLEANUP_JOB_TASK_TYPE};
138 : Label* case_labels[] = {&is_callable,
139 : &is_callback,
140 : &is_promise_fulfill_reaction_job,
141 : &is_promise_reject_reaction_job,
142 : &is_promise_resolve_thenable_job,
143 56 : &is_finalization_group_cleanup_job};
144 : static_assert(arraysize(case_values) == arraysize(case_labels), "");
145 : Switch(microtask_type, &is_unreachable, case_values, case_labels,
146 56 : arraysize(case_labels));
147 :
148 56 : BIND(&is_callable);
149 : {
150 : // Enter the context of the {microtask}.
151 : TNode<Context> microtask_context =
152 56 : LoadObjectField<Context>(microtask, CallableTask::kContextOffset);
153 56 : TNode<Context> native_context = LoadNativeContext(microtask_context);
154 56 : PrepareForContext(native_context);
155 :
156 : TNode<JSReceiver> callable =
157 56 : LoadObjectField<JSReceiver>(microtask, CallableTask::kCallableOffset);
158 : Node* const result = CallJS(
159 : CodeFactory::Call(isolate(), ConvertReceiverMode::kNullOrUndefined),
160 56 : microtask_context, callable, UndefinedConstant());
161 56 : GotoIfException(result, &if_exception, &var_exception);
162 56 : RewindEnteredContext(saved_entered_context_count);
163 56 : SetCurrentContext(current_context);
164 56 : Goto(&done);
165 : }
166 :
167 56 : BIND(&is_callback);
168 : {
169 : Node* const microtask_callback =
170 56 : LoadObjectField(microtask, CallbackTask::kCallbackOffset);
171 : Node* const microtask_data =
172 56 : LoadObjectField(microtask, CallbackTask::kDataOffset);
173 :
174 : // If this turns out to become a bottleneck because of the calls
175 : // to C++ via CEntry, we can choose to speed them up using a
176 : // similar mechanism that we use for the CallApiFunction stub,
177 : // except that calling the MicrotaskCallback is even easier, since
178 : // it doesn't accept any tagged parameters, doesn't return a value
179 : // and ignores exceptions.
180 : //
181 : // But from our current measurements it doesn't seem to be a
182 : // serious performance problem, even if the microtask is full
183 : // of CallHandlerTasks (which is not a realistic use case anyways).
184 : Node* const result =
185 : CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
186 56 : microtask_callback, microtask_data);
187 56 : GotoIfException(result, &if_exception, &var_exception);
188 56 : Goto(&done);
189 : }
190 :
191 56 : BIND(&is_promise_resolve_thenable_job);
192 : {
193 : // Enter the context of the {microtask}.
194 : TNode<Context> microtask_context = LoadObjectField<Context>(
195 56 : microtask, PromiseResolveThenableJobTask::kContextOffset);
196 56 : TNode<Context> native_context = LoadNativeContext(microtask_context);
197 56 : PrepareForContext(native_context);
198 :
199 : Node* const promise_to_resolve = LoadObjectField(
200 56 : microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
201 : Node* const then =
202 56 : LoadObjectField(microtask, PromiseResolveThenableJobTask::kThenOffset);
203 : Node* const thenable = LoadObjectField(
204 56 : microtask, PromiseResolveThenableJobTask::kThenableOffset);
205 :
206 : Node* const result =
207 : CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
208 56 : promise_to_resolve, thenable, then);
209 56 : GotoIfException(result, &if_exception, &var_exception);
210 56 : RewindEnteredContext(saved_entered_context_count);
211 56 : SetCurrentContext(current_context);
212 56 : Goto(&done);
213 : }
214 :
215 56 : BIND(&is_promise_fulfill_reaction_job);
216 : {
217 : // Enter the context of the {microtask}.
218 : TNode<Context> microtask_context = LoadObjectField<Context>(
219 56 : microtask, PromiseReactionJobTask::kContextOffset);
220 56 : TNode<Context> native_context = LoadNativeContext(microtask_context);
221 56 : PrepareForContext(native_context);
222 :
223 : Node* const argument =
224 56 : LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
225 : Node* const handler =
226 56 : LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
227 : Node* const promise_or_capability = LoadObjectField(
228 56 : microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
229 :
230 : // Run the promise before/debug hook if enabled.
231 : RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
232 56 : promise_or_capability);
233 :
234 : Node* const result =
235 : CallBuiltin(Builtins::kPromiseFulfillReactionJob, microtask_context,
236 56 : argument, handler, promise_or_capability);
237 56 : GotoIfException(result, &if_exception, &var_exception);
238 :
239 : // Run the promise after/debug hook if enabled.
240 : RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
241 56 : promise_or_capability);
242 :
243 56 : RewindEnteredContext(saved_entered_context_count);
244 56 : SetCurrentContext(current_context);
245 56 : Goto(&done);
246 : }
247 :
248 56 : BIND(&is_promise_reject_reaction_job);
249 : {
250 : // Enter the context of the {microtask}.
251 : TNode<Context> microtask_context = LoadObjectField<Context>(
252 56 : microtask, PromiseReactionJobTask::kContextOffset);
253 56 : TNode<Context> native_context = LoadNativeContext(microtask_context);
254 56 : PrepareForContext(native_context);
255 :
256 : Node* const argument =
257 56 : LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
258 : Node* const handler =
259 56 : LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
260 : Node* const promise_or_capability = LoadObjectField(
261 56 : microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
262 :
263 : // Run the promise before/debug hook if enabled.
264 : RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
265 56 : promise_or_capability);
266 :
267 : Node* const result =
268 : CallBuiltin(Builtins::kPromiseRejectReactionJob, microtask_context,
269 56 : argument, handler, promise_or_capability);
270 56 : GotoIfException(result, &if_exception, &var_exception);
271 :
272 : // Run the promise after/debug hook if enabled.
273 : RunPromiseHook(Runtime::kPromiseHookAfter, microtask_context,
274 56 : promise_or_capability);
275 :
276 56 : RewindEnteredContext(saved_entered_context_count);
277 56 : SetCurrentContext(current_context);
278 56 : Goto(&done);
279 : }
280 :
281 56 : BIND(&is_finalization_group_cleanup_job);
282 : {
283 : // Enter the context of the {finalization_group}.
284 : TNode<JSFinalizationGroup> finalization_group =
285 : LoadObjectField<JSFinalizationGroup>(
286 : microtask,
287 56 : FinalizationGroupCleanupJobTask::kFinalizationGroupOffset);
288 : TNode<Context> native_context = LoadObjectField<Context>(
289 56 : finalization_group, JSFinalizationGroup::kNativeContextOffset);
290 56 : PrepareForContext(native_context);
291 :
292 : Node* const result = CallRuntime(Runtime::kFinalizationGroupCleanupJob,
293 56 : native_context, finalization_group);
294 :
295 56 : GotoIfException(result, &if_exception, &var_exception);
296 56 : RewindEnteredContext(saved_entered_context_count);
297 56 : SetCurrentContext(current_context);
298 56 : Goto(&done);
299 : }
300 :
301 56 : BIND(&is_unreachable);
302 56 : Unreachable();
303 :
304 56 : BIND(&if_exception);
305 : {
306 : // Report unhandled exceptions from microtasks.
307 : CallRuntime(Runtime::kReportMessage, current_context,
308 56 : var_exception.value());
309 56 : RewindEnteredContext(saved_entered_context_count);
310 56 : SetCurrentContext(current_context);
311 56 : Goto(&done);
312 : }
313 :
314 112 : BIND(&done);
315 56 : }
316 :
317 56 : void MicrotaskQueueBuiltinsAssembler::IncrementFinishedMicrotaskCount(
318 : TNode<RawPtrT> microtask_queue) {
319 : TNode<IntPtrT> count = UncheckedCast<IntPtrT>(
320 : Load(MachineType::IntPtr(), microtask_queue,
321 56 : IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset)));
322 56 : TNode<IntPtrT> new_count = IntPtrAdd(count, IntPtrConstant(1));
323 : StoreNoWriteBarrier(
324 : MachineType::PointerRepresentation(), microtask_queue,
325 56 : IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset), new_count);
326 56 : }
327 :
328 56 : TNode<Context> MicrotaskQueueBuiltinsAssembler::GetCurrentContext() {
329 56 : auto ref = ExternalReference::Create(kContextAddress, isolate());
330 56 : return TNode<Context>::UncheckedCast(LoadFullTagged(ExternalConstant(ref)));
331 : }
332 :
333 616 : void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
334 : TNode<Context> context) {
335 616 : auto ref = ExternalReference::Create(kContextAddress, isolate());
336 616 : StoreFullTaggedNoWriteBarrier(ExternalConstant(ref), context);
337 616 : }
338 :
339 56 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() {
340 56 : auto ref = ExternalReference::handle_scope_implementer_address(isolate());
341 56 : Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
342 :
343 : using ContextStack = DetachableVector<Context>;
344 : TNode<IntPtrT> size_offset =
345 56 : IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
346 56 : ContextStack::kSizeOffset);
347 : TNode<IntPtrT> size =
348 56 : UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
349 56 : return size;
350 : }
351 :
352 280 : void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext(
353 : TNode<Context> native_context) {
354 : CSA_ASSERT(this, IsNativeContext(native_context));
355 :
356 280 : auto ref = ExternalReference::handle_scope_implementer_address(isolate());
357 280 : Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
358 :
359 : using ContextStack = DetachableVector<Context>;
360 : TNode<IntPtrT> capacity_offset =
361 280 : IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
362 280 : ContextStack::kCapacityOffset);
363 : TNode<IntPtrT> size_offset =
364 280 : IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
365 280 : ContextStack::kSizeOffset);
366 :
367 : TNode<IntPtrT> capacity =
368 280 : UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, capacity_offset));
369 : TNode<IntPtrT> size =
370 280 : UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
371 :
372 560 : Label if_append(this), if_grow(this, Label::kDeferred), done(this);
373 280 : Branch(WordEqual(size, capacity), &if_grow, &if_append);
374 280 : BIND(&if_append);
375 : {
376 : TNode<IntPtrT> data_offset =
377 280 : IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
378 280 : ContextStack::kDataOffset);
379 280 : Node* data = Load(MachineType::Pointer(), hsi, data_offset);
380 280 : StoreFullTaggedNoWriteBarrier(data, TimesSystemPointerSize(size),
381 560 : native_context);
382 :
383 280 : TNode<IntPtrT> new_size = IntPtrAdd(size, IntPtrConstant(1));
384 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset,
385 280 : new_size);
386 :
387 : using FlagStack = DetachableVector<int8_t>;
388 : TNode<IntPtrT> flag_data_offset =
389 280 : IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
390 280 : FlagStack::kDataOffset);
391 280 : Node* flag_data = Load(MachineType::Pointer(), hsi, flag_data_offset);
392 : StoreNoWriteBarrier(MachineRepresentation::kWord8, flag_data, size,
393 280 : BoolConstant(true));
394 : StoreNoWriteBarrier(
395 : MachineType::PointerRepresentation(), hsi,
396 280 : IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
397 280 : FlagStack::kSizeOffset),
398 560 : new_size);
399 :
400 280 : Goto(&done);
401 : }
402 :
403 280 : BIND(&if_grow);
404 : {
405 : Node* function =
406 280 : ExternalConstant(ExternalReference::call_enter_context_function());
407 : CallCFunction2(MachineType::Int32(), MachineType::Pointer(),
408 : MachineType::Pointer(), function, hsi,
409 280 : BitcastTaggedToWord(native_context));
410 280 : Goto(&done);
411 : }
412 :
413 560 : BIND(&done);
414 280 : }
415 :
416 336 : void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
417 : TNode<IntPtrT> saved_entered_context_count) {
418 336 : auto ref = ExternalReference::handle_scope_implementer_address(isolate());
419 336 : Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
420 :
421 : using ContextStack = DetachableVector<Context>;
422 : TNode<IntPtrT> size_offset =
423 336 : IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
424 336 : ContextStack::kSizeOffset);
425 :
426 : #ifdef ENABLE_VERIFY_CSA
427 : TNode<IntPtrT> size =
428 : UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
429 : CSA_ASSERT(this, IntPtrLessThan(IntPtrConstant(0), size));
430 : CSA_ASSERT(this, IntPtrLessThanOrEqual(saved_entered_context_count, size));
431 : #endif
432 :
433 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset,
434 336 : saved_entered_context_count);
435 :
436 : using FlagStack = DetachableVector<int8_t>;
437 : StoreNoWriteBarrier(
438 : MachineType::PointerRepresentation(), hsi,
439 336 : IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
440 336 : FlagStack::kSizeOffset),
441 672 : saved_entered_context_count);
442 336 : }
443 :
444 224 : void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
445 : Runtime::FunctionId id, TNode<Context> context,
446 : SloppyTNode<HeapObject> promise_or_capability) {
447 448 : Label hook(this, Label::kDeferred), done_hook(this);
448 : Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), &hook,
449 224 : &done_hook);
450 224 : BIND(&hook);
451 : {
452 : // Get to the underlying JSPromise instance.
453 : TNode<HeapObject> promise = Select<HeapObject>(
454 224 : IsPromiseCapability(promise_or_capability),
455 224 : [=] {
456 224 : return CAST(LoadObjectField(promise_or_capability,
457 : PromiseCapability::kPromiseOffset));
458 224 : },
459 :
460 672 : [=] { return promise_or_capability; });
461 224 : GotoIf(IsUndefined(promise), &done_hook);
462 224 : CallRuntime(id, context, promise);
463 224 : Goto(&done_hook);
464 : }
465 448 : BIND(&done_hook);
466 224 : }
467 :
468 336 : TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
469 : TNode<Microtask> microtask =
470 56 : UncheckedCast<Microtask>(Parameter(Descriptor::kMicrotask));
471 56 : TNode<Context> context = CAST(Parameter(Descriptor::kContext));
472 56 : TNode<Context> native_context = LoadNativeContext(context);
473 56 : TNode<RawPtrT> microtask_queue = GetMicrotaskQueue(native_context);
474 :
475 56 : TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
476 56 : TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
477 56 : TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
478 56 : TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
479 :
480 56 : Label if_grow(this, Label::kDeferred);
481 56 : GotoIf(IntPtrEqual(size, capacity), &if_grow);
482 :
483 : // |microtask_queue| has an unused slot to store |microtask|.
484 : {
485 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), ring_buffer,
486 56 : CalculateRingBufferOffset(capacity, start, size),
487 112 : BitcastTaggedToWord(microtask));
488 : StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
489 56 : IntPtrConstant(MicrotaskQueue::kSizeOffset),
490 112 : IntPtrAdd(size, IntPtrConstant(1)));
491 56 : Return(UndefinedConstant());
492 : }
493 :
494 : // |microtask_queue| has no space to store |microtask|. Fall back to C++
495 : // implementation to grow the buffer.
496 56 : BIND(&if_grow);
497 : {
498 : Node* isolate_constant =
499 56 : ExternalConstant(ExternalReference::isolate_address(isolate()));
500 : Node* function =
501 56 : ExternalConstant(ExternalReference::call_enqueue_microtask_function());
502 : CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(),
503 : MachineType::IntPtr(), MachineType::AnyTagged(), function,
504 56 : isolate_constant, microtask_queue, microtask);
505 56 : Return(UndefinedConstant());
506 56 : }
507 56 : }
508 :
509 280 : TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
510 : // Load the current context from the isolate.
511 56 : TNode<Context> current_context = GetCurrentContext();
512 :
513 : TNode<RawPtrT> microtask_queue =
514 56 : UncheckedCast<RawPtrT>(Parameter(Descriptor::kMicrotaskQueue));
515 :
516 112 : Label loop(this), done(this);
517 56 : Goto(&loop);
518 56 : BIND(&loop);
519 :
520 56 : TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
521 :
522 : // Exit if the queue is empty.
523 56 : GotoIf(WordEqual(size, IntPtrConstant(0)), &done);
524 :
525 56 : TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
526 56 : TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
527 56 : TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
528 :
529 : TNode<IntPtrT> offset =
530 56 : CalculateRingBufferOffset(capacity, start, IntPtrConstant(0));
531 : TNode<RawPtrT> microtask_pointer =
532 56 : UncheckedCast<RawPtrT>(Load(MachineType::Pointer(), ring_buffer, offset));
533 56 : TNode<Microtask> microtask = CAST(BitcastWordToTagged(microtask_pointer));
534 :
535 56 : TNode<IntPtrT> new_size = IntPtrSub(size, IntPtrConstant(1));
536 : TNode<IntPtrT> new_start = WordAnd(IntPtrAdd(start, IntPtrConstant(1)),
537 56 : IntPtrSub(capacity, IntPtrConstant(1)));
538 :
539 : // Remove |microtask| from |ring_buffer| before running it, since its
540 : // invocation may add another microtask into |ring_buffer|.
541 56 : SetMicrotaskQueueSize(microtask_queue, new_size);
542 56 : SetMicrotaskQueueStart(microtask_queue, new_start);
543 :
544 56 : RunSingleMicrotask(current_context, microtask);
545 56 : IncrementFinishedMicrotaskCount(microtask_queue);
546 56 : Goto(&loop);
547 :
548 56 : BIND(&done);
549 : {
550 : // Reset the "current microtask" on the isolate.
551 56 : StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
552 56 : Return(UndefinedConstant());
553 56 : }
554 56 : }
555 :
556 : } // namespace internal
557 86739 : } // namespace v8
|