1 // Copyright 2012 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 #ifndef V8_CODEGEN_COMPILATION_CACHE_H_ 6 #define V8_CODEGEN_COMPILATION_CACHE_H_ 7 8 #include "src/base/hashmap.h" 9 #include "src/objects/compilation-cache-table.h" 10 #include "src/utils/allocation.h" 11 12 namespace v8 { 13 namespace internal { 14 15 template <typename T> 16 class Handle; 17 18 class RootVisitor; 19 struct ScriptDetails; 20 21 // The compilation cache consists of several generational sub-caches which uses 22 // this class as a base class. A sub-cache contains a compilation cache tables 23 // for each generation of the sub-cache. Since the same source code string has 24 // different compiled code for scripts and evals, we use separate sub-caches 25 // for different compilation modes, to avoid retrieving the wrong result. 26 class CompilationSubCache { 27 public: CompilationSubCache(Isolate * isolate,int generations)28 CompilationSubCache(Isolate* isolate, int generations) 29 : isolate_(isolate), generations_(generations) { 30 DCHECK_LE(generations, kMaxGenerations); 31 } 32 33 static constexpr int kFirstGeneration = 0; 34 static constexpr int kMaxGenerations = 2; 35 36 // Get the compilation cache tables for a specific generation. 37 Handle<CompilationCacheTable> GetTable(int generation); 38 39 // Accessors for first generation. GetFirstTable()40 Handle<CompilationCacheTable> GetFirstTable() { 41 return GetTable(kFirstGeneration); 42 } SetFirstTable(Handle<CompilationCacheTable> value)43 void SetFirstTable(Handle<CompilationCacheTable> value) { 44 DCHECK_LT(kFirstGeneration, generations_); 45 tables_[kFirstGeneration] = *value; 46 } 47 48 // Age the sub-cache by evicting the oldest generation and creating a new 49 // young generation. 50 virtual void Age() = 0; 51 52 // GC support. 53 void Iterate(RootVisitor* v); 54 55 // Clear this sub-cache evicting all its content. 56 void Clear(); 57 58 // Remove given shared function info from sub-cache. 59 void Remove(Handle<SharedFunctionInfo> function_info); 60 61 // Number of generations in this sub-cache. generations()62 int generations() const { return generations_; } 63 64 protected: isolate()65 Isolate* isolate() const { return isolate_; } 66 67 // Ageing occurs either by removing the oldest generation, or with 68 // custom logic implemented in CompilationCacheTable::Age. 69 static void AgeByGeneration(CompilationSubCache* c); 70 static void AgeCustom(CompilationSubCache* c); 71 72 private: 73 Isolate* const isolate_; 74 const int generations_; 75 Object tables_[kMaxGenerations]; // One for each generation. 76 77 DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationSubCache); 78 }; 79 80 // Sub-cache for scripts. 81 class CompilationCacheScript : public CompilationSubCache { 82 public: 83 explicit CompilationCacheScript(Isolate* isolate); 84 85 MaybeHandle<SharedFunctionInfo> Lookup(Handle<String> source, 86 const ScriptDetails& script_details, 87 LanguageMode language_mode); 88 89 void Put(Handle<String> source, LanguageMode language_mode, 90 Handle<SharedFunctionInfo> function_info); 91 92 void Age() override; 93 94 private: 95 DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheScript); 96 }; 97 98 // Sub-cache for eval scripts. Two caches for eval are used. One for eval calls 99 // in native contexts and one for eval calls in other contexts. The cache 100 // considers the following pieces of information when checking for matching 101 // entries: 102 // 1. The source string. 103 // 2. The shared function info of the calling function. 104 // 3. Whether the source should be compiled as strict code or as sloppy code. 105 // Note: Currently there are clients of CompileEval that always compile 106 // sloppy code even if the calling function is a strict mode function. 107 // More specifically these are the CompileString, DebugEvaluate and 108 // DebugEvaluateGlobal runtime functions. 109 // 4. The start position of the calling scope. 110 class CompilationCacheEval : public CompilationSubCache { 111 public: CompilationCacheEval(Isolate * isolate)112 explicit CompilationCacheEval(Isolate* isolate) 113 : CompilationSubCache(isolate, 1) {} 114 115 InfoCellPair Lookup(Handle<String> source, 116 Handle<SharedFunctionInfo> outer_info, 117 Handle<Context> native_context, 118 LanguageMode language_mode, int position); 119 120 void Put(Handle<String> source, Handle<SharedFunctionInfo> outer_info, 121 Handle<SharedFunctionInfo> function_info, 122 Handle<Context> native_context, Handle<FeedbackCell> feedback_cell, 123 int position); 124 125 void Age() override; 126 127 private: 128 DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval); 129 }; 130 131 // Sub-cache for regular expressions. 132 class CompilationCacheRegExp : public CompilationSubCache { 133 public: CompilationCacheRegExp(Isolate * isolate,int generations)134 CompilationCacheRegExp(Isolate* isolate, int generations) 135 : CompilationSubCache(isolate, generations) {} 136 137 MaybeHandle<FixedArray> Lookup(Handle<String> source, JSRegExp::Flags flags); 138 139 void Put(Handle<String> source, JSRegExp::Flags flags, 140 Handle<FixedArray> data); 141 142 void Age() override; 143 144 private: 145 DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheRegExp); 146 }; 147 148 // The compilation cache keeps shared function infos for compiled 149 // scripts and evals. The shared function infos are looked up using 150 // the source string as the key. For regular expressions the 151 // compilation data is cached. 152 class V8_EXPORT_PRIVATE CompilationCache { 153 public: 154 CompilationCache(const CompilationCache&) = delete; 155 CompilationCache& operator=(const CompilationCache&) = delete; 156 157 // Finds the script shared function info for a source 158 // string. Returns an empty handle if the cache doesn't contain a 159 // script for the given source string with the right origin. 160 MaybeHandle<SharedFunctionInfo> LookupScript( 161 Handle<String> source, const ScriptDetails& script_details, 162 LanguageMode language_mode); 163 164 // Finds the shared function info for a source string for eval in a 165 // given context. Returns an empty handle if the cache doesn't 166 // contain a script for the given source string. 167 InfoCellPair LookupEval(Handle<String> source, 168 Handle<SharedFunctionInfo> outer_info, 169 Handle<Context> context, LanguageMode language_mode, 170 int position); 171 172 // Returns the regexp data associated with the given regexp if it 173 // is in cache, otherwise an empty handle. 174 MaybeHandle<FixedArray> LookupRegExp(Handle<String> source, 175 JSRegExp::Flags flags); 176 177 // Associate the (source, kind) pair to the shared function 178 // info. This may overwrite an existing mapping. 179 void PutScript(Handle<String> source, LanguageMode language_mode, 180 Handle<SharedFunctionInfo> function_info); 181 182 // Associate the (source, context->closure()->shared(), kind) triple 183 // with the shared function info. This may overwrite an existing mapping. 184 void PutEval(Handle<String> source, Handle<SharedFunctionInfo> outer_info, 185 Handle<Context> context, 186 Handle<SharedFunctionInfo> function_info, 187 Handle<FeedbackCell> feedback_cell, int position); 188 189 // Associate the (source, flags) pair to the given regexp data. 190 // This may overwrite an existing mapping. 191 void PutRegExp(Handle<String> source, JSRegExp::Flags flags, 192 Handle<FixedArray> data); 193 194 // Clear the cache - also used to initialize the cache at startup. 195 void Clear(); 196 197 // Remove given shared function info from all caches. 198 void Remove(Handle<SharedFunctionInfo> function_info); 199 200 // GC support. 201 void Iterate(RootVisitor* v); 202 203 // Notify the cache that a mark-sweep garbage collection is about to 204 // take place. This is used to retire entries from the cache to 205 // avoid keeping them alive too long without using them. 206 void MarkCompactPrologue(); 207 208 // Enable/disable compilation cache. Used by debugger to disable compilation 209 // cache during debugging so that eval and new scripts are always compiled. 210 // TODO(bmeurer, chromium:992277): The RegExp cache cannot be enabled and/or 211 // disabled, since it doesn't affect debugging. However ideally the other 212 // caches should also be always on, even in the presence of the debugger, 213 // but at this point there are too many unclear invariants, and so I decided 214 // to just fix the pressing performance problem for RegExp individually first. 215 void EnableScriptAndEval(); 216 void DisableScriptAndEval(); 217 218 private: 219 explicit CompilationCache(Isolate* isolate); 220 ~CompilationCache() = default; 221 222 base::HashMap* EagerOptimizingSet(); 223 IsEnabledScriptAndEval()224 bool IsEnabledScriptAndEval() const { 225 return FLAG_compilation_cache && enabled_script_and_eval_; 226 } 227 isolate()228 Isolate* isolate() const { return isolate_; } 229 230 Isolate* isolate_; 231 232 CompilationCacheScript script_; 233 CompilationCacheEval eval_global_; 234 CompilationCacheEval eval_contextual_; 235 CompilationCacheRegExp reg_exp_; 236 237 static constexpr int kSubCacheCount = 4; 238 CompilationSubCache* subcaches_[kSubCacheCount]; 239 240 // Current enable state of the compilation cache for scripts and eval. 241 bool enabled_script_and_eval_; 242 243 friend class Isolate; 244 }; 245 246 } // namespace internal 247 } // namespace v8 248 249 #endif // V8_CODEGEN_COMPILATION_CACHE_H_ 250