Revision: 13555
Author:   [email protected]
Date:     Wed Jan 30 05:57:19 2013
Log:      Merged r13361, r13481 into 3.15 branch.

Fix shared function info code replacement.

Fix corner case when JSFunction is evicted from flusher.

[email protected]
BUG=chromium:169209,chromium:168801
TEST=cctest/test-heap

Review URL: https://codereview.chromium.org/12084062
http://code.google.com/p/v8/source/detail?r=13555

Modified:
 /branches/3.15/src/compiler.cc
 /branches/3.15/src/mark-compact.cc
 /branches/3.15/src/mark-compact.h
 /branches/3.15/src/objects-inl.h
 /branches/3.15/src/objects.cc
 /branches/3.15/src/objects.h
 /branches/3.15/src/runtime.cc
 /branches/3.15/src/version.cc
 /branches/3.15/test/cctest/test-heap.cc

=======================================
--- /branches/3.15/src/compiler.cc      Mon Dec 10 11:00:50 2012
+++ /branches/3.15/src/compiler.cc      Wed Jan 30 05:57:19 2013
@@ -697,7 +697,7 @@
   Handle<ScopeInfo> scope_info =
       ScopeInfo::Create(info->scope(), info->zone());
   shared->set_scope_info(*scope_info);
-  shared->set_code(*code);
+  shared->ReplaceCode(*code);
   if (!function.is_null()) {
     function->ReplaceCode(*code);
     ASSERT(!function->IsOptimized());
=======================================
--- /branches/3.15/src/mark-compact.cc  Thu Dec  6 09:32:37 2012
+++ /branches/3.15/src/mark-compact.cc  Wed Jan 30 05:57:19 2013
@@ -885,8 +885,8 @@
     if (!code_mark.Get()) {
       shared->set_code(lazy_compile);
       candidate->set_code(lazy_compile);
-    } else if (code == lazy_compile) {
-      candidate->set_code(lazy_compile);
+    } else {
+      candidate->set_code(code);
     }

     // We are in the middle of a GC cycle so the write barrier in the code
@@ -933,15 +933,42 @@

   shared_function_info_candidates_head_ = NULL;
 }
+
+
+void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) {
+  // Make sure previous flushing decisions are revisited.
+  isolate_->heap()->incremental_marking()->RecordWrites(shared_info);
+
+  SharedFunctionInfo* candidate = shared_function_info_candidates_head_;
+  SharedFunctionInfo* next_candidate;
+  if (candidate == shared_info) {
+    next_candidate = GetNextCandidate(shared_info);
+    shared_function_info_candidates_head_ = next_candidate;
+    ClearNextCandidate(shared_info);
+  } else {
+    while (candidate != NULL) {
+      next_candidate = GetNextCandidate(candidate);
+
+      if (next_candidate == shared_info) {
+        next_candidate = GetNextCandidate(shared_info);
+        SetNextCandidate(candidate, next_candidate);
+        ClearNextCandidate(shared_info);
+        break;
+      }
+
+      candidate = next_candidate;
+    }
+  }
+}


 void CodeFlusher::EvictCandidate(JSFunction* function) {
   ASSERT(!function->next_function_link()->IsUndefined());
   Object* undefined = isolate_->heap()->undefined_value();

-  // The function is no longer a candidate, make sure it gets visited
-  // again so that previous flushing decisions are revisited.
+  // Make sure previous flushing decisions are revisited.
   isolate_->heap()->incremental_marking()->RecordWrites(function);
+ isolate_->heap()->incremental_marking()->RecordWrites(function->shared());

   JSFunction* candidate = jsfunction_candidates_head_;
   JSFunction* next_candidate;
@@ -957,6 +984,7 @@
         next_candidate = GetNextCandidate(function);
         SetNextCandidate(candidate, next_candidate);
         ClearNextCandidate(function, undefined);
+        break;
       }

       candidate = next_candidate;
=======================================
--- /branches/3.15/src/mark-compact.h   Tue Dec  4 05:52:03 2012
+++ /branches/3.15/src/mark-compact.h   Wed Jan 30 05:57:19 2013
@@ -434,6 +434,7 @@
     }
   }

+  void EvictCandidate(SharedFunctionInfo* shared_info);
   void EvictCandidate(JSFunction* function);

   void ProcessCandidates() {
=======================================
--- /branches/3.15/src/objects-inl.h    Tue Dec 11 09:41:11 2012
+++ /branches/3.15/src/objects-inl.h    Wed Jan 30 05:57:19 2013
@@ -4366,6 +4366,19 @@
   WRITE_FIELD(this, kCodeOffset, value);
CONDITIONAL_WRITE_BARRIER(value->GetHeap(), this, kCodeOffset, value, mode);
 }
+
+
+void SharedFunctionInfo::ReplaceCode(Code* value) {
+  // If the GC metadata field is already used then the function was
+  // enqueued as a code flushing candidate and we remove it now.
+  if (code()->gc_metadata() != NULL) {
+ CodeFlusher* flusher = GetHeap()->mark_compact_collector()->code_flusher();
+    flusher->EvictCandidate(this);
+  }
+
+  ASSERT(code()->gc_metadata() == NULL && value->gc_metadata() == NULL);
+  set_code(value);
+}


 ScopeInfo* SharedFunctionInfo::scope_info() {
=======================================
--- /branches/3.15/src/objects.cc       Tue Dec 11 09:41:11 2012
+++ /branches/3.15/src/objects.cc       Wed Jan 30 05:57:19 2013
@@ -8562,7 +8562,7 @@
     // old code, we have to replace it. We should try to avoid this
     // altogether because it flushes valuable type feedback by
     // effectively resetting all IC state.
-    set_code(recompiled);
+    ReplaceCode(recompiled);
   }
   ASSERT(has_deoptimization_support());
 }
=======================================
--- /branches/3.15/src/objects.h        Tue Dec 11 09:41:11 2012
+++ /branches/3.15/src/objects.h        Wed Jan 30 05:57:19 2013
@@ -5429,6 +5429,7 @@

   // [code]: Function code.
   DECL_ACCESSORS(code, Code)
+  inline void ReplaceCode(Code* code);

   // [optimized_code_map]: Map from native context to optimized code
   // and a shared literals array or Smi 0 if none.
=======================================
--- /branches/3.15/src/runtime.cc       Mon Jan 28 04:35:19 2013
+++ /branches/3.15/src/runtime.cc       Wed Jan 30 05:57:19 2013
@@ -2146,7 +2146,7 @@
   // target function to undefined.  SetCode is only used for built-in
   // constructors like String, Array, and Object, and some web code
   // doesn't like seeing source code for constructors.
-  target_shared->set_code(source_shared->code());
+  target_shared->ReplaceCode(source_shared->code());
   target_shared->set_scope_info(source_shared->scope_info());
   target_shared->set_length(source_shared->length());
   target_shared->set_formal_parameter_count(
=======================================
--- /branches/3.15/src/version.cc       Mon Jan 28 04:35:19 2013
+++ /branches/3.15/src/version.cc       Wed Jan 30 05:57:19 2013
@@ -35,7 +35,7 @@
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     15
 #define BUILD_NUMBER      11
-#define PATCH_LEVEL       13
+#define PATCH_LEVEL       14
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
 #define IS_CANDIDATE_VERSION 0
=======================================
--- /branches/3.15/test/cctest/test-heap.cc     Mon Dec 10 11:00:50 2012
+++ /branches/3.15/test/cctest/test-heap.cc     Wed Jan 30 05:57:19 2013
@@ -2575,3 +2575,128 @@
   // Unoptimized code is missing and the deoptimizer will go ballistic.
   CompileRun("g('bozo');");
 }
+
+
+TEST(Regress169209) {
+  i::FLAG_allow_natives_syntax = true;
+  i::FLAG_flush_code_incrementally = true;
+  InitializeVM();
+  v8::HandleScope scope;
+
+  // Perform one initial GC to enable code flushing.
+  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+
+  // Prepare a shared function info eligible for code flushing for which
+  // the unoptimized code will be replaced during optimization.
+  Handle<SharedFunctionInfo> shared1;
+  {
+    HandleScope inner_scope;
+    CompileRun("function f() { return 'foobar'; }"
+               "function g(x) { if (x) f(); }"
+               "f();"
+               "g(false);"
+               "g(false);");
+
+    Handle<JSFunction> f =
+        v8::Utils::OpenHandle(
+            *v8::Handle<v8::Function>::Cast(
+                v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
+    CHECK(f->is_compiled());
+    const int kAgingThreshold = 6;
+    for (int i = 0; i < kAgingThreshold; i++) {
+      f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+    }
+
+    shared1 = inner_scope.CloseAndEscape(handle(f->shared(), ISOLATE));
+  }
+
+  // Prepare a shared function info eligible for code flushing that will
+  // represent the dangling tail of the candidate list.
+  Handle<SharedFunctionInfo> shared2;
+  {
+    HandleScope inner_scope;
+    CompileRun("function flushMe() { return 0; }"
+               "flushMe(1);");
+
+    Handle<JSFunction> f =
+        v8::Utils::OpenHandle(
+            *v8::Handle<v8::Function>::Cast(
+ v8::Context::GetCurrent()->Global()->Get(v8_str("flushMe"))));
+    CHECK(f->is_compiled());
+    const int kAgingThreshold = 6;
+    for (int i = 0; i < kAgingThreshold; i++) {
+      f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+    }
+
+    shared2 = inner_scope.CloseAndEscape(handle(f->shared(), ISOLATE));
+  }
+
+  // Simulate incremental marking and collect code flushing candidates.
+  SimulateIncrementalMarking();
+  CHECK(shared1->code()->gc_metadata() != NULL);
+
+  // Optimize function and make sure the unoptimized code is replaced.
+#ifdef DEBUG
+  FLAG_stop_at = "f";
+#endif
+  CompileRun("%OptimizeFunctionOnNextCall(g);"
+             "g(false);");
+
+  // Finish garbage collection cycle.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  CHECK(shared1->code()->gc_metadata() == NULL);
+}
+
+
+TEST(Regress168801) {
+  i::FLAG_always_compact = true;
+  i::FLAG_cache_optimized_code = false;
+  i::FLAG_allow_natives_syntax = true;
+  i::FLAG_flush_code_incrementally = true;
+  InitializeVM();
+  v8::HandleScope scope;
+
+  // Perform one initial GC to enable code flushing.
+  HEAP->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
+
+  // Ensure the code ends up on an evacuation candidate.
+  SimulateFullSpace(HEAP->code_space());
+
+  // Prepare an unoptimized function that is eligible for code flushing.
+  Handle<JSFunction> function;
+  {
+    HandleScope inner_scope;
+    CompileRun("function mkClosure() {"
+               "  return function(x) { return x + 1; };"
+               "}"
+               "var f = mkClosure();"
+               "f(1); f(2);");
+
+    Handle<JSFunction> f =
+        v8::Utils::OpenHandle(
+            *v8::Handle<v8::Function>::Cast(
+                v8::Context::GetCurrent()->Global()->Get(v8_str("f"))));
+    CHECK(f->is_compiled());
+    const int kAgingThreshold = 6;
+    for (int i = 0; i < kAgingThreshold; i++) {
+      f->shared()->code()->MakeOlder(static_cast<MarkingParity>(i % 2));
+    }
+
+    function = inner_scope.CloseAndEscape(handle(*f, ISOLATE));
+  }
+
+ // Simulate incremental marking so that unoptimized function is enqueued as a + // candidate for code flushing. The shared function info however will not be
+  // explicitly enqueued.
+  SimulateIncrementalMarking();
+
+  // Now optimize the function so that it is taken off the candidate list.
+  {
+    HandleScope inner_scope;
+    CompileRun("%OptimizeFunctionOnNextCall(f); f(3);");
+  }
+
+  // This cycle will bust the heap and subsequent cycles will go ballistic.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags);
+}

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to