Implement fast calls of functions in the presence of eval (if the eval
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 7 May 2010 10:25:11 +0000 (10:25 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 7 May 2010 10:25:11 +0000 (10:25 +0000)
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

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

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

index 8eb4aa2..f238555 100644 (file)
@@ -2787,7 +2787,8 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
       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 @@ void CodeGenerator::VisitCall(Call* node) {
   } 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 @@ void CodeGenerator::VisitCall(Call* node) {
     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);
 
index 6096982..86e31bd 100644 (file)
@@ -4748,7 +4748,8 @@ Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
     } 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 @@ void CodeGenerator::VisitCall(Call* node) {
   } 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 @@ void CodeGenerator::VisitCall(Call* node) {
     ASSERT(!allocator()->is_used(edx));
     frame_->EmitPush(edx);
 
+    done.Bind();
     // Call the function.
     CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
 
index 0fdef25..473fead 100644 (file)
@@ -2866,9 +2866,63 @@ void CodeGenerator::VisitCall(Call* node) {
   } 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 @@ void CodeGenerator::VisitCall(Call* node) {
     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 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
     } 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
index 8271f4c..e174b85 100644 (file)
 // 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 @@ testNoShadowing();
 // 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 @@ testNoShadowing2();
 // 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();
   }