Revision: 3533
Author: [email protected]
Date: Tue Jan  5 01:38:02 2010
Log: Make the ResolvePossiblyDirectEval faster by avoiding the
stack traversal code.
Review URL: http://codereview.chromium.org/523051
http://code.google.com/p/v8/source/detail?r=3533

Modified:
  /branches/bleeding_edge/src/arm/codegen-arm.cc
  /branches/bleeding_edge/src/bootstrapper.cc
  /branches/bleeding_edge/src/contexts.h
  /branches/bleeding_edge/src/ia32/codegen-ia32.cc
  /branches/bleeding_edge/src/runtime.cc
  /branches/bleeding_edge/src/runtime.h
  /branches/bleeding_edge/src/scopes.cc
  /branches/bleeding_edge/src/x64/codegen-x64.cc
  /branches/bleeding_edge/test/mjsunit/eval.js

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Dec 22 04:41:45 2009
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Tue Jan  5 01:38:02 2010
@@ -2891,14 +2891,16 @@
      } else {
        frame_->EmitPush(r2);
      }
+
+    // Push the receiver.
+    __ ldr(r1, frame_->Receiver());
+    frame_->EmitPush(r1);

      // Resolve the call.
-    frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
+    frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);

      // Touch up stack with the right values for the function and the  
receiver.
-    __ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize));
-    __ str(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
-    __ ldr(r1, FieldMemOperand(r0, FixedArray::kHeaderSize +  
kPointerSize));
+    __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
      __ str(r1, MemOperand(sp, arg_count * kPointerSize));

      // Call the function.
=======================================
--- /branches/bleeding_edge/src/bootstrapper.cc Fri Dec 11 02:40:01 2009
+++ /branches/bleeding_edge/src/bootstrapper.cc Tue Jan  5 01:38:02 2010
@@ -992,6 +992,7 @@
    INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun);
    INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun);
    INSTALL_NATIVE(JSFunction, "ToBoolean", to_boolean_fun);
+  INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun);
    INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun);
    INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance",
                   configure_instance_fun);
=======================================
--- /branches/bleeding_edge/src/contexts.h      Mon May 25 03:05:56 2009
+++ /branches/bleeding_edge/src/contexts.h      Tue Jan  5 01:38:02 2010
@@ -77,6 +77,7 @@
    V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
    V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
    V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \
+  V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
    V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
    V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
    V(FUNCTION_MAP_INDEX, Map, function_map) \
@@ -202,6 +203,7 @@
      TO_UINT32_FUN_INDEX,
      TO_INT32_FUN_INDEX,
      TO_BOOLEAN_FUN_INDEX,
+    GLOBAL_EVAL_FUN_INDEX,
      INSTANTIATE_FUN_INDEX,
      CONFIGURE_INSTANCE_FUN_INDEX,
      SPECIAL_FUNCTION_TABLE_INDEX,
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Tue Dec 22 05:10:24  
2009
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Tue Jan  5 01:38:02  
2010
@@ -4579,23 +4579,20 @@
      } else {
        frame_->Push(Factory::undefined_value());
      }
+
+    // Push the receiver.
+    frame_->PushParameterAt(-1);

      // Resolve the call.
      Result result =
-        frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
-
-    // Touch up the stack with the right values for the function and the
-    // receiver.  Use a scratch register to avoid destroying the result.
-    Result scratch = allocator_->Allocate();
-    ASSERT(scratch.is_valid());
-    __ mov(scratch.reg(), FieldOperand(result.reg(),  
FixedArray::kHeaderSize));
-    frame_->SetElementAt(arg_count + 1, &scratch);
-
-    // We can reuse the result register now.
-    frame_->Spill(result.reg());
-    __ mov(result.reg(),
-           FieldOperand(result.reg(), FixedArray::kHeaderSize +  
kPointerSize));
-    frame_->SetElementAt(arg_count, &result);
+        frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+
+    // The runtime call returns a pair of values in eax (function) and
+    // edx (receiver). Touch up the stack with the right values.
+    Result receiver = allocator_->Allocate(edx);
+    frame_->SetElementAt(arg_count + 1, &result);
+    frame_->SetElementAt(arg_count, &receiver);
+    receiver.Unuse();

      // Call the function.
      CodeForSourcePosition(node->position());
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Dec 28 05:01:03 2009
+++ /branches/bleeding_edge/src/runtime.cc      Tue Jan  5 01:38:02 2010
@@ -5233,51 +5233,31 @@
  }


-static Handle<JSFunction> GetBuiltinFunction(String* name) {
-  LookupResult result;
-  Top::global_context()->builtins()->LocalLookup(name, &result);
-  return Handle<JSFunction>(JSFunction::cast(result.GetValue()));
-}
-
-
-static Object* CompileDirectEval(Handle<String> source) {
-  // Compute the eval context.
+static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
+  ASSERT(args.length() == 3);
+  if (!args[0]->IsJSFunction()) {
+    return MakePair(Top::ThrowIllegalOperation(), NULL);
+  }
+
    HandleScope scope;
+  Handle<JSFunction> callee = args.at<JSFunction>(0);
+  Handle<Object> receiver;  // Will be overwritten.
+
+  // Compute the calling context.
+  Handle<Context> context = Handle<Context>(Top::context());
+#ifdef DEBUG
+  // Make sure Top::context() agrees with the old code that traversed
+  // the stack frames to compute the context.
    StackFrameLocator locator;
    JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
-  Handle<Context> context(Context::cast(frame->context()));
-  bool is_global = context->IsGlobalContext();
-
-  // Compile source string in the current context.
-  Handle<JSFunction> boilerplate = Compiler::CompileEval(
-      source,
-      context,
-      is_global,
-      Compiler::DONT_VALIDATE_JSON);
-  if (boilerplate.is_null()) return Failure::Exception();
-  Handle<JSFunction> fun =
-      Factory::NewFunctionFromBoilerplate(boilerplate, context,  
NOT_TENURED);
-  return *fun;
-}
-
-
-static Object* Runtime_ResolvePossiblyDirectEval(Arguments args) {
-  ASSERT(args.length() == 2);
-
-  HandleScope scope;
-
-  CONVERT_ARG_CHECKED(JSFunction, callee, 0);
-
-  Handle<Object> receiver;
+  ASSERT(Context::cast(frame->context()) == *context);
+#endif

    // Find where the 'eval' symbol is bound. It is unaliased only if
    // it is bound in the global context.
-  StackFrameLocator locator;
-  JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
-  Handle<Context> context(Context::cast(frame->context()));
-  int index;
-  PropertyAttributes attributes;
-  while (!context.is_null()) {
+  int index = -1;
+  PropertyAttributes attributes = ABSENT;
+  while (true) {
      receiver = context->Lookup(Factory::eval_symbol(),  
FOLLOW_PROTOTYPE_CHAIN,
                                 &index, &attributes);
      // Stop search when eval is found or when the global context is
@@ -5296,46 +5276,42 @@
      Handle<Object> name = Factory::eval_symbol();
      Handle<Object> reference_error =
          Factory::NewReferenceError("not_defined", HandleVector(&name, 1));
-    return Top::Throw(*reference_error);
+    return MakePair(Top::Throw(*reference_error), NULL);
    }

-  if (context->IsGlobalContext()) {
-    // 'eval' is bound in the global context, but it may have been  
overwritten.
-    // Compare it to the builtin 'GlobalEval' function to make sure.
-    Handle<JSFunction> global_eval =
-      GetBuiltinFunction(Heap::global_eval_symbol());
-    if (global_eval.is_identical_to(callee)) {
-      // A direct eval call.
-      if (args[1]->IsString()) {
-        CONVERT_ARG_CHECKED(String, source, 1);
-        // A normal eval call on a string. Compile it and return the
-        // compiled function bound in the local context.
-        Object* compiled_source = CompileDirectEval(source);
-        if (compiled_source->IsFailure()) return compiled_source;
-        receiver = Handle<Object>(frame->receiver());
-        callee = Handle<JSFunction>(JSFunction::cast(compiled_source));
-      } else {
-        // An eval call that is not called on a string. Global eval
-        // deals better with this.
-        receiver = Handle<Object>(Top::global_context()->global());
-      }
-    } else {
-      // 'eval' is overwritten. Just call the function with the given  
arguments.
-      receiver = Handle<Object>(Top::global_context()->global());
-    }
-  } else {
+  if (!context->IsGlobalContext()) {
      // 'eval' is not bound in the global context. Just call the function
      // with the given arguments. This is not necessarily the global eval.
      if (receiver->IsContext()) {
        context = Handle<Context>::cast(receiver);
        receiver = Handle<Object>(context->get(index));
-    }
+    } else if (receiver->IsJSContextExtensionObject()) {
+      receiver =  
Handle<JSObject>(Top::context()->global()->global_receiver());
+    }
+    return MakePair(*callee, *receiver);
    }

-  Handle<FixedArray> call = Factory::NewFixedArray(2);
-  call->set(0, *callee);
-  call->set(1, *receiver);
-  return *call;
+  // 'eval' is bound in the global context, but it may have been  
overwritten.
+  // Compare it to the builtin 'GlobalEval' function to make sure.
+  if (*callee != Top::global_context()->global_eval_fun() ||
+      !args[1]->IsString()) {
+    return MakePair(*callee, Top::context()->global()->global_receiver());
+  }
+
+  // Deal with a normal eval call with a string argument. Compile it
+  // and return the compiled function bound in the local context.
+  Handle<String> source = args.at<String>(1);
+  Handle<JSFunction> boilerplate = Compiler::CompileEval(
+      source,
+      Handle<Context>(Top::context()),
+      Top::context()->IsGlobalContext(),
+      Compiler::DONT_VALIDATE_JSON);
+  if (boilerplate.is_null()) return MakePair(Failure::Exception(), NULL);
+  callee = Factory::NewFunctionFromBoilerplate(
+      boilerplate,
+      Handle<Context>(Top::context()),
+      NOT_TENURED);
+  return MakePair(*callee, args[2]);
  }


=======================================
--- /branches/bleeding_edge/src/runtime.h       Wed Dec  9 05:06:08 2009
+++ /branches/bleeding_edge/src/runtime.h       Tue Jan  5 01:38:02 2010
@@ -202,7 +202,7 @@
    \
    /* Eval */ \
    F(GlobalReceiver, 1, 1) \
-  F(ResolvePossiblyDirectEval, 2, 1) \
+  F(ResolvePossiblyDirectEval, 3, 1) \
    \
    F(SetProperty, -1 /* 3 or 4 */, 1) \
    F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */, 1) \
=======================================
--- /branches/bleeding_edge/src/scopes.cc       Tue Dec  8 01:43:51 2009
+++ /branches/bleeding_edge/src/scopes.cc       Tue Jan  5 01:38:02 2010
@@ -236,7 +236,7 @@

  Variable* Scope::DeclareGlobal(Handle<String> name) {
    ASSERT(is_global_scope());
-  return variables_.Declare(this, name, Variable::DYNAMIC, true,
+  return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, true,
                              Variable::NORMAL);
  }

=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Tue Dec 22 04:41:45 2009
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Tue Jan  5 01:38:02 2010
@@ -2670,24 +2670,20 @@
      } else {
        frame_->Push(Factory::undefined_value());
      }
+
+    // Push the receiver.
+    frame_->PushParameterAt(-1);

      // Resolve the call.
      Result result =
-        frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 2);
-
-    // Touch up the stack with the right values for the function and the
-    // receiver.  Use a scratch register to avoid destroying the result.
-    Result scratch = allocator_->Allocate();
-    ASSERT(scratch.is_valid());
-    __ movq(scratch.reg(),
-            FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(0)));
-    frame_->SetElementAt(arg_count + 1, &scratch);
-
-    // We can reuse the result register now.
-    frame_->Spill(result.reg());
-    __ movq(result.reg(),
-            FieldOperand(result.reg(), FixedArray::OffsetOfElementAt(1)));
-    frame_->SetElementAt(arg_count, &result);
+        frame_->CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+
+    // The runtime call returns a pair of values in rax (function) and
+    // rdx (receiver). Touch up the stack with the right values.
+    Result receiver = allocator_->Allocate(rdx);
+    frame_->SetElementAt(arg_count + 1, &result);
+    frame_->SetElementAt(arg_count, &receiver);
+    receiver.Unuse();

      // Call the function.
      CodeForSourcePosition(node->position());
=======================================
--- /branches/bleeding_edge/test/mjsunit/eval.js        Thu Nov 27 05:55:06 2008
+++ /branches/bleeding_edge/test/mjsunit/eval.js        Tue Jan  5 01:38:02 2010
@@ -58,16 +58,16 @@

  // Test that un-aliased eval reads from local context.
  foo = 0;
-result =
+result =
    (function() {
      var foo = 2;
      return eval('foo');
    })();
  assertEquals(2, result);

-//Test that un-aliased eval writes to local context.
+// Test that un-aliased eval writes to local context.
  foo = 0;
-result =
+result =
    (function() {
      var foo = 1;
      eval('foo = 2');
@@ -84,7 +84,7 @@
  // Test that aliased eval reads from global context.
  var e = eval;
  foo = 0;
-result =
+result =
    (function() {
      var foo = 2;
      return e('foo');
@@ -105,7 +105,7 @@
  // Try to cheat the 'aliased eval' detection.
  var x = this;
  foo = 0;
-result =
+result =
    (function() {
      var foo = 2;
      return x.eval('foo');
@@ -113,7 +113,7 @@
  assertEquals(0, result);

  foo = 0;
-result =
+result =
    (function() {
      var eval = function(x) { return x; };
      var foo = eval(2);
@@ -128,8 +128,29 @@
    })();
  assertEquals(4, result);

+result =
+  (function() {
+    eval("var eval = function(s) { return this; }");
+    return eval("42");  // Should return the global object
+  })();
+assertEquals(this, result);
+
+result =
+  (function() {
+    var obj = { f: function(eval) { return eval("this"); } };
+    return obj.f(eval);
+  })();
+assertEquals(this, result);
+
+result =
+  (function() {
+    var obj = { f: function(eval) { arguments; return eval("this"); } };
+    return obj.f(eval);
+  })();
+assertEquals(this, result);
+
  eval = function(x) { return 2 * x; };
-result =
+result =
    (function() {
      return (function() { return eval(2); })();
    })();

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

Reply via email to