Revision: 4609
Author: [email protected]
Date: Fri May  7 03:25:11 2010
Log: Implement fast calls of functions in the presence of eval (if the eval
calls do not introduce new bindings).

The infrastructure is already in place for fast loads from context
slots in the presence of eval.  This change simply uses that
infrastructure for calls as well as loads.

Review URL: http://codereview.chromium.org/2027002
http://code.google.com/p/v8/source/detail?r=4609

Modified:
 /branches/bleeding_edge/src/arm/codegen-arm.cc
 /branches/bleeding_edge/src/ia32/codegen-ia32.cc
 /branches/bleeding_edge/src/x64/codegen-x64.cc
 /branches/bleeding_edge/test/mjsunit/property-load-across-eval.js

=======================================
--- /branches/bleeding_edge/src/arm/codegen-arm.cc      Fri May  7 03:16:11 2010
+++ /branches/bleeding_edge/src/arm/codegen-arm.cc      Fri May  7 03:25:11 2010
@@ -2787,7 +2787,8 @@
       frame_->SpillAll();
       Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
       // Only generate the fast case for locals that rewrite to slots.
-      // This rules out argument loads.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
       if (potential_slot != NULL) {
         __ ldr(r0,
                ContextSlotOperandCheckExtensions(potential_slot,
@@ -3703,9 +3704,56 @@
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
     // ----------------------------------
-    // JavaScript example: 'with (obj) foo(1, 2, 3)'  // foo is in obj
+    // JavaScript examples:
+    //
+    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //
+    //  function f() {};
+    //  function g() {
+    //    eval(...);
+    //    f();  // f could be in extension object
+    //  }
     // ----------------------------------

+    // JumpTargets do not yet support merging frames so the frame must be
+    // spilled when jumping to these targets.
+    JumpTarget slow;
+    JumpTarget done;
+
+    // Generate fast-case code for variables that might be shadowed by
+    // eval-introduced variables.  Eval is used a lot without
+    // introducing variables.  In those cases, we do not want to
+    // perform a runtime call for all variables in the scope
+    // containing the eval.
+    if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+ LoadFromGlobalSlotCheckExtensions(var->slot(), NOT_INSIDE_TYPEOF, &slow);
+      frame_->EmitPush(r0);
+      LoadGlobalReceiver(r1);
+      done.Jump();
+
+    } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+      Slot* potential_slot = var->local_if_not_shadowed()->slot();
+      // Only generate the fast case for locals that rewrite to slots.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
+      if (potential_slot != NULL) {
+        __ ldr(r0,
+               ContextSlotOperandCheckExtensions(potential_slot,
+                                                 r1,
+                                                 r2,
+                                                 &slow));
+        if (potential_slot->var()->mode() == Variable::CONST) {
+          __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+          __ cmp(r0, ip);
+          __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
+        }
+        frame_->EmitPush(r0);
+        LoadGlobalReceiver(r1);
+        done.Jump();
+      }
+    }
+
+    slow.Bind();
     // Load the function
     frame_->EmitPush(cp);
     __ mov(r0, Operand(var->name()));
@@ -3717,7 +3765,9 @@
     frame_->EmitPush(r0);  // function
     frame_->EmitPush(r1);  // receiver

-    // Call the function.
+    done.Bind();
+    // Call the function. At this point, everything is spilled but the
+    // function and receiver are in r0 and r1.
     CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
     frame_->EmitPush(r0);

=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Thu May 6 05:57:11 2010 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Fri May 7 03:25:11 2010
@@ -4748,7 +4748,8 @@
     } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
       Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
       // Only generate the fast case for locals that rewrite to slots.
-      // This rules out argument loads.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
       if (potential_slot != NULL) {
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
@@ -5772,11 +5773,66 @@
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
     // ----------------------------------
-    // JavaScript example: 'with (obj) foo(1, 2, 3)'  // foo is in obj
+    // JavaScript examples:
+    //
+    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //
+    //  function f() {};
+    //  function g() {
+    //    eval(...);
+    //    f();  // f could be in extension object
+    //  }
     // ----------------------------------

-    // Load the function from the context.  Sync the frame so we can
-    // push the arguments directly into place.
+    JumpTarget slow;
+    JumpTarget done;
+
+    // Generate fast-case code for variables that might be shadowed by
+    // eval-introduced variables.  Eval is used a lot without
+    // introducing variables.  In those cases, we do not want to
+    // perform a runtime call for all variables in the scope
+    // containing the eval.
+    Result function;
+    if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+      function = LoadFromGlobalSlotCheckExtensions(var->slot(),
+                                                   NOT_INSIDE_TYPEOF,
+                                                   &slow);
+      frame_->Push(&function);
+      LoadGlobalReceiver();
+      done.Jump();
+
+    } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+      Slot* potential_slot = var->local_if_not_shadowed()->slot();
+      // Only generate the fast case for locals that rewrite to slots.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
+      if (potential_slot != NULL) {
+        // Allocate a fresh register to use as a temp in
+        // ContextSlotOperandCheckExtensions and to hold the result
+        // value.
+        function = allocator()->Allocate();
+        ASSERT(function.is_valid());
+        __ mov(function.reg(),
+               ContextSlotOperandCheckExtensions(potential_slot,
+                                                 function,
+                                                 &slow));
+        JumpTarget push_function_and_receiver;
+        if (potential_slot->var()->mode() == Variable::CONST) {
+          __ cmp(function.reg(), Factory::the_hole_value());
+          push_function_and_receiver.Branch(not_equal, &function);
+          __ mov(function.reg(), Factory::undefined_value());
+        }
+        push_function_and_receiver.Bind(&function);
+        frame_->Push(&function);
+        LoadGlobalReceiver();
+        done.Jump();
+      }
+    }
+
+    slow.Bind();
+    // Enter the runtime system to load the function from the context.
+    // Sync the frame so we can push the arguments directly into
+    // place.
     frame_->SyncRange(0, frame_->element_count() - 1);
     frame_->EmitPush(esi);
     frame_->EmitPush(Immediate(var->name()));
@@ -5793,6 +5849,7 @@
     ASSERT(!allocator()->is_used(edx));
     frame_->EmitPush(edx);

+    done.Bind();
     // Call the function.
     CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());

=======================================
--- /branches/bleeding_edge/src/x64/codegen-x64.cc      Thu May  6 04:59:10 2010
+++ /branches/bleeding_edge/src/x64/codegen-x64.cc      Fri May  7 03:25:11 2010
@@ -2866,9 +2866,63 @@
   } else if (var != NULL && var->slot() != NULL &&
              var->slot()->type() == Slot::LOOKUP) {
     // ----------------------------------
-    // JavaScript example: 'with (obj) foo(1, 2, 3)'  // foo is in obj
+    // JavaScript examples:
+    //
+    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //
+    //  function f() {};
+    //  function g() {
+    //    eval(...);
+    //    f();  // f could be in extension object
+    //  }
     // ----------------------------------

+    JumpTarget slow;
+    JumpTarget done;
+
+    // Generate fast-case code for variables that might be shadowed by
+    // eval-introduced variables.  Eval is used a lot without
+    // introducing variables.  In those cases, we do not want to
+    // perform a runtime call for all variables in the scope
+    // containing the eval.
+    Result function;
+    if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+      function = LoadFromGlobalSlotCheckExtensions(var->slot(),
+                                                   NOT_INSIDE_TYPEOF,
+                                                   &slow);
+      frame_->Push(&function);
+      LoadGlobalReceiver();
+      done.Jump();
+
+    } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+      Slot* potential_slot = var->local_if_not_shadowed()->slot();
+      // Only generate the fast case for locals that rewrite to slots.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
+      if (potential_slot != NULL) {
+        // Allocate a fresh register to use as a temp in
+        // ContextSlotOperandCheckExtensions and to hold the result
+        // value.
+        function = allocator()->Allocate();
+        ASSERT(function.is_valid());
+        __ movq(function.reg(),
+                ContextSlotOperandCheckExtensions(potential_slot,
+                                                  function,
+                                                  &slow));
+        JumpTarget push_function_and_receiver;
+        if (potential_slot->var()->mode() == Variable::CONST) {
+          __ CompareRoot(function.reg(), Heap::kTheHoleValueRootIndex);
+          push_function_and_receiver.Branch(not_equal, &function);
+          __ LoadRoot(function.reg(), Heap::kUndefinedValueRootIndex);
+        }
+        push_function_and_receiver.Bind(&function);
+        frame_->Push(&function);
+        LoadGlobalReceiver();
+        done.Jump();
+      }
+    }
+
+    slow.Bind();
     // Load the function from the context.  Sync the frame so we can
     // push the arguments directly into place.
     frame_->SyncRange(0, frame_->element_count() - 1);
@@ -2887,6 +2941,7 @@
     ASSERT(!allocator()->is_used(rdx));
     frame_->EmitPush(rdx);

+    done.Bind();
     // Call the function.
     CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());

@@ -5178,7 +5233,8 @@
     } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
       Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
       // Only generate the fast case for locals that rewrite to slots.
-      // This rules out argument loads.
+      // This rules out argument loads because eval forces arguments
+      // access to be through the arguments object.
       if (potential_slot != NULL) {
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
=======================================
--- /branches/bleeding_edge/test/mjsunit/property-load-across-eval.js Fri Feb 27 05:00:32 2009 +++ /branches/bleeding_edge/test/mjsunit/property-load-across-eval.js Fri May 7 03:25:11 2010
@@ -28,17 +28,55 @@
 // Tests loading of properties across eval calls.

 var x = 1;
+function global_function() { return 'global'; }
+const const_uninitialized;
+const const_initialized = function() { return "const_global"; }

 // Test loading across an eval call that does not shadow variables.
 function testNoShadowing() {
   var y = 2;
+  function local_function() { return 'local'; }
+  const local_const_uninitialized;
+  const local_const_initialized = function() { return "const_local"; }
   function f() {
     eval('1');
     assertEquals(1, x);
     assertEquals(2, y);
+    assertEquals('global', global_function());
+    assertEquals('local', local_function());
+    try {
+      const_uninitialized();
+      assertUnreachable();
+    } catch(e) {
+      // Ignore.
+    }
+    assertEquals('const_global', const_initialized());
+    try {
+      local_const_uninitialized();
+      assertUnreachable();
+    } catch(e) {
+      // Ignore.
+    }
+    assertEquals('const_local', local_const_initialized());
     function g() {
       assertEquals(1, x);
       assertEquals(2, y);
+      assertEquals('global', global_function());
+      assertEquals('local', local_function());
+      try {
+        const_uninitialized();
+        assertUnreachable();
+      } catch(e) {
+        // Ignore.
+      }
+      assertEquals('const_global', const_initialized());
+      try {
+        local_const_uninitialized();
+        assertUnreachable();
+      } catch(e) {
+        // Ignore.
+      }
+      assertEquals('const_local', local_const_initialized());
     }
     g();
   }
@@ -50,14 +88,19 @@
 // Test loading across eval calls that do not shadow variables.
 function testNoShadowing2() {
   var y = 2;
+  function local_function() { return 'local'; }
   eval('1');
   function f() {
     eval('1');
     assertEquals(1, x);
     assertEquals(2, y);
+    assertEquals('global', global_function());
+    assertEquals('local', local_function());
     function g() {
       assertEquals(1, x);
       assertEquals(2, y);
+      assertEquals('global', global_function());
+      assertEquals('local', local_function());
     }
     g();
   }
@@ -69,13 +112,20 @@
 // Test loading across an eval call that shadows variables.
 function testShadowing() {
   var y = 2;
+  function local_function() { return 'local'; }
   function f() {
     eval('var x = 3; var y = 4;');
     assertEquals(3, x);
     assertEquals(4, y);
+    eval('function local_function() { return "new_local"; }');
+    eval('function global_function() { return "new_nonglobal"; }');
+    assertEquals('new_nonglobal', global_function());
+    assertEquals('new_local', local_function());
     function g() {
       assertEquals(3, x);
       assertEquals(4, y);
+      assertEquals('new_nonglobal', global_function());
+      assertEquals('new_local', local_function());
     }
     g();
   }

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

Reply via email to