compilation-cache.cc 13.6 KB
Newer Older
1
// Copyright 2011 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/compilation-cache.h"
6

7 8
#include "src/counters.h"
#include "src/factory.h"
yangguo's avatar
yangguo committed
9
#include "src/globals.h"
10
#include "src/objects-inl.h"
11
#include "src/objects/compilation-cache-inl.h"
12
#include "src/visitors.h"
13

14 15
namespace v8 {
namespace internal {
16

17 18 19 20

// The number of generations for each sub cache.
static const int kRegExpGenerations = 2;

21
// Initial size of each compilation cache table allocated.
22 23
static const int kInitialCacheSize = 64;

24 25
CompilationCache::CompilationCache(Isolate* isolate)
    : isolate_(isolate),
26 27 28
      script_(isolate),
      eval_global_(isolate),
      eval_contextual_(isolate),
29
      reg_exp_(isolate, kRegExpGenerations),
30
      enabled_(true) {
31 32 33 34
  CompilationSubCache* subcaches[kSubCacheCount] =
    {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
  for (int i = 0; i < kSubCacheCount; ++i) {
    subcaches_[i] = subcaches[i];
35
  }
36
}
37 38


39
CompilationCache::~CompilationCache() {}
40

41

42
Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
43
  DCHECK(generation < generations_);
44
  Handle<CompilationCacheTable> result;
45
  if (tables_[generation]->IsUndefined(isolate())) {
46
    result = CompilationCacheTable::New(isolate(), kInitialCacheSize);
47
    tables_[generation] = *result;
48
  } else {
49 50
    CompilationCacheTable* table =
        CompilationCacheTable::cast(tables_[generation]);
51
    result = Handle<CompilationCacheTable>(table, isolate());
52 53 54 55
  }
  return result;
}

56

57
void CompilationSubCache::Age() {
58 59
  // Don't directly age single-generation caches.
  if (generations_ == 1) {
60
    if (!tables_[0]->IsUndefined(isolate())) {
61 62 63 64 65
      CompilationCacheTable::cast(tables_[0])->Age();
    }
    return;
  }

66 67 68
  // Age the generations implicitly killing off the oldest.
  for (int i = generations_ - 1; i > 0; i--) {
    tables_[i] = tables_[i - 1];
69
  }
70 71

  // Set the first generation as unborn.
72
  tables_[0] = isolate()->heap()->undefined_value();
73 74 75
}


76
void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
77
  Object* undefined = isolate()->heap()->undefined_value();
78 79 80 81 82 83 84
  for (int i = 0; i < generations_; i++) {
    if (tables_[i] != undefined) {
      reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
    }
  }
}

85 86 87
void CompilationSubCache::Iterate(RootVisitor* v) {
  v->VisitRootPointers(Root::kCompilationCache, &tables_[0],
                       &tables_[generations_]);
88 89 90 91
}


void CompilationSubCache::Clear() {
92
  MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
93 94 95
}


96 97 98
void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
  // Probe the script generation tables. Make sure not to leak handles
  // into the caller's handle scope.
99
  { HandleScope scope(isolate());
100 101 102 103 104 105 106
    for (int generation = 0; generation < generations(); generation++) {
      Handle<CompilationCacheTable> table = GetTable(generation);
      table->Remove(*function_info);
    }
  }
}

107 108
CompilationCacheScript::CompilationCacheScript(Isolate* isolate)
    : CompilationSubCache(isolate, 1) {}
109

110 111 112
// We only re-use a cached function for some script source code if the
// script originates from the same place. This is to avoid issues
// when reporting errors, etc.
113 114 115
bool CompilationCacheScript::HasOrigin(Handle<SharedFunctionInfo> function_info,
                                       Handle<Object> name, int line_offset,
                                       int column_offset,
116
                                       ScriptOriginOptions resource_options) {
117
  Handle<Script> script =
118
      Handle<Script>(Script::cast(function_info->script()), isolate());
119 120 121
  // If the script name isn't set, the boilerplate script should have
  // an undefined name to have the same origin.
  if (name.is_null()) {
122
    return script->name()->IsUndefined(isolate());
123 124
  }
  // Do the fast bailout checks first.
125 126
  if (line_offset != script->line_offset()) return false;
  if (column_offset != script->column_offset()) return false;
127 128
  // Check that both names are strings. If not, no match.
  if (!name->IsString() || !script->name()->IsString()) return false;
129 130
  // Are the origin_options same?
  if (resource_options.Flags() != script->origin_options().Flags())
131
    return false;
132
  // Compare the two name strings for equality.
133 134
  return String::Equals(Handle<String>::cast(name),
                        Handle<String>(String::cast(script->name())));
135 136 137
}


138 139 140 141
// TODO(245): Need to allow identical code from different contexts to
// be cached in the same script generation. Currently the first use
// will be cached, but subsequent code from different source / line
// won't.
142
InfoVectorPair CompilationCacheScript::Lookup(
143
    Handle<String> source, Handle<Object> name, int line_offset,
144 145
    int column_offset, ScriptOriginOptions resource_options,
    Handle<Context> context, LanguageMode language_mode) {
146
  InfoVectorPair result;
147 148 149

  // Probe the script generation tables. Make sure not to leak handles
  // into the caller's handle scope.
150
  { HandleScope scope(isolate());
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
    const int generation = 0;
    DCHECK(generations() == 1);
    Handle<CompilationCacheTable> table = GetTable(generation);
    InfoVectorPair probe = table->LookupScript(source, context, language_mode);
    if (probe.has_shared()) {
      Handle<SharedFunctionInfo> function_info(probe.shared(), isolate());
      Handle<Cell> vector_handle;
      if (probe.has_vector()) {
        vector_handle = Handle<Cell>(probe.vector(), isolate());
      }
      // Break when we've found a suitable shared function info that
      // matches the origin.
      if (HasOrigin(function_info, name, line_offset, column_offset,
                    resource_options)) {
        result = InfoVectorPair(*function_info,
                                probe.has_vector() ? *vector_handle : nullptr);
167 168 169 170
      }
    }
  }

whesse@chromium.org's avatar
whesse@chromium.org committed
171
  // Once outside the manacles of the handle scope, we need to recheck
172 173
  // to see if we actually found a cached script. If so, we return a
  // handle created in the caller's handle scope.
174
  if (result.has_shared()) {
175 176 177
#ifdef DEBUG
    // Since HasOrigin can allocate, we need to protect the SharedFunctionInfo
    // and the FeedbackVector with handles during the call.
178
    Handle<SharedFunctionInfo> shared(result.shared(), isolate());
179 180 181 182
    Handle<Cell> vector_handle;
    if (result.has_vector()) {
      vector_handle = Handle<Cell>(result.vector(), isolate());
    }
183 184
    DCHECK(
        HasOrigin(shared, name, line_offset, column_offset, resource_options));
185 186 187
    result =
        InfoVectorPair(*shared, result.has_vector() ? *vector_handle : nullptr);
#endif
188
    isolate()->counters()->compilation_cache_hits()->Increment();
189
  } else {
190
    isolate()->counters()->compilation_cache_misses()->Increment();
191
  }
192
  return result;
193 194
}

195
void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
196
                                 LanguageMode language_mode,
197 198
                                 Handle<SharedFunctionInfo> function_info,
                                 Handle<Cell> literals) {
199
  HandleScope scope(isolate());
200
  Handle<CompilationCacheTable> table = GetFirstTable();
201 202
  SetFirstTable(CompilationCacheTable::PutScript(
      table, source, context, language_mode, function_info, literals));
203 204
}

205
InfoVectorPair CompilationCacheEval::Lookup(
206
    Handle<String> source, Handle<SharedFunctionInfo> outer_info,
207
    Handle<Context> native_context, LanguageMode language_mode, int position) {
208
  HandleScope scope(isolate());
209 210 211
  // Make sure not to leak the table into the surrounding handle
  // scope. Otherwise, we risk keeping old tables around even after
  // having cleared the cache.
212 213 214 215 216
  InfoVectorPair result;
  const int generation = 0;
  DCHECK(generations() == 1);
  Handle<CompilationCacheTable> table = GetTable(generation);
  result = table->LookupEval(source, outer_info, native_context, language_mode,
217
                             position);
218
  if (result.has_shared()) {
219
    isolate()->counters()->compilation_cache_hits()->Increment();
220
  } else {
221
    isolate()->counters()->compilation_cache_misses()->Increment();
222
  }
223
  return result;
224 225 226
}

void CompilationCacheEval::Put(Handle<String> source,
227
                               Handle<SharedFunctionInfo> outer_info,
228
                               Handle<SharedFunctionInfo> function_info,
229
                               Handle<Context> native_context,
230
                               Handle<Cell> literals, int position) {
231
  HandleScope scope(isolate());
232
  Handle<CompilationCacheTable> table = GetFirstTable();
233 234
  table =
      CompilationCacheTable::PutEval(table, source, outer_info, function_info,
235
                                     native_context, literals, position);
236
  SetFirstTable(table);
237 238 239
}


240 241 242
MaybeHandle<FixedArray> CompilationCacheRegExp::Lookup(
    Handle<String> source,
    JSRegExp::Flags flags) {
243
  HandleScope scope(isolate());
244 245 246
  // Make sure not to leak the table into the surrounding handle
  // scope. Otherwise, we risk keeping old tables around even after
  // having cleared the cache.
247
  Handle<Object> result = isolate()->factory()->undefined_value();
248
  int generation;
249 250 251 252
  for (generation = 0; generation < generations(); generation++) {
    Handle<CompilationCacheTable> table = GetTable(generation);
    result = table->LookupRegExp(source, flags);
    if (result->IsFixedArray()) break;
253 254
  }
  if (result->IsFixedArray()) {
255
    Handle<FixedArray> data = Handle<FixedArray>::cast(result);
256 257 258
    if (generation != 0) {
      Put(source, flags, data);
    }
259
    isolate()->counters()->compilation_cache_hits()->Increment();
260
    return scope.CloseAndEscape(data);
261
  } else {
262
    isolate()->counters()->compilation_cache_misses()->Increment();
263
    return MaybeHandle<FixedArray>();
264 265 266 267 268 269 270
  }
}


void CompilationCacheRegExp::Put(Handle<String> source,
                                 JSRegExp::Flags flags,
                                 Handle<FixedArray> data) {
271
  HandleScope scope(isolate());
272 273
  Handle<CompilationCacheTable> table = GetFirstTable();
  SetFirstTable(CompilationCacheTable::PutRegExp(table, source, flags, data));
274 275 276
}


277 278 279
void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
  if (!IsEnabled()) return;

280 281 282
  eval_global_.Remove(function_info);
  eval_contextual_.Remove(function_info);
  script_.Remove(function_info);
283 284
}

285
InfoVectorPair CompilationCache::LookupScript(
286
    Handle<String> source, Handle<Object> name, int line_offset,
287 288
    int column_offset, ScriptOriginOptions resource_options,
    Handle<Context> context, LanguageMode language_mode) {
289 290
  InfoVectorPair empty_result;
  if (!IsEnabled()) return empty_result;
291

292
  return script_.Lookup(source, name, line_offset, column_offset,
293
                        resource_options, context, language_mode);
294 295
}

296
InfoVectorPair CompilationCache::LookupEval(
297
    Handle<String> source, Handle<SharedFunctionInfo> outer_info,
298
    Handle<Context> context, LanguageMode language_mode, int position) {
299 300
  InfoVectorPair result;
  if (!IsEnabled()) return result;
301

302
  if (context->IsNativeContext()) {
303
    result = eval_global_.Lookup(source, outer_info, context, language_mode,
304
                                 position);
305
  } else {
306
    DCHECK(position != kNoSourcePosition);
307 308
    Handle<Context> native_context(context->native_context(), isolate());
    result = eval_contextual_.Lookup(source, outer_info, native_context,
309
                                     language_mode, position);
310
  }
311

312 313 314 315
  return result;
}


316
MaybeHandle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
317
                                                       JSRegExp::Flags flags) {
318
  if (!IsEnabled()) return MaybeHandle<FixedArray>();
319

320
  return reg_exp_.Lookup(source, flags);
321 322
}

323
void CompilationCache::PutScript(Handle<String> source, Handle<Context> context,
324
                                 LanguageMode language_mode,
325 326
                                 Handle<SharedFunctionInfo> function_info,
                                 Handle<Cell> literals) {
327
  if (!IsEnabled()) return;
328

329
  script_.Put(source, context, language_mode, function_info, literals);
330 331 332
}

void CompilationCache::PutEval(Handle<String> source,
333
                               Handle<SharedFunctionInfo> outer_info,
334
                               Handle<Context> context,
335
                               Handle<SharedFunctionInfo> function_info,
336
                               Handle<Cell> literals, int position) {
337
  if (!IsEnabled()) return;
338

339
  HandleScope scope(isolate());
340
  if (context->IsNativeContext()) {
341
    eval_global_.Put(source, outer_info, function_info, context, literals,
342
                     position);
343
  } else {
344
    DCHECK(position != kNoSourcePosition);
345 346
    Handle<Context> native_context(context->native_context(), isolate());
    eval_contextual_.Put(source, outer_info, function_info, native_context,
347
                         literals, position);
348
  }
349 350 351
}


352 353 354 355

void CompilationCache::PutRegExp(Handle<String> source,
                                 JSRegExp::Flags flags,
                                 Handle<FixedArray> data) {
356 357 358 359
  if (!IsEnabled()) {
    return;
  }

360
  reg_exp_.Put(source, flags, data);
361 362 363
}


364
void CompilationCache::Clear() {
365
  for (int i = 0; i < kSubCacheCount; i++) {
366
    subcaches_[i]->Clear();
367 368 369
  }
}

370
void CompilationCache::Iterate(RootVisitor* v) {
371
  for (int i = 0; i < kSubCacheCount; i++) {
372
    subcaches_[i]->Iterate(v);
373
  }
374 375 376
}


377
void CompilationCache::IterateFunctions(ObjectVisitor* v) {
378
  for (int i = 0; i < kSubCacheCount; i++) {
379
    subcaches_[i]->IterateFunctions(v);
380
  }
381 382 383 384
}


void CompilationCache::MarkCompactPrologue() {
385
  for (int i = 0; i < kSubCacheCount; i++) {
386
    subcaches_[i]->Age();
387
  }
388 389 390
}


391
void CompilationCache::Enable() {
392
  enabled_ = true;
393 394 395 396
}


void CompilationCache::Disable() {
397
  enabled_ = false;
398 399 400 401
  Clear();
}


402 403
}  // namespace internal
}  // namespace v8