Version 3.30.33 (based on 6bee6dcebc3033d4665a8069020302ce5018522d)

`1..isPrototypeOf.call(null)` should return false, not throw TypeError (issue 3483).

Refactor ObjectGetOwnPropertyKeys to accept bitmask rather than boolean (issue 3549).

Add debug mirror support for ES6 Map/Set iterators (Chromium issue 427868).

Performance and stability improvements on all platforms.

Cr-Commit-Position: refs/heads/candidates@{#25122}
git-svn-id: https://v8.googlecode.com/svn/trunk@25122 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/BUILD.gn b/BUILD.gn
index 0b1b06e..1758ee9 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -498,6 +498,7 @@
     "src/compiler/control-builders.h",
     "src/compiler/control-reducer.cc",
     "src/compiler/control-reducer.h",
+    "src/compiler/diamond.h",
     "src/compiler/frame.h",
     "src/compiler/gap-resolver.cc",
     "src/compiler/gap-resolver.h",
@@ -571,6 +572,8 @@
     "src/compiler/raw-machine-assembler.h",
     "src/compiler/register-allocator.cc",
     "src/compiler/register-allocator.h",
+    "src/compiler/register-configuration.cc",
+    "src/compiler/register-configuration.h",
     "src/compiler/representation-change.h",
     "src/compiler/schedule.cc",
     "src/compiler/schedule.h",
diff --git a/ChangeLog b/ChangeLog
index 6e758a3..1e29853 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2014-11-05: Version 3.30.33
+
+        `1..isPrototypeOf.call(null)` should return false, not throw TypeError
+        (issue 3483).
+
+        Refactor ObjectGetOwnPropertyKeys to accept bitmask rather than boolean
+        (issue 3549).
+
+        Add debug mirror support for ES6 Map/Set iterators (Chromium issue
+        427868).
+
+        Performance and stability improvements on all platforms.
+
+
 2014-11-04: Version 3.30.30
 
         Performance and stability improvements on all platforms.
diff --git a/include/v8.h b/include/v8.h
index 37e0ddc..d5433a6 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -1626,6 +1626,18 @@
   bool IsSet() const;
 
   /**
+   * Returns true if this value is a Map Iterator.
+   * This is an experimental feature.
+   */
+  bool IsMapIterator() const;
+
+  /**
+   * Returns true if this value is a Set Iterator.
+   * This is an experimental feature.
+   */
+  bool IsSetIterator() const;
+
+  /**
    * Returns true if this value is a WeakMap.
    * This is an experimental feature.
    */
diff --git a/src/api.cc b/src/api.cc
index c9231a3..2c8009e 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -2571,6 +2571,16 @@
 }
 
 
+bool Value::IsMapIterator() const {
+  return Utils::OpenHandle(this)->IsJSMapIterator();
+}
+
+
+bool Value::IsSetIterator() const {
+  return Utils::OpenHandle(this)->IsJSSetIterator();
+}
+
+
 Local<String> Value::ToString(Isolate* v8_isolate) const {
   i::Handle<i::Object> obj = Utils::OpenHandle(this);
   i::Handle<i::Object> str;
diff --git a/src/arm/interface-descriptors-arm.cc b/src/arm/interface-descriptors-arm.cc
index 1b397ff..6e77ee4 100644
--- a/src/arm/interface-descriptors-arm.cc
+++ b/src/arm/interface-descriptors-arm.cc
@@ -152,6 +152,15 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // register state
+  // cp -- context
+  Register registers[] = {cp};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // register state
diff --git a/src/arm64/interface-descriptors-arm64.cc b/src/arm64/interface-descriptors-arm64.cc
index 705e61b..57eebcc 100644
--- a/src/arm64/interface-descriptors-arm64.cc
+++ b/src/arm64/interface-descriptors-arm64.cc
@@ -185,6 +185,14 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // cp: context
+  Register registers[] = {cp};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // cp: context
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 8fd762c..264d1c6 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -215,11 +215,9 @@
   void InstallNativeFunctions_##id();             \
   void InitializeGlobal_##id();
 
-  SHIPPING_FEATURES(DECLARE_FEATURE_INITIALIZATION)
-  HARMONY_FEATURES(DECLARE_FEATURE_INITIALIZATION)
-  STAGED_FEATURES(DECLARE_FEATURE_INITIALIZATION)
-
-  DECLARE_FEATURE_INITIALIZATION(harmony_proxies, "")
+  HARMONY_INPROGRESS(DECLARE_FEATURE_INITIALIZATION)
+  HARMONY_STAGED(DECLARE_FEATURE_INITIALIZATION)
+  HARMONY_SHIPPING(DECLARE_FEATURE_INITIALIZATION)
 #undef DECLARE_FEATURE_INITIALIZATION
 
   Handle<JSFunction> InstallInternalArray(Handle<JSBuiltinsObject> builtins,
@@ -1344,7 +1342,7 @@
 
 #define FEATURE_INITIALIZE_GLOBAL(id, descr) InitializeGlobal_##id();
 
-  SHIPPING_FEATURES(FEATURE_INITIALIZE_GLOBAL)
+  HARMONY_SHIPPING(FEATURE_INITIALIZE_GLOBAL)
 #undef FEATURE_INITIALIZE_GLOBAL
 
   // Initialize the embedder data slot.
@@ -1379,8 +1377,8 @@
 void Genesis::InitializeExperimentalGlobal() {
 #define FEATURE_INITIALIZE_GLOBAL(id, descr) InitializeGlobal_##id();
 
-  HARMONY_FEATURES(FEATURE_INITIALIZE_GLOBAL)
-  STAGED_FEATURES(FEATURE_INITIALIZE_GLOBAL)
+  HARMONY_INPROGRESS(FEATURE_INITIALIZE_GLOBAL)
+  HARMONY_STAGED(FEATURE_INITIALIZE_GLOBAL)
 #undef FEATURE_INITIALIZE_GLOBAL
 }
 
@@ -1565,8 +1563,7 @@
   INSTALL_NATIVE(JSFunction, "ArrayValues", array_values_iterator);
 
 #define INSTALL_NATIVE_FUNCTIONS_FOR(id, descr) InstallNativeFunctions_##id();
-
-  SHIPPING_FEATURES(INSTALL_NATIVE_FUNCTIONS_FOR)
+  HARMONY_SHIPPING(INSTALL_NATIVE_FUNCTIONS_FOR)
 #undef INSTALL_NATIVE_FUNCTIONS_FOR
 }
 
@@ -1580,10 +1577,8 @@
   }
 
 #define INSTALL_NATIVE_FUNCTIONS_FOR(id, descr) InstallNativeFunctions_##id();
-
-  HARMONY_FEATURES(INSTALL_NATIVE_FUNCTIONS_FOR)
-  STAGED_FEATURES(INSTALL_NATIVE_FUNCTIONS_FOR)
-  INSTALL_NATIVE_FUNCTIONS_FOR(harmony_proxies, "")
+  HARMONY_INPROGRESS(INSTALL_NATIVE_FUNCTIONS_FOR)
+  HARMONY_STAGED(INSTALL_NATIVE_FUNCTIONS_FOR)
 #undef INSTALL_NATIVE_FUNCTIONS_FOR
 }
 
@@ -1626,6 +1621,7 @@
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_arrow_functions)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_numeric_literals)
 EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tostring)
+EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_proxies)
 
 void Genesis::InitializeGlobal_harmony_regexps() {
   Handle<JSObject> builtins(native_context()->builtins());
@@ -2180,16 +2176,14 @@
       }                                                              \
     }                                                                \
   }
-    INSTALL_EXPERIMENTAL_NATIVES(harmony_proxies, "");
     // Iterate over flags that are not enabled by default.
-    HARMONY_FEATURES(INSTALL_EXPERIMENTAL_NATIVES);
-    STAGED_FEATURES(INSTALL_EXPERIMENTAL_NATIVES);
+    HARMONY_INPROGRESS(INSTALL_EXPERIMENTAL_NATIVES);
+    HARMONY_STAGED(INSTALL_EXPERIMENTAL_NATIVES);
 #undef INSTALL_EXPERIMENTAL_NATIVES
   }
 
 #define USE_NATIVES_FOR_FEATURE(id, descr) USE(id##_natives);
-
-  SHIPPING_FEATURES(USE_NATIVES_FOR_FEATURE)
+  HARMONY_SHIPPING(USE_NATIVES_FOR_FEATURE)
 #undef USE_NATIVES_FOR_FEATURE
 
   InstallExperimentalNativeFunctions();
diff --git a/src/code-factory.cc b/src/code-factory.cc
index ae94fd5..e68d539 100644
--- a/src/code-factory.cc
+++ b/src/code-factory.cc
@@ -104,6 +104,13 @@
 
 
 // static
+Callable CodeFactory::AllocateHeapNumber(Isolate* isolate) {
+  AllocateHeapNumberStub stub(isolate);
+  return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
+}
+
+
+// static
 Callable CodeFactory::CallFunction(Isolate* isolate, int argc,
                                    CallFunctionFlags flags) {
   CallFunctionStub stub(isolate, argc, flags);
diff --git a/src/code-factory.h b/src/code-factory.h
index 2426cf0..f26bf2a 100644
--- a/src/code-factory.h
+++ b/src/code-factory.h
@@ -55,9 +55,13 @@
   static Callable StringAdd(Isolate* isolate, StringAddFlags flags,
                             PretenureFlag pretenure_flag);
 
+  static Callable AllocateHeapNumber(Isolate* isolate);
+
   static Callable CallFunction(Isolate* isolate, int argc,
                                CallFunctionFlags flags);
 };
-}
-}
+
+}  // namespace internal
+}  // namespace v8
+
 #endif  // V8_CODE_FACTORY_H_
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc
index e3cbe7d..5579e05 100644
--- a/src/code-stubs-hydrogen.cc
+++ b/src/code-stubs-hydrogen.cc
@@ -883,6 +883,22 @@
   return DoGenerateCode(this);
 }
 
+
+template <>
+HValue* CodeStubGraphBuilder<AllocateHeapNumberStub>::BuildCodeStub() {
+  HValue* result =
+      Add<HAllocate>(Add<HConstant>(HeapNumber::kSize), HType::HeapNumber(),
+                     NOT_TENURED, HEAP_NUMBER_TYPE);
+  AddStoreMapConstant(result, isolate()->factory()->heap_number_map());
+  return result;
+}
+
+
+Handle<Code> AllocateHeapNumberStub::GenerateCode() {
+  return DoGenerateCode(this);
+}
+
+
 HValue* CodeStubGraphBuilderBase::BuildArrayConstructor(
     ElementsKind kind,
     AllocationSiteOverrideMode override_mode,
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 9832650..552b7eb 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -697,6 +697,13 @@
 }
 
 
+void AllocateHeapNumberStub::InitializeDescriptor(
+    CodeStubDescriptor* descriptor) {
+  descriptor->Initialize(
+      Runtime::FunctionForId(Runtime::kAllocateHeapNumber)->entry);
+}
+
+
 void CompareNilICStub::InitializeDescriptor(CodeStubDescriptor* descriptor) {
   descriptor->Initialize(FUNCTION_ADDR(CompareNilIC_Miss));
   descriptor->SetMissHandler(
diff --git a/src/code-stubs.h b/src/code-stubs.h
index e8b7c83..6921883 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -51,6 +51,7 @@
   V(StubFailureTrampoline)                  \
   V(SubString)                              \
   /* HydrogenCodeStubs */                   \
+  V(AllocateHeapNumber)                     \
   V(ArrayNArgumentsConstructor)             \
   V(ArrayNoArgumentConstructor)             \
   V(ArraySingleArgumentConstructor)         \
@@ -2113,6 +2114,17 @@
 };
 
 
+class AllocateHeapNumberStub FINAL : public HydrogenCodeStub {
+ public:
+  explicit AllocateHeapNumberStub(Isolate* isolate)
+      : HydrogenCodeStub(isolate) {}
+
+ private:
+  DEFINE_CALL_INTERFACE_DESCRIPTOR(AllocateHeapNumber);
+  DEFINE_HYDROGEN_CODE_STUB(AllocateHeapNumber, HydrogenCodeStub);
+};
+
+
 class ArrayConstructorStubBase : public HydrogenCodeStub {
  public:
   ArrayConstructorStubBase(Isolate* isolate,
diff --git a/src/compiler/change-lowering.cc b/src/compiler/change-lowering.cc
index 0b4a399..cf18e62 100644
--- a/src/compiler/change-lowering.cc
+++ b/src/compiler/change-lowering.cc
@@ -4,6 +4,8 @@
 
 #include "src/compiler/change-lowering.h"
 
+#include "src/code-factory.h"
+#include "src/compiler/diamond.h"
 #include "src/compiler/js-graph.h"
 #include "src/compiler/linkage.h"
 #include "src/compiler/machine-operator.h"
@@ -66,19 +68,16 @@
 
 
 Node* ChangeLowering::AllocateHeapNumberWithValue(Node* value, Node* control) {
-  // The AllocateHeapNumber() runtime function does not use the context, so we
-  // can safely pass in Smi zero here.
+  // The AllocateHeapNumberStub does not use the context, so we can safely pass
+  // in Smi zero here.
+  Callable callable = CodeFactory::AllocateHeapNumber(isolate());
+  CallDescriptor* descriptor = linkage()->GetStubCallDescriptor(
+      callable.descriptor(), 0, CallDescriptor::kNoFlags);
+  Node* target = jsgraph()->HeapConstant(callable.code());
   Node* context = jsgraph()->ZeroConstant();
   Node* effect = graph()->NewNode(common()->ValueEffect(1), value);
-  const Runtime::Function* function =
-      Runtime::FunctionForId(Runtime::kAllocateHeapNumber);
-  DCHECK_EQ(0, function->nargs);
-  CallDescriptor* desc = linkage()->GetRuntimeCallDescriptor(
-      function->function_id, 0, Operator::kNoProperties);
-  Node* heap_number = graph()->NewNode(
-      common()->Call(desc), jsgraph()->CEntryStubConstant(),
-      jsgraph()->ExternalConstant(ExternalReference(function, isolate())),
-      jsgraph()->Int32Constant(function->nargs), context, effect, control);
+  Node* heap_number = graph()->NewNode(common()->Call(descriptor), target,
+                                       context, effect, control);
   Node* store = graph()->NewNode(
       machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
       heap_number, HeapNumberValueIndexConstant(), value, heap_number, control);
@@ -103,20 +102,11 @@
 
 
 Reduction ChangeLowering::ChangeBitToBool(Node* val, Node* control) {
-  Node* branch = graph()->NewNode(common()->Branch(), val, control);
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* true_value = jsgraph()->TrueConstant();
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-  Node* false_value = jsgraph()->FalseConstant();
-
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  Node* phi = graph()->NewNode(
-      common()->Phi(static_cast<MachineType>(kTypeBool | kRepTagged), 2),
-      true_value, false_value, merge);
-
-  return Replace(phi);
+  Diamond d(graph(), common(), val);
+  d.Chain(control);
+  MachineType machine_type = static_cast<MachineType>(kTypeBool | kRepTagged);
+  return Replace(d.Phi(machine_type, jsgraph()->TrueConstant(),
+                       jsgraph()->FalseConstant()));
 }
 
 
@@ -142,21 +132,12 @@
   Node* add = graph()->NewNode(machine()->Int32AddWithOverflow(), val, val);
   Node* ovf = graph()->NewNode(common()->Projection(1), add);
 
-  Node* branch =
-      graph()->NewNode(common()->Branch(BranchHint::kTrue), ovf, control);
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+  Diamond d(graph(), common(), ovf, BranchHint::kFalse);
+  d.Chain(control);
   Node* heap_number = AllocateHeapNumberWithValue(
-      graph()->NewNode(machine()->ChangeInt32ToFloat64(), val), if_true);
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+      graph()->NewNode(machine()->ChangeInt32ToFloat64(), val), d.if_true);
   Node* smi = graph()->NewNode(common()->Projection(0), add);
-
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  Node* phi = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), heap_number,
-                               smi, merge);
-
-  return Replace(phi);
+  return Replace(d.Phi(kMachAnyTagged, heap_number, smi));
 }
 
 
@@ -167,23 +148,17 @@
 
   Node* tag = graph()->NewNode(machine()->WordAnd(), val,
                                jsgraph()->IntPtrConstant(kSmiTagMask));
-  Node* branch = graph()->NewNode(common()->Branch(), tag, control);
 
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+  Diamond d(graph(), common(), tag, BranchHint::kFalse);
+  d.Chain(control);
   const Operator* op = (signedness == kSigned)
                            ? machine()->ChangeFloat64ToInt32()
                            : machine()->ChangeFloat64ToUint32();
-  Node* change = graph()->NewNode(op, LoadHeapNumberValue(val, if_true));
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+  Node* load = graph()->NewNode(op, LoadHeapNumberValue(val, d.if_true));
   Node* number = ChangeSmiToInt32(val);
 
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  Node* phi = graph()->NewNode(
-      common()->Phi((signedness == kSigned) ? kMachInt32 : kMachUint32, 2),
-      change, number, merge);
-
-  return Replace(phi);
+  return Replace(
+      d.Phi((signedness == kSigned) ? kMachInt32 : kMachUint32, load, number));
 }
 
 
@@ -193,20 +168,13 @@
 
   Node* tag = graph()->NewNode(machine()->WordAnd(), val,
                                jsgraph()->IntPtrConstant(kSmiTagMask));
-  Node* branch = graph()->NewNode(common()->Branch(), tag, control);
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* load = LoadHeapNumberValue(val, if_true);
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+  Diamond d(graph(), common(), tag, BranchHint::kFalse);
+  d.Chain(control);
+  Node* load = LoadHeapNumberValue(val, d.if_true);
   Node* number = graph()->NewNode(machine()->ChangeInt32ToFloat64(),
                                   ChangeSmiToInt32(val));
 
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  Node* phi =
-      graph()->NewNode(common()->Phi(kMachFloat64, 2), load, number, merge);
-
-  return Replace(phi);
+  return Replace(d.Phi(kMachFloat64, load, number));
 }
 
 
@@ -216,10 +184,8 @@
 
   Node* cmp = graph()->NewNode(machine()->Uint32LessThanOrEqual(), val,
                                SmiMaxValueConstant());
-  Node* branch =
-      graph()->NewNode(common()->Branch(BranchHint::kTrue), cmp, control);
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+  Diamond d(graph(), common(), cmp, BranchHint::kTrue);
+  d.Chain(control);
   Node* smi = graph()->NewNode(
       machine()->WordShl(),
       machine()->Is64()
@@ -227,15 +193,10 @@
           : val,
       SmiShiftBitsConstant());
 
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
   Node* heap_number = AllocateHeapNumberWithValue(
-      graph()->NewNode(machine()->ChangeUint32ToFloat64(), val), if_false);
+      graph()->NewNode(machine()->ChangeUint32ToFloat64(), val), d.if_false);
 
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  Node* phi = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), smi,
-                               heap_number, merge);
-
-  return Replace(phi);
+  return Replace(d.Phi(kMachAnyTagged, smi, heap_number));
 }
 
 
diff --git a/src/compiler/diamond.h b/src/compiler/diamond.h
new file mode 100644
index 0000000..6133cc5
--- /dev/null
+++ b/src/compiler/diamond.h
@@ -0,0 +1,85 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_DIAMOND_H_
+#define V8_COMPILER_DIAMOND_H_
+
+#include "src/v8.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/node.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// A helper to make it easier to build diamond-shaped control patterns.
+struct Diamond {
+  Graph* graph;
+  CommonOperatorBuilder* common;
+  Node* branch;
+  Node* if_true;
+  Node* if_false;
+  Node* merge;
+
+  Diamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
+          BranchHint hint = BranchHint::kNone) {
+    graph = g;
+    common = b;
+    branch = graph->NewNode(common->Branch(hint), cond, graph->start());
+    if_true = graph->NewNode(common->IfTrue(), branch);
+    if_false = graph->NewNode(common->IfFalse(), branch);
+    merge = graph->NewNode(common->Merge(2), if_true, if_false);
+  }
+
+  // Place {this} after {that} in control flow order.
+  void Chain(Diamond& that) { branch->ReplaceInput(1, that.merge); }
+
+  // Place {this} after {that} in control flow order.
+  void Chain(Node* that) { branch->ReplaceInput(1, that); }
+
+  // Nest {this} into either the if_true or if_false branch of {that}.
+  void Nest(Diamond& that, bool if_true) {
+    if (if_true) {
+      branch->ReplaceInput(1, that.if_true);
+      that.merge->ReplaceInput(0, merge);
+    } else {
+      branch->ReplaceInput(1, that.if_false);
+      that.merge->ReplaceInput(1, merge);
+    }
+  }
+
+  Node* Phi(MachineType machine_type, Node* tv, Node* fv) {
+    return graph->NewNode(common->Phi(machine_type, 2), tv, fv, merge);
+  }
+
+  Node* EffectPhi(Node* tv, Node* fv) {
+    return graph->NewNode(common->EffectPhi(2), tv, fv, merge);
+  }
+
+  void OverwriteWithPhi(Node* node, MachineType machine_type, Node* tv,
+                        Node* fv) {
+    DCHECK(node->InputCount() >= 3);
+    node->set_op(common->Phi(machine_type, 2));
+    node->ReplaceInput(0, tv);
+    node->ReplaceInput(1, fv);
+    node->ReplaceInput(2, merge);
+    node->TrimInputCount(3);
+  }
+
+  void OverwriteWithEffectPhi(Node* node, Node* te, Node* fe) {
+    DCHECK(node->InputCount() >= 3);
+    node->set_op(common->EffectPhi(2));
+    node->ReplaceInput(0, te);
+    node->ReplaceInput(1, fe);
+    node->ReplaceInput(2, merge);
+    node->TrimInputCount(3);
+  }
+};
+}
+}
+}  // namespace v8::internal::compiler
+
+#endif  // V8_COMPILER_DIAMOND_H_
diff --git a/src/compiler/graph-visualizer.cc b/src/compiler/graph-visualizer.cc
index 559da32..19f24cf 100644
--- a/src/compiler/graph-visualizer.cc
+++ b/src/compiler/graph-visualizer.cc
@@ -680,7 +680,9 @@
       for (int j = instruction_block->first_instruction_index();
            j <= instruction_block->last_instruction_index(); j++) {
         PrintIndent();
-        os_ << j << " " << *instructions->InstructionAt(j) << " <|@\n";
+        PrintableInstruction printable = {RegisterConfiguration::ArchDefault(),
+                                          instructions->InstructionAt(j)};
+        os_ << j << " " << printable << " <|@\n";
       }
     }
   }
diff --git a/src/compiler/instruction.cc b/src/compiler/instruction.cc
index 9705bea..76a6950 100644
--- a/src/compiler/instruction.cc
+++ b/src/compiler/instruction.cc
@@ -6,17 +6,15 @@
 #include "src/compiler/generic-node-inl.h"
 #include "src/compiler/graph.h"
 #include "src/compiler/instruction.h"
-#include "src/macro-assembler.h"
 
 namespace v8 {
 namespace internal {
 namespace compiler {
 
-STATIC_ASSERT(kMaxGeneralRegisters >= Register::kNumRegisters);
-STATIC_ASSERT(kMaxDoubleRegisters >= DoubleRegister::kMaxNumRegisters);
-
-
-std::ostream& operator<<(std::ostream& os, const InstructionOperand& op) {
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableInstructionOperand& printable) {
+  const InstructionOperand& op = *printable.op_;
+  const RegisterConfiguration* conf = printable.register_configuration_;
   switch (op.kind()) {
     case InstructionOperand::INVALID:
       return os << "(0)";
@@ -30,10 +28,10 @@
         case UnallocatedOperand::NONE:
           return os;
         case UnallocatedOperand::FIXED_REGISTER:
-          return os << "(=" << Register::AllocationIndexToString(
+          return os << "(=" << conf->general_register_name(
                                    unalloc->fixed_register_index()) << ")";
         case UnallocatedOperand::FIXED_DOUBLE_REGISTER:
-          return os << "(=" << DoubleRegister::AllocationIndexToString(
+          return os << "(=" << conf->double_register_name(
                                    unalloc->fixed_register_index()) << ")";
         case UnallocatedOperand::MUST_HAVE_REGISTER:
           return os << "(R)";
@@ -52,11 +50,9 @@
     case InstructionOperand::DOUBLE_STACK_SLOT:
       return os << "[double_stack:" << op.index() << "]";
     case InstructionOperand::REGISTER:
-      return os << "[" << Register::AllocationIndexToString(op.index())
-                << "|R]";
+      return os << "[" << conf->general_register_name(op.index()) << "|R]";
     case InstructionOperand::DOUBLE_REGISTER:
-      return os << "[" << DoubleRegister::AllocationIndexToString(op.index())
-                << "|R]";
+      return os << "[" << conf->double_register_name(op.index()) << "|R]";
   }
   UNREACHABLE();
   return os;
@@ -101,9 +97,17 @@
 }
 
 
-std::ostream& operator<<(std::ostream& os, const MoveOperands& mo) {
-  os << *mo.destination();
-  if (!mo.source()->Equals(mo.destination())) os << " = " << *mo.source();
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableMoveOperands& printable) {
+  const MoveOperands& mo = *printable.move_operands_;
+  PrintableInstructionOperand printable_op = {printable.register_configuration_,
+                                              mo.destination()};
+
+  os << printable_op;
+  if (!mo.source()->Equals(mo.destination())) {
+    printable_op.op_ = mo.source();
+    os << " = " << printable_op;
+  }
   return os << ";";
 }
 
@@ -116,14 +120,17 @@
 }
 
 
-std::ostream& operator<<(std::ostream& os, const ParallelMove& pm) {
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableParallelMove& printable) {
+  const ParallelMove& pm = *printable.parallel_move_;
   bool first = true;
   for (ZoneList<MoveOperands>::iterator move = pm.move_operands()->begin();
        move != pm.move_operands()->end(); ++move) {
     if (move->IsEliminated()) continue;
     if (!first) os << " ";
     first = false;
-    os << *move;
+    PrintableMoveOperands pmo = {printable.register_configuration_, move};
+    os << pmo;
   }
   return os;
 }
@@ -256,11 +263,16 @@
 }
 
 
-std::ostream& operator<<(std::ostream& os, const Instruction& instr) {
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableInstruction& printable) {
+  const Instruction& instr = *printable.instr_;
+  PrintableInstructionOperand printable_op = {printable.register_configuration_,
+                                              NULL};
   if (instr.OutputCount() > 1) os << "(";
   for (size_t i = 0; i < instr.OutputCount(); i++) {
     if (i > 0) os << ", ";
-    os << *instr.OutputAt(i);
+    printable_op.op_ = instr.OutputAt(i);
+    os << printable_op;
   }
 
   if (instr.OutputCount() > 1) os << ") = ";
@@ -272,7 +284,11 @@
     for (int i = GapInstruction::FIRST_INNER_POSITION;
          i <= GapInstruction::LAST_INNER_POSITION; i++) {
       os << "(";
-      if (gap->parallel_moves_[i] != NULL) os << *gap->parallel_moves_[i];
+      if (gap->parallel_moves_[i] != NULL) {
+        PrintableParallelMove ppm = {printable.register_configuration_,
+                                     gap->parallel_moves_[i]};
+        os << ppm;
+      }
       os << ") ";
     }
   } else if (instr.IsSourcePosition()) {
@@ -293,7 +309,8 @@
   }
   if (instr.InputCount() > 0) {
     for (size_t i = 0; i < instr.InputCount(); i++) {
-      os << " " << *instr.InputAt(i);
+      printable_op.op_ = instr.InputAt(i);
+      os << " " << printable_op;
     }
   }
   return os;
@@ -585,7 +602,9 @@
 }
 
 
-std::ostream& operator<<(std::ostream& os, const InstructionSequence& code) {
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableInstructionSequence& printable) {
+  const InstructionSequence& code = *printable.sequence_;
   for (size_t i = 0; i < code.immediates_.size(); ++i) {
     Constant constant = code.immediates_[i];
     os << "IMM#" << i << ": " << constant << "\n";
@@ -626,20 +645,16 @@
     }
 
     ScopedVector<char> buf(32);
+    PrintableInstruction printable_instr;
+    printable_instr.register_configuration_ = printable.register_configuration_;
     for (int j = block->first_instruction_index();
          j <= block->last_instruction_index(); j++) {
       // TODO(svenpanne) Add some basic formatting to our streams.
       SNPrintF(buf, "%5d", j);
-      os << "   " << buf.start() << ": " << *code.InstructionAt(j) << "\n";
+      printable_instr.instr_ = code.InstructionAt(j);
+      os << "   " << buf.start() << ": " << printable_instr << "\n";
     }
 
-    // TODO(dcarney): add this back somehow?
-    // os << "  " << block->control();
-
-    // if (block->control_input() != NULL) {
-    //   os << " v" << block->control_input()->id();
-    // }
-
     for (auto succ : block->successors()) {
       const InstructionBlock* succ_block = code.InstructionBlockAt(succ);
       os << " B" << succ_block->id();
diff --git a/src/compiler/instruction.h b/src/compiler/instruction.h
index 4f6649f..b42e17b 100644
--- a/src/compiler/instruction.h
+++ b/src/compiler/instruction.h
@@ -14,6 +14,7 @@
 #include "src/compiler/frame.h"
 #include "src/compiler/instruction-codes.h"
 #include "src/compiler/opcodes.h"
+#include "src/compiler/register-configuration.h"
 #include "src/compiler/schedule.h"
 #include "src/compiler/source-position.h"
 #include "src/zone-allocator.h"
@@ -27,18 +28,13 @@
 const InstructionCode kBlockStartInstruction = -2;
 const InstructionCode kSourcePositionInstruction = -3;
 
-// Platform independent maxes.
-static const int kMaxGeneralRegisters = 32;
-static const int kMaxDoubleRegisters = 32;
-
-
-#define INSTRUCTION_OPERAND_LIST(V)           \
-  V(Constant, CONSTANT, 0)                    \
-  V(Immediate, IMMEDIATE, 0)                  \
-  V(StackSlot, STACK_SLOT, 128)               \
-  V(DoubleStackSlot, DOUBLE_STACK_SLOT, 128)  \
-  V(Register, REGISTER, kMaxGeneralRegisters) \
-  V(DoubleRegister, DOUBLE_REGISTER, kMaxDoubleRegisters)
+#define INSTRUCTION_OPERAND_LIST(V)                                  \
+  V(Constant, CONSTANT, 0)                                           \
+  V(Immediate, IMMEDIATE, 0)                                         \
+  V(StackSlot, STACK_SLOT, 128)                                      \
+  V(DoubleStackSlot, DOUBLE_STACK_SLOT, 128)                         \
+  V(Register, REGISTER, RegisterConfiguration::kMaxGeneralRegisters) \
+  V(DoubleRegister, DOUBLE_REGISTER, RegisterConfiguration::kMaxDoubleRegisters)
 
 class InstructionOperand : public ZoneObject {
  public:
@@ -87,7 +83,13 @@
 
 typedef ZoneVector<InstructionOperand*> InstructionOperandVector;
 
-std::ostream& operator<<(std::ostream& os, const InstructionOperand& op);
+struct PrintableInstructionOperand {
+  const RegisterConfiguration* register_configuration_;
+  const InstructionOperand* op_;
+};
+
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableInstructionOperand& op);
 
 class UnallocatedOperand : public InstructionOperand {
  public:
@@ -306,7 +308,15 @@
   InstructionOperand* destination_;
 };
 
-std::ostream& operator<<(std::ostream& os, const MoveOperands& mo);
+
+struct PrintableMoveOperands {
+  const RegisterConfiguration* register_configuration_;
+  const MoveOperands* move_operands_;
+};
+
+
+std::ostream& operator<<(std::ostream& os, const PrintableMoveOperands& mo);
+
 
 template <InstructionOperand::Kind kOperandKind, int kNumCachedOperands>
 class SubKindOperand FINAL : public InstructionOperand {
@@ -359,7 +369,15 @@
   ZoneList<MoveOperands> move_operands_;
 };
 
-std::ostream& operator<<(std::ostream& os, const ParallelMove& pm);
+
+struct PrintableParallelMove {
+  const RegisterConfiguration* register_configuration_;
+  const ParallelMove* parallel_move_;
+};
+
+
+std::ostream& operator<<(std::ostream& os, const PrintableParallelMove& pm);
+
 
 class PointerMap FINAL : public ZoneObject {
  public:
@@ -534,7 +552,13 @@
   InstructionOperand* operands_[1];
 };
 
-std::ostream& operator<<(std::ostream& os, const Instruction& instr);
+
+struct PrintableInstruction {
+  const RegisterConfiguration* register_configuration_;
+  const Instruction* instr_;
+};
+std::ostream& operator<<(std::ostream& os, const PrintableInstruction& instr);
+
 
 // Represents moves inserted before an instruction due to register allocation.
 // TODO(titzer): squash GapInstruction back into Instruction, since essentially
@@ -585,7 +609,8 @@
   }
 
  private:
-  friend std::ostream& operator<<(std::ostream& os, const Instruction& instr);
+  friend std::ostream& operator<<(std::ostream& os,
+                                  const PrintableInstruction& instr);
   ParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
 };
 
@@ -847,6 +872,9 @@
 typedef ZoneVector<FrameStateDescriptor*> DeoptimizationVector;
 typedef ZoneVector<InstructionBlock*> InstructionBlocks;
 
+struct PrintableInstructionSequence;
+
+
 // Represents architecture-specific generated code before, during, and after
 // register allocation.
 // TODO(titzer): s/IsDouble/IsFloat64/
@@ -961,7 +989,7 @@
 
  private:
   friend std::ostream& operator<<(std::ostream& os,
-                                  const InstructionSequence& code);
+                                  const PrintableInstructionSequence& code);
 
   typedef std::set<int, std::less<int>, ZoneIntAllocator> VirtualRegisterSet;
 
@@ -977,7 +1005,15 @@
   DeoptimizationVector deoptimization_entries_;
 };
 
-std::ostream& operator<<(std::ostream& os, const InstructionSequence& code);
+
+struct PrintableInstructionSequence {
+  const RegisterConfiguration* register_configuration_;
+  const InstructionSequence* sequence_;
+};
+
+
+std::ostream& operator<<(std::ostream& os,
+                         const PrintableInstructionSequence& code);
 
 }  // namespace compiler
 }  // namespace internal
diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc
index dbaa293..b363d3d 100644
--- a/src/compiler/js-builtin-reducer.cc
+++ b/src/compiler/js-builtin-reducer.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "src/compiler/diamond.h"
 #include "src/compiler/graph-inl.h"
 #include "src/compiler/js-builtin-reducer.h"
 #include "src/compiler/node-matchers.h"
@@ -106,17 +107,10 @@
     // Math.abs(a:number) -> (a > 0 ? a : 0 - a)
     Node* value = r.left();
     Node* zero = jsgraph()->ZeroConstant();
-    Node* control = graph()->start();
-    Node* tag = graph()->NewNode(simplified()->NumberLessThan(), zero, value);
-
-    Node* branch = graph()->NewNode(common()->Branch(), tag, control);
-    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-
+    Node* cmp = graph()->NewNode(simplified()->NumberLessThan(), zero, value);
+    Diamond d(graph(), common(), cmp);
     Node* neg = graph()->NewNode(simplified()->NumberSubtract(), zero, value);
-    value = graph()->NewNode(common()->Phi(kMachNone, 2), value, neg, merge);
-    return Replace(value);
+    return Replace(d.Phi(kMachNone, value, neg));
   }
   return NoChange();
 }
@@ -150,15 +144,9 @@
     Node* value = r.GetJSCallInput(0);
     for (int i = 1; i < r.GetJSCallArity(); i++) {
       Node* p = r.GetJSCallInput(i);
-      Node* control = graph()->start();
-      Node* tag = graph()->NewNode(simplified()->NumberLessThan(), value, p);
-
-      Node* branch = graph()->NewNode(common()->Branch(), tag, control);
-      Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-      Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-      Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-
-      value = graph()->NewNode(common()->Phi(kMachNone, 2), p, value, merge);
+      Node* cmp = graph()->NewNode(simplified()->NumberLessThan(), value, p);
+      Diamond d(graph(), common(), cmp);
+      value = d.Phi(kMachNone, p, value);
     }
     return Replace(value);
   }
diff --git a/src/compiler/js-intrinsic-builder.cc b/src/compiler/js-intrinsic-builder.cc
index 4d8f607..d4c0dcf 100644
--- a/src/compiler/js-intrinsic-builder.cc
+++ b/src/compiler/js-intrinsic-builder.cc
@@ -4,6 +4,7 @@
 
 #include "src/compiler/access-builder.h"
 #include "src/compiler/common-operator.h"
+#include "src/compiler/diamond.h"
 #include "src/compiler/generic-node-inl.h"
 #include "src/compiler/js-intrinsic-builder.h"
 #include "src/compiler/js-operator.h"
@@ -68,29 +69,23 @@
   SimplifiedOperatorBuilder simplified(jsgraph_->zone());
 
   Node* is_smi = graph()->NewNode(simplified.ObjectIsSmi(), object);
-  Node* branch = graph()->NewNode(common()->Branch(), is_smi, graph()->start());
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+  Diamond d(graph(), common(), is_smi);
 
   Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()),
-                               object, effect, if_false);
+                               object, effect, d.if_false);
 
   Node* instance_type = graph()->NewNode(
       simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map,
-      if_false);
+      d.if_false);
 
   Node* has_map_type =
       graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type,
                        jsgraph_->Int32Constant(map_type));
 
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
+  Node* phi = d.Phi(static_cast<MachineType>(kTypeBool | kRepTagged),
+                    jsgraph_->FalseConstant(), has_map_type);
 
-  Node* phi =
-      graph()->NewNode(common()->Phi((MachineType)(kTypeBool | kRepTagged), 2),
-                       jsgraph_->FalseConstant(), has_map_type, merge);
-
-  Node* ephi =
-      graph()->NewNode(common()->EffectPhi(2), effect, instance_type, merge);
+  Node* ephi = d.EffectPhi(effect, instance_type);
 
   return ResultAndEffect(phi, ephi);
 }
@@ -112,44 +107,32 @@
   SimplifiedOperatorBuilder simplified(jsgraph_->zone());
 
   Node* is_smi = graph()->NewNode(simplified.ObjectIsSmi(), object);
-  Node* branch = graph()->NewNode(common()->Branch(), is_smi, graph()->start());
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+
+  Diamond if_is_smi(graph(), common(), is_smi);
 
   Node* map = graph()->NewNode(simplified.LoadField(AccessBuilder::ForMap()),
-                               object, effect, if_false);
+                               object, effect, if_is_smi.if_false);
 
   Node* instance_type = graph()->NewNode(
       simplified.LoadField(AccessBuilder::ForMapInstanceType()), map, map,
-      if_false);
+      if_is_smi.if_false);
 
   Node* is_value =
       graph()->NewNode(jsgraph_->machine()->Word32Equal(), instance_type,
                        jsgraph_->Constant(JS_VALUE_TYPE));
 
-  Node* branch_is_value =
-      graph()->NewNode(common()->Branch(), is_value, if_false);
-  Node* is_value_true = graph()->NewNode(common()->IfTrue(), branch_is_value);
-  Node* is_value_false = graph()->NewNode(common()->IfFalse(), branch_is_value);
+  Diamond if_is_value(graph(), common(), is_value);
+  if_is_value.Nest(if_is_smi, false);
 
   Node* value =
       graph()->NewNode(simplified.LoadField(AccessBuilder::ForValue()), object,
-                       instance_type, is_value_true);
+                       instance_type, if_is_value.if_true);
 
-  Node* merge_is_value =
-      graph()->NewNode(common()->Merge(2), is_value_true, is_value_false);
+  Node* phi_is_value = if_is_value.Phi(kTypeAny, value, object);
 
-  Node* phi_is_value = graph()->NewNode(common()->Phi((MachineType)kTypeAny, 2),
-                                        value, object, merge_is_value);
+  Node* phi = if_is_smi.Phi(kTypeAny, object, phi_is_value);
 
-
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, merge_is_value);
-
-  Node* phi = graph()->NewNode(common()->Phi((MachineType)kTypeAny, 2), object,
-                               phi_is_value, merge);
-
-  Node* ephi =
-      graph()->NewNode(common()->EffectPhi(2), effect, instance_type, merge);
+  Node* ephi = if_is_smi.EffectPhi(effect, instance_type);
 
   return ResultAndEffect(phi, ephi);
 }
diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc
index f51125a..d1a275d 100644
--- a/src/compiler/machine-operator-reducer.cc
+++ b/src/compiler/machine-operator-reducer.cc
@@ -7,6 +7,7 @@
 #include "src/base/bits.h"
 #include "src/base/division-by-constant.h"
 #include "src/codegen.h"
+#include "src/compiler/diamond.h"
 #include "src/compiler/generic-node-inl.h"
 #include "src/compiler/graph.h"
 #include "src/compiler/js-graph.h"
@@ -640,22 +641,10 @@
 
       Node* check =
           graph()->NewNode(machine()->Int32LessThan(), dividend, zero);
-      Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
-                                      check, graph()->start());
-
-      Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+      Diamond d(graph(), common(), check, BranchHint::kFalse);
       Node* neg = Int32Sub(zero, Word32And(Int32Sub(zero, dividend), mask));
-
-      Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
       Node* pos = Word32And(dividend, mask);
-
-      Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-
-      DCHECK_EQ(3, node->InputCount());
-      node->set_op(common()->Phi(kMachInt32, 2));
-      node->ReplaceInput(0, neg);
-      node->ReplaceInput(1, pos);
-      node->ReplaceInput(2, merge);
+      d.OverwriteWithPhi(node, kMachInt32, neg, pos);
     } else {
       Node* quotient = Int32Div(dividend, divisor);
       node->set_op(machine()->Int32Sub());
diff --git a/src/compiler/mips/code-generator-mips.cc b/src/compiler/mips/code-generator-mips.cc
index 2a07940..1c6688e 100644
--- a/src/compiler/mips/code-generator-mips.cc
+++ b/src/compiler/mips/code-generator-mips.cc
@@ -240,11 +240,10 @@
       __ Ror(i.OutputRegister(), i.InputRegister(0), i.InputOperand(1));
       break;
     case kMipsTst:
-      // Psuedo-instruction used for tst/branch.
-      __ And(kCompareReg, i.InputRegister(0), i.InputOperand(1));
+      // Pseudo-instruction used for tst/branch. No opcode emitted here.
       break;
     case kMipsCmp:
-      // Psuedo-instruction used for cmp/branch. No opcode emitted here.
+      // Pseudo-instruction used for cmp/branch. No opcode emitted here.
       break;
     case kMipsMov:
       // TODO(plind): Should we combine mov/li like this, or use separate instr?
@@ -418,7 +417,6 @@
   //    not separated by other instructions.
 
   if (instr->arch_opcode() == kMipsTst) {
-    // The kMipsTst psuedo-instruction emits And to 'kCompareReg' register.
     switch (condition) {
       case kNotEqual:
         cc = ne;
@@ -430,7 +428,8 @@
         UNSUPPORTED_COND(kMipsTst, condition);
         break;
     }
-    __ Branch(tlabel, cc, kCompareReg, Operand(zero_reg));
+    __ And(at, i.InputRegister(0), i.InputOperand(1));
+    __ Branch(tlabel, cc, at, Operand(zero_reg));
 
   } else if (instr->arch_opcode() == kMipsAddOvf ||
              instr->arch_opcode() == kMipsSubOvf) {
@@ -557,7 +556,6 @@
   // TODO(plind): Add CHECK() to ensure that test/cmp and this branch were
   //    not separated by other instructions.
   if (instr->arch_opcode() == kMipsTst) {
-    // The kMipsTst psuedo-instruction emits And to 'kCompareReg' register.
     switch (condition) {
       case kNotEqual:
         cc = ne;
@@ -569,7 +567,8 @@
         UNSUPPORTED_COND(kMipsTst, condition);
         break;
     }
-    __ Branch(USE_DELAY_SLOT, &done, cc, kCompareReg, Operand(zero_reg));
+    __ And(at, i.InputRegister(0), i.InputOperand(1));
+    __ Branch(USE_DELAY_SLOT, &done, cc, at, Operand(zero_reg));
     __ li(result, Operand(1));  // In delay slot.
 
   } else if (instr->arch_opcode() == kMipsAddOvf ||
diff --git a/src/compiler/mips/instruction-selector-mips.cc b/src/compiler/mips/instruction-selector-mips.cc
index f5a79ec..13df133 100644
--- a/src/compiler/mips/instruction-selector-mips.cc
+++ b/src/compiler/mips/instruction-selector-mips.cc
@@ -541,70 +541,114 @@
   VisitWordCompare(selector, node, kMipsCmp, cont, false);
 }
 
-
-void VisitWordTest(InstructionSelector* selector, Node* node,
-                   FlagsContinuation* cont) {
-  MipsOperandGenerator g(selector);
-  // kMipsTst is a pseudo-instruction to do logical 'and' and leave the result
-  // in a dedicated tmp register.
-  VisitCompare(selector, kMipsTst, g.UseRegister(node), g.UseRegister(node),
-               cont);
-}
-
 }  // namespace
 
 
+// Shared routine for word comparisons against zero.
+void VisitWordCompareZero(InstructionSelector* selector, Node* user,
+                          Node* value, FlagsContinuation* cont) {
+  while (selector->CanCover(user, value)) {
+    switch (value->opcode()) {
+      case IrOpcode::kWord32Equal: {
+        // Combine with comparisons against 0 by simply inverting the
+        // continuation.
+        Int32BinopMatcher m(value);
+        if (m.right().Is(0)) {
+          user = value;
+          value = m.left().node();
+          cont->Negate();
+          continue;
+        }
+        cont->OverwriteAndNegateIfEqual(kEqual);
+        return VisitWordCompare(selector, value, cont);
+      }
+      case IrOpcode::kInt32LessThan:
+        cont->OverwriteAndNegateIfEqual(kSignedLessThan);
+        return VisitWordCompare(selector, value, cont);
+      case IrOpcode::kInt32LessThanOrEqual:
+        cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
+      case IrOpcode::kUint32LessThan:
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
+        return VisitWordCompare(selector, value, cont);
+      case IrOpcode::kUint32LessThanOrEqual:
+        cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+        return VisitWordCompare(selector, value, cont);
+      case IrOpcode::kFloat64Equal:
+        cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
+        return VisitFloat64Compare(selector, value, cont);
+      case IrOpcode::kFloat64LessThan:
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThan);
+        return VisitFloat64Compare(selector, value, cont);
+      case IrOpcode::kFloat64LessThanOrEqual:
+        cont->OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual);
+        return VisitFloat64Compare(selector, value, cont);
+      case IrOpcode::kProjection:
+        // Check if this is the overflow output projection of an
+        // <Operation>WithOverflow node.
+        if (OpParameter<size_t>(value) == 1u) {
+          // We cannot combine the <Operation>WithOverflow with this branch
+          // unless the 0th projection (the use of the actual value of the
+          // <Operation> is either NULL, which means there's no use of the
+          // actual value, or was already defined, which means it is scheduled
+          // *AFTER* this branch).
+          Node* const node = value->InputAt(0);
+          Node* const result = node->FindProjection(0);
+          if (!result || selector->IsDefined(result)) {
+            switch (node->opcode()) {
+              case IrOpcode::kInt32AddWithOverflow:
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kMipsAddOvf, cont);
+              case IrOpcode::kInt32SubWithOverflow:
+                cont->OverwriteAndNegateIfEqual(kOverflow);
+                return VisitBinop(selector, node, kMipsSubOvf, cont);
+              default:
+                break;
+            }
+          }
+        }
+        break;
+      case IrOpcode::kWord32And:
+        return VisitWordCompare(selector, value, kMipsTst, cont, true);
+      default:
+        break;
+    }
+    break;
+  }
+
+  // Continuation could not be combined with a compare, emit compare against 0.
+  MipsOperandGenerator g(selector);
+  InstructionCode const opcode = cont->Encode(kMipsCmp);
+  InstructionOperand* const value_operand = g.UseRegister(value);
+  if (cont->IsBranch()) {
+    selector->Emit(opcode, nullptr, value_operand, g.TempImmediate(0),
+                   g.Label(cont->true_block()),
+                   g.Label(cont->false_block()))->MarkAsControl();
+  } else {
+    selector->Emit(opcode, g.DefineAsRegister(cont->result()), value_operand,
+                   g.TempImmediate(0));
+  }
+}
+
+
 void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch,
                                       BasicBlock* fbranch) {
-  MipsOperandGenerator g(this);
-  Node* user = branch;
-  Node* value = branch->InputAt(0);
-
   FlagsContinuation cont(kNotEqual, tbranch, fbranch);
-
   // If we can fall through to the true block, invert the branch.
   if (IsNextInAssemblyOrder(tbranch)) {
     cont.Negate();
     cont.SwapBlocks();
   }
-
-  // Try to combine with comparisons against 0 by simply inverting the branch.
-  while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) {
-    Int32BinopMatcher m(value);
-    if (m.right().Is(0)) {
-      user = value;
-      value = m.left().node();
-      cont.Negate();
-    } else {
-      break;
-    }
-  }
-
-  // Try to combine the branch with a comparison.
-  if (CanCover(user, value)) {
-    switch (value->opcode()) {
-      case IrOpcode::kWord32And:
-        // TODO(plind): understand the significance of 'IR and' special case.
-        return VisitWordCompare(this, value, kMipsTst, &cont, true);
-      default:
-        break;
-    }
-  }
-
-  // Branch could not be combined with a compare, emit compare against 0.
-  return VisitWordTest(this, value, &cont);
+  VisitWordCompareZero(this, branch, branch->InputAt(0), &cont);
 }
 
 
 void InstructionSelector::VisitWord32Equal(Node* const node) {
-  Node* const user = node;
   FlagsContinuation cont(kEqual, node);
-  Int32BinopMatcher m(user);
+  Int32BinopMatcher m(node);
   if (m.right().Is(0)) {
-    Node* const value = m.left().node();
-    return VisitWordTest(this, value, &cont);
+    return VisitWordCompareZero(this, m.node(), m.left().node(), &cont);
   }
-
   VisitWordCompare(this, node, &cont);
 }
 
diff --git a/src/compiler/pipeline.cc b/src/compiler/pipeline.cc
index 5c384c2..f2571e3 100644
--- a/src/compiler/pipeline.cc
+++ b/src/compiler/pipeline.cc
@@ -558,8 +558,10 @@
 
   if (FLAG_trace_turbo) {
     OFStream os(stdout);
+    PrintableInstructionSequence printable = {
+        RegisterConfiguration::ArchDefault(), &sequence};
     os << "----- Instruction sequence before register allocation -----\n"
-       << sequence;
+       << printable;
     TurboCfgFile tcf(isolate());
     tcf << AsC1V("CodeGen", data->schedule(), data->source_positions(),
                  &sequence);
@@ -587,7 +589,7 @@
 #endif
 
 
-    RegisterAllocator allocator(RegisterAllocator::PlatformConfig(),
+    RegisterAllocator allocator(RegisterConfiguration::ArchDefault(),
                                 zone_scope.zone(), &frame, &sequence,
                                 debug_name.get());
     if (!allocator.Allocate(data->pipeline_statistics())) {
@@ -602,8 +604,10 @@
 
   if (FLAG_trace_turbo) {
     OFStream os(stdout);
+    PrintableInstructionSequence printable = {
+        RegisterConfiguration::ArchDefault(), &sequence};
     os << "----- Instruction sequence after register allocation -----\n"
-       << sequence;
+       << printable;
   }
 
   if (data->pipeline_statistics() != NULL) {
diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc
index ced88a8..7a82768 100644
--- a/src/compiler/register-allocator.cc
+++ b/src/compiler/register-allocator.cc
@@ -5,7 +5,6 @@
 #include "src/compiler/linkage.h"
 #include "src/compiler/pipeline-statistics.h"
 #include "src/compiler/register-allocator.h"
-#include "src/macro-assembler.h"  // TODO(dcarney): remove this.
 #include "src/string-stream.h"
 
 namespace v8 {
@@ -507,22 +506,9 @@
 }
 
 
-RegisterAllocator::Config RegisterAllocator::PlatformConfig() {
-  DCHECK_EQ(Register::kMaxNumAllocatableRegisters,
-            Register::NumAllocatableRegisters());
-  Config config;
-  config.num_general_registers_ = Register::kMaxNumAllocatableRegisters;
-  config.num_double_registers_ = DoubleRegister::kMaxNumAllocatableRegisters;
-  config.num_aliased_double_registers_ =
-      DoubleRegister::NumAllocatableAliasedRegisters();
-  config.GeneralRegisterName = Register::AllocationIndexToString;
-  config.DoubleRegisterName = DoubleRegister::AllocationIndexToString;
-  return config;
-}
-
-
-RegisterAllocator::RegisterAllocator(const Config& config, Zone* local_zone,
-                                     Frame* frame, InstructionSequence* code,
+RegisterAllocator::RegisterAllocator(const RegisterConfiguration* config,
+                                     Zone* local_zone, Frame* frame,
+                                     InstructionSequence* code,
                                      const char* debug_name)
     : zone_(local_zone),
       frame_(frame),
@@ -531,8 +517,8 @@
       config_(config),
       live_in_sets_(code->InstructionBlockCount(), zone()),
       live_ranges_(code->VirtualRegisterCount() * 2, zone()),
-      fixed_live_ranges_(this->config().num_general_registers_, NULL, zone()),
-      fixed_double_live_ranges_(this->config().num_double_registers_, NULL,
+      fixed_live_ranges_(this->config()->num_general_registers(), NULL, zone()),
+      fixed_double_live_ranges_(this->config()->num_double_registers(), NULL,
                                 zone()),
       unhandled_live_ranges_(code->VirtualRegisterCount() * 2, zone()),
       active_live_ranges_(8, zone()),
@@ -541,12 +527,14 @@
       mode_(UNALLOCATED_REGISTERS),
       num_registers_(-1),
       allocation_ok_(true) {
-  DCHECK(this->config().num_general_registers_ <= kMaxGeneralRegisters);
-  DCHECK(this->config().num_double_registers_ <= kMaxDoubleRegisters);
+  DCHECK(this->config()->num_general_registers() <=
+         RegisterConfiguration::kMaxGeneralRegisters);
+  DCHECK(this->config()->num_double_registers() <=
+         RegisterConfiguration::kMaxDoubleRegisters);
   // TryAllocateFreeReg and AllocateBlockedReg assume this
   // when allocating local arrays.
-  DCHECK(this->config().num_double_registers_ >=
-         this->config().num_general_registers_);
+  DCHECK(this->config()->num_double_registers() >=
+         this->config()->num_general_registers());
 }
 
 
@@ -603,7 +591,7 @@
 
 
 int RegisterAllocator::FixedDoubleLiveRangeID(int index) {
-  return -index - 1 - config().num_general_registers_;
+  return -index - 1 - config()->num_general_registers();
 }
 
 
@@ -635,7 +623,7 @@
 
 
 LiveRange* RegisterAllocator::FixedLiveRangeFor(int index) {
-  DCHECK(index < config().num_general_registers_);
+  DCHECK(index < config()->num_general_registers());
   LiveRange* result = fixed_live_ranges_[index];
   if (result == NULL) {
     // TODO(titzer): add a utility method to allocate a new LiveRange:
@@ -653,7 +641,7 @@
 
 
 LiveRange* RegisterAllocator::FixedDoubleLiveRangeFor(int index) {
-  DCHECK(index < config().num_aliased_double_registers_);
+  DCHECK(index < config()->num_aliased_double_registers());
   LiveRange* result = fixed_double_live_ranges_[index];
   if (result == NULL) {
     result = new (zone()) LiveRange(FixedDoubleLiveRangeID(index), code_zone());
@@ -1031,7 +1019,7 @@
       }
 
       if (instr->ClobbersRegisters()) {
-        for (int i = 0; i < config().num_general_registers_; ++i) {
+        for (int i = 0; i < config()->num_general_registers(); ++i) {
           if (!IsOutputRegisterOf(instr, i)) {
             LiveRange* range = FixedLiveRangeFor(i);
             range->AddUseInterval(curr_position, curr_position.InstructionEnd(),
@@ -1041,7 +1029,7 @@
       }
 
       if (instr->ClobbersDoubleRegisters()) {
-        for (int i = 0; i < config().num_aliased_double_registers_; ++i) {
+        for (int i = 0; i < config()->num_aliased_double_registers(); ++i) {
           if (!IsOutputDoubleRegisterOf(instr, i)) {
             LiveRange* range = FixedDoubleLiveRangeFor(i);
             range->AddUseInterval(curr_position, curr_position.InstructionEnd(),
@@ -1126,10 +1114,10 @@
 
 
 bool RegisterAllocator::Allocate(PipelineStatistics* stats) {
-  assigned_registers_ =
-      new (code_zone()) BitVector(config().num_general_registers_, code_zone());
+  assigned_registers_ = new (code_zone())
+      BitVector(config()->num_general_registers(), code_zone());
   assigned_double_registers_ = new (code_zone())
-      BitVector(config().num_aliased_double_registers_, code_zone());
+      BitVector(config()->num_aliased_double_registers(), code_zone());
   {
     PhaseScope phase_scope(stats, "meet register constraints");
     MeetRegisterConstraints();
@@ -1535,14 +1523,14 @@
 
 
 void RegisterAllocator::AllocateGeneralRegisters() {
-  num_registers_ = config().num_general_registers_;
+  num_registers_ = config()->num_general_registers();
   mode_ = GENERAL_REGISTERS;
   AllocateRegisters();
 }
 
 
 void RegisterAllocator::AllocateDoubleRegisters() {
-  num_registers_ = config().num_aliased_double_registers_;
+  num_registers_ = config()->num_aliased_double_registers();
   mode_ = DOUBLE_REGISTERS;
   AllocateRegisters();
 }
@@ -1566,7 +1554,7 @@
   DCHECK(inactive_live_ranges_.is_empty());
 
   if (mode_ == DOUBLE_REGISTERS) {
-    for (int i = 0; i < config().num_aliased_double_registers_; ++i) {
+    for (int i = 0; i < config()->num_aliased_double_registers(); ++i) {
       LiveRange* current = fixed_double_live_ranges_.at(i);
       if (current != NULL) {
         AddToInactive(current);
@@ -1658,9 +1646,9 @@
 
 const char* RegisterAllocator::RegisterName(int allocation_index) {
   if (mode_ == GENERAL_REGISTERS) {
-    return config().GeneralRegisterName(allocation_index);
+    return config()->general_register_name(allocation_index);
   } else {
-    return config().DoubleRegisterName(allocation_index);
+    return config()->double_register_name(allocation_index);
   }
 }
 
@@ -1805,7 +1793,7 @@
 
 
 bool RegisterAllocator::TryAllocateFreeReg(LiveRange* current) {
-  LifetimePosition free_until_pos[kMaxDoubleRegisters];
+  LifetimePosition free_until_pos[RegisterConfiguration::kMaxDoubleRegisters];
 
   for (int i = 0; i < num_registers_; i++) {
     free_until_pos[i] = LifetimePosition::MaxPosition();
@@ -1888,8 +1876,8 @@
     return;
   }
 
-  LifetimePosition use_pos[kMaxGeneralRegisters];
-  LifetimePosition block_pos[kMaxDoubleRegisters];
+  LifetimePosition use_pos[RegisterConfiguration::kMaxGeneralRegisters];
+  LifetimePosition block_pos[RegisterConfiguration::kMaxDoubleRegisters];
 
   for (int i = 0; i < num_registers_; i++) {
     use_pos[i] = block_pos[i] = LifetimePosition::MaxPosition();
diff --git a/src/compiler/register-allocator.h b/src/compiler/register-allocator.h
index db30f96..b167437 100644
--- a/src/compiler/register-allocator.h
+++ b/src/compiler/register-allocator.h
@@ -319,19 +319,9 @@
 
 class RegisterAllocator FINAL {
  public:
-  class Config {
-   public:
-    int num_general_registers_;
-    int num_double_registers_;
-    int num_aliased_double_registers_;
-    const char* (*GeneralRegisterName)(int allocation_index);
-    const char* (*DoubleRegisterName)(int allocation_index);
-  };
-
-  static Config PlatformConfig();
-
-  explicit RegisterAllocator(const Config& config, Zone* local_zone,
-                             Frame* frame, InstructionSequence* code,
+  explicit RegisterAllocator(const RegisterConfiguration* config,
+                             Zone* local_zone, Frame* frame,
+                             InstructionSequence* code,
                              const char* debug_name = nullptr);
 
   bool Allocate(PipelineStatistics* stats = NULL);
@@ -502,14 +492,14 @@
 
   Frame* frame() const { return frame_; }
   const char* debug_name() const { return debug_name_; }
-  const Config& config() const { return config_; }
+  const RegisterConfiguration* config() const { return config_; }
 
   Zone* const zone_;
   Frame* const frame_;
   InstructionSequence* const code_;
   const char* const debug_name_;
 
-  const Config config_;
+  const RegisterConfiguration* config_;
 
   // During liveness analysis keep a mapping from block id to live_in sets
   // for blocks already analyzed.
diff --git a/src/compiler/register-configuration.cc b/src/compiler/register-configuration.cc
new file mode 100644
index 0000000..e7d8bbd
--- /dev/null
+++ b/src/compiler/register-configuration.cc
@@ -0,0 +1,68 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/register-configuration.h"
+#include "src/macro-assembler.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+namespace {
+
+STATIC_ASSERT(RegisterConfiguration::kMaxGeneralRegisters >=
+              Register::kNumRegisters);
+STATIC_ASSERT(RegisterConfiguration::kMaxDoubleRegisters >=
+              DoubleRegister::kMaxNumRegisters);
+
+class ArchDefaultRegisterConfiguration : public RegisterConfiguration {
+ public:
+  ArchDefaultRegisterConfiguration()
+      : RegisterConfiguration(Register::kMaxNumAllocatableRegisters,
+                              DoubleRegister::kMaxNumAllocatableRegisters,
+                              DoubleRegister::NumAllocatableAliasedRegisters(),
+                              general_register_name_table_,
+                              double_register_name_table_) {
+    DCHECK_EQ(Register::kMaxNumAllocatableRegisters,
+              Register::NumAllocatableRegisters());
+    for (int i = 0; i < Register::kMaxNumAllocatableRegisters; ++i) {
+      general_register_name_table_[i] = Register::AllocationIndexToString(i);
+    }
+    for (int i = 0; i < DoubleRegister::kMaxNumAllocatableRegisters; ++i) {
+      double_register_name_table_[i] =
+          DoubleRegister::AllocationIndexToString(i);
+    }
+  }
+
+  const char*
+      general_register_name_table_[Register::kMaxNumAllocatableRegisters];
+  const char*
+      double_register_name_table_[DoubleRegister::kMaxNumAllocatableRegisters];
+};
+
+
+static base::LazyInstance<ArchDefaultRegisterConfiguration>::type
+    kDefaultRegisterConfiguration = LAZY_INSTANCE_INITIALIZER;
+
+}  // namepace
+
+
+const RegisterConfiguration* RegisterConfiguration::ArchDefault() {
+  return &kDefaultRegisterConfiguration.Get();
+}
+
+RegisterConfiguration::RegisterConfiguration(
+    int num_general_registers, int num_double_registers,
+    int num_aliased_double_registers, const char* const* general_register_names,
+    const char* const* double_register_names)
+    : num_general_registers_(num_general_registers),
+      num_double_registers_(num_double_registers),
+      num_aliased_double_registers_(num_aliased_double_registers),
+      general_register_names_(general_register_names),
+      double_register_names_(double_register_names) {}
+
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/src/compiler/register-configuration.h b/src/compiler/register-configuration.h
new file mode 100644
index 0000000..8178ba2
--- /dev/null
+++ b/src/compiler/register-configuration.h
@@ -0,0 +1,56 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_COMPILER_REGISTER_CONFIGURATION_H_
+#define V8_COMPILER_REGISTER_CONFIGURATION_H_
+
+#include "src/v8.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// An architecture independent representation of the sets of registers available
+// for instruction creation.
+class RegisterConfiguration {
+ public:
+  // Architecture independent maxes.
+  static const int kMaxGeneralRegisters = 32;
+  static const int kMaxDoubleRegisters = 32;
+
+  static const RegisterConfiguration* ArchDefault();
+
+  RegisterConfiguration(int num_general_registers, int num_double_registers,
+                        int num_aliased_double_registers,
+                        const char* const* general_register_name,
+                        const char* const* double_register_name);
+
+  int num_general_registers() const { return num_general_registers_; }
+  int num_double_registers() const { return num_double_registers_; }
+  int num_aliased_double_registers() const {
+    return num_aliased_double_registers_;
+  }
+
+  const char* general_register_name(int offset) const {
+    DCHECK(offset >= 0 && offset < kMaxGeneralRegisters);
+    return general_register_names_[offset];
+  }
+  const char* double_register_name(int offset) const {
+    DCHECK(offset >= 0 && offset < kMaxDoubleRegisters);
+    return double_register_names_[offset];
+  }
+
+ private:
+  const int num_general_registers_;
+  const int num_double_registers_;
+  const int num_aliased_double_registers_;
+  const char* const* general_register_names_;
+  const char* const* double_register_names_;
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_COMPILER_REGISTER_CONFIGURATION_H_
diff --git a/src/compiler/select-lowering.cc b/src/compiler/select-lowering.cc
index 4e553d1..2e51d72 100644
--- a/src/compiler/select-lowering.cc
+++ b/src/compiler/select-lowering.cc
@@ -5,6 +5,7 @@
 #include "src/compiler/select-lowering.h"
 
 #include "src/compiler/common-operator.h"
+#include "src/compiler/diamond.h"
 #include "src/compiler/generic-node-inl.h"
 #include "src/compiler/graph.h"
 
@@ -26,17 +27,13 @@
   SelectParameters const p = SelectParametersOf(node->op());
 
   Node* const cond = node->InputAt(0);
-  Node* const control = graph()->start();
 
   // Check if we already have a diamond for this condition.
   auto i = merges_.find(cond);
   if (i == merges_.end()) {
     // Create a new diamond for this condition and remember its merge node.
-    Node* branch = graph()->NewNode(common()->Branch(p.hint()), cond, control);
-    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-    i = merges_.insert(std::make_pair(cond, merge)).first;
+    Diamond d(graph(), common(), cond, p.hint());
+    i = merges_.insert(std::make_pair(cond, d.merge)).first;
   }
 
   DCHECK_EQ(cond, i->first);
diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc
index 3069865..c50b338 100644
--- a/src/compiler/simplified-lowering.cc
+++ b/src/compiler/simplified-lowering.cc
@@ -9,6 +9,7 @@
 #include "src/base/bits.h"
 #include "src/code-factory.h"
 #include "src/compiler/common-operator.h"
+#include "src/compiler/diamond.h"
 #include "src/compiler/graph-inl.h"
 #include "src/compiler/node-matchers.h"
 #include "src/compiler/node-properties-inl.h"
@@ -632,7 +633,7 @@
           if (lower()) DeferReplacement(node, lowering->Int32Div(node));
           break;
         }
-        if (CanLowerToUint32Binop(node, use)) {
+        if (BothInputsAre(node, Type::Unsigned32()) && !CanObserveNaN(use)) {
           // => unsigned Uint32Div
           VisitUint32Binop(node);
           if (lower()) DeferReplacement(node, lowering->Uint32Div(node));
@@ -1172,11 +1173,9 @@
       node->ReplaceInput(2, effect);
       node->ReplaceInput(3, graph()->start());
     } else {
-      Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
-                                      check, graph()->start());
+      Diamond d(graph(), common(), check, BranchHint::kTrue);
 
-      Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-      Node* load = graph()->NewNode(op, base, index, effect, if_true);
+      Node* load = graph()->NewNode(op, base, index, effect, d.if_true);
       Node* result = load;
       if (output_type & kRepTagged) {
         // TODO(turbofan): This is ugly as hell!
@@ -1187,7 +1186,6 @@
             changer.GetTaggedRepresentationFor(result, access.machine_type);
       }
 
-      Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
       Node* undefined;
       if (output_type & kRepTagged) {
         DCHECK_EQ(0, access.machine_type & kRepTagged);
@@ -1202,23 +1200,10 @@
         undefined = jsgraph()->Int32Constant(0);
       }
 
-      Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-      Node* phi = graph()->NewNode(common()->EffectPhi(2), load, effect, merge);
-
       // Replace effect uses of node with the effect phi.
-      for (UseIter i = node->uses().begin(); i != node->uses().end();) {
-        if (NodeProperties::IsEffectEdge(i.edge())) {
-          i = i.UpdateToAndIncrement(phi);
-        } else {
-          ++i;
-        }
-      }
+      NodeProperties::ReplaceWithValue(node, node, d.EffectPhi(load, effect));
 
-      node->set_op(common()->Phi(output_type, 2));
-      node->ReplaceInput(0, result);
-      node->ReplaceInput(1, undefined);
-      node->ReplaceInput(2, merge);
-      node->TrimInputCount(3);
+      d.OverwriteWithPhi(node, output_type, result, undefined);
     }
   }
 }
@@ -1257,21 +1242,10 @@
       node->ReplaceInput(1, select);
       node->RemoveInput(2);
     } else {
-      Node* branch =
-          graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
-
-      Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-      Node* store = graph()->NewNode(op, base, index, value, effect, if_true);
-
-      Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-
-      Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-
-      node->set_op(common()->EffectPhi(2));
-      node->ReplaceInput(0, store);
-      node->ReplaceInput(1, effect);
-      node->ReplaceInput(2, merge);
-      node->TrimInputCount(3);
+      Diamond d(graph(), common(), check, BranchHint::kTrue);
+      d.Chain(control);
+      Node* store = graph()->NewNode(op, base, index, value, effect, d.if_true);
+      d.OverwriteWithEffectPhi(node, store, effect);
     }
   }
 }
@@ -1325,34 +1299,20 @@
     return graph()->NewNode(machine()->Int32Div(), lhs, rhs, graph()->start());
   }
 
-  Node* check0 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
-  Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0,
-                                   graph()->start());
+  Diamond if_zero(graph(), common(),
+                  graph()->NewNode(machine()->Word32Equal(), rhs, zero),
+                  BranchHint::kFalse);
 
-  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
-  Node* true0 = zero;
+  Diamond if_minus_one(graph(), common(),
+                       graph()->NewNode(machine()->Word32Equal(), rhs,
+                                        jsgraph()->Int32Constant(-1)),
+                       BranchHint::kFalse);
+  if_minus_one.Nest(if_zero, false);
+  Node* sub = graph()->NewNode(machine()->Int32Sub(), zero, lhs);
+  Node* div =
+      graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_minus_one.if_false);
 
-  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
-  Node* false0 = nullptr;
-  {
-    Node* check1 = graph()->NewNode(machine()->Word32Equal(), rhs,
-                                    jsgraph()->Int32Constant(-1));
-    Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
-                                     check1, if_false0);
-
-    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
-    Node* true1 = graph()->NewNode(machine()->Int32Sub(), zero, lhs);
-
-    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
-    Node* false1 = graph()->NewNode(machine()->Int32Div(), lhs, rhs, if_false1);
-
-    if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
-    false0 = graph()->NewNode(common()->Phi(kMachInt32, 2), true1, false1,
-                              if_false0);
-  }
-
-  Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
-  return graph()->NewNode(common()->Phi(kMachInt32, 2), true0, false0, merge0);
+  return if_zero.Phi(kMachInt32, zero, if_minus_one.Phi(kMachInt32, sub, div));
 }
 
 
@@ -1368,34 +1328,19 @@
     return graph()->NewNode(machine()->Int32Mod(), lhs, rhs, graph()->start());
   }
 
-  Node* check0 = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
-  Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse), check0,
-                                   graph()->start());
+  Diamond if_zero(graph(), common(),
+                  graph()->NewNode(machine()->Word32Equal(), rhs, zero),
+                  BranchHint::kFalse);
 
-  Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
-  Node* true0 = zero;
+  Diamond if_minus_one(graph(), common(),
+                       graph()->NewNode(machine()->Word32Equal(), rhs,
+                                        jsgraph()->Int32Constant(-1)),
+                       BranchHint::kFalse);
+  if_minus_one.Nest(if_zero, false);
+  Node* mod =
+      graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_minus_one.if_false);
 
-  Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
-  Node* false0 = nullptr;
-  {
-    Node* check1 = graph()->NewNode(machine()->Word32Equal(), rhs,
-                                    jsgraph()->Int32Constant(-1));
-    Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
-                                     check1, if_false0);
-
-    Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
-    Node* true1 = zero;
-
-    Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
-    Node* false1 = graph()->NewNode(machine()->Int32Mod(), lhs, rhs, if_false1);
-
-    if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
-    false0 = graph()->NewNode(common()->Phi(kMachInt32, 2), true1, false1,
-                              if_false0);
-  }
-
-  Node* merge0 = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
-  return graph()->NewNode(common()->Phi(kMachInt32, 2), true0, false0, merge0);
+  return if_zero.Phi(kMachInt32, zero, if_minus_one.Phi(kMachInt32, zero, mod));
 }
 
 
@@ -1412,17 +1357,9 @@
   }
 
   Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
-  Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check,
-                                  graph()->start());
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* vtrue = zero;
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-  Node* vfalse = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, if_false);
-
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  return graph()->NewNode(common()->Phi(kMachUint32, 2), vtrue, vfalse, merge);
+  Diamond d(graph(), common(), check, BranchHint::kFalse);
+  Node* div = graph()->NewNode(machine()->Uint32Div(), lhs, rhs, d.if_false);
+  return d.Phi(kMachUint32, zero, div);
 }
 
 
@@ -1439,17 +1376,9 @@
   }
 
   Node* check = graph()->NewNode(machine()->Word32Equal(), rhs, zero);
-  Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), check,
-                                  graph()->start());
-
-  Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
-  Node* vtrue = zero;
-
-  Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
-  Node* vfalse = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, if_false);
-
-  Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
-  return graph()->NewNode(common()->Phi(kMachUint32, 2), vtrue, vfalse, merge);
+  Diamond d(graph(), common(), check, BranchHint::kFalse);
+  Node* mod = graph()->NewNode(machine()->Uint32Mod(), lhs, rhs, d.if_false);
+  return d.Phi(kMachUint32, zero, mod);
 }
 
 
diff --git a/src/factory.cc b/src/factory.cc
index 5713f73..72974a3 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -1722,6 +1722,22 @@
 }
 
 
+Handle<JSMapIterator> Factory::NewJSMapIterator() {
+  Handle<Map> map(isolate()->native_context()->map_iterator_map());
+  CALL_HEAP_FUNCTION(isolate(),
+                     isolate()->heap()->AllocateJSObjectFromMap(*map),
+                     JSMapIterator);
+}
+
+
+Handle<JSSetIterator> Factory::NewJSSetIterator() {
+  Handle<Map> map(isolate()->native_context()->set_iterator_map());
+  CALL_HEAP_FUNCTION(isolate(),
+                     isolate()->heap()->AllocateJSObjectFromMap(*map),
+                     JSSetIterator);
+}
+
+
 namespace {
 
 ElementsKind GetExternalArrayElementsKind(ExternalArrayType type) {
diff --git a/src/factory.h b/src/factory.h
index e9972cc..9f9813c 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -445,6 +445,10 @@
   Handle<JSDataView> NewJSDataView(Handle<JSArrayBuffer> buffer,
                                    size_t byte_offset, size_t byte_length);
 
+  // TODO(aandrey): Maybe these should take table, index and kind arguments.
+  Handle<JSMapIterator> NewJSMapIterator();
+  Handle<JSSetIterator> NewJSSetIterator();
+
   // Allocates a Harmony proxy.
   Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
 
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index d161d55..690e5c7 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -155,54 +155,65 @@
 // Flags for language modes and experimental language features.
 DEFINE_BOOL(use_strict, false, "enforce strict mode")
 
-DEFINE_BOOL(es_staging, false, "enable upcoming ES6+ features")
-DEFINE_BOOL(harmony, false, "enable all harmony features (except proxies)")
+DEFINE_BOOL(es_staging, false, "enable all completed harmony features")
+DEFINE_BOOL(harmony, false, "enable all completed harmony features")
 DEFINE_IMPLICATION(harmony, es_staging)
+// TODO(rossberg): activate once we have staged scoping:
+// DEFINE_IMPLICATION(es_staging, harmony)
 
-#define HARMONY_FEATURES(V)                                       \
+// Features that are still work in progress (behind individual flags).
+#define HARMONY_INPROGRESS(V)                                     \
   V(harmony_scoping, "harmony block scoping")                     \
   V(harmony_modules, "harmony modules (implies block scoping)")   \
-  V(harmony_arrays, "harmony arrays")                             \
-  V(harmony_classes, "harmony classes")                           \
+  V(harmony_arrays, "harmony array methods")                      \
+  V(harmony_classes,                                              \
+    "harmony classes (implies block scoping & object literal extension)") \
   V(harmony_object_literals, "harmony object literal extensions") \
-  V(harmony_regexps, "reg-exp related harmony features")          \
+  V(harmony_regexps, "harmony regular expression extensions")     \
   V(harmony_arrow_functions, "harmony arrow functions")           \
-  V(harmony_tostring, "harmony Symbol.toStringTag")
+  V(harmony_tostring, "harmony toString")                         \
+  V(harmony_proxies, "harmony proxies")
 
-#define STAGED_FEATURES(V)              \
-  V(harmony_strings, "harmony strings") \
-  V(harmony_numeric_literals, "harmony numeric literals (0o77, 0b11)")
+// Features that are complete (but still behind --harmony/es-staging flag).
+#define HARMONY_STAGED(V)                      \
+  V(harmony_strings, "harmony string methods") \
+  V(harmony_numeric_literals, "harmony numeric literals")
 
-#define SHIPPING_FEATURES(V)
+// Features that are shipping (turned on by default, but internal flag remains).
+#define HARMONY_SHIPPING(V)
 
-#define FLAG_FEATURES(id, description)           \
+// Once a shipping feature has proved stable in the wild, it will be dropped
+// from HARMONY_SHIPPING, all occurrences of the FLAG_ variable are removed,
+// and associated tests are moved from the harmony directory to the appropriate
+// esN directory.
+
+
+#define FLAG_INPROGRESS_FEATURES(id, description) \
+  DEFINE_BOOL(id, false, "enable " #description " (in progress)")
+HARMONY_INPROGRESS(FLAG_INPROGRESS_FEATURES)
+#undef FLAG_INPROGRESS_FEATURES
+
+// TODO(rossberg): temporary, remove once we have staged scoping.
+// After that, --harmony will be synonymous to --es-staging.
+DEFINE_IMPLICATION(harmony, harmony_scoping)
+
+#define FLAG_STAGED_FEATURES(id, description) \
   DEFINE_BOOL(id, false, "enable " #description) \
-  DEFINE_IMPLICATION(harmony, id)
-
-HARMONY_FEATURES(FLAG_FEATURES)
-STAGED_FEATURES(FLAG_FEATURES)
-#undef FLAG_FEATURES
-
-#define FLAG_STAGED_FEATURES(id, description) DEFINE_IMPLICATION(es_staging, id)
-
-STAGED_FEATURES(FLAG_STAGED_FEATURES)
+  DEFINE_IMPLICATION(es_staging, id)
+HARMONY_STAGED(FLAG_STAGED_FEATURES)
 #undef FLAG_STAGED_FEATURES
 
 #define FLAG_SHIPPING_FEATURES(id, description) \
   DEFINE_BOOL_READONLY(id, true, "enable " #description)
-
-SHIPPING_FEATURES(FLAG_SHIPPING_FEATURES)
+HARMONY_SHIPPING(FLAG_SHIPPING_FEATURES)
 #undef FLAG_SHIPPING_FEATURES
 
+
 // Feature dependencies.
 DEFINE_IMPLICATION(harmony_modules, harmony_scoping)
 DEFINE_IMPLICATION(harmony_classes, harmony_scoping)
 DEFINE_IMPLICATION(harmony_classes, harmony_object_literals)
 
-DEFINE_BOOL(harmony_proxies, false, "enable harmony proxies")
-// TODO(rossberg): Reenable when problems are sorted out.
-// DEFINE_IMPLICATION(harmony, harmony_proxies)
-
 
 // Flags for experimental implementation features.
 DEFINE_BOOL(compiled_keyed_generic_loads, false,
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index 5b19fca..2cefebf 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -456,7 +456,6 @@
 void MarkCompactCollector::StartSweeperThreads() {
   DCHECK(free_list_old_pointer_space_.get()->IsEmpty());
   DCHECK(free_list_old_data_space_.get()->IsEmpty());
-  sweeping_in_progress_ = true;
   V8::GetCurrentPlatform()->CallOnBackgroundThread(
       new SweeperTask(heap(), heap()->old_data_space()),
       v8::Platform::kShortRunningTask);
@@ -471,13 +470,15 @@
 
   // If sweeping is not completed or not running at all, we try to complete it
   // here.
-  if (!IsSweepingCompleted()) {
+  if (FLAG_predictable || !IsSweepingCompleted()) {
     SweepInParallel(heap()->paged_space(OLD_DATA_SPACE), 0);
     SweepInParallel(heap()->paged_space(OLD_POINTER_SPACE), 0);
   }
   // Wait twice for both jobs.
-  pending_sweeper_jobs_semaphore_.Wait();
-  pending_sweeper_jobs_semaphore_.Wait();
+  if (!FLAG_predictable) {
+    pending_sweeper_jobs_semaphore_.Wait();
+    pending_sweeper_jobs_semaphore_.Wait();
+  }
   ParallelSweepSpacesComplete();
   sweeping_in_progress_ = false;
   RefillFreeList(heap()->paged_space(OLD_DATA_SPACE));
@@ -4185,7 +4186,7 @@
       SweepSpace(heap()->old_pointer_space(), CONCURRENT_SWEEPING);
       SweepSpace(heap()->old_data_space(), CONCURRENT_SWEEPING);
     }
-
+    sweeping_in_progress_ = true;
     if (!FLAG_predictable) {
       StartSweeperThreads();
     }
diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc
index 430f31d..2b696ea 100644
--- a/src/heap/spaces.cc
+++ b/src/heap/spaces.cc
@@ -2569,7 +2569,8 @@
 
 
 intptr_t PagedSpace::SizeOfObjects() {
-  DCHECK(heap()->mark_compact_collector()->sweeping_in_progress() ||
+  DCHECK(FLAG_predictable ||
+         heap()->mark_compact_collector()->sweeping_in_progress() ||
          (unswept_free_bytes_ == 0));
   return Size() - unswept_free_bytes_ - (limit() - top());
 }
diff --git a/src/ia32/interface-descriptors-ia32.cc b/src/ia32/interface-descriptors-ia32.cc
index e5096b3..6c77ef8 100644
--- a/src/ia32/interface-descriptors-ia32.cc
+++ b/src/ia32/interface-descriptors-ia32.cc
@@ -155,6 +155,15 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // register state
+  // esi -- context
+  Register registers[] = {esi};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // register state
diff --git a/src/ic/ic.cc b/src/ic/ic.cc
index d0c0edb..a9369ed 100644
--- a/src/ic/ic.cc
+++ b/src/ic/ic.cc
@@ -2688,7 +2688,7 @@
   PrototypeIterator iter(isolate, receiver,
                          PrototypeIterator::START_AT_RECEIVER);
   bool found = false;
-  while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) {
+  for (; !iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN); iter.Advance()) {
     Handle<Object> current = PrototypeIterator::GetCurrent(iter);
     if (current->IsJSObject() &&
         Handle<JSObject>::cast(current)->HasNamedInterceptor()) {
diff --git a/src/interface-descriptors.h b/src/interface-descriptors.h
index 3e4f3e7..5d02e84 100644
--- a/src/interface-descriptors.h
+++ b/src/interface-descriptors.h
@@ -33,6 +33,7 @@
   V(CallConstruct)                            \
   V(RegExpConstructResult)                    \
   V(TransitionElementsKind)                   \
+  V(AllocateHeapNumber)                       \
   V(ArrayConstructorConstantArgCount)         \
   V(ArrayConstructor)                         \
   V(InternalArrayConstructorConstantArgCount) \
@@ -346,6 +347,12 @@
 };
 
 
+class AllocateHeapNumberDescriptor : public CallInterfaceDescriptor {
+ public:
+  DECLARE_DESCRIPTOR(AllocateHeapNumberDescriptor, CallInterfaceDescriptor)
+};
+
+
 class ArrayConstructorConstantArgCountDescriptor
     : public CallInterfaceDescriptor {
  public:
diff --git a/src/isolate.cc b/src/isolate.cc
index a49eee6..2595d2f 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -1047,15 +1047,15 @@
 }
 
 
-void Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
+bool Isolate::ComputeLocationFromStackTrace(MessageLocation* target,
                                             Handle<Object> exception) {
   *target = MessageLocation(Handle<Script>(heap_.empty_script()), -1, -1);
 
-  if (!exception->IsJSObject()) return;
+  if (!exception->IsJSObject()) return false;
   Handle<Name> key = factory()->stack_trace_symbol();
   Handle<Object> property =
       JSObject::GetDataProperty(Handle<JSObject>::cast(exception), key);
-  if (!property->IsJSArray()) return;
+  if (!property->IsJSArray()) return false;
   Handle<JSArray> simple_stack_trace = Handle<JSArray>::cast(property);
 
   Handle<FixedArray> elements(FixedArray::cast(simple_stack_trace->elements()));
@@ -1075,9 +1075,10 @@
       int pos = code->SourcePosition(pc);
       Handle<Script> casted_script(Script::cast(script));
       *target = MessageLocation(casted_script, pos, pos + 1);
-      break;
+      return true;
     }
   }
+  return false;
 }
 
 
@@ -1149,10 +1150,6 @@
       // at this throw site.
       stack_trace_object =
           GetDetailedStackTrace(Handle<JSObject>::cast(exception));
-      if (!location) {
-        ComputeLocationFromStackTrace(&potential_computed_location, exception);
-        location = &potential_computed_location;
-      }
     }
     if (stack_trace_object.is_null()) {
       // Not an error object, we capture stack and location at throw site.
@@ -1162,7 +1159,10 @@
     }
   }
   if (!location) {
-    ComputeLocation(&potential_computed_location);
+    if (!ComputeLocationFromStackTrace(&potential_computed_location,
+                                       exception)) {
+      ComputeLocation(&potential_computed_location);
+    }
     location = &potential_computed_location;
   }
 
diff --git a/src/isolate.h b/src/isolate.h
index 7e50929..3551632 100644
--- a/src/isolate.h
+++ b/src/isolate.h
@@ -801,7 +801,7 @@
   // Attempts to compute the current source location, storing the
   // result in the target out parameter.
   void ComputeLocation(MessageLocation* target);
-  void ComputeLocationFromStackTrace(MessageLocation* target,
+  bool ComputeLocationFromStackTrace(MessageLocation* target,
                                      Handle<Object> exception);
 
   Handle<JSMessageObject> CreateMessage(Handle<Object> exception,
diff --git a/src/mips/interface-descriptors-mips.cc b/src/mips/interface-descriptors-mips.cc
index 7e14967..ecdaecf 100644
--- a/src/mips/interface-descriptors-mips.cc
+++ b/src/mips/interface-descriptors-mips.cc
@@ -152,6 +152,15 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // register state
+  // cp -- context
+  Register registers[] = {cp};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // register state
diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc
index a5af1b8..deb3ff6 100644
--- a/src/mips/macro-assembler-mips.cc
+++ b/src/mips/macro-assembler-mips.cc
@@ -2037,18 +2037,26 @@
         b(offset);
         break;
       case eq:
-        // We don't want any other register but scratch clobbered.
-        DCHECK(!scratch.is(rs));
-        r2 = scratch;
-        li(r2, rt);
-        beq(rs, r2, offset);
+        if (rt.imm32_ == 0) {
+          beq(rs, zero_reg, offset);
+        } else {
+          // We don't want any other register but scratch clobbered.
+          DCHECK(!scratch.is(rs));
+          r2 = scratch;
+          li(r2, rt);
+          beq(rs, r2, offset);
+        }
         break;
       case ne:
-        // We don't want any other register but scratch clobbered.
-        DCHECK(!scratch.is(rs));
-        r2 = scratch;
-        li(r2, rt);
-        bne(rs, r2, offset);
+        if (rt.imm32_ == 0) {
+          bne(rs, zero_reg, offset);
+        } else {
+          // We don't want any other register but scratch clobbered.
+          DCHECK(!scratch.is(rs));
+          r2 = scratch;
+          li(r2, rt);
+          bne(rs, r2, offset);
+        }
         break;
       // Signed comparison.
       case greater:
@@ -2290,18 +2298,28 @@
         b(offset);
         break;
       case eq:
-        DCHECK(!scratch.is(rs));
-        r2 = scratch;
-        li(r2, rt);
-        offset = shifted_branch_offset(L, false);
-        beq(rs, r2, offset);
+        if (rt.imm32_ == 0) {
+          offset = shifted_branch_offset(L, false);
+          beq(rs, zero_reg, offset);
+        } else {
+          DCHECK(!scratch.is(rs));
+          r2 = scratch;
+          li(r2, rt);
+          offset = shifted_branch_offset(L, false);
+          beq(rs, r2, offset);
+        }
         break;
       case ne:
-        DCHECK(!scratch.is(rs));
-        r2 = scratch;
-        li(r2, rt);
-        offset = shifted_branch_offset(L, false);
-        bne(rs, r2, offset);
+        if (rt.imm32_ == 0) {
+          offset = shifted_branch_offset(L, false);
+          bne(rs, zero_reg, offset);
+        } else {
+          DCHECK(!scratch.is(rs));
+          r2 = scratch;
+          li(r2, rt);
+          offset = shifted_branch_offset(L, false);
+          bne(rs, r2, offset);
+        }
         break;
       // Signed comparison.
       case greater:
diff --git a/src/mips64/interface-descriptors-mips64.cc b/src/mips64/interface-descriptors-mips64.cc
index a14a08b..44c8dff 100644
--- a/src/mips64/interface-descriptors-mips64.cc
+++ b/src/mips64/interface-descriptors-mips64.cc
@@ -152,6 +152,15 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // register state
+  // cp -- context
+  Register registers[] = {cp};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // register state
diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js
index c36d6fd..da031d3 100644
--- a/src/mirror-debugger.js
+++ b/src/mirror-debugger.js
@@ -85,6 +85,8 @@
     mirror = new MapMirror(value);
   } else if (IS_SET(value) || IS_WEAKSET(value)) {
     mirror = new SetMirror(value);
+  } else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
+    mirror = new IteratorMirror(value);
   } else if (ObjectIsPromise(value)) {
     mirror = new PromiseMirror(value);
   } else if (IS_GENERATOR(value)) {
@@ -163,6 +165,7 @@
 var PROMISE_TYPE = 'promise';
 var MAP_TYPE = 'map';
 var SET_TYPE = 'set';
+var ITERATOR_TYPE = 'iterator';
 var GENERATOR_TYPE = 'generator';
 
 // Maximum length when sending strings through the JSON protocol.
@@ -217,6 +220,7 @@
 //         - PromiseMirror
 //         - MapMirror
 //         - SetMirror
+//         - IteratorMirror
 //         - GeneratorMirror
 //     - PropertyMirror
 //     - InternalPropertyMirror
@@ -456,6 +460,15 @@
 
 
 /**
+ * Check whether the mirror reflects an iterator.
+ * @returns {boolean} True if the mirror reflects an iterator
+ */
+Mirror.prototype.isIterator = function() {
+  return this instanceof IteratorMirror;
+};
+
+
+/**
  * Allocate a handle id for this object.
  */
 Mirror.prototype.allocateHandle_ = function() {
@@ -1343,6 +1356,16 @@
 inherits(SetMirror, ObjectMirror);
 
 
+function IteratorGetValues_(iter, next_function) {
+  var result = [];
+  var next;
+  while (!(next = %_CallFunction(iter, next_function)).done) {
+    result.push(next.value);
+  }
+  return result;
+}
+
+
 /**
  * Returns an array of elements of a set.
  * This will keep elements alive for WeakSets.
@@ -1354,13 +1377,31 @@
     return %GetWeakSetValues(this.value_);
   }
 
-  var result = [];
   var iter = %_CallFunction(this.value_, builtins.SetValues);
-  var next;
-  while (!(next = iter.next()).done) {
-    result.push(next.value);
+  return IteratorGetValues_(iter, builtins.SetIteratorNextJS);
+};
+
+
+function IteratorMirror(value) {
+  %_CallFunction(this, value, ITERATOR_TYPE, ObjectMirror);
+}
+inherits(IteratorMirror, ObjectMirror);
+
+
+/**
+ * Returns a preview of elements of an iterator.
+ * Does not change the backing iterator state.
+ *
+ * @returns {Array.<Object>} Array of elements of an iterator.
+ */
+IteratorMirror.prototype.preview = function() {
+  if (IS_MAP_ITERATOR(this.value_)) {
+    return IteratorGetValues_(%MapIteratorClone(this.value_),
+                              builtins.MapIteratorNextJS);
+  } else if (IS_SET_ITERATOR(this.value_)) {
+    return IteratorGetValues_(%SetIteratorClone(this.value_),
+                              builtins.SetIteratorNextJS);
   }
-  return result;
 };
 
 
diff --git a/src/objects-inl.h b/src/objects-inl.h
index b25ac5c..6d0f8d4 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2801,8 +2801,10 @@
 // Perform a binary search in a fixed array. Low and high are entry indices. If
 // there are three entries in this array it should be called with low=0 and
 // high=2.
-template<SearchMode search_mode, typename T>
-int BinarySearch(T* array, Name* name, int low, int high, int valid_entries) {
+template <SearchMode search_mode, typename T>
+int BinarySearch(T* array, Name* name, int low, int high, int valid_entries,
+                 int* out_insertion_index) {
+  DCHECK(search_mode == ALL_ENTRIES || out_insertion_index == NULL);
   uint32_t hash = name->Hash();
   int limit = high;
 
@@ -2823,7 +2825,13 @@
   for (; low <= limit; ++low) {
     int sort_index = array->GetSortedKeyIndex(low);
     Name* entry = array->GetKey(sort_index);
-    if (entry->Hash() != hash) break;
+    uint32_t current_hash = entry->Hash();
+    if (current_hash != hash) {
+      if (out_insertion_index != NULL) {
+        *out_insertion_index = sort_index + (current_hash > hash ? 0 : 1);
+      }
+      return T::kNotFound;
+    }
     if (entry->Equals(name)) {
       if (search_mode == ALL_ENTRIES || sort_index < valid_entries) {
         return sort_index;
@@ -2832,37 +2840,45 @@
     }
   }
 
+  if (out_insertion_index != NULL) *out_insertion_index = limit + 1;
   return T::kNotFound;
 }
 
 
 // Perform a linear search in this fixed array. len is the number of entry
 // indices that are valid.
-template<SearchMode search_mode, typename T>
-int LinearSearch(T* array, Name* name, int len, int valid_entries) {
+template <SearchMode search_mode, typename T>
+int LinearSearch(T* array, Name* name, int len, int valid_entries,
+                 int* out_insertion_index) {
   uint32_t hash = name->Hash();
   if (search_mode == ALL_ENTRIES) {
     for (int number = 0; number < len; number++) {
       int sorted_index = array->GetSortedKeyIndex(number);
       Name* entry = array->GetKey(sorted_index);
       uint32_t current_hash = entry->Hash();
-      if (current_hash > hash) break;
+      if (current_hash > hash) {
+        if (out_insertion_index != NULL) *out_insertion_index = sorted_index;
+        return T::kNotFound;
+      }
       if (current_hash == hash && entry->Equals(name)) return sorted_index;
     }
+    if (out_insertion_index != NULL) *out_insertion_index = len;
+    return T::kNotFound;
   } else {
     DCHECK(len >= valid_entries);
+    DCHECK_EQ(NULL, out_insertion_index);  // Not supported here.
     for (int number = 0; number < valid_entries; number++) {
       Name* entry = array->GetKey(number);
       uint32_t current_hash = entry->Hash();
       if (current_hash == hash && entry->Equals(name)) return number;
     }
+    return T::kNotFound;
   }
-  return T::kNotFound;
 }
 
 
-template<SearchMode search_mode, typename T>
-int Search(T* array, Name* name, int valid_entries) {
+template <SearchMode search_mode, typename T>
+int Search(T* array, Name* name, int valid_entries, int* out_insertion_index) {
   if (search_mode == VALID_ENTRIES) {
     SLOW_DCHECK(array->IsSortedNoDuplicates(valid_entries));
   } else {
@@ -2870,7 +2886,10 @@
   }
 
   int nof = array->number_of_entries();
-  if (nof == 0) return T::kNotFound;
+  if (nof == 0) {
+    if (out_insertion_index != NULL) *out_insertion_index = 0;
+    return T::kNotFound;
+  }
 
   // Fast case: do linear search for small arrays.
   const int kMaxElementsForLinearSearch = 8;
@@ -2878,16 +2897,18 @@
        nof <= kMaxElementsForLinearSearch) ||
       (search_mode == VALID_ENTRIES &&
        valid_entries <= (kMaxElementsForLinearSearch * 3))) {
-    return LinearSearch<search_mode>(array, name, nof, valid_entries);
+    return LinearSearch<search_mode>(array, name, nof, valid_entries,
+                                     out_insertion_index);
   }
 
   // Slow case: perform binary search.
-  return BinarySearch<search_mode>(array, name, 0, nof - 1, valid_entries);
+  return BinarySearch<search_mode>(array, name, 0, nof - 1, valid_entries,
+                                   out_insertion_index);
 }
 
 
 int DescriptorArray::Search(Name* name, int valid_descriptors) {
-  return internal::Search<VALID_ENTRIES>(this, name, valid_descriptors);
+  return internal::Search<VALID_ENTRIES>(this, name, valid_descriptors, NULL);
 }
 
 
diff --git a/src/objects.h b/src/objects.h
index 0f2cfc0..d12896f 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -3145,12 +3145,9 @@
 
 enum SearchMode { ALL_ENTRIES, VALID_ENTRIES };
 
-template<SearchMode search_mode, typename T>
-inline int LinearSearch(T* array, Name* name, int len, int valid_entries);
-
-
-template<SearchMode search_mode, typename T>
-inline int Search(T* array, Name* name, int valid_entries = 0);
+template <SearchMode search_mode, typename T>
+inline int Search(T* array, Name* name, int valid_entries = 0,
+                  int* out_insertion_index = NULL);
 
 
 // HashTable is a subclass of FixedArray that implements a hash table
diff --git a/src/runtime/runtime-collections.cc b/src/runtime/runtime-collections.cc
index c1a63dc..45ac41c 100644
--- a/src/runtime/runtime-collections.cc
+++ b/src/runtime/runtime-collections.cc
@@ -92,6 +92,20 @@
 }
 
 
+RUNTIME_FUNCTION(Runtime_SetIteratorClone) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
+
+  Handle<JSSetIterator> result = isolate->factory()->NewJSSetIterator();
+  result->set_table(holder->table());
+  result->set_index(Smi::FromInt(Smi::cast(holder->index())->value()));
+  result->set_kind(Smi::FromInt(Smi::cast(holder->kind())->value()));
+
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
   SealHandleScope shs(isolate);
   DCHECK(args.length() == 2);
@@ -197,6 +211,20 @@
 }
 
 
+RUNTIME_FUNCTION(Runtime_MapIteratorClone) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
+
+  Handle<JSMapIterator> result = isolate->factory()->NewJSMapIterator();
+  result->set_table(holder->table());
+  result->set_index(Smi::FromInt(Smi::cast(holder->index())->value()));
+  result->set_kind(Smi::FromInt(Smi::cast(holder->kind())->value()));
+
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 599a024..448010a 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -310,6 +310,7 @@
   F(SetGetSize, 1, 1)                                  \
                                                        \
   F(SetIteratorInitialize, 3, 1)                       \
+  F(SetIteratorClone, 1, 1)                            \
   F(SetIteratorNext, 2, 1)                             \
                                                        \
   /* Harmony maps */                                   \
@@ -322,6 +323,7 @@
   F(MapGetSize, 1, 1)                                  \
                                                        \
   F(MapIteratorInitialize, 3, 1)                       \
+  F(MapIteratorClone, 1, 1)                            \
   F(MapIteratorNext, 2, 1)                             \
                                                        \
   /* Harmony weak maps and sets */                     \
diff --git a/src/symbol.js b/src/symbol.js
index d6ac527..b4ae708 100644
--- a/src/symbol.js
+++ b/src/symbol.js
@@ -73,7 +73,7 @@
 
   // TODO(arv): Proxies use a shared trap for String and Symbol keys.
 
-  return ObjectGetOwnPropertyKeys(obj, true);
+  return ObjectGetOwnPropertyKeys(obj, PROPERTY_ATTRIBUTES_STRING);
 }
 
 
diff --git a/src/transitions-inl.h b/src/transitions-inl.h
index 6ed86a1..087755d 100644
--- a/src/transitions-inl.h
+++ b/src/transitions-inl.h
@@ -140,13 +140,16 @@
 }
 
 
-int TransitionArray::Search(Name* name) {
+int TransitionArray::Search(Name* name, int* out_insertion_index) {
   if (IsSimpleTransition()) {
     Name* key = GetKey(kSimpleTransitionIndex);
     if (key->Equals(name)) return kSimpleTransitionIndex;
+    if (out_insertion_index != NULL) {
+      *out_insertion_index = key->Hash() > name->Hash() ? 0 : 1;
+    }
     return kNotFound;
   }
-  return internal::Search<ALL_ENTRIES>(this, name);
+  return internal::Search<ALL_ENTRIES>(this, name, 0, out_insertion_index);
 }
 
 
diff --git a/src/transitions.cc b/src/transitions.cc
index 3e570ff..ec1b7f4 100644
--- a/src/transitions.cc
+++ b/src/transitions.cc
@@ -41,11 +41,6 @@
 }
 
 
-static bool InsertionPointFound(Name* key1, Name* key2) {
-  return key1->Hash() > key2->Hash();
-}
-
-
 Handle<TransitionArray> TransitionArray::NewWith(Handle<Map> map,
                                                  Handle<Name> name,
                                                  Handle<Map> target,
@@ -99,30 +94,36 @@
   int number_of_transitions = map->transitions()->number_of_transitions();
   int new_nof = number_of_transitions;
 
-  int insertion_index = map->transitions()->Search(*name);
-  if (insertion_index == kNotFound) ++new_nof;
+  int insertion_index = kNotFound;
+  int index = map->transitions()->Search(*name, &insertion_index);
+
+  if (index == kNotFound) {
+    ++new_nof;
+  } else {
+    insertion_index = index;
+  }
+  DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
+
   CHECK(new_nof <= kMaxNumberOfTransitions);
 
   if (new_nof <= map->transitions()->number_of_transitions_storage()) {
     DisallowHeapAllocation no_gc;
     TransitionArray* array = map->transitions();
 
-    if (insertion_index != kNotFound) {
-      array->SetTarget(insertion_index, *target);
+    if (index != kNotFound) {
+      array->SetTarget(index, *target);
       return handle(array);
     }
 
     array->SetNumberOfTransitions(new_nof);
-    uint32_t hash = name->Hash();
-    for (insertion_index = number_of_transitions; insertion_index > 0;
-         --insertion_index) {
-      Name* key = array->GetKey(insertion_index - 1);
-      if (key->Hash() <= hash) break;
-      array->SetKey(insertion_index, key);
-      array->SetTarget(insertion_index, array->GetTarget(insertion_index - 1));
+    for (index = number_of_transitions; index > insertion_index; --index) {
+      Name* key = array->GetKey(index - 1);
+      DCHECK(key->Hash() > name->Hash());
+      array->SetKey(index, key);
+      array->SetTarget(index, array->GetTarget(index - 1));
     }
-    array->SetKey(insertion_index, *name);
-    array->SetTarget(insertion_index, *target);
+    array->SetKey(index, *name);
+    array->SetTarget(index, *target);
     return handle(array);
   }
 
@@ -142,8 +143,14 @@
     number_of_transitions = array->number_of_transitions();
     new_nof = number_of_transitions;
 
-    insertion_index = array->Search(*name);
-    if (insertion_index == kNotFound) ++new_nof;
+    insertion_index = kNotFound;
+    index = array->Search(*name, &insertion_index);
+    if (index == kNotFound) {
+      ++new_nof;
+    } else {
+      insertion_index = index;
+    }
+    DCHECK(insertion_index >= 0 && insertion_index <= number_of_transitions);
 
     result->Shrink(ToKeyIndex(new_nof));
     result->SetNumberOfTransitions(new_nof);
@@ -153,18 +160,13 @@
     result->SetPrototypeTransitions(array->GetPrototypeTransitions());
   }
 
-  insertion_index = 0;
-  for (; insertion_index < number_of_transitions; ++insertion_index) {
-    if (InsertionPointFound(array->GetKey(insertion_index), *name)) break;
-    result->NoIncrementalWriteBarrierCopyFrom(
-        array, insertion_index, insertion_index);
+  DCHECK_NE(kNotFound, insertion_index);
+  for (int i = 0; i < insertion_index; ++i) {
+    result->NoIncrementalWriteBarrierCopyFrom(array, i, i);
   }
-
   result->NoIncrementalWriteBarrierSet(insertion_index, *name, *target);
-
-  for (; insertion_index < number_of_transitions; ++insertion_index) {
-    result->NoIncrementalWriteBarrierCopyFrom(
-        array, insertion_index, insertion_index + 1);
+  for (int i = insertion_index; i < number_of_transitions; ++i) {
+    result->NoIncrementalWriteBarrierCopyFrom(array, i, i + 1);
   }
 
   result->set_back_pointer_storage(array->back_pointer_storage());
diff --git a/src/transitions.h b/src/transitions.h
index b7e4ebe..c5f9a30 100644
--- a/src/transitions.h
+++ b/src/transitions.h
@@ -100,7 +100,7 @@
                                         SimpleTransitionFlag flag);
 
   // Search a transition for a given property name.
-  inline int Search(Name* name);
+  inline int Search(Name* name, int* out_insertion_index = NULL);
 
   // Allocates a TransitionArray.
   static Handle<TransitionArray> Allocate(Isolate* isolate,
diff --git a/src/v8natives.js b/src/v8natives.js
index 7636b70..6215ab0 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -252,8 +252,8 @@
 
 // ECMA-262 - 15.2.4.6
 function ObjectIsPrototypeOf(V) {
-  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.isPrototypeOf");
   if (!IS_SPEC_OBJECT(V)) return false;
+  CHECK_OBJECT_COERCIBLE(this, "Object.prototype.isPrototypeOf");
   return %IsInPrototypeChain(this, V);
 }
 
@@ -1038,16 +1038,14 @@
 }
 
 
-function ObjectGetOwnPropertyKeys(obj, symbolsOnly) {
+function ObjectGetOwnPropertyKeys(obj, filter) {
   var nameArrays = new InternalArray();
-  var filter = symbolsOnly ?
-      PROPERTY_ATTRIBUTES_STRING | PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL :
-      PROPERTY_ATTRIBUTES_SYMBOLIC;
+  filter |= PROPERTY_ATTRIBUTES_PRIVATE_SYMBOL;
 
   // Find all the indexed properties.
 
   // Only get own element names if we want to include string keys.
-  if (!symbolsOnly) {
+  if ((filter & PROPERTY_ATTRIBUTES_STRING) === 0) {
     var ownElementNames = %GetOwnElementNames(obj);
     for (var i = 0; i < ownElementNames.length; ++i) {
       ownElementNames[i] = %_NumberToString(ownElementNames[i]);
@@ -1089,10 +1087,12 @@
     var j = 0;
     for (var i = 0; i < propertyNames.length; ++i) {
       var name = propertyNames[i];
-      if (symbolsOnly) {
-        if (!IS_SYMBOL(name) || IS_PRIVATE(name)) continue;
+      if (IS_SYMBOL(name)) {
+        if ((filter & PROPERTY_ATTRIBUTES_SYMBOLIC) || IS_PRIVATE(name)) {
+          continue;
+        }
       } else {
-        if (IS_SYMBOL(name)) continue;
+        if (filter & PROPERTY_ATTRIBUTES_STRING) continue;
         name = ToString(name);
       }
       if (seenKeys[name]) continue;
@@ -1116,7 +1116,7 @@
     return ToNameArray(names, "getOwnPropertyNames", false);
   }
 
-  return ObjectGetOwnPropertyKeys(obj, false);
+  return ObjectGetOwnPropertyKeys(obj, PROPERTY_ATTRIBUTES_SYMBOLIC);
 }
 
 
diff --git a/src/version.cc b/src/version.cc
index 09aa482..70a1f9d 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
 // system so their names cannot be changed without changing the scripts.
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     30
-#define BUILD_NUMBER      32
+#define BUILD_NUMBER      33
 #define PATCH_LEVEL       0
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/interface-descriptors-x64.cc b/src/x64/interface-descriptors-x64.cc
index bc058ff..f19979d 100644
--- a/src/x64/interface-descriptors-x64.cc
+++ b/src/x64/interface-descriptors-x64.cc
@@ -155,6 +155,15 @@
 }
 
 
+void AllocateHeapNumberDescriptor::Initialize(
+    CallInterfaceDescriptorData* data) {
+  // register state
+  // rsi -- context
+  Register registers[] = {rsi};
+  data->Initialize(arraysize(registers), registers, nullptr);
+}
+
+
 void ArrayConstructorConstantArgCountDescriptor::Initialize(
     CallInterfaceDescriptorData* data) {
   // register state
diff --git a/test/cctest/compiler/test-codegen-deopt.cc b/test/cctest/compiler/test-codegen-deopt.cc
index c9a033b..974b423 100644
--- a/test/cctest/compiler/test-codegen-deopt.cc
+++ b/test/cctest/compiler/test-codegen-deopt.cc
@@ -75,18 +75,22 @@
     selector.SelectInstructions();
 
     if (FLAG_trace_turbo) {
+      PrintableInstructionSequence printable = {
+          RegisterConfiguration::ArchDefault(), code};
       os << "----- Instruction sequence before register allocation -----\n"
-         << *code;
+         << printable;
     }
 
     Frame frame;
-    RegisterAllocator allocator(RegisterAllocator::PlatformConfig(),
+    RegisterAllocator allocator(RegisterConfiguration::ArchDefault(),
                                 scope_->main_zone(), &frame, code);
     CHECK(allocator.Allocate());
 
     if (FLAG_trace_turbo) {
+      PrintableInstructionSequence printable = {
+          RegisterConfiguration::ArchDefault(), code};
       os << "----- Instruction sequence after register allocation -----\n"
-         << *code;
+         << printable;
     }
 
     compiler::CodeGenerator generator(&frame, linkage, code, &info);
diff --git a/test/cctest/compiler/test-control-reducer.cc b/test/cctest/compiler/test-control-reducer.cc
index 58437c5..67fdb68 100644
--- a/test/cctest/compiler/test-control-reducer.cc
+++ b/test/cctest/compiler/test-control-reducer.cc
@@ -973,6 +973,7 @@
 };
 
 
+// TODO(titzer): use the diamonds from src/compiler/diamond.h here.
 struct Diamond {
   Node* branch;
   Node* if_true;
diff --git a/test/cctest/compiler/test-gap-resolver.cc b/test/cctest/compiler/test-gap-resolver.cc
index aeaf8b9..ea6f4ee 100644
--- a/test/cctest/compiler/test-gap-resolver.cc
+++ b/test/cctest/compiler/test-gap-resolver.cc
@@ -65,7 +65,9 @@
       if (it != is.values_.begin()) os << " ";
       InstructionOperand source(it->first.first, it->first.second);
       InstructionOperand destination(it->second.first, it->second.second);
-      os << MoveOperands(&source, &destination);
+      MoveOperands mo(&source, &destination);
+      PrintableMoveOperands pmo = {RegisterConfiguration::ArchDefault(), &mo};
+      os << pmo;
     }
     return os;
   }
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 0e6d128..068a07e 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -8638,6 +8638,8 @@
 THREADED_TEST(ExceptionGetMessage) {
   LocalContext context;
   v8::HandleScope scope(context->GetIsolate());
+  v8::Handle<String> foo_str = v8_str("foo");
+  v8::Handle<String> message_str = v8_str("message");
 
   v8::V8::SetCaptureStackTraceForUncaughtExceptions(true);
 
@@ -8655,8 +8657,6 @@
   CHECK(try_catch.HasCaught());
 
   v8::Handle<v8::Value> error = try_catch.Exception();
-  v8::Handle<String> foo_str = v8_str("foo");
-  v8::Handle<String> message_str = v8_str("message");
   CHECK(error->IsObject());
   CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
 
@@ -8670,6 +8670,30 @@
   CHECK_EQ(2, stackTrace->GetFrameCount());
 
   v8::V8::SetCaptureStackTraceForUncaughtExceptions(false);
+
+  // Now check message location when SetCaptureStackTraceForUncaughtExceptions
+  // is false.
+  try_catch.Reset();
+
+  CompileRun(
+      "function f2() {\n"
+      "  return throwV8Exception();\n"
+      "};\n"
+      "f2();");
+  CHECK(try_catch.HasCaught());
+
+  error = try_catch.Exception();
+  CHECK(error->IsObject());
+  CHECK(error.As<v8::Object>()->Get(message_str)->Equals(foo_str));
+
+  message = v8::Exception::GetMessage(error);
+  CHECK(!message.IsEmpty());
+  CHECK_EQ(2, message->GetLineNumber());
+  CHECK_EQ(9, message->GetStartColumn());
+
+  // Should be empty stack trace.
+  stackTrace = message->GetStackTrace();
+  CHECK(stackTrace.IsEmpty());
 }
 
 
diff --git a/test/mjsunit/asm/uint32div.js b/test/mjsunit/asm/uint32div.js
index 54a2138..dcbb73b 100644
--- a/test/mjsunit/asm/uint32div.js
+++ b/test/mjsunit/asm/uint32div.js
@@ -22,8 +22,24 @@
 var divisors = [0, 1, 3, 4, 10, 42, 64, 100, 1024, 2147483647, 4294967295];
 for (var i in divisors) {
   var divisor = divisors[i];
-  var mod = Uint32Div(divisor);
+  var div = Uint32Div(divisor);
   for (var dividend = 0; dividend < 4294967296; dividend += 3999773) {
-    assertEquals((dividend / divisor) >>> 0, mod(dividend));
+    assertEquals((dividend / divisor) >>> 0, div(dividend));
+  }
+}
+
+var div = (function(stdlib, foreign, heap) {
+  "use asm";
+  function div(dividend, divisor) {
+    return (dividend >>> 0) / (divisor >>> 0) | 0;
+  }
+  return {div: div};
+})(stdlib, foreign, heap).div;
+
+for (var i in divisors) {
+  var divisor =  divisors[i];
+  for (var dividend = 0; dividend < 4294967296; dividend += 3999773) {
+    assertEquals((dividend >>> 0) / (divisor >>> 0) | 0,
+                 div(dividend, divisor));
   }
 }
diff --git a/test/mjsunit/es6/mirror-iterators.js b/test/mjsunit/es6/mirror-iterators.js
new file mode 100644
index 0000000..02fe7ff
--- /dev/null
+++ b/test/mjsunit/es6/mirror-iterators.js
@@ -0,0 +1,62 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug
+// Test the mirror object for collection iterators.
+
+function testIteratorMirror(iter, offset, expected) {
+  while (offset-- > 0) iter.next();
+
+  var mirror = debug.MakeMirror(iter);
+  assertTrue(mirror.isIterator());
+
+  var preview = mirror.preview();
+  assertArrayEquals(expected, preview);
+
+  // Check that iterator has not changed after taking preview.
+  var values = [];
+  for (var i of iter) values.push(i);
+  assertArrayEquals(expected, values);
+}
+
+var o1 = { foo: 1 };
+var o2 = { foo: 2 };
+
+var map = new Map();
+map.set(41, 42);
+map.set(o1, o2);
+
+testIteratorMirror(map.keys(), 0, [41, o1]);
+testIteratorMirror(map.values(), 0, [42, o2]);
+testIteratorMirror(map.entries(), 0, [[41, 42], [o1, o2]]);
+
+testIteratorMirror(map.keys(), 1, [o1]);
+testIteratorMirror(map.values(), 1, [o2]);
+testIteratorMirror(map.entries(), 1, [[o1, o2]]);
+
+testIteratorMirror(map.keys(), 2, []);
+testIteratorMirror(map.values(), 2, []);
+testIteratorMirror(map.entries(), 2, []);
+
+var set = new Set();
+set.add(41);
+set.add(42);
+set.add(o1);
+set.add(o2);
+
+testIteratorMirror(set.keys(), 0, [41, 42, o1, o2]);
+testIteratorMirror(set.values(), 0, [41, 42, o1, o2]);
+testIteratorMirror(set.entries(), 0, [[41, 41], [42, 42], [o1, o1], [o2, o2]]);
+
+testIteratorMirror(set.keys(), 1, [42, o1, o2]);
+testIteratorMirror(set.values(), 1, [42, o1, o2]);
+testIteratorMirror(set.entries(), 1, [[42, 42], [o1, o1], [o2, o2]]);
+
+testIteratorMirror(set.keys(), 3, [o2]);
+testIteratorMirror(set.values(), 3, [o2]);
+testIteratorMirror(set.entries(), 3, [[o2, o2]]);
+
+testIteratorMirror(set.keys(), 5, []);
+testIteratorMirror(set.values(), 5, []);
+testIteratorMirror(set.entries(), 5, []);
diff --git a/test/mjsunit/function-call.js b/test/mjsunit/function-call.js
index 88df353..fb91dcd 100644
--- a/test/mjsunit/function-call.js
+++ b/test/mjsunit/function-call.js
@@ -162,13 +162,10 @@
 
   var exception = false;
   try {
-    // We call all functions with no parameters, which means that essential
-    // parameters will have the undefined value.
-    // The test for whether the "this" value is null or undefined is always
-    // performed before access to the other parameters, so even if the
-    // undefined value is an invalid argument value, it mustn't change
-    // the result of the test.
-    should_throw_on_null_and_undefined[i].call(null);
+    // We need to pass a dummy object argument ({}) to these functions because
+    // of Object.prototype.isPrototypeOf's special behavior, see issue 3483
+    // for more details.
+    should_throw_on_null_and_undefined[i].call(null, {});
   } catch (e) {
     exception = true;
     checkExpectedMessage(e);
@@ -177,7 +174,7 @@
 
   exception = false;
   try {
-    should_throw_on_null_and_undefined[i].call(undefined);
+    should_throw_on_null_and_undefined[i].call(undefined, {});
   } catch (e) {
     exception = true;
     checkExpectedMessage(e);
@@ -186,7 +183,7 @@
 
   exception = false;
   try {
-    should_throw_on_null_and_undefined[i].apply(null);
+    should_throw_on_null_and_undefined[i].apply(null, [{}]);
   } catch (e) {
     exception = true;
     checkExpectedMessage(e);
@@ -195,7 +192,7 @@
 
   exception = false;
   try {
-    should_throw_on_null_and_undefined[i].apply(undefined);
+    should_throw_on_null_and_undefined[i].apply(undefined, [{}]);
   } catch (e) {
     exception = true;
     checkExpectedMessage(e);
@@ -248,7 +245,9 @@
 
 // Test that we still throw when calling with thisArg null or undefined
 // through an array mapping function.
-var array = [1,2,3,4,5];
+// We need to make sure that the elements of `array` are all object values,
+// see issue 3483 for more details.
+var array = [{}, [], new Number, new Map, new WeakSet];
 for (var j = 0; j < mapping_functions.length; j++) {
   for (var i = 0; i < should_throw_on_null_and_undefined.length; i++) {
     exception = false;
diff --git a/test/mjsunit/harmony/classes.js b/test/mjsunit/harmony/classes.js
index c5c2b72..59371e4 100644
--- a/test/mjsunit/harmony/classes.js
+++ b/test/mjsunit/harmony/classes.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Flags: --harmony
+// Flags: --harmony-classes
 
 (function TestBasics() {
   var C = class C {}
diff --git a/test/mjsunit/harmony/regress/regress-343928.js b/test/mjsunit/harmony/regress/regress-343928.js
index b102ab9..f2ff371 100644
--- a/test/mjsunit/harmony/regress/regress-343928.js
+++ b/test/mjsunit/harmony/regress/regress-343928.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Flags: --harmony --expose-debug-as=debug
+// Flags: --harmony-modules --expose-debug-as=debug
 
 (function () {  // Scope for utility functions.
   escaping_function = function(object) {
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index 9e3435d..1185a11 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -236,6 +236,8 @@
   # TODO(mstarzinger): Takes too long with TF.
   'array-sort': [PASS, NO_VARIANTS],
   'regress/regress-91008': [PASS, NO_VARIANTS],
+  'regress/regress-417709a': [PASS, ['arch == arm64', NO_VARIANTS]],
+  'regress/regress-transcendental': [PASS, ['arch == arm64', NO_VARIANTS]],
   'compiler/osr-regress-max-locals': [PASS, NO_VARIANTS],
   'math-floor-of-div': [PASS, NO_VARIANTS],
   'unicodelctest': [PASS, NO_VARIANTS],
diff --git a/test/mjsunit/polymorph-arrays.js b/test/mjsunit/polymorph-arrays.js
index 2bb0433..6a05c9f 100644
--- a/test/mjsunit/polymorph-arrays.js
+++ b/test/mjsunit/polymorph-arrays.js
@@ -36,7 +36,7 @@
   for (var i = 0; i < 10; ++i ){
     a[i] = i;
   }
-  a[5000000] = 256;
+  a[200000] = 256;
   return %NormalizeElements(a);
 }
 
@@ -115,7 +115,7 @@
     var sparse_object_array = new Object;
     var js_array = new Array(10);
     var sparse_js_array = [];
-    sparse_js_array.length = 5000001;
+    sparse_js_array.length = 200001;
 
     init_array(object_array);
     init_array(js_array);
@@ -134,7 +134,8 @@
   var sparse_object_array = new Object;
   var js_array = new Array(10);
   var sparse_js_array = %NormalizeElements([]);
-  sparse_js_array.length = 5000001;
+  sparse_js_array.length = 200001;
+  assertTrue(%HasDictionaryElements(sparse_js_array));
 
   init_array(object_array);
   init_array(js_array);
diff --git a/test/mjsunit/regress/regress-3483.js b/test/mjsunit/regress/regress-3483.js
new file mode 100644
index 0000000..dec95c4
--- /dev/null
+++ b/test/mjsunit/regress/regress-3483.js
@@ -0,0 +1,30 @@
+// Copyright 2014 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.
+
+assertFalse(Object.prototype.isPrototypeOf.call());
+assertFalse(Object.prototype.isPrototypeOf.call(null, 1));
+assertFalse(Object.prototype.isPrototypeOf.call(undefined, 1));
diff --git a/test/test262-es6/test262-es6.status b/test/test262-es6/test262-es6.status
index b1db063..3791dfd 100644
--- a/test/test262-es6/test262-es6.status
+++ b/test/test262-es6/test262-es6.status
@@ -50,6 +50,25 @@
 
   ###################### MISSING ES6 FEATURES #######################
 
+  # Array.fill (currently requires --harmony-arrays)
+  'S22.1.3.6_T1': [FAIL],
+
+  # Array.find (currently requires --harmony-arrays)
+  'S22.1.2.3_T1': [FAIL],
+  'S22.1.2.3_T2': [FAIL],
+  'Array.prototype.find_empty-array-undefined': [FAIL],
+  'Array.prototype.find_length-property': [FAIL],
+  'Array.prototype.find_modify-after-start': [FAIL],
+  'Array.prototype.find_non-returning-predicate': [FAIL],
+  'Array.prototype.find_predicate-arguments': [FAIL],
+  'Array.prototype.find_push-after-start': [FAIL],
+  'Array.prototype.find_remove-after-start': [FAIL],
+  'Array.prototype.find_return-found-value': [FAIL],
+  'Array.prototype.find_skip-empty': [FAIL],
+  'Array.prototype.find_this-defined': [FAIL],
+  'Array.prototype.find_this-is-object': [FAIL],
+  'Array.prototype.find_this-undefined': [FAIL],
+
   # Array.from
   'S22.1.2.1_T1': [FAIL],
   'S22.1.2.1_T2': [FAIL],
diff --git a/test/unittests/compiler/change-lowering-unittest.cc b/test/unittests/compiler/change-lowering-unittest.cc
index 5f14b8e..a6a0db7 100644
--- a/test/unittests/compiler/change-lowering-unittest.cc
+++ b/test/unittests/compiler/change-lowering-unittest.cc
@@ -79,13 +79,9 @@
 
   Matcher<Node*> IsAllocateHeapNumber(const Matcher<Node*>& effect_matcher,
                                       const Matcher<Node*>& control_matcher) {
-    return IsCall(
-        _, IsHeapConstant(Unique<HeapObject>::CreateImmovable(
-               CEntryStub(isolate(), 1).GetCode())),
-        IsExternalConstant(ExternalReference(
-            Runtime::FunctionForId(Runtime::kAllocateHeapNumber), isolate())),
-        IsInt32Constant(0), IsNumberConstant(0.0), effect_matcher,
-        control_matcher);
+    return IsCall(_, IsHeapConstant(Unique<HeapObject>::CreateImmovable(
+                         AllocateHeapNumberStub(isolate()).GetCode())),
+                  IsNumberConstant(0.0), effect_matcher, control_matcher);
   }
   Matcher<Node*> IsLoadHeapNumber(const Matcher<Node*>& value_matcher,
                                   const Matcher<Node*>& control_matcher) {
diff --git a/test/unittests/compiler/diamond-unittest.cc b/test/unittests/compiler/diamond-unittest.cc
new file mode 100644
index 0000000..c14886f
--- /dev/null
+++ b/test/unittests/compiler/diamond-unittest.cc
@@ -0,0 +1,161 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/diamond.h"
+#include "test/unittests/compiler/graph-unittest.h"
+#include "test/unittests/compiler/node-test-utils.h"
+#include "testing/gmock-support.h"
+
+using testing::AllOf;
+using testing::Capture;
+using testing::CaptureEq;
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class DiamondTest : public GraphTest {
+ public:
+  DiamondTest() : GraphTest(5) {}
+};
+
+
+TEST_F(DiamondTest, SimpleDiamond) {
+  Node* p = Parameter(0);
+  Diamond d(graph(), common(), p);
+  EXPECT_THAT(d.branch, IsBranch(p, graph()->start()));
+  EXPECT_THAT(d.if_true, IsIfTrue(d.branch));
+  EXPECT_THAT(d.if_false, IsIfFalse(d.branch));
+  EXPECT_THAT(d.merge, IsMerge(d.if_true, d.if_false));
+}
+
+
+TEST_F(DiamondTest, DiamondChainDiamond) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Diamond d0(graph(), common(), p0);
+  Diamond d1(graph(), common(), p1);
+  d1.Chain(d0);
+  EXPECT_THAT(d1.branch, IsBranch(p1, d0.merge));
+  EXPECT_THAT(d0.branch, IsBranch(p0, graph()->start()));
+}
+
+
+TEST_F(DiamondTest, DiamondChainNode) {
+  Node* p1 = Parameter(1);
+  Diamond d1(graph(), common(), p1);
+  Node* other = graph()->NewNode(common()->Merge(0));
+  d1.Chain(other);
+  EXPECT_THAT(d1.branch, IsBranch(p1, other));
+}
+
+
+TEST_F(DiamondTest, DiamondChainN) {
+  Node* params[5] = {Parameter(0), Parameter(1), Parameter(2), Parameter(3),
+                     Parameter(4)};
+  Diamond d[5] = {Diamond(graph(), common(), params[0]),
+                  Diamond(graph(), common(), params[1]),
+                  Diamond(graph(), common(), params[2]),
+                  Diamond(graph(), common(), params[3]),
+                  Diamond(graph(), common(), params[4])};
+
+  for (int i = 1; i < 5; i++) {
+    d[i].Chain(d[i - 1]);
+    EXPECT_THAT(d[i].branch, IsBranch(params[i], d[i - 1].merge));
+  }
+}
+
+
+TEST_F(DiamondTest, DiamondNested_true) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Diamond d0(graph(), common(), p0);
+  Diamond d1(graph(), common(), p1);
+
+  d1.Nest(d0, true);
+
+  EXPECT_THAT(d0.branch, IsBranch(p0, graph()->start()));
+  EXPECT_THAT(d0.if_true, IsIfTrue(d0.branch));
+  EXPECT_THAT(d0.if_false, IsIfFalse(d0.branch));
+  EXPECT_THAT(d0.merge, IsMerge(d1.merge, d0.if_false));
+
+  EXPECT_THAT(d1.branch, IsBranch(p1, d0.if_true));
+  EXPECT_THAT(d1.if_true, IsIfTrue(d1.branch));
+  EXPECT_THAT(d1.if_false, IsIfFalse(d1.branch));
+  EXPECT_THAT(d1.merge, IsMerge(d1.if_true, d1.if_false));
+}
+
+
+TEST_F(DiamondTest, DiamondNested_false) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Diamond d0(graph(), common(), p0);
+  Diamond d1(graph(), common(), p1);
+
+  d1.Nest(d0, false);
+
+  EXPECT_THAT(d0.branch, IsBranch(p0, graph()->start()));
+  EXPECT_THAT(d0.if_true, IsIfTrue(d0.branch));
+  EXPECT_THAT(d0.if_false, IsIfFalse(d0.branch));
+  EXPECT_THAT(d0.merge, IsMerge(d0.if_true, d1.merge));
+
+  EXPECT_THAT(d1.branch, IsBranch(p1, d0.if_false));
+  EXPECT_THAT(d1.if_true, IsIfTrue(d1.branch));
+  EXPECT_THAT(d1.if_false, IsIfFalse(d1.branch));
+  EXPECT_THAT(d1.merge, IsMerge(d1.if_true, d1.if_false));
+}
+
+
+TEST_F(DiamondTest, DiamondPhis) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Node* p2 = Parameter(2);
+  Diamond d(graph(), common(), p0);
+
+  MachineType types[] = {kMachAnyTagged, kMachUint32, kMachInt32};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* phi = d.Phi(types[i], p1, p2);
+
+    EXPECT_THAT(d.branch, IsBranch(p0, graph()->start()));
+    EXPECT_THAT(d.if_true, IsIfTrue(d.branch));
+    EXPECT_THAT(d.if_false, IsIfFalse(d.branch));
+    EXPECT_THAT(d.merge, IsMerge(d.if_true, d.if_false));
+    EXPECT_THAT(phi, IsPhi(types[i], p1, p2, d.merge));
+  }
+}
+
+
+TEST_F(DiamondTest, DiamondEffectPhis) {
+  Node* p0 = Parameter(0);
+  Node* p1 = Parameter(1);
+  Node* p2 = Parameter(2);
+  Diamond d(graph(), common(), p0);
+
+  Node* phi = d.EffectPhi(p1, p2);
+
+  EXPECT_THAT(d.branch, IsBranch(p0, graph()->start()));
+  EXPECT_THAT(d.if_true, IsIfTrue(d.branch));
+  EXPECT_THAT(d.if_false, IsIfFalse(d.branch));
+  EXPECT_THAT(d.merge, IsMerge(d.if_true, d.if_false));
+  EXPECT_THAT(phi, IsEffectPhi(p1, p2, d.merge));
+}
+
+
+TEST_F(DiamondTest, BranchHint) {
+  Diamond dn(graph(), common(), Parameter(0));
+  CHECK(BranchHint::kNone == BranchHintOf(dn.branch->op()));
+
+  Diamond dt(graph(), common(), Parameter(0), BranchHint::kTrue);
+  CHECK(BranchHint::kTrue == BranchHintOf(dt.branch->op()));
+
+  Diamond df(graph(), common(), Parameter(0), BranchHint::kFalse);
+  CHECK(BranchHint::kFalse == BranchHintOf(df.branch->op()));
+}
+
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/test/unittests/compiler/instruction-selector-unittest.cc b/test/unittests/compiler/instruction-selector-unittest.cc
index 57ce690..c79a9e4 100644
--- a/test/unittests/compiler/instruction-selector-unittest.cc
+++ b/test/unittests/compiler/instruction-selector-unittest.cc
@@ -46,8 +46,10 @@
   selector.SelectInstructions();
   if (FLAG_trace_turbo) {
     OFStream out(stdout);
+    PrintableInstructionSequence printable = {
+        RegisterConfiguration::ArchDefault(), &sequence};
     out << "=== Code sequence after instruction selection ===" << std::endl
-        << sequence;
+        << printable;
   }
   Stream s;
   // Map virtual registers.
diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc
index 4c1e10b..5f1dea3 100644
--- a/test/unittests/compiler/node-test-utils.cc
+++ b/test/unittests/compiler/node-test-utils.cc
@@ -300,6 +300,45 @@
 };
 
 
+class IsEffectPhiMatcher FINAL : public NodeMatcher {
+ public:
+  IsEffectPhiMatcher(const Matcher<Node*>& effect0_matcher,
+                     const Matcher<Node*>& effect1_matcher,
+                     const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kEffectPhi),
+        effect0_matcher_(effect0_matcher),
+        effect1_matcher_(effect1_matcher),
+        control_matcher_(control_matcher) {}
+
+  virtual void DescribeTo(std::ostream* os) const OVERRIDE {
+    NodeMatcher::DescribeTo(os);
+    *os << "), effect0 (";
+    effect0_matcher_.DescribeTo(os);
+    *os << "), effect1 (";
+    effect1_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  virtual bool MatchAndExplain(Node* node,
+                               MatchResultListener* listener) const OVERRIDE {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetEffectInput(node, 0),
+                                 "effect0", effect0_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetEffectInput(node, 1),
+                                 "effect1", effect1_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<Node*> effect0_matcher_;
+  const Matcher<Node*> effect1_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
 class IsProjectionMatcher FINAL : public NodeMatcher {
  public:
   IsProjectionMatcher(const Matcher<size_t>& index_matcher,
@@ -332,15 +371,66 @@
 };
 
 
-class IsCallMatcher FINAL : public NodeMatcher {
+class IsCall2Matcher FINAL : public NodeMatcher {
  public:
-  IsCallMatcher(const Matcher<CallDescriptor*>& descriptor_matcher,
-                const Matcher<Node*>& value0_matcher,
-                const Matcher<Node*>& value1_matcher,
-                const Matcher<Node*>& value2_matcher,
-                const Matcher<Node*>& value3_matcher,
-                const Matcher<Node*>& effect_matcher,
-                const Matcher<Node*>& control_matcher)
+  IsCall2Matcher(const Matcher<CallDescriptor*>& descriptor_matcher,
+                 const Matcher<Node*>& value0_matcher,
+                 const Matcher<Node*>& value1_matcher,
+                 const Matcher<Node*>& effect_matcher,
+                 const Matcher<Node*>& control_matcher)
+      : NodeMatcher(IrOpcode::kCall),
+        descriptor_matcher_(descriptor_matcher),
+        value0_matcher_(value0_matcher),
+        value1_matcher_(value1_matcher),
+        effect_matcher_(effect_matcher),
+        control_matcher_(control_matcher) {}
+
+  virtual void DescribeTo(std::ostream* os) const OVERRIDE {
+    NodeMatcher::DescribeTo(os);
+    *os << " whose value0 (";
+    value0_matcher_.DescribeTo(os);
+    *os << ") and value1 (";
+    value1_matcher_.DescribeTo(os);
+    *os << ") and effect (";
+    effect_matcher_.DescribeTo(os);
+    *os << ") and control (";
+    control_matcher_.DescribeTo(os);
+    *os << ")";
+  }
+
+  virtual bool MatchAndExplain(Node* node,
+                               MatchResultListener* listener) const OVERRIDE {
+    return (NodeMatcher::MatchAndExplain(node, listener) &&
+            PrintMatchAndExplain(OpParameter<CallDescriptor*>(node),
+                                 "descriptor", descriptor_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0),
+                                 "value0", value0_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1),
+                                 "value1", value1_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetEffectInput(node), "effect",
+                                 effect_matcher_, listener) &&
+            PrintMatchAndExplain(NodeProperties::GetControlInput(node),
+                                 "control", control_matcher_, listener));
+  }
+
+ private:
+  const Matcher<CallDescriptor*> descriptor_matcher_;
+  const Matcher<Node*> value0_matcher_;
+  const Matcher<Node*> value1_matcher_;
+  const Matcher<Node*> effect_matcher_;
+  const Matcher<Node*> control_matcher_;
+};
+
+
+class IsCall4Matcher FINAL : public NodeMatcher {
+ public:
+  IsCall4Matcher(const Matcher<CallDescriptor*>& descriptor_matcher,
+                 const Matcher<Node*>& value0_matcher,
+                 const Matcher<Node*>& value1_matcher,
+                 const Matcher<Node*>& value2_matcher,
+                 const Matcher<Node*>& value3_matcher,
+                 const Matcher<Node*>& effect_matcher,
+                 const Matcher<Node*>& control_matcher)
       : NodeMatcher(IrOpcode::kCall),
         descriptor_matcher_(descriptor_matcher),
         value0_matcher_(value0_matcher),
@@ -829,6 +919,14 @@
 }
 
 
+Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
+                           const Matcher<Node*>& effect1_matcher,
+                           const Matcher<Node*>& merge_matcher) {
+  return MakeMatcher(
+      new IsEffectPhiMatcher(effect0_matcher, effect1_matcher, merge_matcher));
+}
+
+
 Matcher<Node*> IsProjection(const Matcher<size_t>& index_matcher,
                             const Matcher<Node*>& base_matcher) {
   return MakeMatcher(new IsProjectionMatcher(index_matcher, base_matcher));
@@ -838,11 +936,22 @@
 Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
                       const Matcher<Node*>& value0_matcher,
                       const Matcher<Node*>& value1_matcher,
+                      const Matcher<Node*>& effect_matcher,
+                      const Matcher<Node*>& control_matcher) {
+  return MakeMatcher(new IsCall2Matcher(descriptor_matcher, value0_matcher,
+                                        value1_matcher, effect_matcher,
+                                        control_matcher));
+}
+
+
+Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
+                      const Matcher<Node*>& value0_matcher,
+                      const Matcher<Node*>& value1_matcher,
                       const Matcher<Node*>& value2_matcher,
                       const Matcher<Node*>& value3_matcher,
                       const Matcher<Node*>& effect_matcher,
                       const Matcher<Node*>& control_matcher) {
-  return MakeMatcher(new IsCallMatcher(
+  return MakeMatcher(new IsCall4Matcher(
       descriptor_matcher, value0_matcher, value1_matcher, value2_matcher,
       value3_matcher, effect_matcher, control_matcher));
 }
diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h
index 7f153bd..89fcc4b 100644
--- a/test/unittests/compiler/node-test-utils.h
+++ b/test/unittests/compiler/node-test-utils.h
@@ -56,11 +56,19 @@
                      const Matcher<Node*>& value0_matcher,
                      const Matcher<Node*>& value1_matcher,
                      const Matcher<Node*>& merge_matcher);
+Matcher<Node*> IsEffectPhi(const Matcher<Node*>& effect0_matcher,
+                           const Matcher<Node*>& effect1_matcher,
+                           const Matcher<Node*>& merge_matcher);
 Matcher<Node*> IsProjection(const Matcher<size_t>& index_matcher,
                             const Matcher<Node*>& base_matcher);
 Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
                       const Matcher<Node*>& value0_matcher,
                       const Matcher<Node*>& value1_matcher,
+                      const Matcher<Node*>& effect_matcher,
+                      const Matcher<Node*>& control_matcher);
+Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
+                      const Matcher<Node*>& value0_matcher,
+                      const Matcher<Node*>& value1_matcher,
                       const Matcher<Node*>& value2_matcher,
                       const Matcher<Node*>& value3_matcher,
                       const Matcher<Node*>& effect_matcher,
diff --git a/test/unittests/compiler/register-allocator-unittest.cc b/test/unittests/compiler/register-allocator-unittest.cc
index 5c61708..752999f 100644
--- a/test/unittests/compiler/register-allocator-unittest.cc
+++ b/test/unittests/compiler/register-allocator-unittest.cc
@@ -15,29 +15,22 @@
 
 namespace {
 
-static const char* general_register_names_[kMaxGeneralRegisters];
-static const char* double_register_names_[kMaxDoubleRegisters];
-static char register_names_[10 * (kMaxGeneralRegisters + kMaxDoubleRegisters)];
-
-
-static const char* GeneralRegisterName(int allocation_index) {
-  return general_register_names_[allocation_index];
-}
-
-
-static const char* DoubleRegisterName(int allocation_index) {
-  return double_register_names_[allocation_index];
-}
+static const char*
+    general_register_names_[RegisterConfiguration::kMaxGeneralRegisters];
+static const char*
+    double_register_names_[RegisterConfiguration::kMaxDoubleRegisters];
+static char register_names_[10 * (RegisterConfiguration::kMaxGeneralRegisters +
+                                  RegisterConfiguration::kMaxDoubleRegisters)];
 
 
 static void InitializeRegisterNames() {
   char* loc = register_names_;
-  for (int i = 0; i < kMaxGeneralRegisters; ++i) {
+  for (int i = 0; i < RegisterConfiguration::kMaxGeneralRegisters; ++i) {
     general_register_names_[i] = loc;
     loc += base::OS::SNPrintF(loc, 100, "gp_%d", i);
     *loc++ = 0;
   }
-  for (int i = 0; i < kMaxDoubleRegisters; ++i) {
+  for (int i = 0; i < RegisterConfiguration::kMaxDoubleRegisters; ++i) {
     double_register_names_[i] = loc;
     loc += base::OS::SNPrintF(loc, 100, "fp_%d", i) + 1;
     *loc++ = 0;
@@ -54,15 +47,21 @@
   static const int kDefaultNRegs = 4;
 
   RegisterAllocatorTest()
-      : basic_blocks_(zone()),
+      : num_general_registers_(kDefaultNRegs),
+        num_double_registers_(kDefaultNRegs),
+        basic_blocks_(zone()),
         instruction_blocks_(zone()),
         current_block_(NULL) {
     InitializeRegisterNames();
-    config_.num_general_registers_ = kDefaultNRegs;
-    config_.num_double_registers_ = kDefaultNRegs;
-    config_.num_aliased_double_registers_ = kDefaultNRegs;
-    config_.GeneralRegisterName = GeneralRegisterName;
-    config_.DoubleRegisterName = DoubleRegisterName;
+  }
+
+  RegisterConfiguration* config() {
+    if (config_.is_empty()) {
+      config_.Reset(new RegisterConfiguration(
+          num_general_registers_, num_double_registers_, num_double_registers_,
+          general_register_names_, double_register_names_));
+    }
+    return config_.get();
   }
 
   Frame* frame() {
@@ -82,7 +81,7 @@
   RegisterAllocator* allocator() {
     if (allocator_.is_empty()) {
       allocator_.Reset(
-          new RegisterAllocator(config_, zone(), frame(), sequence()));
+          new RegisterAllocator(config(), zone(), frame(), sequence()));
     }
     return allocator_.get();
   }
@@ -118,12 +117,14 @@
   void Allocate() {
     if (FLAG_trace_alloc) {
       OFStream os(stdout);
-      os << "Before: " << std::endl << *sequence() << std::endl;
+      PrintableInstructionSequence printable = {config(), sequence()};
+      os << "Before: " << std::endl << printable << std::endl;
     }
     allocator()->Allocate();
     if (FLAG_trace_alloc) {
       OFStream os(stdout);
-      os << "After: " << std::endl << *sequence() << std::endl;
+      PrintableInstructionSequence printable = {config(), sequence()};
+      os << "After: " << std::endl << printable << std::endl;
     }
   }
 
@@ -167,7 +168,9 @@
     return op;
   }
 
-  RegisterAllocator::Config config_;
+  int num_general_registers_;
+  int num_double_registers_;
+  SmartPointer<RegisterConfiguration> config_;
   ZoneVector<BasicBlock*> basic_blocks_;
   InstructionBlocks instruction_blocks_;
   InstructionBlock* current_block_;
diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp
index b849c63..a881e46 100644
--- a/test/unittests/unittests.gyp
+++ b/test/unittests/unittests.gyp
@@ -39,6 +39,7 @@
         'compiler/change-lowering-unittest.cc',
         'compiler/common-operator-unittest.cc',
         'compiler/compiler-test-utils.h',
+        'compiler/diamond-unittest.cc',
         'compiler/graph-reducer-unittest.cc',
         'compiler/graph-unittest.cc',
         'compiler/graph-unittest.h',
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 4f89bcf..5874865 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -411,6 +411,7 @@
         '../../src/compiler/control-builders.h',
         '../../src/compiler/control-reducer.cc',
         '../../src/compiler/control-reducer.h',
+        '../../src/compiler/diamond.h',
         '../../src/compiler/frame.h',
         '../../src/compiler/gap-resolver.cc',
         '../../src/compiler/gap-resolver.h',
@@ -484,6 +485,8 @@
         '../../src/compiler/raw-machine-assembler.h',
         '../../src/compiler/register-allocator.cc',
         '../../src/compiler/register-allocator.h',
+        '../../src/compiler/register-configuration.cc',
+        '../../src/compiler/register-configuration.h',
         '../../src/compiler/representation-change.h',
         '../../src/compiler/schedule.cc',
         '../../src/compiler/schedule.h',