Implement fast load and call of arguments in the presence of eval.
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 10 May 2010 12:20:06 +0000 (12:20 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 10 May 2010 12:20:06 +0000 (12:20 +0000)
Load the arguments object from the context if there are no extensions
objects on the way. Then load the argument with a keyed load ic.

Review URL: http://codereview.chromium.org/2033004

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

src/arm/codegen-arm.cc
src/ia32/codegen-ia32.cc
src/x64/codegen-x64.cc
test/mjsunit/arguments-load-across-eval.js [new file with mode: 0644]
test/mjsunit/property-load-across-eval.js

index 21d1432..dea2a37 100644 (file)
@@ -2776,22 +2776,15 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
     // containing the eval.
     if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
       LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
-      // If there was no control flow to slow, we can exit early.
-      if (!slow.is_linked()) {
-        frame_->EmitPush(r0);
-        return;
-      }
       frame_->SpillAll();
-
       done.Jump();
 
     } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
       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 because eval forces arguments
-      // access to be through the arguments object.
+      Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         __ ldr(r0,
                ContextSlotOperandCheckExtensions(potential_slot,
                                                  r1,
@@ -2802,10 +2795,32 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
           __ cmp(r0, ip);
           __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
         }
-        // There is always control flow to slow from
-        // ContextSlotOperandCheckExtensions so we have to jump around
-        // it.
         done.Jump();
+      } else if (rewrite != NULL) {
+        // Generate fast case for argument loads.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            __ ldr(r0,
+                   ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                     r1,
+                                                     r2,
+                                                     &slow));
+            frame_->EmitPush(r0);
+            __ mov(r1, Operand(key_literal->handle()));
+            frame_->EmitPush(r1);
+            EmitKeyedLoad();
+            done.Jump();
+          }
+        }
       }
     }
 
@@ -3708,12 +3723,12 @@ void CodeGenerator::VisitCall(Call* node) {
     // ----------------------------------
     // JavaScript examples:
     //
-    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //  with (obj) foo(1, 2, 3)  // foo may be in obj.
     //
     //  function f() {};
     //  function g() {
     //    eval(...);
-    //    f();  // f could be in extension object
+    //    f();  // f could be in extension object.
     //  }
     // ----------------------------------
 
@@ -3735,10 +3750,9 @@ void CodeGenerator::VisitCall(Call* node) {
 
     } 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.
+      Expression* rewrite = var->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         __ ldr(r0,
                ContextSlotOperandCheckExtensions(potential_slot,
                                                  r1,
@@ -3752,6 +3766,33 @@ void CodeGenerator::VisitCall(Call* node) {
         frame_->EmitPush(r0);
         LoadGlobalReceiver(r1);
         done.Jump();
+      } else if (rewrite != NULL) {
+        // Generate fast case for calls of an argument function.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            __ ldr(r0,
+                   ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                     r1,
+                                                     r2,
+                                                     &slow));
+            frame_->EmitPush(r0);
+            __ mov(r1, Operand(key_literal->handle()));
+            frame_->EmitPush(r1);
+            EmitKeyedLoad();
+            frame_->EmitPush(r0);
+            LoadGlobalReceiver(r1);
+            done.Jump();
+          }
+        }
       }
     }
 
index c2701c8..d456055 100644 (file)
@@ -4731,16 +4731,13 @@ Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
     // containing the eval.
     if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
       result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
-      // If there was no control flow to slow, we can exit early.
-      if (!slow.is_linked()) return result;
       done.Jump(&result);
 
     } 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 because eval forces arguments
-      // access to be through the arguments object.
+      Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
         // value.
@@ -4755,10 +4752,32 @@ Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
           done.Branch(not_equal, &result);
           __ mov(result.reg(), Factory::undefined_value());
         }
-        // There is always control flow to slow from
-        // ContextSlotOperandCheckExtensions so we have to jump around
-        // it.
         done.Jump(&result);
+      } else if (rewrite != NULL) {
+        // Generate fast case for argument loads.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            Result arguments = allocator()->Allocate();
+            ASSERT(arguments.is_valid());
+            __ mov(arguments.reg(),
+                   ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                     arguments,
+                                                     &slow));
+            frame_->Push(&arguments);
+            frame_->Push(key_literal->handle());
+            result = EmitKeyedLoad();
+            done.Jump(&result);
+          }
+        }
       }
     }
 
@@ -5765,12 +5784,12 @@ void CodeGenerator::VisitCall(Call* node) {
     // ----------------------------------
     // JavaScript examples:
     //
-    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //  with (obj) foo(1, 2, 3)  // foo may be in obj.
     //
     //  function f() {};
     //  function g() {
     //    eval(...);
-    //    f();  // f could be in extension object
+    //    f();  // f could be in extension object.
     //  }
     // ----------------------------------
 
@@ -5782,25 +5801,23 @@ void CodeGenerator::VisitCall(Call* node) {
     // 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);
+      Result 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.
+      Expression* rewrite = var->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
         // value.
-        function = allocator()->Allocate();
+        Result function = allocator()->Allocate();
         ASSERT(function.is_valid());
         __ mov(function.reg(),
                ContextSlotOperandCheckExtensions(potential_slot,
@@ -5816,6 +5833,33 @@ void CodeGenerator::VisitCall(Call* node) {
         frame_->Push(&function);
         LoadGlobalReceiver();
         done.Jump();
+      } else if (rewrite != NULL) {
+        // Generate fast case for calls of an argument function.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            Result arguments = allocator()->Allocate();
+            ASSERT(arguments.is_valid());
+            __ mov(arguments.reg(),
+                   ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                     arguments,
+                                                     &slow));
+            frame_->Push(&arguments);
+            frame_->Push(key_literal->handle());
+            Result value = EmitKeyedLoad();
+            frame_->Push(&value);
+            LoadGlobalReceiver();
+            done.Jump();
+          }
+        }
       }
     }
 
index 8556b58..47e9482 100644 (file)
@@ -2867,12 +2867,12 @@ void CodeGenerator::VisitCall(Call* node) {
     // ----------------------------------
     // JavaScript examples:
     //
-    //  with (obj) foo(1, 2, 3)  // foo is in obj
+    //  with (obj) foo(1, 2, 3)  // foo may be in obj.
     //
     //  function f() {};
     //  function g() {
     //    eval(...);
-    //    f();  // f could be in extension object
+    //    f();  // f could be in extension object.
     //  }
     // ----------------------------------
 
@@ -2895,10 +2895,9 @@ void CodeGenerator::VisitCall(Call* node) {
 
     } 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.
+      Expression* rewrite = var->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
         // value.
@@ -2918,6 +2917,34 @@ void CodeGenerator::VisitCall(Call* node) {
         frame_->Push(&function);
         LoadGlobalReceiver();
         done.Jump();
+      } else if (rewrite != NULL) {
+        // Generate fast case for calls of an argument function.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            Result arguments = allocator()->Allocate();
+            ASSERT(arguments.is_valid());
+            __ movq(arguments.reg(),
+                    ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                      arguments,
+                                                      &slow));
+            frame_->Push(&arguments);
+            frame_->Push(key_literal->handle());
+            Result value = EmitKeyedLoad(false);
+            frame_->Drop(2);  // Drop key and receiver.
+            frame_->Push(&value);
+            LoadGlobalReceiver();
+            done.Jump();
+          }
+        }
       }
     }
 
@@ -5342,20 +5369,13 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
     // containing the eval.
     if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
       value = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
-      // If there was no control flow to slow, we can exit early.
-      if (!slow.is_linked()) {
-        frame_->Push(&value);
-        return;
-      }
-
       done.Jump(&value);
 
     } 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 because eval forces arguments
-      // access to be through the arguments object.
+      Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite();
       if (potential_slot != NULL) {
+        // Generate fast case for locals that rewrite to slots.
         // Allocate a fresh register to use as a temp in
         // ContextSlotOperandCheckExtensions and to hold the result
         // value.
@@ -5370,10 +5390,33 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
           done.Branch(not_equal, &value);
           __ LoadRoot(value.reg(), Heap::kUndefinedValueRootIndex);
         }
-        // There is always control flow to slow from
-        // ContextSlotOperandCheckExtensions so we have to jump around
-        // it.
         done.Jump(&value);
+      } else if (rewrite != NULL) {
+        // Generate fast case for argument loads.
+        Property* property = rewrite->AsProperty();
+        if (property != NULL) {
+          VariableProxy* obj_proxy = property->obj()->AsVariableProxy();
+          Literal* key_literal = property->key()->AsLiteral();
+          if (obj_proxy != NULL &&
+              key_literal != NULL &&
+              obj_proxy->IsArguments() &&
+              key_literal->handle()->IsSmi()) {
+            // Load arguments object if there are no eval-introduced
+            // variables. Then load the argument from the arguments
+            // object using keyed load.
+            Result arguments = allocator()->Allocate();
+            ASSERT(arguments.is_valid());
+            __ movq(arguments.reg(),
+                    ContextSlotOperandCheckExtensions(obj_proxy->var()->slot(),
+                                                      arguments,
+                                                      &slow));
+            frame_->Push(&arguments);
+            frame_->Push(key_literal->handle());
+            value = EmitKeyedLoad(false);
+            frame_->Drop(2);  // Drop key and receiver.
+            done.Jump(&value);
+          }
+        }
       }
     }
 
diff --git a/test/mjsunit/arguments-load-across-eval.js b/test/mjsunit/arguments-load-across-eval.js
new file mode 100644 (file)
index 0000000..e97c113
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests loading of aguments across eval calls.
+
+// Test loading across an eval call that does not shadow variables.
+function testNoShadowing(x, h) {
+  function f() {
+    eval('1');
+    assertEquals(1, x);
+    assertEquals(2, h());
+    function g() {
+      assertEquals(1, x);
+      assertEquals(2, h());
+    }
+    g();
+  }
+  f();
+}
+
+testNoShadowing(1, function() { return 2; });
+
+// Test loading across eval calls that do not shadow variables.
+function testNoShadowing2(x, h) {
+  eval('1');
+  function f() {
+    eval('1');
+    assertEquals(1, x);
+    assertEquals(2, h());
+    function g() {
+      assertEquals(1, x);
+      assertEquals(2, h());
+    }
+    g();
+  }
+  f();
+}
+
+testNoShadowing2(1, function() { return 2; });
+
+// Test loading across an eval call that shadows variables.
+function testShadowing(x, h) {
+  function f() {
+    assertEquals(1, x);
+    assertEquals(2, h());
+    eval('var x = 3; var h = function() { return 4; };');
+    assertEquals(3, x);
+    assertEquals(4, h());
+    function g() {
+      assertEquals(3, x);
+      assertEquals(4, h());
+    }
+    g();
+  }
+  f();
+  assertEquals(1, x);
+  assertEquals(2, h());
+}
+
+testShadowing(1, function() { return 2; });
+
+
index e174b85..5419cc7 100644 (file)
@@ -41,6 +41,7 @@ function testNoShadowing() {
   function f() {
     eval('1');
     assertEquals(1, x);
+    try { typeof(asdf); } catch(e) { assertUnreachable(); }
     assertEquals(2, y);
     assertEquals('global', global_function());
     assertEquals('local', local_function());
@@ -60,6 +61,7 @@ function testNoShadowing() {
     assertEquals('const_local', local_const_initialized());
     function g() {
       assertEquals(1, x);
+      try { typeof(asdf); } catch(e) { assertUnreachable(); }
       assertEquals(2, y);
       assertEquals('global', global_function());
       assertEquals('local', local_function());