// young generation.
void Age();
+ bool HasFunction(SharedFunctionInfo* function_info);
+
// GC support.
void Iterate(ObjectVisitor* v);
}
+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() {
// Age the generations implicitly killing off the oldest.
for (int i = generations_ - 1; i > 0; i--) {
}
+bool CompilationCache::HasFunction(SharedFunctionInfo* function_info) {
+ return script.HasFunction(function_info);
+}
+
+
void CompilationCache::Iterate(ObjectVisitor* v) {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches[i]->Iterate(v);
// 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);
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());
}
EnsureFromSpaceIsCommitted();
if (collector == MARK_COMPACTOR) {
+ // Flush all potentially unused code.
+ FlushCode();
+
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
}
+// 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,
ZoneScopeInfo* sinfo,
Code::Flags flags,
// 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;
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)
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);
// Bit positions in compiler_hints.
static const int kHasOnlySimpleThisPropertyAssignments = 0;
static const int kTryFullCodegen = 1;
+ static const int kAllowLazyCompilation = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
};
// 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());
+}