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