| OLD | NEW |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
| 4 // met: | 4 // met: |
| 5 // | 5 // |
| 6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
| (...skipping 1686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1697 "foo();"; | 1697 "foo();"; |
| 1698 Handle<String> source = factory->InternalizeUtf8String(raw_source); | 1698 Handle<String> source = factory->InternalizeUtf8String(raw_source); |
| 1699 Handle<Context> native_context = isolate->native_context(); | 1699 Handle<Context> native_context = isolate->native_context(); |
| 1700 | 1700 |
| 1701 { | 1701 { |
| 1702 v8::HandleScope scope(CcTest::isolate()); | 1702 v8::HandleScope scope(CcTest::isolate()); |
| 1703 CompileRun(raw_source); | 1703 CompileRun(raw_source); |
| 1704 } | 1704 } |
| 1705 | 1705 |
| 1706 // The script should be in the cache now. | 1706 // The script should be in the cache now. |
| 1707 MaybeHandle<SharedFunctionInfo> info = compilation_cache->LookupScript( | 1707 InfoVectorPair pair = compilation_cache->LookupScript( |
| 1708 source, Handle<Object>(), 0, 0, v8::ScriptOriginOptions(true, false), | 1708 source, Handle<Object>(), 0, 0, v8::ScriptOriginOptions(true, false), |
| 1709 native_context, language_mode); | 1709 native_context, language_mode); |
| 1710 CHECK(!info.is_null()); | 1710 CHECK(pair.has_shared()); |
| 1711 | 1711 |
| 1712 // Check that the code cache entry survives at least on GC. | 1712 // Check that the code cache entry survives at least on GC. |
| 1713 // (Unless --optimize-for-size, in which case it might get collected | 1713 // (Unless --optimize-for-size, in which case it might get collected |
| 1714 // immediately.) | 1714 // immediately.) |
| 1715 if (!FLAG_optimize_for_size) { | 1715 if (!FLAG_optimize_for_size) { |
| 1716 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 1716 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
| 1717 info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, | 1717 pair = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, |
| 1718 v8::ScriptOriginOptions(true, false), | 1718 v8::ScriptOriginOptions(true, false), |
| 1719 native_context, language_mode); | 1719 native_context, language_mode); |
| 1720 CHECK(!info.is_null()); | 1720 CHECK(pair.has_shared()); |
| 1721 } | 1721 } |
| 1722 | 1722 |
| 1723 // Progress code age until it's old and ready for GC. | 1723 // Progress code age until it's old and ready for GC. |
| 1724 const int kAgingThreshold = 6; | 1724 const int kAgingThreshold = 6; |
| 1725 for (int i = 0; i < kAgingThreshold; i++) { | 1725 for (int i = 0; i < kAgingThreshold; i++) { |
| 1726 info.ToHandleChecked()->code()->MakeOlder(); | 1726 pair.shared()->code()->MakeOlder(); |
| 1727 if (info.ToHandleChecked()->HasBytecodeArray()) { | 1727 if (pair.shared()->HasBytecodeArray()) { |
| 1728 info.ToHandleChecked()->bytecode_array()->MakeOlder(); | 1728 pair.shared()->bytecode_array()->MakeOlder(); |
| 1729 } | 1729 } |
| 1730 } | 1730 } |
| 1731 | 1731 |
| 1732 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 1732 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
| 1733 // Ensure code aging cleared the entry from the cache. | 1733 // Ensure code aging cleared the entry from the cache. |
| 1734 info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, | 1734 pair = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, |
| 1735 v8::ScriptOriginOptions(true, false), | 1735 v8::ScriptOriginOptions(true, false), |
| 1736 native_context, language_mode); | 1736 native_context, language_mode); |
| 1737 CHECK(info.is_null()); | 1737 CHECK(!pair.has_shared()); |
| 1738 } | 1738 } |
| 1739 | 1739 |
| 1740 | 1740 |
| 1741 static void OptimizeEmptyFunction(const char* name) { | 1741 static void OptimizeEmptyFunction(const char* name) { |
| 1742 HandleScope scope(CcTest::i_isolate()); | 1742 HandleScope scope(CcTest::i_isolate()); |
| 1743 EmbeddedVector<char, 256> source; | 1743 EmbeddedVector<char, 256> source; |
| 1744 SNPrintF(source, | 1744 SNPrintF(source, |
| 1745 "function %s() { return 0; }" | 1745 "function %s() { return 0; }" |
| 1746 "%s(); %s();" | 1746 "%s(); %s();" |
| 1747 "%%OptimizeFunctionOnNextCall(%s);" | 1747 "%%OptimizeFunctionOnNextCall(%s);" |
| (...skipping 2604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4352 CompileRun("function g() { return 2 }" | 4352 CompileRun("function g() { return 2 }" |
| 4353 "g(); %OptimizeFunctionOnNextCall(g); g();"); | 4353 "g(); %OptimizeFunctionOnNextCall(g); g();"); |
| 4354 | 4354 |
| 4355 Handle<JSFunction> g = Handle<JSFunction>::cast( | 4355 Handle<JSFunction> g = Handle<JSFunction>::cast( |
| 4356 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | 4356 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( |
| 4357 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); | 4357 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); |
| 4358 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); | 4358 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); |
| 4359 if (!code->is_optimized_code()) return; | 4359 if (!code->is_optimized_code()) return; |
| 4360 } | 4360 } |
| 4361 | 4361 |
| 4362 Handle<TypeFeedbackVector> vector = | |
| 4363 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
| 4364 Handle<Context> context(isolate->context()); | 4362 Handle<Context> context(isolate->context()); |
| 4365 | 4363 |
| 4366 // Add the new code several times to the optimized code map and also set an | 4364 // Add the new code several times to the optimized code map and also set an |
| 4367 // allocation timeout so that expanding the code map will trigger a GC. | 4365 // allocation timeout so that expanding the code map will trigger a GC. |
| 4368 heap->set_allocation_timeout(5); | 4366 heap->set_allocation_timeout(5); |
| 4369 FLAG_gc_interval = 1000; | 4367 FLAG_gc_interval = 1000; |
| 4370 for (int i = 0; i < 10; ++i) { | 4368 for (int i = 0; i < 10; ++i) { |
| 4371 BailoutId id = BailoutId(i); | 4369 BailoutId id = BailoutId(i); |
| 4372 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | 4370 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, id); |
| 4373 id); | |
| 4374 } | 4371 } |
| 4375 } | 4372 } |
| 4376 #endif // DEBUG | 4373 #endif // DEBUG |
| 4377 | 4374 |
| 4378 TEST(Regress514122) { | |
| 4379 if (!i::FLAG_incremental_marking) return; | |
| 4380 i::FLAG_allow_natives_syntax = true; | |
| 4381 CcTest::InitializeVM(); | |
| 4382 Isolate* isolate = CcTest::i_isolate(); | |
| 4383 LocalContext env; | |
| 4384 Heap* heap = isolate->heap(); | |
| 4385 HandleScope scope(isolate); | |
| 4386 | |
| 4387 // Perfrom one initial GC to enable code flushing. | |
| 4388 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
| 4389 | |
| 4390 // Prepare function whose optimized code map we can use. | |
| 4391 Handle<SharedFunctionInfo> shared; | |
| 4392 { | |
| 4393 HandleScope inner_scope(isolate); | |
| 4394 CompileRun( | |
| 4395 "function f() { return 1 }" | |
| 4396 "f(); %OptimizeFunctionOnNextCall(f); f();"); | |
| 4397 | |
| 4398 Handle<JSFunction> f = Handle<JSFunction>::cast( | |
| 4399 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
| 4400 CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked()))); | |
| 4401 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); | |
| 4402 CompileRun("f = null"); | |
| 4403 } | |
| 4404 | |
| 4405 // Prepare optimized code that we can use. | |
| 4406 Handle<Code> code; | |
| 4407 { | |
| 4408 HandleScope inner_scope(isolate); | |
| 4409 CompileRun( | |
| 4410 "function g() { return 2 }" | |
| 4411 "g(); %OptimizeFunctionOnNextCall(g); g();"); | |
| 4412 | |
| 4413 Handle<JSFunction> g = Handle<JSFunction>::cast( | |
| 4414 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
| 4415 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); | |
| 4416 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); | |
| 4417 if (!code->is_optimized_code()) return; | |
| 4418 } | |
| 4419 | |
| 4420 Handle<TypeFeedbackVector> vector = | |
| 4421 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
| 4422 Handle<Context> context(isolate->context()); | |
| 4423 | |
| 4424 // Add the code several times to the optimized code map. | |
| 4425 for (int i = 0; i < 3; ++i) { | |
| 4426 HandleScope inner_scope(isolate); | |
| 4427 BailoutId id = BailoutId(i); | |
| 4428 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
| 4429 id); | |
| 4430 } | |
| 4431 shared->optimized_code_map()->Print(); | |
| 4432 | |
| 4433 // Add the code with a feedback vector to be evacuated. | |
| 4434 Page* evac_page; | |
| 4435 { | |
| 4436 HandleScope inner_scope(isolate); | |
| 4437 AlwaysAllocateScope always_allocate(isolate); | |
| 4438 // Make sure literal is placed on an old-space evacuation candidate. | |
| 4439 heap::SimulateFullSpace(heap->old_space()); | |
| 4440 | |
| 4441 // Make sure there the number of literals is > 0. | |
| 4442 Handle<TypeFeedbackVector> vector = | |
| 4443 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
| 4444 evac_page = Page::FromAddress(vector->address()); | |
| 4445 BailoutId id = BailoutId(100); | |
| 4446 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
| 4447 id); | |
| 4448 } | |
| 4449 | |
| 4450 // Heap is ready, force {lit_page} to become an evacuation candidate and | |
| 4451 // simulate incremental marking to enqueue optimized code map. | |
| 4452 FLAG_manual_evacuation_candidates_selection = true; | |
| 4453 heap::ForceEvacuationCandidate(evac_page); | |
| 4454 heap::SimulateIncrementalMarking(heap); | |
| 4455 | |
| 4456 // No matter whether reachable or not, {boomer} is doomed. | |
| 4457 Handle<Object> boomer(shared->optimized_code_map(), isolate); | |
| 4458 | |
| 4459 // Add the code several times to the optimized code map. This will leave old | |
| 4460 // copies of the optimized code map unreachable but still marked. | |
| 4461 for (int i = 3; i < 6; ++i) { | |
| 4462 HandleScope inner_scope(isolate); | |
| 4463 BailoutId id = BailoutId(i); | |
| 4464 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
| 4465 id); | |
| 4466 } | |
| 4467 | |
| 4468 // Trigger a GC to flush out the bug. | |
| 4469 CcTest::CollectGarbage(i::OLD_SPACE); | |
| 4470 boomer->Print(); | |
| 4471 } | |
| 4472 | |
| 4473 TEST(OptimizedCodeMapReuseEntries) { | |
| 4474 i::FLAG_allow_natives_syntax = true; | |
| 4475 // BUG(v8:4598): Since TurboFan doesn't treat maps in code weakly, we can't | |
| 4476 // run this test. | |
| 4477 if (i::FLAG_turbo) return; | |
| 4478 CcTest::InitializeVM(); | |
| 4479 v8::Isolate* v8_isolate = CcTest::isolate(); | |
| 4480 Isolate* isolate = CcTest::i_isolate(); | |
| 4481 HandleScope scope(isolate); | |
| 4482 | |
| 4483 // Create 3 contexts, allow the 2nd one to be disposed, and verify that | |
| 4484 // a 4th context will re-use the weak slots in the optimized code map | |
| 4485 // to hold data, rather than expanding the map. | |
| 4486 v8::Local<v8::Context> c1 = v8::Context::New(v8_isolate); | |
| 4487 const char* source = "function foo(x) { var l = [1]; return x+l[0]; }"; | |
| 4488 v8::ScriptCompiler::Source script_source( | |
| 4489 v8::String::NewFromUtf8(v8_isolate, source, v8::NewStringType::kNormal) | |
| 4490 .ToLocalChecked()); | |
| 4491 v8::Local<v8::UnboundScript> indep = | |
| 4492 v8::ScriptCompiler::CompileUnboundScript(v8_isolate, &script_source) | |
| 4493 .ToLocalChecked(); | |
| 4494 const char* toplevel = "foo(3); %OptimizeFunctionOnNextCall(foo); foo(3);"; | |
| 4495 // Perfrom one initial GC to enable code flushing. | |
| 4496 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
| 4497 | |
| 4498 c1->Enter(); | |
| 4499 indep->BindToCurrentContext()->Run(c1).ToLocalChecked(); | |
| 4500 CompileRun(toplevel); | |
| 4501 | |
| 4502 Handle<SharedFunctionInfo> shared; | |
| 4503 Handle<JSFunction> foo = Handle<JSFunction>::cast( | |
| 4504 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
| 4505 CcTest::global()->Get(c1, v8_str("foo")).ToLocalChecked()))); | |
| 4506 CHECK(foo->shared()->is_compiled()); | |
| 4507 shared = handle(foo->shared()); | |
| 4508 c1->Exit(); | |
| 4509 | |
| 4510 { | |
| 4511 HandleScope scope(isolate); | |
| 4512 v8::Local<v8::Context> c2 = v8::Context::New(v8_isolate); | |
| 4513 c2->Enter(); | |
| 4514 indep->BindToCurrentContext()->Run(c2).ToLocalChecked(); | |
| 4515 CompileRun(toplevel); | |
| 4516 c2->Exit(); | |
| 4517 } | |
| 4518 | |
| 4519 { | |
| 4520 HandleScope scope(isolate); | |
| 4521 v8::Local<v8::Context> c3 = v8::Context::New(v8_isolate); | |
| 4522 c3->Enter(); | |
| 4523 indep->BindToCurrentContext()->Run(c3).ToLocalChecked(); | |
| 4524 CompileRun(toplevel); | |
| 4525 c3->Exit(); | |
| 4526 | |
| 4527 // Now, collect garbage. Context c2 should have no roots to it, and it's | |
| 4528 // entry in the optimized code map should be free for a new context. | |
| 4529 for (int i = 0; i < 4; i++) { | |
| 4530 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
| 4531 } | |
| 4532 | |
| 4533 Handle<FixedArray> optimized_code_map = | |
| 4534 handle(shared->optimized_code_map()); | |
| 4535 // There should be 3 entries in the map. | |
| 4536 CHECK_EQ( | |
| 4537 3, ((optimized_code_map->length() - SharedFunctionInfo::kEntriesStart) / | |
| 4538 SharedFunctionInfo::kEntryLength)); | |
| 4539 // But one of them (formerly for c2) should be cleared. | |
| 4540 int cleared_count = 0; | |
| 4541 for (int i = SharedFunctionInfo::kEntriesStart; | |
| 4542 i < optimized_code_map->length(); | |
| 4543 i += SharedFunctionInfo::kEntryLength) { | |
| 4544 cleared_count += | |
| 4545 WeakCell::cast( | |
| 4546 optimized_code_map->get(i + SharedFunctionInfo::kContextOffset)) | |
| 4547 ->cleared() | |
| 4548 ? 1 | |
| 4549 : 0; | |
| 4550 } | |
| 4551 CHECK_EQ(1, cleared_count); | |
| 4552 | |
| 4553 // Verify that a new context uses the cleared entry rather than creating a | |
| 4554 // new | |
| 4555 // optimized code map array. | |
| 4556 v8::Local<v8::Context> c4 = v8::Context::New(v8_isolate); | |
| 4557 c4->Enter(); | |
| 4558 indep->BindToCurrentContext()->Run(c4).ToLocalChecked(); | |
| 4559 CompileRun(toplevel); | |
| 4560 c4->Exit(); | |
| 4561 CHECK_EQ(*optimized_code_map, shared->optimized_code_map()); | |
| 4562 | |
| 4563 // Now each entry is in use. | |
| 4564 cleared_count = 0; | |
| 4565 for (int i = SharedFunctionInfo::kEntriesStart; | |
| 4566 i < optimized_code_map->length(); | |
| 4567 i += SharedFunctionInfo::kEntryLength) { | |
| 4568 cleared_count += | |
| 4569 WeakCell::cast( | |
| 4570 optimized_code_map->get(i + SharedFunctionInfo::kContextOffset)) | |
| 4571 ->cleared() | |
| 4572 ? 1 | |
| 4573 : 0; | |
| 4574 } | |
| 4575 CHECK_EQ(0, cleared_count); | |
| 4576 } | |
| 4577 } | |
| 4578 | |
| 4579 TEST(Regress513496) { | 4375 TEST(Regress513496) { |
| 4580 i::FLAG_allow_natives_syntax = true; | 4376 i::FLAG_allow_natives_syntax = true; |
| 4581 CcTest::InitializeVM(); | 4377 CcTest::InitializeVM(); |
| 4582 Isolate* isolate = CcTest::i_isolate(); | 4378 Isolate* isolate = CcTest::i_isolate(); |
| 4583 HandleScope scope(isolate); | 4379 HandleScope scope(isolate); |
| 4584 | 4380 |
| 4585 // Perfrom one initial GC to enable code flushing. | 4381 // Perfrom one initial GC to enable code flushing. |
| 4586 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 4382 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
| 4587 | 4383 |
| 4588 // Prepare an optimized closure with containing an inlined function. Then age | 4384 // Prepare an optimized closure with containing an inlined function. Then age |
| (...skipping 25 matching lines...) Expand all Loading... |
| 4614 Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle( | 4410 Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle( |
| 4615 *v8::Local<v8::Function>::Cast(CcTest::global() | 4411 *v8::Local<v8::Function>::Cast(CcTest::global() |
| 4616 ->Get(context.local(), v8_str("f")) | 4412 ->Get(context.local(), v8_str("f")) |
| 4617 .ToLocalChecked()))); | 4413 .ToLocalChecked()))); |
| 4618 CHECK(f->is_compiled()); | 4414 CHECK(f->is_compiled()); |
| 4619 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); | 4415 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); |
| 4620 CompileRun("f = null"); | 4416 CompileRun("f = null"); |
| 4621 } | 4417 } |
| 4622 | 4418 |
| 4623 // Lookup the optimized code and keep it alive. | 4419 // Lookup the optimized code and keep it alive. |
| 4624 CodeAndVector result = shared->SearchOptimizedCodeMap( | 4420 Code* result = shared->SearchOptimizedCodeMap( |
| 4625 isolate->context()->native_context(), BailoutId::None()); | 4421 isolate->context()->native_context(), BailoutId::None()); |
| 4626 Handle<Code> optimized_code(result.code, isolate); | 4422 Handle<Code> optimized_code(result, isolate); |
| 4627 | 4423 |
| 4628 // Finish a full GC cycle so that the unoptimized code of 'g' is flushed even | 4424 // Finish a full GC cycle so that the unoptimized code of 'g' is flushed even |
| 4629 // though the optimized code for 'f' is reachable via the optimized code map. | 4425 // though the optimized code for 'f' is reachable via the optimized code map. |
| 4630 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 4426 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
| 4631 | 4427 |
| 4632 // Make a new closure that will get code installed from the code map. | 4428 // Make a new closure that will get code installed from the code map. |
| 4633 // Unoptimized code is missing and the deoptimizer will go ballistic. | 4429 // Unoptimized code is missing and the deoptimizer will go ballistic. |
| 4634 CompileRun("var h = mkClosure(); h('bozo');"); | 4430 CompileRun("var h = mkClosure(); h('bozo');"); |
| 4635 } | 4431 } |
| 4636 | 4432 |
| (...skipping 2511 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7148 CHECK(!heap->code_space()->FirstPage()->Contains(code->address())); | 6944 CHECK(!heap->code_space()->FirstPage()->Contains(code->address())); |
| 7149 | 6945 |
| 7150 // Ensure it's not in large object space. | 6946 // Ensure it's not in large object space. |
| 7151 MemoryChunk* chunk = MemoryChunk::FromAddress(code->address()); | 6947 MemoryChunk* chunk = MemoryChunk::FromAddress(code->address()); |
| 7152 CHECK(chunk->owner()->identity() != LO_SPACE); | 6948 CHECK(chunk->owner()->identity() != LO_SPACE); |
| 7153 CHECK(chunk->NeverEvacuate()); | 6949 CHECK(chunk->NeverEvacuate()); |
| 7154 } | 6950 } |
| 7155 | 6951 |
| 7156 } // namespace internal | 6952 } // namespace internal |
| 7157 } // namespace v8 | 6953 } // namespace v8 |
| OLD | NEW |