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

Generated by: LCOV version 1.10