• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/api.h"
6 #include "src/builtins/builtins-utils-gen.h"
7 #include "src/codegen/code-stub-assembler.h"
8 #include "src/execution/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 using compiler::ScopedExceptionHandler;
18 
19 class MicrotaskQueueBuiltinsAssembler : public CodeStubAssembler {
20  public:
MicrotaskQueueBuiltinsAssembler(compiler::CodeAssemblerState * state)21   explicit MicrotaskQueueBuiltinsAssembler(compiler::CodeAssemblerState* state)
22       : 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 
37   void PrepareForContext(TNode<Context> microtask_context, Label* bailout);
38   void RunSingleMicrotask(TNode<Context> current_context,
39                           TNode<Microtask> microtask);
40   void IncrementFinishedMicrotaskCount(TNode<RawPtrT> microtask_queue);
41 
42   TNode<Context> GetCurrentContext();
43   void SetCurrentContext(TNode<Context> context);
44 
45   TNode<IntPtrT> GetEnteredContextCount();
46   void EnterMicrotaskContext(TNode<Context> native_context);
47   void RewindEnteredContext(TNode<IntPtrT> saved_entered_context_count);
48 
49   void RunAllPromiseHooks(PromiseHookType type, TNode<Context> context,
50                           TNode<HeapObject> promise_or_capability);
51   void RunPromiseHook(Runtime::FunctionId id, TNode<Context> context,
52                       TNode<HeapObject> promise_or_capability,
53                       TNode<Uint32T> promiseHookFlags);
54 };
55 
GetMicrotaskQueue(TNode<Context> native_context)56 TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueue(
57     TNode<Context> native_context) {
58   CSA_DCHECK(this, IsNativeContext(native_context));
59   return LoadExternalPointerFromObject(native_context,
60                                        NativeContext::kMicrotaskQueueOffset,
61                                        kNativeContextMicrotaskQueueTag);
62 }
63 
GetMicrotaskRingBuffer(TNode<RawPtrT> microtask_queue)64 TNode<RawPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskRingBuffer(
65     TNode<RawPtrT> microtask_queue) {
66   return Load<RawPtrT>(microtask_queue,
67                        IntPtrConstant(MicrotaskQueue::kRingBufferOffset));
68 }
69 
GetMicrotaskQueueCapacity(TNode<RawPtrT> microtask_queue)70 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueCapacity(
71     TNode<RawPtrT> microtask_queue) {
72   return Load<IntPtrT>(microtask_queue,
73                        IntPtrConstant(MicrotaskQueue::kCapacityOffset));
74 }
75 
GetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue)76 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueSize(
77     TNode<RawPtrT> microtask_queue) {
78   return Load<IntPtrT>(microtask_queue,
79                        IntPtrConstant(MicrotaskQueue::kSizeOffset));
80 }
81 
SetMicrotaskQueueSize(TNode<RawPtrT> microtask_queue,TNode<IntPtrT> new_size)82 void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueSize(
83     TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_size) {
84   StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
85                       IntPtrConstant(MicrotaskQueue::kSizeOffset), new_size);
86 }
87 
GetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue)88 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetMicrotaskQueueStart(
89     TNode<RawPtrT> microtask_queue) {
90   return Load<IntPtrT>(microtask_queue,
91                        IntPtrConstant(MicrotaskQueue::kStartOffset));
92 }
93 
SetMicrotaskQueueStart(TNode<RawPtrT> microtask_queue,TNode<IntPtrT> new_start)94 void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueStart(
95     TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_start) {
96   StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
97                       IntPtrConstant(MicrotaskQueue::kStartOffset), new_start);
98 }
99 
CalculateRingBufferOffset(TNode<IntPtrT> capacity,TNode<IntPtrT> start,TNode<IntPtrT> index)100 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::CalculateRingBufferOffset(
101     TNode<IntPtrT> capacity, TNode<IntPtrT> start, TNode<IntPtrT> index) {
102   return TimesSystemPointerSize(
103       WordAnd(IntPtrAdd(start, index), IntPtrSub(capacity, IntPtrConstant(1))));
104 }
105 
PrepareForContext(TNode<Context> native_context,Label * bailout)106 void MicrotaskQueueBuiltinsAssembler::PrepareForContext(
107     TNode<Context> native_context, Label* bailout) {
108   CSA_DCHECK(this, IsNativeContext(native_context));
109 
110   // Skip the microtask execution if the associated context is shutdown.
111   GotoIf(WordEqual(GetMicrotaskQueue(native_context), IntPtrConstant(0)),
112          bailout);
113 
114   EnterMicrotaskContext(native_context);
115   SetCurrentContext(native_context);
116 }
117 
RunSingleMicrotask(TNode<Context> current_context,TNode<Microtask> microtask)118 void MicrotaskQueueBuiltinsAssembler::RunSingleMicrotask(
119     TNode<Context> current_context, TNode<Microtask> microtask) {
120   CSA_DCHECK(this, TaggedIsNotSmi(microtask));
121 
122   StoreRoot(RootIndex::kCurrentMicrotask, microtask);
123   TNode<IntPtrT> saved_entered_context_count = GetEnteredContextCount();
124   TNode<Map> microtask_map = LoadMap(microtask);
125   TNode<Uint16T> microtask_type = LoadMapInstanceType(microtask_map);
126 
127   TVARIABLE(Object, var_exception);
128   Label if_exception(this, Label::kDeferred);
129   Label is_callable(this), is_callback(this),
130       is_promise_fulfill_reaction_job(this),
131       is_promise_reject_reaction_job(this),
132       is_promise_resolve_thenable_job(this),
133       is_unreachable(this, Label::kDeferred), done(this);
134 
135   int32_t case_values[] = {CALLABLE_TASK_TYPE, CALLBACK_TASK_TYPE,
136                            PROMISE_FULFILL_REACTION_JOB_TASK_TYPE,
137                            PROMISE_REJECT_REACTION_JOB_TASK_TYPE,
138                            PROMISE_RESOLVE_THENABLE_JOB_TASK_TYPE};
139   Label* case_labels[] = {
140       &is_callable, &is_callback, &is_promise_fulfill_reaction_job,
141       &is_promise_reject_reaction_job, &is_promise_resolve_thenable_job};
142   static_assert(arraysize(case_values) == arraysize(case_labels), "");
143   Switch(microtask_type, &is_unreachable, case_values, case_labels,
144          arraysize(case_labels));
145 
146   BIND(&is_callable);
147   {
148     // Enter the context of the {microtask}.
149     TNode<Context> microtask_context =
150         LoadObjectField<Context>(microtask, CallableTask::kContextOffset);
151     TNode<NativeContext> native_context = LoadNativeContext(microtask_context);
152     PrepareForContext(native_context, &done);
153 
154     TNode<JSReceiver> callable =
155         LoadObjectField<JSReceiver>(microtask, CallableTask::kCallableOffset);
156     {
157       ScopedExceptionHandler handler(this, &if_exception, &var_exception);
158       Call(microtask_context, callable, UndefinedConstant());
159     }
160     RewindEnteredContext(saved_entered_context_count);
161     SetCurrentContext(current_context);
162     Goto(&done);
163   }
164 
165   BIND(&is_callback);
166   {
167     const TNode<Object> microtask_callback =
168         LoadObjectField(microtask, CallbackTask::kCallbackOffset);
169     const TNode<Object> microtask_data =
170         LoadObjectField(microtask, CallbackTask::kDataOffset);
171 
172     // If this turns out to become a bottleneck because of the calls
173     // to C++ via CEntry, we can choose to speed them up using a
174     // similar mechanism that we use for the CallApiFunction stub,
175     // except that calling the MicrotaskCallback is even easier, since
176     // it doesn't accept any tagged parameters, doesn't return a value
177     // and ignores exceptions.
178     //
179     // But from our current measurements it doesn't seem to be a
180     // serious performance problem, even if the microtask is full
181     // of CallHandlerTasks (which is not a realistic use case anyways).
182     {
183       ScopedExceptionHandler handler(this, &if_exception, &var_exception);
184       CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
185                   microtask_callback, microtask_data);
186     }
187     Goto(&done);
188   }
189 
190   BIND(&is_promise_resolve_thenable_job);
191   {
192     // Enter the context of the {microtask}.
193     TNode<Context> microtask_context = LoadObjectField<Context>(
194         microtask, PromiseResolveThenableJobTask::kContextOffset);
195     TNode<NativeContext> native_context = LoadNativeContext(microtask_context);
196     PrepareForContext(native_context, &done);
197 
198     const TNode<Object> promise_to_resolve = LoadObjectField(
199         microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
200     const TNode<Object> then =
201         LoadObjectField(microtask, PromiseResolveThenableJobTask::kThenOffset);
202     const TNode<Object> thenable = LoadObjectField(
203         microtask, PromiseResolveThenableJobTask::kThenableOffset);
204 
205     RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context,
206                    CAST(promise_to_resolve));
207 
208     {
209       ScopedExceptionHandler handler(this, &if_exception, &var_exception);
210       CallBuiltin(Builtin::kPromiseResolveThenableJob, native_context,
211                   promise_to_resolve, thenable, then);
212     }
213 
214     RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context,
215                    CAST(promise_to_resolve));
216 
217     RewindEnteredContext(saved_entered_context_count);
218     SetCurrentContext(current_context);
219     Goto(&done);
220   }
221 
222   BIND(&is_promise_fulfill_reaction_job);
223   {
224     // Enter the context of the {microtask}.
225     TNode<Context> microtask_context = LoadObjectField<Context>(
226         microtask, PromiseReactionJobTask::kContextOffset);
227     TNode<NativeContext> native_context = LoadNativeContext(microtask_context);
228     PrepareForContext(native_context, &done);
229 
230     const TNode<Object> argument =
231         LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
232     const TNode<Object> job_handler =
233         LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
234     const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField(
235         microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset));
236 
237     TNode<Object> preserved_embedder_data = LoadObjectField(
238         microtask,
239         PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset);
240     Label preserved_data_done(this);
241     GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_done);
242     StoreContextElement(native_context,
243                         Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
244                         preserved_embedder_data);
245     Goto(&preserved_data_done);
246     BIND(&preserved_data_done);
247 
248     // Run the promise before/debug hook if enabled.
249     RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context,
250                        promise_or_capability);
251 
252     {
253       ScopedExceptionHandler handler(this, &if_exception, &var_exception);
254       CallBuiltin(Builtin::kPromiseFulfillReactionJob, microtask_context,
255                   argument, job_handler, promise_or_capability);
256     }
257 
258     // Run the promise after/debug hook if enabled.
259     RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context,
260                        promise_or_capability);
261 
262     Label preserved_data_reset_done(this);
263     GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_reset_done);
264     StoreContextElement(native_context,
265                         Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
266                         UndefinedConstant());
267     Goto(&preserved_data_reset_done);
268     BIND(&preserved_data_reset_done);
269 
270     RewindEnteredContext(saved_entered_context_count);
271     SetCurrentContext(current_context);
272     Goto(&done);
273   }
274 
275   BIND(&is_promise_reject_reaction_job);
276   {
277     // Enter the context of the {microtask}.
278     TNode<Context> microtask_context = LoadObjectField<Context>(
279         microtask, PromiseReactionJobTask::kContextOffset);
280     TNode<NativeContext> native_context = LoadNativeContext(microtask_context);
281     PrepareForContext(native_context, &done);
282 
283     const TNode<Object> argument =
284         LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
285     const TNode<Object> job_handler =
286         LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
287     const TNode<HeapObject> promise_or_capability = CAST(LoadObjectField(
288         microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset));
289 
290     TNode<Object> preserved_embedder_data = LoadObjectField(
291         microtask,
292         PromiseReactionJobTask::kContinuationPreservedEmbedderDataOffset);
293     Label preserved_data_done(this);
294     GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_done);
295     StoreContextElement(native_context,
296                         Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
297                         preserved_embedder_data);
298     Goto(&preserved_data_done);
299     BIND(&preserved_data_done);
300 
301     // Run the promise before/debug hook if enabled.
302     RunAllPromiseHooks(PromiseHookType::kBefore, microtask_context,
303                        promise_or_capability);
304 
305     {
306       ScopedExceptionHandler handler(this, &if_exception, &var_exception);
307       CallBuiltin(Builtin::kPromiseRejectReactionJob, microtask_context,
308                   argument, job_handler, promise_or_capability);
309     }
310 
311     // Run the promise after/debug hook if enabled.
312     RunAllPromiseHooks(PromiseHookType::kAfter, microtask_context,
313                        promise_or_capability);
314 
315     Label preserved_data_reset_done(this);
316     GotoIf(IsUndefined(preserved_embedder_data), &preserved_data_reset_done);
317     StoreContextElement(native_context,
318                         Context::CONTINUATION_PRESERVED_EMBEDDER_DATA_INDEX,
319                         UndefinedConstant());
320     Goto(&preserved_data_reset_done);
321     BIND(&preserved_data_reset_done);
322 
323     RewindEnteredContext(saved_entered_context_count);
324     SetCurrentContext(current_context);
325     Goto(&done);
326   }
327 
328   BIND(&is_unreachable);
329   Unreachable();
330 
331   BIND(&if_exception);
332   {
333     // Report unhandled exceptions from microtasks.
334     CallRuntime(Runtime::kReportMessageFromMicrotask, GetCurrentContext(),
335                 var_exception.value());
336     RewindEnteredContext(saved_entered_context_count);
337     SetCurrentContext(current_context);
338     Goto(&done);
339   }
340 
341   BIND(&done);
342 }
343 
IncrementFinishedMicrotaskCount(TNode<RawPtrT> microtask_queue)344 void MicrotaskQueueBuiltinsAssembler::IncrementFinishedMicrotaskCount(
345     TNode<RawPtrT> microtask_queue) {
346   TNode<IntPtrT> count = Load<IntPtrT>(
347       microtask_queue,
348       IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset));
349   TNode<IntPtrT> new_count = IntPtrAdd(count, IntPtrConstant(1));
350   StoreNoWriteBarrier(
351       MachineType::PointerRepresentation(), microtask_queue,
352       IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset), new_count);
353 }
354 
GetCurrentContext()355 TNode<Context> MicrotaskQueueBuiltinsAssembler::GetCurrentContext() {
356   auto ref = ExternalReference::Create(kContextAddress, isolate());
357   // TODO(delphick): Add a checked cast. For now this is not possible as context
358   // can actually be Smi(0).
359   return TNode<Context>::UncheckedCast(LoadFullTagged(ExternalConstant(ref)));
360 }
361 
SetCurrentContext(TNode<Context> context)362 void MicrotaskQueueBuiltinsAssembler::SetCurrentContext(
363     TNode<Context> context) {
364   auto ref = ExternalReference::Create(kContextAddress, isolate());
365   StoreFullTaggedNoWriteBarrier(ExternalConstant(ref), context);
366 }
367 
GetEnteredContextCount()368 TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() {
369   auto ref = ExternalReference::handle_scope_implementer_address(isolate());
370   TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
371 
372   using ContextStack = DetachableVector<Context>;
373   TNode<IntPtrT> size_offset =
374       IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
375                      ContextStack::kSizeOffset);
376   return Load<IntPtrT>(hsi, size_offset);
377 }
378 
EnterMicrotaskContext(TNode<Context> native_context)379 void MicrotaskQueueBuiltinsAssembler::EnterMicrotaskContext(
380     TNode<Context> native_context) {
381   CSA_DCHECK(this, IsNativeContext(native_context));
382 
383   auto ref = ExternalReference::handle_scope_implementer_address(isolate());
384   TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
385 
386   using ContextStack = DetachableVector<Context>;
387   TNode<IntPtrT> capacity_offset =
388       IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
389                      ContextStack::kCapacityOffset);
390   TNode<IntPtrT> size_offset =
391       IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
392                      ContextStack::kSizeOffset);
393 
394   TNode<IntPtrT> capacity = Load<IntPtrT>(hsi, capacity_offset);
395   TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset);
396 
397   Label if_append(this), if_grow(this, Label::kDeferred), done(this);
398   Branch(WordEqual(size, capacity), &if_grow, &if_append);
399   BIND(&if_append);
400   {
401     TNode<IntPtrT> data_offset =
402         IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
403                        ContextStack::kDataOffset);
404     TNode<RawPtrT> data = Load<RawPtrT>(hsi, data_offset);
405     StoreFullTaggedNoWriteBarrier(data, TimesSystemPointerSize(size),
406                                   native_context);
407 
408     TNode<IntPtrT> new_size = IntPtrAdd(size, IntPtrConstant(1));
409     StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset,
410                         new_size);
411 
412     using FlagStack = DetachableVector<int8_t>;
413     TNode<IntPtrT> flag_data_offset =
414         IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
415                        FlagStack::kDataOffset);
416     TNode<IntPtrT> flag_capacity_offset =
417         IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
418                        FlagStack::kCapacityOffset);
419     TNode<IntPtrT> flag_size_offset =
420         IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
421                        FlagStack::kSizeOffset);
422     // Ensure both stacks are in sync.
423     USE(flag_capacity_offset);
424     CSA_DCHECK(this,
425                WordEqual(capacity, Load<IntPtrT>(hsi, flag_capacity_offset)));
426     CSA_DCHECK(this, WordEqual(size, Load<IntPtrT>(hsi, flag_size_offset)));
427 
428     TNode<RawPtrT> flag_data = Load<RawPtrT>(hsi, flag_data_offset);
429     StoreNoWriteBarrier(MachineRepresentation::kWord8, flag_data, size,
430                         BoolConstant(true));
431     StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi,
432                         flag_size_offset, new_size);
433 
434     Goto(&done);
435   }
436 
437   BIND(&if_grow);
438   {
439     TNode<ExternalReference> function =
440         ExternalConstant(ExternalReference::call_enter_context_function());
441     CallCFunction(function, MachineType::Int32(),
442                   std::make_pair(MachineType::Pointer(), hsi),
443                   std::make_pair(MachineType::Pointer(),
444                                  BitcastTaggedToWord(native_context)));
445     Goto(&done);
446   }
447 
448   BIND(&done);
449 }
450 
RewindEnteredContext(TNode<IntPtrT> saved_entered_context_count)451 void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
452     TNode<IntPtrT> saved_entered_context_count) {
453   auto ref = ExternalReference::handle_scope_implementer_address(isolate());
454   TNode<RawPtrT> hsi = Load<RawPtrT>(ExternalConstant(ref));
455 
456   using ContextStack = DetachableVector<Context>;
457   TNode<IntPtrT> size_offset =
458       IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
459                      ContextStack::kSizeOffset);
460 
461   if (DEBUG_BOOL) {
462     TNode<IntPtrT> size = Load<IntPtrT>(hsi, size_offset);
463     CSA_CHECK(this, IntPtrLessThan(IntPtrConstant(0), size));
464     CSA_CHECK(this, IntPtrLessThanOrEqual(saved_entered_context_count, size));
465   }
466 
467   StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset,
468                       saved_entered_context_count);
469 
470   using FlagStack = DetachableVector<int8_t>;
471   StoreNoWriteBarrier(
472       MachineType::PointerRepresentation(), hsi,
473       IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
474                      FlagStack::kSizeOffset),
475       saved_entered_context_count);
476 }
477 
RunAllPromiseHooks(PromiseHookType type,TNode<Context> context,TNode<HeapObject> promise_or_capability)478 void MicrotaskQueueBuiltinsAssembler::RunAllPromiseHooks(
479     PromiseHookType type, TNode<Context> context,
480     TNode<HeapObject> promise_or_capability) {
481   TNode<Uint32T> promiseHookFlags = PromiseHookFlags();
482 #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
483   Label hook(this, Label::kDeferred), done_hook(this);
484   Branch(NeedsAnyPromiseHooks(promiseHookFlags), &hook, &done_hook);
485   BIND(&hook);
486   {
487 #endif
488     switch (type) {
489       case PromiseHookType::kBefore:
490 #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
491         RunContextPromiseHookBefore(context, promise_or_capability,
492                                     promiseHookFlags);
493 #endif
494         RunPromiseHook(Runtime::kPromiseHookBefore, context,
495                        promise_or_capability, promiseHookFlags);
496         break;
497       case PromiseHookType::kAfter:
498 #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
499         RunContextPromiseHookAfter(context, promise_or_capability,
500                                    promiseHookFlags);
501 #endif
502         RunPromiseHook(Runtime::kPromiseHookAfter, context,
503                        promise_or_capability, promiseHookFlags);
504         break;
505       default:
506         UNREACHABLE();
507     }
508 #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS
509     Goto(&done_hook);
510   }
511   BIND(&done_hook);
512 #endif
513 }
514 
RunPromiseHook(Runtime::FunctionId id,TNode<Context> context,TNode<HeapObject> promise_or_capability,TNode<Uint32T> promiseHookFlags)515 void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
516     Runtime::FunctionId id, TNode<Context> context,
517     TNode<HeapObject> promise_or_capability,
518     TNode<Uint32T> promiseHookFlags) {
519   Label hook(this, Label::kDeferred), done_hook(this);
520   Branch(IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(
521       promiseHookFlags), &hook, &done_hook);
522   BIND(&hook);
523   {
524     // Get to the underlying JSPromise instance.
525     TNode<HeapObject> promise = Select<HeapObject>(
526         IsPromiseCapability(promise_or_capability),
527         [=] {
528           return CAST(LoadObjectField(promise_or_capability,
529                                       PromiseCapability::kPromiseOffset));
530         },
531 
532         [=] { return promise_or_capability; });
533     GotoIf(IsUndefined(promise), &done_hook);
534     CallRuntime(id, context, promise);
535     Goto(&done_hook);
536   }
537   BIND(&done_hook);
538 }
539 
TF_BUILTIN(EnqueueMicrotask,MicrotaskQueueBuiltinsAssembler)540 TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
541   auto microtask = Parameter<Microtask>(Descriptor::kMicrotask);
542   auto context = Parameter<Context>(Descriptor::kContext);
543   TNode<NativeContext> native_context = LoadNativeContext(context);
544   TNode<RawPtrT> microtask_queue = GetMicrotaskQueue(native_context);
545 
546   // Do not store the microtask if MicrotaskQueue is not available, that may
547   // happen when the context shutdown.
548   Label if_shutdown(this, Label::kDeferred);
549   GotoIf(WordEqual(microtask_queue, IntPtrConstant(0)), &if_shutdown);
550 
551   TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
552   TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
553   TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
554   TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
555 
556   Label if_grow(this, Label::kDeferred);
557   GotoIf(IntPtrEqual(size, capacity), &if_grow);
558 
559   // |microtask_queue| has an unused slot to store |microtask|.
560   {
561     StoreNoWriteBarrier(MachineType::PointerRepresentation(), ring_buffer,
562                         CalculateRingBufferOffset(capacity, start, size),
563                         BitcastTaggedToWord(microtask));
564     StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
565                         IntPtrConstant(MicrotaskQueue::kSizeOffset),
566                         IntPtrAdd(size, IntPtrConstant(1)));
567     Return(UndefinedConstant());
568   }
569 
570   // |microtask_queue| has no space to store |microtask|. Fall back to C++
571   // implementation to grow the buffer.
572   BIND(&if_grow);
573   {
574     TNode<ExternalReference> isolate_constant =
575         ExternalConstant(ExternalReference::isolate_address(isolate()));
576     TNode<ExternalReference> function =
577         ExternalConstant(ExternalReference::call_enqueue_microtask_function());
578     CallCFunction(function, MachineType::AnyTagged(),
579                   std::make_pair(MachineType::Pointer(), isolate_constant),
580                   std::make_pair(MachineType::IntPtr(), microtask_queue),
581                   std::make_pair(MachineType::AnyTagged(), microtask));
582     Return(UndefinedConstant());
583   }
584 
585   Bind(&if_shutdown);
586   Return(UndefinedConstant());
587 }
588 
TF_BUILTIN(RunMicrotasks,MicrotaskQueueBuiltinsAssembler)589 TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
590   // Load the current context from the isolate.
591   TNode<Context> current_context = GetCurrentContext();
592 
593   auto microtask_queue =
594       UncheckedParameter<RawPtrT>(Descriptor::kMicrotaskQueue);
595 
596   Label loop(this), done(this);
597   Goto(&loop);
598   BIND(&loop);
599 
600   TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
601 
602   // Exit if the queue is empty.
603   GotoIf(WordEqual(size, IntPtrConstant(0)), &done);
604 
605   TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
606   TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
607   TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
608 
609   TNode<IntPtrT> offset =
610       CalculateRingBufferOffset(capacity, start, IntPtrConstant(0));
611   TNode<RawPtrT> microtask_pointer = Load<RawPtrT>(ring_buffer, offset);
612   TNode<Microtask> microtask = CAST(BitcastWordToTagged(microtask_pointer));
613 
614   TNode<IntPtrT> new_size = IntPtrSub(size, IntPtrConstant(1));
615   TNode<IntPtrT> new_start = WordAnd(IntPtrAdd(start, IntPtrConstant(1)),
616                                      IntPtrSub(capacity, IntPtrConstant(1)));
617 
618   // Remove |microtask| from |ring_buffer| before running it, since its
619   // invocation may add another microtask into |ring_buffer|.
620   SetMicrotaskQueueSize(microtask_queue, new_size);
621   SetMicrotaskQueueStart(microtask_queue, new_start);
622 
623   RunSingleMicrotask(current_context, microtask);
624   IncrementFinishedMicrotaskCount(microtask_queue);
625   Goto(&loop);
626 
627   BIND(&done);
628   {
629     // Reset the "current microtask" on the isolate.
630     StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
631     Return(UndefinedConstant());
632   }
633 }
634 
635 }  // namespace internal
636 }  // namespace v8
637