Version 3.9.17

Fixed VFP detection through compiler defines. (issue 1996)

Add Code-related fields to postmortem metadata.

Performance and stability improvements on all platforms.

git-svn-id: http://v8.googlecode.com/svn/trunk@11000 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/SConscript b/src/SConscript
index 40b2c54..fde7a80 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -59,6 +59,7 @@
     counters.cc
     cpu-profiler.cc
     data-flow.cc
+    date.cc
     dateparser.cc
     debug-agent.cc
     debug.cc
diff --git a/src/allocation.h b/src/allocation.h
index 69e72bd..31067dd 100644
--- a/src/allocation.h
+++ b/src/allocation.h
@@ -80,7 +80,7 @@
 
 
 template <typename T>
-T* NewArray(int size) {
+T* NewArray(size_t size) {
   T* result = new T[size];
   if (result == NULL) Malloced::FatalProcessOutOfMemory();
   return result;
diff --git a/src/api.cc b/src/api.cc
index ede6018..d8c7ba0 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -4735,8 +4735,8 @@
   if (IsDeadCheck(isolate, "v8::Date::NumberValue()")) return 0;
   LOG_API(isolate, "Date::NumberValue");
   i::Handle<i::Object> obj = Utils::OpenHandle(this);
-  i::Handle<i::JSValue> jsvalue = i::Handle<i::JSValue>::cast(obj);
-  return jsvalue->value()->Number();
+  i::Handle<i::JSDate> jsdate = i::Handle<i::JSDate>::cast(obj);
+  return jsdate->value()->Number();
 }
 
 
@@ -4747,8 +4747,10 @@
   LOG_API(isolate, "Date::DateTimeConfigurationChangeNotification");
   ENTER_V8(isolate);
 
+  isolate->date_cache()->ResetDateCache();
+
   i::HandleScope scope(isolate);
-  // Get the function ResetDateCache (defined in date-delay.js).
+  // Get the function ResetDateCache (defined in date.js).
   i::Handle<i::String> func_name_str =
       isolate->factory()->LookupAsciiSymbol("ResetDateCache");
   i::MaybeObject* result =
@@ -5979,7 +5981,7 @@
   i::Isolate* isolate = i::Isolate::Current();
   IsDeadCheck(isolate, "v8::HeapSnapshot::GetNodeById");
   return reinterpret_cast<const HeapGraphNode*>(
-      ToInternal(this)->GetEntryById(id));
+      ToInternal(this)->GetEntryById(static_cast<i::SnapshotObjectId>(id)));
 }
 
 
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index 2592236..ff15221 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -66,11 +66,13 @@
 
 #ifdef __arm__
   // If the compiler is allowed to use VFP then we can use VFP too in our code
-  // generation even when generating snapshots.  This won't work for cross
-  // compilation. VFPv3 implies ARMv7, see ARM DDI 0406B, page A1-6.
-#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+  // generation even when generating snapshots. ARMv7 and hardware floating
+  // point support implies VFPv3, see ARM DDI 0406B, page A1-6.
+#if defined(CAN_USE_ARMV7_INSTRUCTIONS) && defined(__VFP_FP__) \
+    && !defined(__SOFTFP__)
   answer |= 1u << VFP3 | 1u << ARMv7;
-#endif  // defined(__VFP_FP__) && !defined(__SOFTFP__)
+#endif  // defined(CAN_USE_ARMV7_INSTRUCTIONS) && defined(__VFP_FP__)
+        // && !defined(__SOFTFP__)
 #endif  // def __arm__
 
   return answer;
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index 3a4ce74..250f020 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -6661,6 +6661,8 @@
   ASSERT(state_ == CompareIC::STRINGS);
   Label miss;
 
+  bool equality = Token::IsEqualityOp(op_);
+
   // Registers containing left and right operands respectively.
   Register left = r1;
   Register right = r0;
@@ -6694,28 +6696,39 @@
 
   // Check that both strings are symbols. If they are, we're done
   // because we already know they are not identical.
-  ASSERT(GetCondition() == eq);
-  STATIC_ASSERT(kSymbolTag != 0);
-  __ and_(tmp3, tmp1, Operand(tmp2));
-  __ tst(tmp3, Operand(kIsSymbolMask));
-  // Make sure r0 is non-zero. At this point input operands are
-  // guaranteed to be non-zero.
-  ASSERT(right.is(r0));
-  __ Ret(ne);
+  if (equality) {
+    ASSERT(GetCondition() == eq);
+    STATIC_ASSERT(kSymbolTag != 0);
+    __ and_(tmp3, tmp1, Operand(tmp2));
+    __ tst(tmp3, Operand(kIsSymbolMask));
+    // Make sure r0 is non-zero. At this point input operands are
+    // guaranteed to be non-zero.
+    ASSERT(right.is(r0));
+    __ Ret(ne);
+  }
 
   // Check that both strings are sequential ASCII.
   Label runtime;
-  __ JumpIfBothInstanceTypesAreNotSequentialAscii(tmp1, tmp2, tmp3, tmp4,
-                                                  &runtime);
+  __ JumpIfBothInstanceTypesAreNotSequentialAscii(
+      tmp1, tmp2, tmp3, tmp4, &runtime);
 
   // Compare flat ASCII strings. Returns when done.
-  StringCompareStub::GenerateFlatAsciiStringEquals(
-      masm, left, right, tmp1, tmp2, tmp3);
+  if (equality) {
+    StringCompareStub::GenerateFlatAsciiStringEquals(
+        masm, left, right, tmp1, tmp2, tmp3);
+  } else {
+    StringCompareStub::GenerateCompareFlatAsciiStrings(
+        masm, left, right, tmp1, tmp2, tmp3, tmp4);
+  }
 
   // Handle more complex cases in runtime.
   __ bind(&runtime);
   __ Push(left, right);
-  __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  if (equality) {
+    __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  } else {
+    __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+  }
 
   __ bind(&miss);
   GenerateMiss(masm);
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 303f0b0..77f4e44 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -2947,6 +2947,50 @@
 }
 
 
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  ASSERT(args->length() == 2);
+  ASSERT_NE(NULL, args->at(1)->AsLiteral());
+  Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
+
+  VisitForAccumulatorValue(args->at(0));  // Load the object.
+
+  Label runtime, done;
+  Register object = r0;
+  Register result = r0;
+  Register scratch0 = r9;
+  Register scratch1 = r1;
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CompareObjectType(object, scratch1, scratch1, JS_DATE_TYPE);
+  __ Assert(eq, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ mov(scratch1, Operand(stamp));
+      __ ldr(scratch1, MemOperand(scratch1));
+      __ ldr(scratch0, FieldMemOperand(object, JSDate::kCacheStampOffset));
+      __ cmp(scratch1, scratch0);
+      __ b(ne, &runtime);
+      __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
+                                             kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2, scratch1);
+    __ mov(r1, Operand(index));
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ bind(&done);
+  }
+  context()->Plug(r0);
+}
+
+
 void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
   // Load the arguments on the stack and call the runtime function.
   ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index 108a6de..36421d9 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1602,6 +1602,13 @@
 }
 
 
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+  LOperand* object = UseFixed(instr->value(), r0);
+  LDateField* result = new LDateField(object, FixedTemp(r1), instr->index());
+  return MarkAsCall(DefineFixed(result, r0), instr);
+}
+
+
 LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
   LOperand* value = UseRegisterAtStart(instr->index());
   LOperand* length = UseRegister(instr->length());
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 35ed67d..ae19677 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -177,8 +177,8 @@
   V(ForInPrepareMap)                            \
   V(ForInCacheArray)                            \
   V(CheckMapValue)                              \
-  V(LoadFieldByIndex)
-
+  V(LoadFieldByIndex)                           \
+  V(DateField)
 
 
 #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic)              \
@@ -990,6 +990,41 @@
 };
 
 
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+  LDateField(LOperand* date, LOperand* temp, Smi* index) : index_(index) {
+    inputs_[0] = date;
+    temps_[0] = temp;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+  DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+  Smi* index() const { return index_; }
+
+ private:
+  Smi* index_;
+};
+
+
+class LSetDateField: public LTemplateInstruction<1, 2, 1> {
+ public:
+  LSetDateField(LOperand* date, LOperand* value, LOperand* temp, int index)
+      : index_(index) {
+    inputs_[0] = date;
+    inputs_[1] = value;
+    temps_[0] = temp;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(DateField, "date-set-field")
+  DECLARE_HYDROGEN_ACCESSOR(DateField)
+
+  int index() const { return index_; }
+
+ private:
+  int index_;
+};
+
+
 class LThrow: public LTemplateInstruction<0, 1, 0> {
  public:
   explicit LThrow(LOperand* value) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 2a707a8..012ea45 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -1438,6 +1438,46 @@
 }
 
 
+void LCodeGen::DoDateField(LDateField* instr) {
+  Register object = ToRegister(instr->InputAt(0));
+  Register result = ToRegister(instr->result());
+  Register scratch = ToRegister(instr->TempAt(0));
+  Smi* index = instr->index();
+  Label runtime, done;
+  ASSERT(object.is(result));
+  ASSERT(object.is(r0));
+  ASSERT(!scratch.is(scratch0()));
+  ASSERT(!scratch.is(object));
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CompareObjectType(object, scratch, scratch, JS_DATE_TYPE);
+  __ Assert(eq, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ mov(scratch, Operand(stamp));
+      __ ldr(scratch, MemOperand(scratch));
+      __ ldr(scratch0(), FieldMemOperand(object, JSDate::kCacheStampOffset));
+      __ cmp(scratch, scratch0());
+      __ b(ne, &runtime);
+      __ ldr(result, FieldMemOperand(object, JSDate::kValueOffset +
+                                             kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2, scratch);
+    __ mov(r1, Operand(index));
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ bind(&done);
+  }
+}
+
+
 void LCodeGen::DoBitNotI(LBitNotI* instr) {
   Register input = ToRegister(instr->InputAt(0));
   Register result = ToRegister(instr->result());
diff --git a/src/assembler.cc b/src/assembler.cc
index 0bec577..07509b5 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -813,6 +813,17 @@
 }
 
 
+ExternalReference ExternalReference::get_date_field_function(
+    Isolate* isolate) {
+  return ExternalReference(Redirect(isolate, FUNCTION_ADDR(JSDate::GetField)));
+}
+
+
+ExternalReference ExternalReference::date_cache_stamp(Isolate* isolate) {
+  return ExternalReference(isolate->date_cache()->stamp_address());
+}
+
+
 ExternalReference ExternalReference::transcendental_cache_array_address(
     Isolate* isolate) {
   return ExternalReference(
diff --git a/src/assembler.h b/src/assembler.h
index e7c92b4..5063879 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -595,6 +595,9 @@
   static ExternalReference transcendental_cache_array_address(Isolate* isolate);
   static ExternalReference delete_handle_scope_extensions(Isolate* isolate);
 
+  static ExternalReference get_date_field_function(Isolate* isolate);
+  static ExternalReference date_cache_stamp(Isolate* isolate);
+
   // Deoptimization support.
   static ExternalReference new_deoptimizer_function(Isolate* isolate);
   static ExternalReference compute_output_frames_function(Isolate* isolate);
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 19a6a15..a48bb4d 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -927,7 +927,7 @@
   {  // --- D a t e ---
     // Builtin functions for Date.prototype.
     Handle<JSFunction> date_fun =
-        InstallFunction(global, "Date", JS_VALUE_TYPE, JSValue::kSize,
+        InstallFunction(global, "Date", JS_DATE_TYPE, JSDate::kSize,
                         isolate->initial_object_prototype(),
                         Builtins::kIllegal, true);
 
diff --git a/src/builtins.cc b/src/builtins.cc
index 7290a2c..ca202f2 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -310,28 +310,6 @@
 }
 
 
-static void CopyElements(Heap* heap,
-                         AssertNoAllocation* no_gc,
-                         FixedArray* dst,
-                         int dst_index,
-                         FixedArray* src,
-                         int src_index,
-                         int len) {
-  if (len == 0) return;
-  ASSERT(dst != src);  // Use MoveElements instead.
-  ASSERT(dst->map() != HEAP->fixed_cow_array_map());
-  ASSERT(len > 0);
-  CopyWords(dst->data_start() + dst_index,
-            src->data_start() + src_index,
-            len);
-  WriteBarrierMode mode = dst->GetWriteBarrierMode(*no_gc);
-  if (mode == UPDATE_WRITE_BARRIER) {
-    heap->RecordWrites(dst->address(), dst->OffsetOfElementAt(dst_index), len);
-  }
-  heap->incremental_marking()->RecordWrites(dst);
-}
-
-
 static void MoveElements(Heap* heap,
                          AssertNoAllocation* no_gc,
                          FixedArray* dst,
@@ -531,7 +509,8 @@
     FixedArray* new_elms = FixedArray::cast(obj);
 
     AssertNoAllocation no_gc;
-    CopyElements(heap, &no_gc, new_elms, 0, elms, 0, len);
+    CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+                               new_elms, FAST_ELEMENTS, 0, len);
     FillWithHoles(heap, new_elms, new_length, capacity);
 
     elms = new_elms;
@@ -667,7 +646,8 @@
     }
     FixedArray* new_elms = FixedArray::cast(obj);
     AssertNoAllocation no_gc;
-    CopyElements(heap, &no_gc, new_elms, to_add, elms, 0, len);
+    CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+                               new_elms, FAST_ELEMENTS, to_add, len);
     FillWithHoles(heap, new_elms, new_length, capacity);
     elms = new_elms;
     array->set_elements(elms);
@@ -778,8 +758,9 @@
   if (!maybe_array->To(&result_array)) return maybe_array;
 
   AssertNoAllocation no_gc;
-  CopyElements(heap, &no_gc, FixedArray::cast(result_array->elements()), 0,
-               elms, k, result_len);
+  CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, k,
+                             FixedArray::cast(result_array->elements()),
+                             FAST_ELEMENTS, 0, result_len);
 
   return result_array;
 }
@@ -852,11 +833,9 @@
   {
     AssertNoAllocation no_gc;
     // Fill newly created array.
-    CopyElements(heap,
-                 &no_gc,
-                 FixedArray::cast(result_array->elements()), 0,
-                 elms, actual_start,
-                 actual_delete_count);
+    CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, actual_start,
+                               FixedArray::cast(result_array->elements()),
+                               FAST_ELEMENTS, 0, actual_delete_count);
   }
 
   int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0;
@@ -906,12 +885,13 @@
       {
         AssertNoAllocation no_gc;
         // Copy the part before actual_start as is.
-        CopyElements(heap, &no_gc, new_elms, 0, elms, 0, actual_start);
+        CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+                                   new_elms, FAST_ELEMENTS, 0, actual_start);
         const int to_copy = len - actual_delete_count - actual_start;
-        CopyElements(heap, &no_gc,
-                     new_elms, actual_start + item_count,
-                     elms, actual_start + actual_delete_count,
-                     to_copy);
+        CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS,
+                                   actual_start + actual_delete_count,
+                                   new_elms, FAST_ELEMENTS,
+                                   actual_start + item_count, to_copy);
       }
 
       FillWithHoles(heap, new_elms, new_length, capacity);
@@ -1000,7 +980,9 @@
     JSArray* array = JSArray::cast(args[i]);
     int len = Smi::cast(array->length())->value();
     FixedArray* elms = FixedArray::cast(array->elements());
-    CopyElements(heap, &no_gc, result_elms, start_pos, elms, 0, len);
+    CopyObjectToObjectElements(&no_gc, elms, FAST_ELEMENTS, 0,
+                               result_elms, FAST_ELEMENTS,
+                               start_pos, len);
     start_pos += len;
   }
   ASSERT(start_pos == result_len);
diff --git a/src/compiler.cc b/src/compiler.cc
index 520db34..d689e87 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -116,7 +116,6 @@
 bool CompilationInfo::ShouldSelfOptimize() {
   return FLAG_self_optimization &&
       FLAG_crankshaft &&
-      !Serializer::enabled() &&
       !function()->flags()->Contains(kDontSelfOptimize) &&
       !function()->flags()->Contains(kDontOptimize) &&
       function()->scope()->allows_lazy_recompilation() &&
diff --git a/src/date.cc b/src/date.cc
new file mode 100644
index 0000000..a377451
--- /dev/null
+++ b/src/date.cc
@@ -0,0 +1,384 @@
+// Copyright 2012 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.
+
+#include "date.h"
+
+#include "v8.h"
+
+#include "objects.h"
+#include "objects-inl.h"
+
+namespace v8 {
+namespace internal {
+
+
+static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
+static const int kDaysIn4Years = 4 * 365 + 1;
+static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
+static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
+static const int kDays1970to2000 = 30 * 365 + 7;
+static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
+                               kDays1970to2000;
+static const int kYearsOffset = 400000;
+static const char kDaysInMonths[] =
+    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+
+void DateCache::ResetDateCache() {
+  static const int kMaxStamp = Smi::kMaxValue;
+  stamp_ = Smi::FromInt(stamp_->value() + 1);
+  if (stamp_->value() > kMaxStamp) {
+    stamp_ = Smi::FromInt(0);
+  }
+  ASSERT(stamp_ != Smi::FromInt(kInvalidStamp));
+  for (int i = 0; i < kDSTSize; ++i) {
+    ClearSegment(&dst_[i]);
+  }
+  dst_usage_counter_ = 0;
+  before_ = &dst_[0];
+  after_ = &dst_[1];
+  local_offset_ms_ = kInvalidLocalOffsetInMs;
+  ymd_valid_ = false;
+}
+
+
+void DateCache::ClearSegment(DST* segment) {
+  segment->start_sec = kMaxEpochTimeInSec;
+  segment->end_sec = -kMaxEpochTimeInSec;
+  segment->offset_ms = 0;
+  segment->last_used = 0;
+}
+
+
+void DateCache::YearMonthDayFromDays(
+    int days, int* year, int* month, int* day) {
+  if (ymd_valid_) {
+    // Check conservatively if the given 'days' has
+    // the same year and month as the cached 'days'.
+    int new_day = ymd_day_ + (days - ymd_days_);
+    if (new_day >= 1 && new_day <= 28) {
+      ymd_day_ = new_day;
+      ymd_days_ = days;
+      *year = ymd_year_;
+      *month = ymd_month_;
+      *day = new_day;
+      return;
+    }
+  }
+  int save_days = days;
+
+  days += kDaysOffset;
+  *year = 400 * (days / kDaysIn400Years) - kYearsOffset;
+  days %= kDaysIn400Years;
+
+  ASSERT(DaysFromYearMonth(*year, 0) + days == save_days);
+
+  days--;
+  int yd1 = days / kDaysIn100Years;
+  days %= kDaysIn100Years;
+  *year += 100 * yd1;
+
+  days++;
+  int yd2 = days / kDaysIn4Years;
+  days %= kDaysIn4Years;
+  *year += 4 * yd2;
+
+  days--;
+  int yd3 = days / 365;
+  days %= 365;
+  *year += yd3;
+
+
+  bool is_leap = (!yd1 || yd2) && !yd3;
+
+  ASSERT(days >= -1);
+  ASSERT(is_leap || (days >= 0));
+  ASSERT((days < 365) || (is_leap && (days < 366)));
+  ASSERT(is_leap == ((*year % 4 == 0) && (*year % 100 || (*year % 400 == 0))));
+  ASSERT(is_leap || ((DaysFromYearMonth(*year, 0) + days) == save_days));
+  ASSERT(!is_leap || ((DaysFromYearMonth(*year, 0) + days + 1) == save_days));
+
+  days += is_leap;
+
+  // Check if the date is after February.
+  if (days >= 31 + 28 + is_leap) {
+    days -= 31 + 28 + is_leap;
+    // Find the date starting from March.
+    for (int i = 2; i < 12; i++) {
+      if (days < kDaysInMonths[i]) {
+        *month = i;
+        *day = days + 1;
+        break;
+      }
+      days -= kDaysInMonths[i];
+    }
+  } else {
+    // Check January and February.
+    if (days < 31) {
+      *month = 0;
+      *day = days + 1;
+    } else {
+      *month = 1;
+      *day = days - 31 + 1;
+    }
+  }
+  ASSERT(DaysFromYearMonth(*year, *month) + *day - 1 == save_days);
+  ymd_valid_ = true;
+  ymd_year_ = *year;
+  ymd_month_ = *month;
+  ymd_day_ = *day;
+  ymd_days_ = save_days;
+}
+
+
+int DateCache::DaysFromYearMonth(int year, int month) {
+  static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
+                                       181, 212, 243, 273, 304, 334};
+  static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
+                                            182, 213, 244, 274, 305, 335};
+
+  year += month / 12;
+  month %= 12;
+  if (month < 0) {
+    year--;
+    month += 12;
+  }
+
+  ASSERT(month >= 0);
+  ASSERT(month < 12);
+
+  // year_delta is an arbitrary number such that:
+  // a) year_delta = -1 (mod 400)
+  // b) year + year_delta > 0 for years in the range defined by
+  //    ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
+  //    Jan 1 1970. This is required so that we don't run into integer
+  //    division of negative numbers.
+  // c) there shouldn't be an overflow for 32-bit integers in the following
+  //    operations.
+  static const int year_delta = 399999;
+  static const int base_day = 365 * (1970 + year_delta) +
+                              (1970 + year_delta) / 4 -
+                              (1970 + year_delta) / 100 +
+                              (1970 + year_delta) / 400;
+
+  int year1 = year + year_delta;
+  int day_from_year = 365 * year1 +
+                      year1 / 4 -
+                      year1 / 100 +
+                      year1 / 400 -
+                      base_day;
+
+  if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
+    return day_from_year + day_from_month[month];
+  }
+  return day_from_year + day_from_month_leap[month];
+}
+
+
+void DateCache::ExtendTheAfterSegment(int time_sec, int offset_ms) {
+  if (after_->offset_ms == offset_ms &&
+      after_->start_sec <= time_sec + kDefaultDSTDeltaInSec &&
+      time_sec <= after_->end_sec) {
+    // Extend the after_ segment.
+    after_->start_sec = time_sec;
+  } else {
+    // The after_ segment is either invalid or starts too late.
+    if (after_->start_sec <= after_->end_sec) {
+      // If the after_ segment is valid, replace it with a new segment.
+      after_ = LeastRecentlyUsedDST(before_);
+    }
+    after_->start_sec = time_sec;
+    after_->end_sec = time_sec;
+    after_->offset_ms = offset_ms;
+    after_->last_used = ++dst_usage_counter_;
+  }
+}
+
+
+int DateCache::DaylightSavingsOffsetInMs(int64_t time_ms) {
+  int time_sec = (time_ms >= 0 && time_ms <= kMaxEpochTimeInMs)
+      ? static_cast<int>(time_ms / 1000)
+      : static_cast<int>(EquivalentTime(time_ms) / 1000);
+
+  // Invalidate cache if the usage counter is close to overflow.
+  // Note that dst_usage_counter is incremented less than ten times
+  // in this function.
+  if (dst_usage_counter_ >= kMaxInt - 10) {
+    dst_usage_counter_ = 0;
+    for (int i = 0; i < kDSTSize; ++i) {
+      ClearSegment(&dst_[i]);
+    }
+  }
+
+  // Optimistic fast check.
+  if (before_->start_sec <= time_sec &&
+      time_sec <= before_->end_sec) {
+    // Cache hit.
+    before_->last_used = ++dst_usage_counter_;
+    return before_->offset_ms;
+  }
+
+  ProbeDST(time_sec);
+
+  ASSERT(InvalidSegment(before_) || before_->start_sec <= time_sec);
+  ASSERT(InvalidSegment(after_) || time_sec < after_->start_sec);
+
+  if (InvalidSegment(before_)) {
+    // Cache miss.
+    before_->start_sec = time_sec;
+    before_->end_sec = time_sec;
+    before_->offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
+    before_->last_used = ++dst_usage_counter_;
+    return before_->offset_ms;
+  }
+
+  if (time_sec <= before_->end_sec) {
+    // Cache hit.
+    before_->last_used = ++dst_usage_counter_;
+    return before_->offset_ms;
+  }
+
+  if (time_sec > before_->end_sec + kDefaultDSTDeltaInSec) {
+    // If the before_ segment ends too early, then just
+    // query for the offset of the time_sec
+    int offset_ms = GetDaylightSavingsOffsetFromOS(time_sec);
+    ExtendTheAfterSegment(time_sec, offset_ms);
+    // This swap helps the optimistic fast check in subsequent invocations.
+    DST* temp = before_;
+    before_ = after_;
+    after_ = temp;
+    return offset_ms;
+  }
+
+  // Now the time_sec is between
+  // before_->end_sec and before_->end_sec + default DST delta.
+  // Update the usage counter of before_ since it is going to be used.
+  before_->last_used = ++dst_usage_counter_;
+
+  // Check if after_ segment is invalid or starts too late.
+  // Note that start_sec of invalid segments is kMaxEpochTimeInSec.
+  if (before_->end_sec + kDefaultDSTDeltaInSec <= after_->start_sec) {
+    int new_after_start_sec = before_->end_sec + kDefaultDSTDeltaInSec;
+    int new_offset_ms = GetDaylightSavingsOffsetFromOS(new_after_start_sec);
+    ExtendTheAfterSegment(new_after_start_sec, new_offset_ms);
+  } else {
+    ASSERT(!InvalidSegment(after_));
+    // Update the usage counter of after_ since it is going to be used.
+    after_->last_used = ++dst_usage_counter_;
+  }
+
+  // Now the time_sec is between before_->end_sec and after_->start_sec.
+  // Only one daylight savings offset change can occur in this interval.
+
+  if (before_->offset_ms == after_->offset_ms) {
+    // Merge two segments if they have the same offset.
+    before_->end_sec = after_->end_sec;
+    ClearSegment(after_);
+    return before_->offset_ms;
+  }
+
+  // Binary search for daylight savings offset change point,
+  // but give up if we don't find it in four iterations.
+  for (int i = 4; i >= 0; --i) {
+    int delta = after_->start_sec - before_->end_sec;
+    int middle_sec = (i == 0) ? time_sec : before_->end_sec + delta / 2;
+    int offset_ms = GetDaylightSavingsOffsetFromOS(middle_sec);
+    if (before_->offset_ms == offset_ms) {
+      before_->end_sec = middle_sec;
+      if (time_sec <= before_->end_sec) {
+        return offset_ms;
+      }
+    } else {
+      ASSERT(after_->offset_ms == offset_ms);
+      after_->start_sec = middle_sec;
+      if (time_sec >= after_->start_sec) {
+        // This swap helps the optimistic fast check in subsequent invocations.
+        DST* temp = before_;
+        before_ = after_;
+        after_ = temp;
+        return offset_ms;
+      }
+    }
+  }
+  UNREACHABLE();
+  return 0;
+}
+
+
+void DateCache::ProbeDST(int time_sec) {
+  DST* before = NULL;
+  DST* after = NULL;
+  ASSERT(before_ != after_);
+
+  for (int i = 0; i < kDSTSize; ++i) {
+    if (dst_[i].start_sec <= time_sec) {
+      if (before == NULL || before->start_sec < dst_[i].start_sec) {
+        before = &dst_[i];
+      }
+    } else if (time_sec < dst_[i].end_sec) {
+      if (after == NULL || after->end_sec > dst_[i].end_sec) {
+        after = &dst_[i];
+      }
+    }
+  }
+
+  // If before or after segments were not found,
+  // then set them to any invalid segment.
+  if (before == NULL) {
+    before = InvalidSegment(before_) ? before_ : LeastRecentlyUsedDST(after);
+  }
+  if (after == NULL) {
+    after = InvalidSegment(after_) && before != after_
+            ? after_ : LeastRecentlyUsedDST(before);
+  }
+
+  ASSERT(before != NULL);
+  ASSERT(after != NULL);
+  ASSERT(before != after);
+  ASSERT(InvalidSegment(before) || before->start_sec <= time_sec);
+  ASSERT(InvalidSegment(after) || time_sec < after->start_sec);
+  ASSERT(InvalidSegment(before) || InvalidSegment(after) ||
+         before->end_sec < after->start_sec);
+
+  before_ = before;
+  after_ = after;
+}
+
+
+DateCache::DST* DateCache::LeastRecentlyUsedDST(DST* skip) {
+  DST* result = NULL;
+  for (int i = 0; i < kDSTSize; ++i) {
+    if (&dst_[i] == skip) continue;
+    if (result == NULL || result->last_used > dst_[i].last_used) {
+      result = &dst_[i];
+    }
+  }
+  ClearSegment(result);
+  return result;
+}
+
+} }  // namespace v8::internal
diff --git a/src/date.h b/src/date.h
new file mode 100644
index 0000000..fcd61db
--- /dev/null
+++ b/src/date.h
@@ -0,0 +1,260 @@
+// Copyright 2012 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.
+
+#ifndef V8_DATE_H_
+#define V8_DATE_H_
+
+#include "allocation.h"
+#include "globals.h"
+#include "platform.h"
+
+
+namespace v8 {
+namespace internal {
+
+class DateCache {
+ public:
+  static const int kMsPerMin = 60 * 1000;
+  static const int kSecPerDay = 24 * 60 * 60;
+  static const int64_t kMsPerDay = kSecPerDay * 1000;
+
+  // The largest time that can be passed to OS date-time library functions.
+  static const int kMaxEpochTimeInSec = kMaxInt;
+  static const int64_t kMaxEpochTimeInMs =
+      static_cast<int64_t>(kMaxInt) * 1000;
+
+  // The largest time that can be stored in JSDate.
+  static const int64_t kMaxTimeInMs =
+      static_cast<int64_t>(864000000) * 10000000;
+
+  // Conservative upper bound on time that can be stored in JSDate
+  // before UTC conversion.
+  static const int64_t kMaxTimeBeforeUTCInMs =
+      kMaxTimeInMs + 10 * kMsPerDay;
+
+  // Sentinel that denotes an invalid local offset.
+  static const int kInvalidLocalOffsetInMs = kMaxInt;
+  // Sentinel that denotes an invalid cache stamp.
+  // It is an invariant of DateCache that cache stamp is non-negative.
+  static const int kInvalidStamp = -1;
+
+  DateCache() : stamp_(0) {
+    ResetDateCache();
+  }
+
+  virtual ~DateCache() {}
+
+
+  // Clears cached timezone information and increments the cache stamp.
+  void ResetDateCache();
+
+
+  // Computes floor(time_ms / kMsPerDay).
+  static int DaysFromTime(int64_t time_ms) {
+    if (time_ms < 0) time_ms -= (kMsPerDay - 1);
+    return static_cast<int>(time_ms / kMsPerDay);
+  }
+
+
+  // Computes modulo(time_ms, kMsPerDay) given that
+  // days = floor(time_ms / kMsPerDay).
+  static int TimeInDay(int64_t time_ms, int days) {
+    return static_cast<int>(time_ms - days * kMsPerDay);
+  }
+
+
+  // Given the number of days since the epoch, computes the weekday.
+  // ECMA 262 - 15.9.1.6.
+  int Weekday(int days) {
+    int result = (days + 4) % 7;
+    return result >= 0 ? result : result + 7;
+  }
+
+
+  bool IsLeap(int year) {
+    return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
+  }
+
+
+  // ECMA 262 - 15.9.1.7.
+  int LocalOffsetInMs() {
+    if (local_offset_ms_ == kInvalidLocalOffsetInMs)  {
+      local_offset_ms_ = GetLocalOffsetFromOS();
+    }
+    return local_offset_ms_;
+  }
+
+
+  const char* LocalTimezone(int64_t time_ms) {
+    if (time_ms < 0 || time_ms > kMaxEpochTimeInMs) {
+      time_ms = EquivalentTime(time_ms);
+    }
+    return OS::LocalTimezone(static_cast<double>(time_ms));
+  }
+
+  // ECMA 262 - 15.9.5.26
+  int TimezoneOffset(int64_t time_ms) {
+    int64_t local_ms = ToLocal(time_ms);
+    return static_cast<int>((time_ms - local_ms) / kMsPerMin);
+  }
+
+  // ECMA 262 - 15.9.1.9
+  int64_t ToLocal(int64_t time_ms) {
+    return time_ms + LocalOffsetInMs() + DaylightSavingsOffsetInMs(time_ms);
+  }
+
+  // ECMA 262 - 15.9.1.9
+  int64_t ToUTC(int64_t time_ms) {
+    time_ms -= LocalOffsetInMs();
+    return time_ms - DaylightSavingsOffsetInMs(time_ms);
+  }
+
+
+  // Computes a time equivalent to the given time according
+  // to ECMA 262 - 15.9.1.9.
+  // The issue here is that some library calls don't work right for dates
+  // that cannot be represented using a non-negative signed 32 bit integer
+  // (measured in whole seconds based on the 1970 epoch).
+  // We solve this by mapping the time to a year with same leap-year-ness
+  // and same starting day for the year. The ECMAscript specification says
+  // we must do this, but for compatibility with other browsers, we use
+  // the actual year if it is in the range 1970..2037
+  int64_t EquivalentTime(int64_t time_ms) {
+    int days = DaysFromTime(time_ms);
+    int time_within_day_ms = static_cast<int>(time_ms - days * kMsPerDay);
+    int year, month, day;
+    YearMonthDayFromDays(days, &year, &month, &day);
+    int new_days = DaysFromYearMonth(EquivalentYear(year), month) + day - 1;
+    return static_cast<int64_t>(new_days) * kMsPerDay + time_within_day_ms;
+  }
+
+  // Returns an equivalent year in the range [2008-2035] matching
+  // - leap year,
+  // - week day of first day.
+  // ECMA 262 - 15.9.1.9.
+  int EquivalentYear(int year) {
+    int week_day = Weekday(DaysFromYearMonth(year, 0));
+    int recent_year = (IsLeap(year) ? 1956 : 1967) + (week_day * 12) % 28;
+    // Find the year in the range 2008..2037 that is equivalent mod 28.
+    // Add 3*28 to give a positive argument to the modulus operator.
+    return 2008 + (recent_year + 3 * 28 - 2008) % 28;
+  }
+
+  // Given the number of days since the epoch, computes
+  // the corresponding year, month, and day.
+  void YearMonthDayFromDays(int days, int* year, int* month, int* day);
+
+  // Computes the number of days since the epoch for
+  // the first day of the given month in the given year.
+  int DaysFromYearMonth(int year, int month);
+
+  // Cache stamp is used for invalidating caches in JSDate.
+  // We increment the stamp each time when the timezone information changes.
+  // JSDate objects perform stamp check and invalidate their caches if
+  // their saved stamp is not equal to the current stamp.
+  Smi* stamp() { return stamp_; }
+  void* stamp_address() { return &stamp_; }
+
+  // These functions are virtual so that we can override them when testing.
+  virtual int GetDaylightSavingsOffsetFromOS(int64_t time_sec) {
+    double time_ms = static_cast<double>(time_sec * 1000);
+    return static_cast<int>(OS::DaylightSavingsOffset(time_ms));
+  }
+
+  virtual int GetLocalOffsetFromOS() {
+    double offset = OS::LocalTimeOffset();
+    ASSERT(offset < kInvalidLocalOffsetInMs);
+    return static_cast<int>(offset);
+  }
+
+ private:
+  // The implementation relies on the fact that no time zones have
+  // more than one daylight savings offset change per 19 days.
+  // In Egypt in 2010 they decided to suspend DST during Ramadan. This
+  // led to a short interval where DST is in effect from September 10 to
+  // September 30.
+  static const int kDefaultDSTDeltaInSec = 19 * kSecPerDay;
+
+  // Size of the Daylight Savings Time cache.
+  static const int kDSTSize = 32;
+
+  // Daylight Savings Time segment stores a segment of time where
+  // daylight savings offset does not change.
+  struct DST {
+    int start_sec;
+    int end_sec;
+    int offset_ms;
+    int last_used;
+  };
+
+  // Computes the daylight savings offset for the given time.
+  // ECMA 262 - 15.9.1.8
+  int DaylightSavingsOffsetInMs(int64_t time_ms);
+
+  // Sets the before_ and the after_ segments from the DST cache such that
+  // the before_ segment starts earlier than the given time and
+  // the after_ segment start later than the given time.
+  // Both segments might be invalid.
+  // The last_used counters of the before_ and after_ are updated.
+  void ProbeDST(int time_sec);
+
+  // Finds the least recently used segment from the DST cache that is not
+  // equal to the given 'skip' segment.
+  DST* LeastRecentlyUsedDST(DST* skip);
+
+  // Extends the after_ segment with the given point or resets it
+  // if it starts later than the given time + kDefaultDSTDeltaInSec.
+  inline void ExtendTheAfterSegment(int time_sec, int offset_ms);
+
+  // Makes the given segment invalid.
+  inline void ClearSegment(DST* segment);
+
+  bool InvalidSegment(DST* segment) {
+    return segment->start_sec > segment->end_sec;
+  }
+
+  Smi* stamp_;
+
+  // Daylight Saving Time cache.
+  DST dst_[kDSTSize];
+  int dst_usage_counter_;
+  DST* before_;
+  DST* after_;
+
+  int local_offset_ms_;
+
+  // Year/Month/Day cache.
+  bool ymd_valid_;
+  int ymd_days_;
+  int ymd_year_;
+  int ymd_month_;
+  int ymd_day_;
+};
+
+} }   // namespace v8::internal
+
+#endif
diff --git a/src/date.js b/src/date.js
index 8c51a93..75edf6d 100644
--- a/src/date.js
+++ b/src/date.js
@@ -44,173 +44,6 @@
   throw new $TypeError('this is not a Date object.');
 }
 
-// ECMA 262 - 5.2
-function Modulo(value, remainder) {
-  var mod = value % remainder;
-  // Guard against returning -0.
-  if (mod == 0) return 0;
-  return mod >= 0 ? mod : mod + remainder;
-}
-
-
-function TimeWithinDay(time) {
-  return Modulo(time, msPerDay);
-}
-
-
-// ECMA 262 - 15.9.1.3
-function DaysInYear(year) {
-  if (year % 4 != 0) return 365;
-  if ((year % 100 == 0) && (year % 400 != 0)) return 365;
-  return 366;
-}
-
-
-function DayFromYear(year) {
-  return 365 * (year-1970)
-      + FLOOR((year-1969)/4)
-      - FLOOR((year-1901)/100)
-      + FLOOR((year-1601)/400);
-}
-
-
-function TimeFromYear(year) {
-  return msPerDay * DayFromYear(year);
-}
-
-
-function InLeapYear(time) {
-  return DaysInYear(YearFromTime(time)) - 365;  // Returns 1 or 0.
-}
-
-
-// ECMA 262 - 15.9.1.9
-function EquivalentYear(year) {
-  // Returns an equivalent year in the range [2008-2035] matching
-  // - leap year.
-  // - week day of first day.
-  var time = TimeFromYear(year);
-  var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
-      (WeekDay(time) * 12) % 28;
-  // Find the year in the range 2008..2037 that is equivalent mod 28.
-  // Add 3*28 to give a positive argument to the modulus operator.
-  return 2008 + (recent_year + 3*28 - 2008) % 28;
-}
-
-
-function EquivalentTime(t) {
-  // The issue here is that some library calls don't work right for dates
-  // that cannot be represented using a non-negative signed 32 bit integer
-  // (measured in whole seconds based on the 1970 epoch).
-  // We solve this by mapping the time to a year with same leap-year-ness
-  // and same starting day for the year.  The ECMAscript specification says
-  // we must do this, but for compatibility with other browsers, we use
-  // the actual year if it is in the range 1970..2037
-  if (t >= 0 && t <= 2.1e12) return t;
-
-  var day = MakeDay(EquivalentYear(YearFromTime(t)),
-                    MonthFromTime(t),
-                    DateFromTime(t));
-  return MakeDate(day, TimeWithinDay(t));
-}
-
-
-// local_time_offset is initialized when the DST_offset_cache is missed.
-// It must not be used until after a call to DaylightSavingsOffset().
-// In this way, only one check, for a DST cache miss, is needed.
-var local_time_offset;
-
-
-// Because computing the DST offset is an expensive operation,
-// we keep a cache of the last computed DST offset along with a time interval
-// where we know the cache is valid.
-// When the cache is valid, local_time_offset is also valid.
-var DST_offset_cache = {
-  // Cached DST offset.
-  offset: 0,
-  // Time interval where the cached offset is valid.
-  start: 0, end: -1,
-  // Size of next interval expansion.
-  increment: 0,
-  initial_increment: 19 * msPerDay
-};
-
-
-// NOTE: The implementation relies on the fact that no time zones have
-// more than one daylight savings offset change per 19 days.
-//
-// In Egypt in 2010 they decided to suspend DST during Ramadan. This
-// led to a short interval where DST is in effect from September 10 to
-// September 30.
-//
-// If this function is called with NaN it returns NaN.
-function DaylightSavingsOffset(t) {
-  // Load the cache object from the builtins object.
-  var cache = DST_offset_cache;
-
-  // Cache the start and the end in local variables for fast access.
-  var start = cache.start;
-  var end = cache.end;
-
-  if (start <= t) {
-    // If the time fits in the cached interval, return the cached offset.
-    if (t <= end) return cache.offset;
-
-    // If the cache misses, the local_time_offset may not be initialized.
-    if (IS_UNDEFINED(local_time_offset)) {
-      local_time_offset = %DateLocalTimeOffset();
-    }
-
-    // Compute a possible new interval end.
-    var new_end = end + cache.increment;
-
-    if (t <= new_end) {
-      var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
-      if (cache.offset == end_offset) {
-        // If the offset at the end of the new interval still matches
-        // the offset in the cache, we grow the cached time interval
-        // and return the offset.
-        cache.end = new_end;
-        cache.increment = cache.initial_increment;
-        return end_offset;
-      } else {
-        var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
-        if (offset == end_offset) {
-          // The offset at the given time is equal to the offset at the
-          // new end of the interval, so that means that we've just skipped
-          // the point in time where the DST offset change occurred. Updated
-          // the interval to reflect this and reset the increment.
-          cache.start = t;
-          cache.end = new_end;
-          cache.increment = cache.initial_increment;
-        } else {
-          // The interval contains a DST offset change and the given time is
-          // before it. Adjust the increment to avoid a linear search for
-          // the offset change point and change the end of the interval.
-          cache.increment /= 3;
-          cache.end = t;
-        }
-        // Update the offset in the cache and return it.
-        cache.offset = offset;
-        return offset;
-      }
-    }
-  }
-
-  // If the cache misses, the local_time_offset may not be initialized.
-  if (IS_UNDEFINED(local_time_offset)) {
-    local_time_offset = %DateLocalTimeOffset();
-  }
-  // Compute the DST offset for the time and shrink the cache interval
-  // to only contain the time. This allows fast repeated DST offset
-  // computations for the same time.
-  var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
-  cache.offset = offset;
-  cache.start = cache.end = t;
-  cache.increment = cache.initial_increment;
-  return offset;
-}
-
 
 var timezone_cache_time = $NaN;
 var timezone_cache_timezone;
@@ -220,57 +53,18 @@
   if (t == timezone_cache_time) {
     return timezone_cache_timezone;
   }
-  var timezone = %DateLocalTimezone(EquivalentTime(t));
+  var timezone = %DateLocalTimezone(t);
   timezone_cache_time = t;
   timezone_cache_timezone = timezone;
   return timezone;
 }
 
 
-function WeekDay(time) {
-  return Modulo(DAY(time) + 4, 7);
-}
-
-
-function LocalTime(time) {
-  if (NUMBER_IS_NAN(time)) return time;
-  // DaylightSavingsOffset called before local_time_offset used.
-  return time + DaylightSavingsOffset(time) + local_time_offset;
-}
-
-
-var ltcache = {
-  key: null,
-  val: null
-};
-
-function LocalTimeNoCheck(time) {
-  var ltc = ltcache;
-  if (%_ObjectEquals(time, ltc.key)) return ltc.val;
-
-  // Inline the DST offset cache checks for speed.
-  // The cache is hit, or DaylightSavingsOffset is called,
-  // before local_time_offset is used.
-  var cache = DST_offset_cache;
-  if (cache.start <= time && time <= cache.end) {
-    var dst_offset = cache.offset;
-  } else {
-    var dst_offset = DaylightSavingsOffset(time);
-  }
-  ltc.key = time;
-  return (ltc.val = time + local_time_offset + dst_offset);
-}
-
-
 function UTC(time) {
   if (NUMBER_IS_NAN(time)) return time;
   // local_time_offset is needed before the call to DaylightSavingsOffset,
   // so it may be uninitialized.
-  if (IS_UNDEFINED(local_time_offset)) {
-    local_time_offset = %DateLocalTimeOffset();
-  }
-  var tmp = time - local_time_offset;
-  return tmp - DaylightSavingsOffset(tmp);
+  return %DateToUTC(time);
 }
 
 
@@ -293,48 +87,6 @@
 }
 
 
-var ymd_from_time_cache = [1970, 0, 1];
-var ymd_from_time_cached_time = 0;
-
-function YearFromTime(t) {
-  if (t !== ymd_from_time_cached_time) {
-    if (!$isFinite(t)) {
-      return $NaN;
-    }
-
-    %DateYMDFromTime(t, ymd_from_time_cache);
-    ymd_from_time_cached_time = t;
-  }
-
-  return ymd_from_time_cache[0];
-}
-
-function MonthFromTime(t) {
-  if (t !== ymd_from_time_cached_time) {
-    if (!$isFinite(t)) {
-      return $NaN;
-    }
-    %DateYMDFromTime(t, ymd_from_time_cache);
-    ymd_from_time_cached_time = t;
-  }
-
-  return ymd_from_time_cache[1];
-}
-
-function DateFromTime(t) {
-  if (t !== ymd_from_time_cached_time) {
-    if (!$isFinite(t)) {
-      return $NaN;
-    }
-
-    %DateYMDFromTime(t, ymd_from_time_cache);
-    ymd_from_time_cached_time = t;
-  }
-
-  return ymd_from_time_cache[2];
-}
-
-
 // Compute number of days given a year, month, date.
 // Note that month and date can lie outside the normal range.
 //   For example:
@@ -385,9 +137,6 @@
 var Date_cache = {
   // Cached time value.
   time: $NaN,
-  // Cached year when interpreting the time as a local time. Only
-  // valid when the time matches cached time.
-  year: $NaN,
   // String input for which the cached time is valid.
   string: null
 };
@@ -404,11 +153,10 @@
   var value;
   if (argc == 0) {
     value = %DateCurrentTime();
-
+    SET_UTC_DATE_VALUE(this, value);
   } else if (argc == 1) {
     if (IS_NUMBER(year)) {
-      value = TimeClip(year);
-
+      value = year;
     } else if (IS_STRING(year)) {
       // Probe the Date cache. If we already have a time value for the
       // given time, we re-use that instead of parsing the string again.
@@ -419,7 +167,6 @@
         value = DateParse(year);
         if (!NUMBER_IS_NAN(value)) {
           cache.time = value;
-          cache.year = YearFromTime(LocalTimeNoCheck(value));
           cache.string = year;
         }
       }
@@ -433,9 +180,9 @@
       // which is the default for everything else than Date objects.
       // This makes us behave like KJS and SpiderMonkey.
       var time = ToPrimitive(year, NUMBER_HINT);
-      value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
+      value = IS_STRING(time) ? DateParse(time) : ToNumber(time);
     }
-
+    SET_UTC_DATE_VALUE(this, value);
   } else {
     year = ToNumber(year);
     month = ToNumber(month);
@@ -449,9 +196,9 @@
             TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
     var day = MakeDay(year, month, date);
     var time = MakeTime(hours, minutes, seconds, ms);
-    value = TimeClip(UTC(MakeDate(day, time)));
+    value = MakeDate(day, time);
+    SET_LOCAL_DATE_VALUE(this, value);
   }
-  %_SetValueOf(this, value);
 });
 
 
@@ -468,11 +215,11 @@
 }
 
 
-function DateString(time) {
-  return WeekDays[WeekDay(time)] + ' '
-      + Months[MonthFromTime(time)] + ' '
-      + TwoDigitString(DateFromTime(time)) + ' '
-      + YearFromTime(time);
+function DateString(date) {
+  return WeekDays[LOCAL_WEEKDAY(date)] + ' '
+      + Months[LOCAL_MONTH(date)] + ' '
+      + TwoDigitString(LOCAL_DAY(date)) + ' '
+      + LOCAL_YEAR(date);
 }
 
 
@@ -482,38 +229,32 @@
     'July', 'August', 'September', 'October', 'November', 'December'];
 
 
-function LongDateString(time) {
-  return LongWeekDays[WeekDay(time)] + ', '
-      + LongMonths[MonthFromTime(time)] + ' '
-      + TwoDigitString(DateFromTime(time)) + ', '
-      + YearFromTime(time);
+function LongDateString(date) {
+  return LongWeekDays[LOCAL_WEEKDAY(date)] + ', '
+      + LongMonths[LOCAL_MONTH(date)] + ' '
+      + TwoDigitString(LOCAL_DAY(date)) + ', '
+      + LOCAL_YEAR(date);
 }
 
 
-function TimeString(time) {
-  return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
-      + TwoDigitString(MIN_FROM_TIME(time)) + ':'
-      + TwoDigitString(SEC_FROM_TIME(time));
+function TimeString(date) {
+  return TwoDigitString(LOCAL_HOUR(date)) + ':'
+      + TwoDigitString(LOCAL_MIN(date)) + ':'
+      + TwoDigitString(LOCAL_SEC(date));
 }
 
 
-function LocalTimezoneString(time) {
-  var old_timezone = timezone_cache_timezone;
-  var timezone = LocalTimezone(time);
-  if (old_timezone && timezone != old_timezone) {
-    // If the timezone string has changed from the one that we cached,
-    // the local time offset may now be wrong. So we need to update it
-    // and try again.
-    local_time_offset = %DateLocalTimeOffset();
-    // We also need to invalidate the DST cache as the new timezone may have
-    // different DST times.
-    var dst_cache = DST_offset_cache;
-    dst_cache.start = 0;
-    dst_cache.end = -1;
-  }
+function TimeStringUTC(date) {
+  return TwoDigitString(UTC_HOUR(date)) + ':'
+      + TwoDigitString(UTC_MIN(date)) + ':'
+      + TwoDigitString(UTC_SEC(date));
+}
 
-  var timezoneOffset =
-      (DaylightSavingsOffset(time) + local_time_offset) / msPerMinute;
+
+function LocalTimezoneString(date) {
+  var timezone = LocalTimezone(UTC_DATE_VALUE(date));
+
+  var timezoneOffset = -TIMEZONE_OFFSET(date);
   var sign = (timezoneOffset >= 0) ? 1 : -1;
   var hours = FLOOR((sign * timezoneOffset)/60);
   var min   = FLOOR((sign * timezoneOffset)%60);
@@ -523,8 +264,8 @@
 }
 
 
-function DatePrintString(time) {
-  return DateString(time) + ' ' + TimeString(time);
+function DatePrintString(date) {
+  return DateString(date) + ' ' + TimeString(date);
 }
 
 // -------------------------------------------------------------------
@@ -564,7 +305,7 @@
           TO_INTEGER(year) <= 99) ? 1900 + TO_INTEGER(year) : year;
   var day = MakeDay(year, month, date);
   var time = MakeTime(hours, minutes, seconds, ms);
-  return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
+  return TimeClip(MakeDate(day, time));
 }
 
 
@@ -577,27 +318,30 @@
 
 // ECMA 262 - 15.9.5.2
 function DateToString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this)
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  var time_zone_string = LocalTimezoneString(t);  // May update local offset.
-  return DatePrintString(LocalTimeNoCheck(t)) + time_zone_string;
+  var time_zone_string = LocalTimezoneString(this)
+  return DatePrintString(this) + time_zone_string;
 }
 
 
 // ECMA 262 - 15.9.5.3
 function DateToDateString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  return DateString(LocalTimeNoCheck(t));
+  return DateString(this);
 }
 
 
 // ECMA 262 - 15.9.5.4
 function DateToTimeString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  var time_zone_string = LocalTimezoneString(t);  // May update local offset.
-  return TimeString(LocalTimeNoCheck(t)) + time_zone_string;
+  var time_zone_string = LocalTimezoneString(this);
+  return TimeString(this) + time_zone_string;
 }
 
 
@@ -609,363 +353,388 @@
 
 // ECMA 262 - 15.9.5.6
 function DateToLocaleDateString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  return LongDateString(LocalTimeNoCheck(t));
+  return LongDateString(this);
 }
 
 
 // ECMA 262 - 15.9.5.7
 function DateToLocaleTimeString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
-  var lt = LocalTimeNoCheck(t);
-  return TimeString(lt);
+  return TimeString(this);
 }
 
 
 // ECMA 262 - 15.9.5.8
 function DateValueOf() {
-  return DATE_VALUE(this);
+  CHECK_DATE(this);
+  return UTC_DATE_VALUE(this);
 }
 
 
 // ECMA 262 - 15.9.5.9
 function DateGetTime() {
-  return DATE_VALUE(this);
+  CHECK_DATE(this);
+  return UTC_DATE_VALUE(this);
 }
 
 
 // ECMA 262 - 15.9.5.10
 function DateGetFullYear() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  var cache = Date_cache;
-  if (cache.time === t) return cache.year;
-  return YearFromTime(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_YEAR(this);
 }
 
 
 // ECMA 262 - 15.9.5.11
 function DateGetUTCFullYear() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return YearFromTime(t);
+  CHECK_DATE(this);
+  return UTC_YEAR(this);
 }
 
 
 // ECMA 262 - 15.9.5.12
 function DateGetMonth() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return MonthFromTime(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_MONTH(this);
 }
 
 
 // ECMA 262 - 15.9.5.13
 function DateGetUTCMonth() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return MonthFromTime(t);
+  CHECK_DATE(this);
+  return UTC_MONTH(this);
 }
 
 
 // ECMA 262 - 15.9.5.14
 function DateGetDate() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return DateFromTime(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_DAY(this);
 }
 
 
 // ECMA 262 - 15.9.5.15
 function DateGetUTCDate() {
-  var t = DATE_VALUE(this);
-  return NAN_OR_DATE_FROM_TIME(t);
+  CHECK_DATE(this);
+  return UTC_DAY(this);
 }
 
 
 // ECMA 262 - 15.9.5.16
 function DateGetDay() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return WeekDay(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_WEEKDAY(this);
 }
 
 
 // ECMA 262 - 15.9.5.17
 function DateGetUTCDay() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return WeekDay(t);
+  CHECK_DATE(this);
+  return UTC_WEEKDAY(this);
 }
 
 
 // ECMA 262 - 15.9.5.18
 function DateGetHours() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return HOUR_FROM_TIME(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_HOUR(this);
 }
 
 
 // ECMA 262 - 15.9.5.19
 function DateGetUTCHours() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return HOUR_FROM_TIME(t);
+  CHECK_DATE(this);
+  return UTC_HOUR(this);
 }
 
 
 // ECMA 262 - 15.9.5.20
 function DateGetMinutes() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return MIN_FROM_TIME(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_MIN(this);
 }
 
 
 // ECMA 262 - 15.9.5.21
 function DateGetUTCMinutes() {
-  var t = DATE_VALUE(this);
-  return NAN_OR_MIN_FROM_TIME(t);
+  CHECK_DATE(this);
+  return UTC_MIN(this);
 }
 
 
 // ECMA 262 - 15.9.5.22
 function DateGetSeconds() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return SEC_FROM_TIME(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_SEC(this);
 }
 
 
 // ECMA 262 - 15.9.5.23
 function DateGetUTCSeconds() {
-  var t = DATE_VALUE(this);
-  return NAN_OR_SEC_FROM_TIME(t);
+  CHECK_DATE(this);
+  return UTC_SEC(this)
 }
 
 
 // ECMA 262 - 15.9.5.24
 function DateGetMilliseconds() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return MS_FROM_TIME(LocalTimeNoCheck(t));
+  CHECK_DATE(this);
+  return LOCAL_MS(this);
 }
 
 
 // ECMA 262 - 15.9.5.25
 function DateGetUTCMilliseconds() {
-  var t = DATE_VALUE(this);
-  return NAN_OR_MS_FROM_TIME(t);
+  CHECK_DATE(this);
+  return UTC_MS(this);
 }
 
 
 // ECMA 262 - 15.9.5.26
 function DateGetTimezoneOffset() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return t;
-  return (t - LocalTimeNoCheck(t)) / msPerMinute;
+  CHECK_DATE(this);
+  return TIMEZONE_OFFSET(this);
 }
 
 
 // ECMA 262 - 15.9.5.27
 function DateSetTime(ms) {
-  if (!IS_DATE(this)) ThrowDateTypeError();
-  return %_SetValueOf(this, TimeClip(ToNumber(ms)));
+  CHECK_DATE(this);
+  SET_UTC_DATE_VALUE(this, ToNumber(ms));
+  return UTC_DATE_VALUE(this);
 }
 
 
 // ECMA 262 - 15.9.5.28
 function DateSetMilliseconds(ms) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   ms = ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t),
-                      MIN_FROM_TIME(t),
-                      SEC_FROM_TIME(t),
-                      ms);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+  var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), LOCAL_SEC(this), ms);
+  SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
+  return this;
 }
 
 
 // ECMA 262 - 15.9.5.29
 function DateSetUTCMilliseconds(ms) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   ms = ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t),
-                      MIN_FROM_TIME(t),
-                      SEC_FROM_TIME(t),
+  var time = MakeTime(UTC_HOUR(this),
+                      UTC_MIN(this),
+                      UTC_SEC(this),
                       ms);
-  return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.30
 function DateSetSeconds(sec, ms) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   sec = ToNumber(sec);
-  ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+  ms = %_ArgumentsLength() < 2 ? LOCAL_MS(this) : ToNumber(ms);
+  var time = MakeTime(LOCAL_HOUR(this), LOCAL_MIN(this), sec, ms);
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.31
 function DateSetUTCSeconds(sec, ms) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   sec = ToNumber(sec);
-  ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
-  return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+  ms = %_ArgumentsLength() < 2 ? UTC_MS(this) : ToNumber(ms);
+  var time = MakeTime(UTC_HOUR(this), UTC_MIN(this), sec, ms);
+  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.33
 function DateSetMinutes(min, sec, ms) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   min = ToNumber(min);
   var argc = %_ArgumentsLength();
-  sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
-  ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+  sec = argc < 2 ? LOCAL_SEC(this) : ToNumber(sec);
+  ms = argc < 3 ? LOCAL_MS(this) : ToNumber(ms);
+  var time = MakeTime(LOCAL_HOUR(this), min, sec, ms);
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.34
 function DateSetUTCMinutes(min, sec, ms) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   min = ToNumber(min);
   var argc = %_ArgumentsLength();
-  sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
-  ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
-  var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
-  return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+  sec = argc < 2 ? UTC_SEC(this) : ToNumber(sec);
+  ms = argc < 3 ? UTC_MS(this) : ToNumber(ms);
+  var time = MakeTime(UTC_HOUR(this), min, sec, ms);
+  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.35
 function DateSetHours(hour, min, sec, ms) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   hour = ToNumber(hour);
   var argc = %_ArgumentsLength();
-  min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
-  sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
-  ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
+  min = argc < 2 ? LOCAL_MIN(this) : ToNumber(min);
+  sec = argc < 3 ? LOCAL_SEC(this) : ToNumber(sec);
+  ms = argc < 4 ? LOCAL_MS(this) : ToNumber(ms);
   var time = MakeTime(hour, min, sec, ms);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(LOCAL_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.34
 function DateSetUTCHours(hour, min, sec, ms) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   hour = ToNumber(hour);
   var argc = %_ArgumentsLength();
-  min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
-  sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
-  ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
+  min = argc < 2 ? UTC_MIN(this) : ToNumber(min);
+  sec = argc < 3 ? UTC_SEC(this) : ToNumber(sec);
+  ms = argc < 4 ? UTC_MS(this) : ToNumber(ms);
   var time = MakeTime(hour, min, sec, ms);
-  return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
+  return SET_UTC_DATE_VALUE(this, MakeDate(UTC_DAYS(this), time));
 }
 
 
 // ECMA 262 - 15.9.5.36
 function DateSetDate(date) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   date = ToNumber(date);
-  var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+  var day = MakeDay(LOCAL_YEAR(this), LOCAL_MONTH(this), date);
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
 }
 
 
 // ECMA 262 - 15.9.5.37
 function DateSetUTCDate(date) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   date = ToNumber(date);
-  var day = MakeDay(YearFromTime(t), MonthFromTime(t), date);
-  return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+  var day = MakeDay(UTC_YEAR(this), UTC_MONTH(this), date);
+  return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
 }
 
 
 // ECMA 262 - 15.9.5.38
 function DateSetMonth(month, date) {
-  var t = LocalTime(DATE_VALUE(this));
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   month = ToNumber(month);
-  date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
-  var day = MakeDay(YearFromTime(t), month, date);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+  date = %_ArgumentsLength() < 2 ? LOCAL_DAY(this) : ToNumber(date);
+  var day = MakeDay(LOCAL_YEAR(this), month, date);
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, LOCAL_TIME_IN_DAY(this)));
 }
 
 
 // ECMA 262 - 15.9.5.39
 function DateSetUTCMonth(month, date) {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   month = ToNumber(month);
-  date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
-  var day = MakeDay(YearFromTime(t), month, date);
-  return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+  date = %_ArgumentsLength() < 2 ? UTC_DAY(this) : ToNumber(date);
+  var day = MakeDay(UTC_YEAR(this), month, date);
+  return SET_UTC_DATE_VALUE(this, MakeDate(day, UTC_TIME_IN_DAY(this)));
 }
 
 
 // ECMA 262 - 15.9.5.40
 function DateSetFullYear(year, month, date) {
-  var t = DATE_VALUE(this);
-  t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
+  CHECK_DATE(this);
+  var t = LOCAL_DATE_VALUE(this);
   year = ToNumber(year);
   var argc = %_ArgumentsLength();
-  month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
-  date = argc < 3 ? DateFromTime(t) : ToNumber(date);
+  var time ;
+  if (NUMBER_IS_NAN(t)) {
+    month = argc < 2 ? 0 : ToNumber(month);
+    date = argc < 3 ? 1 : ToNumber(date);
+    time = 0;
+  } else {
+    month = argc < 2 ? LOCAL_MONTH(this) : ToNumber(month);
+    date = argc < 3 ? LOCAL_DAY(this) : ToNumber(date);
+    time = LOCAL_TIME_IN_DAY(this);
+  }
   var day = MakeDay(year, month, date);
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
 }
 
 
 // ECMA 262 - 15.9.5.41
 function DateSetUTCFullYear(year, month, date) {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) t = 0;
-  var argc = %_ArgumentsLength();
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   year = ToNumber(year);
-  month = argc < 2 ? MonthFromTime(t) : ToNumber(month);
-  date = argc < 3 ? DateFromTime(t) : ToNumber(date);
+  var argc = %_ArgumentsLength();
+  var time ;
+  if (NUMBER_IS_NAN(t)) {
+    month = argc < 2 ? 0 : ToNumber(month);
+    date = argc < 3 ? 1 : ToNumber(date);
+    time = 0;
+  } else {
+    month = argc < 2 ? UTC_MONTH(this) : ToNumber(month);
+    date = argc < 3 ? UTC_DAY(this) : ToNumber(date);
+    time = UTC_TIME_IN_DAY(this);
+  }
   var day = MakeDay(year, month, date);
-  return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
+  return SET_UTC_DATE_VALUE(this, MakeDate(day, time));
 }
 
 
 // ECMA 262 - 15.9.5.42
 function DateToUTCString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) return kInvalidDate;
   // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
-  return WeekDays[WeekDay(t)] + ', '
-      + TwoDigitString(DateFromTime(t)) + ' '
-      + Months[MonthFromTime(t)] + ' '
-      + YearFromTime(t) + ' '
-      + TimeString(t) + ' GMT';
+  return WeekDays[UTC_WEEKDAY(this)] + ', '
+      + TwoDigitString(UTC_DAY(this)) + ' '
+      + Months[UTC_MONTH(this)] + ' '
+      + UTC_YEAR(this) + ' '
+      + TimeStringUTC(this) + ' GMT';
 }
 
 
 // ECMA 262 - B.2.4
 function DateGetYear() {
-  var t = DATE_VALUE(this);
-  if (NUMBER_IS_NAN(t)) return $NaN;
-  return YearFromTime(LocalTimeNoCheck(t)) - 1900;
+  CHECK_DATE(this);
+  return LOCAL_YEAR(this) - 1900;
 }
 
 
 // ECMA 262 - B.2.5
 function DateSetYear(year) {
-  var t = LocalTime(DATE_VALUE(this));
-  if (NUMBER_IS_NAN(t)) t = 0;
+  CHECK_DATE(this);
   year = ToNumber(year);
-  if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
+  if (NUMBER_IS_NAN(year)) return SET_UTC_DATE_VALUE(this, $NaN);
   year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
       ? 1900 + TO_INTEGER(year) : year;
-  var day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
-  return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
+  var t = LOCAL_DATE_VALUE(this);
+  var month, date, time;
+  if (NUMBER_IS_NAN(t))  {
+    month = 0;
+    date = 1;
+    time = 0;
+  } else {
+    month = LOCAL_MONTH(this);
+    date = LOCAL_DAY(this);
+    time = LOCAL_TIME_IN_DAY(this);
+  }
+  var day = MakeDay(year, month, date);
+  return SET_LOCAL_DATE_VALUE(this, MakeDate(day, time));
 }
 
 
@@ -989,7 +758,8 @@
 
 // ECMA 262 - 15.9.5.43
 function DateToISOString() {
-  var t = DATE_VALUE(this);
+  CHECK_DATE(this);
+  var t = UTC_DATE_VALUE(this);
   if (NUMBER_IS_NAN(t)) throw MakeRangeError("invalid_time_value", []);
   var year = this.getUTCFullYear();
   var year_string;
@@ -1024,34 +794,13 @@
 
 
 function ResetDateCache() {
-
-  // Reset the local_time_offset:
-  local_time_offset = %DateLocalTimeOffset();
-
-  // Reset the DST offset cache:
-  var cache = DST_offset_cache;
-  cache.offset = 0;
-  cache.start = 0;
-  cache.end = -1;
-  cache.increment = 0;
-  cache.initial_increment = 19 * msPerDay;
-
   // Reset the timezone cache:
   timezone_cache_time = $NaN;
   timezone_cache_timezone = undefined;
 
-  // Reset the ltcache:
-  ltcache.key = null;
-  ltcache.val = null;
-
-  // Reset the ymd_from_time_cache:
-  ymd_from_time_cache = [$NaN, $NaN, $NaN];
-  ymd_from_time_cached_time = $NaN;
-
   // Reset the date cache:
   cache = Date_cache;
   cache.time = $NaN;
-  cache.year = $NaN;
   cache.string = null;
 }
 
diff --git a/src/elements.cc b/src/elements.cc
index e1be677..331f6bc 100644
--- a/src/elements.cc
+++ b/src/elements.cc
@@ -59,6 +59,53 @@
 namespace internal {
 
 
+// First argument in list is the accessor class, the second argument is the
+// accessor ElementsKind, and the third is the backing store class.  Use the
+// fast element handler for smi-only arrays.  The implementation is currently
+// identical.  Note that the order must match that of the ElementsKind enum for
+// the |accessor_array[]| below to work.
+#define ELEMENTS_LIST(V)                                                \
+  V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS, FixedArray)     \
+  V(FastObjectElementsAccessor, FAST_ELEMENTS, FixedArray)              \
+  V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS, FixedDoubleArray) \
+  V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS,                    \
+    SeededNumberDictionary)                                             \
+  V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS,  \
+    FixedArray)                                                         \
+  V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS,               \
+    ExternalByteArray)                                                  \
+  V(ExternalUnsignedByteElementsAccessor,                               \
+    EXTERNAL_UNSIGNED_BYTE_ELEMENTS, ExternalUnsignedByteArray)         \
+  V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS,             \
+    ExternalShortArray)                                                 \
+  V(ExternalUnsignedShortElementsAccessor,                              \
+    EXTERNAL_UNSIGNED_SHORT_ELEMENTS, ExternalUnsignedShortArray)       \
+  V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS,                 \
+    ExternalIntArray)                                                   \
+  V(ExternalUnsignedIntElementsAccessor,                                \
+    EXTERNAL_UNSIGNED_INT_ELEMENTS, ExternalUnsignedIntArray)           \
+  V(ExternalFloatElementsAccessor,                                      \
+    EXTERNAL_FLOAT_ELEMENTS, ExternalFloatArray)                        \
+  V(ExternalDoubleElementsAccessor,                                     \
+    EXTERNAL_DOUBLE_ELEMENTS, ExternalDoubleArray)                      \
+  V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS, ExternalPixelArray)
+
+
+template<ElementsKind Kind> class ElementsKindTraits {
+ public:
+  typedef FixedArrayBase BackingStore;
+};
+
+#define ELEMENTS_TRAITS(Class, KindParam, Store)               \
+template<> class ElementsKindTraits<KindParam> {               \
+  public:                                                      \
+  static const ElementsKind Kind = KindParam;                  \
+  typedef Store BackingStore;                                  \
+};
+ELEMENTS_LIST(ELEMENTS_TRAITS)
+#undef ELEMENTS_TRAITS
+
+
 ElementsAccessor** ElementsAccessor::elements_accessors_;
 
 
@@ -84,6 +131,140 @@
 }
 
 
+void CopyObjectToObjectElements(AssertNoAllocation* no_gc,
+                                FixedArray* from_obj,
+                                ElementsKind from_kind,
+                                uint32_t from_start,
+                                FixedArray* to_obj,
+                                ElementsKind to_kind,
+                                uint32_t to_start,
+                                int copy_size) {
+  ASSERT(to_obj->map() != HEAP->fixed_cow_array_map());
+  ASSERT(from_kind == FAST_ELEMENTS || from_kind == FAST_SMI_ONLY_ELEMENTS);
+  ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+  if (copy_size == -1) {
+    copy_size = Min(from_obj->length() - from_start,
+                    to_obj->length() - to_start);
+  }
+  ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+          (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+  if (copy_size == 0) return;
+  Address to = to_obj->address() + FixedArray::kHeaderSize;
+  Address from = from_obj->address() + FixedArray::kHeaderSize;
+  CopyWords(reinterpret_cast<Object**>(to) + to_start,
+            reinterpret_cast<Object**>(from) + from_start,
+            copy_size);
+  if (from_kind == FAST_ELEMENTS && to_kind == FAST_ELEMENTS) {
+    Heap* heap = from_obj->GetHeap();
+    WriteBarrierMode mode = to_obj->GetWriteBarrierMode(*no_gc);
+    if (mode == UPDATE_WRITE_BARRIER) {
+      heap->RecordWrites(to_obj->address(),
+                         to_obj->OffsetOfElementAt(to_start),
+                         copy_size);
+    }
+    heap->incremental_marking()->RecordWrites(to_obj);
+  }
+}
+
+
+
+
+static void CopyDictionaryToObjectElements(SeededNumberDictionary* from,
+                                           uint32_t from_start,
+                                           FixedArray* to,
+                                           ElementsKind to_kind,
+                                           uint32_t to_start,
+                                           int copy_size) {
+  ASSERT(to != from);
+  ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+  ASSERT(copy_size == -1 ||
+         (copy_size + static_cast<int>(to_start)) <= to->length());
+  WriteBarrierMode mode = to_kind == FAST_ELEMENTS
+      ? UPDATE_WRITE_BARRIER
+      : SKIP_WRITE_BARRIER;
+  uint32_t copy_limit = (copy_size == -1)
+      ? to->length()
+      : Min(to_start + copy_size, static_cast<uint32_t>(to->length()));
+  for (int i = 0; i < from->Capacity(); ++i) {
+    Object* key = from->KeyAt(i);
+    if (key->IsNumber()) {
+      uint32_t entry = static_cast<uint32_t>(key->Number());
+      if (entry >= to_start && entry < copy_limit) {
+        Object* value = from->ValueAt(i);
+        ASSERT(to_kind == FAST_ELEMENTS || value->IsSmi());
+        to->set(entry, value, mode);
+      }
+    }
+  }
+}
+
+
+MUST_USE_RESULT static MaybeObject* CopyDoubleToObjectElements(
+    FixedDoubleArray* from_obj,
+    uint32_t from_start,
+    FixedArray* to_obj,
+    ElementsKind to_kind,
+    uint32_t to_start,
+    int copy_size) {
+  ASSERT(to_kind == FAST_ELEMENTS || to_kind == FAST_SMI_ONLY_ELEMENTS);
+  if (copy_size == -1) {
+    copy_size = Min(from_obj->length() - from_start,
+                    to_obj->length() - to_start);
+  }
+  ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+          (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+  if (copy_size == 0) return from_obj;
+  for (int i = 0; i < copy_size; ++i) {
+    if (to_kind == FAST_SMI_ONLY_ELEMENTS) {
+      UNIMPLEMENTED();
+      return Failure::Exception();
+    } else {
+      MaybeObject* maybe_value = from_obj->get(i + from_start);
+      Object* value;
+      ASSERT(to_kind == FAST_ELEMENTS);
+      // Because FAST_DOUBLE_ELEMENTS -> FAST_ELEMENT allocate HeapObjects
+      // iteratively, the allocate must succeed within a single GC cycle,
+      // otherwise the retry after the GC will also fail. In order to ensure
+      // that no GC is triggered, allocate HeapNumbers from old space if they
+      // can't be taken from new space.
+      if (!maybe_value->ToObject(&value)) {
+        ASSERT(maybe_value->IsRetryAfterGC() || maybe_value->IsOutOfMemory());
+        Heap* heap = from_obj->GetHeap();
+        MaybeObject* maybe_value_object =
+            heap->AllocateHeapNumber(from_obj->get_scalar(i + from_start),
+                                     TENURED);
+        if (!maybe_value_object->ToObject(&value)) return maybe_value_object;
+      }
+      to_obj->set(i + to_start, value, UPDATE_WRITE_BARRIER);
+    }
+  }
+  return to_obj;
+}
+
+
+static void CopyDoubleToDoubleElements(FixedDoubleArray* from_obj,
+                                       uint32_t from_start,
+                                       FixedDoubleArray* to_obj,
+                                       uint32_t to_start,
+                                       int copy_size) {
+  if (copy_size == -1) {
+    copy_size = Min(from_obj->length() - from_start,
+                    to_obj->length() - to_start);
+  }
+  ASSERT(((copy_size + static_cast<int>(to_start)) <= to_obj->length() &&
+          (copy_size + static_cast<int>(from_start)) <= from_obj->length()));
+  if (copy_size == 0) return;
+  Address to = to_obj->address() + FixedDoubleArray::kHeaderSize;
+  Address from = from_obj->address() + FixedDoubleArray::kHeaderSize;
+  to += kDoubleSize * to_start;
+  from += kDoubleSize * from_start;
+  int words_per_double = (kDoubleSize / kPointerSize);
+  CopyWords(reinterpret_cast<Object**>(to),
+            reinterpret_cast<Object**>(from),
+            words_per_double * copy_size);
+}
+
+
 // Base class for element handler implementations. Contains the
 // the common logic for objects with different ElementsKinds.
 // Subclasses must specialize method for which the element
@@ -101,15 +282,22 @@
 // http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).  We use
 // CRTP to guarantee aggressive compile time optimizations (i.e.  inlining and
 // specialization of SomeElementsAccessor methods).
-template <typename ElementsAccessorSubclass, typename BackingStoreClass>
+template <typename ElementsAccessorSubclass,
+          typename ElementsTraitsParam>
 class ElementsAccessorBase : public ElementsAccessor {
  protected:
-  explicit ElementsAccessorBase(const char* name) : ElementsAccessor(name) { }
+  explicit ElementsAccessorBase(const char* name)
+      : ElementsAccessor(name) { }
+
+  typedef ElementsTraitsParam ElementsTraits;
+  typedef typename ElementsTraitsParam::BackingStore BackingStore;
+
+  virtual ElementsKind kind() const { return ElementsTraits::Kind; }
 
   static bool HasElementImpl(Object* receiver,
                              JSObject* holder,
                              uint32_t key,
-                             BackingStoreClass* backing_store) {
+                             BackingStore* backing_store) {
     MaybeObject* element =
         ElementsAccessorSubclass::GetImpl(receiver, holder, key, backing_store);
     return !element->IsTheHole();
@@ -123,7 +311,7 @@
       backing_store = holder->elements();
     }
     return ElementsAccessorSubclass::HasElementImpl(
-        receiver, holder, key, BackingStoreClass::cast(backing_store));
+        receiver, holder, key, BackingStore::cast(backing_store));
   }
 
   virtual MaybeObject* Get(Object* receiver,
@@ -134,13 +322,13 @@
       backing_store = holder->elements();
     }
     return ElementsAccessorSubclass::GetImpl(
-        receiver, holder, key, BackingStoreClass::cast(backing_store));
+        receiver, holder, key, BackingStore::cast(backing_store));
   }
 
   static MaybeObject* GetImpl(Object* receiver,
                               JSObject* obj,
                               uint32_t key,
-                              BackingStoreClass* backing_store) {
+                              BackingStore* backing_store) {
     return (key < ElementsAccessorSubclass::GetCapacityImpl(backing_store))
            ? backing_store->get(key)
            : backing_store->GetHeap()->the_hole_value();
@@ -149,12 +337,12 @@
   virtual MaybeObject* SetLength(JSArray* array,
                                  Object* length) {
     return ElementsAccessorSubclass::SetLengthImpl(
-        array, length, BackingStoreClass::cast(array->elements()));
+        array, length, BackingStore::cast(array->elements()));
   }
 
   static MaybeObject* SetLengthImpl(JSObject* obj,
                                     Object* length,
-                                    BackingStoreClass* backing_store);
+                                    BackingStore* backing_store);
 
   virtual MaybeObject* SetCapacityAndLength(JSArray* array,
                                             int capacity,
@@ -176,6 +364,30 @@
                               uint32_t key,
                               JSReceiver::DeleteMode mode) = 0;
 
+  static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+                                       uint32_t from_start,
+                                       FixedArrayBase* to,
+                                       ElementsKind to_kind,
+                                       uint32_t to_start,
+                                       int copy_size) {
+    UNREACHABLE();
+    return NULL;
+  }
+
+  virtual MaybeObject* CopyElements(JSObject* from_holder,
+                                    uint32_t from_start,
+                                    FixedArrayBase* to,
+                                    ElementsKind to_kind,
+                                    uint32_t to_start,
+                                    int copy_size,
+                                    FixedArrayBase* from) {
+    if (from == NULL) {
+      from = from_holder->elements();
+    }
+    return ElementsAccessorSubclass::CopyElementsImpl(
+        from, from_start, to, to_kind, to_start, copy_size);
+  }
+
   virtual MaybeObject* AddElementsToFixedArray(Object* receiver,
                                                JSObject* holder,
                                                FixedArray* to,
@@ -191,7 +403,7 @@
     if (from == NULL) {
       from = holder->elements();
     }
-    BackingStoreClass* backing_store = BackingStoreClass::cast(from);
+    BackingStore* backing_store = BackingStore::cast(from);
     uint32_t len1 = ElementsAccessorSubclass::GetCapacityImpl(backing_store);
 
     // Optimize if 'other' is empty.
@@ -258,16 +470,16 @@
   }
 
  protected:
-  static uint32_t GetCapacityImpl(BackingStoreClass* backing_store) {
+  static uint32_t GetCapacityImpl(BackingStore* backing_store) {
     return backing_store->length();
   }
 
   virtual uint32_t GetCapacity(FixedArrayBase* backing_store) {
     return ElementsAccessorSubclass::GetCapacityImpl(
-        BackingStoreClass::cast(backing_store));
+        BackingStore::cast(backing_store));
   }
 
-  static uint32_t GetKeyForIndexImpl(BackingStoreClass* backing_store,
+  static uint32_t GetKeyForIndexImpl(BackingStore* backing_store,
                                      uint32_t index) {
     return index;
   }
@@ -275,7 +487,7 @@
   virtual uint32_t GetKeyForIndex(FixedArrayBase* backing_store,
                                   uint32_t index) {
     return ElementsAccessorSubclass::GetKeyForIndexImpl(
-        BackingStoreClass::cast(backing_store), index);
+        BackingStore::cast(backing_store), index);
   }
 
  private:
@@ -285,16 +497,18 @@
 
 // Super class for all fast element arrays.
 template<typename FastElementsAccessorSubclass,
-         typename BackingStore,
+         typename KindTraits,
          int ElementSize>
 class FastElementsAccessor
-    : public ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore> {
+    : public ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits> {
  public:
   explicit FastElementsAccessor(const char* name)
       : ElementsAccessorBase<FastElementsAccessorSubclass,
-                             BackingStore>(name) {}
+                             KindTraits>(name) {}
  protected:
-  friend class ElementsAccessorBase<FastElementsAccessorSubclass, BackingStore>;
+  friend class ElementsAccessorBase<FastElementsAccessorSubclass, KindTraits>;
+
+  typedef typename KindTraits::BackingStore BackingStore;
 
   // Adjusts the length of the fast backing store or returns the new length or
   // undefined in case conversion to a slow backing store should be performed.
@@ -349,12 +563,12 @@
 
 class FastObjectElementsAccessor
     : public FastElementsAccessor<FastObjectElementsAccessor,
-                                  FixedArray,
+                                  ElementsKindTraits<FAST_ELEMENTS>,
                                   kPointerSize> {
  public:
   explicit FastObjectElementsAccessor(const char* name)
       : FastElementsAccessor<FastObjectElementsAccessor,
-                             FixedArray,
+                             ElementsKindTraits<FAST_ELEMENTS>,
                              kPointerSize>(name) {}
 
   static MaybeObject* DeleteCommon(JSObject* obj,
@@ -403,6 +617,28 @@
     return heap->true_value();
   }
 
+  static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+                                       uint32_t from_start,
+                                       FixedArrayBase* to,
+                                       ElementsKind to_kind,
+                                       uint32_t to_start,
+                                       int copy_size) {
+    switch (to_kind) {
+      case FAST_SMI_ONLY_ELEMENTS:
+      case FAST_ELEMENTS: {
+        AssertNoAllocation no_gc;
+        CopyObjectToObjectElements(
+            &no_gc, FixedArray::cast(from), ElementsTraits::Kind, from_start,
+            FixedArray::cast(to), to_kind, to_start, copy_size);
+        return from;
+      }
+      default:
+        UNREACHABLE();
+    }
+    return to->GetHeap()->undefined_value();
+  }
+
+
   static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
                                                        uint32_t capacity,
                                                        uint32_t length) {
@@ -417,7 +653,7 @@
 
  protected:
   friend class FastElementsAccessor<FastObjectElementsAccessor,
-                                    FixedArray,
+                                    ElementsKindTraits<FAST_ELEMENTS>,
                                     kPointerSize>;
 
   virtual MaybeObject* Delete(JSObject* obj,
@@ -430,12 +666,12 @@
 
 class FastDoubleElementsAccessor
     : public FastElementsAccessor<FastDoubleElementsAccessor,
-                                  FixedDoubleArray,
+                                  ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
                                   kDoubleSize> {
  public:
   explicit FastDoubleElementsAccessor(const char* name)
       : FastElementsAccessor<FastDoubleElementsAccessor,
-                             FixedDoubleArray,
+                             ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
                              kDoubleSize>(name) {}
 
   static MaybeObject* SetFastElementsCapacityAndLength(JSObject* obj,
@@ -446,11 +682,34 @@
 
  protected:
   friend class ElementsAccessorBase<FastDoubleElementsAccessor,
-                                    FixedDoubleArray>;
+                                    ElementsKindTraits<FAST_DOUBLE_ELEMENTS> >;
   friend class FastElementsAccessor<FastDoubleElementsAccessor,
-                                    FixedDoubleArray,
+                                    ElementsKindTraits<FAST_DOUBLE_ELEMENTS>,
                                     kDoubleSize>;
 
+  static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+                                       uint32_t from_start,
+                                       FixedArrayBase* to,
+                                       ElementsKind to_kind,
+                                       uint32_t to_start,
+                                       int copy_size) {
+    switch (to_kind) {
+      case FAST_SMI_ONLY_ELEMENTS:
+      case FAST_ELEMENTS:
+        return CopyDoubleToObjectElements(
+            FixedDoubleArray::cast(from), from_start, FixedArray::cast(to),
+            to_kind, to_start, copy_size);
+      case FAST_DOUBLE_ELEMENTS:
+        CopyDoubleToDoubleElements(FixedDoubleArray::cast(from), from_start,
+                                   FixedDoubleArray::cast(to),
+                                   to_start, copy_size);
+        return from;
+      default:
+        UNREACHABLE();
+    }
+    return to->GetHeap()->undefined_value();
+  }
+
   virtual MaybeObject* Delete(JSObject* obj,
                               uint32_t key,
                               JSReceiver::DeleteMode mode) {
@@ -474,23 +733,25 @@
 
 // Super class for all external element arrays.
 template<typename ExternalElementsAccessorSubclass,
-         typename ExternalArray>
+         ElementsKind Kind>
 class ExternalElementsAccessor
     : public ElementsAccessorBase<ExternalElementsAccessorSubclass,
-                                  ExternalArray> {
+                                  ElementsKindTraits<Kind> > {
  public:
   explicit ExternalElementsAccessor(const char* name)
       : ElementsAccessorBase<ExternalElementsAccessorSubclass,
-                             ExternalArray>(name) {}
+                             ElementsKindTraits<Kind> >(name) {}
 
  protected:
+  typedef typename ElementsKindTraits<Kind>::BackingStore BackingStore;
+
   friend class ElementsAccessorBase<ExternalElementsAccessorSubclass,
-                                    ExternalArray>;
+                                    ElementsKindTraits<Kind> >;
 
   static MaybeObject* GetImpl(Object* receiver,
                               JSObject* obj,
                               uint32_t key,
-                              ExternalArray* backing_store) {
+                              BackingStore* backing_store) {
     return
         key < ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store)
         ? backing_store->get(key)
@@ -499,7 +760,7 @@
 
   static MaybeObject* SetLengthImpl(JSObject* obj,
                                     Object* length,
-                                    ExternalArray* backing_store) {
+                                    BackingStore* backing_store) {
     // External arrays do not support changing their length.
     UNREACHABLE();
     return obj;
@@ -515,7 +776,7 @@
   static bool HasElementImpl(Object* receiver,
                              JSObject* holder,
                              uint32_t key,
-                             ExternalArray* backing_store) {
+                             BackingStore* backing_store) {
     uint32_t capacity =
         ExternalElementsAccessorSubclass::GetCapacityImpl(backing_store);
     return key < capacity;
@@ -525,101 +786,101 @@
 
 class ExternalByteElementsAccessor
     : public ExternalElementsAccessor<ExternalByteElementsAccessor,
-                                      ExternalByteArray> {
+                                      EXTERNAL_BYTE_ELEMENTS> {
  public:
   explicit ExternalByteElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalByteElementsAccessor,
-                                 ExternalByteArray>(name) {}
+                                 EXTERNAL_BYTE_ELEMENTS>(name) {}
 };
 
 
 class ExternalUnsignedByteElementsAccessor
     : public ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
-                                      ExternalUnsignedByteArray> {
+                                      EXTERNAL_UNSIGNED_BYTE_ELEMENTS> {
  public:
   explicit ExternalUnsignedByteElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalUnsignedByteElementsAccessor,
-                                 ExternalUnsignedByteArray>(name) {}
+                                 EXTERNAL_UNSIGNED_BYTE_ELEMENTS>(name) {}
 };
 
 
 class ExternalShortElementsAccessor
     : public ExternalElementsAccessor<ExternalShortElementsAccessor,
-                                      ExternalShortArray> {
+                                      EXTERNAL_SHORT_ELEMENTS> {
  public:
   explicit ExternalShortElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalShortElementsAccessor,
-                                 ExternalShortArray>(name) {}
+                                 EXTERNAL_SHORT_ELEMENTS>(name) {}
 };
 
 
 class ExternalUnsignedShortElementsAccessor
     : public ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
-                                      ExternalUnsignedShortArray> {
+                                      EXTERNAL_UNSIGNED_SHORT_ELEMENTS> {
  public:
   explicit ExternalUnsignedShortElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalUnsignedShortElementsAccessor,
-                                        ExternalUnsignedShortArray>(name) {}
+                                 EXTERNAL_UNSIGNED_SHORT_ELEMENTS>(name) {}
 };
 
 
 class ExternalIntElementsAccessor
     : public ExternalElementsAccessor<ExternalIntElementsAccessor,
-                                      ExternalIntArray> {
+                                      EXTERNAL_INT_ELEMENTS> {
  public:
   explicit ExternalIntElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalIntElementsAccessor,
-                                 ExternalIntArray>(name) {}
+                                 EXTERNAL_INT_ELEMENTS>(name) {}
 };
 
 
 class ExternalUnsignedIntElementsAccessor
     : public ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
-                                      ExternalUnsignedIntArray> {
+                                      EXTERNAL_UNSIGNED_INT_ELEMENTS> {
  public:
   explicit ExternalUnsignedIntElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalUnsignedIntElementsAccessor,
-                                 ExternalUnsignedIntArray>(name) {}
+                                 EXTERNAL_UNSIGNED_INT_ELEMENTS>(name) {}
 };
 
 
 class ExternalFloatElementsAccessor
     : public ExternalElementsAccessor<ExternalFloatElementsAccessor,
-                                      ExternalFloatArray> {
+                                      EXTERNAL_FLOAT_ELEMENTS> {
  public:
   explicit ExternalFloatElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalFloatElementsAccessor,
-                                 ExternalFloatArray>(name) {}
+                                 EXTERNAL_FLOAT_ELEMENTS>(name) {}
 };
 
 
 class ExternalDoubleElementsAccessor
     : public ExternalElementsAccessor<ExternalDoubleElementsAccessor,
-                                      ExternalDoubleArray> {
+                                      EXTERNAL_DOUBLE_ELEMENTS> {
  public:
   explicit ExternalDoubleElementsAccessor(const char* name)
       : ExternalElementsAccessor<ExternalDoubleElementsAccessor,
-                                 ExternalDoubleArray>(name) {}
+                                 EXTERNAL_DOUBLE_ELEMENTS>(name) {}
 };
 
 
 class PixelElementsAccessor
     : public ExternalElementsAccessor<PixelElementsAccessor,
-                                      ExternalPixelArray> {
+                                      EXTERNAL_PIXEL_ELEMENTS> {
  public:
   explicit PixelElementsAccessor(const char* name)
       : ExternalElementsAccessor<PixelElementsAccessor,
-                                 ExternalPixelArray>(name) {}
+                                 EXTERNAL_PIXEL_ELEMENTS>(name) {}
 };
 
 
 class DictionaryElementsAccessor
     : public ElementsAccessorBase<DictionaryElementsAccessor,
-                                  SeededNumberDictionary> {
+                                  ElementsKindTraits<DICTIONARY_ELEMENTS> > {
  public:
   explicit DictionaryElementsAccessor(const char* name)
       : ElementsAccessorBase<DictionaryElementsAccessor,
-                             SeededNumberDictionary>(name) {}
+                             ElementsKindTraits<DICTIONARY_ELEMENTS> >(name) {}
 
   // Adjusts the length of the dictionary backing store and returns the new
   // length according to ES5 section 15.4.5.2 behavior.
@@ -723,9 +984,29 @@
     return heap->true_value();
   }
 
+  static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+                                       uint32_t from_start,
+                                       FixedArrayBase* to,
+                                       ElementsKind to_kind,
+                                       uint32_t to_start,
+                                       int copy_size) {
+    switch (to_kind) {
+      case FAST_SMI_ONLY_ELEMENTS:
+      case FAST_ELEMENTS:
+        CopyDictionaryToObjectElements(
+            SeededNumberDictionary::cast(from), from_start,
+            FixedArray::cast(to), to_kind, to_start, copy_size);
+        return from;
+      default:
+        UNREACHABLE();
+    }
+    return to->GetHeap()->undefined_value();
+  }
+
+
  protected:
   friend class ElementsAccessorBase<DictionaryElementsAccessor,
-                                    SeededNumberDictionary>;
+                                    ElementsKindTraits<DICTIONARY_ELEMENTS> >;
 
   virtual MaybeObject* Delete(JSObject* obj,
                               uint32_t key,
@@ -769,16 +1050,18 @@
 };
 
 
-class NonStrictArgumentsElementsAccessor
-    : public ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
-                                  FixedArray> {
+class NonStrictArgumentsElementsAccessor : public ElementsAccessorBase<
+    NonStrictArgumentsElementsAccessor,
+    ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> > {
  public:
   explicit NonStrictArgumentsElementsAccessor(const char* name)
-      : ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
-                             FixedArray>(name) {}
+      : ElementsAccessorBase<
+          NonStrictArgumentsElementsAccessor,
+          ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >(name) {}
  protected:
-  friend class ElementsAccessorBase<NonStrictArgumentsElementsAccessor,
-                                    FixedArray>;
+  friend class ElementsAccessorBase<
+      NonStrictArgumentsElementsAccessor,
+      ElementsKindTraits<NON_STRICT_ARGUMENTS_ELEMENTS> >;
 
   static MaybeObject* GetImpl(Object* receiver,
                               JSObject* obj,
@@ -840,6 +1123,19 @@
     return obj->GetHeap()->true_value();
   }
 
+  static MaybeObject* CopyElementsImpl(FixedArrayBase* from,
+                                       uint32_t from_start,
+                                       FixedArrayBase* to,
+                                       ElementsKind to_kind,
+                                       uint32_t to_start,
+                                       int copy_size) {
+    FixedArray* parameter_map = FixedArray::cast(from);
+    FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
+    ElementsAccessor* accessor = ElementsAccessor::ForArray(arguments);
+    return accessor->CopyElements(NULL, from_start, to, to_kind,
+                                  to_start, copy_size, arguments);
+  }
+
   static uint32_t GetCapacityImpl(FixedArray* parameter_map) {
     FixedArrayBase* arguments = FixedArrayBase::cast(parameter_map->get(1));
     return Max(static_cast<uint32_t>(parameter_map->length() - 2),
@@ -913,45 +1209,22 @@
 
 
 void ElementsAccessor::InitializeOncePerProcess() {
-  // First argument in list is the accessor class, the second argument is can
-  // be any arbitrary unique identifier, in this case chosen to be the
-  // corresponding enum.  Use the fast element handler for smi-only arrays.
-  // The implementation is currently identical.  Note that the order must match
-  // that of the ElementsKind enum for the |accessor_array[]| below to work.
-#define ELEMENTS_LIST(V)                                                       \
-  V(FastObjectElementsAccessor, FAST_SMI_ONLY_ELEMENTS)                        \
-  V(FastObjectElementsAccessor, FAST_ELEMENTS)                                 \
-  V(FastDoubleElementsAccessor, FAST_DOUBLE_ELEMENTS)                          \
-  V(DictionaryElementsAccessor, DICTIONARY_ELEMENTS)                           \
-  V(NonStrictArgumentsElementsAccessor, NON_STRICT_ARGUMENTS_ELEMENTS)         \
-  V(ExternalByteElementsAccessor, EXTERNAL_BYTE_ELEMENTS)                      \
-  V(ExternalUnsignedByteElementsAccessor, EXTERNAL_UNSIGNED_BYTE_ELEMENTS)     \
-  V(ExternalShortElementsAccessor, EXTERNAL_SHORT_ELEMENTS)                    \
-  V(ExternalUnsignedShortElementsAccessor, EXTERNAL_UNSIGNED_SHORT_ELEMENTS)   \
-  V(ExternalIntElementsAccessor, EXTERNAL_INT_ELEMENTS)                        \
-  V(ExternalUnsignedIntElementsAccessor, EXTERNAL_UNSIGNED_INT_ELEMENTS)       \
-  V(ExternalFloatElementsAccessor, EXTERNAL_FLOAT_ELEMENTS)                    \
-  V(ExternalDoubleElementsAccessor, EXTERNAL_DOUBLE_ELEMENTS)                  \
-  V(PixelElementsAccessor, EXTERNAL_PIXEL_ELEMENTS)
-
   static struct ConcreteElementsAccessors {
-#define ACCESSOR_STRUCT(Class, Name) Class* Name##_handler;
+#define ACCESSOR_STRUCT(Class, Kind, Store) Class* Kind##_handler;
     ELEMENTS_LIST(ACCESSOR_STRUCT)
 #undef ACCESSOR_STRUCT
   } element_accessors = {
-#define ACCESSOR_INIT(Class, Name) new Class(#Name),
+#define ACCESSOR_INIT(Class, Kind, Store) new Class(#Kind),
     ELEMENTS_LIST(ACCESSOR_INIT)
 #undef ACCESSOR_INIT
   };
 
   static ElementsAccessor* accessor_array[] = {
-#define ACCESSOR_ARRAY(Class, Name) element_accessors.Name##_handler,
+#define ACCESSOR_ARRAY(Class, Kind, Store) element_accessors.Kind##_handler,
     ELEMENTS_LIST(ACCESSOR_ARRAY)
 #undef ACCESSOR_ARRAY
   };
 
-#undef ELEMENTS_LIST
-
   STATIC_ASSERT((sizeof(accessor_array) / sizeof(*accessor_array)) ==
                 kElementsKindCount);
 
@@ -959,11 +1232,12 @@
 }
 
 
-template <typename ElementsAccessorSubclass, typename BackingStoreClass>
-MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass, BackingStoreClass>::
+template <typename ElementsAccessorSubclass, typename ElementsKindTraits>
+MaybeObject* ElementsAccessorBase<ElementsAccessorSubclass,
+                                  ElementsKindTraits>::
     SetLengthImpl(JSObject* obj,
                   Object* length,
-                  BackingStoreClass* backing_store) {
+                  typename ElementsKindTraits::BackingStore* backing_store) {
   JSArray* array = JSArray::cast(obj);
 
   // Fast case: The new length fits into a Smi.
diff --git a/src/elements.h b/src/elements.h
index 036df5f..5b5be23 100644
--- a/src/elements.h
+++ b/src/elements.h
@@ -29,6 +29,8 @@
 #define V8_ELEMENTS_H_
 
 #include "objects.h"
+#include "heap.h"
+#include "isolate.h"
 
 namespace v8 {
 namespace internal {
@@ -40,7 +42,8 @@
   explicit ElementsAccessor(const char* name) : name_(name) { }
   virtual ~ElementsAccessor() { }
 
-  virtual const char* name() const { return name_; }
+  virtual ElementsKind kind() const = 0;
+  const char* name() const { return name_; }
 
   // Returns true if a holder contains an element with the specified key
   // without iterating up the prototype chain.  The caller can optionally pass
@@ -85,6 +88,25 @@
                               uint32_t key,
                               JSReceiver::DeleteMode mode) = 0;
 
+  // Copy elements from one backing store to another. Typically, callers specify
+  // the source JSObject or JSArray in source_holder. If the holder's backing
+  // store is available, it can be passed in source and source_holder is
+  // ignored.
+  virtual MaybeObject* CopyElements(JSObject* source_holder,
+                                    uint32_t source_start,
+                                    FixedArrayBase* destination,
+                                    ElementsKind destination_kind,
+                                    uint32_t destination_start,
+                                    int copy_size,
+                                    FixedArrayBase* source = NULL) = 0;
+
+  MaybeObject* CopyElements(JSObject* from_holder,
+                            FixedArrayBase* to,
+                            ElementsKind to_kind,
+                            FixedArrayBase* from = NULL) {
+    return CopyElements(from_holder, 0, to, to_kind, 0, -1, from);
+  }
+
   virtual MaybeObject* AddElementsToFixedArray(Object* receiver,
                                                JSObject* holder,
                                                FixedArray* to,
@@ -123,6 +145,17 @@
   DISALLOW_COPY_AND_ASSIGN(ElementsAccessor);
 };
 
+
+void CopyObjectToObjectElements(AssertNoAllocation* no_gc,
+                                FixedArray* from_obj,
+                                ElementsKind from_kind,
+                                uint32_t from_start,
+                                FixedArray* to_obj,
+                                ElementsKind to_kind,
+                                uint32_t to_start,
+                                int copy_size);
+
+
 } }  // namespace v8::internal
 
 #endif  // V8_ELEMENTS_H_
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index c62e18d..ac30b29 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -177,22 +177,28 @@
 // Experimental profiler changes.
 DEFINE_bool(experimental_profiler, false, "enable all profiler experiments")
 DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability")
-DEFINE_int(frame_count, 2, "number of stack frames inspected by the profiler")
+DEFINE_int(frame_count, 1, "number of stack frames inspected by the profiler")
 DEFINE_bool(self_optimization, false,
             "primitive functions trigger their own optimization")
+DEFINE_bool(direct_self_opt, false,
+            "call recompile stub directly when self-optimizing")
+DEFINE_bool(retry_self_opt, false, "re-try self-optimization if it failed")
 DEFINE_bool(count_based_interrupts, false,
             "trigger profiler ticks based on counting instead of timing")
 DEFINE_bool(interrupt_at_exit, false,
             "insert an interrupt check at function exit")
 DEFINE_bool(weighted_back_edges, false,
             "weight back edges by jump distance for interrupt triggering")
-DEFINE_int(interrupt_budget, 10000,
+DEFINE_int(interrupt_budget, 5900,
            "execution budget before interrupt is triggered")
-DEFINE_int(type_info_threshold, 0,
+DEFINE_int(type_info_threshold, 40,
            "percentage of ICs that must have type info to allow optimization")
+DEFINE_int(self_opt_count, 130, "call count before self-optimization")
 
 DEFINE_implication(experimental_profiler, watch_ic_patching)
 DEFINE_implication(experimental_profiler, self_optimization)
+// Not implying direct_self_opt here because it seems to be a bad idea.
+DEFINE_implication(experimental_profiler, retry_self_opt)
 DEFINE_implication(experimental_profiler, count_based_interrupts)
 DEFINE_implication(experimental_profiler, interrupt_at_exit)
 DEFINE_implication(experimental_profiler, weighted_back_edges)
diff --git a/src/full-codegen.h b/src/full-codegen.h
index 0ffb885..25e3dba 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -437,6 +437,9 @@
   // the offset of the start of the table.
   unsigned EmitStackCheckTable();
 
+  void EmitProfilingCounterDecrement(int delta);
+  void EmitProfilingCounterReset();
+
   // Platform-specific return sequence
   void EmitReturnSequence();
 
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index a05fc77..5733e51 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -184,7 +184,8 @@
   V(ForInPrepareMap)                           \
   V(ForInCacheArray)                           \
   V(CheckMapValue)                             \
-  V(LoadFieldByIndex)
+  V(LoadFieldByIndex)                          \
+  V(DateField)
 
 #define GVN_FLAG_LIST(V)                       \
   V(Calls)                                     \
@@ -4602,6 +4603,26 @@
 };
 
 
+class HDateField: public HUnaryOperation {
+ public:
+  HDateField(HValue* date, Smi* index)
+      : HUnaryOperation(date), index_(index) {
+    set_representation(Representation::Tagged());
+  }
+
+  Smi* index() const { return index_; }
+
+  virtual Representation RequiredInputRepresentation(int index) {
+    return Representation::Tagged();
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(DateField)
+
+ private:
+  Smi* index_;
+};
+
+
 class HDeleteProperty: public HBinaryOperation {
  public:
   HDeleteProperty(HValue* context, HValue* obj, HValue* key)
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index fe23a14..11b1157 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -7122,6 +7122,17 @@
 }
 
 
+void HGraphBuilder::GenerateDateField(CallRuntime* call) {
+  ASSERT(call->arguments()->length() == 2);
+  ASSERT_NE(NULL, call->arguments()->at(1)->AsLiteral());
+  Smi* index = Smi::cast(*(call->arguments()->at(1)->AsLiteral()->handle()));
+  CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
+  HValue* date = Pop();
+  HDateField* result = new(zone()) HDateField(date, index);
+  return ast_context()->ReturnInstruction(result, call->id());
+}
+
+
 void HGraphBuilder::GenerateSetValueOf(CallRuntime* call) {
   ASSERT(call->arguments()->length() == 2);
   CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 54a1a34..d3e2a91 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -6462,7 +6462,7 @@
   __ mov_b(scratch, Operand(left, index, times_1, 0));
   __ cmpb(scratch, Operand(right, index, times_1, 0));
   __ j(not_equal, chars_not_equal, chars_not_equal_near);
-  __ add(index, Immediate(1));
+  __ inc(index);
   __ j(not_zero, &loop);
 }
 
@@ -6645,9 +6645,10 @@
 
 void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
   ASSERT(state_ == CompareIC::STRINGS);
-  ASSERT(GetCondition() == equal);
   Label miss;
 
+  bool equality = Token::IsEqualityOp(op_);
+
   // Registers containing left and right operands respectively.
   Register left = edx;
   Register right = eax;
@@ -6686,25 +6687,33 @@
   __ bind(&not_same);
 
   // Check that both strings are symbols. If they are, we're done
-  // because we already know they are not identical.
-  Label do_compare;
-  STATIC_ASSERT(kSymbolTag != 0);
-  __ and_(tmp1, tmp2);
-  __ test(tmp1, Immediate(kIsSymbolMask));
-  __ j(zero, &do_compare, Label::kNear);
-  // Make sure eax is non-zero. At this point input operands are
-  // guaranteed to be non-zero.
-  ASSERT(right.is(eax));
-  __ ret(0);
+  // because we already know they are not identical.  But in the case of
+  // non-equality compare, we still need to determine the order.
+  if (equality) {
+    Label do_compare;
+    STATIC_ASSERT(kSymbolTag != 0);
+    __ and_(tmp1, tmp2);
+    __ test(tmp1, Immediate(kIsSymbolMask));
+    __ j(zero, &do_compare, Label::kNear);
+    // Make sure eax is non-zero. At this point input operands are
+    // guaranteed to be non-zero.
+    ASSERT(right.is(eax));
+    __ ret(0);
+    __ bind(&do_compare);
+  }
 
   // Check that both strings are sequential ASCII.
   Label runtime;
-  __ bind(&do_compare);
   __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
 
   // Compare flat ASCII strings. Returns when done.
-  StringCompareStub::GenerateFlatAsciiStringEquals(
-      masm, left, right, tmp1, tmp2);
+  if (equality) {
+    StringCompareStub::GenerateFlatAsciiStringEquals(
+        masm, left, right, tmp1, tmp2);
+  } else {
+    StringCompareStub::GenerateCompareFlatAsciiStrings(
+        masm, left, right, tmp1, tmp2, tmp3);
+  }
 
   // Handle more complex cases in runtime.
   __ bind(&runtime);
@@ -6712,7 +6721,11 @@
   __ push(left);
   __ push(right);
   __ push(tmp1);
-  __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  if (equality) {
+    __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  } else {
+    __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+  }
 
   __ bind(&miss);
   GenerateMiss(masm);
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index 67291c3..11de1c4 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -206,7 +206,7 @@
 
 
 static const byte kJnsInstruction = 0x79;
-static const byte kJnsOffset = 0x11;
+static const byte kJnsOffset = 0x13;
 static const byte kJaeInstruction = 0x73;
 static const byte kJaeOffset = 0x07;
 static const byte kCallInstruction = 0xe8;
@@ -219,8 +219,8 @@
                                         Code* check_code,
                                         Code* replacement_code) {
   Address call_target_address = pc_after - kIntSize;
-  ASSERT(check_code->entry() ==
-         Assembler::target_address_at(call_target_address));
+  ASSERT_EQ(check_code->entry(),
+            Assembler::target_address_at(call_target_address));
   // The stack check code matches the pattern:
   //
   //     cmp esp, <limit>
@@ -239,13 +239,13 @@
   // ok:
 
   if (FLAG_count_based_interrupts) {
-    ASSERT(*(call_target_address - 3) == kJnsInstruction);
-    ASSERT(*(call_target_address - 2) == kJnsOffset);
+    ASSERT_EQ(*(call_target_address - 3), kJnsInstruction);
+    ASSERT_EQ(*(call_target_address - 2), kJnsOffset);
   } else {
-    ASSERT(*(call_target_address - 3) == kJaeInstruction);
-    ASSERT(*(call_target_address - 2) == kJaeOffset);
+    ASSERT_EQ(*(call_target_address - 3), kJaeInstruction);
+    ASSERT_EQ(*(call_target_address - 2), kJaeOffset);
   }
-  ASSERT(*(call_target_address - 1) == kCallInstruction);
+  ASSERT_EQ(*(call_target_address - 1), kCallInstruction);
   *(call_target_address - 3) = kNopByteOne;
   *(call_target_address - 2) = kNopByteTwo;
   Assembler::set_target_address_at(call_target_address,
@@ -261,14 +261,14 @@
                                          Code* check_code,
                                          Code* replacement_code) {
   Address call_target_address = pc_after - kIntSize;
-  ASSERT(replacement_code->entry() ==
-         Assembler::target_address_at(call_target_address));
+  ASSERT_EQ(replacement_code->entry(),
+            Assembler::target_address_at(call_target_address));
 
   // Replace the nops from patching (Deoptimizer::PatchStackCheckCode) to
   // restore the conditional branch.
-  ASSERT(*(call_target_address - 3) == kNopByteOne &&
-         *(call_target_address - 2) == kNopByteTwo &&
-         *(call_target_address - 1) == kCallInstruction);
+  ASSERT_EQ(*(call_target_address - 3), kNopByteOne);
+  ASSERT_EQ(*(call_target_address - 2), kNopByteTwo);
+  ASSERT_EQ(*(call_target_address - 1), kCallInstruction);
   if (FLAG_count_based_interrupts) {
     *(call_target_address - 3) = kJnsInstruction;
     *(call_target_address - 2) = kJnsOffset;
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index b55f428..b42ce95 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -127,28 +127,6 @@
   SetFunctionPosition(function());
   Comment cmnt(masm_, "[ function compiled by full code generator");
 
-  // We can optionally optimize based on counters rather than statistical
-  // sampling.
-  if (info->ShouldSelfOptimize()) {
-    if (FLAG_trace_opt_verbose) {
-      PrintF("[adding self-optimization header to %s]\n",
-             *info->function()->debug_name()->ToCString());
-    }
-    has_self_optimization_header_ = true;
-    MaybeObject* maybe_cell = isolate()->heap()->AllocateJSGlobalPropertyCell(
-        Smi::FromInt(Compiler::kCallsUntilPrimitiveOpt));
-    JSGlobalPropertyCell* cell;
-    if (maybe_cell->To(&cell)) {
-      __ sub(Operand::Cell(Handle<JSGlobalPropertyCell>(cell)),
-             Immediate(Smi::FromInt(1)));
-      Handle<Code> compile_stub(
-          isolate()->builtins()->builtin(Builtins::kLazyRecompile));
-      STATIC_ASSERT(kSmiTag == 0);
-      __ j(zero, compile_stub);
-      ASSERT(masm_->pc_offset() == self_optimization_header_size());
-    }
-  }
-
 #ifdef DEBUG
   if (strlen(FLAG_stop_at) > 0 &&
       info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
@@ -330,6 +308,25 @@
 }
 
 
+void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
+  __ mov(ebx, Immediate(profiling_counter_));
+  __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
+         Immediate(Smi::FromInt(delta)));
+}
+
+
+void FullCodeGenerator::EmitProfilingCounterReset() {
+  int reset_value = FLAG_interrupt_budget;
+  if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
+    // Self-optimization is a one-off thing: if it fails, don't try again.
+    reset_value = Smi::kMaxValue;
+  }
+  __ mov(ebx, Immediate(profiling_counter_));
+  __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
+         Immediate(Smi::FromInt(reset_value)));
+}
+
+
 void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt,
                                        Label* back_edge_target) {
   Comment cmnt(masm_, "[ Stack check");
@@ -342,15 +339,7 @@
       int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
       weight = Min(127, Max(1, distance / 100));
     }
-    if (Serializer::enabled()) {
-      __ mov(ebx, Immediate(profiling_counter_));
-      __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
-             Immediate(Smi::FromInt(weight)));
-    } else {
-      // This version is slightly faster, but not snapshot safe.
-      __ sub(Operand::Cell(profiling_counter_),
-             Immediate(Smi::FromInt(weight)));
-    }
+    EmitProfilingCounterDecrement(weight);
     __ j(positive, &ok, Label::kNear);
     InterruptStub stub;
     __ CallStub(&stub);
@@ -379,15 +368,7 @@
   __ test(eax, Immediate(Min(loop_depth(), Code::kMaxLoopNestingMarker)));
 
   if (FLAG_count_based_interrupts) {
-    // Reset the countdown.
-    if (Serializer::enabled()) {
-      __ mov(ebx, Immediate(profiling_counter_));
-      __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
-             Immediate(Smi::FromInt(FLAG_interrupt_budget)));
-    } else {
-      __ mov(Operand::Cell(profiling_counter_),
-             Immediate(Smi::FromInt(FLAG_interrupt_budget)));
-    }
+    EmitProfilingCounterReset();
   }
 
   __ bind(&ok);
@@ -410,37 +391,28 @@
       __ push(eax);
       __ CallRuntime(Runtime::kTraceExit, 1);
     }
-    if (FLAG_interrupt_at_exit) {
+    if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
       // Pretend that the exit is a backwards jump to the entry.
       int weight = 1;
-      if (FLAG_weighted_back_edges) {
+      if (info_->ShouldSelfOptimize()) {
+        weight = FLAG_interrupt_budget / FLAG_self_opt_count;
+      } else if (FLAG_weighted_back_edges) {
         int distance = masm_->pc_offset();
         weight = Min(127, Max(1, distance / 100));
       }
-      if (Serializer::enabled()) {
-        __ mov(ebx, Immediate(profiling_counter_));
-        __ sub(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
-               Immediate(Smi::FromInt(weight)));
-      } else {
-        // This version is slightly faster, but not snapshot safe.
-        __ sub(Operand::Cell(profiling_counter_),
-               Immediate(Smi::FromInt(weight)));
-      }
+      EmitProfilingCounterDecrement(weight);
       Label ok;
       __ j(positive, &ok, Label::kNear);
       __ push(eax);
-      InterruptStub stub;
-      __ CallStub(&stub);
-      __ pop(eax);
-      // Reset the countdown.
-      if (Serializer::enabled()) {
-        __ mov(ebx, Immediate(profiling_counter_));
-        __ mov(FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset),
-               Immediate(Smi::FromInt(FLAG_interrupt_budget)));
+      if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
+        __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
+        __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
       } else {
-        __ mov(Operand::Cell(profiling_counter_),
-               Immediate(Smi::FromInt(FLAG_interrupt_budget)));
+        InterruptStub stub;
+        __ CallStub(&stub);
       }
+      __ pop(eax);
+      EmitProfilingCounterReset();
       __ bind(&ok);
     }
 #ifdef DEBUG
@@ -2961,6 +2933,48 @@
 }
 
 
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  ASSERT(args->length() == 2);
+  ASSERT_NE(NULL, args->at(1)->AsLiteral());
+  Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
+
+  VisitForAccumulatorValue(args->at(0));  // Load the object.
+
+  Label runtime, done;
+  Register object = eax;
+  Register result = eax;
+  Register scratch = ecx;
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+  __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ mov(result, FieldOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ mov(scratch, Operand::StaticVariable(stamp));
+      __ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+      __ j(not_equal, &runtime, Label::kNear);
+      __ mov(result, FieldOperand(object, JSDate::kValueOffset +
+                                          kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2, scratch);
+    __ mov(Operand(esp, 0), object);
+    __ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ bind(&done);
+  }
+  context()->Plug(result);
+}
+
+
 void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
   // Load the arguments on the stack and call the runtime function.
   ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 3d953c3..a656175 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -1273,6 +1273,7 @@
   Register result = ToRegister(instr->result());
   Register map = ToRegister(instr->TempAt(0));
   ASSERT(input.is(result));
+
   Label done;
   // If the object is a smi return the object.
   __ JumpIfSmi(input, &done, Label::kNear);
@@ -1286,6 +1287,43 @@
 }
 
 
+void LCodeGen::DoDateField(LDateField* instr) {
+  Register object = ToRegister(instr->InputAt(0));
+  Register result = ToRegister(instr->result());
+  Register scratch = ToRegister(instr->TempAt(0));
+  Smi* index = instr->index();
+  Label runtime, done;
+  ASSERT(object.is(result));
+  ASSERT(object.is(eax));
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+  __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ mov(result, FieldOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ mov(scratch, Operand::StaticVariable(stamp));
+      __ cmp(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+      __ j(not_equal, &runtime, Label::kNear);
+      __ mov(result, FieldOperand(object, JSDate::kValueOffset +
+                                          kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2, scratch);
+    __ mov(Operand(esp, 0), object);
+    __ mov(Operand(esp, 1 * kPointerSize), Immediate(index));
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ bind(&done);
+  }
+}
+
+
 void LCodeGen::DoBitNotI(LBitNotI* instr) {
   LOperand* input = instr->InputAt(0);
   ASSERT(input->Equals(instr->result()));
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 7587016..223fde2 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1647,6 +1647,14 @@
 }
 
 
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+  LOperand* date = UseFixed(instr->value(), eax);
+  LDateField* result =
+      new(zone()) LDateField(date, FixedTemp(ecx), instr->index());
+  return MarkAsCall(DefineFixed(result, eax), instr);
+}
+
+
 LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
   return AssignEnvironment(new(zone()) LBoundsCheck(
       UseRegisterOrConstantAtStart(instr->index()),
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index bf8dbc6..dd41bfb 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -172,7 +172,8 @@
   V(ForInPrepareMap)                            \
   V(ForInCacheArray)                            \
   V(CheckMapValue)                              \
-  V(LoadFieldByIndex)
+  V(LoadFieldByIndex)                           \
+  V(DateField)
 
 
 #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic)              \
@@ -1002,6 +1003,24 @@
 };
 
 
+class LDateField: public LTemplateInstruction<1, 1, 1> {
+ public:
+  LDateField(LOperand* date, LOperand* temp, Smi* index)
+      : index_(index) {
+    inputs_[0] = date;
+    temps_[0] = temp;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(DateField, "date-field")
+  DECLARE_HYDROGEN_ACCESSOR(DateField)
+
+  Smi* index() const { return index_; }
+
+ private:
+  Smi* index_;
+};
+
+
 class LThrow: public LTemplateInstruction<0, 2, 0> {
  public:
   LThrow(LOperand* context, LOperand* value) {
diff --git a/src/ic.cc b/src/ic.cc
index 88d8a84..c2ee45e 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -2490,9 +2490,13 @@
           return HEAP_NUMBERS;
         }
       }
-      if (!Token::IsEqualityOp(op_)) return GENERIC;
-      if (x->IsSymbol() && y->IsSymbol()) return SYMBOLS;
+      if (x->IsSymbol() && y->IsSymbol()) {
+        // We compare symbols as strings if we need to determine
+        // the order in a non-equality compare.
+        return Token::IsEqualityOp(op_) ? SYMBOLS : STRINGS;
+      }
       if (x->IsString() && y->IsString()) return STRINGS;
+      if (!Token::IsEqualityOp(op_)) return GENERIC;
       if (x->IsJSObject() && y->IsJSObject()) {
         if (Handle<JSObject>::cast(x)->map() ==
             Handle<JSObject>::cast(y)->map() &&
diff --git a/src/isolate.cc b/src/isolate.cc
index 93742bd..3dfcbb5 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -1486,6 +1486,7 @@
       has_installed_extensions_(false),
       string_tracker_(NULL),
       regexp_stack_(NULL),
+      date_cache_(NULL),
       embedder_data_(NULL),
       context_exit_happened_(false) {
   TRACE_ISOLATE(constructor);
@@ -1618,6 +1619,9 @@
   delete unicode_cache_;
   unicode_cache_ = NULL;
 
+  delete date_cache_;
+  date_cache_ = NULL;
+
   delete regexp_stack_;
   regexp_stack_ = NULL;
 
@@ -1782,6 +1786,7 @@
   stub_cache_ = new StubCache(this);
   regexp_stack_ = new RegExpStack();
   regexp_stack_->isolate_ = this;
+  date_cache_ = new DateCache();
 
   // Enable logging before setting up the heap
   logger_->SetUp();
diff --git a/src/isolate.h b/src/isolate.h
index 71fe883..2c2618a 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -36,6 +36,7 @@
 #include "contexts.h"
 #include "execution.h"
 #include "frames.h"
+#include "date.h"
 #include "global-handles.h"
 #include "handles.h"
 #include "hashmap.h"
@@ -1017,6 +1018,17 @@
     return OS::TimeCurrentMillis() - time_millis_at_init_;
   }
 
+  DateCache* date_cache() {
+    return date_cache_;
+  }
+
+  void set_date_cache(DateCache* date_cache) {
+    if (date_cache != date_cache_) {
+      delete date_cache_;
+    }
+    date_cache_ = date_cache;
+  }
+
  private:
   Isolate();
 
@@ -1184,6 +1196,9 @@
   unibrow::Mapping<unibrow::Ecma262Canonicalize>
       regexp_macro_assembler_canonicalize_;
   RegExpStack* regexp_stack_;
+
+  DateCache* date_cache_;
+
   unibrow::Mapping<unibrow::Ecma262Canonicalize> interp_canonicalize_mapping_;
   void* embedder_data_;
 
diff --git a/src/macros.py b/src/macros.py
index 8e9c62d..93287ae 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -164,16 +164,36 @@
 
 # Gets the value of a Date object. If arg is not a Date object
 # a type error is thrown.
-macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateTypeError());
-macro DAY(time) = ($floor(time / 86400000));
-macro NAN_OR_DATE_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : DateFromTime(time));
-macro HOUR_FROM_TIME(time) = (Modulo($floor(time / 3600000), 24));
-macro MIN_FROM_TIME(time) = (Modulo($floor(time / 60000), 60));
-macro NAN_OR_MIN_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MIN_FROM_TIME(time));
-macro SEC_FROM_TIME(time) = (Modulo($floor(time / 1000), 60));
-macro NAN_OR_SEC_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : SEC_FROM_TIME(time));
-macro MS_FROM_TIME(time) = (Modulo(time, 1000));
-macro NAN_OR_MS_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MS_FROM_TIME(time));
+macro CHECK_DATE(arg) = if (%_ClassOf(arg) !== 'Date') ThrowDateTypeError();
+macro LOCAL_DATE_VALUE(arg) = (%_DateField(arg, 0) + %_DateField(arg, 21));
+macro UTC_DATE_VALUE(arg)    = (%_DateField(arg, 0));
+
+macro LOCAL_YEAR(arg)        = (%_DateField(arg, 1));
+macro LOCAL_MONTH(arg)       = (%_DateField(arg, 2));
+macro LOCAL_DAY(arg)         = (%_DateField(arg, 3));
+macro LOCAL_WEEKDAY(arg)     = (%_DateField(arg, 4));
+macro LOCAL_HOUR(arg)        = (%_DateField(arg, 5));
+macro LOCAL_MIN(arg)         = (%_DateField(arg, 6));
+macro LOCAL_SEC(arg)         = (%_DateField(arg, 7));
+macro LOCAL_MS(arg)          = (%_DateField(arg, 8));
+macro LOCAL_DAYS(arg)        = (%_DateField(arg, 9));
+macro LOCAL_TIME_IN_DAY(arg) = (%_DateField(arg, 10));
+
+macro UTC_YEAR(arg)        = (%_DateField(arg, 11));
+macro UTC_MONTH(arg)       = (%_DateField(arg, 12));
+macro UTC_DAY(arg)         = (%_DateField(arg, 13));
+macro UTC_WEEKDAY(arg)     = (%_DateField(arg, 14));
+macro UTC_HOUR(arg)        = (%_DateField(arg, 15));
+macro UTC_MIN(arg)         = (%_DateField(arg, 16));
+macro UTC_SEC(arg)         = (%_DateField(arg, 17));
+macro UTC_MS(arg)          = (%_DateField(arg, 18));
+macro UTC_DAYS(arg)        = (%_DateField(arg, 19));
+macro UTC_TIME_IN_DAY(arg) = (%_DateField(arg, 20));
+
+macro TIMEZONE_OFFSET(arg)   = (%_DateField(arg, 21));
+
+macro SET_UTC_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 1));
+macro SET_LOCAL_DATE_VALUE(arg, value) = (%DateSetValue(arg, value, 0));
 
 # Last input and last subject of regexp matches.
 macro LAST_SUBJECT(array) = ((array)[1]);
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index e259fc4..c5ef2cc 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -1015,6 +1015,16 @@
   // We got a fixed array in register v0. Iterate through that.
   Label non_proxy;
   __ bind(&fixed_array);
+
+  Handle<JSGlobalPropertyCell> cell =
+      isolate()->factory()->NewJSGlobalPropertyCell(
+          Handle<Object>(
+              Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker)));
+  RecordTypeFeedbackCell(stmt->PrepareId(), cell);
+  __ LoadHeapObject(a1, cell);
+  __ li(a2, Operand(Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker)));
+  __ sw(a2, FieldMemOperand(a1, JSGlobalPropertyCell::kValueOffset));
+
   __ li(a1, Operand(Smi::FromInt(1)));  // Smi indicates slow check
   __ lw(a2, MemOperand(sp, 0 * kPointerSize));  // Get enumerated object
   STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 7aef912..16e03e7 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -138,6 +138,9 @@
     case JS_VALUE_TYPE:
       JSValue::cast(this)->JSValueVerify();
       break;
+    case JS_DATE_TYPE:
+      JSDate::cast(this)->JSDateVerify();
+      break;
     case JS_FUNCTION_TYPE:
       JSFunction::cast(this)->JSFunctionVerify();
       break;
@@ -371,6 +374,53 @@
 }
 
 
+void JSDate::JSDateVerify() {
+  if (value()->IsHeapObject()) {
+    VerifyHeapPointer(value());
+  }
+  CHECK(value()->IsUndefined() || value()->IsSmi() || value()->IsHeapNumber());
+  CHECK(year()->IsUndefined() || year()->IsSmi() || year()->IsNaN());
+  CHECK(month()->IsUndefined() || month()->IsSmi() || month()->IsNaN());
+  CHECK(day()->IsUndefined() || day()->IsSmi() || day()->IsNaN());
+  CHECK(weekday()->IsUndefined() || weekday()->IsSmi() || weekday()->IsNaN());
+  CHECK(hour()->IsUndefined() || hour()->IsSmi() || hour()->IsNaN());
+  CHECK(min()->IsUndefined() || min()->IsSmi() || min()->IsNaN());
+  CHECK(sec()->IsUndefined() || sec()->IsSmi() || sec()->IsNaN());
+  CHECK(cache_stamp()->IsUndefined() ||
+        cache_stamp()->IsSmi() ||
+        cache_stamp()->IsNaN());
+
+  if (month()->IsSmi()) {
+    int month = Smi::cast(this->month())->value();
+    CHECK(0 <= month && month <= 11);
+  }
+  if (day()->IsSmi()) {
+    int day = Smi::cast(this->day())->value();
+    CHECK(1 <= day && day <= 31);
+  }
+  if (hour()->IsSmi()) {
+    int hour = Smi::cast(this->hour())->value();
+    CHECK(0 <= hour && hour <= 23);
+  }
+  if (min()->IsSmi()) {
+    int min = Smi::cast(this->min())->value();
+    CHECK(0 <= min && min <= 59);
+  }
+  if (sec()->IsSmi()) {
+    int sec = Smi::cast(this->sec())->value();
+    CHECK(0 <= sec && sec <= 59);
+  }
+  if (weekday()->IsSmi()) {
+    int weekday = Smi::cast(this->weekday())->value();
+    CHECK(0 <= weekday && weekday <= 6);
+  }
+  if (cache_stamp()->IsSmi()) {
+    CHECK(Smi::cast(cache_stamp())->value() <=
+          Smi::cast(Isolate::Current()->date_cache()->stamp())->value());
+  }
+}
+
+
 void JSMessageObject::JSMessageObjectVerify() {
   CHECK(IsJSMessageObject());
   CHECK(type()->IsString());
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 176be9b..d0e9bf8 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -605,6 +605,7 @@
 TYPE_CHECKER(JSGlobalPropertyCell, JS_GLOBAL_PROPERTY_CELL_TYPE)
 TYPE_CHECKER(SharedFunctionInfo, SHARED_FUNCTION_INFO_TYPE)
 TYPE_CHECKER(JSValue, JS_VALUE_TYPE)
+TYPE_CHECKER(JSDate, JS_DATE_TYPE)
 TYPE_CHECKER(JSMessageObject, JS_MESSAGE_OBJECT_TYPE)
 
 
@@ -800,6 +801,11 @@
 }
 
 
+bool Object::IsNaN() {
+  return this->IsHeapNumber() && isnan(HeapNumber::cast(this)->value());
+}
+
+
 MaybeObject* Object::ToSmi() {
   if (IsSmi()) return this;
   if (IsHeapNumber()) {
@@ -1425,6 +1431,8 @@
       return JSFunction::kSize;
     case JS_VALUE_TYPE:
       return JSValue::kSize;
+    case JS_DATE_TYPE:
+      return JSDate::kSize;
     case JS_ARRAY_TYPE:
       return JSArray::kSize;
     case JS_WEAK_MAP_TYPE:
@@ -4119,6 +4127,24 @@
 }
 
 
+ACCESSORS(JSDate, value, Object, kValueOffset)
+ACCESSORS(JSDate, cache_stamp, Object, kCacheStampOffset)
+ACCESSORS(JSDate, year, Object, kYearOffset)
+ACCESSORS(JSDate, month, Object, kMonthOffset)
+ACCESSORS(JSDate, day, Object, kDayOffset)
+ACCESSORS(JSDate, weekday, Object, kWeekdayOffset)
+ACCESSORS(JSDate, hour, Object, kHourOffset)
+ACCESSORS(JSDate, min, Object, kMinOffset)
+ACCESSORS(JSDate, sec, Object, kSecOffset)
+
+
+JSDate* JSDate::cast(Object* obj) {
+  ASSERT(obj->IsJSDate());
+  ASSERT(HeapObject::cast(obj)->Size() == JSDate::kSize);
+  return reinterpret_cast<JSDate*>(obj);
+}
+
+
 ACCESSORS(JSMessageObject, type, String, kTypeOffset)
 ACCESSORS(JSMessageObject, arguments, JSArray, kArgumentsOffset)
 ACCESSORS(JSMessageObject, script, Object, kScriptOffset)
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index d5c02f4..38e6138 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -151,6 +151,9 @@
       PrintF(out, "Value wrapper around:");
       JSValue::cast(this)->value()->Print(out);
       break;
+    case JS_DATE_TYPE:
+      JSDate::cast(this)->value()->Print(out);
+      break;
     case CODE_TYPE:
       Code::cast(this)->CodePrint(out);
       break;
@@ -660,6 +663,30 @@
 }
 
 
+static const char* const weekdays[] = {
+  "???", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+void JSDate::JSDatePrint(FILE* out) {
+  HeapObject::PrintHeader(out, "JSDate");
+  PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+  PrintF(out, " - value = ");
+  value()->Print(out);
+  if (!year()->IsSmi()) {
+    PrintF(out, " - time = NaN\n");
+  } else {
+    PrintF(out, " - time = %s %04d/%02d/%02d %02d:%02d:%02d\n",
+           weekdays[weekday()->IsSmi() ? Smi::cast(weekday())->value() + 1 : 0],
+           year()->IsSmi() ? Smi::cast(year())->value() : -1,
+           month()->IsSmi() ? Smi::cast(month())->value() : -1,
+           day()->IsSmi() ? Smi::cast(day())->value() : -1,
+           hour()->IsSmi() ? Smi::cast(hour())->value() : -1,
+           min()->IsSmi() ? Smi::cast(min())->value() : -1,
+           sec()->IsSmi() ? Smi::cast(sec())->value() : -1);
+  }
+}
+
+
 void JSProxy::JSProxyPrint(FILE* out) {
   HeapObject::PrintHeader(out, "JSProxy");
   PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc
index 9ca102b..c7c8a87 100644
--- a/src/objects-visiting.cc
+++ b/src/objects-visiting.cc
@@ -134,6 +134,7 @@
     case JS_OBJECT_TYPE:
     case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
     case JS_VALUE_TYPE:
+    case JS_DATE_TYPE:
     case JS_ARRAY_TYPE:
     case JS_GLOBAL_PROXY_TYPE:
     case JS_GLOBAL_OBJECT_TYPE:
diff --git a/src/objects.cc b/src/objects.cc
index bb2f2d3..e0a9537 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -33,6 +33,7 @@
 #include "codegen.h"
 #include "debug.h"
 #include "deoptimizer.h"
+#include "date.h"
 #include "elements.h"
 #include "execution.h"
 #include "full-codegen.h"
@@ -1067,7 +1068,7 @@
   switch (map()->instance_type()) {
     case JS_ARRAY_TYPE: {
       double length = JSArray::cast(this)->length()->Number();
-      accumulator->Add("<JS array[%u]>", static_cast<uint32_t>(length));
+      accumulator->Add("<JS Array[%u]>", static_cast<uint32_t>(length));
       break;
     }
     case JS_WEAK_MAP_TYPE: {
@@ -1338,6 +1339,7 @@
     case JS_OBJECT_TYPE:
     case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
     case JS_VALUE_TYPE:
+    case JS_DATE_TYPE:
     case JS_ARRAY_TYPE:
     case JS_SET_TYPE:
     case JS_MAP_TYPE:
@@ -8454,43 +8456,6 @@
 #endif  // ENABLE_DISASSEMBLER
 
 
-static void CopyFastElementsToFast(FixedArray* source,
-                                   FixedArray* destination,
-                                   WriteBarrierMode mode) {
-  int count = source->length();
-  int copy_size = Min(count, destination->length());
-  if (mode == SKIP_WRITE_BARRIER ||
-      !Page::FromAddress(destination->address())->IsFlagSet(
-          MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)) {
-    Address to = destination->address() + FixedArray::kHeaderSize;
-    Address from = source->address() + FixedArray::kHeaderSize;
-    memcpy(reinterpret_cast<void*>(to),
-           reinterpret_cast<void*>(from),
-           kPointerSize * copy_size);
-  } else {
-    for (int i = 0; i < copy_size; ++i) {
-      destination->set(i, source->get(i), mode);
-    }
-  }
-}
-
-
-static void CopySlowElementsToFast(SeededNumberDictionary* source,
-                                   FixedArray* destination,
-                                   WriteBarrierMode mode) {
-  int destination_length = destination->length();
-  for (int i = 0; i < source->Capacity(); ++i) {
-    Object* key = source->KeyAt(i);
-    if (key->IsNumber()) {
-      uint32_t entry = static_cast<uint32_t>(key->Number());
-      if (entry < static_cast<uint32_t>(destination_length)) {
-        destination->set(entry, source->ValueAt(i), mode);
-      }
-    }
-  }
-}
-
-
 MaybeObject* JSObject::SetFastElementsCapacityAndLength(
     int capacity,
     int length,
@@ -8524,79 +8489,17 @@
 
   FixedArrayBase* old_elements_raw = elements();
   ElementsKind elements_kind = GetElementsKind();
-  switch (elements_kind) {
-    case FAST_SMI_ONLY_ELEMENTS:
-    case FAST_ELEMENTS: {
-      AssertNoAllocation no_gc;
-      WriteBarrierMode mode(new_elements->GetWriteBarrierMode(no_gc));
-      CopyFastElementsToFast(FixedArray::cast(old_elements_raw),
-                             new_elements, mode);
-      set_map_and_elements(new_map, new_elements);
-      break;
-    }
-    case DICTIONARY_ELEMENTS: {
-      AssertNoAllocation no_gc;
-      WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
-      CopySlowElementsToFast(SeededNumberDictionary::cast(old_elements_raw),
-                             new_elements,
-                             mode);
-      set_map_and_elements(new_map, new_elements);
-      break;
-    }
-    case NON_STRICT_ARGUMENTS_ELEMENTS: {
-      AssertNoAllocation no_gc;
-      WriteBarrierMode mode = new_elements->GetWriteBarrierMode(no_gc);
-      // The object's map and the parameter map are unchanged, the unaliased
-      // arguments are copied to the new backing store.
-      FixedArray* parameter_map = FixedArray::cast(old_elements_raw);
-      FixedArray* arguments = FixedArray::cast(parameter_map->get(1));
-      if (arguments->IsDictionary()) {
-        CopySlowElementsToFast(SeededNumberDictionary::cast(arguments),
-                               new_elements,
-                               mode);
-      } else {
-        CopyFastElementsToFast(arguments, new_elements, mode);
-      }
-      parameter_map->set(1, new_elements);
-      break;
-    }
-    case FAST_DOUBLE_ELEMENTS: {
-      FixedDoubleArray* old_elements = FixedDoubleArray::cast(old_elements_raw);
-      uint32_t old_length = static_cast<uint32_t>(old_elements->length());
-      // Fill out the new array with this content and array holes.
-      for (uint32_t i = 0; i < old_length; i++) {
-        if (!old_elements->is_the_hole(i)) {
-          Object* obj;
-          // Objects must be allocated in the old object space, since the
-          // overall number of HeapNumbers needed for the conversion might
-          // exceed the capacity of new space, and we would fail repeatedly
-          // trying to convert the FixedDoubleArray.
-          MaybeObject* maybe_value_object =
-              GetHeap()->AllocateHeapNumber(old_elements->get_scalar(i),
-                                            TENURED);
-          if (!maybe_value_object->To(&obj)) return maybe_value_object;
-          // Force write barrier. It's not worth trying to exploit
-          // elems->GetWriteBarrierMode(), since it requires an
-          // AssertNoAllocation stack object that would have to be positioned
-          // after the HeapNumber allocation anyway.
-          new_elements->set(i, obj, UPDATE_WRITE_BARRIER);
-        }
-      }
-      set_map(new_map);
-      set_elements(new_elements);
-      break;
-    }
-    case EXTERNAL_BYTE_ELEMENTS:
-    case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
-    case EXTERNAL_SHORT_ELEMENTS:
-    case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
-    case EXTERNAL_INT_ELEMENTS:
-    case EXTERNAL_UNSIGNED_INT_ELEMENTS:
-    case EXTERNAL_FLOAT_ELEMENTS:
-    case EXTERNAL_DOUBLE_ELEMENTS:
-    case EXTERNAL_PIXEL_ELEMENTS:
-      UNREACHABLE();
-      break;
+  ElementsAccessor* accessor = ElementsAccessor::ForKind(elements_kind);
+  ElementsKind to_kind = (elements_kind == FAST_SMI_ONLY_ELEMENTS)
+      ? FAST_SMI_ONLY_ELEMENTS
+      : FAST_ELEMENTS;
+  //  int copy_size = Min(old_elements_raw->length(), new_elements->length());
+  accessor->CopyElements(this, new_elements, to_kind);
+  if (elements_kind != NON_STRICT_ARGUMENTS_ELEMENTS) {
+    set_map_and_elements(new_map, new_elements);
+  } else {
+    FixedArray* parameter_map = FixedArray::cast(old_elements_raw);
+    parameter_map->set(1, new_elements);
   }
 
   if (FLAG_trace_elements_transitions) {
@@ -12969,4 +12872,133 @@
 #endif  // ENABLE_DEBUGGER_SUPPORT
 
 
+MaybeObject* JSDate::GetField(Object* object, Smi* index) {
+  return JSDate::cast(object)->DoGetField(
+      static_cast<FieldIndex>(index->value()));
+}
+
+
+Object* JSDate::DoGetField(FieldIndex index) {
+  ASSERT(index != kDateValue);
+
+  DateCache* date_cache = GetIsolate()->date_cache();
+
+  if (index < kFirstUncachedField) {
+    Object* stamp = cache_stamp();
+    if (stamp != date_cache->stamp() && stamp->IsSmi()) {
+      // Since the stamp is not NaN, the value is also not NaN.
+      int64_t local_time_ms =
+          date_cache->ToLocal(static_cast<int64_t>(value()->Number()));
+      SetLocalFields(local_time_ms, date_cache);
+    }
+    switch (index) {
+      case kYear: return year();
+      case kMonth: return month();
+      case kDay: return day();
+      case kWeekday: return weekday();
+      case kHour: return hour();
+      case kMinute: return min();
+      case kSecond: return sec();
+      default: UNREACHABLE();
+    }
+  }
+
+  if (index >= kFirstUTCField) {
+    return GetUTCField(index, value()->Number(), date_cache);
+  }
+
+  double time = value()->Number();
+  if (isnan(time)) return GetIsolate()->heap()->nan_value();
+
+  int64_t local_time_ms = date_cache->ToLocal(static_cast<int64_t>(time));
+  int days = DateCache::DaysFromTime(local_time_ms);
+
+  if (index == kDays) return Smi::FromInt(days);
+
+  int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
+  if (index == kMillisecond) return Smi::FromInt(time_in_day_ms % 1000);
+  ASSERT(index == kTimeInDay);
+  return Smi::FromInt(time_in_day_ms);
+}
+
+
+Object* JSDate::GetUTCField(FieldIndex index,
+                            double value,
+                            DateCache* date_cache) {
+  ASSERT(index >= kFirstUTCField);
+
+  if (isnan(value)) return GetIsolate()->heap()->nan_value();
+
+  int64_t time_ms = static_cast<int64_t>(value);
+
+  if (index == kTimezoneOffset) {
+    return Smi::FromInt(date_cache->TimezoneOffset(time_ms));
+  }
+
+  int days = DateCache::DaysFromTime(time_ms);
+
+  if (index == kWeekdayUTC) return Smi::FromInt(date_cache->Weekday(days));
+
+  if (index <= kDayUTC) {
+    int year, month, day;
+    date_cache->YearMonthDayFromDays(days, &year, &month, &day);
+    if (index == kYearUTC) return Smi::FromInt(year);
+    if (index == kMonthUTC) return Smi::FromInt(month);
+    ASSERT(index == kDayUTC);
+    return Smi::FromInt(day);
+  }
+
+  int time_in_day_ms = DateCache::TimeInDay(time_ms, days);
+  switch (index) {
+    case kHourUTC: return Smi::FromInt(time_in_day_ms / (60 * 60 * 1000));
+    case kMinuteUTC: return Smi::FromInt((time_in_day_ms / (60 * 1000)) % 60);
+    case kSecondUTC: return Smi::FromInt((time_in_day_ms / 1000) % 60);
+    case kMillisecondUTC: return Smi::FromInt(time_in_day_ms % 1000);
+    case kDaysUTC: return Smi::FromInt(days);
+    case kTimeInDayUTC: return Smi::FromInt(time_in_day_ms);
+    default: UNREACHABLE();
+  }
+
+  UNREACHABLE();
+  return NULL;
+}
+
+
+void JSDate::SetValue(Object* value, bool is_value_nan) {
+  set_value(value);
+  if (is_value_nan) {
+    HeapNumber* nan = GetIsolate()->heap()->nan_value();
+    set_cache_stamp(nan, SKIP_WRITE_BARRIER);
+    set_year(nan, SKIP_WRITE_BARRIER);
+    set_month(nan, SKIP_WRITE_BARRIER);
+    set_day(nan, SKIP_WRITE_BARRIER);
+    set_hour(nan, SKIP_WRITE_BARRIER);
+    set_min(nan, SKIP_WRITE_BARRIER);
+    set_sec(nan, SKIP_WRITE_BARRIER);
+    set_weekday(nan, SKIP_WRITE_BARRIER);
+  } else {
+    set_cache_stamp(Smi::FromInt(DateCache::kInvalidStamp), SKIP_WRITE_BARRIER);
+  }
+}
+
+
+void JSDate::SetLocalFields(int64_t local_time_ms, DateCache* date_cache) {
+  int days = DateCache::DaysFromTime(local_time_ms);
+  int time_in_day_ms = DateCache::TimeInDay(local_time_ms, days);
+  int year, month, day;
+  date_cache->YearMonthDayFromDays(days, &year, &month, &day);
+  int weekday = date_cache->Weekday(days);
+  int hour = time_in_day_ms / (60 * 60 * 1000);
+  int min = (time_in_day_ms / (60 * 1000)) % 60;
+  int sec = (time_in_day_ms / 1000) % 60;
+  set_cache_stamp(date_cache->stamp());
+  set_year(Smi::FromInt(year), SKIP_WRITE_BARRIER);
+  set_month(Smi::FromInt(month), SKIP_WRITE_BARRIER);
+  set_day(Smi::FromInt(day), SKIP_WRITE_BARRIER);
+  set_weekday(Smi::FromInt(weekday), SKIP_WRITE_BARRIER);
+  set_hour(Smi::FromInt(hour), SKIP_WRITE_BARRIER);
+  set_min(Smi::FromInt(min), SKIP_WRITE_BARRIER);
+  set_sec(Smi::FromInt(sec), SKIP_WRITE_BARRIER);
+}
+
 } }  // namespace v8::internal
diff --git a/src/objects.h b/src/objects.h
index 65cd6fb..7906d14 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -64,6 +64,7 @@
 //             - JSBuiltinsObject
 //           - JSGlobalProxy
 //           - JSValue
+//             - JSDate
 //           - JSMessageObject
 //         - JSProxy
 //           - JSFunctionProxy
@@ -300,6 +301,7 @@
   V(JS_MESSAGE_OBJECT_TYPE)                                                    \
                                                                                \
   V(JS_VALUE_TYPE)                                                             \
+  V(JS_DATE_TYPE)                                                              \
   V(JS_OBJECT_TYPE)                                                            \
   V(JS_CONTEXT_EXTENSION_OBJECT_TYPE)                                          \
   V(JS_GLOBAL_OBJECT_TYPE)                                                     \
@@ -619,6 +621,7 @@
   JS_PROXY_TYPE,  // LAST_JS_PROXY_TYPE
 
   JS_VALUE_TYPE,  // FIRST_JS_OBJECT_TYPE
+  JS_DATE_TYPE,
   JS_OBJECT_TYPE,
   JS_CONTEXT_EXTENSION_OBJECT_TYPE,
   JS_GLOBAL_OBJECT_TYPE,
@@ -813,6 +816,7 @@
   V(Oddball)                                   \
   V(SharedFunctionInfo)                        \
   V(JSValue)                                   \
+  V(JSDate)                                    \
   V(JSMessageObject)                           \
   V(StringWrapper)                             \
   V(Foreign)                                   \
@@ -889,6 +893,7 @@
 
   // Extract the number.
   inline double Number();
+  inline bool IsNaN();
 
   // Returns true if the object is of the correct type to be used as a
   // implementation of a JSObject's elements.
@@ -5994,7 +5999,7 @@
 };
 
 
-// Representation for JS Wrapper objects, String, Number, Boolean, Date, etc.
+// Representation for JS Wrapper objects, String, Number, Boolean, etc.
 class JSValue: public JSObject {
  public:
   // [value]: the object being wrapped.
@@ -6023,6 +6028,106 @@
 };
 
 
+class DateCache;
+
+// Representation for JS date objects.
+class JSDate: public JSObject {
+ public:
+  // If one component is NaN, all of them are, indicating a NaN time value.
+  // [value]: the time value.
+  DECL_ACCESSORS(value, Object)
+  // [year]: caches year. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(year, Object)
+  // [month]: caches month. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(month, Object)
+  // [day]: caches day. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(day, Object)
+  // [weekday]: caches day of week. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(weekday, Object)
+  // [hour]: caches hours. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(hour, Object)
+  // [min]: caches minutes. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(min, Object)
+  // [sec]: caches seconds. Either undefined, smi, or NaN.
+  DECL_ACCESSORS(sec, Object)
+  // [cache stamp]: sample of the date cache stamp at the
+  // moment when local fields were cached.
+  DECL_ACCESSORS(cache_stamp, Object)
+
+  // Casting.
+  static inline JSDate* cast(Object* obj);
+
+  // Returns the date field with the specified index.
+  // See FieldIndex for the list of date fields.
+  static MaybeObject* GetField(Object* date, Smi* index);
+
+  void SetValue(Object* value, bool is_value_nan);
+
+
+  // Dispatched behavior.
+#ifdef OBJECT_PRINT
+  inline void JSDatePrint() {
+    JSDatePrint(stdout);
+  }
+  void JSDatePrint(FILE* out);
+#endif
+#ifdef DEBUG
+  void JSDateVerify();
+#endif
+  // The order is important. It must be kept in sync with date macros
+  // in macros.py.
+  enum FieldIndex {
+    kDateValue,
+    kYear,
+    kMonth,
+    kDay,
+    kWeekday,
+    kHour,
+    kMinute,
+    kSecond,
+    kFirstUncachedField,
+    kMillisecond = kFirstUncachedField,
+    kDays,
+    kTimeInDay,
+    kFirstUTCField,
+    kYearUTC = kFirstUTCField,
+    kMonthUTC,
+    kDayUTC,
+    kWeekdayUTC,
+    kHourUTC,
+    kMinuteUTC,
+    kSecondUTC,
+    kMillisecondUTC,
+    kDaysUTC,
+    kTimeInDayUTC,
+    kTimezoneOffset
+  };
+
+  // Layout description.
+  static const int kValueOffset = JSObject::kHeaderSize;
+  static const int kYearOffset = kValueOffset + kPointerSize;
+  static const int kMonthOffset = kYearOffset + kPointerSize;
+  static const int kDayOffset = kMonthOffset + kPointerSize;
+  static const int kWeekdayOffset = kDayOffset + kPointerSize;
+  static const int kHourOffset = kWeekdayOffset  + kPointerSize;
+  static const int kMinOffset = kHourOffset + kPointerSize;
+  static const int kSecOffset = kMinOffset + kPointerSize;
+  static const int kCacheStampOffset = kSecOffset + kPointerSize;
+  static const int kSize = kCacheStampOffset + kPointerSize;
+
+ private:
+  inline Object* DoGetField(FieldIndex index);
+
+  Object* GetUTCField(FieldIndex index, double value, DateCache* date_cache);
+
+  // Computes and caches the cacheable fields of the date.
+  inline void SetLocalFields(int64_t local_time_ms, DateCache* date_cache);
+
+
+  DISALLOW_IMPLICIT_CONSTRUCTORS(JSDate);
+};
+
+
 // Representation of message objects used for error reporting through
 // the API. The messages are formatted in JavaScript so this object is
 // a real JavaScript object. The information used for formatting the
diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h
index 7a70b01..d967ed3 100644
--- a/src/profile-generator-inl.h
+++ b/src/profile-generator-inl.h
@@ -95,7 +95,7 @@
 }
 
 
-uint64_t HeapObjectsMap::GetNthGcSubrootId(int delta) {
+SnapshotObjectId HeapObjectsMap::GetNthGcSubrootId(int delta) {
   return kGcRootsFirstSubrootId + delta * kObjectIdStep;
 }
 
@@ -115,10 +115,10 @@
 }
 
 
-uint64_t HeapEntry::id() {
+SnapshotObjectId HeapEntry::id() {
   union {
     Id stored_id;
-    uint64_t returned_id;
+    SnapshotObjectId returned_id;
   } id_adaptor = {id_};
   return id_adaptor.returned_id;
 }
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 14349cc..ca97565 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -965,7 +965,7 @@
 void HeapEntry::Init(HeapSnapshot* snapshot,
                      Type type,
                      const char* name,
-                     uint64_t id,
+                     SnapshotObjectId id,
                      int self_size,
                      int children_count,
                      int retainers_count) {
@@ -980,7 +980,7 @@
   dominator_ = NULL;
 
   union {
-    uint64_t set_id;
+    SnapshotObjectId set_id;
     Id stored_id;
   } id_adaptor = {id};
   id_ = id_adaptor.stored_id;
@@ -1096,9 +1096,9 @@
 }
 
 
-int HeapEntry::EntriesSize(int entries_count,
-                           int children_count,
-                           int retainers_count) {
+size_t HeapEntry::EntriesSize(int entries_count,
+                              int children_count,
+                              int retainers_count) {
   return sizeof(HeapEntry) * entries_count         // NOLINT
       + sizeof(HeapGraphEdge) * children_count     // NOLINT
       + sizeof(HeapGraphEdge*) * retainers_count;  // NOLINT
@@ -1114,13 +1114,14 @@
 template <> struct SnapshotSizeConstants<4> {
   static const int kExpectedHeapGraphEdgeSize = 12;
   static const int kExpectedHeapEntrySize = 36;
-  static const int kMaxSerializableSnapshotRawSize = 256 * MB;
+  static const size_t kMaxSerializableSnapshotRawSize = 256 * MB;
 };
 
 template <> struct SnapshotSizeConstants<8> {
   static const int kExpectedHeapGraphEdgeSize = 24;
   static const int kExpectedHeapEntrySize = 48;
-  static const int kMaxSerializableSnapshotRawSize = 768 * MB;
+  static const uint64_t kMaxSerializableSnapshotRawSize =
+      static_cast<uint64_t>(6000) * MB;
 };
 
 }  // namespace
@@ -1221,7 +1222,7 @@
 
 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
                                   const char* name,
-                                  uint64_t id,
+                                  SnapshotObjectId id,
                                   int size,
                                   int children_count,
                                   int retainers_count) {
@@ -1253,7 +1254,7 @@
 }
 
 
-HeapEntry* HeapSnapshot::GetEntryById(uint64_t id) {
+HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
   List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
 
   // Perform a binary search by id.
@@ -1262,7 +1263,7 @@
   while (low <= high) {
     int mid =
         (static_cast<unsigned int>(low) + static_cast<unsigned int>(high)) >> 1;
-    uint64_t mid_id = entries_by_id->at(mid)->id();
+    SnapshotObjectId mid_id = entries_by_id->at(mid)->id();
     if (mid_id > id)
       high = mid - 1;
     else if (mid_id < id)
@@ -1298,12 +1299,12 @@
 
 // We split IDs on evens for embedder objects (see
 // HeapObjectsMap::GenerateId) and odds for native objects.
-const uint64_t HeapObjectsMap::kInternalRootObjectId = 1;
-const uint64_t HeapObjectsMap::kGcRootsObjectId =
+const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
+const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
     HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
-const uint64_t HeapObjectsMap::kGcRootsFirstSubrootId =
+const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
     HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
-const uint64_t HeapObjectsMap::kFirstAvailableObjectId =
+const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
     HeapObjectsMap::kGcRootsFirstSubrootId +
     VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
 
@@ -1325,12 +1326,12 @@
 }
 
 
-uint64_t HeapObjectsMap::FindObject(Address addr) {
+SnapshotObjectId HeapObjectsMap::FindObject(Address addr) {
   if (!initial_fill_mode_) {
-    uint64_t existing = FindEntry(addr);
+    SnapshotObjectId existing = FindEntry(addr);
     if (existing != 0) return existing;
   }
-  uint64_t id = next_id_;
+  SnapshotObjectId id = next_id_;
   next_id_ += kObjectIdStep;
   AddEntry(addr, id);
   return id;
@@ -1353,7 +1354,7 @@
 }
 
 
-void HeapObjectsMap::AddEntry(Address addr, uint64_t id) {
+void HeapObjectsMap::AddEntry(Address addr, SnapshotObjectId id) {
   HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
   ASSERT(entry->value == NULL);
   entry->value = reinterpret_cast<void*>(entries_->length());
@@ -1361,7 +1362,7 @@
 }
 
 
-uint64_t HeapObjectsMap::FindEntry(Address addr) {
+SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
   HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
   if (entry != NULL) {
     int entry_index =
@@ -1401,8 +1402,8 @@
 }
 
 
-uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
-  uint64_t id = static_cast<uint64_t>(info->GetHash());
+SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+  SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
   const char* label = info->GetLabel();
   id ^= HashSequentialString(label,
                              static_cast<int>(strlen(label)),
@@ -1472,7 +1473,8 @@
 }
 
 
-Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(uint64_t id) {
+Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
+    SnapshotObjectId id) {
   // First perform a full GC in order to avoid dead objects.
   HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
                           "HeapSnapshotsCollection::FindHeapObjectById");
@@ -3436,8 +3438,8 @@
   HeapEntry* root = result->AddRootEntry(1);
   const char* text = snapshot_->collection()->names()->GetFormatted(
       "The snapshot is too big. "
-      "Maximum snapshot size is %d MB. "
-      "Actual snapshot size is %d MB.",
+      "Maximum snapshot size is %"  V8_PTR_PREFIX "u MB. "
+      "Actual snapshot size is %"  V8_PTR_PREFIX "u MB.",
       SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB,
       (snapshot_->raw_entries_size() + MB - 1) / MB);
   HeapEntry* message = result->AddEntry(
@@ -3526,21 +3528,21 @@
 
 
 void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
-  // The buffer needs space for 6 ints, 1 uint64_t, 7 commas, \n and \0
+  // The buffer needs space for 6 ints, 1 uint32_t, 7 commas, \n and \0
   static const int kBufferSize =
       6 * MaxDecimalDigitsIn<sizeof(int)>::kSigned  // NOLINT
-      + MaxDecimalDigitsIn<sizeof(uint64_t)>::kUnsigned  // NOLINT
+      + MaxDecimalDigitsIn<sizeof(uint32_t)>::kUnsigned  // NOLINT
       + 7 + 1 + 1;
   EmbeddedVector<char, kBufferSize> buffer;
   Vector<HeapGraphEdge> children = entry->children();
   STATIC_CHECK(sizeof(int) == sizeof(entry->type()));  // NOLINT
   STATIC_CHECK(sizeof(int) == sizeof(GetStringId(entry->name())));  // NOLINT
-  STATIC_CHECK(sizeof(uint64_t) == sizeof(entry->id()));  // NOLINT
+  STATIC_CHECK(sizeof(unsigned) == sizeof(entry->id()));  // NOLINT
   STATIC_CHECK(sizeof(int) == sizeof(entry->self_size()));  // NOLINT
   STATIC_CHECK(sizeof(int) == sizeof(entry->retained_size()));  // NOLINT
   STATIC_CHECK(sizeof(int) == sizeof(GetNodeId(entry->dominator())));  // NOLINT
   STATIC_CHECK(sizeof(int) == sizeof(children.length()));  // NOLINT
-  int result = OS::SNPrintF(buffer, "\n,%d,%d,%llu,%d,%d,%d,%d",
+  int result = OS::SNPrintF(buffer, "\n,%d,%d,%u,%d,%d,%d,%d",
       entry->type(),
       GetStringId(entry->name()),
       entry->id(),
diff --git a/src/profile-generator.h b/src/profile-generator.h
index f9ae5f9..fadae7e 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -35,6 +35,8 @@
 namespace v8 {
 namespace internal {
 
+typedef uint32_t SnapshotObjectId;
+
 class TokenEnumerator {
  public:
   TokenEnumerator();
@@ -533,7 +535,7 @@
   void Init(HeapSnapshot* snapshot,
             Type type,
             const char* name,
-            uint64_t id,
+            SnapshotObjectId id,
             int self_size,
             int children_count,
             int retainers_count);
@@ -542,7 +544,7 @@
   Type type() { return static_cast<Type>(type_); }
   const char* name() { return name_; }
   void set_name(const char* name) { name_ = name; }
-  inline uint64_t id();
+  inline SnapshotObjectId id();
   int self_size() { return self_size_; }
   int retained_size() { return retained_size_; }
   void add_retained_size(int size) { retained_size_ += size; }
@@ -575,16 +577,18 @@
                          int retainer_index);
   void SetUnidirElementReference(int child_index, int index, HeapEntry* entry);
 
-  int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); }
+  size_t EntrySize() {
+    return EntriesSize(1, children_count_, retainers_count_);
+  }
 
   void Print(
       const char* prefix, const char* edge_name, int max_depth, int indent);
 
   Handle<HeapObject> GetHeapObject();
 
-  static int EntriesSize(int entries_count,
-                         int children_count,
-                         int retainers_count);
+  static size_t EntriesSize(int entries_count,
+                            int children_count,
+                            int retainers_count);
 
  private:
   HeapGraphEdge* children_arr() {
@@ -645,13 +649,13 @@
   HeapEntry* natives_root() { return natives_root_entry_; }
   HeapEntry* gc_subroot(int index) { return gc_subroot_entries_[index]; }
   List<HeapEntry*>* entries() { return &entries_; }
-  int raw_entries_size() { return raw_entries_size_; }
+  size_t raw_entries_size() { return raw_entries_size_; }
 
   void AllocateEntries(
       int entries_count, int children_count, int retainers_count);
   HeapEntry* AddEntry(HeapEntry::Type type,
                       const char* name,
-                      uint64_t id,
+                      SnapshotObjectId id,
                       int size,
                       int children_count,
                       int retainers_count);
@@ -662,7 +666,7 @@
                                int retainers_count);
   HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
   void ClearPaint();
-  HeapEntry* GetEntryById(uint64_t id);
+  HeapEntry* GetEntryById(SnapshotObjectId id);
   List<HeapEntry*>* GetSortedEntriesList();
   template<class Visitor>
   void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); }
@@ -685,7 +689,7 @@
   char* raw_entries_;
   List<HeapEntry*> entries_;
   bool entries_sorted_;
-  int raw_entries_size_;
+  size_t raw_entries_size_;
 
   friend class HeapSnapshotTester;
 
@@ -699,29 +703,31 @@
   ~HeapObjectsMap();
 
   void SnapshotGenerationFinished();
-  uint64_t FindObject(Address addr);
+  SnapshotObjectId FindObject(Address addr);
   void MoveObject(Address from, Address to);
 
-  static uint64_t GenerateId(v8::RetainedObjectInfo* info);
-  static inline uint64_t GetNthGcSubrootId(int delta);
+  static SnapshotObjectId GenerateId(v8::RetainedObjectInfo* info);
+  static inline SnapshotObjectId GetNthGcSubrootId(int delta);
 
   static const int kObjectIdStep = 2;
-  static const uint64_t kInternalRootObjectId;
-  static const uint64_t kGcRootsObjectId;
-  static const uint64_t kNativesRootObjectId;
-  static const uint64_t kGcRootsFirstSubrootId;
-  static const uint64_t kFirstAvailableObjectId;
+  static const SnapshotObjectId kInternalRootObjectId;
+  static const SnapshotObjectId kGcRootsObjectId;
+  static const SnapshotObjectId kNativesRootObjectId;
+  static const SnapshotObjectId kGcRootsFirstSubrootId;
+  static const SnapshotObjectId kFirstAvailableObjectId;
 
  private:
   struct EntryInfo {
-    explicit EntryInfo(uint64_t id) : id(id), accessed(true) { }
-    EntryInfo(uint64_t id, bool accessed) : id(id), accessed(accessed) { }
-    uint64_t id;
+    explicit EntryInfo(SnapshotObjectId id) : id(id), accessed(true) { }
+    EntryInfo(SnapshotObjectId id, bool accessed)
+      : id(id),
+        accessed(accessed) { }
+    SnapshotObjectId id;
     bool accessed;
   };
 
-  void AddEntry(Address addr, uint64_t id);
-  uint64_t FindEntry(Address addr);
+  void AddEntry(Address addr, SnapshotObjectId id);
+  SnapshotObjectId FindEntry(Address addr);
   void RemoveDeadEntries();
 
   static bool AddressesMatch(void* key1, void* key2) {
@@ -735,7 +741,7 @@
   }
 
   bool initial_fill_mode_;
-  uint64_t next_id_;
+  SnapshotObjectId next_id_;
   HashMap entries_map_;
   List<EntryInfo>* entries_;
 
@@ -760,8 +766,8 @@
   StringsStorage* names() { return &names_; }
   TokenEnumerator* token_enumerator() { return token_enumerator_; }
 
-  uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
-  Handle<HeapObject> FindHeapObjectById(uint64_t id);
+  SnapshotObjectId GetObjectId(Address addr) { return ids_.FindObject(addr); }
+  Handle<HeapObject> FindHeapObjectById(SnapshotObjectId id);
   void ObjectMoveEvent(Address from, Address to) { ids_.MoveObject(from, to); }
 
  private:
diff --git a/src/runtime.cc b/src/runtime.cc
index 019851e..cc5aeab 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -40,6 +40,7 @@
 #include "dateparser-inl.h"
 #include "debug.h"
 #include "deoptimizer.h"
+#include "date.h"
 #include "execution.h"
 #include "global-handles.h"
 #include "isolate-inl.h"
@@ -7542,51 +7543,6 @@
 }
 
 
-static int MakeDay(int year, int month) {
-  static const int day_from_month[] = {0, 31, 59, 90, 120, 151,
-                                       181, 212, 243, 273, 304, 334};
-  static const int day_from_month_leap[] = {0, 31, 60, 91, 121, 152,
-                                            182, 213, 244, 274, 305, 335};
-
-  year += month / 12;
-  month %= 12;
-  if (month < 0) {
-    year--;
-    month += 12;
-  }
-
-  ASSERT(month >= 0);
-  ASSERT(month < 12);
-
-  // year_delta is an arbitrary number such that:
-  // a) year_delta = -1 (mod 400)
-  // b) year + year_delta > 0 for years in the range defined by
-  //    ECMA 262 - 15.9.1.1, i.e. upto 100,000,000 days on either side of
-  //    Jan 1 1970. This is required so that we don't run into integer
-  //    division of negative numbers.
-  // c) there shouldn't be an overflow for 32-bit integers in the following
-  //    operations.
-  static const int year_delta = 399999;
-  static const int base_day = 365 * (1970 + year_delta) +
-                              (1970 + year_delta) / 4 -
-                              (1970 + year_delta) / 100 +
-                              (1970 + year_delta) / 400;
-
-  int year1 = year + year_delta;
-  int day_from_year = 365 * year1 +
-                      year1 / 4 -
-                      year1 / 100 +
-                      year1 / 400 -
-                      base_day;
-
-  if ((year % 4 != 0) || (year % 100 == 0 && year % 400 != 0)) {
-    return day_from_year + day_from_month[month];
-  }
-
-  return day_from_year + day_from_month_leap[month];
-}
-
-
 RUNTIME_FUNCTION(MaybeObject*, Runtime_DateMakeDay) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 2);
@@ -7594,319 +7550,44 @@
   CONVERT_SMI_ARG_CHECKED(year, 0);
   CONVERT_SMI_ARG_CHECKED(month, 1);
 
-  return Smi::FromInt(MakeDay(year, month));
+  return Smi::FromInt(isolate->date_cache()->DaysFromYearMonth(year, month));
 }
 
 
-static const int kDays4Years[] = {0, 365, 2 * 365, 3 * 365 + 1};
-static const int kDaysIn4Years = 4 * 365 + 1;
-static const int kDaysIn100Years = 25 * kDaysIn4Years - 1;
-static const int kDaysIn400Years = 4 * kDaysIn100Years + 1;
-static const int kDays1970to2000 = 30 * 365 + 7;
-static const int kDaysOffset = 1000 * kDaysIn400Years + 5 * kDaysIn400Years -
-                               kDays1970to2000;
-static const int kYearsOffset = 400000;
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateSetValue) {
+  HandleScope scope(isolate);
+  ASSERT(args.length() == 3);
 
-static const char kDayInYear[] = {
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+  CONVERT_ARG_HANDLE_CHECKED(JSDate, date, 0);
+  CONVERT_DOUBLE_ARG_CHECKED(time, 1);
+  CONVERT_SMI_ARG_CHECKED(is_utc, 2);
 
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+  DateCache* date_cache = isolate->date_cache();
 
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30,
-      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
-      22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
-
-static const char kMonthInYear[] = {
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      2, 2, 2, 2, 2, 2,
-      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-      3, 3, 3, 3, 3,
-      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-      4, 4, 4, 4, 4, 4,
-      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      5, 5, 5, 5, 5,
-      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-      6, 6, 6, 6, 6, 6,
-      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-      7, 7, 7, 7, 7, 7,
-      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-      8, 8, 8, 8, 8,
-      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
-      9, 9, 9, 9, 9, 9,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      2, 2, 2, 2, 2, 2,
-      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-      3, 3, 3, 3, 3,
-      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-      4, 4, 4, 4, 4, 4,
-      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      5, 5, 5, 5, 5,
-      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-      6, 6, 6, 6, 6, 6,
-      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-      7, 7, 7, 7, 7, 7,
-      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-      8, 8, 8, 8, 8,
-      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
-      9, 9, 9, 9, 9, 9,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1, 1,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      2, 2, 2, 2, 2, 2,
-      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-      3, 3, 3, 3, 3,
-      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-      4, 4, 4, 4, 4, 4,
-      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      5, 5, 5, 5, 5,
-      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-      6, 6, 6, 6, 6, 6,
-      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-      7, 7, 7, 7, 7, 7,
-      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-      8, 8, 8, 8, 8,
-      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
-      9, 9, 9, 9, 9, 9,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-      0, 0, 0, 0, 0, 0,
-      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-      1, 1, 1,
-      2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
-      2, 2, 2, 2, 2, 2,
-      3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-      3, 3, 3, 3, 3,
-      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
-      4, 4, 4, 4, 4, 4,
-      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      5, 5, 5, 5, 5,
-      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
-      6, 6, 6, 6, 6, 6,
-      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
-      7, 7, 7, 7, 7, 7,
-      8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
-      8, 8, 8, 8, 8,
-      9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
-      9, 9, 9, 9, 9, 9,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
-      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11};
-
-
-// This function works for dates from 1970 to 2099.
-static inline void DateYMDFromTimeAfter1970(int date,
-                                            int& year, int& month, int& day) {
-#ifdef DEBUG
-  int save_date = date;  // Need this for ASSERT in the end.
-#endif
-
-  year = 1970 + (4 * date + 2) / kDaysIn4Years;
-  date %= kDaysIn4Years;
-
-  month = kMonthInYear[date];
-  day = kDayInYear[date];
-
-  ASSERT(MakeDay(year, month) + day - 1 == save_date);
-}
-
-
-static inline void DateYMDFromTimeSlow(int date,
-                                       int& year, int& month, int& day) {
-#ifdef DEBUG
-  int save_date = date;  // Need this for ASSERT in the end.
-#endif
-
-  date += kDaysOffset;
-  year = 400 * (date / kDaysIn400Years) - kYearsOffset;
-  date %= kDaysIn400Years;
-
-  ASSERT(MakeDay(year, 0) + date == save_date);
-
-  date--;
-  int yd1 = date / kDaysIn100Years;
-  date %= kDaysIn100Years;
-  year += 100 * yd1;
-
-  date++;
-  int yd2 = date / kDaysIn4Years;
-  date %= kDaysIn4Years;
-  year += 4 * yd2;
-
-  date--;
-  int yd3 = date / 365;
-  date %= 365;
-  year += yd3;
-
-  bool is_leap = (!yd1 || yd2) && !yd3;
-
-  ASSERT(date >= -1);
-  ASSERT(is_leap || (date >= 0));
-  ASSERT((date < 365) || (is_leap && (date < 366)));
-  ASSERT(is_leap == ((year % 4 == 0) && (year % 100 || (year % 400 == 0))));
-  ASSERT(is_leap || ((MakeDay(year, 0) + date) == save_date));
-  ASSERT(!is_leap || ((MakeDay(year, 0) + date + 1) == save_date));
-
-  if (is_leap) {
-    day = kDayInYear[2*365 + 1 + date];
-    month = kMonthInYear[2*365 + 1 + date];
+  Object* value = NULL;
+  bool is_value_nan = false;
+  if (isnan(time)) {
+    value = isolate->heap()->nan_value();
+    is_value_nan = true;
+  } else if (!is_utc &&
+             (time < -DateCache::kMaxTimeBeforeUTCInMs ||
+              time > DateCache::kMaxTimeBeforeUTCInMs)) {
+    value = isolate->heap()->nan_value();
+    is_value_nan = true;
   } else {
-    day = kDayInYear[date];
-    month = kMonthInYear[date];
+    time = is_utc ? time : date_cache->ToUTC(static_cast<int64_t>(time));
+    if (time < -DateCache::kMaxTimeInMs ||
+        time > DateCache::kMaxTimeInMs) {
+      value = isolate->heap()->nan_value();
+      is_value_nan = true;
+    } else  {
+      MaybeObject* maybe_result =
+          isolate->heap()->AllocateHeapNumber(DoubleToInteger(time));
+      if (!maybe_result->ToObject(&value)) return maybe_result;
+    }
   }
-
-  ASSERT(MakeDay(year, month) + day - 1 == save_date);
-}
-
-
-static inline void DateYMDFromTime(int date,
-                                   int& year, int& month, int& day) {
-  if (date >= 0 && date < 32 * kDaysIn4Years) {
-    DateYMDFromTimeAfter1970(date, year, month, day);
-  } else {
-    DateYMDFromTimeSlow(date, year, month, day);
-  }
-}
-
-
-RUNTIME_FUNCTION(MaybeObject*, Runtime_DateYMDFromTime) {
-  NoHandleAllocation ha;
-  ASSERT(args.length() == 2);
-
-  CONVERT_DOUBLE_ARG_CHECKED(t, 0);
-  CONVERT_ARG_CHECKED(JSArray, res_array, 1);
-
-  int year, month, day;
-  DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day);
-
-  FixedArrayBase* elms_base = FixedArrayBase::cast(res_array->elements());
-  RUNTIME_ASSERT(elms_base->length() == 3);
-  RUNTIME_ASSERT(res_array->HasFastTypeElements());
-
-  MaybeObject* maybe = res_array->EnsureWritableFastElements();
-  if (maybe->IsFailure()) return maybe;
-  FixedArray* elms = FixedArray::cast(res_array->elements());
-  elms->set(0, Smi::FromInt(year));
-  elms->set(1, Smi::FromInt(month));
-  elms->set(2, Smi::FromInt(day));
-
-  return isolate->heap()->undefined_value();
+  date->SetValue(value, is_value_nan);
+  return *date;
 }
 
 
@@ -9382,25 +9063,20 @@
   ASSERT(args.length() == 1);
 
   CONVERT_DOUBLE_ARG_CHECKED(x, 0);
-  const char* zone = OS::LocalTimezone(x);
+  int64_t time = isolate->date_cache()->EquivalentTime(static_cast<int64_t>(x));
+  const char* zone = OS::LocalTimezone(static_cast<double>(time));
   return isolate->heap()->AllocateStringFromUtf8(CStrVector(zone));
 }
 
 
-RUNTIME_FUNCTION(MaybeObject*, Runtime_DateLocalTimeOffset) {
-  NoHandleAllocation ha;
-  ASSERT(args.length() == 0);
-
-  return isolate->heap()->NumberFromDouble(OS::LocalTimeOffset());
-}
-
-
-RUNTIME_FUNCTION(MaybeObject*, Runtime_DateDaylightSavingsOffset) {
+RUNTIME_FUNCTION(MaybeObject*, Runtime_DateToUTC) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 1);
 
   CONVERT_DOUBLE_ARG_CHECKED(x, 0);
-  return isolate->heap()->NumberFromDouble(OS::DaylightSavingsOffset(x));
+  int64_t time = isolate->date_cache()->ToUTC(static_cast<int64_t>(x));
+
+  return isolate->heap()->NumberFromDouble(static_cast<double>(time));
 }
 
 
diff --git a/src/runtime.h b/src/runtime.h
index 1f9566b..c5ce3c3 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -244,10 +244,9 @@
   F(DateCurrentTime, 0, 1) \
   F(DateParseString, 2, 1) \
   F(DateLocalTimezone, 1, 1) \
-  F(DateLocalTimeOffset, 0, 1) \
-  F(DateDaylightSavingsOffset, 1, 1) \
+  F(DateToUTC, 1, 1) \
   F(DateMakeDay, 2, 1) \
-  F(DateYMDFromTime, 2, 1) \
+  F(DateSetValue, 3, 1) \
   \
   /* Numbers */ \
   \
@@ -491,6 +490,7 @@
   F(Arguments, 1, 1)                                                         \
   F(ValueOf, 1, 1)                                                           \
   F(SetValueOf, 2, 1)                                                        \
+  F(DateField, 2 /* date object, field index */, 1)                \
   F(StringCharFromCode, 1, 1)                                                \
   F(StringCharAt, 2, 1)                                                      \
   F(ObjectEquals, 2, 1)                                                      \
diff --git a/src/scopes.cc b/src/scopes.cc
index 766e1ae..859cbd1 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -1006,7 +1006,7 @@
       Isolate* isolate = Isolate::Current();
       Factory* factory = isolate->factory();
       Handle<JSArray> array = factory->NewJSArray(1);
-      array->SetElement(array, 0, var->name(), NONE, kStrictMode);
+      USE(JSObject::SetElement(array, 0, var->name(), NONE, kStrictMode));
       Handle<Object> result =
           factory->NewSyntaxError("module_type_error", array);
       isolate->Throw(*result, &location);
diff --git a/src/serialize.cc b/src/serialize.cc
index 8d8dbfd..4249d36 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -502,6 +502,14 @@
       UNCLASSIFIED,
       45,
       "the_hole_nan");
+  Add(ExternalReference::get_date_field_function(isolate).address(),
+      UNCLASSIFIED,
+      46,
+      "JSDate::GetField");
+  Add(ExternalReference::date_cache_stamp(isolate).address(),
+      UNCLASSIFIED,
+      47,
+      "date_cache_stamp");
 }
 
 
diff --git a/src/version.cc b/src/version.cc
index 57c60d0..57e6594 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
 // cannot be changed without changing the SCons build script.
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     9
-#define BUILD_NUMBER      16
+#define BUILD_NUMBER      17
 #define PATCH_LEVEL       0
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 1354e62..d616749 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -5454,7 +5454,7 @@
   __ movb(scratch, Operand(left, index, times_1, 0));
   __ cmpb(scratch, Operand(right, index, times_1, 0));
   __ j(not_equal, chars_not_equal, near_jump);
-  __ addq(index, Immediate(1));
+  __ incq(index);
   __ j(not_zero, &loop);
 }
 
@@ -5625,9 +5625,10 @@
 
 void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
   ASSERT(state_ == CompareIC::STRINGS);
-  ASSERT(GetCondition() == equal);
   Label miss;
 
+  bool equality = Token::IsEqualityOp(op_);
+
   // Registers containing left and right operands respectively.
   Register left = rdx;
   Register right = rax;
@@ -5665,24 +5666,31 @@
 
   // Check that both strings are symbols. If they are, we're done
   // because we already know they are not identical.
-  Label do_compare;
-  STATIC_ASSERT(kSymbolTag != 0);
-  __ and_(tmp1, tmp2);
-  __ testb(tmp1, Immediate(kIsSymbolMask));
-  __ j(zero, &do_compare, Label::kNear);
-  // Make sure rax is non-zero. At this point input operands are
-  // guaranteed to be non-zero.
-  ASSERT(right.is(rax));
-  __ ret(0);
+  if (equality) {
+    Label do_compare;
+    STATIC_ASSERT(kSymbolTag != 0);
+    __ and_(tmp1, tmp2);
+    __ testb(tmp1, Immediate(kIsSymbolMask));
+    __ j(zero, &do_compare, Label::kNear);
+    // Make sure rax is non-zero. At this point input operands are
+    // guaranteed to be non-zero.
+    ASSERT(right.is(rax));
+    __ ret(0);
+    __ bind(&do_compare);
+  }
 
   // Check that both strings are sequential ASCII.
   Label runtime;
-  __ bind(&do_compare);
   __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
 
   // Compare flat ASCII strings. Returns when done.
-  StringCompareStub::GenerateFlatAsciiStringEquals(
-      masm, left, right, tmp1, tmp2);
+  if (equality) {
+    StringCompareStub::GenerateFlatAsciiStringEquals(
+        masm, left, right, tmp1, tmp2);
+  } else {
+    StringCompareStub::GenerateCompareFlatAsciiStrings(
+        masm, left, right, tmp1, tmp2, tmp3, kScratchRegister);
+  }
 
   // Handle more complex cases in runtime.
   __ bind(&runtime);
@@ -5690,7 +5698,11 @@
   __ push(left);
   __ push(right);
   __ push(tmp1);
-  __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  if (equality) {
+    __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+  } else {
+    __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
+  }
 
   __ bind(&miss);
   GenerateMiss(masm);
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 49adf6a..6739cc8 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -2834,6 +2834,54 @@
 }
 
 
+void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
+  ZoneList<Expression*>* args = expr->arguments();
+  ASSERT(args->length() == 2);
+  ASSERT_NE(NULL, args->at(1)->AsLiteral());
+  Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
+
+  VisitForAccumulatorValue(args->at(0));  // Load the object.
+
+  Label runtime, done;
+  Register object = rax;
+  Register result = rax;
+  Register scratch = rcx;
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CmpObjectType(object, JS_DATE_TYPE, scratch);
+  __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ movq(result, FieldOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ movq(scratch, stamp);
+      __ cmpq(scratch, FieldOperand(object, JSDate::kCacheStampOffset));
+      __ j(not_equal, &runtime, Label::kNear);
+      __ movq(result, FieldOperand(object, JSDate::kValueOffset +
+                                           kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2);
+#ifdef _WIN64
+  __ movq(rcx, object);
+  __ movq(rdx, index, RelocInfo::NONE);
+#else
+  __ movq(rdi, object);
+  __ movq(rsi, index, RelocInfo::NONE);
+#endif
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+    __ bind(&done);
+  }
+  context()->Plug(rax);
+}
+
+
 void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
   // Load the arguments on the stack and call the runtime function.
   ZoneList<Expression*>* args = expr->arguments();
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 893ec59..f707df0 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -1224,6 +1224,49 @@
 }
 
 
+void LCodeGen::DoDateField(LDateField* instr) {
+  Register object = ToRegister(instr->InputAt(0));
+  Register result = ToRegister(instr->result());
+  Smi* index = instr->index();
+  Label runtime, done;
+  ASSERT(object.is(result));
+  ASSERT(object.is(rax));
+
+#ifdef DEBUG
+  __ AbortIfSmi(object);
+  __ CmpObjectType(object, JS_DATE_TYPE, kScratchRegister);
+  __ Assert(equal, "Trying to get date field from non-date.");
+#endif
+
+  if (index->value() == 0) {
+    __ movq(result, FieldOperand(object, JSDate::kValueOffset));
+  } else {
+    if (index->value() < JSDate::kFirstUncachedField) {
+      ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
+      __ movq(kScratchRegister, stamp);
+      __ cmpq(kScratchRegister, FieldOperand(object,
+                                             JSDate::kCacheStampOffset));
+      __ j(not_equal, &runtime, Label::kNear);
+      __ movq(result, FieldOperand(object, JSDate::kValueOffset +
+                                           kPointerSize * index->value()));
+      __ jmp(&done);
+    }
+    __ bind(&runtime);
+    __ PrepareCallCFunction(2);
+#ifdef _WIN64
+  __ movq(rcx, object);
+  __ movq(rdx, index, RelocInfo::NONE);
+#else
+  __ movq(rdi, object);
+  __ movq(rsi, index, RelocInfo::NONE);
+#endif
+    __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
+    __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+    __ bind(&done);
+  }
+}
+
+
 void LCodeGen::DoBitNotI(LBitNotI* instr) {
   LOperand* input = instr->InputAt(0);
   ASSERT(input->Equals(instr->result()));
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index 79ca5f5..e2569c9 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1601,6 +1601,13 @@
 }
 
 
+LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
+  LOperand* object = UseFixed(instr->value(), rax);
+  LDateField* result = new LDateField(object, instr->index());
+  return MarkAsCall(DefineFixed(result, rax), instr);
+}
+
+
 LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
   LOperand* value = UseRegisterOrConstantAtStart(instr->index());
   LOperand* length = Use(instr->length());
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index 5f5a201..3d5d854 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -177,7 +177,8 @@
   V(ForInPrepareMap)                            \
   V(ForInCacheArray)                            \
   V(CheckMapValue)                              \
-  V(LoadFieldByIndex)
+  V(LoadFieldByIndex)                           \
+  V(DateField)
 
 
 #define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic)              \
@@ -987,6 +988,22 @@
 };
 
 
+class LDateField: public LTemplateInstruction<1, 1, 0> {
+ public:
+  LDateField(LOperand* date, Smi* index) : index_(index) {
+    inputs_[0] = date;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
+  DECLARE_HYDROGEN_ACCESSOR(ValueOf)
+
+  Smi* index() const { return index_; }
+
+ private:
+  Smi* index_;
+};
+
+
 class LThrow: public LTemplateInstruction<0, 1, 0> {
  public:
   explicit LThrow(LOperand* value) {