LCOV - code coverage report
Current view: top level - src/builtins - builtins-microtask-queue-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 204 205 99.5 %
Date: 2019-01-20 Functions: 21 22 95.5 %

          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

Generated by: LCOV version 1.10