Revision: 4814
Author: [email protected]
Date: Mon Jun 7 08:39:10 2010
Log: Flushing of code from functions that we expect not to use again.
This adds an additional step to full gc, removing code from functions
that are no longer in the compilation cache. The code is replaced with
a lazy compile version enabling us to recompile the function in case
we do actually need it again.
Review URL: http://codereview.chromium.org/2632003
http://code.google.com/p/v8/source/detail?r=4814
Modified:
/branches/bleeding_edge/src/compilation-cache.cc
/branches/bleeding_edge/src/compilation-cache.h
/branches/bleeding_edge/src/compiler.cc
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/test/cctest/test-heap.cc
=======================================
--- /branches/bleeding_edge/src/compilation-cache.cc Mon Apr 19 05:39:07
2010
+++ /branches/bleeding_edge/src/compilation-cache.cc Mon Jun 7 08:39:10
2010
@@ -79,6 +79,8 @@
// young generation.
void Age();
+ bool HasFunction(SharedFunctionInfo* function_info);
+
// GC support.
void Iterate(ObjectVisitor* v);
@@ -202,6 +204,27 @@
}
return result;
}
+
+
+bool CompilationSubCache::HasFunction(SharedFunctionInfo* function_info) {
+ if (function_info->script()->IsUndefined() ||
+ Script::cast(function_info->script())->source()->IsUndefined()) {
+ return false;
+ }
+
+ String* source =
+ String::cast(Script::cast(function_info->script())->source());
+ // Check all generations.
+ for (int generation = 0; generation < generations(); generation++) {
+ if (tables_[generation]->IsUndefined()) continue;
+
+ CompilationCacheTable* table =
+ CompilationCacheTable::cast(tables_[generation]);
+ Object* object = table->Lookup(source);
+ if (object->IsSharedFunctionInfo()) return true;
+ }
+ return false;
+}
void CompilationSubCache::Age() {
@@ -504,6 +527,11 @@
subcaches[i]->Clear();
}
}
+
+
+bool CompilationCache::HasFunction(SharedFunctionInfo* function_info) {
+ return script.HasFunction(function_info);
+}
void CompilationCache::Iterate(ObjectVisitor* v) {
=======================================
--- /branches/bleeding_edge/src/compilation-cache.h Mon Apr 19 05:39:07 2010
+++ /branches/bleeding_edge/src/compilation-cache.h Mon Jun 7 08:39:10 2010
@@ -79,6 +79,9 @@
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
+
+ static bool HasFunction(SharedFunctionInfo* function_info);
+
// GC support.
static void Iterate(ObjectVisitor* v);
=======================================
--- /branches/bleeding_edge/src/compiler.cc Tue May 25 07:08:17 2010
+++ /branches/bleeding_edge/src/compiler.cc Mon Jun 7 08:39:10 2010
@@ -601,6 +601,7 @@
lit->has_only_simple_this_property_assignments(),
*lit->this_property_assignments());
function_info->set_try_full_codegen(lit->try_full_codegen());
+ function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
}
=======================================
--- /branches/bleeding_edge/src/heap.cc Mon Jun 7 04:31:44 2010
+++ /branches/bleeding_edge/src/heap.cc Mon Jun 7 08:39:10 2010
@@ -607,6 +607,9 @@
EnsureFromSpaceIsCommitted();
if (collector == MARK_COMPACTOR) {
+ // Flush all potentially unused code.
+ FlushCode();
+
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
@@ -2184,6 +2187,85 @@
return result;
}
+
+
+// The StackVisitor is used to traverse all the archived threads to see if
+// there are activations on any of the stacks corresponding to the code.
+class FlushingStackVisitor : public ThreadVisitor {
+ public:
+ explicit FlushingStackVisitor(Code* code) : found_(false), code_(code) {}
+
+ void VisitThread(ThreadLocalTop* top) {
+ // If we already found the code in a previous traversed thread we
return.
+ if (found_) return;
+
+ for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ if (code_->contains(it.frame()->pc())) {
+ found_ = true;
+ return;
+ }
+ }
+ }
+ bool FoundCode() {return found_;}
+
+ private:
+ bool found_;
+ Code* code_;
+};
+
+
+static void FlushCodeForFunction(SharedFunctionInfo* function_info) {
+ // The function must be compiled and have the source code available,
+ // to be able to recompile it in case we need the function again.
+ if (!(function_info->is_compiled() && function_info->HasSourceCode()))
return;
+
+ // We never flush code for Api functions.
+ if (function_info->IsApiFunction()) return;
+
+ // Only flush code for functions.
+ if (!function_info->code()->kind() == Code::FUNCTION) return;
+
+ // Function must be lazy compilable.
+ if (!function_info->allows_lazy_compilation()) return;
+
+ // If this is a full script wrapped in a function we do no flush the
code.
+ if (function_info->is_toplevel()) return;
+
+ // If this function is in the compilation cache we do not flush the code.
+ if (CompilationCache::HasFunction(function_info)) return;
+
+ // Make sure we are not referencing the code from the stack.
+ for (StackFrameIterator it; !it.done(); it.Advance()) {
+ if (function_info->code()->contains(it.frame()->pc())) return;
+ }
+ // Iterate the archived stacks in all threads to check if
+ // the code is referenced.
+ FlushingStackVisitor threadvisitor(function_info->code());
+ ThreadManager::IterateArchivedThreads(&threadvisitor);
+ if (threadvisitor.FoundCode()) return;
+
+ HandleScope scope;
+ // Compute the lazy compilable version of the code.
+ function_info->set_code(*ComputeLazyCompile(function_info->length()));
+}
+
+
+void Heap::FlushCode() {
+ // Do not flush code if the debugger is loaded or there are breakpoints.
+ if (Debug::IsLoaded() || Debug::has_break_points()) return;
+ HeapObjectIterator it(old_pointer_space());
+ for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
+ if (obj->IsJSFunction()) {
+ JSFunction* jsfunction = JSFunction::cast(obj);
+
+ // The function must have a valid context and not be a builtin.
+ if (jsfunction->unchecked_context()->IsContext() &&
+ !jsfunction->IsBuiltin()) {
+ FlushCodeForFunction(jsfunction->shared());
+ }
+ }
+ }
+}
Object* Heap::CreateCode(const CodeDesc& desc,
=======================================
--- /branches/bleeding_edge/src/heap.h Mon Jun 7 01:49:07 2010
+++ /branches/bleeding_edge/src/heap.h Mon Jun 7 08:39:10 2010
@@ -1274,6 +1274,10 @@
// Flush the number to string cache.
static void FlushNumberStringCache();
+ // Flush code from functions we do not expect to use again. The code will
+ // be replaced with a lazy compilable version.
+ static void FlushCode();
+
static const int kInitialSymbolTableSize = 2048;
static const int kInitialEvalCacheSize = 64;
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Mon Jun 7 02:36:30 2010
+++ /branches/bleeding_edge/src/objects-inl.h Mon Jun 7 08:39:10 2010
@@ -2468,6 +2468,10 @@
compiler_hints,
try_full_codegen,
kTryFullCodegen)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ allows_lazy_compilation,
+ kAllowLazyCompilation)
#if V8_HOST_ARCH_32_BIT
SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
=======================================
--- /branches/bleeding_edge/src/objects.h Mon Jun 7 02:36:30 2010
+++ /branches/bleeding_edge/src/objects.h Mon Jun 7 08:39:10 2010
@@ -3308,6 +3308,12 @@
inline bool try_full_codegen();
inline void set_try_full_codegen(bool flag);
+ // Indicates if this function can be lazy compiled.
+ // This is used to determine if we can safely flush code from a function
+ // when doing GC if we expect that the function will no longer be used.
+ inline bool allows_lazy_compilation();
+ inline void set_allows_lazy_compilation(bool flag);
+
// Check whether a inlined constructor can be generated with the given
// prototype.
bool CanGenerateInlineConstructor(Object* prototype);
@@ -3433,6 +3439,7 @@
// Bit positions in compiler_hints.
static const int kHasOnlySimpleThisPropertyAssignments = 0;
static const int kTryFullCodegen = 1;
+ static const int kAllowLazyCompilation = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
};
=======================================
--- /branches/bleeding_edge/test/cctest/test-heap.cc Thu May 27 05:30:45
2010
+++ /branches/bleeding_edge/test/cctest/test-heap.cc Mon Jun 7 08:39:10
2010
@@ -957,3 +957,42 @@
// Check that region covering inobject property 1 is marked dirty.
CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize)));
}
+
+TEST(TestCodeFlushing) {
+ i::FLAG_allow_natives_syntax = true;
+ InitializeVM();
+ v8::HandleScope scope;
+ const char* source = "function foo() {"
+ " var x = 42;"
+ " var y = 42;"
+ " var z = x + y;"
+ "};"
+ "foo()";
+ Handle<String> foo_name = Factory::LookupAsciiSymbol("foo");
+
+ // This compile will add the code to the compilation cache.
+ CompileRun(source);
+
+ // Check function is compiled.
+ Object* func_value = Top::context()->global()->GetProperty(*foo_name);
+ CHECK(func_value->IsJSFunction());
+ Handle<JSFunction> function(JSFunction::cast(func_value));
+ CHECK(function->shared()->is_compiled());
+
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+
+ // foo should still be in the compilation cache and therefore not
+ // have been removed.
+ CHECK(function->shared()->is_compiled());
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+
+ // foo should no longer be in the compilation cache
+ CHECK(!function->shared()->is_compiled());
+ // Call foo to get it recompiled.
+ CompileRun("foo()");
+ CHECK(function->shared()->is_compiled());
+}
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev