Track loop nesting across function calls when the function
authorkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 17 Nov 2008 05:50:52 +0000 (05:50 +0000)
committerkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 17 Nov 2008 05:50:52 +0000 (05:50 +0000)
is called through an IC the first time.
Review URL: http://codereview.chromium.org/10746

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

17 files changed:
src/ast.h
src/codegen-arm.h
src/codegen-ia32.cc
src/codegen-ia32.h
src/codegen.cc
src/codegen.h
src/compiler.cc
src/compiler.h
src/debug.cc
src/globals.h
src/handles.cc
src/handles.h
src/ic.cc
src/objects.cc
src/runtime.cc
src/stub-cache.cc
src/stub-cache.h

index 4344535..b383f76 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1125,6 +1125,7 @@ class FunctionLiteral: public Expression {
         start_position_(start_position),
         end_position_(end_position),
         is_expression_(is_expression),
+        loop_nesting_(0),
         function_token_position_(RelocInfo::kNoPosition) {
   }
 
@@ -1149,6 +1150,9 @@ class FunctionLiteral: public Expression {
 
   bool AllowsLazyCompilation();
 
+  bool loop_nesting() const { return loop_nesting_; }
+  void set_loop_nesting(int nesting) { loop_nesting_ = nesting; }
+
  private:
   Handle<String> name_;
   Scope* scope_;
@@ -1160,6 +1164,7 @@ class FunctionLiteral: public Expression {
   int start_position_;
   int end_position_;
   bool is_expression_;
+  int loop_nesting_;
   int function_token_position_;
 };
 
index 50f1906..2b889cd 100644 (file)
@@ -296,6 +296,7 @@ class CodeGenerator: public Visitor {
   void ProcessDeclarations(ZoneList<Declaration*>* declarations);
 
   Handle<Code> ComputeCallInitialize(int argc);
+  Handle<Code> ComputeCallInitializeInLoop(int argc);
 
   // Declare global variables and functions in the given array of
   // name/value pairs.
index d85c8a8..230dc17 100644 (file)
@@ -197,6 +197,10 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
   VirtualFrame virtual_frame(this);
   frame_ = &virtual_frame;
   cc_reg_ = no_condition;
+
+  // Adjust for function-level loop nesting.
+  loop_nesting_ += fun->loop_nesting();
+
   {
     CodeGenState state(this);
 
@@ -383,11 +387,15 @@ void CodeGenerator::GenCode(FunctionLiteral* fun) {
     }
   }
 
+  // Adjust for function-level loop nesting.
+  loop_nesting_ -= fun->loop_nesting();
+
   // Code generation state must be reset.
   scope_ = NULL;
   frame_ = NULL;
   ASSERT(!has_cc());
   ASSERT(state_ == NULL);
+  ASSERT(loop_nesting() == 0);
 }
 
 
@@ -2694,14 +2702,15 @@ void CodeGenerator::VisitCall(Call* node) {
     // patch the stack to use the global proxy as 'this' in the
     // invoked function.
     LoadGlobal();
-
     // Load the arguments.
     for (int i = 0; i < args->length(); i++) {
       Load(args->at(i));
     }
 
     // Setup the receiver register and call the IC initialization code.
-    Handle<Code> stub = ComputeCallInitialize(args->length());
+    Handle<Code> stub = (loop_nesting() > 0)
+        ? ComputeCallInitializeInLoop(args->length())
+        : ComputeCallInitialize(args->length());
     __ RecordPosition(node->position());
     __ call(stub, RelocInfo::CODE_TARGET_CONTEXT);
     __ mov(esi, frame_->Context());
@@ -2745,7 +2754,9 @@ void CodeGenerator::VisitCall(Call* node) {
       for (int i = 0; i < args->length(); i++) Load(args->at(i));
 
       // Call the IC initialization code.
-      Handle<Code> stub = ComputeCallInitialize(args->length());
+      Handle<Code> stub = (loop_nesting() > 0)
+        ? ComputeCallInitializeInLoop(args->length())
+        : ComputeCallInitialize(args->length());
       __ RecordPosition(node->position());
       __ call(stub, RelocInfo::CODE_TARGET);
       __ mov(esi, frame_->Context());
index cb1c185..ee6df47 100644 (file)
@@ -320,6 +320,7 @@ class CodeGenerator: public Visitor {
   void ProcessDeclarations(ZoneList<Declaration*>* declarations);
 
   Handle<Code> ComputeCallInitialize(int argc);
+  Handle<Code> ComputeCallInitializeInLoop(int argc);
 
   // Declare global variables and functions in the given array of
   // name/value pairs.
index 70acf65..14c20dc 100644 (file)
@@ -242,6 +242,15 @@ Handle<Code> CodeGenerator::ComputeCallInitialize(int argc) {
 }
 
 
+Handle<Code> CodeGenerator::ComputeCallInitializeInLoop(int argc) {
+  // Force the creation of the corresponding stub outside loops,
+  // because it will be used when clearing the ICs later - when we
+  // don't know if we're inside a loop or not.
+  ComputeCallInitialize(argc);
+  CALL_HEAP_FUNCTION(StubCache::ComputeCallInitializeInLoop(argc), Code);
+}
+
+
 void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) {
   int length = declarations->length();
   int globals = 0;
index ca11c34..6b543eb 100644 (file)
@@ -48,6 +48,7 @@
 // CodeGenerator::GenCode
 // CodeGenerator::BuildBoilerplate
 // CodeGenerator::ComputeCallInitialize
+// CodeGenerator::ComputeCallInitializeInLoop
 // CodeGenerator::ProcessDeclarations
 // CodeGenerator::DeclareGlobals
 // CodeGenerator::CheckForInlineRuntimeCall
index 2078985..eb76d38 100644 (file)
@@ -239,7 +239,8 @@ Handle<JSFunction> Compiler::CompileEval(Handle<String> source,
 }
 
 
-bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared) {
+bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared,
+                           int loop_nesting) {
   ZoneScope zone_scope(DELETE_ON_EXIT);
 
   // The VM is in the COMPILER state until exiting this function.
@@ -271,6 +272,9 @@ bool Compiler::CompileLazy(Handle<SharedFunctionInfo> shared) {
     return false;
   }
 
+  // Update the loop nesting in the function literal.
+  lit->set_loop_nesting(loop_nesting);
+
   // Measure how long it takes to do the lazy compilation; only take
   // the rest of the function into account to avoid overlap with the
   // lazy parsing statistics.
index 3bbc9aa..0f4143c 100644 (file)
@@ -64,7 +64,7 @@ class Compiler : public AllStatic {
   // Compile from function info (used for lazy compilation). Returns
   // true on success and false if the compilation resulted in a stack
   // overflow.
-  static bool CompileLazy(Handle<SharedFunctionInfo> shared);
+  static bool CompileLazy(Handle<SharedFunctionInfo> shared, int loop_nesting);
 };
 
 } }  // namespace v8::internal
index c59af39..7acc117 100644 (file)
@@ -1135,7 +1135,7 @@ void Debug::ClearStepNext() {
 
 bool Debug::EnsureCompiled(Handle<SharedFunctionInfo> shared) {
   if (shared->is_compiled()) return true;
-  return CompileLazyShared(shared, CLEAR_EXCEPTION);
+  return CompileLazyShared(shared, CLEAR_EXCEPTION, 0);
 }
 
 
index 57ec295..387ed88 100644 (file)
@@ -282,6 +282,8 @@ typedef void (*InlineCacheCallback)(Code* code, Address ic);
 enum InlineCacheState {
   // Has never been executed.
   UNINITIALIZED,
+  // Has never been executed, but is in a loop.
+  UNINITIALIZED_IN_LOOP,
   // Has been executed but monomorhic state has been delayed.
   PREMONOMORPHIC,
   // Has been executed and only one receiver type has been seen.
index ada10f1..a33cbfc 100644 (file)
@@ -422,10 +422,11 @@ Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object) {
 
 
 bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
-                       ClearExceptionFlag flag) {
+                       ClearExceptionFlag flag,
+                       int loop_nesting) {
   // Compile the source information to a code object.
   ASSERT(!shared->is_compiled());
-  bool result = Compiler::CompileLazy(shared);
+  bool result = Compiler::CompileLazy(shared, loop_nesting);
   ASSERT(result != Top::has_pending_exception());
   if (!result && flag == CLEAR_EXCEPTION) Top::clear_pending_exception();
   return result;
@@ -435,10 +436,16 @@ bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
 bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag) {
   // Compile the source information to a code object.
   Handle<SharedFunctionInfo> shared(function->shared());
-  return CompileLazyShared(shared, flag);
+  return CompileLazyShared(shared, flag, 0);
 }
 
 
+bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag) {
+  // Compile the source information to a code object.
+  Handle<SharedFunctionInfo> shared(function->shared());
+  return CompileLazyShared(shared, flag, 1);
+}
+
 OptimizedObjectForAddingMultipleProperties::
 OptimizedObjectForAddingMultipleProperties(Handle<JSObject> object,
                                            bool condition) {
index a9adf3d..6d75c77 100644 (file)
@@ -194,9 +194,13 @@ Handle<Object> SetPrototype(Handle<JSFunction> function,
 // Do lazy compilation of the given function. Returns true on success
 // and false if the compilation resulted in a stack overflow.
 enum ClearExceptionFlag { KEEP_EXCEPTION, CLEAR_EXCEPTION };
+
 bool CompileLazyShared(Handle<SharedFunctionInfo> shared,
-                       ClearExceptionFlag flag);
+                       ClearExceptionFlag flag,
+                       int loop_nesting);
+
 bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag);
+bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag);
 
 // These deal with lazily loaded properties.
 void SetupLazy(Handle<JSFunction> fun,
index 2ec7b09..c0b7624 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -41,6 +41,7 @@ namespace v8 { namespace internal {
 static char TransitionMarkFromState(IC::State state) {
   switch (state) {
     case UNINITIALIZED: return '0';
+    case UNINITIALIZED_IN_LOOP: return 'L';
     case PREMONOMORPHIC: return '0';
     case MONOMORPHIC: return '1';
     case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
@@ -223,7 +224,8 @@ void IC::Clear(Address address) {
 
 
 void CallIC::Clear(Address address, Code* target) {
-  if (target->ic_state() == UNINITIALIZED) return;
+  State state = target->ic_state();
+  if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return;
   Code* code = StubCache::FindCallInitialize(target->arguments_count());
   SetTargetAtAddress(address, code);
 }
@@ -434,8 +436,9 @@ void CallIC::UpdateCaches(LookupResult* lookup,
   if (code->IsFailure()) return;
 
   // Patch the call site depending on the state of the cache.
-  if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
-      state == MONOMORPHIC || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
+  if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP ||
+      state == PREMONOMORPHIC || state == MONOMORPHIC ||
+      state == MONOMORPHIC_PROTOTYPE_FAILURE) {
     set_target(Code::cast(code));
   }
 
@@ -1044,7 +1047,17 @@ Object* CallIC_Miss(Arguments args) {
   ASSERT(args.length() == 2);
   CallIC ic;
   IC::State state = IC::StateFrom(ic.target(), args[0]);
-  return ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
+  Object* result =
+      ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
+  if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction())
+    return result;
+
+  // Compile the function with the knowledge that it's called from
+  // within a loop. This enables further optimization of the function.
+  HandleScope scope;
+  Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
+  if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION);
+  return *function;
 }
 
 
index 23bd4c0..511f9f7 100644 (file)
@@ -4603,6 +4603,7 @@ const char* Code::Kind2String(Kind kind) {
 const char* Code::ICState2String(InlineCacheState state) {
   switch (state) {
     case UNINITIALIZED: return "UNINITIALIZED";
+    case UNINITIALIZED_IN_LOOP: return "UNINITIALIZED_IN_LOOP";
     case PREMONOMORPHIC: return "PREMONOMORPHIC";
     case MONOMORPHIC: return "MONOMORPHIC";
     case MONOMORPHIC_PROTOTYPE_FAILURE: return "MONOMORPHIC_PROTOTYPE_FAILURE";
index 132ccb4..1d40e4c 100644 (file)
@@ -5088,7 +5088,7 @@ static Object* FindSharedFunctionInfoInScript(Handle<Script> script,
     if (!done) {
       // If the candidate is not compiled compile it to reveal any inner
       // functions which might contain the requested source position.
-      CompileLazyShared(target, KEEP_EXCEPTION);
+      CompileLazyShared(target, KEEP_EXCEPTION, 0);
     }
   }
 
index 2904889..3ee1b9e 100644 (file)
@@ -543,6 +543,17 @@ Object* StubCache::ComputeCallInitialize(int argc) {
 }
 
 
+Object* StubCache::ComputeCallInitializeInLoop(int argc) {
+  Code::Flags flags =
+      Code::ComputeFlags(Code::CALL_IC, UNINITIALIZED_IN_LOOP, NORMAL, argc);
+  Object* probe = ProbeCache(flags);
+  if (!probe->IsUndefined()) return probe;
+  StubCompiler compiler;
+  return FillCache(compiler.CompileCallInitialize(flags));
+}
+
+
+
 Object* StubCache::ComputeCallPreMonomorphic(int argc) {
   Code::Flags flags =
       Code::ComputeFlags(Code::CALL_IC, PREMONOMORPHIC, NORMAL, argc);
index 5d67afe..66d03fe 100644 (file)
@@ -148,6 +148,7 @@ class StubCache : public AllStatic {
   // ---
 
   static Object* ComputeCallInitialize(int argc);
+  static Object* ComputeCallInitializeInLoop(int argc);
   static Object* ComputeCallPreMonomorphic(int argc);
   static Object* ComputeCallNormal(int argc);
   static Object* ComputeCallMegamorphic(int argc);