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 1659 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1670 "foo();"; | 1670 "foo();"; |
1671 Handle<String> source = factory->InternalizeUtf8String(raw_source); | 1671 Handle<String> source = factory->InternalizeUtf8String(raw_source); |
1672 Handle<Context> native_context = isolate->native_context(); | 1672 Handle<Context> native_context = isolate->native_context(); |
1673 | 1673 |
1674 { | 1674 { |
1675 v8::HandleScope scope(CcTest::isolate()); | 1675 v8::HandleScope scope(CcTest::isolate()); |
1676 CompileRun(raw_source); | 1676 CompileRun(raw_source); |
1677 } | 1677 } |
1678 | 1678 |
1679 // The script should be in the cache now. | 1679 // The script should be in the cache now. |
1680 MaybeHandle<SharedFunctionInfo> info = compilation_cache->LookupScript( | 1680 InfoVectorPair pair = compilation_cache->LookupScript( |
1681 source, Handle<Object>(), 0, 0, v8::ScriptOriginOptions(true, false), | 1681 source, Handle<Object>(), 0, 0, v8::ScriptOriginOptions(true, false), |
1682 native_context, language_mode); | 1682 native_context, language_mode); |
1683 CHECK(!info.is_null()); | 1683 CHECK(pair.has_info()); |
1684 | 1684 |
1685 // Check that the code cache entry survives at least on GC. | 1685 // Check that the code cache entry survives at least on GC. |
1686 // (Unless --optimize-for-size, in which case it might get collected | 1686 // (Unless --optimize-for-size, in which case it might get collected |
1687 // immediately.) | 1687 // immediately.) |
1688 if (!FLAG_optimize_for_size) { | 1688 if (!FLAG_optimize_for_size) { |
1689 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 1689 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
1690 info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, | 1690 pair = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, |
1691 v8::ScriptOriginOptions(true, false), | 1691 v8::ScriptOriginOptions(true, false), |
1692 native_context, language_mode); | 1692 native_context, language_mode); |
1693 CHECK(!info.is_null()); | 1693 CHECK(pair.has_info()); |
1694 } | 1694 } |
1695 | 1695 |
1696 // Progress code age until it's old and ready for GC. | 1696 // Progress code age until it's old and ready for GC. |
1697 const int kAgingThreshold = 6; | 1697 const int kAgingThreshold = 6; |
1698 for (int i = 0; i < kAgingThreshold; i++) { | 1698 for (int i = 0; i < kAgingThreshold; i++) { |
1699 info.ToHandleChecked()->code()->MakeOlder(); | 1699 pair.info()->code()->MakeOlder(); |
1700 if (info.ToHandleChecked()->HasBytecodeArray()) { | 1700 if (pair.info()->HasBytecodeArray()) { |
1701 info.ToHandleChecked()->bytecode_array()->MakeOlder(); | 1701 pair.info()->bytecode_array()->MakeOlder(); |
1702 } | 1702 } |
1703 } | 1703 } |
1704 | 1704 |
1705 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 1705 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
1706 // Ensure code aging cleared the entry from the cache. | 1706 // Ensure code aging cleared the entry from the cache. |
1707 info = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, | 1707 pair = compilation_cache->LookupScript(source, Handle<Object>(), 0, 0, |
1708 v8::ScriptOriginOptions(true, false), | 1708 v8::ScriptOriginOptions(true, false), |
1709 native_context, language_mode); | 1709 native_context, language_mode); |
1710 CHECK(info.is_null()); | 1710 CHECK(!pair.has_info()); |
1711 } | 1711 } |
1712 | 1712 |
1713 | 1713 |
1714 static void OptimizeEmptyFunction(const char* name) { | 1714 static void OptimizeEmptyFunction(const char* name) { |
1715 HandleScope scope(CcTest::i_isolate()); | 1715 HandleScope scope(CcTest::i_isolate()); |
1716 EmbeddedVector<char, 256> source; | 1716 EmbeddedVector<char, 256> source; |
1717 SNPrintF(source, | 1717 SNPrintF(source, |
1718 "function %s() { return 0; }" | 1718 "function %s() { return 0; }" |
1719 "%s(); %s();" | 1719 "%s(); %s();" |
1720 "%%OptimizeFunctionOnNextCall(%s);" | 1720 "%%OptimizeFunctionOnNextCall(%s);" |
(...skipping 2604 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4325 CompileRun("function g() { return 2 }" | 4325 CompileRun("function g() { return 2 }" |
4326 "g(); %OptimizeFunctionOnNextCall(g); g();"); | 4326 "g(); %OptimizeFunctionOnNextCall(g); g();"); |
4327 | 4327 |
4328 Handle<JSFunction> g = Handle<JSFunction>::cast( | 4328 Handle<JSFunction> g = Handle<JSFunction>::cast( |
4329 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | 4329 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( |
4330 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); | 4330 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); |
4331 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); | 4331 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); |
4332 if (!code->is_optimized_code()) return; | 4332 if (!code->is_optimized_code()) return; |
4333 } | 4333 } |
4334 | 4334 |
4335 Handle<TypeFeedbackVector> vector = | |
4336 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
4337 Handle<Context> context(isolate->context()); | 4335 Handle<Context> context(isolate->context()); |
4338 | 4336 |
4339 // Add the new code several times to the optimized code map and also set an | 4337 // Add the new code several times to the optimized code map and also set an |
4340 // allocation timeout so that expanding the code map will trigger a GC. | 4338 // allocation timeout so that expanding the code map will trigger a GC. |
4341 heap->set_allocation_timeout(5); | 4339 heap->set_allocation_timeout(5); |
4342 FLAG_gc_interval = 1000; | 4340 FLAG_gc_interval = 1000; |
4343 for (int i = 0; i < 10; ++i) { | 4341 for (int i = 0; i < 10; ++i) { |
4344 BailoutId id = BailoutId(i); | 4342 BailoutId id = BailoutId(i); |
4345 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | 4343 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, id); |
4346 id); | |
4347 } | 4344 } |
4348 } | 4345 } |
4349 #endif // DEBUG | 4346 #endif // DEBUG |
4350 | 4347 |
4351 TEST(Regress514122) { | |
4352 if (!i::FLAG_incremental_marking) return; | |
4353 i::FLAG_allow_natives_syntax = true; | |
4354 CcTest::InitializeVM(); | |
4355 Isolate* isolate = CcTest::i_isolate(); | |
4356 LocalContext env; | |
4357 Heap* heap = isolate->heap(); | |
4358 HandleScope scope(isolate); | |
4359 | |
4360 // Perfrom one initial GC to enable code flushing. | |
4361 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
4362 | |
4363 // Prepare function whose optimized code map we can use. | |
4364 Handle<SharedFunctionInfo> shared; | |
4365 { | |
4366 HandleScope inner_scope(isolate); | |
4367 CompileRun( | |
4368 "function f() { return 1 }" | |
4369 "f(); %OptimizeFunctionOnNextCall(f); f();"); | |
4370 | |
4371 Handle<JSFunction> f = Handle<JSFunction>::cast( | |
4372 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
4373 CcTest::global()->Get(env.local(), v8_str("f")).ToLocalChecked()))); | |
4374 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); | |
4375 CompileRun("f = null"); | |
4376 } | |
4377 | |
4378 // Prepare optimized code that we can use. | |
4379 Handle<Code> code; | |
4380 { | |
4381 HandleScope inner_scope(isolate); | |
4382 CompileRun( | |
4383 "function g() { return 2 }" | |
4384 "g(); %OptimizeFunctionOnNextCall(g); g();"); | |
4385 | |
4386 Handle<JSFunction> g = Handle<JSFunction>::cast( | |
4387 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
4388 CcTest::global()->Get(env.local(), v8_str("g")).ToLocalChecked()))); | |
4389 code = inner_scope.CloseAndEscape(handle(g->code(), isolate)); | |
4390 if (!code->is_optimized_code()) return; | |
4391 } | |
4392 | |
4393 Handle<TypeFeedbackVector> vector = | |
4394 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
4395 Handle<Context> context(isolate->context()); | |
4396 | |
4397 // Add the code several times to the optimized code map. | |
4398 for (int i = 0; i < 3; ++i) { | |
4399 HandleScope inner_scope(isolate); | |
4400 BailoutId id = BailoutId(i); | |
4401 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
4402 id); | |
4403 } | |
4404 shared->optimized_code_map()->Print(); | |
4405 | |
4406 // Add the code with a feedback vector to be evacuated. | |
4407 Page* evac_page; | |
4408 { | |
4409 HandleScope inner_scope(isolate); | |
4410 AlwaysAllocateScope always_allocate(isolate); | |
4411 // Make sure literal is placed on an old-space evacuation candidate. | |
4412 heap::SimulateFullSpace(heap->old_space()); | |
4413 | |
4414 // Make sure there the number of literals is > 0. | |
4415 Handle<TypeFeedbackVector> vector = | |
4416 TypeFeedbackVector::New(isolate, handle(shared->feedback_metadata())); | |
4417 evac_page = Page::FromAddress(vector->address()); | |
4418 BailoutId id = BailoutId(100); | |
4419 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
4420 id); | |
4421 } | |
4422 | |
4423 // Heap is ready, force {lit_page} to become an evacuation candidate and | |
4424 // simulate incremental marking to enqueue optimized code map. | |
4425 FLAG_manual_evacuation_candidates_selection = true; | |
4426 heap::ForceEvacuationCandidate(evac_page); | |
4427 heap::SimulateIncrementalMarking(heap); | |
4428 | |
4429 // No matter whether reachable or not, {boomer} is doomed. | |
4430 Handle<Object> boomer(shared->optimized_code_map(), isolate); | |
4431 | |
4432 // Add the code several times to the optimized code map. This will leave old | |
4433 // copies of the optimized code map unreachable but still marked. | |
4434 for (int i = 3; i < 6; ++i) { | |
4435 HandleScope inner_scope(isolate); | |
4436 BailoutId id = BailoutId(i); | |
4437 SharedFunctionInfo::AddToOptimizedCodeMap(shared, context, code, vector, | |
4438 id); | |
4439 } | |
4440 | |
4441 // Trigger a GC to flush out the bug. | |
4442 CcTest::CollectGarbage(i::OLD_SPACE); | |
4443 boomer->Print(); | |
4444 } | |
4445 | |
4446 TEST(OptimizedCodeMapReuseEntries) { | |
4447 i::FLAG_allow_natives_syntax = true; | |
4448 // BUG(v8:4598): Since TurboFan doesn't treat maps in code weakly, we can't | |
4449 // run this test. | |
4450 if (i::FLAG_turbo) return; | |
4451 CcTest::InitializeVM(); | |
4452 v8::Isolate* v8_isolate = CcTest::isolate(); | |
4453 Isolate* isolate = CcTest::i_isolate(); | |
4454 HandleScope scope(isolate); | |
4455 | |
4456 // Create 3 contexts, allow the 2nd one to be disposed, and verify that | |
4457 // a 4th context will re-use the weak slots in the optimized code map | |
4458 // to hold data, rather than expanding the map. | |
4459 v8::Local<v8::Context> c1 = v8::Context::New(v8_isolate); | |
4460 const char* source = "function foo(x) { var l = [1]; return x+l[0]; }"; | |
4461 v8::ScriptCompiler::Source script_source( | |
4462 v8::String::NewFromUtf8(v8_isolate, source, v8::NewStringType::kNormal) | |
4463 .ToLocalChecked()); | |
4464 v8::Local<v8::UnboundScript> indep = | |
4465 v8::ScriptCompiler::CompileUnboundScript(v8_isolate, &script_source) | |
4466 .ToLocalChecked(); | |
4467 const char* toplevel = "foo(3); %OptimizeFunctionOnNextCall(foo); foo(3);"; | |
4468 // Perfrom one initial GC to enable code flushing. | |
4469 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
4470 | |
4471 c1->Enter(); | |
4472 indep->BindToCurrentContext()->Run(c1).ToLocalChecked(); | |
4473 CompileRun(toplevel); | |
4474 | |
4475 Handle<SharedFunctionInfo> shared; | |
4476 Handle<JSFunction> foo = Handle<JSFunction>::cast( | |
4477 v8::Utils::OpenHandle(*v8::Local<v8::Function>::Cast( | |
4478 CcTest::global()->Get(c1, v8_str("foo")).ToLocalChecked()))); | |
4479 CHECK(foo->shared()->is_compiled()); | |
4480 shared = handle(foo->shared()); | |
4481 c1->Exit(); | |
4482 | |
4483 { | |
4484 HandleScope scope(isolate); | |
4485 v8::Local<v8::Context> c2 = v8::Context::New(v8_isolate); | |
4486 c2->Enter(); | |
4487 indep->BindToCurrentContext()->Run(c2).ToLocalChecked(); | |
4488 CompileRun(toplevel); | |
4489 c2->Exit(); | |
4490 } | |
4491 | |
4492 { | |
4493 HandleScope scope(isolate); | |
4494 v8::Local<v8::Context> c3 = v8::Context::New(v8_isolate); | |
4495 c3->Enter(); | |
4496 indep->BindToCurrentContext()->Run(c3).ToLocalChecked(); | |
4497 CompileRun(toplevel); | |
4498 c3->Exit(); | |
4499 | |
4500 // Now, collect garbage. Context c2 should have no roots to it, and it's | |
4501 // entry in the optimized code map should be free for a new context. | |
4502 for (int i = 0; i < 4; i++) { | |
4503 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | |
4504 } | |
4505 | |
4506 Handle<FixedArray> optimized_code_map = | |
4507 handle(shared->optimized_code_map()); | |
4508 // There should be 3 entries in the map. | |
4509 CHECK_EQ( | |
4510 3, ((optimized_code_map->length() - SharedFunctionInfo::kEntriesStart) / | |
4511 SharedFunctionInfo::kEntryLength)); | |
4512 // But one of them (formerly for c2) should be cleared. | |
4513 int cleared_count = 0; | |
4514 for (int i = SharedFunctionInfo::kEntriesStart; | |
4515 i < optimized_code_map->length(); | |
4516 i += SharedFunctionInfo::kEntryLength) { | |
4517 cleared_count += | |
4518 WeakCell::cast( | |
4519 optimized_code_map->get(i + SharedFunctionInfo::kContextOffset)) | |
4520 ->cleared() | |
4521 ? 1 | |
4522 : 0; | |
4523 } | |
4524 CHECK_EQ(1, cleared_count); | |
4525 | |
4526 // Verify that a new context uses the cleared entry rather than creating a | |
4527 // new | |
4528 // optimized code map array. | |
4529 v8::Local<v8::Context> c4 = v8::Context::New(v8_isolate); | |
4530 c4->Enter(); | |
4531 indep->BindToCurrentContext()->Run(c4).ToLocalChecked(); | |
4532 CompileRun(toplevel); | |
4533 c4->Exit(); | |
4534 CHECK_EQ(*optimized_code_map, shared->optimized_code_map()); | |
4535 | |
4536 // Now each entry is in use. | |
4537 cleared_count = 0; | |
4538 for (int i = SharedFunctionInfo::kEntriesStart; | |
4539 i < optimized_code_map->length(); | |
4540 i += SharedFunctionInfo::kEntryLength) { | |
4541 cleared_count += | |
4542 WeakCell::cast( | |
4543 optimized_code_map->get(i + SharedFunctionInfo::kContextOffset)) | |
4544 ->cleared() | |
4545 ? 1 | |
4546 : 0; | |
4547 } | |
4548 CHECK_EQ(0, cleared_count); | |
4549 } | |
4550 } | |
4551 | |
4552 TEST(Regress513496) { | 4348 TEST(Regress513496) { |
4553 i::FLAG_allow_natives_syntax = true; | 4349 i::FLAG_allow_natives_syntax = true; |
4554 CcTest::InitializeVM(); | 4350 CcTest::InitializeVM(); |
4555 Isolate* isolate = CcTest::i_isolate(); | 4351 Isolate* isolate = CcTest::i_isolate(); |
4556 HandleScope scope(isolate); | 4352 HandleScope scope(isolate); |
4557 | 4353 |
4558 // Perfrom one initial GC to enable code flushing. | 4354 // Perfrom one initial GC to enable code flushing. |
4559 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 4355 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
4560 | 4356 |
4561 // Prepare an optimized closure with containing an inlined function. Then age | 4357 // Prepare an optimized closure with containing an inlined function. Then age |
(...skipping 25 matching lines...) Expand all Loading... |
4587 Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle( | 4383 Handle<JSFunction> f = Handle<JSFunction>::cast(v8::Utils::OpenHandle( |
4588 *v8::Local<v8::Function>::Cast(CcTest::global() | 4384 *v8::Local<v8::Function>::Cast(CcTest::global() |
4589 ->Get(context.local(), v8_str("f")) | 4385 ->Get(context.local(), v8_str("f")) |
4590 .ToLocalChecked()))); | 4386 .ToLocalChecked()))); |
4591 CHECK(f->is_compiled()); | 4387 CHECK(f->is_compiled()); |
4592 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); | 4388 shared = inner_scope.CloseAndEscape(handle(f->shared(), isolate)); |
4593 CompileRun("f = null"); | 4389 CompileRun("f = null"); |
4594 } | 4390 } |
4595 | 4391 |
4596 // Lookup the optimized code and keep it alive. | 4392 // Lookup the optimized code and keep it alive. |
4597 CodeAndVector result = shared->SearchOptimizedCodeMap( | 4393 Code* result = shared->SearchOptimizedCodeMap( |
4598 isolate->context()->native_context(), BailoutId::None()); | 4394 isolate->context()->native_context(), BailoutId::None()); |
4599 Handle<Code> optimized_code(result.code, isolate); | 4395 Handle<Code> optimized_code(result, isolate); |
4600 | 4396 |
4601 // Finish a full GC cycle so that the unoptimized code of 'g' is flushed even | 4397 // Finish a full GC cycle so that the unoptimized code of 'g' is flushed even |
4602 // though the optimized code for 'f' is reachable via the optimized code map. | 4398 // though the optimized code for 'f' is reachable via the optimized code map. |
4603 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); | 4399 CcTest::CollectAllGarbage(i::Heap::kFinalizeIncrementalMarkingMask); |
4604 | 4400 |
4605 // Make a new closure that will get code installed from the code map. | 4401 // Make a new closure that will get code installed from the code map. |
4606 // Unoptimized code is missing and the deoptimizer will go ballistic. | 4402 // Unoptimized code is missing and the deoptimizer will go ballistic. |
4607 CompileRun("var h = mkClosure(); h('bozo');"); | 4403 CompileRun("var h = mkClosure(); h('bozo');"); |
4608 } | 4404 } |
4609 | 4405 |
(...skipping 2515 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7125 CHECK(!heap->code_space()->FirstPage()->Contains(code->address())); | 6921 CHECK(!heap->code_space()->FirstPage()->Contains(code->address())); |
7126 | 6922 |
7127 // Ensure it's not in large object space. | 6923 // Ensure it's not in large object space. |
7128 MemoryChunk* chunk = MemoryChunk::FromAddress(code->address()); | 6924 MemoryChunk* chunk = MemoryChunk::FromAddress(code->address()); |
7129 CHECK(chunk->owner()->identity() != LO_SPACE); | 6925 CHECK(chunk->owner()->identity() != LO_SPACE); |
7130 CHECK(chunk->NeverEvacuate()); | 6926 CHECK(chunk->NeverEvacuate()); |
7131 } | 6927 } |
7132 | 6928 |
7133 } // namespace internal | 6929 } // namespace internal |
7134 } // namespace v8 | 6930 } // namespace v8 |
OLD | NEW |