diff options
Diffstat (limited to 'deps/v8/test/unittests/objects/osr-optimized-code-cache-unittest.cc')
-rw-r--r-- | deps/v8/test/unittests/objects/osr-optimized-code-cache-unittest.cc | 412 |
1 files changed, 412 insertions, 0 deletions
diff --git a/deps/v8/test/unittests/objects/osr-optimized-code-cache-unittest.cc b/deps/v8/test/unittests/objects/osr-optimized-code-cache-unittest.cc new file mode 100644 index 0000000000..225048de63 --- /dev/null +++ b/deps/v8/test/unittests/objects/osr-optimized-code-cache-unittest.cc @@ -0,0 +1,412 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <cmath> +#include <iostream> +#include <limits> + +#include "src/deoptimizer/deoptimizer.h" +#include "src/objects/objects-inl.h" +#include "src/objects/objects.h" +#include "src/objects/osr-optimized-code-cache.h" +#include "test/unittests/test-utils.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace v8 { +namespace internal { + +namespace { + +const char* code_template_string = + "function f%d() { return 0; };" + "%%PrepareFunctionForOptimization(f%d);" + "f%d(); f%d();" + "%%OptimizeFunctionOnNextCall(f%d);" + "f%d(); f%d;"; + +void GetSource(i::ScopedVector<char>* source, int index) { + i::SNPrintF(*source, code_template_string, index, index, index, index, index, + index, index); +} + +const int kInitialLength = OSROptimizedCodeCache::kInitialLength; +const int kInitialEntries = + kInitialLength / OSROptimizedCodeCache::kEntryLength; +const int kMaxLength = OSROptimizedCodeCache::kMaxLength; +const int kMaxEntries = kMaxLength / OSROptimizedCodeCache::kEntryLength; + +} // namespace + +TEST_F(TestWithNativeContext, AddCodeToEmptyCache) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + BailoutId bailout_id(1); + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + bailout_id); + + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kInitialLength); + + HeapObject sfi_entry; + osr_cache->Get(OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&sfi_entry); + EXPECT_EQ(sfi_entry, *shared); + HeapObject code_entry; + osr_cache->Get(OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&code_entry); + EXPECT_EQ(code_entry, *code); + Smi osr_offset_entry; + osr_cache->Get(OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&osr_offset_entry); + EXPECT_EQ(osr_offset_entry.value(), bailout_id.ToInt()); +} + +TEST_F(TestWithNativeContext, GrowCodeCache) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + int bailout_id = 0; + for (bailout_id = 0; bailout_id < kInitialEntries; bailout_id++) { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kInitialLength); + + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kInitialLength * 2); + + int index = kInitialLength; + HeapObject sfi_entry; + osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&sfi_entry); + EXPECT_EQ(sfi_entry, *shared); + HeapObject code_entry; + osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&code_entry); + EXPECT_EQ(code_entry, *code); + Smi osr_offset_entry; + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset) + ->ToSmi(&osr_offset_entry); + EXPECT_EQ(osr_offset_entry.value(), bailout_id); +} + +TEST_F(TestWithNativeContext, FindCachedEntry) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + int bailout_id = 0; + for (bailout_id = 0; bailout_id < kInitialEntries; bailout_id++) { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + + i::ScopedVector<char> source1(1024); + GetSource(&source1, 1); + Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin()); + Handle<SharedFunctionInfo> shared1(function1->shared(), isolate); + Handle<Code> code1(function1->code(), isolate); + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1, + BailoutId(bailout_id)); + + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->GetOptimizedCode(shared, BailoutId(0), isolate), *code); + EXPECT_EQ( + osr_cache->GetOptimizedCode(shared1, BailoutId(bailout_id), isolate), + *code1); + + RunJS("%DeoptimizeFunction(f1)"); + EXPECT_TRUE( + osr_cache->GetOptimizedCode(shared1, BailoutId(bailout_id), isolate) + .is_null()); + + osr_cache->Set(OSROptimizedCodeCache::kCachedCodeOffset, + HeapObjectReference::ClearedValue(isolate)); + EXPECT_TRUE( + osr_cache->GetOptimizedCode(shared, BailoutId(0), isolate).is_null()); +} + +TEST_F(TestWithNativeContext, MaxCapacityCache) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + int bailout_id = 0; + // Add max_capacity - 1 entries. + for (bailout_id = 0; bailout_id < kMaxEntries - 1; bailout_id++) { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kMaxLength); + + // Add an entry to reach max capacity. + i::ScopedVector<char> source1(1024); + GetSource(&source1, 1); + Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin()); + Handle<SharedFunctionInfo> shared1(function1->shared(), isolate); + Handle<Code> code1(function1->code(), isolate); + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1, + BailoutId(bailout_id)); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kMaxLength); + + int index = (kMaxEntries - 1) * OSROptimizedCodeCache::kEntryLength; + HeapObject object; + Smi smi; + osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *shared1); + osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *code1); + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi); + EXPECT_EQ(smi.value(), bailout_id); + + // Add an entry beyond max capacity. + i::ScopedVector<char> source2(1024); + GetSource(&source2, 2); + Handle<JSFunction> function2 = RunJS<JSFunction>(source2.begin()); + Handle<SharedFunctionInfo> shared2(function2->shared(), isolate); + Handle<Code> code2(function2->code(), isolate); + bailout_id++; + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared2, code2, + BailoutId(bailout_id)); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kMaxLength); + + index = 0; + osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *shared2); + osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *code2); + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi); + EXPECT_EQ(smi.value(), bailout_id); +} + +TEST_F(TestWithNativeContext, ReuseClearedEntry) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + int num_entries = kInitialEntries * 2; + int expected_length = kInitialLength * 2; + int bailout_id = 0; + for (bailout_id = 0; bailout_id < num_entries; bailout_id++) { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + int clear_index1 = 0; + int clear_index2 = (num_entries - 1) * OSROptimizedCodeCache::kEntryLength; + osr_cache->Set(clear_index1 + OSROptimizedCodeCache::kSharedOffset, + HeapObjectReference::ClearedValue(isolate)); + osr_cache->Set(clear_index2 + OSROptimizedCodeCache::kCachedCodeOffset, + HeapObjectReference::ClearedValue(isolate)); + + i::ScopedVector<char> source1(1024); + GetSource(&source1, 1); + Handle<JSFunction> function1 = RunJS<JSFunction>(source1.begin()); + Handle<SharedFunctionInfo> shared1(function1->shared(), isolate); + Handle<Code> code1(function1->code(), isolate); + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared1, code1, + BailoutId(bailout_id)); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + int index = clear_index1; + HeapObject object; + Smi smi; + osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *shared1); + osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *code1); + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi); + EXPECT_EQ(smi.value(), bailout_id); + + i::ScopedVector<char> source2(1024); + GetSource(&source2, 2); + Handle<JSFunction> function2 = RunJS<JSFunction>(source2.begin()); + Handle<SharedFunctionInfo> shared2(function2->shared(), isolate); + Handle<Code> code2(function2->code(), isolate); + bailout_id++; + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared2, code2, + BailoutId(bailout_id)); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + index = clear_index2; + osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *shared2); + osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->GetHeapObject(&object); + EXPECT_EQ(object, *code2); + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->ToSmi(&smi); + EXPECT_EQ(smi.value(), bailout_id); +} + +TEST_F(TestWithNativeContext, EvictDeoptedEntriesNoCompact) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + i::ScopedVector<char> source1(1024); + GetSource(&source1, 1); + Handle<JSFunction> deopt_function = RunJS<JSFunction>(source1.begin()); + Handle<SharedFunctionInfo> deopt_shared(deopt_function->shared(), isolate); + Handle<Code> deopt_code(deopt_function->code(), isolate); + + int num_entries = kInitialEntries * 2; + int expected_length = kInitialLength * 2; + int deopt_id1 = num_entries - 2; + int deopt_id2 = 0; + int bailout_id = 0; + for (bailout_id = 0; bailout_id < num_entries; bailout_id++) { + if (bailout_id == deopt_id1 || bailout_id == deopt_id2) { + OSROptimizedCodeCache::AddOptimizedCode( + native_context, deopt_shared, deopt_code, BailoutId(bailout_id)); + } else { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + } + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + RunJS("%DeoptimizeFunction(f1)"); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + int index = (num_entries - 2) * OSROptimizedCodeCache::kEntryLength; + EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->IsCleared()); + EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->IsCleared()); + EXPECT_TRUE( + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->IsCleared()); + + index = (num_entries - 1) * OSROptimizedCodeCache::kEntryLength; + EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kSharedOffset) + ->IsCleared()); + EXPECT_TRUE(osr_cache->Get(index + OSROptimizedCodeCache::kCachedCodeOffset) + ->IsCleared()); + EXPECT_TRUE( + osr_cache->Get(index + OSROptimizedCodeCache::kOsrIdOffset)->IsCleared()); +} + +TEST_F(TestWithNativeContext, EvictDeoptedEntriesCompact) { + if (!i::FLAG_opt) return; + + i::FLAG_allow_natives_syntax = true; + + i::ScopedVector<char> source(1024); + GetSource(&source, 0); + Handle<JSFunction> function = RunJS<JSFunction>(source.begin()); + Isolate* isolate = function->GetIsolate(); + Handle<NativeContext> native_context(function->native_context(), isolate); + Handle<SharedFunctionInfo> shared(function->shared(), isolate); + Handle<Code> code(function->code(), isolate); + + i::ScopedVector<char> source1(1024); + GetSource(&source1, 1); + Handle<JSFunction> deopt_function = RunJS<JSFunction>(source1.begin()); + Handle<SharedFunctionInfo> deopt_shared(deopt_function->shared(), isolate); + Handle<Code> deopt_code(deopt_function->code(), isolate); + + int num_entries = kInitialEntries + 1; + int expected_length = kInitialLength * 2; + int bailout_id = 0; + for (bailout_id = 0; bailout_id < num_entries; bailout_id++) { + if (bailout_id % 2 == 0) { + OSROptimizedCodeCache::AddOptimizedCode( + native_context, deopt_shared, deopt_code, BailoutId(bailout_id)); + } else { + OSROptimizedCodeCache::AddOptimizedCode(native_context, shared, code, + BailoutId(bailout_id)); + } + } + Handle<OSROptimizedCodeCache> osr_cache( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), expected_length); + + RunJS("%DeoptimizeFunction(f1)"); + osr_cache = Handle<OSROptimizedCodeCache>( + native_context->GetOSROptimizedCodeCache(), isolate); + EXPECT_EQ(osr_cache->length(), kInitialLength); +} + +} // namespace internal +} // namespace v8 |