LCOV - code coverage report
Current view: top level - src/builtins - builtins-microtask-queue-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 227 228 99.6 %
Date: 2019-04-17 Functions: 22 23 95.7 %

          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             :   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           0 : 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         224 :            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         224 :            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         224 :            IntPtrConstant(MicrotaskQueue::kSizeOffset)));
      80             : }
      81             : 
      82          56 : void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueSize(
      83             :     TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_size) {
      84             :   StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
      85         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         224 :            IntPtrConstant(MicrotaskQueue::kStartOffset)));
      93             : }
      94             : 
      95          56 : void MicrotaskQueueBuiltinsAssembler::SetMicrotaskQueueStart(
      96             :     TNode<RawPtrT> microtask_queue, TNode<IntPtrT> new_start) {
      97             :   StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
      98         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         336 :       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         840 :   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         168 :   VARIABLE(var_exception, MachineRepresentation::kTagged, TheHoleConstant());
     129          56 :   Label if_exception(this, Label::kDeferred);
     130          56 :   Label is_callable(this), is_callback(this),
     131          56 :       is_promise_fulfill_reaction_job(this),
     132          56 :       is_promise_reject_reaction_job(this),
     133          56 :       is_promise_resolve_thenable_job(this),
     134          56 :       is_finalization_group_cleanup_job(this),
     135          56 :       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             :   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             :         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             :         LoadObjectField<JSReceiver>(microtask, CallableTask::kCallableOffset);
     163             :     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             :         LoadObjectField(microtask, CallbackTask::kCallbackOffset);
     176             :     Node* const microtask_data =
     177             :         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             :         CallRuntime(Runtime::kRunMicrotaskCallback, current_context,
     191             :                     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             :         microtask, PromiseResolveThenableJobTask::kContextOffset);
     201          56 :     TNode<Context> native_context = LoadNativeContext(microtask_context);
     202          56 :     PrepareForContext(native_context, &done);
     203             : 
     204             :     Node* const promise_to_resolve = LoadObjectField(
     205             :         microtask, PromiseResolveThenableJobTask::kPromiseToResolveOffset);
     206             :     Node* const then =
     207             :         LoadObjectField(microtask, PromiseResolveThenableJobTask::kThenOffset);
     208             :     Node* const thenable = LoadObjectField(
     209             :         microtask, PromiseResolveThenableJobTask::kThenableOffset);
     210             : 
     211             :     Node* const result =
     212         112 :         CallBuiltin(Builtins::kPromiseResolveThenableJob, native_context,
     213          56 :                     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             :         microtask, PromiseReactionJobTask::kContextOffset);
     225          56 :     TNode<Context> native_context = LoadNativeContext(microtask_context);
     226          56 :     PrepareForContext(native_context, &done);
     227             : 
     228             :     Node* const argument =
     229             :         LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
     230             :     Node* const handler =
     231             :         LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
     232             :     Node* const promise_or_capability = LoadObjectField(
     233             :         microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
     234             : 
     235             :     // Run the promise before/debug hook if enabled.
     236          56 :     RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
     237          56 :                    promise_or_capability);
     238             : 
     239             :     Node* const result =
     240         112 :         CallBuiltin(Builtins::kPromiseFulfillReactionJob, microtask_context,
     241          56 :                     argument, handler, promise_or_capability);
     242          56 :     GotoIfException(result, &if_exception, &var_exception);
     243             : 
     244             :     // Run the promise after/debug hook if enabled.
     245          56 :     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             :         microtask, PromiseReactionJobTask::kContextOffset);
     258          56 :     TNode<Context> native_context = LoadNativeContext(microtask_context);
     259          56 :     PrepareForContext(native_context, &done);
     260             : 
     261             :     Node* const argument =
     262             :         LoadObjectField(microtask, PromiseReactionJobTask::kArgumentOffset);
     263             :     Node* const handler =
     264             :         LoadObjectField(microtask, PromiseReactionJobTask::kHandlerOffset);
     265             :     Node* const promise_or_capability = LoadObjectField(
     266             :         microtask, PromiseReactionJobTask::kPromiseOrCapabilityOffset);
     267             : 
     268             :     // Run the promise before/debug hook if enabled.
     269          56 :     RunPromiseHook(Runtime::kPromiseHookBefore, microtask_context,
     270          56 :                    promise_or_capability);
     271             : 
     272             :     Node* const result =
     273         112 :         CallBuiltin(Builtins::kPromiseRejectReactionJob, microtask_context,
     274          56 :                     argument, handler, promise_or_capability);
     275          56 :     GotoIfException(result, &if_exception, &var_exception);
     276             : 
     277             :     // Run the promise after/debug hook if enabled.
     278          56 :     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             :             FinalizationGroupCleanupJobTask::kFinalizationGroupOffset);
     293             :     TNode<Context> native_context = LoadObjectField<Context>(
     294             :         finalization_group, JSFinalizationGroup::kNativeContextOffset);
     295          56 :     PrepareForContext(native_context, &done);
     296             : 
     297             :     Node* const result = CallRuntime(Runtime::kFinalizationGroupCleanupJob,
     298             :                                      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         112 :            IntPtrConstant(MicrotaskQueue::kFinishedMicrotaskCountOffset)));
     327          56 :   TNode<IntPtrT> new_count = IntPtrAdd(count, IntPtrConstant(1));
     328             :   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         112 :   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        1232 :   StoreFullTaggedNoWriteBarrier(ExternalConstant(ref), context);
     342         616 : }
     343             : 
     344          56 : TNode<IntPtrT> MicrotaskQueueBuiltinsAssembler::GetEnteredContextCount() {
     345          56 :   auto ref = ExternalReference::handle_scope_implementer_address(isolate());
     346         112 :   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         560 :   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         280 :   Label if_append(this), if_grow(this, Label::kDeferred), done(this);
     378         560 :   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             :     StoreFullTaggedNoWriteBarrier(data, TimesSystemPointerSize(size),
     386         280 :                                   native_context);
     387             : 
     388         280 :     TNode<IntPtrT> new_size = IntPtrAdd(size, IntPtrConstant(1));
     389             :     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             :     StoreNoWriteBarrier(MachineRepresentation::kWord8, flag_data, size,
     398         560 :                         BoolConstant(true));
     399             :     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         560 :         ExternalConstant(ExternalReference::call_enter_context_function());
     412             :     CallCFunction(function, MachineType::Int32(),
     413             :                   std::make_pair(MachineType::Pointer(), hsi),
     414             :                   std::make_pair(MachineType::Pointer(),
     415         560 :                                  BitcastTaggedToWord(native_context)));
     416         280 :     Goto(&done);
     417             :   }
     418             : 
     419         280 :   BIND(&done);
     420         280 : }
     421             : 
     422         336 : void MicrotaskQueueBuiltinsAssembler::RewindEnteredContext(
     423             :     TNode<IntPtrT> saved_entered_context_count) {
     424         336 :   auto ref = ExternalReference::handle_scope_implementer_address(isolate());
     425         672 :   Node* hsi = Load(MachineType::Pointer(), ExternalConstant(ref));
     426             : 
     427             :   using ContextStack = DetachableVector<Context>;
     428             :   TNode<IntPtrT> size_offset =
     429         336 :       IntPtrConstant(HandleScopeImplementer::kEnteredContextsOffset +
     430         336 :                      ContextStack::kSizeOffset);
     431             : 
     432             : #ifdef ENABLE_VERIFY_CSA
     433             :   TNode<IntPtrT> size =
     434             :       UncheckedCast<IntPtrT>(Load(MachineType::IntPtr(), hsi, size_offset));
     435             :   CSA_ASSERT(this, IntPtrLessThan(IntPtrConstant(0), size));
     436             :   CSA_ASSERT(this, IntPtrLessThanOrEqual(saved_entered_context_count, size));
     437             : #endif
     438             : 
     439             :   StoreNoWriteBarrier(MachineType::PointerRepresentation(), hsi, size_offset,
     440         336 :                       saved_entered_context_count);
     441             : 
     442             :   using FlagStack = DetachableVector<int8_t>;
     443             :   StoreNoWriteBarrier(
     444             :       MachineType::PointerRepresentation(), hsi,
     445        1008 :       IntPtrConstant(HandleScopeImplementer::kIsMicrotaskContextOffset +
     446         336 :                      FlagStack::kSizeOffset),
     447         336 :       saved_entered_context_count);
     448         336 : }
     449             : 
     450         224 : void MicrotaskQueueBuiltinsAssembler::RunPromiseHook(
     451             :     Runtime::FunctionId id, TNode<Context> context,
     452             :     SloppyTNode<HeapObject> promise_or_capability) {
     453         448 :   Label hook(this, Label::kDeferred), done_hook(this);
     454         448 :   Branch(IsPromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate(), &hook,
     455         224 :          &done_hook);
     456         224 :   BIND(&hook);
     457             :   {
     458             :     // Get to the underlying JSPromise instance.
     459             :     TNode<HeapObject> promise = Select<HeapObject>(
     460         448 :         IsPromiseCapability(promise_or_capability),
     461         224 :         [=] {
     462         224 :           return CAST(LoadObjectField(promise_or_capability,
     463             :                                       PromiseCapability::kPromiseOffset));
     464         224 :         },
     465             : 
     466         896 :         [=] { return promise_or_capability; });
     467         448 :     GotoIf(IsUndefined(promise), &done_hook);
     468             :     CallRuntime(id, context, promise);
     469         224 :     Goto(&done_hook);
     470             :   }
     471         224 :   BIND(&done_hook);
     472         224 : }
     473             : 
     474         224 : TF_BUILTIN(EnqueueMicrotask, MicrotaskQueueBuiltinsAssembler) {
     475             :   TNode<Microtask> microtask =
     476             :       UncheckedCast<Microtask>(Parameter(Descriptor::kMicrotask));
     477             :   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
     478          56 :   TNode<Context> native_context = LoadNativeContext(context);
     479             :   TNode<RawPtrT> microtask_queue = GetMicrotaskQueue(native_context);
     480             : 
     481             :   // Do not store the microtask if MicrotaskQueue is not available, that may
     482             :   // happen when the context shutdown.
     483          56 :   Label if_shutdown(this, Label::kDeferred);
     484         168 :   GotoIf(WordEqual(microtask_queue, IntPtrConstant(0)), &if_shutdown);
     485             : 
     486          56 :   TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
     487          56 :   TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
     488          56 :   TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
     489          56 :   TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
     490             : 
     491          56 :   Label if_grow(this, Label::kDeferred);
     492         112 :   GotoIf(IntPtrEqual(size, capacity), &if_grow);
     493             : 
     494             :   // |microtask_queue| has an unused slot to store |microtask|.
     495             :   {
     496             :     StoreNoWriteBarrier(MachineType::PointerRepresentation(), ring_buffer,
     497         112 :                         CalculateRingBufferOffset(capacity, start, size),
     498         168 :                         BitcastTaggedToWord(microtask));
     499             :     StoreNoWriteBarrier(MachineType::PointerRepresentation(), microtask_queue,
     500         112 :                         IntPtrConstant(MicrotaskQueue::kSizeOffset),
     501         112 :                         IntPtrAdd(size, IntPtrConstant(1)));
     502         112 :     Return(UndefinedConstant());
     503             :   }
     504             : 
     505             :   // |microtask_queue| has no space to store |microtask|. Fall back to C++
     506             :   // implementation to grow the buffer.
     507          56 :   BIND(&if_grow);
     508             :   {
     509             :     Node* isolate_constant =
     510         112 :         ExternalConstant(ExternalReference::isolate_address(isolate()));
     511             :     Node* function =
     512         112 :         ExternalConstant(ExternalReference::call_enqueue_microtask_function());
     513             :     CallCFunction(function, MachineType::AnyTagged(),
     514             :                   std::make_pair(MachineType::Pointer(), isolate_constant),
     515             :                   std::make_pair(MachineType::IntPtr(), microtask_queue),
     516          56 :                   std::make_pair(MachineType::AnyTagged(), microtask));
     517         112 :     Return(UndefinedConstant());
     518             :   }
     519             : 
     520          56 :   Bind(&if_shutdown);
     521         112 :   Return(UndefinedConstant());
     522          56 : }
     523             : 
     524         280 : TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
     525             :   // Load the current context from the isolate.
     526          56 :   TNode<Context> current_context = GetCurrentContext();
     527             : 
     528             :   TNode<RawPtrT> microtask_queue =
     529          56 :       UncheckedCast<RawPtrT>(Parameter(Descriptor::kMicrotaskQueue));
     530             : 
     531          56 :   Label loop(this), done(this);
     532          56 :   Goto(&loop);
     533          56 :   BIND(&loop);
     534             : 
     535          56 :   TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);
     536             : 
     537             :   // Exit if the queue is empty.
     538         168 :   GotoIf(WordEqual(size, IntPtrConstant(0)), &done);
     539             : 
     540          56 :   TNode<RawPtrT> ring_buffer = GetMicrotaskRingBuffer(microtask_queue);
     541          56 :   TNode<IntPtrT> capacity = GetMicrotaskQueueCapacity(microtask_queue);
     542          56 :   TNode<IntPtrT> start = GetMicrotaskQueueStart(microtask_queue);
     543             : 
     544             :   TNode<IntPtrT> offset =
     545          56 :       CalculateRingBufferOffset(capacity, start, IntPtrConstant(0));
     546             :   TNode<RawPtrT> microtask_pointer =
     547          56 :       UncheckedCast<RawPtrT>(Load(MachineType::Pointer(), ring_buffer, offset));
     548          56 :   TNode<Microtask> microtask = CAST(BitcastWordToTagged(microtask_pointer));
     549             : 
     550         112 :   TNode<IntPtrT> new_size = IntPtrSub(size, IntPtrConstant(1));
     551             :   TNode<IntPtrT> new_start = WordAnd(IntPtrAdd(start, IntPtrConstant(1)),
     552         168 :                                      IntPtrSub(capacity, IntPtrConstant(1)));
     553             : 
     554             :   // Remove |microtask| from |ring_buffer| before running it, since its
     555             :   // invocation may add another microtask into |ring_buffer|.
     556          56 :   SetMicrotaskQueueSize(microtask_queue, new_size);
     557          56 :   SetMicrotaskQueueStart(microtask_queue, new_start);
     558             : 
     559          56 :   RunSingleMicrotask(current_context, microtask);
     560          56 :   IncrementFinishedMicrotaskCount(microtask_queue);
     561          56 :   Goto(&loop);
     562             : 
     563          56 :   BIND(&done);
     564             :   {
     565             :     // Reset the "current microtask" on the isolate.
     566         112 :     StoreRoot(RootIndex::kCurrentMicrotask, UndefinedConstant());
     567         112 :     Return(UndefinedConstant());
     568             :   }
     569          56 : }
     570             : 
     571             : }  // namespace internal
     572       59456 : }  // namespace v8

Generated by: LCOV version 1.10