Introduce an OptimizingCompiler class, responsible for maintaining the state needed...
authorsanjoy@chromium.org <sanjoy@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 17 Jul 2012 16:24:40 +0000 (16:24 +0000)
committersanjoy@chromium.org <sanjoy@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 17 Jul 2012 16:24:40 +0000 (16:24 +0000)
BUG=
TEST=

Review URL: https://chromiumcodereview.appspot.com/10700188

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12112 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/compiler.cc
src/compiler.h
src/hydrogen.h
src/type-info.h

index 22118ee..f93ce6b 100644 (file)
@@ -165,22 +165,27 @@ static bool AlwaysFullCompiler(Isolate* isolate) {
 }
 
 
-static void FinishOptimization(Handle<JSFunction> function, int64_t start) {
+void OptimizingCompiler::RecordOptimizationStats() {
+  Handle<JSFunction> function = info()->closure();
   int opt_count = function->shared()->opt_count();
   function->shared()->set_opt_count(opt_count + 1);
-  double ms = static_cast<double>(OS::Ticks() - start) / 1000;
+  double ms_creategraph =
+      static_cast<double>(time_taken_to_create_graph_) / 1000;
+  double ms_optimize = static_cast<double>(time_taken_to_optimize_) / 1000;
+  double ms_codegen = static_cast<double>(time_taken_to_codegen_) / 1000;
   if (FLAG_trace_opt) {
     PrintF("[optimizing: ");
     function->PrintName();
     PrintF(" / %" V8PRIxPTR, reinterpret_cast<intptr_t>(*function));
-    PrintF(" - took %0.3f ms]\n", ms);
+    PrintF(" - took %0.3f, %0.3f, %0.3f ms]\n", ms_creategraph, ms_optimize,
+           ms_codegen);
   }
   if (FLAG_trace_opt_stats) {
     static double compilation_time = 0.0;
     static int compiled_functions = 0;
     static int code_size = 0;
 
-    compilation_time += ms;
+    compilation_time += (ms_creategraph + ms_optimize + ms_codegen);
     compiled_functions++;
     code_size += function->shared()->SourceSize();
     PrintF("Compiled: %d functions with %d byte source size in %fms.\n",
@@ -191,38 +196,52 @@ static void FinishOptimization(Handle<JSFunction> function, int64_t start) {
 }
 
 
+// A return value of true indicates the compilation pipeline is still
+// going, not necessarily that we optimized the code.
 static bool MakeCrankshaftCode(CompilationInfo* info) {
+  OptimizingCompiler compiler(info);
+  OptimizingCompiler::Status status = compiler.CreateGraph();
+
+  if (status != OptimizingCompiler::SUCCEEDED) {
+    return status != OptimizingCompiler::FAILED;
+  }
+  status = compiler.OptimizeGraph();
+  if (status != OptimizingCompiler::SUCCEEDED) {
+    return status != OptimizingCompiler::FAILED;
+  }
+  status = compiler.GenerateAndInstallCode();
+  return status != OptimizingCompiler::FAILED;
+}
+
+
+OptimizingCompiler::Status OptimizingCompiler::CreateGraph() {
   ASSERT(V8::UseCrankshaft());
-  ASSERT(info->IsOptimizing());
-  ASSERT(!info->IsCompilingForDebugging());
+  ASSERT(info()->IsOptimizing());
+  ASSERT(!info()->IsCompilingForDebugging());
 
-  // We should never arrive here if there is not code object on the
+  // We should never arrive here if there is no code object on the
   // shared function object.
-  Handle<Code> code(info->shared_info()->code());
+  Handle<Code> code(info()->shared_info()->code());
   ASSERT(code->kind() == Code::FUNCTION);
 
   // We should never arrive here if optimization has been disabled on the
   // shared function info.
-  ASSERT(!info->shared_info()->optimization_disabled());
+  ASSERT(!info()->shared_info()->optimization_disabled());
 
   // Fall back to using the full code generator if it's not possible
   // to use the Hydrogen-based optimizing compiler. We already have
   // generated code for this from the shared function object.
-  if (AlwaysFullCompiler(info->isolate())) {
-    info->SetCode(code);
-    return true;
+  if (AlwaysFullCompiler(info()->isolate())) {
+    info()->SetCode(code);
+    return SetLastStatus(BAILED_OUT);
   }
 
   // Limit the number of times we re-compile a functions with
   // the optimizing compiler.
   const int kMaxOptCount =
       FLAG_deopt_every_n_times == 0 ? Compiler::kDefaultMaxOptCount : 1000;
-  if (info->shared_info()->opt_count() > kMaxOptCount) {
-    info->AbortOptimization();
-    info->shared_info()->DisableOptimization();
-    // True indicates the compilation pipeline is still going, not
-    // necessarily that we optimized the code.
-    return true;
+  if (info()->shared_info()->opt_count() > kMaxOptCount) {
+    return AbortOptimization();
   }
 
   // Due to an encoding limit on LUnallocated operands in the Lithium
@@ -234,26 +253,22 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
   // the negative indices and locals the non-negative ones.
   const int parameter_limit = -LUnallocated::kMinFixedIndex;
   const int locals_limit = LUnallocated::kMaxFixedIndex;
-  Scope* scope = info->scope();
+  Scope* scope = info()->scope();
   if ((scope->num_parameters() + 1) > parameter_limit ||
-      (info->osr_ast_id() != AstNode::kNoNumber &&
+      (info()->osr_ast_id() != AstNode::kNoNumber &&
        scope->num_parameters() + 1 + scope->num_stack_slots() > locals_limit)) {
-    info->AbortOptimization();
-    info->shared_info()->DisableOptimization();
-    // True indicates the compilation pipeline is still going, not
-    // necessarily that we optimized the code.
-    return true;
+    return AbortOptimization();
   }
 
   // Take --hydrogen-filter into account.
-  Handle<String> name = info->function()->debug_name();
+  Handle<String> name = info()->function()->debug_name();
   if (*FLAG_hydrogen_filter != '\0') {
     Vector<const char> filter = CStrVector(FLAG_hydrogen_filter);
     if ((filter[0] == '-'
          && name->IsEqualTo(filter.SubVector(1, filter.length())))
         || (filter[0] != '-' && !name->IsEqualTo(filter))) {
-      info->SetCode(code);
-      return true;
+      info()->SetCode(code);
+      return SetLastStatus(BAILED_OUT);
     }
   }
 
@@ -261,20 +276,20 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
   // doesn't have deoptimization support. Alternatively, we may decide to
   // run the full code generator to get a baseline for the compile-time
   // performance of the hydrogen-based compiler.
-  int64_t start = OS::Ticks();
-  bool should_recompile = !info->shared_info()->has_deoptimization_support();
+  Timer t(this, &time_taken_to_create_graph_);
+  bool should_recompile = !info()->shared_info()->has_deoptimization_support();
   if (should_recompile || FLAG_hydrogen_stats) {
     HPhase phase(HPhase::kFullCodeGen);
-    CompilationInfoWithZone unoptimized(info->shared_info());
+    CompilationInfoWithZone unoptimized(info()->shared_info());
     // Note that we use the same AST that we will use for generating the
     // optimized code.
-    unoptimized.SetFunction(info->function());
-    unoptimized.SetScope(info->scope());
+    unoptimized.SetFunction(info()->function());
+    unoptimized.SetScope(info()->scope());
     if (should_recompile) unoptimized.EnableDeoptimizationSupport();
     bool succeeded = FullCodeGenerator::MakeCode(&unoptimized);
     if (should_recompile) {
-      if (!succeeded) return false;
-      Handle<SharedFunctionInfo> shared = info->shared_info();
+      if (!succeeded) return SetLastStatus(FAILED);
+      Handle<SharedFunctionInfo> shared = info()->shared_info();
       shared->EnableDeoptimizationSupport(*unoptimized.code());
       // The existing unoptimized code was replaced with the new one.
       Compiler::RecordFunctionCompilation(
@@ -288,52 +303,70 @@ static bool MakeCrankshaftCode(CompilationInfo* info) {
   // is safe as long as the unoptimized code has deoptimization
   // support.
   ASSERT(FLAG_always_opt || code->optimizable());
-  ASSERT(info->shared_info()->has_deoptimization_support());
+  ASSERT(info()->shared_info()->has_deoptimization_support());
 
   if (FLAG_trace_hydrogen) {
     PrintF("-----------------------------------------------------------\n");
     PrintF("Compiling method %s using hydrogen\n", *name->ToCString());
-    HTracer::Instance()->TraceCompilation(info->function());
+    HTracer::Instance()->TraceCompilation(info()->function());
   }
-
-  Handle<Context> global_context(info->closure()->context()->global_context());
-  TypeFeedbackOracle oracle(code, global_context, info->isolate(),
-                            info->zone());
-  HGraphBuilder builder(info, &oracle);
+  Handle<Context> global_context(
+      info()->closure()->context()->global_context());
+  oracle_ = new(info()->zone()) TypeFeedbackOracle(
+      code, global_context, info()->isolate(), info()->zone());
+  graph_builder_ = new(info()->zone()) HGraphBuilder(info(), oracle_);
   HPhase phase(HPhase::kTotal);
-  HGraph* graph = builder.CreateGraph();
-  if (info->isolate()->has_pending_exception()) {
-    info->SetCode(Handle<Code>::null());
-    return false;
+  graph_ = graph_builder_->CreateGraph();
+
+  if (info()->isolate()->has_pending_exception()) {
+    info()->SetCode(Handle<Code>::null());
+    return SetLastStatus(FAILED);
   }
 
-  if (graph != NULL) {
-    SmartArrayPointer<char> bailout_reason;
-    if (!graph->Optimize(&bailout_reason)) {
-      if (!bailout_reason.is_empty()) builder.Bailout(*bailout_reason);
+  // The function being compiled may have bailed out due to an inline
+  // candidate bailing out.  In such a case, we don't disable
+  // optimization on the shared_info.
+  ASSERT(!graph_builder_->inline_bailout() || graph_ == NULL);
+  if (graph_ == NULL) {
+    if (graph_builder_->inline_bailout()) {
+      info_->AbortOptimization();
+      return SetLastStatus(BAILED_OUT);
     } else {
-      LChunk* chunk = LChunk::NewChunk(graph);
-      if (chunk != NULL) {
-        Handle<Code> optimized_code = chunk->Codegen();
-        if (!optimized_code.is_null()) {
-          info->SetCode(optimized_code);
-          FinishOptimization(info->closure(), start);
-          return true;
-        }
-      }
+      return AbortOptimization();
     }
   }
 
-  // Keep using the shared code.
-  info->AbortOptimization();
-  if (!builder.inline_bailout()) {
-    // Mark the shared code as unoptimizable unless it was an inlined
-    // function that bailed out.
-    info->shared_info()->DisableOptimization();
+  return SetLastStatus(SUCCEEDED);
+}
+
+OptimizingCompiler::Status OptimizingCompiler::OptimizeGraph() {
+  ASSERT(last_status() == SUCCEEDED);
+  Timer t(this, &time_taken_to_optimize_);
+  ASSERT(graph_ != NULL);
+  SmartArrayPointer<char> bailout_reason;
+  if (!graph_->Optimize(&bailout_reason)) {
+    if (!bailout_reason.is_empty()) graph_builder_->Bailout(*bailout_reason);
+    return AbortOptimization();
+  } else {
+    chunk_ = LChunk::NewChunk(graph_);
+    if (chunk_ == NULL) {
+      return AbortOptimization();
+    }
   }
-  // True indicates the compilation pipeline is still going, not necessarily
-  // that we optimized the code.
-  return true;
+  return SetLastStatus(SUCCEEDED);
+}
+
+
+OptimizingCompiler::Status OptimizingCompiler::GenerateAndInstallCode() {
+  ASSERT(last_status() == SUCCEEDED);
+  Timer timer(this, &time_taken_to_codegen_);
+  ASSERT(chunk_ != NULL);
+  ASSERT(graph_ != NULL);
+  Handle<Code> optimized_code = chunk_->Codegen();
+  if (optimized_code.is_null()) return AbortOptimization();
+  info()->SetCode(optimized_code);
+  RecordOptimizationStats();
+  return SetLastStatus(SUCCEEDED);
 }
 
 
index 1359048..dfc11bd 100644 (file)
@@ -312,6 +312,79 @@ class CompilationHandleScope BASE_EMBEDDED {
 };
 
 
+class HGraph;
+class HGraphBuilder;
+class LChunk;
+
+// A helper class that calls the three compilation phases in
+// Crankshaft and keeps track of its state.  The three phases
+// CreateGraph, OptimizeGraph and GenerateAndInstallCode can either
+// fail, bail-out to the full code generator or succeed.  Apart from
+// their return value, the status of the phase last run can be checked
+// using last_status().
+class OptimizingCompiler: public ZoneObject {
+ public:
+  explicit OptimizingCompiler(CompilationInfo* info)
+      : info_(info),
+        oracle_(NULL),
+        graph_builder_(NULL),
+        graph_(NULL),
+        chunk_(NULL),
+        time_taken_to_create_graph_(0),
+        time_taken_to_optimize_(0),
+        time_taken_to_codegen_(0),
+        last_status_(FAILED) { }
+
+  enum Status {
+    FAILED, BAILED_OUT, SUCCEEDED
+  };
+
+  MUST_USE_RESULT Status CreateGraph();
+  MUST_USE_RESULT Status OptimizeGraph();
+  MUST_USE_RESULT Status GenerateAndInstallCode();
+
+  Status last_status() const { return last_status_; }
+  CompilationInfo* info() const { return info_; }
+
+ private:
+  CompilationInfo* info_;
+  TypeFeedbackOracle* oracle_;
+  HGraphBuilder* graph_builder_;
+  HGraph* graph_;
+  LChunk* chunk_;
+  int64_t time_taken_to_create_graph_;
+  int64_t time_taken_to_optimize_;
+  int64_t time_taken_to_codegen_;
+  Status last_status_;
+
+  MUST_USE_RESULT Status SetLastStatus(Status status) {
+    last_status_ = status;
+    return last_status_;
+  }
+  void RecordOptimizationStats();
+  MUST_USE_RESULT Status AbortOptimization() {
+    info_->AbortOptimization();
+    info_->shared_info()->DisableOptimization();
+    return SetLastStatus(BAILED_OUT);
+  }
+
+  struct Timer {
+    Timer(OptimizingCompiler* compiler, int64_t* location)
+        : compiler_(compiler),
+          start_(OS::Ticks()),
+          location_(location) { }
+
+    ~Timer() {
+      *location_ += (OS::Ticks() - start_);
+    }
+
+    OptimizingCompiler* compiler_;
+    int64_t start_;
+    int64_t* location_;
+  };
+};
+
+
 // The V8 compiler
 //
 // General strategy: Source code is translated into an anonymous function w/o
index 60a4420..9b1ad6f 100644 (file)
@@ -871,6 +871,11 @@ class HGraphBuilder: public AstVisitor {
 
   void VisitDeclarations(ZoneList<Declaration*>* declarations);
 
+  void* operator new(size_t size, Zone* zone) {
+    return zone->New(size);
+  }
+  void operator delete(void* ptr) { }
+
  private:
   // Type of a member function that generates inline code for a native function.
   typedef void (HGraphBuilder::*InlineFunctionGenerator)(CallRuntime* call);
index 74910cd..fbd4d2a 100644 (file)
@@ -232,7 +232,7 @@ class UnaryOperation;
 class ForInStatement;
 
 
-class TypeFeedbackOracle BASE_EMBEDDED {
+class TypeFeedbackOracle: public ZoneObject {
  public:
   TypeFeedbackOracle(Handle<Code> code,
                      Handle<Context> global_context,