compilation-cache.cc 16.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// Copyright 2008 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "v8.h"

#include "compilation-cache.h"
31
#include "serialize.h"
32

33 34
namespace v8 {
namespace internal {
35

36 37

// The number of generations for each sub cache.
38 39
// The number of ScriptGenerations is carefully chosen based on histograms.
// See issue 458: http://code.google.com/p/v8/issues/detail?id=458
40 41 42 43 44
static const int kScriptGenerations = 5;
static const int kEvalGlobalGenerations = 2;
static const int kEvalContextualGenerations = 2;
static const int kRegExpGenerations = 2;

45
// Initial size of each compilation cache table allocated.
46 47 48
static const int kInitialCacheSize = 64;


49 50 51 52 53 54
CompilationCache::CompilationCache(Isolate* isolate)
    : isolate_(isolate),
      script_(isolate, kScriptGenerations),
      eval_global_(isolate, kEvalGlobalGenerations),
      eval_contextual_(isolate, kEvalContextualGenerations),
      reg_exp_(isolate, kRegExpGenerations),
55 56 57 58 59 60
      enabled_(true),
      eager_optimizing_set_(NULL) {
  CompilationSubCache* subcaches[kSubCacheCount] =
    {&script_, &eval_global_, &eval_contextual_, &reg_exp_};
  for (int i = 0; i < kSubCacheCount; ++i) {
    subcaches_[i] = subcaches[i];
61
  }
62
}
63 64


65 66 67
CompilationCache::~CompilationCache() {
  delete eager_optimizing_set_;
  eager_optimizing_set_ = NULL;
68 69
}

70

71
static Handle<CompilationCacheTable> AllocateTable(Isolate* isolate, int size) {
72 73
  CALL_HEAP_FUNCTION(isolate,
                     CompilationCacheTable::Allocate(size),
74 75 76 77
                     CompilationCacheTable);
}


78 79
Handle<CompilationCacheTable> CompilationSubCache::GetTable(int generation) {
  ASSERT(generation < generations_);
80
  Handle<CompilationCacheTable> result;
81
  if (tables_[generation]->IsUndefined()) {
82
    result = AllocateTable(isolate(), kInitialCacheSize);
83
    tables_[generation] = *result;
84
  } else {
85 86
    CompilationCacheTable* table =
        CompilationCacheTable::cast(tables_[generation]);
87
    result = Handle<CompilationCacheTable>(table, isolate());
88 89 90 91
  }
  return result;
}

92 93 94 95
void CompilationSubCache::Age() {
  // Age the generations implicitly killing off the oldest.
  for (int i = generations_ - 1; i > 0; i--) {
    tables_[i] = tables_[i - 1];
96
  }
97 98

  // Set the first generation as unborn.
99
  tables_[0] = isolate()->heap()->undefined_value();
100 101 102
}


103
void CompilationSubCache::IterateFunctions(ObjectVisitor* v) {
104
  Object* undefined = isolate()->heap()->raw_unchecked_undefined_value();
105 106 107 108 109 110 111 112
  for (int i = 0; i < generations_; i++) {
    if (tables_[i] != undefined) {
      reinterpret_cast<CompilationCacheTable*>(tables_[i])->IterateElements(v);
    }
  }
}


113 114 115 116 117 118
void CompilationSubCache::Iterate(ObjectVisitor* v) {
  v->VisitPointers(&tables_[0], &tables_[generations_]);
}


void CompilationSubCache::Clear() {
119
  MemsetPointer(tables_, isolate()->heap()->undefined_value(), generations_);
120 121 122
}


123 124 125
void CompilationSubCache::Remove(Handle<SharedFunctionInfo> function_info) {
  // Probe the script generation tables. Make sure not to leak handles
  // into the caller's handle scope.
126
  { HandleScope scope(isolate());
127 128 129 130 131 132 133 134
    for (int generation = 0; generation < generations(); generation++) {
      Handle<CompilationCacheTable> table = GetTable(generation);
      table->Remove(*function_info);
    }
  }
}


135 136 137 138 139
CompilationCacheScript::CompilationCacheScript(Isolate* isolate,
                                               int generations)
    : CompilationSubCache(isolate, generations),
      script_histogram_(NULL),
      script_histogram_initialized_(false) { }
140 141


142 143 144
// 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.
145 146 147 148 149
bool CompilationCacheScript::HasOrigin(
    Handle<SharedFunctionInfo> function_info,
    Handle<Object> name,
    int line_offset,
    int column_offset) {
150
  Handle<Script> script =
151
      Handle<Script>(Script::cast(function_info->script()), isolate());
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  // If the script name isn't set, the boilerplate script should have
  // an undefined name to have the same origin.
  if (name.is_null()) {
    return script->name()->IsUndefined();
  }
  // Do the fast bailout checks first.
  if (line_offset != script->line_offset()->value()) return false;
  if (column_offset != script->column_offset()->value()) return false;
  // Check that both names are strings. If not, no match.
  if (!name->IsString() || !script->name()->IsString()) return false;
  // Compare the two name strings for equality.
  return String::cast(*name)->Equals(String::cast(script->name()));
}


167 168 169 170
// 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.
171 172 173 174
Handle<SharedFunctionInfo> CompilationCacheScript::Lookup(Handle<String> source,
                                                          Handle<Object> name,
                                                          int line_offset,
                                                          int column_offset) {
175
  Object* result = NULL;
176
  int generation;
177 178 179

  // Probe the script generation tables. Make sure not to leak handles
  // into the caller's handle scope.
180
  { HandleScope scope(isolate());
181
    for (generation = 0; generation < generations(); generation++) {
182
      Handle<CompilationCacheTable> table = GetTable(generation);
183
      Handle<Object> probe(table->Lookup(*source), isolate());
184 185 186
      if (probe->IsSharedFunctionInfo()) {
        Handle<SharedFunctionInfo> function_info =
            Handle<SharedFunctionInfo>::cast(probe);
187
        // Break when we've found a suitable shared function info that
188
        // matches the origin.
189 190
        if (HasOrigin(function_info, name, line_offset, column_offset)) {
          result = *function_info;
191 192 193 194 195 196
          break;
        }
      }
    }
  }

197
  if (!script_histogram_initialized_) {
198
    script_histogram_ = isolate()->stats_table()->CreateHistogram(
199 200 201 202 203 204
        "V8.ScriptCache",
        0,
        kScriptGenerations,
        kScriptGenerations + 1);
    script_histogram_initialized_ = true;
  }
205

206
  if (script_histogram_ != NULL) {
207
    // The level NUMBER_OF_SCRIPT_GENERATIONS is equivalent to a cache miss.
208
    isolate()->stats_table()->AddHistogramSample(script_histogram_, generation);
209 210
  }

whesse@chromium.org's avatar
whesse@chromium.org committed
211
  // Once outside the manacles of the handle scope, we need to recheck
212 213 214
  // to see if we actually found a cached script. If so, we return a
  // handle created in the caller's handle scope.
  if (result != NULL) {
215 216
    Handle<SharedFunctionInfo> shared(SharedFunctionInfo::cast(result),
                                      isolate());
217
    ASSERT(HasOrigin(shared, name, line_offset, column_offset));
218 219
    // If the script was found in a later generation, we promote it to
    // the first generation to let it survive longer in the cache.
220
    if (generation != 0) Put(source, shared);
221
    isolate()->counters()->compilation_cache_hits()->Increment();
222
    return shared;
223
  } else {
224
    isolate()->counters()->compilation_cache_misses()->Increment();
225
    return Handle<SharedFunctionInfo>::null();
226
  }
227 228 229
}


230
MaybeObject* CompilationCacheScript::TryTablePut(
231 232 233 234 235 236 237
    Handle<String> source,
    Handle<SharedFunctionInfo> function_info) {
  Handle<CompilationCacheTable> table = GetFirstTable();
  return table->Put(*source, *function_info);
}


238 239
Handle<CompilationCacheTable> CompilationCacheScript::TablePut(
    Handle<String> source,
240
    Handle<SharedFunctionInfo> function_info) {
241
  CALL_HEAP_FUNCTION(isolate(),
242 243
                     TryTablePut(source, function_info),
                     CompilationCacheTable);
244 245 246
}


247
void CompilationCacheScript::Put(Handle<String> source,
248
                                 Handle<SharedFunctionInfo> function_info) {
249
  HandleScope scope(isolate());
250
  SetFirstTable(TablePut(source, function_info));
251 252 253
}


254
Handle<SharedFunctionInfo> CompilationCacheEval::Lookup(
255 256 257
    Handle<String> source,
    Handle<Context> context,
    StrictModeFlag strict_mode) {
258 259 260 261 262
  // 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.
  Object* result = NULL;
  int generation;
263
  { HandleScope scope(isolate());
264 265
    for (generation = 0; generation < generations(); generation++) {
      Handle<CompilationCacheTable> table = GetTable(generation);
266
      result = table->LookupEval(*source, *context, strict_mode);
267
      if (result->IsSharedFunctionInfo()) {
268 269 270 271
        break;
      }
    }
  }
272 273
  if (result->IsSharedFunctionInfo()) {
    Handle<SharedFunctionInfo>
274
        function_info(SharedFunctionInfo::cast(result), isolate());
275
    if (generation != 0) {
276
      Put(source, context, function_info);
277
    }
278
    isolate()->counters()->compilation_cache_hits()->Increment();
279
    return function_info;
280
  } else {
281
    isolate()->counters()->compilation_cache_misses()->Increment();
282
    return Handle<SharedFunctionInfo>::null();
283 284 285 286
  }
}


287
MaybeObject* CompilationCacheEval::TryTablePut(
288 289 290 291 292 293 294 295
    Handle<String> source,
    Handle<Context> context,
    Handle<SharedFunctionInfo> function_info) {
  Handle<CompilationCacheTable> table = GetFirstTable();
  return table->PutEval(*source, *context, *function_info);
}


296 297 298
Handle<CompilationCacheTable> CompilationCacheEval::TablePut(
    Handle<String> source,
    Handle<Context> context,
299
    Handle<SharedFunctionInfo> function_info) {
300
  CALL_HEAP_FUNCTION(isolate(),
301
                     TryTablePut(source, context, function_info),
302 303 304 305
                     CompilationCacheTable);
}


306 307
void CompilationCacheEval::Put(Handle<String> source,
                               Handle<Context> context,
308
                               Handle<SharedFunctionInfo> function_info) {
309
  HandleScope scope(isolate());
310
  SetFirstTable(TablePut(source, context, function_info));
311 312 313 314 315 316 317 318 319 320
}


Handle<FixedArray> CompilationCacheRegExp::Lookup(Handle<String> source,
                                                  JSRegExp::Flags flags) {
  // 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.
  Object* result = NULL;
  int generation;
321
  { HandleScope scope(isolate());
322 323 324 325 326 327 328 329 330
    for (generation = 0; generation < generations(); generation++) {
      Handle<CompilationCacheTable> table = GetTable(generation);
      result = table->LookupRegExp(*source, flags);
      if (result->IsFixedArray()) {
        break;
      }
    }
  }
  if (result->IsFixedArray()) {
331
    Handle<FixedArray> data(FixedArray::cast(result), isolate());
332 333 334
    if (generation != 0) {
      Put(source, flags, data);
    }
335
    isolate()->counters()->compilation_cache_hits()->Increment();
336 337
    return data;
  } else {
338
    isolate()->counters()->compilation_cache_misses()->Increment();
339 340 341 342 343
    return Handle<FixedArray>::null();
  }
}


344
MaybeObject* CompilationCacheRegExp::TryTablePut(
345 346 347 348 349 350 351 352
    Handle<String> source,
    JSRegExp::Flags flags,
    Handle<FixedArray> data) {
  Handle<CompilationCacheTable> table = GetFirstTable();
  return table->PutRegExp(*source, flags, *data);
}


353 354 355 356
Handle<CompilationCacheTable> CompilationCacheRegExp::TablePut(
    Handle<String> source,
    JSRegExp::Flags flags,
    Handle<FixedArray> data) {
357
  CALL_HEAP_FUNCTION(isolate(),
358 359
                     TryTablePut(source, flags, data),
                     CompilationCacheTable);
360 361 362
}


363 364 365
void CompilationCacheRegExp::Put(Handle<String> source,
                                 JSRegExp::Flags flags,
                                 Handle<FixedArray> data) {
366
  HandleScope scope(isolate());
367
  SetFirstTable(TablePut(source, flags, data));
368 369 370
}


371 372 373
void CompilationCache::Remove(Handle<SharedFunctionInfo> function_info) {
  if (!IsEnabled()) return;

374 375 376
  eval_global_.Remove(function_info);
  eval_contextual_.Remove(function_info);
  script_.Remove(function_info);
377 378 379
}


380 381 382 383
Handle<SharedFunctionInfo> CompilationCache::LookupScript(Handle<String> source,
                                                          Handle<Object> name,
                                                          int line_offset,
                                                          int column_offset) {
384
  if (!IsEnabled()) {
385
    return Handle<SharedFunctionInfo>::null();
386 387
  }

388
  return script_.Lookup(source, name, line_offset, column_offset);
389 390 391
}


392 393 394 395 396
Handle<SharedFunctionInfo> CompilationCache::LookupEval(
    Handle<String> source,
    Handle<Context> context,
    bool is_global,
    StrictModeFlag strict_mode) {
397
  if (!IsEnabled()) {
398
    return Handle<SharedFunctionInfo>::null();
399 400
  }

401
  Handle<SharedFunctionInfo> result;
402
  if (is_global) {
403
    result = eval_global_.Lookup(source, context, strict_mode);
404
  } else {
405
    result = eval_contextual_.Lookup(source, context, strict_mode);
406 407 408 409 410
  }
  return result;
}


411 412
Handle<FixedArray> CompilationCache::LookupRegExp(Handle<String> source,
                                                  JSRegExp::Flags flags) {
413 414 415 416
  if (!IsEnabled()) {
    return Handle<FixedArray>::null();
  }

417
  return reg_exp_.Lookup(source, flags);
418 419 420
}


421
void CompilationCache::PutScript(Handle<String> source,
422
                                 Handle<SharedFunctionInfo> function_info) {
423 424 425 426
  if (!IsEnabled()) {
    return;
  }

427
  script_.Put(source, function_info);
428 429 430 431 432
}


void CompilationCache::PutEval(Handle<String> source,
                               Handle<Context> context,
433
                               bool is_global,
434
                               Handle<SharedFunctionInfo> function_info) {
435 436 437 438
  if (!IsEnabled()) {
    return;
  }

439
  HandleScope scope(isolate());
440
  if (is_global) {
441
    eval_global_.Put(source, context, function_info);
442
  } else {
443
    eval_contextual_.Put(source, context, function_info);
444
  }
445 446 447
}


448 449 450 451

void CompilationCache::PutRegExp(Handle<String> source,
                                 JSRegExp::Flags flags,
                                 Handle<FixedArray> data) {
452 453 454 455
  if (!IsEnabled()) {
    return;
  }

456
  reg_exp_.Put(source, flags, data);
457 458 459
}


460 461 462 463 464
static bool SourceHashCompare(void* key1, void* key2) {
  return key1 == key2;
}


465 466 467 468 469
HashMap* CompilationCache::EagerOptimizingSet() {
  if (eager_optimizing_set_ == NULL) {
    eager_optimizing_set_ = new HashMap(&SourceHashCompare);
  }
  return eager_optimizing_set_;
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
}


bool CompilationCache::ShouldOptimizeEagerly(Handle<JSFunction> function) {
  if (FLAG_opt_eagerly) return true;
  uint32_t hash = function->SourceHash();
  void* key = reinterpret_cast<void*>(hash);
  return EagerOptimizingSet()->Lookup(key, hash, false) != NULL;
}


void CompilationCache::MarkForEagerOptimizing(Handle<JSFunction> function) {
  uint32_t hash = function->SourceHash();
  void* key = reinterpret_cast<void*>(hash);
  EagerOptimizingSet()->Lookup(key, hash, true);
}


void CompilationCache::MarkForLazyOptimizing(Handle<JSFunction> function) {
  uint32_t hash = function->SourceHash();
  void* key = reinterpret_cast<void*>(hash);
  EagerOptimizingSet()->Remove(key, hash);
}


void CompilationCache::ResetEagerOptimizingData() {
  HashMap* set = EagerOptimizingSet();
  if (set->occupancy() > 0) set->Clear();
}


501
void CompilationCache::Clear() {
502
  for (int i = 0; i < kSubCacheCount; i++) {
503
    subcaches_[i]->Clear();
504 505 506
  }
}

507

508 509
void CompilationCache::Iterate(ObjectVisitor* v) {
  for (int i = 0; i < kSubCacheCount; i++) {
510
    subcaches_[i]->Iterate(v);
511
  }
512 513 514
}


515
void CompilationCache::IterateFunctions(ObjectVisitor* v) {
516
  for (int i = 0; i < kSubCacheCount; i++) {
517
    subcaches_[i]->IterateFunctions(v);
518
  }
519 520 521 522
}


void CompilationCache::MarkCompactPrologue() {
523
  for (int i = 0; i < kSubCacheCount; i++) {
524
    subcaches_[i]->Age();
525
  }
526 527 528
}


529
void CompilationCache::Enable() {
530
  enabled_ = true;
531 532 533 534
}


void CompilationCache::Disable() {
535
  enabled_ = false;
536 537 538 539
  Clear();
}


540
} }  // namespace v8::internal