Revision: 11766
Author:   [email protected]
Date:     Mon Jun 11 09:57:27 2012
Log: Reland r11425 "Re-enable optimization for hot functions that have optimization disabled due to many deopts."

Fix performance regressions introduced in r11425 by
- counting number of function deoptimizations instead of function optimizations,
- packing several counters into one field of shared function info.

BUG=v8:2040,121196
[email protected]

Review URL: https://chromiumcodereview.appspot.com/10534063
http://code.google.com/p/v8/source/detail?r=11766

Modified:
 /branches/bleeding_edge/src/deoptimizer.cc
 /branches/bleeding_edge/src/heap.cc
 /branches/bleeding_edge/src/heap.h
 /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc
 /branches/bleeding_edge/src/objects-inl.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime-profiler.cc

=======================================
--- /branches/bleeding_edge/src/deoptimizer.cc  Mon Jun  4 07:42:58 2012
+++ /branches/bleeding_edge/src/deoptimizer.cc  Mon Jun 11 09:57:27 2012
@@ -378,6 +378,7 @@
            reinterpret_cast<intptr_t>(from),
            fp_to_sp_delta - (2 * kPointerSize));
   }
+  function->shared()->increment_deopt_count();
   // Find the optimized code.
   if (type == EAGER) {
     ASSERT(from == NULL);
=======================================
--- /branches/bleeding_edge/src/heap.cc Sun Jun 10 23:59:56 2012
+++ /branches/bleeding_edge/src/heap.cc Mon Jun 11 09:57:27 2012
@@ -3022,8 +3022,8 @@
   share->set_initial_map(undefined_value(), SKIP_WRITE_BARRIER);
share->set_this_property_assignments(undefined_value(), SKIP_WRITE_BARRIER);
   share->set_ast_node_count(0);
-  share->set_deopt_counter(FLAG_deopt_every_n_times);
-  share->set_ic_age(0);
+  share->set_stress_deopt_counter(FLAG_deopt_every_n_times);
+  share->set_counters(0);

   // Set integer fields (smi or int, depending on the architecture).
   share->set_length(0);
=======================================
--- /branches/bleeding_edge/src/heap.h  Wed May 23 07:24:29 2012
+++ /branches/bleeding_edge/src/heap.h  Mon Jun 11 09:57:27 2012
@@ -1597,7 +1597,7 @@
   }

   void AgeInlineCaches() {
-    ++global_ic_age_;
+ global_ic_age_ = (global_ic_age_ + 1) & SharedFunctionInfo::ICAgeBits::kMax;
   }

  private:
=======================================
--- /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Mon Jun 11 05:42:31 2012 +++ /branches/bleeding_edge/src/ia32/lithium-codegen-ia32.cc Mon Jun 11 09:57:27 2012
@@ -566,19 +566,22 @@
     __ push(eax);
     __ push(ebx);
     __ mov(ebx, shared);
- __ mov(eax, FieldOperand(ebx, SharedFunctionInfo::kDeoptCounterOffset));
+    __ mov(eax,
+ FieldOperand(ebx, SharedFunctionInfo::kStressDeoptCounterOffset));
     __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
     __ j(not_zero, &no_deopt, Label::kNear);
     if (FLAG_trap_on_deopt) __ int3();
     __ mov(eax, Immediate(Smi::FromInt(FLAG_deopt_every_n_times)));
- __ mov(FieldOperand(ebx, SharedFunctionInfo::kDeoptCounterOffset), eax); + __ mov(FieldOperand(ebx, SharedFunctionInfo::kStressDeoptCounterOffset),
+           eax);
     __ pop(ebx);
     __ pop(eax);
     __ popfd();
     __ jmp(entry, RelocInfo::RUNTIME_ENTRY);

     __ bind(&no_deopt);
- __ mov(FieldOperand(ebx, SharedFunctionInfo::kDeoptCounterOffset), eax); + __ mov(FieldOperand(ebx, SharedFunctionInfo::kStressDeoptCounterOffset),
+           eax);
     __ pop(ebx);
     __ pop(eax);
     __ popfd();
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Mon Jun 11 01:41:48 2012
+++ /branches/bleeding_edge/src/objects-inl.h   Mon Jun 11 09:57:27 2012
@@ -3673,7 +3673,7 @@
 ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
 ACCESSORS(SharedFunctionInfo, this_property_assignments, Object,
           kThisPropertyAssignmentsOffset)
-SMI_ACCESSORS(SharedFunctionInfo, ic_age, kICAgeOffset)
+SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)


 BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
@@ -3722,8 +3722,10 @@
 SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
               kThisPropertyAssignmentsCountOffset)
 SMI_ACCESSORS(SharedFunctionInfo, opt_count, kOptCountOffset)
-SMI_ACCESSORS(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset)
-SMI_ACCESSORS(SharedFunctionInfo, deopt_counter, kDeoptCounterOffset)
+SMI_ACCESSORS(SharedFunctionInfo, counters, kCountersOffset)
+SMI_ACCESSORS(SharedFunctionInfo,
+              stress_deopt_counter,
+              kStressDeoptCounterOffset)
 #else

 #define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset)             \
@@ -3775,8 +3777,10 @@
                         kThisPropertyAssignmentsCountOffset)
 PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, opt_count, kOptCountOffset)

-PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, ast_node_count, kAstNodeCountOffset) -PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, deopt_counter, kDeoptCounterOffset)
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, counters, kCountersOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+                        stress_deopt_counter,
+                        kStressDeoptCounterOffset)
 #endif


@@ -3975,12 +3979,64 @@
   int hints = compiler_hints() & ~(kCodeAgeMask << kCodeAgeShift);
   set_compiler_hints(hints | ((code_age & kCodeAgeMask) << kCodeAgeShift));
 }
+
+
+int SharedFunctionInfo::ic_age() {
+  return ICAgeBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_ic_age(int ic_age) {
+  set_counters(ICAgeBits::update(counters(), ic_age));
+}
+
+
+int SharedFunctionInfo::deopt_count() {
+  return DeoptCountBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_deopt_count(int deopt_count) {
+  set_counters(DeoptCountBits::update(counters(), deopt_count));
+}
+
+
+void SharedFunctionInfo::increment_deopt_count() {
+  int value = counters();
+  int deopt_count = DeoptCountBits::decode(value);
+  deopt_count = (deopt_count + 1) & DeoptCountBits::kMax;
+  set_counters(DeoptCountBits::update(value, deopt_count));
+}
+
+
+int SharedFunctionInfo::opt_reenable_tries() {
+  return OptReenableTriesBits::decode(counters());
+}
+
+
+void SharedFunctionInfo::set_opt_reenable_tries(int tries) {
+  set_counters(OptReenableTriesBits::update(counters(), tries));
+}


 bool SharedFunctionInfo::has_deoptimization_support() {
   Code* code = this->code();
return code->kind() == Code::FUNCTION && code->has_deoptimization_support();
 }
+
+
+void SharedFunctionInfo::TryReenableOptimization() {
+  int tries = opt_reenable_tries();
+  set_opt_reenable_tries((tries + 1) & OptReenableTriesBits::kMax);
+  // We reenable optimization whenever the number of tries is a large
+  // enough power of 2.
+  if (tries >= 16 && (((tries - 1) & tries) == 0)) {
+    set_optimization_disabled(false);
+    set_opt_count(0);
+    set_deopt_count(0);
+    code()->set_optimizable(true);
+  }
+}


 bool JSFunction::IsBuiltin() {
=======================================
--- /branches/bleeding_edge/src/objects.cc      Sun Jun 10 23:59:56 2012
+++ /branches/bleeding_edge/src/objects.cc      Mon Jun 11 09:57:27 2012
@@ -7983,6 +7983,7 @@
       code()->set_optimizable(true);
     }
     set_opt_count(0);
+    set_deopt_count(0);
   }
 }

=======================================
--- /branches/bleeding_edge/src/objects.h       Mon Jun 11 05:42:31 2012
+++ /branches/bleeding_edge/src/objects.h       Mon Jun 11 09:57:27 2012
@@ -5412,8 +5412,8 @@

   // A counter used to determine when to stress the deoptimizer with a
   // deopt.
-  inline int deopt_counter();
-  inline void set_deopt_counter(int counter);
+  inline int stress_deopt_counter();
+  inline void set_stress_deopt_counter(int counter);

   inline int profiler_ticks();

@@ -5541,9 +5541,26 @@
   bool HasSourceCode();
   Handle<Object> GetSourceCode();

+  // Number of times the function was optimized.
   inline int opt_count();
   inline void set_opt_count(int opt_count);

+  // Number of times the function was deoptimized.
+  inline void set_deopt_count(int value);
+  inline int deopt_count();
+  inline void increment_deopt_count();
+
+  // Number of time we tried to re-enable optimization after it
+  // was disabled due to high number of deoptimizations.
+  inline void set_opt_reenable_tries(int value);
+  inline int opt_reenable_tries();
+
+  inline void TryReenableOptimization();
+
+  // Stores deopt_count, opt_reenable_tries and ic_age as bit-fields.
+  inline void set_counters(int value);
+  inline int counters();
+
   // Source size of this function.
   int SourceSize();

@@ -5600,13 +5617,14 @@
       kInferredNameOffset + kPointerSize;
   static const int kThisPropertyAssignmentsOffset =
       kInitialMapOffset + kPointerSize;
- // ic_age is a Smi field. It could be grouped with another Smi field into a
-  // PSEUDO_SMI_ACCESSORS pair (on x64), if one becomes available.
- static const int kICAgeOffset = kThisPropertyAssignmentsOffset + kPointerSize; + // ast_node_count is a Smi field. It could be grouped with another Smi field
+  // into a PSEUDO_SMI_ACCESSORS pair (on x64), if one becomes available.
+  static const int kAstNodeCountOffset =
+      kThisPropertyAssignmentsOffset + kPointerSize;
 #if V8_HOST_ARCH_32_BIT
   // Smi fields.
   static const int kLengthOffset =
-      kICAgeOffset + kPointerSize;
+      kAstNodeCountOffset + kPointerSize;
static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize;
   static const int kExpectedNofPropertiesOffset =
       kFormalParameterCountOffset + kPointerSize;
@@ -5624,12 +5642,11 @@
       kCompilerHintsOffset + kPointerSize;
   static const int kOptCountOffset =
       kThisPropertyAssignmentsCountOffset + kPointerSize;
-  static const int kAstNodeCountOffset = kOptCountOffset + kPointerSize;
- static const int kDeoptCounterOffset = kAstNodeCountOffset + kPointerSize;
-
+  static const int kCountersOffset = kOptCountOffset + kPointerSize;
+ static const int kStressDeoptCounterOffset = kCountersOffset + kPointerSize;

   // Total size.
-  static const int kSize = kDeoptCounterOffset + kPointerSize;
+  static const int kSize = kStressDeoptCounterOffset + kPointerSize;
 #else
   // The only reason to use smi fields instead of int fields
   // is to allow iteration without maps decoding during
@@ -5641,7 +5658,7 @@
   // word is not set and thus this word cannot be treated as pointer
   // to HeapObject during old space traversal.
   static const int kLengthOffset =
-      kICAgeOffset + kPointerSize;
+      kAstNodeCountOffset + kPointerSize;
   static const int kFormalParameterCountOffset =
       kLengthOffset + kIntSize;

@@ -5665,11 +5682,11 @@
   static const int kOptCountOffset =
       kThisPropertyAssignmentsCountOffset + kIntSize;

-  static const int kAstNodeCountOffset = kOptCountOffset + kIntSize;
-  static const int kDeoptCounterOffset = kAstNodeCountOffset + kIntSize;
+  static const int kCountersOffset = kOptCountOffset + kIntSize;
+  static const int kStressDeoptCounterOffset = kCountersOffset + kIntSize;

   // Total size.
-  static const int kSize = kDeoptCounterOffset + kIntSize;
+  static const int kSize = kStressDeoptCounterOffset + kIntSize;

 #endif

@@ -5722,6 +5739,10 @@
     kCompilerHintsCount  // Pseudo entry
   };

+  class DeoptCountBits: public BitField<int, 0, 4> {};
+  class OptReenableTriesBits: public BitField<int, 4, 18> {};
+  class ICAgeBits: public BitField<int, 22, 8> {};
+
  private:
 #if V8_HOST_ARCH_32_BIT
   // On 32 bit platforms, compiler hints is a smi.
=======================================
--- /branches/bleeding_edge/src/runtime-profiler.cc     Mon Apr 30 04:54:34 2012
+++ /branches/bleeding_edge/src/runtime-profiler.cc     Mon Jun 11 09:57:27 2012
@@ -65,13 +65,20 @@
 // Number of times a function has to be seen on the stack before it is
 // optimized.
 static const int kProfilerTicksBeforeOptimization = 2;
+// If the function optimization was disabled due to high deoptimization count, +// but the function is hot and has been seen on the stack this number of times,
+// then we try to reenable optimization for this function.
+static const int kProfilerTicksBeforeReenablingOptimization = 250;
 // If a function does not have enough type info (according to
 // FLAG_type_info_threshold), but has seen a huge number of ticks,
 // optimize it as it is.
 static const int kTicksWhenNotEnoughTypeInfo = 100;
 // We only have one byte to store the number of ticks.
+STATIC_ASSERT(kProfilerTicksBeforeOptimization < 256);
+STATIC_ASSERT(kProfilerTicksBeforeReenablingOptimization < 256);
 STATIC_ASSERT(kTicksWhenNotEnoughTypeInfo < 256);

+
 // Maximum size in bytes of generated code for a function to be optimized
 // the very first time it is seen on the stack.
 static const int kMaxSizeEarlyOpt = 500;
@@ -263,7 +270,9 @@
       }
     }

-    Code* shared_code = function->shared()->code();
+    SharedFunctionInfo* shared = function->shared();
+    Code* shared_code = shared->code();
+
     if (shared_code->kind() != Code::FUNCTION) continue;

     if (function->IsMarkedForLazyRecompilation()) {
@@ -272,20 +281,34 @@
       int new_nesting = Min(nesting + 1, Code::kMaxLoopNestingMarker);
       shared_code->set_allow_osr_at_loop_nesting_level(new_nesting);
     }
-
-    // Do not record non-optimizable functions.
-    if (!function->IsOptimizable()) continue;
-    if (function->shared()->optimization_disabled()) continue;

     // Only record top-level code on top of the execution stack and
     // avoid optimizing excessively large scripts since top-level code
     // will be executed only once.
     const int kMaxToplevelSourceSize = 10 * 1024;
-    if (function->shared()->is_toplevel()
-        && (frame_count > 1
- || function->shared()->SourceSize() > kMaxToplevelSourceSize)) {
+    if (shared->is_toplevel() &&
+ (frame_count > 1 || shared->SourceSize() > kMaxToplevelSourceSize)) {
       continue;
     }
+
+    // Do not record non-optimizable functions.
+    if (shared->optimization_disabled()) {
+      if (shared->deopt_count() >= Compiler::kDefaultMaxOptCount) {
+        // If optimization was disabled due to many deoptimizations,
+ // then check if the function is hot and try to reenable optimization.
+        int ticks = shared_code->profiler_ticks();
+        if (ticks >= kProfilerTicksBeforeReenablingOptimization) {
+          shared_code->set_profiler_ticks(0);
+          shared->TryReenableOptimization();
+        } else {
+          shared_code->set_profiler_ticks(ticks + 1);
+        }
+      }
+      continue;
+    }
+    if (!function->IsOptimizable()) continue;
+
+

     if (FLAG_watch_ic_patching) {
       int ticks = shared_code->profiler_ticks();

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to