ConcurrentDelazification.h (6267B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_ConcurrentDelazification_h 8 #define vm_ConcurrentDelazification_h 9 10 #include "mozilla/Maybe.h" // mozilla::Maybe 11 #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf 12 13 #include <stddef.h> // size_t 14 #include <utility> // std::pair 15 16 #include "frontend/CompilationStencil.h" // frontend::{InitialStencilAndDelazifications, CompilationStencil, ScriptStencilRef, CompilationStencilMerger} 17 #include "frontend/ScriptIndex.h" // frontend::ScriptIndex 18 #include "js/AllocPolicy.h" // SystemAllocPolicy 19 #include "js/CompileOptions.h" // JS::PrefableCompileOptions, JS::ReadOnlyCompileOptions 20 #include "js/experimental/JSStencil.h" // RefPtrTraits for InitialStencilAndDelazifications 21 #include "js/UniquePtr.h" // UniquePtr 22 #include "js/Vector.h" // Vector 23 24 namespace js { 25 26 class FrontendContext; 27 28 // Base class for implementing the various strategies to iterate over the 29 // functions to be delazified, or to decide when to stop doing any 30 // delazification. 31 // 32 // When created, the `add` function should be called with the top-level 33 // ScriptIndex. 34 struct DelazifyStrategy { 35 using ScriptStencilRef = frontend::ScriptStencilRef; 36 using ScriptIndex = frontend::ScriptIndex; 37 using InitialStencilAndDelazifications = 38 frontend::InitialStencilAndDelazifications; 39 40 virtual ~DelazifyStrategy() = default; 41 42 // Returns true if no more functions should be delazified. Note, this does not 43 // imply that every function got delazified. 44 virtual bool done() const = 0; 45 46 // Return a function identifier which represent the next function to be 47 // delazified. If no more function should be delazified, then return 0. 48 virtual ScriptStencilRef next() = 0; 49 50 // Empty the list of functions to be processed next. done() should return true 51 // after this call. 52 virtual void clear() = 0; 53 54 // Insert an index in the container of the delazification strategy. A strategy 55 // can choose to ignore the insertion of an index in its queue of function to 56 // delazify. Return false only in case of errors while inserting, and true 57 // otherwise. 58 [[nodiscard]] virtual bool insert(ScriptStencilRef& ref) = 0; 59 60 // Add the inner functions of a delazified function. This function should only 61 // be called with a function which has some bytecode associated with it, and 62 // register functions which parent are already delazified. 63 // 64 // This function is called with the script index of: 65 // - top-level script, when starting the off-thread delazification. 66 // - functions added by `add` and delazified by `DelazificationContext`. 67 [[nodiscard]] bool add(FrontendContext* fc, ScriptStencilRef& ref); 68 }; 69 70 // Delazify all functions using a Depth First traversal of the function-tree 71 // ordered, where each functions is visited in source-order. 72 // 73 // When `add` is called with the top-level ScriptIndex. This will push all inner 74 // functions to a stack such that they are popped in source order. Each 75 // function, once delazified, would be used to schedule their inner functions 76 // the same way. 77 // 78 // Hypothesis: This strategy parses all functions in source order, with the 79 // expectation that calls will follow the same order, and that helper thread 80 // would always be ahead of the execution. 81 struct DepthFirstDelazification final : public DelazifyStrategy { 82 Vector<frontend::ScriptStencilRef, 0, SystemAllocPolicy> stack; 83 84 bool done() const override { return stack.empty(); } 85 ScriptStencilRef next() override { return stack.popCopy(); } 86 void clear() override { return stack.clear(); } 87 bool insert(frontend::ScriptStencilRef& ref) override { 88 return stack.append(ref); 89 } 90 }; 91 92 // Delazify all functions using a traversal which select the largest function 93 // first. The intent being that if the main thread races with the helper thread, 94 // then the main thread should only have to parse small functions instead of the 95 // large ones which would be prioritized by this delazification strategy. 96 struct LargeFirstDelazification final : public DelazifyStrategy { 97 using SourceSize = uint32_t; 98 Vector<std::pair<SourceSize, ScriptStencilRef>, 0, SystemAllocPolicy> heap; 99 100 bool done() const override { return heap.empty(); } 101 ScriptStencilRef next() override; 102 void clear() override { return heap.clear(); } 103 bool insert(frontend::ScriptStencilRef&) override; 104 }; 105 106 class DelazificationContext { 107 const JS::PrefableCompileOptions initialPrefableOptions_; 108 using Stencils = frontend::InitialStencilAndDelazifications; 109 110 // Queue of functions to be processed while delazifying. 111 UniquePtr<DelazifyStrategy> strategy_; 112 113 RefPtr<Stencils> stencils_; 114 mozilla::Maybe<Stencils::RelativeIndexesGuard> indexesGuard_; 115 116 // Record any errors happening while parsing or generating bytecode. 117 FrontendContext fc_; 118 119 size_t stackQuota_; 120 121 bool isInterrupted_ = false; 122 123 public: 124 explicit DelazificationContext( 125 const JS::PrefableCompileOptions& initialPrefableOptions, 126 size_t stackQuota) 127 : initialPrefableOptions_(initialPrefableOptions), 128 stackQuota_(stackQuota) {} 129 130 bool init(const JS::ReadOnlyCompileOptions& options, Stencils* stencils); 131 bool delazify(); 132 133 // This function is called by `delazify` function to know whether the 134 // delazification should be interrupted. 135 // 136 // The `delazify` function holds on a thread until all functions iterated 137 // over by the strategy. However, as a `delazify` function iterates over 138 // multiple functions, it can easily be interrupted at function boundaries. 139 // 140 // TODO: (Bug 1773683) Plug this with the mozilla::Task::RequestInterrupt 141 // function which is wrapping HelperThreads tasks within Mozilla. 142 bool isInterrupted() const { return isInterrupted_; } 143 void interrupt() { isInterrupted_ = true; } 144 145 bool done() const; 146 147 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 148 }; 149 150 } /* namespace js */ 151 152 #endif /* vm_ConcurrentDelazification_h */