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