LCOV - code coverage report
Current view: top level - src/builtins - builtins-microtask-queue-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 274 274 100.0 %
Date: 2019-03-21 Functions: 33 33 100.0 %

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

Generated by: LCOV version 1.10