Version 3.2.2

Fixed a number of crash and correctness bugs.

Improved Crankshaft performance on all platforms.

Fixed Crankshaft on Solaris/Illumos.


git-svn-id: http://v8.googlecode.com/svn/trunk@7162 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 32a58b2..2c330b3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2011-03-14: Version 3.2.2
+
+        Fixed a number of crash and correctness bugs.
+
+        Improved Crankshaft performance on all platforms.
+
+        Fixed Crankshaft on Solaris/Illumos.
+
+
 2011-03-10: Version 3.2.1
 
         Fixed a number of crash bugs.
diff --git a/include/v8-profiler.h b/include/v8-profiler.h
index 675a229..d466d5e 100644
--- a/include/v8-profiler.h
+++ b/include/v8-profiler.h
@@ -245,14 +245,15 @@
 class V8EXPORT HeapGraphNode {
  public:
   enum Type {
-    kHidden = 0,     // Hidden node, may be filtered when shown to user.
-    kArray = 1,      // An array of elements.
-    kString = 2,     // A string.
-    kObject = 3,     // A JS object (except for arrays and strings).
-    kCode = 4,       // Compiled code.
-    kClosure = 5,    // Function closure.
-    kRegExp = 6,     // RegExp.
-    kHeapNumber = 7  // Number stored in the heap.
+    kHidden = 0,      // Hidden node, may be filtered when shown to user.
+    kArray = 1,       // An array of elements.
+    kString = 2,      // A string.
+    kObject = 3,      // A JS object (except for arrays and strings).
+    kCode = 4,        // Compiled code.
+    kClosure = 5,     // Function closure.
+    kRegExp = 6,      // RegExp.
+    kHeapNumber = 7,  // Number stored in the heap.
+    kNative = 8       // Native object (not from V8 heap).
   };
 
   /** Returns node type (see HeapGraphNode::Type). */
@@ -392,11 +393,22 @@
 };
 
 
+class RetainedObjectInfo;
+
 /**
  * Interface for controlling heap profiling.
  */
 class V8EXPORT HeapProfiler {
  public:
+  /**
+   * Callback function invoked for obtaining RetainedObjectInfo for
+   * the given JavaScript wrapper object. It is prohibited to enter V8
+   * while the callback is running: only getters on the handle and
+   * GetPointerFromInternalField on the objects are allowed.
+   */
+  typedef RetainedObjectInfo* (*WrapperInfoCallback)
+      (uint16_t class_id, Handle<Value> wrapper);
+
   /** Returns the number of snapshots taken. */
   static int GetSnapshotsCount();
 
@@ -414,6 +426,81 @@
       Handle<String> title,
       HeapSnapshot::Type type = HeapSnapshot::kFull,
       ActivityControl* control = NULL);
+
+  /** Binds a callback to embedder's class ID. */
+  static void DefineWrapperClass(
+      uint16_t class_id,
+      WrapperInfoCallback callback);
+
+  /**
+   * Default value of persistent handle class ID. Must not be used to
+   * define a class. Can be used to reset a class of a persistent
+   * handle.
+   */
+  static const uint16_t kPersistentHandleNoClassId = 0;
+};
+
+
+/**
+ * Interface for providing information about embedder's objects
+ * held by global handles. This information is reported in two ways:
+ *
+ *  1. When calling AddObjectGroup, an embedder may pass
+ *     RetainedObjectInfo instance describing the group.  To collect
+ *     this information while taking a heap snapshot, V8 calls GC
+ *     prologue and epilogue callbacks.
+ *
+ *  2. When a heap snapshot is collected, V8 additionally
+ *     requests RetainedObjectInfos for persistent handles that
+ *     were not previously reported via AddObjectGroup.
+ *
+ * Thus, if an embedder wants to provide information about native
+ * objects for heap snapshots, he can do it in a GC prologue
+ * handler, and / or by assigning wrapper class ids in the following way:
+ *
+ *  1. Bind a callback to class id by calling DefineWrapperClass.
+ *  2. Call SetWrapperClassId on certain persistent handles.
+ *
+ * V8 takes ownership of RetainedObjectInfo instances passed to it and
+ * keeps them alive only during snapshot collection. Afterwards, they
+ * are freed by calling the Dispose class function.
+ */
+class V8EXPORT RetainedObjectInfo {  // NOLINT
+ public:
+  /** Called by V8 when it no longer needs an instance. */
+  virtual void Dispose() = 0;
+
+  /** Returns whether two instances are equivalent. */
+  virtual bool IsEquivalent(RetainedObjectInfo* other) = 0;
+
+  /**
+   * Returns hash value for the instance. Equivalent instances
+   * must have the same hash value.
+   */
+  virtual intptr_t GetHash() = 0;
+
+  /**
+   * Returns human-readable label. It must be a NUL-terminated UTF-8
+   * encoded string. V8 copies its contents during a call to GetLabel.
+   */
+  virtual const char* GetLabel() = 0;
+
+  /**
+   * Returns element count in case if a global handle retains
+   * a subgraph by holding one of its nodes.
+   */
+  virtual intptr_t GetElementCount() { return -1; }
+
+  /** Returns embedder's object size in bytes. */
+  virtual intptr_t GetSizeInBytes() { return -1; }
+
+ protected:
+  RetainedObjectInfo() {}
+  virtual ~RetainedObjectInfo() {}
+
+ private:
+  RetainedObjectInfo(const RetainedObjectInfo&);
+  RetainedObjectInfo& operator=(const RetainedObjectInfo&);
 };
 
 
diff --git a/include/v8-testing.h b/include/v8-testing.h
index 4db30a4..245f74d 100644
--- a/include/v8-testing.h
+++ b/include/v8-testing.h
@@ -87,6 +87,11 @@
    * should be between 0 and one less than the result from GetStressRuns()
    */
   static void PrepareStressRun(int run);
+
+  /**
+   * Force deoptimization of all functions.
+   */
+  static void DeoptimizeAll();
 };
 
 
diff --git a/include/v8.h b/include/v8.h
index 7b3d3f9..ddbd6af 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -396,6 +396,12 @@
    */
   inline bool IsWeak() const;
 
+  /**
+   * Assigns a wrapper class ID to the handle. See RetainedObjectInfo
+   * interface description in v8-profiler.h for details.
+   */
+  inline void SetWrapperClassId(uint16_t class_id);
+
  private:
   friend class ImplementationUtilities;
   friend class ObjectTemplate;
@@ -2534,6 +2540,8 @@
 };
 
 
+class RetainedObjectInfo;
+
 /**
  * Container class for static utility functions.
  */
@@ -2703,8 +2711,11 @@
    * intended to be used in the before-garbage-collection callback
    * function, for instance to simulate DOM tree connections among JS
    * wrapper objects.
+   * See v8-profiler.h for RetainedObjectInfo interface description.
    */
-  static void AddObjectGroup(Persistent<Value>* objects, size_t length);
+  static void AddObjectGroup(Persistent<Value>* objects,
+                             size_t length,
+                             RetainedObjectInfo* info = NULL);
 
   /**
    * Initializes from snapshot if possible. Otherwise, attempts to
@@ -2913,6 +2924,8 @@
   static void ClearWeak(internal::Object** global_handle);
   static bool IsGlobalNearDeath(internal::Object** global_handle);
   static bool IsGlobalWeak(internal::Object** global_handle);
+  static void SetWrapperClassId(internal::Object** global_handle,
+                                uint16_t class_id);
 
   template <class T> friend class Handle;
   template <class T> friend class Local;
@@ -3561,6 +3574,10 @@
   V8::ClearWeak(reinterpret_cast<internal::Object**>(**this));
 }
 
+template <class T>
+void Persistent<T>::SetWrapperClassId(uint16_t class_id) {
+  V8::SetWrapperClassId(reinterpret_cast<internal::Object**>(**this), class_id);
+}
 
 Arguments::Arguments(internal::Object** implicit_args,
                      internal::Object** values, int length,
diff --git a/samples/shell.cc b/samples/shell.cc
index 64f78f0..fc3d76c 100644
--- a/samples/shell.cc
+++ b/samples/shell.cc
@@ -65,6 +65,11 @@
   // Create a new execution environment containing the built-in
   // functions
   v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+  if (context.IsEmpty()) {
+    printf("Error creating context\n");
+    return 1;
+  }
+
   bool run_shell = (argc == 1);
   for (int i = 1; i < argc; i++) {
     // Enter the execution environment before evaluating any code.
@@ -139,6 +144,8 @@
       v8::Testing::PrepareStressRun(i);
       result = RunMain(argc, argv);
     }
+    printf("======== Full Deoptimization =======\n");
+    v8::Testing::DeoptimizeAll();
   } else {
     result = RunMain(argc, argv);
   }
diff --git a/src/api.cc b/src/api.cc
index 87793ae..26d2246 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -3580,6 +3580,11 @@
 }
 
 
+void V8::SetWrapperClassId(i::Object** global_handle, uint16_t class_id) {
+  i::GlobalHandles::SetWrapperClassId(global_handle, class_id);
+}
+
+
 Local<v8::Object> ObjectTemplate::NewInstance() {
   ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
   LOG_API("ObjectTemplate::NewInstance");
@@ -4116,10 +4121,13 @@
 }
 
 
-void V8::AddObjectGroup(Persistent<Value>* objects, size_t length) {
+void V8::AddObjectGroup(Persistent<Value>* objects,
+                        size_t length,
+                        RetainedObjectInfo* info) {
   if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
   STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
-  i::GlobalHandles::AddGroup(reinterpret_cast<i::Object***>(objects), length);
+  i::GlobalHandles::AddGroup(
+      reinterpret_cast<i::Object***>(objects), length, info);
 }
 
 
@@ -5065,6 +5073,12 @@
           *Utils::OpenHandle(*title), internal_type, control));
 }
 
+
+void HeapProfiler::DefineWrapperClass(uint16_t class_id,
+                                      WrapperInfoCallback callback) {
+  i::HeapProfiler::DefineWrapperClass(class_id, callback);
+}
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
 
 
@@ -5129,6 +5143,11 @@
 }
 
 
+void Testing::DeoptimizeAll() {
+  internal::Deoptimizer::DeoptimizeAll();
+}
+
+
 namespace internal {
 
 
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index c91d4ba..48b91a6 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -767,11 +767,35 @@
 }
 
 
-bool Operand::is_single_instruction() const {
+bool Operand::is_single_instruction(Instr instr) const {
   if (rm_.is_valid()) return true;
-  if (must_use_constant_pool()) return false;
   uint32_t dummy1, dummy2;
-  return fits_shifter(imm32_, &dummy1, &dummy2, NULL);
+  if (must_use_constant_pool() ||
+      !fits_shifter(imm32_, &dummy1, &dummy2, &instr)) {
+    // The immediate operand cannot be encoded as a shifter operand, or use of
+    // constant pool is required. For a mov instruction not setting the
+    // condition code additional instruction conventions can be used.
+    if ((instr & ~kCondMask) == 13*B21) {  // mov, S not set
+      if (must_use_constant_pool() || !CpuFeatures::IsSupported(ARMv7)) {
+        // mov instruction will be an ldr from constant pool (one instruction).
+        return true;
+      } else {
+        // mov instruction will be a mov or movw followed by movt (two
+        // instructions).
+        return false;
+      }
+    } else {
+      // If this is not a mov or mvn instruction there will always an additional
+      // instructions - either mov or ldr. The mov might actually be two
+      // instructions mov or movw followed by movt so including the actual
+      // instruction two or three instructions will be generated.
+      return false;
+    }
+  } else {
+    // No use of constant pool and the immediate operand can be encoded as a
+    // shifter operand.
+    return true;
+  }
 }
 
 
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index f5eb507..4594ecb 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -389,8 +389,11 @@
   INLINE(bool is_reg() const);
 
   // Return true if this operand fits in one instruction so that no
-  // 2-instruction solution with a load into the ip register is necessary.
-  bool is_single_instruction() const;
+  // 2-instruction solution with a load into the ip register is necessary. If
+  // the instruction this operand is used for is a MOV or MVN instruction the
+  // actual instruction to use is required for this calculation. For other
+  // instructions instr is ignored.
+  bool is_single_instruction(Instr instr = 0) const;
   bool must_use_constant_pool() const;
 
   inline int32_t immediate() const {
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index bce0c51..31a7288 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -397,20 +397,18 @@
                            Register scratch2,
                            Label* not_number);
 
-  // Loads the number from object into dst as a 32-bit integer if possible. If
-  // the object cannot be converted to a 32-bit integer control continues at
-  // the label not_int32. If VFP is supported double_scratch is used
-  // but not scratch2.
-  // Floating point value in the 32-bit integer range will be rounded
-  // to an integer.
-  static void LoadNumberAsInteger(MacroAssembler* masm,
-                                  Register object,
-                                  Register dst,
-                                  Register heap_number_map,
-                                  Register scratch1,
-                                  Register scratch2,
-                                  DwVfpRegister double_scratch,
-                                  Label* not_int32);
+  // Convert the smi or heap number in object to an int32 using the rules
+  // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
+  // and brought into the range -2^31 .. +2^31 - 1.
+  static void ConvertNumberToInt32(MacroAssembler* masm,
+                                   Register object,
+                                   Register dst,
+                                   Register heap_number_map,
+                                   Register scratch1,
+                                   Register scratch2,
+                                   Register scratch3,
+                                   DwVfpRegister double_scratch,
+                                   Label* not_int32);
 
   // Load the number from object into double_dst in the double format.
   // Control will jump to not_int32 if the value cannot be exactly represented
@@ -606,27 +604,97 @@
 }
 
 
-void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm,
-                                              Register object,
-                                              Register dst,
-                                              Register heap_number_map,
-                                              Register scratch1,
-                                              Register scratch2,
-                                              DwVfpRegister double_scratch,
-                                              Label* not_int32) {
+void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
+                                               Register object,
+                                               Register dst,
+                                               Register heap_number_map,
+                                               Register scratch1,
+                                               Register scratch2,
+                                               Register scratch3,
+                                               DwVfpRegister double_scratch,
+                                               Label* not_number) {
   if (FLAG_debug_code) {
     __ AbortIfNotRootValue(heap_number_map,
                            Heap::kHeapNumberMapRootIndex,
                            "HeapNumberMap register clobbered.");
   }
-  Label is_smi, done;
+  Label is_smi;
+  Label done;
+  Label not_in_int32_range;
+
   __ JumpIfSmi(object, &is_smi);
   __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
   __ cmp(scratch1, heap_number_map);
-  __ b(ne, not_int32);
-  __ ConvertToInt32(
-      object, dst, scratch1, scratch2, double_scratch, not_int32);
+  __ b(ne, not_number);
+  __ ConvertToInt32(object,
+                    dst,
+                    scratch1,
+                    scratch2,
+                    double_scratch,
+                    &not_in_int32_range);
   __ jmp(&done);
+
+  __ bind(&not_in_int32_range);
+  __ ldr(scratch2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+  __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
+
+  // Register scratch1 contains mantissa word, scratch2 contains
+  // sign, exponent and mantissa. Extract biased exponent into dst.
+  __ Ubfx(dst,
+          scratch2,
+          HeapNumber::kExponentShift,
+          HeapNumber::kExponentBits);
+
+  // Express exponent as delta to 31.
+  __ sub(dst, dst, Operand(HeapNumber::kExponentBias + 31));
+
+  Label normal_exponent;
+  // If the delta is larger than kMantissaBits plus one, all bits
+  // would be shifted away, which means that we can return 0.
+  __ cmp(dst, Operand(HeapNumber::kMantissaBits + 1));
+  __ b(&normal_exponent, lt);
+  __ mov(dst, Operand(0));
+  __ jmp(&done);
+
+  __ bind(&normal_exponent);
+  const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
+  // Calculate shift.
+  __ add(scratch3, dst, Operand(kShiftBase));
+
+  // Put implicit 1 before the mantissa part in scratch2.
+  __ orr(scratch2,
+         scratch2,
+         Operand(1 << HeapNumber::kMantissaBitsInTopWord));
+
+  // Save sign.
+  Register sign = dst;
+  __ and_(sign, scratch2, Operand(HeapNumber::kSignMask));
+
+  // Shift mantisssa bits the correct position in high word.
+  __ mov(scratch2, Operand(scratch2, LSL, scratch3));
+
+  // Replace the shifted bits with bits from the lower mantissa word.
+  Label pos_shift, shift_done;
+  __ rsb(scratch3, scratch3, Operand(32), SetCC);
+  __ b(&pos_shift, ge);
+
+  // Negate scratch3.
+  __ rsb(scratch3, scratch3, Operand(0));
+  __ mov(scratch1, Operand(scratch1, LSL, scratch3));
+  __ jmp(&shift_done);
+
+  __ bind(&pos_shift);
+  __ mov(scratch1, Operand(scratch1, LSR, scratch3));
+
+  __ bind(&shift_done);
+  __ orr(scratch2, scratch2, Operand(scratch1));
+
+  // Restore sign if necessary.
+  __ cmp(sign, Operand(0));
+  __ rsb(dst, scratch2, Operand(0), LeaveCC, ne);
+  __ mov(dst, scratch2, LeaveCC, eq);
+  __ jmp(&done);
+
   __ bind(&is_smi);
   __ SmiUntag(dst, object);
   __ bind(&done);
@@ -3024,6 +3092,7 @@
   Register right = r0;
   Register scratch1 = r7;
   Register scratch2 = r9;
+  Register scratch3 = r4;
 
   ASSERT(smi_operands || (not_numbers != NULL));
   if (smi_operands && FLAG_debug_code) {
@@ -3111,22 +3180,24 @@
         __ SmiUntag(r2, right);
       } else {
         // Convert operands to 32-bit integers. Right in r2 and left in r3.
-        FloatingPointHelper::LoadNumberAsInteger(masm,
-                                                 left,
-                                                 r3,
-                                                 heap_number_map,
-                                                 scratch1,
-                                                 scratch2,
-                                                 d0,
-                                                 not_numbers);
-        FloatingPointHelper::LoadNumberAsInteger(masm,
-                                                 right,
-                                                 r2,
-                                                 heap_number_map,
-                                                 scratch1,
-                                                 scratch2,
-                                                 d0,
-                                                 not_numbers);
+        FloatingPointHelper::ConvertNumberToInt32(masm,
+                                                  left,
+                                                  r3,
+                                                  heap_number_map,
+                                                  scratch1,
+                                                  scratch2,
+                                                  scratch3,
+                                                  d0,
+                                                  not_numbers);
+        FloatingPointHelper::ConvertNumberToInt32(masm,
+                                                  right,
+                                                  r2,
+                                                  heap_number_map,
+                                                  scratch1,
+                                                  scratch2,
+                                                  scratch3,
+                                                  d0,
+                                                  not_numbers);
       }
 
       Label result_not_a_smi;
@@ -3572,13 +3643,10 @@
 
 
 void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
-  Label not_numbers, call_runtime;
+  Label call_runtime;
   ASSERT(operands_type_ == TRBinaryOpIC::HEAP_NUMBER);
 
-  GenerateFPOperation(masm, false, &not_numbers, &call_runtime);
-
-  __ bind(&not_numbers);
-  GenerateTypeTransition(masm);
+  GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
 
   __ bind(&call_runtime);
   GenerateCallRuntime(masm);
diff --git a/src/arm/deoptimizer-arm.cc b/src/arm/deoptimizer-arm.cc
index 9a5aa90..40e0fc1 100644
--- a/src/arm/deoptimizer-arm.cc
+++ b/src/arm/deoptimizer-arm.cc
@@ -46,6 +46,7 @@
 
 
 void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+  HandleScope scope;
   AssertNoAllocation no_allocation;
 
   if (!function->IsOptimized()) return;
@@ -69,8 +70,6 @@
     int deoptimization_index = safepoint_entry.deoptimization_index();
     int gap_code_size = safepoint_entry.gap_code_size();
     // Check that we did not shoot past next safepoint.
-    // TODO(srdjan): How do we guarantee that safepoint code does not
-    // overlap other safepoint patching code?
     CHECK(pc_offset >= last_pc_offset);
 #ifdef DEBUG
     // Destroy the code which is not supposed to be run again.
@@ -117,6 +116,11 @@
     PrintF("[forced deoptimization: ");
     function->PrintName();
     PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
+#ifdef DEBUG
+    if (FLAG_print_code) {
+      code->PrintLn();
+    }
+#endif
   }
 }
 
diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc
index c5b0c1d..a9b33a0 100644
--- a/src/arm/lithium-arm.cc
+++ b/src/arm/lithium-arm.cc
@@ -1350,10 +1350,21 @@
     // when we provide a native implementation.
     ASSERT(instr->left()->representation().IsInteger32());
     ASSERT(instr->right()->representation().IsInteger32());
-    LOperand* value = UseFixed(instr->left(), r0);
-    LOperand* divisor = UseFixed(instr->right(), r1);
-    LInstruction* result = DefineFixed(new LModI(value, divisor), r0);
-    result = AssignEnvironment(AssignPointerMap(result));
+
+    LInstruction* result;
+    if (instr->HasPowerOf2Divisor()) {
+      ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+      LOperand* value = UseRegisterAtStart(instr->left());
+      LModI* mod = new LModI(value, UseOrConstant(instr->right()));
+      result = DefineSameAsFirst(mod);
+      result = AssignEnvironment(result);
+    } else {
+      LOperand* value = UseFixed(instr->left(), r0);
+      LOperand* divisor = UseFixed(instr->right(), r1);
+      result = DefineFixed(new LModI(value, divisor), r0);
+      result = AssignEnvironment(AssignPointerMap(result));
+    }
+
     return result;
   } else if (instr->representation().IsTagged()) {
     return DoArithmeticT(Token::MOD, instr);
@@ -1893,6 +1904,13 @@
 }
 
 
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+  LOperand* char_code = UseRegister(instr->value());
+  LStringCharFromCode* result = new LStringCharFromCode(char_code);
+  return AssignPointerMap(DefineAsRegister(result));
+}
+
+
 LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
   LOperand* string = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new LStringLength(string));
diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h
index 00ca024..54faafd 100644
--- a/src/arm/lithium-arm.h
+++ b/src/arm/lithium-arm.h
@@ -149,6 +149,7 @@
   V(StoreNamedGeneric)                          \
   V(StorePixelArrayElement)                     \
   V(StringCharCodeAt)                           \
+  V(StringCharFromCode)                         \
   V(StringLength)                               \
   V(SubI)                                       \
   V(TaggedToI)                                  \
@@ -1635,6 +1636,19 @@
 };
 
 
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LStringCharFromCode(LOperand* char_code) {
+    inputs_[0] = char_code;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+  DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+  LOperand* char_code() { return inputs_[0]; }
+};
+
+
 class LStringLength: public LTemplateInstruction<1, 1, 0> {
  public:
   explicit LStringLength(LOperand* string) {
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index b5046ec..f06e7a0 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -34,7 +34,7 @@
 namespace internal {
 
 
-class SafepointGenerator : public PostCallGenerator {
+class SafepointGenerator : public CallWrapper {
  public:
   SafepointGenerator(LCodeGen* codegen,
                      LPointerMap* pointers,
@@ -44,7 +44,24 @@
         deoptimization_index_(deoptimization_index) { }
   virtual ~SafepointGenerator() { }
 
-  virtual void Generate() {
+  virtual void BeforeCall(int call_size) {
+    ASSERT(call_size >= 0);
+    // Ensure that we have enough space after the previous safepoint position
+    // for the generated code there.
+    int call_end = codegen_->masm()->pc_offset() + call_size;
+    int prev_jump_end =
+        codegen_->LastSafepointEnd() + Deoptimizer::patch_size();
+    if (call_end < prev_jump_end) {
+      int padding_size = prev_jump_end - call_end;
+      ASSERT_EQ(0, padding_size % Assembler::kInstrSize);
+      while (padding_size > 0) {
+        codegen_->masm()->nop();
+        padding_size -= Assembler::kInstrSize;
+      }
+    }
+  }
+
+  virtual void AfterCall() {
     codegen_->RecordSafepoint(pointers_, deoptimization_index_);
   }
 
@@ -779,6 +796,30 @@
 
 
 void LCodeGen::DoModI(LModI* instr) {
+  if (instr->hydrogen()->HasPowerOf2Divisor()) {
+    Register dividend = ToRegister(instr->InputAt(0));
+
+    int32_t divisor =
+        HConstant::cast(instr->hydrogen()->right())->Integer32Value();
+
+    if (divisor < 0) divisor = -divisor;
+
+    Label positive_dividend, done;
+    __ tst(dividend, Operand(dividend));
+    __ b(pl, &positive_dividend);
+    __ rsb(dividend, dividend, Operand(0));
+    __ and_(dividend, dividend, Operand(divisor - 1));
+    __ rsb(dividend, dividend, Operand(0), SetCC);
+    if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+      __ b(ne, &done);
+      DeoptimizeIf(al, instr->environment());
+    }
+    __ bind(&positive_dividend);
+    __ and_(dividend, dividend, Operand(divisor - 1));
+    __ bind(&done);
+    return;
+  }
+
   class DeferredModI: public LDeferredCode {
    public:
     DeferredModI(LCodeGen* codegen, LModI* instr)
@@ -839,6 +880,7 @@
   __ JumpIfNotPowerOfTwoOrZero(right, scratch, &call_stub);
   // Perform modulo operation (scratch contains right - 1).
   __ and_(result, scratch, Operand(left));
+  __ b(&done);
 
   __ bind(&call_stub);
   // Call the stub. The numbers in r0 and r1 have
@@ -3081,6 +3123,56 @@
 }
 
 
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+  class DeferredStringCharFromCode: public LDeferredCode {
+   public:
+    DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+        : LDeferredCode(codegen), instr_(instr) { }
+    virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+   private:
+    LStringCharFromCode* instr_;
+  };
+
+  DeferredStringCharFromCode* deferred =
+      new DeferredStringCharFromCode(this, instr);
+
+  ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+  ASSERT(!char_code.is(result));
+
+  __ cmp(char_code, Operand(String::kMaxAsciiCharCode));
+  __ b(hi, deferred->entry());
+  __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+  __ add(result, result, Operand(char_code, LSL, kPointerSizeLog2));
+  __ ldr(result, FieldMemOperand(result, FixedArray::kHeaderSize));
+  __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+  __ cmp(result, ip);
+  __ b(eq, deferred->entry());
+  __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+
+  // TODO(3095996): Get rid of this. For now, we need to make the
+  // result register contain a valid pointer because it is already
+  // contained in the register pointer map.
+  __ mov(result, Operand(0));
+
+  __ PushSafepointRegisters();
+  __ SmiTag(char_code);
+  __ push(char_code);
+  __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+  RecordSafepointWithRegisters(
+      instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+  __ StoreToSafepointRegisterSlot(r0, result);
+  __ PopSafepointRegisters();
+}
+
+
 void LCodeGen::DoStringLength(LStringLength* instr) {
   Register string = ToRegister(instr->InputAt(0));
   Register result = ToRegister(instr->result());
diff --git a/src/arm/lithium-codegen-arm.h b/src/arm/lithium-codegen-arm.h
index b0ef268..e2b572e 100644
--- a/src/arm/lithium-codegen-arm.h
+++ b/src/arm/lithium-codegen-arm.h
@@ -105,6 +105,7 @@
   void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
   void DoDeferredStackCheck(LGoto* instr);
   void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+  void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
   void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
                                         Label* map_check);
 
@@ -229,6 +230,9 @@
                                               int arguments,
                                               int deoptimization_index);
   void RecordPosition(int position);
+  int LastSafepointEnd() {
+    return static_cast<int>(safepoints_.GetPcAfterGap());
+  }
 
   static Condition TokenToCondition(Token::Value op, bool is_unsigned);
   void EmitGoto(int block, LDeferredCode* deferred_stack_check = NULL);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index d431f6a..916110a 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -103,7 +103,22 @@
 }
 
 
+int MacroAssembler::CallSize(Register target, Condition cond) {
+#if USE_BLX
+  return kInstrSize;
+#else
+  return 2 * kInstrSize;
+#endif
+}
+
+
 void MacroAssembler::Call(Register target, Condition cond) {
+  // Block constant pool for the call instruction sequence.
+  BlockConstPoolScope block_const_pool(this);
+#ifdef DEBUG
+  int pre_position = pc_offset();
+#endif
+
 #if USE_BLX
   blx(target, cond);
 #else
@@ -111,29 +126,46 @@
   mov(lr, Operand(pc), LeaveCC, cond);
   mov(pc, Operand(target), LeaveCC, cond);
 #endif
+
+#ifdef DEBUG
+  int post_position = pc_offset();
+  CHECK_EQ(pre_position + CallSize(target, cond), post_position);
+#endif
 }
 
 
-void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode,
-                          Condition cond) {
+int MacroAssembler::CallSize(
+    intptr_t target, RelocInfo::Mode rmode, Condition cond) {
+  int size = 2 * kInstrSize;
+  Instr mov_instr = cond | MOV | LeaveCC;
+  if (!Operand(target, rmode).is_single_instruction(mov_instr)) {
+    size += kInstrSize;
+  }
+  return size;
+}
+
+
+void MacroAssembler::Call(
+    intptr_t target, RelocInfo::Mode rmode, Condition cond) {
+  // Block constant pool for the call instruction sequence.
+  BlockConstPoolScope block_const_pool(this);
+#ifdef DEBUG
+  int pre_position = pc_offset();
+#endif
+
 #if USE_BLX
   // On ARMv5 and after the recommended call sequence is:
   //  ldr ip, [pc, #...]
   //  blx ip
 
-  // The two instructions (ldr and blx) could be separated by a constant
-  // pool and the code would still work. The issue comes from the
-  // patching code which expect the ldr to be just above the blx.
-  { BlockConstPoolScope block_const_pool(this);
-    // Statement positions are expected to be recorded when the target
-    // address is loaded. The mov method will automatically record
-    // positions when pc is the target, since this is not the case here
-    // we have to do it explicitly.
-    positions_recorder()->WriteRecordedPositions();
+  // Statement positions are expected to be recorded when the target
+  // address is loaded. The mov method will automatically record
+  // positions when pc is the target, since this is not the case here
+  // we have to do it explicitly.
+  positions_recorder()->WriteRecordedPositions();
 
-    mov(ip, Operand(target, rmode), LeaveCC, cond);
-    blx(ip, cond);
-  }
+  mov(ip, Operand(target, rmode), LeaveCC, cond);
+  blx(ip, cond);
 
   ASSERT(kCallTargetAddressOffset == 2 * kInstrSize);
 #else
@@ -141,24 +173,58 @@
   mov(lr, Operand(pc), LeaveCC, cond);
   // Emit a ldr<cond> pc, [pc + offset of target in constant pool].
   mov(pc, Operand(target, rmode), LeaveCC, cond);
-
   ASSERT(kCallTargetAddressOffset == kInstrSize);
 #endif
+
+#ifdef DEBUG
+  int post_position = pc_offset();
+  CHECK_EQ(pre_position + CallSize(target, rmode, cond), post_position);
+#endif
 }
 
 
-void MacroAssembler::Call(byte* target, RelocInfo::Mode rmode,
-                          Condition cond) {
-  ASSERT(!RelocInfo::IsCodeTarget(rmode));
-  Call(reinterpret_cast<intptr_t>(target), rmode, cond);
+int MacroAssembler::CallSize(
+    byte* target, RelocInfo::Mode rmode, Condition cond) {
+  return CallSize(reinterpret_cast<intptr_t>(target), rmode);
 }
 
 
-void MacroAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
-                          Condition cond) {
+void MacroAssembler::Call(
+    byte* target, RelocInfo::Mode rmode, Condition cond) {
+#ifdef DEBUG
+  int pre_position = pc_offset();
+#endif
+
+  ASSERT(!RelocInfo::IsCodeTarget(rmode));
+  Call(reinterpret_cast<intptr_t>(target), rmode, cond);
+
+#ifdef DEBUG
+  int post_position = pc_offset();
+  CHECK_EQ(pre_position + CallSize(target, rmode, cond), post_position);
+#endif
+}
+
+
+int MacroAssembler::CallSize(
+    Handle<Code> code, RelocInfo::Mode rmode, Condition cond) {
+  return CallSize(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+}
+
+
+void MacroAssembler::Call(
+    Handle<Code> code, RelocInfo::Mode rmode, Condition cond) {
+#ifdef DEBUG
+  int pre_position = pc_offset();
+#endif
+
   ASSERT(RelocInfo::IsCodeTarget(rmode));
   // 'code' is always generated ARM code, never THUMB code
   Call(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
+
+#ifdef DEBUG
+  int post_position = pc_offset();
+  CHECK_EQ(pre_position + CallSize(code, rmode, cond), post_position);
+#endif
 }
 
 
@@ -784,7 +850,7 @@
                                     Register code_reg,
                                     Label* done,
                                     InvokeFlag flag,
-                                    PostCallGenerator* post_call_generator) {
+                                    CallWrapper* call_wrapper) {
   bool definitely_matches = false;
   Label regular_invoke;
 
@@ -839,8 +905,11 @@
     Handle<Code> adaptor =
         Handle<Code>(Builtins::builtin(Builtins::ArgumentsAdaptorTrampoline));
     if (flag == CALL_FUNCTION) {
+      if (call_wrapper != NULL) {
+        call_wrapper->BeforeCall(CallSize(adaptor, RelocInfo::CODE_TARGET));
+      }
       Call(adaptor, RelocInfo::CODE_TARGET);
-      if (post_call_generator != NULL) post_call_generator->Generate();
+      if (call_wrapper != NULL) call_wrapper->AfterCall();
       b(done);
     } else {
       Jump(adaptor, RelocInfo::CODE_TARGET);
@@ -854,14 +923,15 @@
                                 const ParameterCount& expected,
                                 const ParameterCount& actual,
                                 InvokeFlag flag,
-                                PostCallGenerator* post_call_generator) {
+                                CallWrapper* call_wrapper) {
   Label done;
 
   InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag,
-                 post_call_generator);
+                 call_wrapper);
   if (flag == CALL_FUNCTION) {
+    if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(code));
     Call(code);
-    if (post_call_generator != NULL) post_call_generator->Generate();
+    if (call_wrapper != NULL) call_wrapper->AfterCall();
   } else {
     ASSERT(flag == JUMP_FUNCTION);
     Jump(code);
@@ -896,7 +966,7 @@
 void MacroAssembler::InvokeFunction(Register fun,
                                     const ParameterCount& actual,
                                     InvokeFlag flag,
-                                    PostCallGenerator* post_call_generator) {
+                                    CallWrapper* call_wrapper) {
   // Contract with called JS functions requires that function is passed in r1.
   ASSERT(fun.is(r1));
 
@@ -913,7 +983,7 @@
       FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
 
   ParameterCount expected(expected_reg);
-  InvokeCode(code_reg, expected, actual, flag, post_call_generator);
+  InvokeCode(code_reg, expected, actual, flag, call_wrapper);
 }
 
 
@@ -2083,11 +2153,12 @@
 
 void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
                                    InvokeJSFlags flags,
-                                   PostCallGenerator* post_call_generator) {
+                                   CallWrapper* call_wrapper) {
   GetBuiltinEntry(r2, id);
   if (flags == CALL_JS) {
+    if (call_wrapper != NULL) call_wrapper->BeforeCall(CallSize(r2));
     Call(r2);
-    if (post_call_generator != NULL) post_call_generator->Generate();
+    if (call_wrapper != NULL) call_wrapper->AfterCall();
   } else {
     ASSERT(flags == JUMP_JS);
     Jump(r2);
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index aaf4458..d79edd7 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -34,7 +34,7 @@
 namespace internal {
 
 // Forward declaration.
-class PostCallGenerator;
+class CallWrapper;
 
 // ----------------------------------------------------------------------------
 // Static helper functions
@@ -96,8 +96,11 @@
   void Jump(Register target, Condition cond = al);
   void Jump(byte* target, RelocInfo::Mode rmode, Condition cond = al);
   void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
+  int CallSize(Register target, Condition cond = al);
   void Call(Register target, Condition cond = al);
+  int CallSize(byte* target, RelocInfo::Mode rmode, Condition cond = al);
   void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
+  int CallSize(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
   void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
   void Ret(Condition cond = al);
 
@@ -343,7 +346,7 @@
                   const ParameterCount& expected,
                   const ParameterCount& actual,
                   InvokeFlag flag,
-                  PostCallGenerator* post_call_generator = NULL);
+                  CallWrapper* call_wrapper = NULL);
 
   void InvokeCode(Handle<Code> code,
                   const ParameterCount& expected,
@@ -356,7 +359,7 @@
   void InvokeFunction(Register function,
                       const ParameterCount& actual,
                       InvokeFlag flag,
-                      PostCallGenerator* post_call_generator = NULL);
+                      CallWrapper* call_wrapper = NULL);
 
   void InvokeFunction(JSFunction* function,
                       const ParameterCount& actual,
@@ -748,7 +751,7 @@
   // the unresolved list if the name does not resolve.
   void InvokeBuiltin(Builtins::JavaScript id,
                      InvokeJSFlags flags,
-                     PostCallGenerator* post_call_generator = NULL);
+                     CallWrapper* call_wrapper = NULL);
 
   // Store the code object for the given builtin in the target register and
   // setup the function in r1.
@@ -911,6 +914,7 @@
 
  private:
   void Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
+  int CallSize(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
   void Call(intptr_t target, RelocInfo::Mode rmode, Condition cond = al);
 
   // Helper functions for generating invokes.
@@ -920,7 +924,7 @@
                       Register code_reg,
                       Label* done,
                       InvokeFlag flag,
-                      PostCallGenerator* post_call_generator = NULL);
+                      CallWrapper* call_wrapper = NULL);
 
   // Activation support.
   void EnterFrame(StackFrame::Type type);
@@ -984,11 +988,15 @@
 // Helper class for generating code or data associated with the code
 // right after a call instruction. As an example this can be used to
 // generate safepoint data after calls for crankshaft.
-class PostCallGenerator {
+class CallWrapper {
  public:
-  PostCallGenerator() { }
-  virtual ~PostCallGenerator() { }
-  virtual void Generate() = 0;
+  CallWrapper() { }
+  virtual ~CallWrapper() { }
+  // Called just before emitting a call. Argument is the size of the generated
+  // call code.
+  virtual void BeforeCall(int call_size) = 0;
+  // Called just after emitting a call, i.e., at the return site for the call.
+  virtual void AfterCall() = 0;
 };
 
 
diff --git a/src/ast.cc b/src/ast.cc
index 7cfc3da..184aaa5 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -540,6 +540,8 @@
       ZoneMapList* types = oracle->LoadReceiverTypes(this, name);
       receiver_types_ = types;
     }
+  } else if (oracle->LoadIsBuiltin(this, Builtins::KeyedLoadIC_String)) {
+    is_string_access_ = true;
   } else if (is_monomorphic_) {
     monomorphic_receiver_type_ = oracle->LoadMonomorphicReceiverType(this);
     if (monomorphic_receiver_type_->has_external_array_elements()) {
diff --git a/src/ast.h b/src/ast.h
index ec02772..b00612d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1214,6 +1214,7 @@
         is_monomorphic_(false),
         is_array_length_(false),
         is_string_length_(false),
+        is_string_access_(false),
         is_function_prototype_(false),
         is_arguments_access_(false) { }
 
@@ -1228,6 +1229,7 @@
   bool is_synthetic() const { return type_ == SYNTHETIC; }
 
   bool IsStringLength() const { return is_string_length_; }
+  bool IsStringAccess() const { return is_string_access_; }
   bool IsFunctionPrototype() const { return is_function_prototype_; }
 
   // Marks that this is actually an argument rewritten to a keyed property
@@ -1265,6 +1267,7 @@
   bool is_monomorphic_ : 1;
   bool is_array_length_ : 1;
   bool is_string_length_ : 1;
+  bool is_string_access_ : 1;
   bool is_function_prototype_ : 1;
   bool is_arguments_access_ : 1;
   Handle<Map> monomorphic_receiver_type_;
diff --git a/src/compiler.cc b/src/compiler.cc
index 667432f..553f486 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -172,6 +172,7 @@
   ASSERT(code->kind() == Code::FUNCTION);
   code->set_optimizable(false);
   info->SetCode(code);
+  CompilationCache::MarkForLazyOptimizing(info->closure());
   if (FLAG_trace_opt) {
     PrintF("[disabled optimization for: ");
     info->closure()->PrintName();
@@ -248,7 +249,7 @@
   // performance of the hydrogen-based compiler.
   int64_t start = OS::Ticks();
   bool should_recompile = !info->shared_info()->has_deoptimization_support();
-  if (should_recompile || FLAG_time_hydrogen) {
+  if (should_recompile || FLAG_hydrogen_stats) {
     HPhase phase(HPhase::kFullCodeGen);
     CompilationInfo unoptimized(info->shared_info());
     // Note that we use the same AST that we will use for generating the
diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h
index 440dedc..a7fffe0 100644
--- a/src/cpu-profiler-inl.h
+++ b/src/cpu-profiler-inl.h
@@ -41,8 +41,8 @@
 
 void CodeCreateEventRecord::UpdateCodeMap(CodeMap* code_map) {
   code_map->AddCode(start, entry, size);
-  if (sfi_address != NULL) {
-    entry->set_shared_id(code_map->GetSFITag(sfi_address));
+  if (shared != NULL) {
+    entry->set_shared_id(code_map->GetSharedId(shared));
   }
 }
 
@@ -57,7 +57,7 @@
 }
 
 
-void SFIMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
+void SharedFunctionInfoMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
   code_map->MoveCode(from, to);
 }
 
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index ad04a00..f82d8ee 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -69,7 +69,7 @@
   rec->start = start;
   rec->entry = generator_->NewCodeEntry(tag, prefix, name);
   rec->size = 1;
-  rec->sfi_address = NULL;
+  rec->shared = NULL;
   events_buffer_.Enqueue(evt_rec);
 }
 
@@ -80,7 +80,7 @@
                                               int line_number,
                                               Address start,
                                               unsigned size,
-                                              Address sfi_address) {
+                                              Address shared) {
   if (FilterOutCodeCreateEvent(tag)) return;
   CodeEventsContainer evt_rec;
   CodeCreateEventRecord* rec = &evt_rec.CodeCreateEventRecord_;
@@ -89,7 +89,7 @@
   rec->start = start;
   rec->entry = generator_->NewCodeEntry(tag, name, resource_name, line_number);
   rec->size = size;
-  rec->sfi_address = sfi_address;
+  rec->shared = shared;
   events_buffer_.Enqueue(evt_rec);
 }
 
@@ -106,7 +106,7 @@
   rec->start = start;
   rec->entry = generator_->NewCodeEntry(tag, name);
   rec->size = size;
-  rec->sfi_address = NULL;
+  rec->shared = NULL;
   events_buffer_.Enqueue(evt_rec);
 }
 
@@ -123,7 +123,7 @@
   rec->start = start;
   rec->entry = generator_->NewCodeEntry(tag, args_count);
   rec->size = size;
-  rec->sfi_address = NULL;
+  rec->shared = NULL;
   events_buffer_.Enqueue(evt_rec);
 }
 
@@ -149,10 +149,12 @@
 }
 
 
-void ProfilerEventsProcessor::SFIMoveEvent(Address from, Address to) {
+void ProfilerEventsProcessor::SharedFunctionInfoMoveEvent(Address from,
+                                                          Address to) {
   CodeEventsContainer evt_rec;
-  SFIMoveEventRecord* rec = &evt_rec.SFIMoveEventRecord_;
-  rec->type = CodeEventRecord::SFI_MOVE;
+  SharedFunctionInfoMoveEventRecord* rec =
+      &evt_rec.SharedFunctionInfoMoveEventRecord_;
+  rec->type = CodeEventRecord::SHARED_FUNC_MOVE;
   rec->order = ++enqueue_order_;
   rec->from = from;
   rec->to = to;
@@ -403,8 +405,8 @@
 }
 
 
-void CpuProfiler::SFIMoveEvent(Address from, Address to) {
-  singleton_->processor_->SFIMoveEvent(from, to);
+void CpuProfiler::SharedFunctionInfoMoveEvent(Address from, Address to) {
+  singleton_->processor_->SharedFunctionInfoMoveEvent(from, to);
 }
 
 
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
index 1ebbfeb..fc4dfb7 100644
--- a/src/cpu-profiler.h
+++ b/src/cpu-profiler.h
@@ -46,11 +46,11 @@
 class ProfileGenerator;
 class TokenEnumerator;
 
-#define CODE_EVENTS_TYPE_LIST(V)                \
-  V(CODE_CREATION, CodeCreateEventRecord)       \
-  V(CODE_MOVE,     CodeMoveEventRecord)         \
-  V(CODE_DELETE,   CodeDeleteEventRecord)       \
-  V(SFI_MOVE,      SFIMoveEventRecord)
+#define CODE_EVENTS_TYPE_LIST(V)                                   \
+  V(CODE_CREATION,    CodeCreateEventRecord)                       \
+  V(CODE_MOVE,        CodeMoveEventRecord)                         \
+  V(CODE_DELETE,      CodeDeleteEventRecord)                       \
+  V(SHARED_FUNC_MOVE, SharedFunctionInfoMoveEventRecord)
 
 
 class CodeEventRecord {
@@ -73,7 +73,7 @@
   Address start;
   CodeEntry* entry;
   unsigned size;
-  Address sfi_address;
+  Address shared;
 
   INLINE(void UpdateCodeMap(CodeMap* code_map));
 };
@@ -96,7 +96,7 @@
 };
 
 
-class SFIMoveEventRecord : public CodeEventRecord {
+class SharedFunctionInfoMoveEventRecord : public CodeEventRecord {
  public:
   Address from;
   Address to;
@@ -149,7 +149,7 @@
                        String* name,
                        String* resource_name, int line_number,
                        Address start, unsigned size,
-                       Address sfi_address);
+                       Address shared);
   void CodeCreateEvent(Logger::LogEventsAndTags tag,
                        const char* name,
                        Address start, unsigned size);
@@ -158,7 +158,7 @@
                        Address start, unsigned size);
   void CodeMoveEvent(Address from, Address to);
   void CodeDeleteEvent(Address from);
-  void SFIMoveEvent(Address from, Address to);
+  void SharedFunctionInfoMoveEvent(Address from, Address to);
   void RegExpCodeCreateEvent(Logger::LogEventsAndTags tag,
                              const char* prefix, String* name,
                              Address start, unsigned size);
@@ -251,7 +251,7 @@
   static void GetterCallbackEvent(String* name, Address entry_point);
   static void RegExpCodeCreateEvent(Code* code, String* source);
   static void SetterCallbackEvent(String* name, Address entry_point);
-  static void SFIMoveEvent(Address from, Address to);
+  static void SharedFunctionInfoMoveEvent(Address from, Address to);
 
   static INLINE(bool is_profiling()) {
     return NoBarrier_Load(&is_profiling_);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 2566766..cdad211 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -111,7 +111,7 @@
 DEFINE_bool(limit_inlining, true, "limit code size growth from inlining")
 DEFINE_bool(eliminate_empty_blocks, true, "eliminate empty blocks")
 DEFINE_bool(loop_invariant_code_motion, true, "loop invariant code motion")
-DEFINE_bool(time_hydrogen, false, "timing for hydrogen")
+DEFINE_bool(hydrogen_stats, false, "print statistics for hydrogen")
 DEFINE_bool(trace_hydrogen, false, "trace generated hydrogen to file")
 DEFINE_bool(trace_inlining, false, "trace inlining decisions")
 DEFINE_bool(trace_alloc, false, "trace register allocator")
diff --git a/src/global-handles.cc b/src/global-handles.cc
index 18cdc5a..6cc0cfb 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -41,6 +41,7 @@
   void Initialize(Object* object) {
     // Set the initial value of the handle.
     object_ = object;
+    class_id_ = v8::HeapProfiler::kPersistentHandleNoClassId;
     state_  = NORMAL;
     parameter_or_next_free_.parameter = NULL;
     callback_ = NULL;
@@ -137,6 +138,14 @@
     return state_ == WEAK;
   }
 
+  bool CanBeRetainer() {
+    return state_ != DESTROYED && state_ != NEAR_DEATH;
+  }
+
+  void SetWrapperClassId(uint16_t class_id) {
+    class_id_ = class_id;
+  }
+
   // Returns the id for this weak handle.
   void set_parameter(void* parameter) {
     ASSERT(state_ != DESTROYED);
@@ -190,6 +199,8 @@
   // Place the handle address first to avoid offset computation.
   Object* object_;  // Storage for object pointer.
 
+  uint16_t class_id_;
+
   // Transition diagram:
   // NORMAL <-> WEAK -> PENDING -> NEAR_DEATH -> { NORMAL, WEAK, DESTROYED }
   enum State {
@@ -199,7 +210,7 @@
     NEAR_DEATH,  // Callback has informed the handle is near death.
     DESTROYED
   };
-  State state_;
+  State state_ : 4;  // Need one more bit for MSVC as it treats enums as signed.
 
  private:
   // Handle specific callback.
@@ -337,6 +348,11 @@
 }
 
 
+void GlobalHandles::SetWrapperClassId(Object** location, uint16_t class_id) {
+  Node::FromLocation(location)->SetWrapperClassId(class_id);
+}
+
+
 void GlobalHandles::IterateWeakRoots(ObjectVisitor* v) {
   // Traversal of GC roots in the global handle list that are marked as
   // WEAK or PENDING.
@@ -435,6 +451,16 @@
 }
 
 
+void GlobalHandles::IterateAllRootsWithClassIds(ObjectVisitor* v) {
+  for (Node* current = head_; current != NULL; current = current->next()) {
+    if (current->class_id_ != v8::HeapProfiler::kPersistentHandleNoClassId &&
+        current->CanBeRetainer()) {
+      v->VisitEmbedderReference(&current->object_, current->class_id_);
+    }
+  }
+}
+
+
 void GlobalHandles::TearDown() {
   // Reset all the lists.
   set_head(NULL);
@@ -515,8 +541,10 @@
   return &groups;
 }
 
-void GlobalHandles::AddGroup(Object*** handles, size_t length) {
-  ObjectGroup* new_entry = new ObjectGroup(length);
+void GlobalHandles::AddGroup(Object*** handles,
+                             size_t length,
+                             v8::RetainedObjectInfo* info) {
+  ObjectGroup* new_entry = new ObjectGroup(length, info);
   for (size_t i = 0; i < length; ++i)
     new_entry->objects_.Add(handles[i]);
   ObjectGroups()->Add(new_entry);
diff --git a/src/global-handles.h b/src/global-handles.h
index 37b2b44..3bab4b8 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -48,10 +48,16 @@
 class ObjectGroup : public Malloced {
  public:
   ObjectGroup() : objects_(4) {}
-  explicit ObjectGroup(size_t capacity)
-      : objects_(static_cast<int>(capacity)) { }
+  ObjectGroup(size_t capacity, v8::RetainedObjectInfo* info)
+      : objects_(static_cast<int>(capacity)),
+        info_(info) { }
+  ~ObjectGroup() { if (info_ != NULL) info_->Dispose(); }
 
   List<Object**> objects_;
+  v8::RetainedObjectInfo* info_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ObjectGroup);
 };
 
 
@@ -75,6 +81,8 @@
                        void* parameter,
                        WeakReferenceCallback callback);
 
+  static void SetWrapperClassId(Object** location, uint16_t class_id);
+
   // Returns the current number of weak handles.
   static int NumberOfWeakHandles() { return number_of_weak_handles_; }
 
@@ -105,6 +113,9 @@
   // Iterates over all handles.
   static void IterateAllRoots(ObjectVisitor* v);
 
+  // Iterates over all handles that have embedder-assigned class ID.
+  static void IterateAllRootsWithClassIds(ObjectVisitor* v);
+
   // Iterates over all weak roots in heap.
   static void IterateWeakRoots(ObjectVisitor* v);
 
@@ -119,7 +130,9 @@
   // Add an object group.
   // Should only used in GC callback function before a collection.
   // All groups are destroyed after a mark-compact collection.
-  static void AddGroup(Object*** handles, size_t length);
+  static void AddGroup(Object*** handles,
+                       size_t length,
+                       v8::RetainedObjectInfo* info);
 
   // Returns the object groups.
   static List<ObjectGroup*>* ObjectGroups();
diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc
index 07b631f..97d10c9 100644
--- a/src/heap-profiler.cc
+++ b/src/heap-profiler.cc
@@ -364,6 +364,27 @@
 }
 
 
+void HeapProfiler::DefineWrapperClass(
+    uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback) {
+  ASSERT(singleton_ != NULL);
+  ASSERT(class_id != v8::HeapProfiler::kPersistentHandleNoClassId);
+  if (singleton_->wrapper_callbacks_.length() <= class_id) {
+    singleton_->wrapper_callbacks_.AddBlock(
+        NULL, class_id - singleton_->wrapper_callbacks_.length() + 1);
+  }
+  singleton_->wrapper_callbacks_[class_id] = callback;
+}
+
+
+v8::RetainedObjectInfo* HeapProfiler::ExecuteWrapperClassCallback(
+    uint16_t class_id, Object** wrapper) {
+  ASSERT(singleton_ != NULL);
+  if (singleton_->wrapper_callbacks_.length() <= class_id) return NULL;
+  return singleton_->wrapper_callbacks_[class_id](
+      class_id, Utils::ToLocal(Handle<Object>(wrapper)));
+}
+
+
 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(const char* name,
                                              int type,
                                              v8::ActivityControl* control) {
@@ -401,7 +422,7 @@
 HeapSnapshot* HeapProfiler::TakeSnapshotImpl(String* name,
                                              int type,
                                              v8::ActivityControl* control) {
-  return TakeSnapshotImpl(snapshots_->GetName(name), type, control);
+  return TakeSnapshotImpl(snapshots_->names()->GetName(name), type, control);
 }
 
 
@@ -872,7 +893,8 @@
             const NumberAndSizeInfo& number_and_size) {
     const char* name = cluster.GetSpecialCaseName();
     if (name == NULL) {
-      name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+      name = snapshot_->collection()->names()->GetFunctionName(
+          cluster.constructor());
     }
     AddEntryFromAggregatedSnapshot(snapshot_,
                                    root_child_index_,
@@ -1013,7 +1035,8 @@
     JSObjectsCluster cluster = HeapObjectAsCluster(obj);
     const char* name = cluster.GetSpecialCaseName();
     if (name == NULL) {
-      name = snapshot_->collection()->GetFunctionName(cluster.constructor());
+      name = snapshot_->collection()->names()->GetFunctionName(
+          cluster.constructor());
     }
     return AddEntryFromAggregatedSnapshot(
         snapshot_, root_child_index_, HeapEntry::kObject, name,
diff --git a/src/heap-profiler.h b/src/heap-profiler.h
index 20ba457..c5a9ab4 100644
--- a/src/heap-profiler.h
+++ b/src/heap-profiler.h
@@ -68,6 +68,11 @@
 
   static void ObjectMoveEvent(Address from, Address to);
 
+  static void DefineWrapperClass(
+      uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
+  static v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id,
+                                                             Object** wrapper);
+
   static INLINE(bool is_profiling()) {
     return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
   }
@@ -88,6 +93,7 @@
 
   HeapSnapshotsCollection* snapshots_;
   unsigned next_snapshot_uid_;
+  List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
 
   static HeapProfiler* singleton_;
 #endif  // ENABLE_LOGGING_AND_PROFILING
diff --git a/src/heap.cc b/src/heap.cc
index 199ee60..dd4d733 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1341,7 +1341,8 @@
 #if defined(ENABLE_LOGGING_AND_PROFILING)
     if (Logger::is_logging() || CpuProfiler::is_profiling()) {
       if (target->IsSharedFunctionInfo()) {
-        PROFILE(SFIMoveEvent(source->address(), target->address()));
+        PROFILE(SharedFunctionInfoMoveEvent(
+            source->address(), target->address()));
       }
     }
 #endif
diff --git a/src/heap.h b/src/heap.h
index 61a41a4..0127933 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -1138,6 +1138,14 @@
 
   static GCTracer* tracer() { return tracer_; }
 
+  static void CallGlobalGCPrologueCallback() {
+    if (global_gc_prologue_callback_ != NULL) global_gc_prologue_callback_();
+  }
+
+  static void CallGlobalGCEpilogueCallback() {
+    if (global_gc_epilogue_callback_ != NULL) global_gc_epilogue_callback_();
+  }
+
  private:
   static int reserved_semispace_size_;
   static int max_semispace_size_;
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
index 6c516f2..fc376bd 100644
--- a/src/hydrogen-instructions.h
+++ b/src/hydrogen-instructions.h
@@ -151,6 +151,7 @@
   V(StoreNamedField)                           \
   V(StoreNamedGeneric)                         \
   V(StringCharCodeAt)                          \
+  V(StringCharFromCode)                        \
   V(StringLength)                              \
   V(Sub)                                       \
   V(Test)                                      \
@@ -2567,6 +2568,16 @@
     SetFlag(kCanBeDivByZero);
   }
 
+  bool HasPowerOf2Divisor() {
+    if (right()->IsConstant() &&
+        HConstant::cast(right())->HasInteger32Value()) {
+      int32_t value = HConstant::cast(right())->Integer32Value();
+      return value != 0 && (IsPowerOf2(value) || IsPowerOf2(-value));
+    }
+
+    return false;
+  }
+
   virtual HValue* EnsureAndPropagateNotMinusZero(BitVector* visited);
 
   DECLARE_CONCRETE_INSTRUCTION(Mod, "mod")
@@ -3258,6 +3269,23 @@
 };
 
 
+class HStringCharFromCode: public HUnaryOperation {
+ public:
+  explicit HStringCharFromCode(HValue* char_code) : HUnaryOperation(char_code) {
+    set_representation(Representation::Tagged());
+    SetFlag(kUseGVN);
+  }
+
+  virtual Representation RequiredInputRepresentation(int index) const {
+    return Representation::Integer32();
+  }
+
+  virtual bool DataEquals(HValue* other) { return true; }
+
+  DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string_char_from_code")
+};
+
+
 class HStringLength: public HUnaryOperation {
  public:
   explicit HStringLength(HValue* string) : HUnaryOperation(string) {
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index dbd9adf..b50399d 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -2136,6 +2136,8 @@
 
 HGraph* HGraphBuilder::CreateGraph() {
   graph_ = new HGraph(info());
+  if (FLAG_hydrogen_stats) HStatistics::Instance()->Initialize(info());
+
   {
     HPhase phase("Block building");
     current_block_ = graph()->entry_block();
@@ -3701,6 +3703,13 @@
                                           FIRST_STRING_TYPE,
                                           LAST_STRING_TYPE));
     instr = new HStringLength(string);
+  } else if (expr->IsStringAccess()) {
+    VISIT_FOR_VALUE(expr->key());
+    HValue* index = Pop();
+    HValue* string = Pop();
+    HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+    AddInstruction(char_code);
+    instr = new HStringCharFromCode(char_code);
 
   } else if (expr->IsFunctionPrototype()) {
     HValue* function = Pop();
@@ -4081,6 +4090,7 @@
   int argument_count = expr->arguments()->length() + 1;  // Plus receiver.
   switch (id) {
     case kStringCharCodeAt:
+    case kStringCharAt:
       if (argument_count == 2 && check_type == STRING_CHECK) {
         HValue* index = Pop();
         HValue* string = Pop();
@@ -4088,7 +4098,13 @@
         AddInstruction(new HCheckPrototypeMaps(
             oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK),
             expr->holder()));
-        HStringCharCodeAt* result = BuildStringCharCodeAt(string, index);
+        HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+        if (id == kStringCharCodeAt) {
+          ast_context()->ReturnInstruction(char_code, expr->id());
+          return true;
+        }
+        AddInstruction(char_code);
+        HStringCharFromCode* result = new HStringCharFromCode(char_code);
         ast_context()->ReturnInstruction(result, expr->id());
         return true;
       }
@@ -4265,10 +4281,12 @@
       }
 
       if (HasCustomCallGenerator(expr->target()) ||
+          CallOptimization(*expr->target()).is_simple_api_call() ||
           expr->check_type() != RECEIVER_MAP_CHECK) {
         // When the target has a custom call IC generator, use the IC,
-        // because it is likely to generate better code. Also use the
-        // IC when a primitive receiver check is required.
+        // because it is likely to generate better code.  Similarly, we
+        // generate better call stubs for some API functions.
+        // Also use the IC when a primitive receiver check is required.
         HContext* context = new HContext;
         AddInstruction(context);
         call = PreProcessCall(new HCallNamed(context, name, argument_count));
@@ -5174,19 +5192,24 @@
 
 // Fast support for string.charAt(n) and string[n].
 void HGraphBuilder::GenerateStringCharFromCode(CallRuntime* call) {
-  BAILOUT("inlined runtime function: StringCharFromCode");
+  ASSERT(call->arguments()->length() == 1);
+  VISIT_FOR_VALUE(call->arguments()->at(0));
+  HValue* char_code = Pop();
+  HStringCharFromCode* result = new HStringCharFromCode(char_code);
+  ast_context()->ReturnInstruction(result, call->id());
 }
 
 
 // Fast support for string.charAt(n) and string[n].
 void HGraphBuilder::GenerateStringCharAt(CallRuntime* call) {
-  ASSERT_EQ(2, call->arguments()->length());
-  VisitArgumentList(call->arguments());
-  CHECK_BAILOUT;
-  HContext* context = new HContext;
-  AddInstruction(context);
-  HCallStub* result = new HCallStub(context, CodeStub::StringCharAt, 2);
-  Drop(2);
+  ASSERT(call->arguments()->length() == 2);
+  VISIT_FOR_VALUE(call->arguments()->at(0));
+  VISIT_FOR_VALUE(call->arguments()->at(1));
+  HValue* index = Pop();
+  HValue* string = Pop();
+  HStringCharCodeAt* char_code = BuildStringCharCodeAt(string, index);
+  AddInstruction(char_code);
+  HStringCharFromCode* result = new HStringCharFromCode(char_code);
   ast_context()->ReturnInstruction(result, call->id());
 }
 
@@ -5736,12 +5759,12 @@
   Tag tag(this, "intervals");
   PrintStringProperty("name", name);
 
-  const ZoneList<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
+  const Vector<LiveRange*>* fixed_d = allocator->fixed_double_live_ranges();
   for (int i = 0; i < fixed_d->length(); ++i) {
     TraceLiveRange(fixed_d->at(i), "fixed");
   }
 
-  const ZoneList<LiveRange*>* fixed = allocator->fixed_live_ranges();
+  const Vector<LiveRange*>* fixed = allocator->fixed_live_ranges();
   for (int i = 0; i < fixed->length(); ++i) {
     TraceLiveRange(fixed->at(i), "fixed");
   }
@@ -5812,6 +5835,11 @@
 }
 
 
+void HStatistics::Initialize(CompilationInfo* info) {
+  source_size_ += info->shared_info()->SourceSize();
+}
+
+
 void HStatistics::Print() {
   PrintF("Timing results:\n");
   int64_t sum = 0;
@@ -5829,9 +5857,10 @@
     double size_percent = static_cast<double>(size) * 100 / total_size_;
     PrintF(" %8u bytes / %4.1f %%\n", size, size_percent);
   }
-  PrintF("%30s - %7.3f ms           %8u bytes\n", "Sum",
-         static_cast<double>(sum) / 1000,
-         total_size_);
+  double source_size_in_kb = static_cast<double>(source_size_) / 1024;
+  PrintF("%30s - %7.3f ms           %7.3f bytes\n", "Sum",
+         (static_cast<double>(sum) / 1000) / source_size_in_kb,
+         total_size_ / source_size_in_kb);
   PrintF("---------------------------------------------------------------\n");
   PrintF("%30s - %7.3f ms (%.1f times slower than full code gen)\n",
          "Total",
@@ -5876,13 +5905,13 @@
   if (allocator != NULL && chunk_ == NULL) {
     chunk_ = allocator->chunk();
   }
-  if (FLAG_time_hydrogen) start_ = OS::Ticks();
+  if (FLAG_hydrogen_stats) start_ = OS::Ticks();
   start_allocation_size_ = Zone::allocation_size_;
 }
 
 
 void HPhase::End() const {
-  if (FLAG_time_hydrogen) {
+  if (FLAG_hydrogen_stats) {
     int64_t end = OS::Ticks();
     unsigned size = Zone::allocation_size_ - start_allocation_size_;
     HStatistics::Instance()->SaveTiming(name_, end - start_, size);
diff --git a/src/hydrogen.h b/src/hydrogen.h
index 21f3677..235b669 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -939,6 +939,7 @@
 
 class HStatistics: public Malloced {
  public:
+  void Initialize(CompilationInfo* info);
   void Print();
   void SaveTiming(const char* name, int64_t ticks, unsigned size);
   static HStatistics* Instance() {
@@ -957,7 +958,8 @@
         sizes_(5),
         total_(0),
         total_size_(0),
-        full_code_gen_(0) { }
+        full_code_gen_(0),
+        source_size_(0) { }
 
   List<int64_t> timing_;
   List<const char*> names_;
@@ -965,6 +967,7 @@
   int64_t total_;
   unsigned total_size_;
   int64_t full_code_gen_;
+  double source_size_;
 };
 
 
diff --git a/src/ia32/deoptimizer-ia32.cc b/src/ia32/deoptimizer-ia32.cc
index 5f4d944..49308bf 100644
--- a/src/ia32/deoptimizer-ia32.cc
+++ b/src/ia32/deoptimizer-ia32.cc
@@ -56,6 +56,7 @@
 
 
 void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+  HandleScope scope;
   AssertNoAllocation no_allocation;
 
   if (!function->IsOptimized()) return;
@@ -132,6 +133,11 @@
     PrintF("[forced deoptimization: ");
     function->PrintName();
     PrintF(" / %x]\n", reinterpret_cast<uint32_t>(function));
+#ifdef DEBUG
+    if (FLAG_print_code) {
+      code->PrintLn();
+    }
+#endif
   }
 }
 
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 28f4679..269591e 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -781,41 +781,64 @@
 
 
 void LCodeGen::DoModI(LModI* instr) {
-  LOperand* right = instr->InputAt(1);
-  ASSERT(ToRegister(instr->result()).is(edx));
-  ASSERT(ToRegister(instr->InputAt(0)).is(eax));
-  ASSERT(!ToRegister(instr->InputAt(1)).is(eax));
-  ASSERT(!ToRegister(instr->InputAt(1)).is(edx));
+  if (instr->hydrogen()->HasPowerOf2Divisor()) {
+    Register dividend = ToRegister(instr->InputAt(0));
 
-  Register right_reg = ToRegister(right);
+    int32_t divisor =
+        HConstant::cast(instr->hydrogen()->right())->Integer32Value();
 
-  // Check for x % 0.
-  if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
-    __ test(right_reg, ToOperand(right));
-    DeoptimizeIf(zero, instr->environment());
-  }
+    if (divisor < 0) divisor = -divisor;
 
-  // Sign extend to edx.
-  __ cdq();
-
-  // Check for (0 % -x) that will produce negative zero.
-  if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
-    NearLabel positive_left;
-    NearLabel done;
-    __ test(eax, Operand(eax));
-    __ j(not_sign, &positive_left);
-    __ idiv(right_reg);
-
-    // Test the remainder for 0, because then the result would be -0.
-    __ test(edx, Operand(edx));
-    __ j(not_zero, &done);
-
-    DeoptimizeIf(no_condition, instr->environment());
-    __ bind(&positive_left);
-    __ idiv(right_reg);
+    NearLabel positive_dividend, done;
+    __ test(dividend, Operand(dividend));
+    __ j(not_sign, &positive_dividend);
+    __ neg(dividend);
+    __ and_(dividend, divisor - 1);
+    __ neg(dividend);
+    if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+      __ j(not_zero, &done);
+      DeoptimizeIf(no_condition, instr->environment());
+    }
+    __ bind(&positive_dividend);
+    __ and_(dividend, divisor - 1);
     __ bind(&done);
   } else {
-    __ idiv(right_reg);
+    LOperand* right = instr->InputAt(1);
+    ASSERT(ToRegister(instr->InputAt(0)).is(eax));
+    ASSERT(ToRegister(instr->result()).is(edx));
+
+    Register right_reg = ToRegister(right);
+    ASSERT(!right_reg.is(eax));
+    ASSERT(!right_reg.is(edx));
+
+    // Check for x % 0.
+    if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+      __ test(right_reg, ToOperand(right));
+      DeoptimizeIf(zero, instr->environment());
+    }
+
+    // Sign extend to edx.
+    __ cdq();
+
+    // Check for (0 % -x) that will produce negative zero.
+    if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+      NearLabel positive_left;
+      NearLabel done;
+      __ test(eax, Operand(eax));
+      __ j(not_sign, &positive_left);
+      __ idiv(right_reg);
+
+      // Test the remainder for 0, because then the result would be -0.
+      __ test(edx, Operand(edx));
+      __ j(not_zero, &done);
+
+      DeoptimizeIf(no_condition, instr->environment());
+      __ bind(&positive_left);
+      __ idiv(right_reg);
+      __ bind(&done);
+    } else {
+      __ idiv(right_reg);
+    }
   }
 }
 
@@ -3007,6 +3030,56 @@
 }
 
 
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+  class DeferredStringCharFromCode: public LDeferredCode {
+   public:
+    DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+        : LDeferredCode(codegen), instr_(instr) { }
+    virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+   private:
+    LStringCharFromCode* instr_;
+  };
+
+  DeferredStringCharFromCode* deferred =
+      new DeferredStringCharFromCode(this, instr);
+
+  ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+  ASSERT(!char_code.is(result));
+
+  __ cmp(char_code, String::kMaxAsciiCharCode);
+  __ j(above, deferred->entry());
+  __ Set(result, Immediate(Factory::single_character_string_cache()));
+  __ mov(result, FieldOperand(result,
+                              char_code, times_pointer_size,
+                              FixedArray::kHeaderSize));
+  __ cmp(result, Factory::undefined_value());
+  __ j(equal, deferred->entry());
+  __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+
+  // TODO(3095996): Get rid of this. For now, we need to make the
+  // result register contain a valid pointer because it is already
+  // contained in the register pointer map.
+  __ Set(result, Immediate(0));
+
+  __ PushSafepointRegisters();
+  __ SmiTag(char_code);
+  __ push(char_code);
+  __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+  RecordSafepointWithRegisters(
+      instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+  __ StoreToSafepointRegisterSlot(result, eax);
+  __ PopSafepointRegisters();
+}
+
+
 void LCodeGen::DoStringLength(LStringLength* instr) {
   Register string = ToRegister(instr->string());
   Register result = ToRegister(instr->result());
diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h
index ecd6caa..c5c76c1 100644
--- a/src/ia32/lithium-codegen-ia32.h
+++ b/src/ia32/lithium-codegen-ia32.h
@@ -95,6 +95,7 @@
   void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
   void DoDeferredStackCheck(LGoto* instr);
   void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+  void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
   void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
                                         Label* map_check);
 
diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc
index 0ade55f..3148ae2 100644
--- a/src/ia32/lithium-ia32.cc
+++ b/src/ia32/lithium-ia32.cc
@@ -1366,13 +1366,23 @@
   if (instr->representation().IsInteger32()) {
     ASSERT(instr->left()->representation().IsInteger32());
     ASSERT(instr->right()->representation().IsInteger32());
-    // The temporary operand is necessary to ensure that right is not allocated
-    // into edx.
-    LOperand* temp = FixedTemp(edx);
-    LOperand* value = UseFixed(instr->left(), eax);
-    LOperand* divisor = UseRegister(instr->right());
-    LModI* mod = new LModI(value, divisor, temp);
-    LInstruction* result = DefineFixed(mod, edx);
+
+    LInstruction* result;
+    if (instr->HasPowerOf2Divisor()) {
+      ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+      LOperand* value = UseRegisterAtStart(instr->left());
+      LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL);
+      result = DefineSameAsFirst(mod);
+    } else {
+      // The temporary operand is necessary to ensure that right is
+      // not allocated into edx.
+      LOperand* temp = FixedTemp(edx);
+      LOperand* value = UseFixed(instr->left(), eax);
+      LOperand* divisor = UseRegister(instr->right());
+      LModI* mod = new LModI(value, divisor, temp);
+      result = DefineFixed(mod, edx);
+    }
+
     return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
             instr->CheckFlag(HValue::kCanBeDivByZero))
         ? AssignEnvironment(result)
@@ -1933,6 +1943,13 @@
 }
 
 
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+  LOperand* char_code = UseRegister(instr->value());
+  LStringCharFromCode* result = new LStringCharFromCode(char_code);
+  return AssignPointerMap(DefineAsRegister(result));
+}
+
+
 LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
   LOperand* string = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new LStringLength(string));
diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h
index e832be5..9a07c6f 100644
--- a/src/ia32/lithium-ia32.h
+++ b/src/ia32/lithium-ia32.h
@@ -151,6 +151,7 @@
   V(StoreNamedGeneric)                          \
   V(StorePixelArrayElement)                     \
   V(StringCharCodeAt)                           \
+  V(StringCharFromCode)                         \
   V(StringLength)                               \
   V(SubI)                                       \
   V(TaggedToI)                                  \
@@ -1715,6 +1716,19 @@
 };
 
 
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LStringCharFromCode(LOperand* char_code) {
+    inputs_[0] = char_code;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+  DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+  LOperand* char_code() { return inputs_[0]; }
+};
+
+
 class LStringLength: public LTemplateInstruction<1, 1, 0> {
  public:
   explicit LStringLength(LOperand* string) {
diff --git a/src/lithium-allocator.cc b/src/lithium-allocator.cc
index 1e79c00..1983390 100644
--- a/src/lithium-allocator.cc
+++ b/src/lithium-allocator.cc
@@ -525,6 +525,24 @@
 }
 
 
+LAllocator::LAllocator(int num_values, HGraph* graph)
+    : chunk_(NULL),
+      live_in_sets_(graph->blocks()->length()),
+      live_ranges_(num_values * 2),
+      fixed_live_ranges_(NULL),
+      fixed_double_live_ranges_(NULL),
+      unhandled_live_ranges_(num_values * 2),
+      active_live_ranges_(8),
+      inactive_live_ranges_(8),
+      reusable_slots_(8),
+      next_virtual_register_(num_values),
+      first_artificial_register_(num_values),
+      mode_(NONE),
+      num_registers_(-1),
+      graph_(graph),
+      has_osr_entry_(false) {}
+
+
 void LAllocator::InitializeLivenessAnalysis() {
   // Initialize the live_in sets for each block to NULL.
   int block_count = graph_->blocks()->length();
@@ -618,11 +636,7 @@
 
 
 LiveRange* LAllocator::FixedLiveRangeFor(int index) {
-  if (index >= fixed_live_ranges_.length()) {
-    fixed_live_ranges_.AddBlock(NULL,
-                                index - fixed_live_ranges_.length() + 1);
-  }
-
+  ASSERT(index < Register::kNumAllocatableRegisters);
   LiveRange* result = fixed_live_ranges_[index];
   if (result == NULL) {
     result = new LiveRange(FixedLiveRangeID(index));
@@ -635,11 +649,7 @@
 
 
 LiveRange* LAllocator::FixedDoubleLiveRangeFor(int index) {
-  if (index >= fixed_double_live_ranges_.length()) {
-    fixed_double_live_ranges_.AddBlock(NULL,
-                                index - fixed_double_live_ranges_.length() + 1);
-  }
-
+  ASSERT(index < DoubleRegister::kNumAllocatableRegisters);
   LiveRange* result = fixed_double_live_ranges_[index];
   if (result == NULL) {
     result = new LiveRange(FixedDoubleLiveRangeID(index));
@@ -650,6 +660,7 @@
   return result;
 }
 
+
 LiveRange* LAllocator::LiveRangeFor(int index) {
   if (index >= live_ranges_.length()) {
     live_ranges_.AddBlock(NULL, index - live_ranges_.length() + 1);
diff --git a/src/lithium-allocator.h b/src/lithium-allocator.h
index d53ea78..14a0201 100644
--- a/src/lithium-allocator.h
+++ b/src/lithium-allocator.h
@@ -428,22 +428,7 @@
 
 class LAllocator BASE_EMBEDDED {
  public:
-  explicit LAllocator(int first_virtual_register, HGraph* graph)
-      : chunk_(NULL),
-        live_in_sets_(0),
-        live_ranges_(16),
-        fixed_live_ranges_(8),
-        fixed_double_live_ranges_(8),
-        unhandled_live_ranges_(8),
-        active_live_ranges_(8),
-        inactive_live_ranges_(8),
-        reusable_slots_(8),
-        next_virtual_register_(first_virtual_register),
-        first_artificial_register_(first_virtual_register),
-        mode_(NONE),
-        num_registers_(-1),
-        graph_(graph),
-        has_osr_entry_(false) {}
+  LAllocator(int first_virtual_register, HGraph* graph);
 
   static void Setup();
   static void TraceAlloc(const char* msg, ...);
@@ -468,10 +453,10 @@
   void Allocate(LChunk* chunk);
 
   const ZoneList<LiveRange*>* live_ranges() const { return &live_ranges_; }
-  const ZoneList<LiveRange*>* fixed_live_ranges() const {
+  const Vector<LiveRange*>* fixed_live_ranges() const {
     return &fixed_live_ranges_;
   }
-  const ZoneList<LiveRange*>* fixed_double_live_ranges() const {
+  const Vector<LiveRange*>* fixed_double_live_ranges() const {
     return &fixed_double_live_ranges_;
   }
 
@@ -616,8 +601,10 @@
   ZoneList<LiveRange*> live_ranges_;
 
   // Lists of live ranges
-  ZoneList<LiveRange*> fixed_live_ranges_;
-  ZoneList<LiveRange*> fixed_double_live_ranges_;
+  EmbeddedVector<LiveRange*, Register::kNumAllocatableRegisters>
+      fixed_live_ranges_;
+  EmbeddedVector<LiveRange*, DoubleRegister::kNumAllocatableRegisters>
+      fixed_double_live_ranges_;
   ZoneList<LiveRange*> unhandled_live_ranges_;
   ZoneList<LiveRange*> active_live_ranges_;
   ZoneList<LiveRange*> inactive_live_ranges_;
diff --git a/src/log.cc b/src/log.cc
index bfef643..b43b685 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -881,9 +881,9 @@
 }
 
 
-void Logger::SFIMoveEvent(Address from, Address to) {
+void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
 #ifdef ENABLE_LOGGING_AND_PROFILING
-  MoveEventInternal(SFI_MOVE_EVENT, from, to);
+  MoveEventInternal(SHARED_FUNC_MOVE_EVENT, from, to);
 #endif
 }
 
diff --git a/src/log.h b/src/log.h
index 5470fd6..6e1736d 100644
--- a/src/log.h
+++ b/src/log.h
@@ -91,7 +91,7 @@
   V(CODE_MOVE_EVENT,                "code-move")                \
   V(CODE_DELETE_EVENT,              "code-delete")              \
   V(CODE_MOVING_GC,                 "code-moving-gc")           \
-  V(SFI_MOVE_EVENT,                 "sfi-move")                 \
+  V(SHARED_FUNC_MOVE_EVENT,         "sfi-move")                 \
   V(SNAPSHOT_POSITION_EVENT,        "snapshot-pos")             \
   V(TICK_EVENT,                     "tick")                     \
   V(REPEAT_META_EVENT,              "repeat")                   \
@@ -224,7 +224,7 @@
   // Emits a code delete event.
   static void CodeDeleteEvent(Address from);
 
-  static void SFIMoveEvent(Address from, Address to);
+  static void SharedFunctionInfoMoveEvent(Address from, Address to);
 
   static void SnapshotPositionEvent(Address addr, int pos);
 
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index a4c782c..9df9af1 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -2827,7 +2827,7 @@
 
   HeapObject* copied_to = HeapObject::FromAddress(new_addr);
   if (copied_to->IsSharedFunctionInfo()) {
-    PROFILE(SFIMoveEvent(old_addr, new_addr));
+    PROFILE(SharedFunctionInfoMoveEvent(old_addr, new_addr));
   }
   HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
 
@@ -2919,7 +2919,7 @@
 
   HeapObject* copied_to = HeapObject::FromAddress(new_addr);
   if (copied_to->IsSharedFunctionInfo()) {
-    PROFILE(SFIMoveEvent(old_addr, new_addr));
+    PROFILE(SharedFunctionInfoMoveEvent(old_addr, new_addr));
   }
   HEAP_PROFILE(ObjectMoveEvent(old_addr, new_addr));
 
diff --git a/src/objects-inl.h b/src/objects-inl.h
index eac311b..01073ca 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -3668,6 +3668,22 @@
 }
 
 
+template <typename schar>
+uint32_t HashSequentialString(const schar* chars, int length) {
+  StringHasher hasher(length);
+  if (!hasher.has_trivial_hash()) {
+    int i;
+    for (i = 0; hasher.is_array_index() && (i < length); i++) {
+      hasher.AddCharacter(chars[i]);
+    }
+    for (; i < length; i++) {
+      hasher.AddCharacterNoIndex(chars[i]);
+    }
+  }
+  return hasher.GetHashField();
+}
+
+
 bool String::AsArrayIndex(uint32_t* index) {
   uint32_t field = hash_field();
   if (IsHashFieldComputed(field) && (field & kIsNotArrayIndexMask)) {
diff --git a/src/objects.cc b/src/objects.cc
index bde9998..6e47946 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -5238,22 +5238,6 @@
 }
 
 
-template <typename schar>
-static inline uint32_t HashSequentialString(const schar* chars, int length) {
-  StringHasher hasher(length);
-  if (!hasher.has_trivial_hash()) {
-    int i;
-    for (i = 0; hasher.is_array_index() && (i < length); i++) {
-      hasher.AddCharacter(chars[i]);
-    }
-    for (; i < length; i++) {
-      hasher.AddCharacterNoIndex(chars[i]);
-    }
-  }
-  return hasher.GetHashField();
-}
-
-
 uint32_t String::ComputeAndSetHash() {
   // Should only be called if hash code has not yet been computed.
   ASSERT(!HasHashCode());
diff --git a/src/objects.h b/src/objects.h
index 9f558a4..3d88568 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -5154,6 +5154,11 @@
 };
 
 
+// Calculates string hash.
+template <typename schar>
+inline uint32_t HashSequentialString(const schar* chars, int length);
+
+
 // The characteristics of a string are stored in its map.  Retrieving these
 // few bits of information is moderately expensive, involving two memory
 // loads where the second is dependent on the first.  To improve efficiency
@@ -6535,6 +6540,9 @@
     VisitExternalReferences(p, p + 1);
   }
 
+  // Visits a handle that has an embedder-assigned class ID.
+  virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {}
+
 #ifdef DEBUG
   // Intended for serialization/deserialization checking: insert, or
   // check for the presence of, a tag at this position in the stream.
diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc
index ebe0475..794fe07 100644
--- a/src/platform-solaris.cc
+++ b/src/platform-solaris.cc
@@ -45,7 +45,7 @@
 #include <errno.h>
 #include <ieeefp.h>  // finite()
 #include <signal.h>  // sigemptyset(), etc
-#include <sys/kdi_regs.h>
+#include <sys/regset.h>
 
 
 #undef MAP_TYPE
@@ -612,11 +612,16 @@
 static pthread_t vm_tid_ = 0;
 
 
+static pthread_t GetThreadID() {
+  return pthread_self();
+}
+
+
 static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
   USE(info);
   if (signal != SIGPROF) return;
   if (active_sampler_ == NULL || !active_sampler_->IsActive()) return;
-  if (vm_tid_ != pthread_self()) return;
+  if (vm_tid_ != GetThreadID()) return;
 
   TickSample sample_obj;
   TickSample* sample = CpuProfiler::TickSampleEvent();
@@ -627,17 +632,10 @@
   mcontext_t& mcontext = ucontext->uc_mcontext;
   sample->state = Top::current_vm_state();
 
-#if V8_HOST_ARCH_IA32
-  sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EIP]);
-  sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_ESP]);
-  sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_EBP]);
-#elif V8_HOST_ARCH_X64
-  sample->pc = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RIP]);
-  sample->sp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RSP]);
-  sample->fp = reinterpret_cast<Address>(mcontext.gregs[KDIREG_RBP]);
-#else
-  UNIMPLEMENTED();
-#endif
+  sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_PC]);
+  sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_SP]);
+  sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_FP]);
+
   active_sampler_->SampleStack(sample);
   active_sampler_->Tick(sample);
 }
@@ -645,26 +643,86 @@
 
 class Sampler::PlatformData : public Malloced {
  public:
-  PlatformData() {
-    signal_handler_installed_ = false;
+  enum SleepInterval {
+    FULL_INTERVAL,
+    HALF_INTERVAL
+  };
+
+  explicit PlatformData(Sampler* sampler)
+      : sampler_(sampler),
+        signal_handler_installed_(false),
+        vm_tgid_(getpid()),
+        signal_sender_launched_(false) {
   }
 
+  void SignalSender() {
+    while (sampler_->IsActive()) {
+      if (rate_limiter_.SuspendIfNecessary()) continue;
+      if (sampler_->IsProfiling() && RuntimeProfiler::IsEnabled()) {
+        SendProfilingSignal();
+        Sleep(HALF_INTERVAL);
+        RuntimeProfiler::NotifyTick();
+        Sleep(HALF_INTERVAL);
+      } else {
+        if (sampler_->IsProfiling()) SendProfilingSignal();
+        if (RuntimeProfiler::IsEnabled()) RuntimeProfiler::NotifyTick();
+        Sleep(FULL_INTERVAL);
+      }
+    }
+  }
+
+  void SendProfilingSignal() {
+    if (!signal_handler_installed_) return;
+    pthread_kill(vm_tid_, SIGPROF);
+  }
+
+  void Sleep(SleepInterval full_or_half) {
+    // Convert ms to us and subtract 100 us to compensate delays
+    // occuring during signal delivery.
+    useconds_t interval = sampler_->interval_ * 1000 - 100;
+    if (full_or_half == HALF_INTERVAL) interval /= 2;
+    int result = usleep(interval);
+#ifdef DEBUG
+    if (result != 0 && errno != EINTR) {
+      fprintf(stderr,
+              "SignalSender usleep error; interval = %u, errno = %d\n",
+              interval,
+              errno);
+      ASSERT(result == 0 || errno == EINTR);
+    }
+#endif
+    USE(result);
+  }
+
+  Sampler* sampler_;
   bool signal_handler_installed_;
   struct sigaction old_signal_handler_;
-  struct itimerval old_timer_value_;
+  int vm_tgid_;
+  bool signal_sender_launched_;
+  pthread_t signal_sender_thread_;
+  RuntimeProfilerRateLimiter rate_limiter_;
 };
 
 
+static void* SenderEntry(void* arg) {
+  Sampler::PlatformData* data =
+      reinterpret_cast<Sampler::PlatformData*>(arg);
+  data->SignalSender();
+  return 0;
+}
+
+
 Sampler::Sampler(int interval)
     : interval_(interval),
       profiling_(false),
       active_(false),
       samples_taken_(0) {
-  data_ = new PlatformData();
+  data_ = new PlatformData(this);
 }
 
 
 Sampler::~Sampler() {
+  ASSERT(!data_->signal_sender_launched_);
   delete data_;
 }
 
@@ -672,41 +730,50 @@
 void Sampler::Start() {
   // There can only be one active sampler at the time on POSIX
   // platforms.
-  if (active_sampler_ != NULL) return;
+  ASSERT(!IsActive());
+  vm_tid_ = GetThreadID();
 
   // Request profiling signals.
   struct sigaction sa;
   sa.sa_sigaction = ProfilerSignalHandler;
   sigemptyset(&sa.sa_mask);
-  sa.sa_flags = SA_SIGINFO;
-  if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
-  data_->signal_handler_installed_ = true;
+  sa.sa_flags = SA_RESTART | SA_SIGINFO;
+  data_->signal_handler_installed_ =
+      sigaction(SIGPROF, &sa, &data_->old_signal_handler_) == 0;
 
-  // Set the itimer to generate a tick for each interval.
-  itimerval itimer;
-  itimer.it_interval.tv_sec = interval_ / 1000;
-  itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
-  itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
-  itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
-  setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
+  // Start a thread that sends SIGPROF signal to VM thread.
+  // Sending the signal ourselves instead of relying on itimer provides
+  // much better accuracy.
+  SetActive(true);
+  if (pthread_create(
+          &data_->signal_sender_thread_, NULL, SenderEntry, data_) == 0) {
+    data_->signal_sender_launched_ = true;
+  }
 
   // Set this sampler as the active sampler.
   active_sampler_ = this;
-  active_ = true;
 }
 
 
 void Sampler::Stop() {
+  SetActive(false);
+
+  // Wait for signal sender termination (it will exit after setting
+  // active_ to false).
+  if (data_->signal_sender_launched_) {
+    Top::WakeUpRuntimeProfilerThreadBeforeShutdown();
+    pthread_join(data_->signal_sender_thread_, NULL);
+    data_->signal_sender_launched_ = false;
+  }
+
   // Restore old signal handler
   if (data_->signal_handler_installed_) {
-    setitimer(ITIMER_PROF, &data_->old_timer_value_, NULL);
     sigaction(SIGPROF, &data_->old_signal_handler_, 0);
     data_->signal_handler_installed_ = false;
   }
 
   // This sampler is no longer the active sampler.
   active_sampler_ = NULL;
-  active_ = false;
 }
 
 #endif  // ENABLE_LOGGING_AND_PROFILING
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 7612eab..099b9df 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -29,6 +29,7 @@
 
 #include "v8.h"
 #include "global-handles.h"
+#include "heap-profiler.h"
 #include "scopeinfo.h"
 #include "top.h"
 #include "unicode.h"
@@ -94,51 +95,70 @@
 }
 
 
-static void DeleteIndexName(char** name_ptr) {
-  DeleteArray(*name_ptr);
-}
-
-
 StringsStorage::~StringsStorage() {
   for (HashMap::Entry* p = names_.Start();
        p != NULL;
        p = names_.Next(p)) {
     DeleteArray(reinterpret_cast<const char*>(p->value));
   }
-  index_names_.Iterate(DeleteIndexName);
+}
+
+
+const char* StringsStorage::GetCopy(const char* src) {
+  int len = static_cast<int>(strlen(src));
+  Vector<char> dst = Vector<char>::New(len + 1);
+  OS::StrNCpy(dst, src, len);
+  dst[len] = '\0';
+  uint32_t hash = HashSequentialString(dst.start(), len);
+  return AddOrDisposeString(dst.start(), hash);
+}
+
+
+const char* StringsStorage::GetFormatted(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  const char* result = GetVFormatted(format, args);
+  va_end(args);
+  return result;
+}
+
+
+const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
+  HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
+  if (cache_entry->value == NULL) {
+    // New entry added.
+    cache_entry->value = str;
+  } else {
+    DeleteArray(str);
+  }
+  return reinterpret_cast<const char*>(cache_entry->value);
+}
+
+
+const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
+  Vector<char> str = Vector<char>::New(1024);
+  int len = OS::VSNPrintF(str, format, args);
+  if (len == -1) {
+    DeleteArray(str.start());
+    return format;
+  }
+  uint32_t hash = HashSequentialString(str.start(), len);
+  return AddOrDisposeString(str.start(), hash);
 }
 
 
 const char* StringsStorage::GetName(String* name) {
   if (name->IsString()) {
-    char* c_name =
-        name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
-    HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true);
-    if (cache_entry->value == NULL) {
-      // New entry added.
-      cache_entry->value = c_name;
-    } else {
-      DeleteArray(c_name);
-    }
-    return reinterpret_cast<const char*>(cache_entry->value);
+    return AddOrDisposeString(
+        name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach(),
+        name->Hash());
   }
   return "";
 }
 
 
 const char* StringsStorage::GetName(int index) {
-  ASSERT(index >= 0);
-  if (index_names_.length() <= index) {
-    index_names_.AddBlock(
-        NULL, index - index_names_.length() + 1);
-  }
-  if (index_names_[index] == NULL) {
-    const int kMaximumNameLength = 32;
-    char* name = NewArray<char>(kMaximumNameLength);
-    OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", index);
-    index_names_[index] = name;
-  }
-  return index_names_[index];
+  return GetFormatted("%d", index);
 }
 
 
@@ -465,7 +485,7 @@
 }
 
 
-CodeEntry* const CodeMap::kSfiCodeEntry = NULL;
+CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
 const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
 const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
     CodeMap::CodeEntryInfo(NULL, 0);
@@ -483,18 +503,18 @@
 }
 
 
-int CodeMap::GetSFITag(Address addr) {
+int CodeMap::GetSharedId(Address addr) {
   CodeTree::Locator locator;
-  // For SFI entries, 'size' field is used to store their IDs.
+  // For shared function entries, 'size' field is used to store their IDs.
   if (tree_.Find(addr, &locator)) {
     const CodeEntryInfo& entry = locator.value();
-    ASSERT(entry.entry == kSfiCodeEntry);
+    ASSERT(entry.entry == kSharedFunctionCodeEntry);
     return entry.size;
   } else {
     tree_.Insert(addr, &locator);
-    int tag = next_sfi_tag_++;
-    locator.set_value(CodeEntryInfo(kSfiCodeEntry, tag));
-    return tag;
+    int id = next_shared_id_++;
+    locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
+    return id;
   }
 }
 
@@ -1009,6 +1029,7 @@
     case kArray: return "/array/";
     case kRegExp: return "/regexp/";
     case kHeapNumber: return "/number/";
+    case kNative: return "/native/";
     default: return "???";
   }
 }
@@ -1205,6 +1226,7 @@
       uid_(uid),
       root_entry_(NULL),
       gc_roots_entry_(NULL),
+      natives_root_entry_(NULL),
       raw_entries_(NULL),
       entries_sorted_(false),
       retaining_paths_(HeapEntry::Match) {
@@ -1279,6 +1301,19 @@
 }
 
 
+HeapEntry* HeapSnapshot::AddNativesRootEntry(int children_count,
+                                                 int retainers_count) {
+  ASSERT(natives_root_entry_ == NULL);
+  return (natives_root_entry_ = AddEntry(
+      HeapEntry::kObject,
+      "(Native objects)",
+      HeapObjectsMap::kNativesRootObjectId,
+      0,
+      children_count,
+      retainers_count));
+}
+
+
 HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
                                   const char* name,
                                   uint64_t id,
@@ -1372,10 +1407,13 @@
 }
 
 
-const uint64_t HeapObjectsMap::kInternalRootObjectId = 0;
-const uint64_t HeapObjectsMap::kGcRootsObjectId = 1;
+// 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 = 3;
+const uint64_t HeapObjectsMap::kNativesRootObjectId = 5;
 // Increase kFirstAvailableObjectId if new 'special' objects appear.
-const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2;
+const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 7;
 
 HeapObjectsMap::HeapObjectsMap()
     : initial_fill_mode_(true),
@@ -1400,7 +1438,8 @@
     uint64_t existing = FindEntry(addr);
     if (existing != 0) return existing;
   }
-  uint64_t id = next_id_++;
+  uint64_t id = next_id_;
+  next_id_ += 2;
   AddEntry(addr, id);
   return id;
 }
@@ -1468,6 +1507,17 @@
 }
 
 
+uint64_t HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
+  uint64_t id = static_cast<uint64_t>(info->GetHash());
+  const char* label = info->GetLabel();
+  id ^= HashSequentialString(label, static_cast<int>(strlen(label)));
+  intptr_t element_count = info->GetElementCount();
+  if (element_count != -1)
+    id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count));
+  return id << 1;
+}
+
+
 HeapSnapshotsCollection::HeapSnapshotsCollection()
     : is_tracking_objects_(false),
       snapshots_uids_(HeapSnapshotsMatch),
@@ -1551,6 +1601,8 @@
         p->key,
         entry_info->children_count,
         entry_info->retainers_count);
+    ASSERT(entry_info->entry != NULL);
+    ASSERT(entry_info->entry != kHeapEntryPlaceholder);
     entry_info->children_count = 0;
     entry_info->retainers_count = 0;
   }
@@ -1630,9 +1682,11 @@
 
 
 HeapObject *const V8HeapExplorer::kInternalRootObject =
-    reinterpret_cast<HeapObject*>(1);
+    reinterpret_cast<HeapObject*>(
+        static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
 HeapObject *const V8HeapExplorer::kGcRootsObject =
-    reinterpret_cast<HeapObject*>(2);
+    reinterpret_cast<HeapObject*>(
+        static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
 
 
 V8HeapExplorer::V8HeapExplorer(
@@ -1669,27 +1723,28 @@
     SharedFunctionInfo* shared = func->shared();
     return AddEntry(object,
                     HeapEntry::kClosure,
-                    collection_->GetName(String::cast(shared->name())),
+                    collection_->names()->GetName(String::cast(shared->name())),
                     children_count,
                     retainers_count);
   } else if (object->IsJSRegExp()) {
     JSRegExp* re = JSRegExp::cast(object);
     return AddEntry(object,
                     HeapEntry::kRegExp,
-                    collection_->GetName(re->Pattern()),
+                    collection_->names()->GetName(re->Pattern()),
                     children_count,
                     retainers_count);
   } else if (object->IsJSObject()) {
     return AddEntry(object,
                     HeapEntry::kObject,
-                    collection_->GetName(GetConstructorNameForHeapProfile(
-                        JSObject::cast(object))),
+                    collection_->names()->GetName(
+                        GetConstructorNameForHeapProfile(
+                            JSObject::cast(object))),
                     children_count,
                     retainers_count);
   } else if (object->IsString()) {
     return AddEntry(object,
                     HeapEntry::kString,
-                    collection_->GetName(String::cast(object)),
+                    collection_->names()->GetName(String::cast(object)),
                     children_count,
                     retainers_count);
   } else if (object->IsCode()) {
@@ -1702,7 +1757,7 @@
     SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
     return AddEntry(object,
                     HeapEntry::kCode,
-                    collection_->GetName(String::cast(shared->name())),
+                    collection_->names()->GetName(String::cast(shared->name())),
                     children_count,
                     retainers_count);
   } else if (object->IsScript()) {
@@ -1710,7 +1765,9 @@
     return AddEntry(object,
                     HeapEntry::kCode,
                     script->name()->IsString() ?
-                    collection_->GetName(String::cast(script->name())) : "",
+                        collection_->names()->GetName(
+                            String::cast(script->name()))
+                        : "",
                     children_count,
                     retainers_count);
   } else if (object->IsFixedArray()) {
@@ -1749,8 +1806,8 @@
 
 
 void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
-  filler->AddEntry(kInternalRootObject);
-  filler->AddEntry(kGcRootsObject);
+  filler->AddEntry(kInternalRootObject, this);
+  filler->AddEntry(kGcRootsObject, this);
 }
 
 
@@ -1940,7 +1997,7 @@
 
 HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
   if (!obj->IsHeapObject()) return NULL;
-  return filler_->FindOrAddEntry(obj);
+  return filler_->FindOrAddEntry(obj, this);
 }
 
 
@@ -1992,7 +2049,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+                               collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2043,7 +2100,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kInternal,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(index),
+                               collection_->names()->GetName(index),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2078,7 +2135,7 @@
     filler_->SetNamedReference(type,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+                               collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
     known_references_.Insert(child_obj);
@@ -2096,7 +2153,7 @@
     filler_->SetNamedReference(HeapGraphEdge::kShortcut,
                                parent_obj,
                                parent_entry,
-                               collection_->GetName(reference_name),
+                               collection_->names()->GetName(reference_name),
                                child_obj,
                                child_entry);
   }
@@ -2132,25 +2189,215 @@
 }
 
 
+class GlobalHandlesExtractor : public ObjectVisitor {
+ public:
+  explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
+      : explorer_(explorer) {}
+  virtual ~GlobalHandlesExtractor() {}
+  virtual void VisitPointers(Object** start, Object** end) {
+    UNREACHABLE();
+  }
+  virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
+    explorer_->VisitSubtreeWrapper(p, class_id);
+  }
+ private:
+  NativeObjectsExplorer* explorer_;
+};
+
+HeapThing const NativeObjectsExplorer::kNativesRootObject =
+    reinterpret_cast<HeapThing>(
+        static_cast<intptr_t>(HeapObjectsMap::kNativesRootObjectId));
+
+
+NativeObjectsExplorer::NativeObjectsExplorer(
+    HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
+    : snapshot_(snapshot),
+      collection_(snapshot_->collection()),
+      progress_(progress),
+      embedder_queried_(false),
+      objects_by_info_(RetainedInfosMatch),
+      filler_(NULL) {
+}
+
+
+NativeObjectsExplorer::~NativeObjectsExplorer() {
+  for (HashMap::Entry* p = objects_by_info_.Start();
+       p != NULL;
+       p = objects_by_info_.Next(p)) {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+    info->Dispose();
+    List<HeapObject*>* objects =
+        reinterpret_cast<List<HeapObject*>* >(p->value);
+    delete objects;
+  }
+}
+
+
+HeapEntry* NativeObjectsExplorer::AllocateEntry(
+    HeapThing ptr, int children_count, int retainers_count) {
+  if (ptr == kNativesRootObject) {
+    return snapshot_->AddNativesRootEntry(children_count, retainers_count);
+  } else {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
+    intptr_t elements = info->GetElementCount();
+    intptr_t size = info->GetSizeInBytes();
+    return snapshot_->AddEntry(
+        HeapEntry::kNative,
+        elements != -1 ?
+            collection_->names()->GetFormatted(
+                "%s / %" V8_PTR_PREFIX "d entries",
+                info->GetLabel(),
+                info->GetElementCount()) :
+                collection_->names()->GetCopy(info->GetLabel()),
+        HeapObjectsMap::GenerateId(info),
+        size != -1 ? static_cast<int>(size) : 0,
+        children_count,
+        retainers_count);
+  }
+}
+
+
+void NativeObjectsExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
+  if (EstimateObjectsCount() <= 0) return;
+  filler->AddEntry(kNativesRootObject, this);
+}
+
+
+int NativeObjectsExplorer::EstimateObjectsCount() {
+  FillRetainedObjects();
+  return objects_by_info_.occupancy();
+}
+
+
+void NativeObjectsExplorer::FillRetainedObjects() {
+  if (embedder_queried_) return;
+  // Record objects that are joined into ObjectGroups.
+  Heap::CallGlobalGCPrologueCallback();
+  List<ObjectGroup*>* groups = GlobalHandles::ObjectGroups();
+  for (int i = 0; i < groups->length(); ++i) {
+    ObjectGroup* group = groups->at(i);
+    if (group->info_ == NULL) continue;
+    List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
+    for (int j = 0; j < group->objects_.length(); ++j) {
+      HeapObject* obj = HeapObject::cast(*group->objects_[j]);
+      list->Add(obj);
+      in_groups_.Insert(obj);
+    }
+    group->info_ = NULL;  // Acquire info object ownership.
+  }
+  GlobalHandles::RemoveObjectGroups();
+  Heap::CallGlobalGCEpilogueCallback();
+  // Record objects that are not in ObjectGroups, but have class ID.
+  GlobalHandlesExtractor extractor(this);
+  GlobalHandles::IterateAllRootsWithClassIds(&extractor);
+  embedder_queried_ = true;
+}
+
+
+List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
+    v8::RetainedObjectInfo* info) {
+  HashMap::Entry* entry =
+      objects_by_info_.Lookup(info, InfoHash(info), true);
+  if (entry->value != NULL) {
+    info->Dispose();
+  } else {
+    entry->value = new List<HeapObject*>(4);
+  }
+  return reinterpret_cast<List<HeapObject*>* >(entry->value);
+}
+
+
+bool NativeObjectsExplorer::IterateAndExtractReferences(
+    SnapshotFillerInterface* filler) {
+  if (EstimateObjectsCount() <= 0) return true;
+  filler_ = filler;
+  FillRetainedObjects();
+  for (HashMap::Entry* p = objects_by_info_.Start();
+       p != NULL;
+       p = objects_by_info_.Next(p)) {
+    v8::RetainedObjectInfo* info =
+        reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
+    SetNativeRootReference(info);
+    List<HeapObject*>* objects =
+        reinterpret_cast<List<HeapObject*>* >(p->value);
+    for (int i = 0; i < objects->length(); ++i) {
+      SetWrapperNativeReferences(objects->at(i), info);
+    }
+  }
+  SetRootNativesRootReference();
+  filler_ = NULL;
+  return true;
+}
+
+
+void NativeObjectsExplorer::SetNativeRootReference(
+    v8::RetainedObjectInfo* info) {
+  HeapEntry* child_entry = filler_->FindOrAddEntry(info, this);
+  ASSERT(child_entry != NULL);
+  filler_->SetIndexedAutoIndexReference(
+      HeapGraphEdge::kElement,
+      kNativesRootObject, snapshot_->natives_root(),
+      info, child_entry);
+}
+
+
+void NativeObjectsExplorer::SetWrapperNativeReferences(
+    HeapObject* wrapper, v8::RetainedObjectInfo* info) {
+  HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
+  ASSERT(wrapper_entry != NULL);
+  HeapEntry* info_entry = filler_->FindOrAddEntry(info, this);
+  ASSERT(info_entry != NULL);
+  filler_->SetNamedReference(HeapGraphEdge::kInternal,
+                             wrapper, wrapper_entry,
+                             "Native",
+                             info, info_entry);
+  filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
+                                        info, info_entry,
+                                        wrapper, wrapper_entry);
+}
+
+
+void NativeObjectsExplorer::SetRootNativesRootReference() {
+  filler_->SetIndexedAutoIndexReference(
+      HeapGraphEdge::kElement,
+      V8HeapExplorer::kInternalRootObject, snapshot_->root(),
+      kNativesRootObject, snapshot_->natives_root());
+}
+
+
+void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
+  if (in_groups_.Contains(*p)) return;
+  v8::RetainedObjectInfo* info =
+      HeapProfiler::ExecuteWrapperClassCallback(class_id, p);
+  if (info == NULL) return;
+  GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
+}
+
+
 HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
                                              v8::ActivityControl* control)
     : snapshot_(snapshot),
       control_(control),
-      v8_heap_explorer_(snapshot_, this) {
+      v8_heap_explorer_(snapshot_, this),
+      dom_explorer_(snapshot_, this) {
 }
 
 
 class SnapshotCounter : public SnapshotFillerInterface {
  public:
-  SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries)
-      : allocator_(allocator), entries_(entries) { }
-  HeapEntry* AddEntry(HeapThing ptr) {
-    entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder);
+  explicit SnapshotCounter(HeapEntriesMap* entries) : entries_(entries) { }
+  HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    entries_->Pair(ptr, allocator, HeapEntriesMap::kHeapEntryPlaceholder);
     return HeapEntriesMap::kHeapEntryPlaceholder;
   }
-  HeapEntry* FindOrAddEntry(HeapThing ptr) {
-    HeapEntry* entry = entries_->Map(ptr);
-    return entry != NULL ? entry : AddEntry(ptr);
+  HeapEntry* FindEntry(HeapThing ptr) {
+    return entries_->Map(ptr);
+  }
+  HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    HeapEntry* entry = FindEntry(ptr);
+    return entry != NULL ? entry : AddEntry(ptr, allocator);
   }
   void SetIndexedReference(HeapGraphEdge::Type,
                            HeapThing parent_ptr,
@@ -2183,7 +2430,6 @@
     entries_->CountReference(parent_ptr, child_ptr);
   }
  private:
-  HeapEntriesAllocator* allocator_;
   HeapEntriesMap* entries_;
 };
 
@@ -2194,13 +2440,16 @@
       : snapshot_(snapshot),
         collection_(snapshot->collection()),
         entries_(entries) { }
-  HeapEntry* AddEntry(HeapThing ptr) {
+  HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
     UNREACHABLE();
     return NULL;
   }
-  HeapEntry* FindOrAddEntry(HeapThing ptr) {
-    HeapEntry* entry = entries_->Map(ptr);
-    return entry != NULL ? entry : AddEntry(ptr);
+  HeapEntry* FindEntry(HeapThing ptr) {
+    return entries_->Map(ptr);
+  }
+  HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
+    HeapEntry* entry = FindEntry(ptr);
+    return entry != NULL ? entry : AddEntry(ptr, allocator);
   }
   void SetIndexedReference(HeapGraphEdge::Type type,
                            HeapThing parent_ptr,
@@ -2247,7 +2496,7 @@
         parent_ptr, child_ptr, &child_index, &retainer_index);
     parent_entry->SetNamedReference(type,
                               child_index,
-                              collection_->GetName(child_index + 1),
+                              collection_->names()->GetName(child_index + 1),
                               child_entry,
                               retainer_index);
   }
@@ -2303,21 +2552,28 @@
 
 void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
   if (control_ == NULL) return;
-  progress_total_ = v8_heap_explorer_.EstimateObjectsCount() * iterations_count;
+  progress_total_ = (
+      v8_heap_explorer_.EstimateObjectsCount() +
+      dom_explorer_.EstimateObjectsCount()) * iterations_count;
   progress_counter_ = 0;
 }
 
 
 bool HeapSnapshotGenerator::CountEntriesAndReferences() {
-  SnapshotCounter counter(&v8_heap_explorer_, &entries_);
+  SnapshotCounter counter(&entries_);
   v8_heap_explorer_.AddRootEntries(&counter);
-  return v8_heap_explorer_.IterateAndExtractReferences(&counter);
+  dom_explorer_.AddRootEntries(&counter);
+  return
+      v8_heap_explorer_.IterateAndExtractReferences(&counter) &&
+      dom_explorer_.IterateAndExtractReferences(&counter);
 }
 
 
 bool HeapSnapshotGenerator::FillReferences() {
   SnapshotFiller filler(snapshot_, &entries_);
-  return v8_heap_explorer_.IterateAndExtractReferences(&filler);
+  return
+      v8_heap_explorer_.IterateAndExtractReferences(&filler) &&
+      dom_explorer_.IterateAndExtractReferences(&filler);
 }
 
 
@@ -2735,7 +2991,8 @@
             "," JSON_S("code")
             "," JSON_S("closure")
             "," JSON_S("regexp")
-            "," JSON_S("number"))
+            "," JSON_S("number")
+            "," JSON_S("native"))
         "," JSON_S("string")
         "," JSON_S("number")
         "," JSON_S("number")
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 4762eb6..559ffc6 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -66,6 +66,9 @@
   StringsStorage();
   ~StringsStorage();
 
+  const char* GetCopy(const char* src);
+  const char* GetFormatted(const char* format, ...);
+  const char* GetVFormatted(const char* format, va_list args);
   const char* GetName(String* name);
   const char* GetName(int index);
   inline const char* GetFunctionName(String* name);
@@ -76,11 +79,10 @@
     return strcmp(reinterpret_cast<char*>(key1),
                   reinterpret_cast<char*>(key2)) == 0;
   }
+  const char* AddOrDisposeString(char* str, uint32_t hash);
 
   // Mapping of strings by String::Hash to const char* strings.
   HashMap names_;
-  // Mapping from ints to char* strings.
-  List<char*> index_names_;
 
   DISALLOW_COPY_AND_ASSIGN(StringsStorage);
 };
@@ -236,12 +238,12 @@
 
 class CodeMap {
  public:
-  CodeMap() : next_sfi_tag_(1) { }
+  CodeMap() : next_shared_id_(1) { }
   INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size));
   INLINE(void MoveCode(Address from, Address to));
   INLINE(void DeleteCode(Address addr));
   CodeEntry* FindEntry(Address addr);
-  int GetSFITag(Address addr);
+  int GetSharedId(Address addr);
 
   void Print();
 
@@ -269,11 +271,11 @@
     void Call(const Address& key, const CodeEntryInfo& value);
   };
 
-  // Fake CodeEntry pointer to distinguish SFI entries.
-  static CodeEntry* const kSfiCodeEntry;
+  // Fake CodeEntry pointer to distinguish shared function entries.
+  static CodeEntry* const kSharedFunctionCodeEntry;
 
   CodeTree tree_;
-  int next_sfi_tag_;
+  int next_shared_id_;
 
   DISALLOW_COPY_AND_ASSIGN(CodeMap);
 };
@@ -517,7 +519,8 @@
     kCode = v8::HeapGraphNode::kCode,
     kClosure = v8::HeapGraphNode::kClosure,
     kRegExp = v8::HeapGraphNode::kRegExp,
-    kHeapNumber = v8::HeapGraphNode::kHeapNumber
+    kHeapNumber = v8::HeapGraphNode::kHeapNumber,
+    kNative = v8::HeapGraphNode::kNative
   };
 
   HeapEntry() { }
@@ -604,8 +607,8 @@
   const char* TypeAsString();
 
   unsigned painted_: 2;
-  unsigned type_: 3;
-  int children_count_: 27;
+  unsigned type_: 4;
+  int children_count_: 26;
   int retainers_count_;
   int self_size_;
   union {
@@ -677,6 +680,7 @@
   unsigned uid() { return uid_; }
   HeapEntry* root() { return root_entry_; }
   HeapEntry* gc_roots() { return gc_roots_entry_; }
+  HeapEntry* natives_root() { return natives_root_entry_; }
   List<HeapEntry*>* entries() { return &entries_; }
 
   void AllocateEntries(
@@ -689,6 +693,7 @@
                       int retainers_count);
   HeapEntry* AddRootEntry(int children_count);
   HeapEntry* AddGcRootsEntry(int children_count, int retainers_count);
+  HeapEntry* AddNativesRootEntry(int children_count, int retainers_count);
   void ClearPaint();
   HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot);
   HeapEntry* GetEntryById(uint64_t id);
@@ -710,6 +715,7 @@
   unsigned uid_;
   HeapEntry* root_entry_;
   HeapEntry* gc_roots_entry_;
+  HeapEntry* natives_root_entry_;
   char* raw_entries_;
   List<HeapEntry*> entries_;
   bool entries_sorted_;
@@ -733,8 +739,11 @@
   uint64_t FindObject(Address addr);
   void MoveObject(Address from, Address to);
 
+  static uint64_t GenerateId(v8::RetainedObjectInfo* info);
+
   static const uint64_t kInternalRootObjectId;
   static const uint64_t kGcRootsObjectId;
+  static const uint64_t kNativesRootObjectId;
   static const uint64_t kFirstAvailableObjectId;
 
  private:
@@ -832,12 +841,7 @@
   List<HeapSnapshot*>* snapshots() { return &snapshots_; }
   HeapSnapshot* GetSnapshot(unsigned uid);
 
-  const char* GetName(String* name) { return names_.GetName(name); }
-  const char* GetName(int index) { return names_.GetName(index); }
-  const char* GetFunctionName(String* name) {
-    return names_.GetFunctionName(name);
-  }
-
+  StringsStorage* names() { return &names_; }
   TokenEnumerator* token_enumerator() { return token_enumerator_; }
 
   uint64_t GetObjectId(Address addr) { return ids_.FindObject(addr); }
@@ -950,8 +954,11 @@
 class SnapshotFillerInterface {
  public:
   virtual ~SnapshotFillerInterface() { }
-  virtual HeapEntry* AddEntry(HeapThing ptr) = 0;
-  virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0;
+  virtual HeapEntry* AddEntry(HeapThing ptr,
+                              HeapEntriesAllocator* allocator) = 0;
+  virtual HeapEntry* FindEntry(HeapThing ptr) = 0;
+  virtual HeapEntry* FindOrAddEntry(HeapThing ptr,
+                                    HeapEntriesAllocator* allocator) = 0;
   virtual void SetIndexedReference(HeapGraphEdge::Type type,
                                    HeapThing parent_ptr,
                                    HeapEntry* parent_entry,
@@ -990,13 +997,15 @@
  public:
   V8HeapExplorer(HeapSnapshot* snapshot,
                  SnapshottingProgressReportingInterface* progress);
-  ~V8HeapExplorer();
+  virtual ~V8HeapExplorer();
   virtual HeapEntry* AllocateEntry(
       HeapThing ptr, int children_count, int retainers_count);
   void AddRootEntries(SnapshotFillerInterface* filler);
   int EstimateObjectsCount();
   bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
 
+  static HeapObject* const kInternalRootObject;
+
  private:
   HeapEntry* AddEntry(
       HeapObject* object, int children_count, int retainers_count);
@@ -1052,7 +1061,6 @@
   HeapObjectsSet known_references_;
   SnapshotFillerInterface* filler_;
 
-  static HeapObject* const kInternalRootObject;
   static HeapObject* const kGcRootsObject;
 
   friend class IndexedReferencesExtractor;
@@ -1062,6 +1070,54 @@
 };
 
 
+// An implementation of retained native objects extractor.
+class NativeObjectsExplorer : public HeapEntriesAllocator {
+ public:
+  NativeObjectsExplorer(HeapSnapshot* snapshot,
+                      SnapshottingProgressReportingInterface* progress);
+  virtual ~NativeObjectsExplorer();
+  virtual HeapEntry* AllocateEntry(
+      HeapThing ptr, int children_count, int retainers_count);
+  void AddRootEntries(SnapshotFillerInterface* filler);
+  int EstimateObjectsCount();
+  bool IterateAndExtractReferences(SnapshotFillerInterface* filler);
+
+ private:
+  void FillRetainedObjects();
+  List<HeapObject*>* GetListMaybeDisposeInfo(v8::RetainedObjectInfo* info);
+  void SetNativeRootReference(v8::RetainedObjectInfo* info);
+  void SetRootNativesRootReference();
+  void SetWrapperNativeReferences(HeapObject* wrapper,
+                                      v8::RetainedObjectInfo* info);
+  void VisitSubtreeWrapper(Object** p, uint16_t class_id);
+
+  static uint32_t InfoHash(v8::RetainedObjectInfo* info) {
+    return ComputeIntegerHash(static_cast<uint32_t>(info->GetHash()));
+  }
+  static bool RetainedInfosMatch(void* key1, void* key2) {
+    return key1 == key2 ||
+        (reinterpret_cast<v8::RetainedObjectInfo*>(key1))->IsEquivalent(
+            reinterpret_cast<v8::RetainedObjectInfo*>(key2));
+  }
+
+  HeapSnapshot* snapshot_;
+  HeapSnapshotsCollection* collection_;
+  SnapshottingProgressReportingInterface* progress_;
+  bool embedder_queried_;
+  HeapObjectsSet in_groups_;
+  // RetainedObjectInfo* -> List<HeapObject*>*
+  HashMap objects_by_info_;
+  // Used during references extraction.
+  SnapshotFillerInterface* filler_;
+
+  static HeapThing const kNativesRootObject;
+
+  friend class GlobalHandlesExtractor;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeObjectsExplorer);
+};
+
+
 class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface {
  public:
   HeapSnapshotGenerator(HeapSnapshot* snapshot,
@@ -1083,6 +1139,7 @@
   HeapSnapshot* snapshot_;
   v8::ActivityControl* control_;
   V8HeapExplorer v8_heap_explorer_;
+  NativeObjectsExplorer dom_explorer_;
   // Mapping from HeapThing pointers to HeapEntry* pointers.
   HeapEntriesMap entries_;
   // Used during snapshot generation.
diff --git a/src/v8.cc b/src/v8.cc
index 945043d..27648b1 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -158,7 +158,7 @@
 void V8::TearDown() {
   if (!has_been_setup_ || has_been_disposed_) return;
 
-  if (FLAG_time_hydrogen) HStatistics::Instance()->Print();
+  if (FLAG_hydrogen_stats) HStatistics::Instance()->Print();
 
   // We must stop the logger before we tear down other components.
   Logger::EnsureTickerStopped();
diff --git a/src/v8globals.h b/src/v8globals.h
index d11bc38..e44b9e7 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -472,7 +472,11 @@
 // The Strict Mode (ECMA-262 5th edition, 4.2.2).
 enum StrictModeFlag {
   kNonStrictMode,
-  kStrictMode
+  kStrictMode,
+  // This value is never used, but is needed to prevent GCC 4.5 from failing
+  // to compile when we assert that a flag is either kNonStrictMode or
+  // kStrictMode.
+  kInvalidStrictFlag
 };
 
 } }  // namespace v8::internal
diff --git a/src/v8natives.js b/src/v8natives.js
index 563de73..52ff8d2 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -495,7 +495,7 @@
 // property descriptor. For a description of the array layout please
 // see the runtime.cc file.
 function ConvertDescriptorArrayToDescriptor(desc_array) {
-  if (desc_array == false) {
+  if (desc_array === false) {
     throw 'Internal error: invalid desc_array';
   }
 
@@ -544,7 +544,7 @@
   var props = %GetOwnProperty(ToObject(obj), ToString(p));
 
   // A false value here means that access checks failed.
-  if (props == false) return void 0;
+  if (props === false) return void 0;
 
   return ConvertDescriptorArrayToDescriptor(props);
 }
@@ -554,7 +554,7 @@
 function DefineOwnProperty(obj, p, desc, should_throw) {
   var current_or_access = %GetOwnProperty(ToObject(obj), ToString(p));
   // A false value here means that access checks failed.
-  if (current_or_access == false) return void 0;
+  if (current_or_access === false) return void 0;
 
   var current = ConvertDescriptorArrayToDescriptor(current_or_access);
   var extensible = %IsExtensible(ToObject(obj));
diff --git a/src/version.cc b/src/version.cc
index 024adca..c1eab1a 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,8 +34,8 @@
 // cannot be changed without changing the SCons build script.
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     2
-#define BUILD_NUMBER      1
-#define PATCH_LEVEL       1
+#define BUILD_NUMBER      2
+#define PATCH_LEVEL       0
 #define CANDIDATE_VERSION false
 
 // Define SONAME to have the SCons build the put a specific SONAME into the
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 41111a7..f879ae7 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -191,12 +191,12 @@
 // Register constants.
 
 const int Register::kRegisterCodeByAllocationIndex[kNumAllocatableRegisters] = {
-  // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12
-  0, 3, 2, 1, 7, 8, 9, 11, 14, 12
+  // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r15
+  0, 3, 2, 1, 7, 8, 9, 11, 14, 15
 };
 
 const int Register::kAllocationIndexByRegisterCode[kNumRegisters] = {
-  0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1
+  0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, -1, -1, 8, 9
 };
 
 
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index ab26d74..0b7bdc0 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -93,8 +93,8 @@
   //  rbp - frame pointer
   //  rsi - context register
   //  r10 - fixed scratch register
+  //  r12 - smi constant register
   //  r13 - root register
-  //  r15 - smi constant register
   static const int kNumRegisters = 16;
   static const int kNumAllocatableRegisters = 10;
 
@@ -120,7 +120,7 @@
       "r9",
       "r11",
       "r14",
-      "r12"
+      "r15"
     };
     return names[index];
   }
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 9999a1c..b11072c 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -46,8 +46,8 @@
   __ Ret();
 
   __ bind(&check_heap_number);
-  __ Move(rbx, Factory::heap_number_map());
-  __ cmpq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
+  __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+                 Heap::kHeapNumberMapRootIndex);
   __ j(not_equal, &call_builtin);
   __ Ret();
 
@@ -104,7 +104,7 @@
   __ pop(rdx);
   __ push(rsi);
   __ push(rdx);
-  __ Push(Factory::false_value());
+  __ PushRoot(Heap::kFalseValueRootIndex);
   __ push(rcx);  // Restore return address.
   __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
 }
@@ -2493,7 +2493,8 @@
   // Check that the JSArray is in fast case.
   __ movq(rbx, FieldOperand(rdi, JSArray::kElementsOffset));
   __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset));
-  __ Cmp(rdi, Factory::fixed_array_map());
+  __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset),
+                 Heap::kFixedArrayMapRootIndex);
   __ j(not_equal, &runtime);
   // Check that the last match info has space for the capture registers and the
   // additional information. Ensure no overflow in add.
@@ -2528,8 +2529,8 @@
   __ testb(rbx, Immediate(kIsNotStringMask | kExternalStringTag));
   __ j(not_zero, &runtime);
   // String is a cons string.
-  __ movq(rdx, FieldOperand(rdi, ConsString::kSecondOffset));
-  __ Cmp(rdx, Factory::empty_string());
+  __ CompareRoot(FieldOperand(rdi, ConsString::kSecondOffset),
+                 Heap::kEmptyStringRootIndex);
   __ j(not_equal, &runtime);
   __ movq(rdi, FieldOperand(rdi, ConsString::kFirstOffset));
   __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
@@ -2793,8 +2794,8 @@
   __ movq(FieldOperand(rax, HeapObject::kMapOffset), rdx);
 
   // Set empty properties FixedArray.
-  __ Move(FieldOperand(rax, JSObject::kPropertiesOffset),
-          Factory::empty_fixed_array());
+  __ LoadRoot(kScratchRegister, Heap::kEmptyFixedArrayRootIndex);
+  __ movq(FieldOperand(rax, JSObject::kPropertiesOffset), kScratchRegister);
 
   // Set elements to point to FixedArray allocated right after the JSArray.
   __ lea(rcx, Operand(rax, JSRegExpResult::kSize));
@@ -2814,13 +2815,13 @@
   // rbx: Number of elements in array as int32.
 
   // Set map.
-  __ Move(FieldOperand(rcx, HeapObject::kMapOffset),
-          Factory::fixed_array_map());
+  __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
+  __ movq(FieldOperand(rcx, HeapObject::kMapOffset), kScratchRegister);
   // Set length.
   __ Integer32ToSmi(rdx, rbx);
   __ movq(FieldOperand(rcx, FixedArray::kLengthOffset), rdx);
   // Fill contents of fixed-array with the-hole.
-  __ Move(rdx, Factory::the_hole_value());
+  __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex);
   __ lea(rcx, FieldOperand(rcx, FixedArray::kHeaderSize));
   // Fill fixed array elements with hole.
   // rax: JSArray.
@@ -3321,7 +3322,7 @@
   // rbp: frame pointer  (restored after C call).
   // rsp: stack pointer  (restored after C call).
   // r14: number of arguments including receiver (C callee-saved).
-  // r12: pointer to the first argument (C callee-saved).
+  // r15: pointer to the first argument (C callee-saved).
   //      This pointer is reused in LeaveExitFrame(), so it is stored in a
   //      callee-saved register.
 
@@ -3362,7 +3363,7 @@
   // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
   // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
   __ movq(StackSpaceOperand(0), r14);  // argc.
-  __ movq(StackSpaceOperand(1), r12);  // argv.
+  __ movq(StackSpaceOperand(1), r15);  // argv.
   if (result_size_ < 2) {
     // Pass a pointer to the Arguments object as the first argument.
     // Return result in single register (rax).
@@ -3378,7 +3379,7 @@
 #else  // _WIN64
   // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
   __ movq(rdi, r14);  // argc.
-  __ movq(rsi, r12);  // argv.
+  __ movq(rsi, r15);  // argv.
 #endif
   __ call(rbx);
   // Result is in rax - do not destroy this register!
@@ -3482,7 +3483,7 @@
   // rbp: frame pointer of exit frame  (restored after C call).
   // rsp: stack pointer (restored after C call).
   // r14: number of arguments including receiver (C callee-saved).
-  // r12: argv pointer (C callee-saved).
+  // r15: argv pointer (C callee-saved).
 
   Label throw_normal_exception;
   Label throw_termination_exception;
@@ -4173,7 +4174,7 @@
   // just allocate a new one.
   Label make_two_character_string, make_flat_ascii_string;
   StringHelper::GenerateTwoCharacterSymbolTableProbe(
-      masm, rbx, rcx, r14, r11, rdi, r12, &make_two_character_string);
+      masm, rbx, rcx, r14, r11, rdi, r15, &make_two_character_string);
   __ IncrementCounter(&Counters::string_add_native, 1);
   __ ret(2 * kPointerSize);
 
@@ -4945,7 +4946,7 @@
   __ bind(&index_out_of_range);
   // When the index is out of range, the spec requires us to return
   // the empty string.
-  __ Move(result, Factory::empty_string());
+  __ LoadRoot(result, Heap::kEmptyStringRootIndex);
   __ jmp(&done);
 
   __ bind(&need_conversion);
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index e3ab7c2..3c054cd 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -7961,7 +7961,7 @@
       && (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
       && (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
       && (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
-      && (allocator()->count(r12) == (frame()->is_used(r12) ? 1 : 0));
+      && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0));
 }
 #endif
 
diff --git a/src/x64/deoptimizer-x64.cc b/src/x64/deoptimizer-x64.cc
index daa9128..1771570 100644
--- a/src/x64/deoptimizer-x64.cc
+++ b/src/x64/deoptimizer-x64.cc
@@ -102,6 +102,7 @@
 
 
 void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
+  HandleScope scope;
   AssertNoAllocation no_allocation;
 
   if (!function->IsOptimized()) return;
@@ -196,6 +197,11 @@
     PrintF("[forced deoptimization: ");
     function->PrintName();
     PrintF(" / %" V8PRIxPTR "]\n", reinterpret_cast<intptr_t>(function));
+#ifdef DEBUG
+    if (FLAG_print_code) {
+      code->PrintLn();
+    }
+#endif
   }
 }
 
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index cca99a1..dbadcac 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -752,41 +752,65 @@
 
 
 void LCodeGen::DoModI(LModI* instr) {
-  LOperand* right = instr->InputAt(1);
-  ASSERT(ToRegister(instr->result()).is(rdx));
-  ASSERT(ToRegister(instr->InputAt(0)).is(rax));
-  ASSERT(!ToRegister(instr->InputAt(1)).is(rax));
-  ASSERT(!ToRegister(instr->InputAt(1)).is(rdx));
+  if (instr->hydrogen()->HasPowerOf2Divisor()) {
+    Register dividend = ToRegister(instr->InputAt(0));
 
-  Register right_reg = ToRegister(right);
+    int32_t divisor =
+        HConstant::cast(instr->hydrogen()->right())->Integer32Value();
 
-  // Check for x % 0.
-  if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
-    __ testl(right_reg, right_reg);
-    DeoptimizeIf(zero, instr->environment());
-  }
+    if (divisor < 0) divisor = -divisor;
 
-  // Sign extend eax to edx. (We are using only the low 32 bits of the values.)
-  __ cdq();
-
-  // Check for (0 % -x) that will produce negative zero.
-  if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
-    NearLabel positive_left;
-    NearLabel done;
-    __ testl(rax, rax);
-    __ j(not_sign, &positive_left);
-    __ idivl(right_reg);
-
-    // Test the remainder for 0, because then the result would be -0.
-    __ testl(rdx, rdx);
-    __ j(not_zero, &done);
-
-    DeoptimizeIf(no_condition, instr->environment());
-    __ bind(&positive_left);
-    __ idivl(right_reg);
+    NearLabel positive_dividend, done;
+    __ testl(dividend, dividend);
+    __ j(not_sign, &positive_dividend);
+    __ negl(dividend);
+    __ andl(dividend, Immediate(divisor - 1));
+    __ negl(dividend);
+    if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+      __ j(not_zero, &done);
+      DeoptimizeIf(no_condition, instr->environment());
+    }
+    __ bind(&positive_dividend);
+    __ andl(dividend, Immediate(divisor - 1));
     __ bind(&done);
   } else {
-    __ idivl(right_reg);
+    LOperand* right = instr->InputAt(1);
+    Register right_reg = ToRegister(right);
+
+    ASSERT(ToRegister(instr->result()).is(rdx));
+    ASSERT(ToRegister(instr->InputAt(0)).is(rax));
+    ASSERT(!right_reg.is(rax));
+    ASSERT(!right_reg.is(rdx));
+
+    // Check for x % 0.
+    if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
+      __ testl(right_reg, right_reg);
+      DeoptimizeIf(zero, instr->environment());
+    }
+
+    // Sign extend eax to edx.
+    // (We are using only the low 32 bits of the values.)
+    __ cdq();
+
+    // Check for (0 % -x) that will produce negative zero.
+    if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+      NearLabel positive_left;
+      NearLabel done;
+      __ testl(rax, rax);
+      __ j(not_sign, &positive_left);
+      __ idivl(right_reg);
+
+      // Test the remainder for 0, because then the result would be -0.
+      __ testl(rdx, rdx);
+      __ j(not_zero, &done);
+
+      DeoptimizeIf(no_condition, instr->environment());
+      __ bind(&positive_left);
+      __ idivl(right_reg);
+      __ bind(&done);
+    } else {
+      __ idivl(right_reg);
+    }
   }
 }
 
@@ -1059,7 +1083,7 @@
 void LCodeGen::DoExternalArrayLength(LExternalArrayLength* instr) {
   Register result = ToRegister(instr->result());
   Register array = ToRegister(instr->InputAt(0));
-  __ movq(result, FieldOperand(array, ExternalPixelArray::kLengthOffset));
+  __ movl(result, FieldOperand(array, ExternalPixelArray::kLengthOffset));
 }
 
 
@@ -1210,7 +1234,7 @@
     Register reg = ToRegister(instr->InputAt(0));
     HType type = instr->hydrogen()->type();
     if (type.IsBoolean()) {
-      __ Cmp(reg, Factory::true_value());
+      __ CompareRoot(reg, Heap::kTrueValueRootIndex);
       EmitBranch(true_block, false_block, equal);
     } else if (type.IsSmi()) {
       __ SmiCompare(reg, Smi::FromInt(0));
@@ -1477,14 +1501,14 @@
 
   int true_block = chunk_->LookupDestination(instr->true_block_id());
 
-  __ Cmp(reg, Factory::null_value());
+  __ CompareRoot(reg, Heap::kNullValueRootIndex);
   if (instr->is_strict()) {
     EmitBranch(true_block, false_block, equal);
   } else {
     Label* true_label = chunk_->GetAssemblyLabel(true_block);
     Label* false_label = chunk_->GetAssemblyLabel(false_block);
     __ j(equal, true_label);
-    __ Cmp(reg, Factory::undefined_value());
+    __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
     __ j(equal, true_label);
     __ JumpIfSmi(reg, false_label);
     // Check for undetectable objects by looking in the bit field in
@@ -2092,14 +2116,14 @@
   __ movq(result, FieldOperand(input, JSObject::kElementsOffset));
   if (FLAG_debug_code) {
     NearLabel done;
-    __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
-           Factory::fixed_array_map());
+    __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+                   Heap::kFixedArrayMapRootIndex);
     __ j(equal, &done);
-    __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
-           Factory::external_pixel_array_map());
+    __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+                   Heap::kExternalPixelArrayMapRootIndex);
     __ j(equal, &done);
-    __ Cmp(FieldOperand(result, HeapObject::kMapOffset),
-           Factory::fixed_cow_array_map());
+    __ CompareRoot(FieldOperand(result, HeapObject::kMapOffset),
+                   Heap::kFixedCOWArrayMapRootIndex);
     __ Check(equal, "Check for fast elements failed.");
     __ bind(&done);
   }
@@ -2146,7 +2170,7 @@
                                FixedArray::kHeaderSize));
 
   // Check for the hole value.
-  __ Cmp(result, Factory::the_hole_value());
+  __ CompareRoot(result, Heap::kTheHoleValueRootIndex);
   DeoptimizeIf(equal, instr->environment());
 }
 
@@ -2978,6 +3002,56 @@
 }
 
 
+void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
+  class DeferredStringCharFromCode: public LDeferredCode {
+   public:
+    DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
+        : LDeferredCode(codegen), instr_(instr) { }
+    virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
+   private:
+    LStringCharFromCode* instr_;
+  };
+
+  DeferredStringCharFromCode* deferred =
+      new DeferredStringCharFromCode(this, instr);
+
+  ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+  ASSERT(!char_code.is(result));
+
+  __ cmpl(char_code, Immediate(String::kMaxAsciiCharCode));
+  __ j(above, deferred->entry());
+  __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
+  __ movq(result, FieldOperand(result,
+                               char_code, times_pointer_size,
+                               FixedArray::kHeaderSize));
+  __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
+  __ j(equal, deferred->entry());
+  __ bind(deferred->exit());
+}
+
+
+void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
+  Register char_code = ToRegister(instr->char_code());
+  Register result = ToRegister(instr->result());
+
+  // TODO(3095996): Get rid of this. For now, we need to make the
+  // result register contain a valid pointer because it is already
+  // contained in the register pointer map.
+  __ Set(result, 0);
+
+  __ PushSafepointRegisters();
+  __ Integer32ToSmi(char_code, char_code);
+  __ push(char_code);
+  __ CallRuntimeSaveDoubles(Runtime::kCharFromCode);
+  RecordSafepointWithRegisters(
+      instr->pointer_map(), 1, Safepoint::kNoDeoptimizationIndex);
+  __ StoreToSafepointRegisterSlot(result, rax);
+  __ PopSafepointRegisters();
+}
+
+
 void LCodeGen::DoStringLength(LStringLength* instr) {
   Register string = ToRegister(instr->string());
   Register result = ToRegister(instr->result());
@@ -3434,7 +3508,9 @@
   } else {
     __ push(rsi);
     __ Push(shared_info);
-    __ Push(pretenure ? Factory::true_value() : Factory::false_value());
+    __ PushRoot(pretenure ?
+                Heap::kTrueValueRootIndex :
+                Heap::kFalseValueRootIndex);
     CallRuntime(Runtime::kNewClosure, 3, instr);
   }
 }
@@ -3517,8 +3593,9 @@
   Condition final_branch_condition = no_condition;
   if (type_name->Equals(Heap::number_symbol())) {
     __ JumpIfSmi(input, true_label);
-    __ Cmp(FieldOperand(input, HeapObject::kMapOffset),
-           Factory::heap_number_map());
+    __ CompareRoot(FieldOperand(input, HeapObject::kMapOffset),
+                   Heap::kHeapNumberMapRootIndex);
+
     final_branch_condition = equal;
 
   } else if (type_name->Equals(Heap::string_symbol())) {
diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h
index 5592456..6b00335 100644
--- a/src/x64/lithium-codegen-x64.h
+++ b/src/x64/lithium-codegen-x64.h
@@ -92,6 +92,7 @@
   void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
   void DoDeferredStackCheck(LGoto* instr);
   void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
+  void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
   void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
                                         Label* map_check);
 
diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc
index e387cee..99a7ace 100644
--- a/src/x64/lithium-x64.cc
+++ b/src/x64/lithium-x64.cc
@@ -1347,13 +1347,23 @@
   if (instr->representation().IsInteger32()) {
     ASSERT(instr->left()->representation().IsInteger32());
     ASSERT(instr->right()->representation().IsInteger32());
-    // The temporary operand is necessary to ensure that right is not allocated
-    // into edx.
-    LOperand* temp = FixedTemp(rdx);
-    LOperand* value = UseFixed(instr->left(), rax);
-    LOperand* divisor = UseRegister(instr->right());
-    LModI* mod = new LModI(value, divisor, temp);
-    LInstruction* result = DefineFixed(mod, rdx);
+
+    LInstruction* result;
+    if (instr->HasPowerOf2Divisor()) {
+      ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
+      LOperand* value = UseRegisterAtStart(instr->left());
+      LModI* mod = new LModI(value, UseOrConstant(instr->right()), NULL);
+      result = DefineSameAsFirst(mod);
+    } else {
+      // The temporary operand is necessary to ensure that right is not
+      // allocated into edx.
+      LOperand* temp = FixedTemp(rdx);
+      LOperand* value = UseFixed(instr->left(), rax);
+      LOperand* divisor = UseRegister(instr->right());
+      LModI* mod = new LModI(value, divisor, temp);
+      result = DefineFixed(mod, rdx);
+    }
+
     return (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
             instr->CheckFlag(HValue::kCanBeDivByZero))
         ? AssignEnvironment(result)
@@ -1791,7 +1801,6 @@
 
 LInstruction* LChunkBuilder::DoLoadPixelArrayElement(
     HLoadPixelArrayElement* instr) {
-  Abort("Pixel array loads in generated code cause segfaults (danno)");
   ASSERT(instr->representation().IsInteger32());
   ASSERT(instr->key()->representation().IsInteger32());
   LOperand* external_pointer =
@@ -1833,7 +1842,6 @@
 
 LInstruction* LChunkBuilder::DoStorePixelArrayElement(
     HStorePixelArrayElement* instr) {
-  Abort("Pixel array stores in generated code sometimes segfaults (danno)");
   ASSERT(instr->value()->representation().IsInteger32());
   ASSERT(instr->external_pointer()->representation().IsExternal());
   ASSERT(instr->key()->representation().IsInteger32());
@@ -1897,6 +1905,13 @@
 }
 
 
+LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
+  LOperand* char_code = UseRegister(instr->value());
+  LStringCharFromCode* result = new LStringCharFromCode(char_code);
+  return AssignPointerMap(DefineAsRegister(result));
+}
+
+
 LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
   LOperand* string = UseRegisterAtStart(instr->value());
   return DefineAsRegister(new LStringLength(string));
diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h
index a5a2046..926924b 100644
--- a/src/x64/lithium-x64.h
+++ b/src/x64/lithium-x64.h
@@ -149,6 +149,7 @@
   V(StoreNamedGeneric)                          \
   V(StorePixelArrayElement)                     \
   V(StringCharCodeAt)                           \
+  V(StringCharFromCode)                         \
   V(StringLength)                               \
   V(SubI)                                       \
   V(TaggedToI)                                  \
@@ -1631,6 +1632,19 @@
 };
 
 
+class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
+ public:
+  explicit LStringCharFromCode(LOperand* char_code) {
+    inputs_[0] = char_code;
+  }
+
+  DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
+  DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
+
+  LOperand* char_code() { return inputs_[0]; }
+};
+
+
 class LStringLength: public LTemplateInstruction<1, 1, 0> {
  public:
   explicit LStringLength(LOperand* string) {
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 45fbd41..f4874ad 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -555,7 +555,7 @@
   // Allocate HandleScope in callee-save registers.
   Register prev_next_address_reg = r14;
   Register prev_limit_reg = rbx;
-  Register base_reg = r12;
+  Register base_reg = r15;
   movq(base_reg, next_address);
   movq(prev_next_address_reg, Operand(base_reg, kNextOffset));
   movq(prev_limit_reg, Operand(base_reg, kLimitOffset));
@@ -721,11 +721,11 @@
       bind(&ok);
     }
   }
-  if (source->value() == 0) {
+  int value = source->value();
+  if (value == 0) {
     xorl(dst, dst);
     return;
   }
-  int value = source->value();
   bool negative = value < 0;
   unsigned int uvalue = negative ? -value : value;
 
@@ -1474,10 +1474,10 @@
   push(r9);
   // r10 is kScratchRegister.
   push(r11);
-  push(r12);
+  // r12 is kSmiConstantRegister.
   // r13 is kRootRegister.
   push(r14);
-  // r15 is kSmiConstantRegister
+  push(r15);
   STATIC_ASSERT(11 == kNumSafepointSavedRegisters);
   // Use lea for symmetry with Popad.
   int sp_delta =
@@ -1491,8 +1491,8 @@
   int sp_delta =
       (kNumSafepointRegisters - kNumSafepointSavedRegisters) * kPointerSize;
   lea(rsp, Operand(rsp, sp_delta));
+  pop(r15);
   pop(r14);
-  pop(r12);
   pop(r11);
   pop(r9);
   pop(r8);
@@ -1511,7 +1511,7 @@
 
 
 // Order general registers are pushed by Pushad:
-// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14.
+// rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
 int MacroAssembler::kSafepointPushRegisterIndices[Register::kNumRegisters] = {
     0,
     1,
@@ -1525,10 +1525,10 @@
     7,
     -1,
     8,
-    9,
     -1,
-    10,
-    -1
+    -1,
+    9,
+    10
 };
 
 
@@ -2077,10 +2077,10 @@
 void MacroAssembler::EnterExitFrame(int arg_stack_space, bool save_doubles) {
   EnterExitFramePrologue(true);
 
-  // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
+  // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
   // so it must be retained across the C-call.
   int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
-  lea(r12, Operand(rbp, r14, times_pointer_size, offset));
+  lea(r15, Operand(rbp, r14, times_pointer_size, offset));
 
   EnterExitFrameEpilogue(arg_stack_space, save_doubles);
 }
@@ -2094,7 +2094,7 @@
 
 void MacroAssembler::LeaveExitFrame(bool save_doubles) {
   // Registers:
-  // r12 : argv
+  // r15 : argv
   if (save_doubles) {
     int offset = -2 * kPointerSize;
     for (int i = 0; i < XMMRegister::kNumAllocatableRegisters; i++) {
@@ -2108,7 +2108,7 @@
 
   // Drop everything up to and including the arguments and the receiver
   // from the caller stack.
-  lea(rsp, Operand(r12, 1 * kPointerSize));
+  lea(rsp, Operand(r15, 1 * kPointerSize));
 
   // Push the return address to get ready to return.
   push(rcx);
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index 5908cef..28f814b 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -48,7 +48,7 @@
 // a spare register). The register isn't callee save, and not used by the
 // function calling convention.
 static const Register kScratchRegister = { 10 };      // r10.
-static const Register kSmiConstantRegister = { 15 };  // r15 (callee save).
+static const Register kSmiConstantRegister = { 12 };  // r12 (callee save).
 static const Register kRootRegister = { 13 };         // r13 (callee save).
 // Value of smi in kSmiConstantRegister.
 static const int kSmiConstantRegisterValue = 1;
@@ -941,7 +941,7 @@
 
   // Calls an API function. Allocates HandleScope, extracts
   // returned value from handle and propagates exceptions.
-  // Clobbers r12, r14, rbx and caller-save registers. Restores context.
+  // Clobbers r14, r15, rbx and caller-save registers. Restores context.
   // On return removes stack_space * kPointerSize (GCed).
   MUST_USE_RESULT MaybeObject* TryCallApiFunctionAndReturn(
       ApiFunction* function, int stack_space);
@@ -1013,7 +1013,7 @@
 
  private:
   // Order general registers are pushed by Pushad.
-  // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r12, r14.
+  // rax, rcx, rdx, rbx, rsi, rdi, r8, r9, r11, r14, r15.
   static int kSafepointPushRegisterIndices[Register::kNumRegisters];
   static const int kNumSafepointSavedRegisters = 11;
 
diff --git a/src/x64/register-allocator-x64-inl.h b/src/x64/register-allocator-x64-inl.h
index c6bea3a..5df3d54 100644
--- a/src/x64/register-allocator-x64-inl.h
+++ b/src/x64/register-allocator-x64-inl.h
@@ -60,10 +60,10 @@
     6,   // r9
     -1,  // r10  Scratch register.
     8,   // r11
-    9,   // r12
+    -1,  // r12  Smi constant.
     -1,  // r13  Roots array.  This is callee saved.
     7,   // r14
-    -1   // r15  Smi constant register.
+    9    // r15
   };
   return kNumbers[reg.code()];
 }
@@ -72,7 +72,7 @@
 Register RegisterAllocator::ToRegister(int num) {
   ASSERT(num >= 0 && num < kNumRegisters);
   const Register kRegisters[] =
-      { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r12 };
+      { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r15 };
   return kRegisters[num];
 }
 
diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc
index 5d292df..1554bda 100644
--- a/test/cctest/test-assembler-x64.cc
+++ b/test/cctest/test-assembler-x64.cc
@@ -50,8 +50,8 @@
 using v8::internal::rsp;
 using v8::internal::r8;
 using v8::internal::r9;
-using v8::internal::r12;
 using v8::internal::r13;
+using v8::internal::r15;
 using v8::internal::times_1;
 
 using v8::internal::FUNCTION_CAST;
@@ -317,7 +317,7 @@
 
     CHECK(Operand(rsp, offset).AddressUsesRegister(rsp));
     CHECK(!Operand(rsp, offset).AddressUsesRegister(rax));
-    CHECK(!Operand(rsp, offset).AddressUsesRegister(r12));
+    CHECK(!Operand(rsp, offset).AddressUsesRegister(r15));
 
     CHECK(Operand(rbp, offset).AddressUsesRegister(rbp));
     CHECK(!Operand(rbp, offset).AddressUsesRegister(rax));
@@ -333,7 +333,7 @@
     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rsp));
     CHECK(Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rbp));
     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(rax));
-    CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r12));
+    CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r15));
     CHECK(!Operand(rsp, rbp, times_1, offset).AddressUsesRegister(r13));
   }
 }
diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc
index ad242fe..f1e9fcd 100644
--- a/test/cctest/test-heap-profiler.cc
+++ b/test/cctest/test-heap-profiler.cc
@@ -1258,4 +1258,141 @@
   CHECK_GT(control.total(), 0);
 }
 
+
+namespace {
+
+class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
+ public:
+  TestRetainedObjectInfo(int hash,
+                         const char* label,
+                         intptr_t element_count = -1,
+                         intptr_t size = -1)
+      : disposed_(false),
+        hash_(hash),
+        label_(label),
+        element_count_(element_count),
+        size_(size) {
+    instances.Add(this);
+  }
+  virtual ~TestRetainedObjectInfo() {}
+  virtual void Dispose() {
+    CHECK(!disposed_);
+    disposed_ = true;
+  }
+  virtual bool IsEquivalent(RetainedObjectInfo* other) {
+    return GetHash() == other->GetHash();
+  }
+  virtual intptr_t GetHash() { return hash_; }
+  virtual const char* GetLabel() { return label_; }
+  virtual intptr_t GetElementCount() { return element_count_; }
+  virtual intptr_t GetSizeInBytes() { return size_; }
+  bool disposed() { return disposed_; }
+
+  static v8::RetainedObjectInfo* WrapperInfoCallback(
+      uint16_t class_id, v8::Handle<v8::Value> wrapper) {
+    if (class_id == 1) {
+      if (wrapper->IsString()) {
+        v8::String::AsciiValue ascii(wrapper);
+        if (strcmp(*ascii, "AAA") == 0)
+          return new TestRetainedObjectInfo(1, "aaa", 100);
+        else if (strcmp(*ascii, "BBB") == 0)
+          return new TestRetainedObjectInfo(1, "aaa", 100);
+      }
+    } else if (class_id == 2) {
+      if (wrapper->IsString()) {
+        v8::String::AsciiValue ascii(wrapper);
+        if (strcmp(*ascii, "CCC") == 0)
+          return new TestRetainedObjectInfo(2, "ccc");
+      }
+    }
+    CHECK(false);
+    return NULL;
+  }
+
+  static i::List<TestRetainedObjectInfo*> instances;
+
+ private:
+  bool disposed_;
+  int category_;
+  int hash_;
+  const char* label_;
+  intptr_t element_count_;
+  intptr_t size_;
+};
+
+
+i::List<TestRetainedObjectInfo*> TestRetainedObjectInfo::instances;
+}
+
+
+static const v8::HeapGraphNode* GetNode(const v8::HeapGraphNode* parent,
+                                        v8::HeapGraphNode::Type type,
+                                        const char* name) {
+  for (int i = 0, count = parent->GetChildrenCount(); i < count; ++i) {
+    const v8::HeapGraphNode* node = parent->GetChild(i)->GetToNode();
+    if (node->GetType() == type && strcmp(name,
+               const_cast<i::HeapEntry*>(
+                   reinterpret_cast<const i::HeapEntry*>(node))->name()) == 0) {
+      return node;
+    }
+  }
+  return NULL;
+}
+
+
+TEST(HeapSnapshotRetainedObjectInfo) {
+  v8::HandleScope scope;
+  LocalContext env;
+
+  v8::HeapProfiler::DefineWrapperClass(
+      1, TestRetainedObjectInfo::WrapperInfoCallback);
+  v8::HeapProfiler::DefineWrapperClass(
+      2, TestRetainedObjectInfo::WrapperInfoCallback);
+  v8::Persistent<v8::String> p_AAA =
+      v8::Persistent<v8::String>::New(v8_str("AAA"));
+  p_AAA.SetWrapperClassId(1);
+  v8::Persistent<v8::String> p_BBB =
+      v8::Persistent<v8::String>::New(v8_str("BBB"));
+  p_BBB.SetWrapperClassId(1);
+  v8::Persistent<v8::String> p_CCC =
+      v8::Persistent<v8::String>::New(v8_str("CCC"));
+  p_CCC.SetWrapperClassId(2);
+  CHECK_EQ(0, TestRetainedObjectInfo::instances.length());
+  const v8::HeapSnapshot* snapshot =
+      v8::HeapProfiler::TakeSnapshot(v8::String::New("retained"));
+
+  CHECK_EQ(3, TestRetainedObjectInfo::instances.length());
+  for (int i = 0; i < TestRetainedObjectInfo::instances.length(); ++i) {
+    CHECK(TestRetainedObjectInfo::instances[i]->disposed());
+    delete TestRetainedObjectInfo::instances[i];
+  }
+
+  const v8::HeapGraphNode* natives = GetNode(
+      snapshot->GetRoot(), v8::HeapGraphNode::kObject, "(Native objects)");
+  CHECK_NE(NULL, natives);
+  CHECK_EQ(2, natives->GetChildrenCount());
+  const v8::HeapGraphNode* aaa = GetNode(
+      natives, v8::HeapGraphNode::kNative, "aaa / 100 entries");
+  CHECK_NE(NULL, aaa);
+  const v8::HeapGraphNode* ccc = GetNode(
+      natives, v8::HeapGraphNode::kNative, "ccc");
+  CHECK_NE(NULL, ccc);
+
+  CHECK_EQ(2, aaa->GetChildrenCount());
+  const v8::HeapGraphNode* n_AAA = GetNode(
+      aaa, v8::HeapGraphNode::kString, "AAA");
+  CHECK_NE(NULL, n_AAA);
+  const v8::HeapGraphNode* n_BBB = GetNode(
+      aaa, v8::HeapGraphNode::kString, "BBB");
+  CHECK_NE(NULL, n_BBB);
+  CHECK_EQ(1, ccc->GetChildrenCount());
+  const v8::HeapGraphNode* n_CCC = GetNode(
+      ccc, v8::HeapGraphNode::kString, "CCC");
+  CHECK_NE(NULL, n_CCC);
+
+  CHECK_EQ(aaa, GetProperty(n_AAA, v8::HeapGraphEdge::kInternal, "Native"));
+  CHECK_EQ(aaa, GetProperty(n_BBB, v8::HeapGraphEdge::kInternal, "Native"));
+  CHECK_EQ(ccc, GetProperty(n_CCC, v8::HeapGraphEdge::kInternal, "Native"));
+}
+
 #endif  // ENABLE_LOGGING_AND_PROFILING
diff --git a/test/cctest/test-macro-assembler-x64.cc b/test/cctest/test-macro-assembler-x64.cc
index 9b1fc46..8a7cf41 100755
--- a/test/cctest/test-macro-assembler-x64.cc
+++ b/test/cctest/test-macro-assembler-x64.cc
@@ -57,9 +57,9 @@
 using v8::internal::r8;
 using v8::internal::r9;
 using v8::internal::r11;
-using v8::internal::r12;
 using v8::internal::r13;
 using v8::internal::r14;
+using v8::internal::r15;
 using v8::internal::times_pointer_size;
 using v8::internal::FUNCTION_CAST;
 using v8::internal::CodeDesc;
@@ -1120,30 +1120,30 @@
   if (!fraction && !overflow && !negative_zero && !division_by_zero) {
     // Division succeeds
     __ movq(rcx, r11);
-    __ movq(r12, Immediate(id));
+    __ movq(r15, Immediate(id));
     int result = x / y;
     __ Move(r8, Smi::FromInt(result));
     __ SmiDiv(r9, rcx, r14, exit);
     // Might have destroyed rcx and r14.
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(r9, r8);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ movq(rcx, r11);
     __ Move(r14, Smi::FromInt(y));
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiDiv(rcx, rcx, r14, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r8);
     __ j(not_equal, exit);
   } else {
     // Division fails.
-    __ movq(r12, Immediate(id + 8));
+    __ movq(r15, Immediate(id + 8));
 
     Label fail_ok, fail_ok2;
     __ movq(rcx, r11);
@@ -1151,16 +1151,16 @@
     __ jmp(exit);
     __ bind(&fail_ok);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiDiv(rcx, rcx, r14, &fail_ok2);
     __ jmp(exit);
     __ bind(&fail_ok2);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
   }
@@ -1184,7 +1184,7 @@
   Label exit;
 
   __ push(r14);
-  __ push(r12);
+  __ push(r15);
   TestSmiDiv(masm, &exit, 0x10, 1, 1);
   TestSmiDiv(masm, &exit, 0x20, 1, 0);
   TestSmiDiv(masm, &exit, 0x30, -1, 0);
@@ -1206,10 +1206,10 @@
   TestSmiDiv(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
   TestSmiDiv(masm, &exit, 0x140, Smi::kMinValue, -1);
 
-  __ xor_(r12, r12);  // Success.
+  __ xor_(r15, r15);  // Success.
   __ bind(&exit);
-  __ movq(rax, r12);
-  __ pop(r12);
+  __ movq(rax, r15);
+  __ pop(r15);
   __ pop(r14);
   ExitCode(masm);
   __ ret(0);
@@ -1232,44 +1232,44 @@
   __ Move(r14, Smi::FromInt(y));
   if (!division_overflow && !negative_zero && !division_by_zero) {
     // Modulo succeeds
-    __ movq(r12, Immediate(id));
+    __ movq(r15, Immediate(id));
     int result = x % y;
     __ Move(r8, Smi::FromInt(result));
     __ SmiMod(r9, rcx, r14, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(r9, r8);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiMod(rcx, rcx, r14, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r8);
     __ j(not_equal, exit);
   } else {
     // Modulo fails.
-    __ movq(r12, Immediate(id + 8));
+    __ movq(r15, Immediate(id + 8));
 
     Label fail_ok, fail_ok2;
     __ SmiMod(r9, rcx, r14, &fail_ok);
     __ jmp(exit);
     __ bind(&fail_ok);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiMod(rcx, rcx, r14, &fail_ok2);
     __ jmp(exit);
     __ bind(&fail_ok2);
 
-    __ incq(r12);
+    __ incq(r15);
     __ SmiCompare(rcx, r11);
     __ j(not_equal, exit);
   }
@@ -1293,7 +1293,7 @@
   Label exit;
 
   __ push(r14);
-  __ push(r12);
+  __ push(r15);
   TestSmiMod(masm, &exit, 0x10, 1, 1);
   TestSmiMod(masm, &exit, 0x20, 1, 0);
   TestSmiMod(masm, &exit, 0x30, -1, 0);
@@ -1315,10 +1315,10 @@
   TestSmiMod(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
   TestSmiMod(masm, &exit, 0x140, Smi::kMinValue, -1);
 
-  __ xor_(r12, r12);  // Success.
+  __ xor_(r15, r15);  // Success.
   __ bind(&exit);
-  __ movq(rax, r12);
-  __ pop(r12);
+  __ movq(rax, r15);
+  __ pop(r15);
   __ pop(r14);
   ExitCode(masm);
   __ ret(0);
@@ -2150,7 +2150,7 @@
   __ push(Immediate(0x108));
   __ push(Immediate(0x109));  // <-- rsp
   // rbp = rsp[9]
-  // r12 = rsp[3]
+  // r15 = rsp[3]
   // rbx = rsp[5]
   // r13 = rsp[7]
   __ lea(r14, Operand(rsp, 3 * kPointerSize));
diff --git a/test/cctest/test-mark-compact.cc b/test/cctest/test-mark-compact.cc
index 3e3175e..54c12eb 100644
--- a/test/cctest/test-mark-compact.cc
+++ b/test/cctest/test-mark-compact.cc
@@ -339,8 +339,8 @@
   {
     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
-    GlobalHandles::AddGroup(g1_objects, 2);
-    GlobalHandles::AddGroup(g2_objects, 2);
+    GlobalHandles::AddGroup(g1_objects, 2, NULL);
+    GlobalHandles::AddGroup(g2_objects, 2, NULL);
   }
   // Do a full GC
   Heap::CollectGarbage(OLD_POINTER_SPACE);
@@ -357,8 +357,8 @@
   {
     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
-    GlobalHandles::AddGroup(g1_objects, 2);
-    GlobalHandles::AddGroup(g2_objects, 2);
+    GlobalHandles::AddGroup(g1_objects, 2, NULL);
+    GlobalHandles::AddGroup(g2_objects, 2, NULL);
   }
 
   Heap::CollectGarbage(OLD_POINTER_SPACE);
diff --git a/test/mjsunit/regress/regress-1233.js b/test/mjsunit/regress/regress-1233.js
new file mode 100644
index 0000000..a09b715
--- /dev/null
+++ b/test/mjsunit/regress/regress-1233.js
@@ -0,0 +1,47 @@
+// Copyright 2011 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.
+
+// Test that Object.freeze and Object.getOwnPropertyDescriptor do not
+// call toString or valueOf on members of the object.
+
+// See http://code.google.com/p/v8/issues/detail?id=1233.
+
+
+var delicate = new Object();
+delicate.toString = function(){ throw Error("toString"); };
+delicate.valueOf = function(){ throw Error("valueOf"); };
+
+var x = { foo: delicate };
+
+var status = "fail";
+try {
+  Object.getOwnPropertyDescriptor(x, "foo");
+  Object.freeze(x);
+  status = "succeed";
+} catch (e) {}
+
+assertEquals("succeed", status);