• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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