Version 2.0.3

Optimized handling and adding of strings, for-in and Array.join.

Heap serialization is now non-destructive.

Improved profiler support with information on time spend in C++ callbacks registered through the API.

Added commands to the debugger protocol for starting/stopping profiling.

Enabled the non-optimizing compiler for top-level code.

Changed the API to only allow strings to be set as data objects on Contexts and scripts to avoid potentially keeping global objects around for too long (issue 528).

OpenBSD support patch by Peter Valchev <pvalchev@gmail.com>.

Fixed bugs.


git-svn-id: http://v8.googlecode.com/svn/trunk@3423 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index ca0184e..86bc18a 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -566,6 +566,7 @@
   // register.
   static const int kPcLoadDelta = 8;
 
+  static const int kJSReturnSequenceLength = 4;
 
   // ---------------------------------------------------------------------------
   // Code generation
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index c62756d..ea3df6c 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -326,7 +326,7 @@
     // Calculate the exact length of the return sequence and make sure that
     // the constant pool is not emitted inside of the return sequence.
     int32_t sp_delta = (scope_->num_parameters() + 1) * kPointerSize;
-    int return_sequence_length = Debug::kARMJSReturnSequenceLength;
+    int return_sequence_length = Assembler::kJSReturnSequenceLength;
     if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) {
       // Additional mov instruction generated.
       return_sequence_length++;
@@ -1560,7 +1560,7 @@
   CheckStack();  // TODO(1222600): ignore if body contains calls.
   VisitAndSpill(node->body());
 
-      // Compile the test.
+  // Compile the test.
   switch (info) {
     case ALWAYS_TRUE:
       // If control can fall off the end of the body, jump back to the
@@ -1775,19 +1775,77 @@
 
   jsobject.Bind();
   // Get the set of properties (as a FixedArray or Map).
-  frame_->EmitPush(r0);  // duplicate the object being enumerated
-  frame_->EmitPush(r0);
+  // r0: value to be iterated over
+  frame_->EmitPush(r0);  // Push the object being iterated over.
+
+  // Check cache validity in generated code. This is a fast case for
+  // the JSObject::IsSimpleEnum cache validity checks. If we cannot
+  // guarantee cache validity, call the runtime system to check cache
+  // validity or get the property names in a fixed array.
+  JumpTarget call_runtime;
+  JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+  JumpTarget check_prototype;
+  JumpTarget use_cache;
+  __ mov(r1, Operand(r0));
+  loop.Bind();
+  // Check that there are no elements.
+  __ ldr(r2, FieldMemOperand(r1, JSObject::kElementsOffset));
+  __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
+  __ cmp(r2, r4);
+  call_runtime.Branch(ne);
+  // Check that instance descriptors are not empty so that we can
+  // check for an enum cache.  Leave the map in r3 for the subsequent
+  // prototype load.
+  __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+  __ ldr(r2, FieldMemOperand(r3, Map::kInstanceDescriptorsOffset));
+  __ LoadRoot(ip, Heap::kEmptyDescriptorArrayRootIndex);
+  __ cmp(r2, ip);
+  call_runtime.Branch(eq);
+  // Check that there in an enum cache in the non-empty instance
+  // descriptors.  This is the case if the next enumeration index
+  // field does not contain a smi.
+  __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumerationIndexOffset));
+  __ tst(r2, Operand(kSmiTagMask));
+  call_runtime.Branch(eq);
+  // For all objects but the receiver, check that the cache is empty.
+  // r4: empty fixed array root.
+  __ cmp(r1, r0);
+  check_prototype.Branch(eq);
+  __ ldr(r2, FieldMemOperand(r2, DescriptorArray::kEnumCacheBridgeCacheOffset));
+  __ cmp(r2, r4);
+  call_runtime.Branch(ne);
+  check_prototype.Bind();
+  // Load the prototype from the map and loop if non-null.
+  __ ldr(r1, FieldMemOperand(r3, Map::kPrototypeOffset));
+  __ LoadRoot(ip, Heap::kNullValueRootIndex);
+  __ cmp(r1, ip);
+  loop.Branch(ne);
+  // The enum cache is valid.  Load the map of the object being
+  // iterated over and use the cache for the iteration.
+  __ ldr(r0, FieldMemOperand(r0, HeapObject::kMapOffset));
+  use_cache.Jump();
+
+  call_runtime.Bind();
+  // Call the runtime to get the property names for the object.
+  frame_->EmitPush(r0);  // push the object (slot 4) for the runtime call
   frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1);
 
-  // If we got a Map, we can do a fast modification check.
-  // Otherwise, we got a FixedArray, and we have to do a slow check.
+  // If we got a map from the runtime call, we can do a fast
+  // modification check. Otherwise, we got a fixed array, and we have
+  // to do a slow check.
+  // r0: map or fixed array (result from call to
+  // Runtime::kGetPropertyNamesFast)
   __ mov(r2, Operand(r0));
   __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
   __ LoadRoot(ip, Heap::kMetaMapRootIndex);
   __ cmp(r1, ip);
   fixed_array.Branch(ne);
 
+  use_cache.Bind();
   // Get enum cache
+  // r0: map (either the result from a call to
+  // Runtime::kGetPropertyNamesFast or has been fetched directly from
+  // the object)
   __ mov(r1, Operand(r0));
   __ ldr(r1, FieldMemOperand(r1, Map::kInstanceDescriptorsOffset));
   __ ldr(r1, FieldMemOperand(r1, DescriptorArray::kEnumerationIndexOffset));
@@ -3308,9 +3366,6 @@
 
   // Now r2 has the string type.
   __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
-  __ and_(r4, r2, Operand(kStringSizeMask));
-  __ add(r4, r4, Operand(String::kLongLengthShift));
-  __ mov(r3, Operand(r3, LSR, r4));
   // Now r3 has the length of the string.  Compare with the index.
   __ cmp(r3, Operand(r0, LSR, kSmiTagSize));
   __ b(le, &slow);
@@ -3508,6 +3563,17 @@
 }
 
 
+void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
+  ASSERT_EQ(2, args->length());
+
+  Load(args->at(0));
+  Load(args->at(1));
+
+  frame_->CallRuntime(Runtime::kStringAdd, 2);
+  frame_->EmitPush(r0);
+}
+
+
 void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
   VirtualFrame::SpilledScope spilled_scope;
   ASSERT(args->length() == 2);
@@ -5149,8 +5215,53 @@
   // We jump to here if something goes wrong (one param is not a number of any
   // sort or new-space allocation fails).
   __ bind(&slow);
+
+  // Push arguments to the stack
   __ push(r1);
   __ push(r0);
+
+  if (Token::ADD == operation) {
+    // Test for string arguments before calling runtime.
+    // r1 : first argument
+    // r0 : second argument
+    // sp[0] : second argument
+    // sp[1] : first argument
+
+    Label not_strings, not_string1, string1;
+    __ tst(r1, Operand(kSmiTagMask));
+    __ b(eq, &not_string1);
+    __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
+    __ b(ge, &not_string1);
+
+    // First argument is a a string, test second.
+    __ tst(r0, Operand(kSmiTagMask));
+    __ b(eq, &string1);
+    __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
+    __ b(ge, &string1);
+
+    // First and second argument are strings.
+    __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
+
+    // Only first argument is a string.
+    __ bind(&string1);
+    __ mov(r0, Operand(2));  // Set number of arguments.
+    __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_JS);
+
+    // First argument was not a string, test second.
+    __ bind(&not_string1);
+    __ tst(r0, Operand(kSmiTagMask));
+    __ b(eq, &not_strings);
+    __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE);
+    __ b(ge, &not_strings);
+
+    // Only second argument is a string.
+    __ b(&not_strings);
+    __ mov(r0, Operand(2));  // Set number of arguments.
+    __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
+
+    __ bind(&not_strings);
+  }
+
   __ mov(r0, Operand(1));  // Set number of arguments.
   __ InvokeBuiltin(builtin, JUMP_JS);  // Tail call.  No return.
 
@@ -6388,7 +6499,7 @@
   __ b(eq, &adaptor);
 
   // Check index against formal parameters count limit passed in
-  // through register eax. Use unsigned comparison to get negative
+  // through register r0. Use unsigned comparison to get negative
   // check for free.
   __ cmp(r1, r0);
   __ b(cs, &slow);
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 30a1ae5..ba7f936 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -366,6 +366,9 @@
   inline void GenerateMathSin(ZoneList<Expression*>* args);
   inline void GenerateMathCos(ZoneList<Expression*>* args);
 
+  // Fast support for StringAdd.
+  void GenerateStringAdd(ZoneList<Expression*>* args);
+
   // Simple condition analysis.
   enum ConditionAnalysis {
     ALWAYS_TRUE,
diff --git a/src/arm/constants-arm.h b/src/arm/constants-arm.h
index 58396f8..9432207 100644
--- a/src/arm/constants-arm.h
+++ b/src/arm/constants-arm.h
@@ -43,24 +43,27 @@
 # define USE_THUMB_INTERWORK 1
 #endif
 
-#if defined(__ARM_ARCH_5T__) || \
-    defined(__ARM_ARCH_5TE__) || \
-    defined(__ARM_ARCH_6__) || \
-    defined(__ARM_ARCH_7A__) || \
+#if defined(__ARM_ARCH_7A__) || \
+    defined(__ARM_ARCH_7R__) || \
     defined(__ARM_ARCH_7__)
-# define CAN_USE_ARMV5_INSTRUCTIONS 1
-# define CAN_USE_THUMB_INSTRUCTIONS 1
+# define CAN_USE_ARMV7_INSTRUCTIONS 1
 #endif
 
-#if defined(__ARM_ARCH_6__) || \
-    defined(__ARM_ARCH_7A__) || \
-    defined(__ARM_ARCH_7__)
+#if defined(__ARM_ARCH_6__) ||   \
+    defined(__ARM_ARCH_6J__) ||  \
+    defined(__ARM_ARCH_6K__) ||  \
+    defined(__ARM_ARCH_6Z__) ||  \
+    defined(__ARM_ARCH_6ZK__) || \
+    defined(__ARM_ARCH_6T2__) || \
+    defined(CAN_USE_ARMV7_INSTRUCTIONS)
 # define CAN_USE_ARMV6_INSTRUCTIONS 1
 #endif
 
-#if defined(__ARM_ARCH_7A__) || \
-    defined(__ARM_ARCH_7__)
-# define CAN_USE_ARMV7_INSTRUCTIONS 1
+#if defined(__ARM_ARCH_5T__)            || \
+    defined(__ARM_ARCH_5TE__)           || \
+    defined(CAN_USE_ARMV6_INSTRUCTIONS)
+# define CAN_USE_ARMV5_INSTRUCTIONS 1
+# define CAN_USE_THUMB_INSTRUCTIONS 1
 #endif
 
 // Simulator should support ARM5 instructions.
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index 102952d..fc9808d 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -61,7 +61,7 @@
 // Restore the JS frame exit code.
 void BreakLocationIterator::ClearDebugBreakAtReturn() {
   rinfo()->PatchCode(original_rinfo()->pc(),
-                     Debug::kARMJSReturnSequenceLength);
+                     Assembler::kJSReturnSequenceLength);
 }
 
 
diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc
index 6d0510e..ab636b6 100644
--- a/src/arm/fast-codegen-arm.cc
+++ b/src/arm/fast-codegen-arm.cc
@@ -73,16 +73,46 @@
 
   bool function_in_register = true;
 
+  // Possibly allocate a local context.
+  if (fun->scope()->num_heap_slots() > 0) {
+    Comment cmnt(masm_, "[ Allocate local context");
+    // Argument to NewContext is the function, which is in r1.
+    __ push(r1);
+    __ CallRuntime(Runtime::kNewContext, 1);
+    function_in_register = false;
+    // Context is returned in both r0 and cp.  It replaces the context
+    // passed to us.  It's saved in the stack and kept live in cp.
+    __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+    // Copy any necessary parameters into the context.
+    int num_parameters = fun->scope()->num_parameters();
+    for (int i = 0; i < num_parameters; i++) {
+      Slot* slot = fun->scope()->parameter(i)->slot();
+      if (slot != NULL && slot->type() == Slot::CONTEXT) {
+        int parameter_offset = StandardFrameConstants::kCallerSPOffset +
+                               (num_parameters - 1 - i) * kPointerSize;
+        // Load parameter from stack.
+        __ ldr(r0, MemOperand(fp, parameter_offset));
+        // Store it in the context
+        __ str(r0, MemOperand(cp, Context::SlotOffset(slot->index())));
+      }
+    }
+  }
+
   Variable* arguments = fun->scope()->arguments()->AsVariable();
   if (arguments != NULL) {
     // Function uses arguments object.
     Comment cmnt(masm_, "[ Allocate arguments object");
-    __ mov(r3, r1);
+    if (!function_in_register) {
+      // Load this again, if it's used by the local context below.
+      __ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
+    } else {
+      __ mov(r3, r1);
+    }
     // Receiver is just before the parameters on the caller's stack.
     __ add(r2, fp, Operand(StandardFrameConstants::kCallerSPOffset +
                                fun->num_parameters() * kPointerSize));
     __ mov(r1, Operand(Smi::FromInt(fun->num_parameters())));
-    __ stm(db_w, sp, r1.bit() | r2.bit() | r3.bit());
+    __ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit());
 
     // Arguments to ArgumentsAccessStub:
     //   function, receiver address, parameter count.
@@ -90,33 +120,12 @@
     // stack frame was an arguments adapter frame.
     ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
     __ CallStub(&stub);
-    __ str(r0, MemOperand(fp, SlotOffset(arguments->slot())));
+    // Duplicate the value; move-to-slot operation might clobber registers.
+    __ mov(r3, r0);
+    Move(arguments->slot(), r0, r1, r2);
     Slot* dot_arguments_slot =
         fun->scope()->arguments_shadow()->AsVariable()->slot();
-    __ str(r0, MemOperand(fp, SlotOffset(dot_arguments_slot)));
-    function_in_register = false;
-  }
-
-  // Possibly allocate a local context.
-  if (fun->scope()->num_heap_slots() > 0) {
-    Comment cmnt(masm_, "[ Allocate local context");
-    if (!function_in_register) {
-      // Load this again, if it's used by the local context below.
-      __ ldr(r1, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
-    }
-    // Argument to NewContext is the function, which is in r1.
-    __ push(r1);
-    __ CallRuntime(Runtime::kNewContext, 1);
-    // Context is returned in both r0 and cp.  It replaces the context
-    // passed to us.  It's saved in the stack and kept live in cp.
-    __ str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
-#ifdef DEBUG
-    // Assert we do not have to copy any parameters into the context.
-    for (int i = 0, len = fun->scope()->num_parameters(); i < len; i++) {
-      Slot* slot = fun->scope()->parameter(i)->slot();
-      ASSERT(slot != NULL && slot->type() != Slot::CONTEXT);
-    }
-#endif
+    Move(dot_arguments_slot, r3, r1, r2);
   }
 
   // Check the stack for overflow or break request.
@@ -179,7 +188,7 @@
     // the constant pool is not emitted inside of the return sequence.
     int num_parameters = function_->scope()->num_parameters();
     int32_t sp_delta = (num_parameters + 1) * kPointerSize;
-    int return_sequence_length = Debug::kARMJSReturnSequenceLength;
+    int return_sequence_length = Assembler::kJSReturnSequenceLength;
     if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) {
       // Additional mov instruction generated.
       return_sequence_length++;
@@ -238,7 +247,42 @@
 }
 
 
-void FastCodeGenerator::Move(Expression::Context context, Slot* source) {
+template <>
+MemOperand FastCodeGenerator::CreateSlotOperand<MemOperand>(
+    Slot* source,
+    Register scratch) {
+  switch (source->type()) {
+    case Slot::PARAMETER:
+    case Slot::LOCAL:
+      return MemOperand(fp, SlotOffset(source));
+    case Slot::CONTEXT: {
+      int context_chain_length =
+          function_->scope()->ContextChainLength(source->var()->scope());
+      __ LoadContext(scratch, context_chain_length);
+      return CodeGenerator::ContextOperand(scratch, source->index());
+      break;
+    }
+    case Slot::LOOKUP:
+      UNIMPLEMENTED();
+      // Fall-through.
+    default:
+      UNREACHABLE();
+      return MemOperand(r0, 0);  // Dead code to make the compiler happy.
+  }
+}
+
+
+void FastCodeGenerator::Move(Register dst, Slot* source) {
+  // Use dst as scratch.
+  MemOperand location = CreateSlotOperand<MemOperand>(source, dst);
+  __ ldr(dst, location);
+}
+
+
+
+void FastCodeGenerator::Move(Expression::Context context,
+                             Slot* source,
+                             Register scratch) {
   switch (context) {
     case Expression::kUninitialized:
       UNREACHABLE();
@@ -248,8 +292,8 @@
     case Expression::kTest:  // Fall through.
     case Expression::kValueTest:  // Fall through.
     case Expression::kTestValue:
-      __ ldr(ip, MemOperand(fp, SlotOffset(source)));
-      Move(context, ip);
+      Move(scratch, source);
+      Move(context, scratch);
       break;
   }
 }
@@ -272,24 +316,60 @@
 }
 
 
+void FastCodeGenerator::Move(Slot* dst,
+                             Register src,
+                             Register scratch1,
+                             Register scratch2) {
+  switch (dst->type()) {
+    case Slot::PARAMETER:
+    case Slot::LOCAL:
+      __ str(src, MemOperand(fp, SlotOffset(dst)));
+      break;
+    case Slot::CONTEXT: {
+      int context_chain_length =
+          function_->scope()->ContextChainLength(dst->var()->scope());
+      __ LoadContext(scratch1, context_chain_length);
+      int index = Context::SlotOffset(dst->index());
+      __ mov(scratch2, Operand(index));
+      __ str(src, MemOperand(scratch1, index));
+      __ RecordWrite(scratch1, scratch2, src);
+      break;
+    }
+    case Slot::LOOKUP:
+      UNIMPLEMENTED();
+    default:
+      UNREACHABLE();
+  }
+}
+
+
+
 void FastCodeGenerator::DropAndMove(Expression::Context context,
-                                    Register source) {
+                                    Register source,
+                                    int drop_count) {
+  ASSERT(drop_count > 0);
   switch (context) {
     case Expression::kUninitialized:
       UNREACHABLE();
     case Expression::kEffect:
-      __ pop();
+      __ add(sp, sp, Operand(drop_count * kPointerSize));
       break;
     case Expression::kValue:
+      if (drop_count > 1) {
+        __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+      }
       __ str(source, MemOperand(sp));
       break;
     case Expression::kTest:
       ASSERT(!source.is(sp));
-      __ pop();
+      __ add(sp, sp, Operand(drop_count * kPointerSize));
       TestAndBranch(source, true_label_, false_label_);
       break;
     case Expression::kValueTest: {
       Label discard;
+      if (drop_count > 1) {
+        __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+      }
       __ str(source, MemOperand(sp));
       TestAndBranch(source, true_label_, &discard);
       __ bind(&discard);
@@ -299,6 +379,9 @@
     }
     case Expression::kTestValue: {
       Label discard;
+      if (drop_count > 1) {
+        __ add(sp, sp, Operand((drop_count - 1) * kPointerSize));
+      }
       __ str(source, MemOperand(sp));
       TestAndBranch(source, &discard, false_label_);
       __ bind(&discard);
@@ -376,26 +459,26 @@
         __ mov(r0, Operand(Factory::the_hole_value()));
         if (FLAG_debug_code) {
           // Check if we have the correct context pointer.
-          __ ldr(r1, CodeGenerator::ContextOperand(
-              cp, Context::FCONTEXT_INDEX));
+          __ ldr(r1, CodeGenerator::ContextOperand(cp,
+                                                   Context::FCONTEXT_INDEX));
           __ cmp(r1, cp);
           __ Check(eq, "Unexpected declaration in current context.");
         }
         __ str(r0, CodeGenerator::ContextOperand(cp, slot->index()));
         // No write barrier since the_hole_value is in old space.
-        ASSERT(Heap::InNewSpace(*Factory::the_hole_value()));
+        ASSERT(!Heap::InNewSpace(*Factory::the_hole_value()));
       } else if (decl->fun() != NULL) {
         Visit(decl->fun());
         __ pop(r0);
         if (FLAG_debug_code) {
           // Check if we have the correct context pointer.
-          __ ldr(r1, CodeGenerator::ContextOperand(
-              cp, Context::FCONTEXT_INDEX));
+          __ ldr(r1, CodeGenerator::ContextOperand(cp,
+                                                   Context::FCONTEXT_INDEX));
           __ cmp(r1, cp);
           __ Check(eq, "Unexpected declaration in current context.");
         }
         __ str(r0, CodeGenerator::ContextOperand(cp, slot->index()));
-        int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
+        int offset = Context::SlotOffset(slot->index());
         __ mov(r2, Operand(offset));
         // We know that we have written a function, which is not a smi.
         __ RecordWrite(cp, r2, r0);
@@ -467,53 +550,60 @@
     DropAndMove(expr->context(), r0);
   } else if (rewrite->AsSlot() != NULL) {
     Slot* slot = rewrite->AsSlot();
-    ASSERT_NE(NULL, slot);
-    switch (slot->type()) {
-      case Slot::LOCAL:
-      case Slot::PARAMETER: {
-        Comment cmnt(masm_, "Stack slot");
-        Move(expr->context(), rewrite->AsSlot());
-        break;
-      }
-
-      case Slot::CONTEXT: {
-        Comment cmnt(masm_, "Context slot");
-        int chain_length =
-            function_->scope()->ContextChainLength(slot->var()->scope());
-        if (chain_length > 0) {
-          // Move up the chain of contexts to the context containing the slot.
-          __ ldr(r0, CodeGenerator::ContextOperand(cp, Context::CLOSURE_INDEX));
-          // Load the function context (which is the incoming, outer context).
-          __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
-          for (int i = 1; i < chain_length; i++) {
-            __ ldr(r0,
-                   CodeGenerator::ContextOperand(r0, Context::CLOSURE_INDEX));
-            // Load the function context (which is the incoming, outer context).
-            __ ldr(r0, FieldMemOperand(r0, JSFunction::kContextOffset));
-          }
-          // The context may be an intermediate context, not a function context.
-          __ ldr(r0,
-                 CodeGenerator::ContextOperand(r0, Context::FCONTEXT_INDEX));
-        } else {  // Slot is in the current context.
-          __ ldr(r0,
-                 CodeGenerator::ContextOperand(cp, Context::FCONTEXT_INDEX));
+    if (FLAG_debug_code) {
+      switch (slot->type()) {
+        case Slot::LOCAL:
+        case Slot::PARAMETER: {
+          Comment cmnt(masm_, "Stack slot");
+          break;
         }
-        __ ldr(r0, CodeGenerator::ContextOperand(r0, slot->index()));
-        Move(expr->context(), r0);
-        break;
+        case Slot::CONTEXT: {
+          Comment cmnt(masm_, "Context slot");
+          break;
+        }
+        case Slot::LOOKUP:
+          UNIMPLEMENTED();
+          break;
+        default:
+          UNREACHABLE();
       }
-
-      case Slot::LOOKUP:
-        UNREACHABLE();
-        break;
     }
+    Move(expr->context(), slot, r0);
   } else {
-    // The parameter variable has been rewritten into an explict access to
-    // the arguments object.
+    // A variable has been rewritten into an explicit access to
+    // an object property.
     Property* property = rewrite->AsProperty();
     ASSERT_NOT_NULL(property);
-    ASSERT_EQ(expr->context(), property->context());
-    Visit(property);
+
+    // Currently the only parameter expressions that can occur are
+    // on the form "slot[literal]".
+
+    // Check that the object is in a slot.
+    Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
+    ASSERT_NOT_NULL(object_var);
+    Slot* object_slot = object_var->slot();
+    ASSERT_NOT_NULL(object_slot);
+
+    // Load the object.
+    Move(r2, object_slot);
+
+    // Check that the key is a smi.
+    Literal* key_literal = property->key()->AsLiteral();
+    ASSERT_NOT_NULL(key_literal);
+    ASSERT(key_literal->handle()->IsSmi());
+
+    // Load the key.
+    __ mov(r1, Operand(key_literal->handle()));
+
+    // Push both as arguments to ic.
+    __ stm(db_w, sp, r2.bit() | r1.bit());
+
+    // Do a KEYED property load.
+    Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
+    __ Call(ic, RelocInfo::CODE_TARGET);
+
+    // Drop key and object left on the stack by IC, and push the result.
+    DropAndMove(expr->context(), r0, 2);
   }
 }
 
@@ -575,8 +665,9 @@
     __ CallRuntime(Runtime::kCloneShallowLiteralBoilerplate, 1);
   }
 
-  // If result_saved == true: the result is saved on top of the stack.
-  // If result_saved == false: the result is in r0.
+  // If result_saved == true: The result is saved on top of the
+  //  stack and in r0.
+  // If result_saved == false: The result not on the stack, just in r0.
   bool result_saved = false;
 
   for (int i = 0; i < expr->properties()->length(); i++) {
@@ -604,6 +695,7 @@
           Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
           __ Call(ic, RelocInfo::CODE_TARGET);
           // StoreIC leaves the receiver on the stack.
+          __ ldr(r0, MemOperand(sp));  // Restore result into r0.
           break;
         }
         // Fall through.
@@ -615,7 +707,7 @@
         Visit(value);
         ASSERT_EQ(Expression::kValue, value->context());
         __ CallRuntime(Runtime::kSetProperty, 3);
-        __ ldr(r0, MemOperand(sp));  // Restore result into r0
+        __ ldr(r0, MemOperand(sp));  // Restore result into r0.
         break;
 
       case ObjectLiteral::Property::GETTER:  // Fall through.
@@ -785,7 +877,7 @@
     // Overwrite the global object on the stack with the result if needed.
     DropAndMove(expr->context(), r0);
 
-  } else {
+  } else if (var->slot()) {
     Slot* slot = var->slot();
     ASSERT_NOT_NULL(slot);  // Variables rewritten as properties not handled.
     switch (slot->type()) {
@@ -884,6 +976,35 @@
         UNREACHABLE();
         break;
     }
+  } else {
+    Property* property = var->rewrite()->AsProperty();
+    ASSERT_NOT_NULL(property);
+
+    // Load object and key onto the stack.
+    Slot* object_slot = property->obj()->AsSlot();
+    ASSERT_NOT_NULL(object_slot);
+    Move(Expression::kValue, object_slot, r0);
+
+    Literal* key_literal = property->key()->AsLiteral();
+    ASSERT_NOT_NULL(key_literal);
+    Move(Expression::kValue, key_literal);
+
+    // Value to store was pushed before object and key on the stack.
+    __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
+
+    // Arguments to ic is value in r0, object and key on stack.
+    Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+    __ Call(ic, RelocInfo::CODE_TARGET);
+
+    if (expr->context() == Expression::kEffect) {
+      __ add(sp, sp, Operand(3 * kPointerSize));
+    } else if (expr->context() == Expression::kValue) {
+      // Value is still on the stack in esp[2 * kPointerSize]
+      __ add(sp, sp, Operand(2 * kPointerSize));
+    } else {
+      __ ldr(r0, MemOperand(sp, 2 * kPointerSize));
+      DropAndMove(expr->context(), r0, 3);
+    }
   }
 }
 
@@ -1134,9 +1255,14 @@
 void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
   Comment cmnt(masm_, "[ CallRuntime");
   ZoneList<Expression*>* args = expr->arguments();
-  Runtime::Function* function = expr->function();
 
-  ASSERT(function != NULL);
+  if (expr->is_jsruntime()) {
+    // Prepare for calling JS runtime function.
+    __ mov(r1, Operand(expr->name()));
+    __ ldr(r0, CodeGenerator::GlobalObject());
+    __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset));
+    __ stm(db_w, sp, r1.bit() | r0.bit());
+  }
 
   // Push the arguments ("left-to-right").
   int arg_count = args->length();
@@ -1145,8 +1271,20 @@
     ASSERT_EQ(Expression::kValue, args->at(i)->context());
   }
 
-  __ CallRuntime(function, arg_count);
-  Move(expr->context(), r0);
+  if (expr->is_jsruntime()) {
+    // Call the JS runtime function.
+    Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
+                                                           NOT_IN_LOOP);
+    __ Call(ic, RelocInfo::CODE_TARGET);
+    // Restore context register.
+    __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
+    // Discard the function left on TOS.
+    DropAndMove(expr->context(), r0);
+  } else {
+    // Call the C runtime function.
+    __ CallRuntime(expr->function(), arg_count);
+    Move(expr->context(), r0);
+  }
 }
 
 
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index ba83645..c56f414 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -107,12 +107,17 @@
   static const int kProbes = 4;
   for (int i = 0; i < kProbes; i++) {
     // Compute the masked index: (hash + i + i * i) & mask.
-    __ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
-    __ mov(t1, Operand(t1, LSR, String::kHashShift));
+    __ ldr(t1, FieldMemOperand(r2, String::kHashFieldOffset));
     if (i > 0) {
-      __ add(t1, t1, Operand(StringDictionary::GetProbeOffset(i)));
+      // Add the probe offset (i + i * i) left shifted to avoid right shifting
+      // the hash in a separate instruction. The value hash + i + i * i is right
+      // shifted in the following and instruction.
+      ASSERT(StringDictionary::GetProbeOffset(i) <
+             1 << (32 - String::kHashFieldOffset));
+      __ add(t1, t1, Operand(
+          StringDictionary::GetProbeOffset(i) << String::kHashShift));
     }
-    __ and_(t1, t1, Operand(r3));
+    __ and_(t1, r3, Operand(t1, LSR, String::kHashShift));
 
     // Scale the index by multiplying by the element size.
     ASSERT(StringDictionary::kEntrySize == 3);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index a668cb1..aa6570c 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -155,6 +155,15 @@
 }
 
 
+void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
+  LoadRoot(ip, Heap::kStackLimitRootIndex);
+  cmp(sp, Operand(ip));
+  b(lo, on_stack_overflow);
+}
+
+
+
+
 void MacroAssembler::SmiJumpTable(Register index, Vector<Label*> targets) {
   // Empty the const pool.
   CheckConstPool(true, true);
@@ -785,15 +794,13 @@
   mov(scratch1, Operand(new_space_allocation_top));
   if ((flags & RESULT_CONTAINS_TOP) == 0) {
     ldr(result, MemOperand(scratch1));
-  } else {
-#ifdef DEBUG
+  } else if (FLAG_debug_code) {
     // Assert that result actually contains top on entry. scratch2 is used
     // immediately below so this use of scratch2 does not cause difference with
     // respect to register content between debug and release mode.
     ldr(scratch2, MemOperand(scratch1));
     cmp(result, scratch2);
     Check(eq, "Unexpected allocation top");
-#endif
   }
 
   // Calculate new top and bail out if new space is exhausted. Use result
@@ -806,7 +813,11 @@
   cmp(result, Operand(scratch2));
   b(hi, gc_required);
 
-  // Update allocation top. result temporarily holds the new top,
+  // Update allocation top. result temporarily holds the new top.
+  if (FLAG_debug_code) {
+    tst(result, Operand(kObjectAlignmentMask));
+    Check(eq, "Unaligned allocation in new space");
+  }
   str(result, MemOperand(scratch1));
 
   // Tag and adjust back to start of new object.
@@ -835,15 +846,13 @@
   mov(scratch1, Operand(new_space_allocation_top));
   if ((flags & RESULT_CONTAINS_TOP) == 0) {
     ldr(result, MemOperand(scratch1));
-  } else {
-#ifdef DEBUG
+  } else if (FLAG_debug_code) {
     // Assert that result actually contains top on entry. scratch2 is used
     // immediately below so this use of scratch2 does not cause difference with
     // respect to register content between debug and release mode.
     ldr(scratch2, MemOperand(scratch1));
     cmp(result, scratch2);
     Check(eq, "Unexpected allocation top");
-#endif
   }
 
   // Calculate new top and bail out if new space is exhausted. Use result
@@ -857,7 +866,11 @@
   cmp(result, Operand(scratch2));
   b(hi, gc_required);
 
-  // Update allocation top. result temporarily holds the new top,
+  // Update allocation top. result temporarily holds the new top.
+  if (FLAG_debug_code) {
+    tst(result, Operand(kObjectAlignmentMask));
+    Check(eq, "Unaligned allocation in new space");
+  }
   str(result, MemOperand(scratch1));
 
   // Adjust back to start of new object.
@@ -1153,6 +1166,9 @@
     RecordComment(msg);
   }
 #endif
+  // Disable stub call restrictions to always allow calls to abort.
+  set_allow_stub_calls(true);
+
   mov(r0, Operand(p0));
   push(r0);
   mov(r0, Operand(Smi::FromInt(p1 - p0)));
@@ -1162,6 +1178,26 @@
 }
 
 
+void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
+  if (context_chain_length > 0) {
+    // Move up the chain of contexts to the context containing the slot.
+    ldr(dst, MemOperand(cp, Context::SlotOffset(Context::CLOSURE_INDEX)));
+    // Load the function context (which is the incoming, outer context).
+    ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
+    for (int i = 1; i < context_chain_length; i++) {
+      ldr(dst, MemOperand(dst, Context::SlotOffset(Context::CLOSURE_INDEX)));
+      ldr(dst, FieldMemOperand(dst, JSFunction::kContextOffset));
+    }
+    // The context may be an intermediate context, not a function context.
+    ldr(dst, MemOperand(dst, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+  } else {  // Slot is in the current function context.
+    // The context may be an intermediate context, not a function context.
+    ldr(dst, MemOperand(cp, Context::SlotOffset(Context::FCONTEXT_INDEX)));
+  }
+}
+
+
+
 #ifdef ENABLE_DEBUGGER_SUPPORT
 CodePatcher::CodePatcher(byte* address, int instructions)
     : address_(address),
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 8c247bf..0974329 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -79,6 +79,11 @@
   void RecordWrite(Register object, Register offset, Register scratch);
 
   // ---------------------------------------------------------------------------
+  // Stack limit support
+
+  void StackLimitCheck(Label* on_stack_limit_hit);
+
+  // ---------------------------------------------------------------------------
   // Activation frames
 
   void EnterInternalFrame() { EnterFrame(StackFrame::INTERNAL); }
@@ -99,6 +104,8 @@
   // Align the stack by optionally pushing a Smi zero.
   void AlignStack(int offset);
 
+  void LoadContext(Register dst, int context_chain_length);
+
   // ---------------------------------------------------------------------------
   // JavaScript invokes
 
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index 8282655..efccaf4 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -105,7 +105,7 @@
   __ b(eq, &miss);
 
   // Get the map of the receiver and compute the hash.
-  __ ldr(scratch, FieldMemOperand(name, String::kLengthOffset));
+  __ ldr(scratch, FieldMemOperand(name, String::kHashFieldOffset));
   __ ldr(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
   __ add(scratch, scratch, Operand(ip));
   __ eor(scratch, scratch, Operand(flags));
@@ -229,10 +229,7 @@
                       miss, &check_wrapper);
 
   // Load length directly from the string.
-  __ and_(scratch1, scratch1, Operand(kStringSizeMask));
-  __ add(scratch1, scratch1, Operand(String::kHashShift));
   __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
-  __ mov(r0, Operand(r0, LSR, scratch1));
   __ mov(r0, Operand(r0, LSL, kSmiTagSize));
   __ Ret();