Upgrade to 3.29

Update V8 to 3.29.88.17 and update makefiles to support building on
all the relevant platforms.

Bug: 17370214

Change-Id: Ia3407c157fd8d72a93e23d8318ccaf6ecf77fa4e
diff --git a/test/cctest/compiler/c-signature.h b/test/cctest/compiler/c-signature.h
new file mode 100644
index 0000000..5d161db
--- /dev/null
+++ b/test/cctest/compiler/c-signature.h
@@ -0,0 +1,133 @@
+// 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_C_SIGNATURE_H_
+#define V8_COMPILER_C_SIGNATURE_H_
+
+#include "src/compiler/machine-type.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+template <typename T>
+inline MachineType MachineTypeForC() {
+  CHECK(false);  // Instantiated with invalid type.
+  return kMachNone;
+}
+
+template <>
+inline MachineType MachineTypeForC<void>() {
+  return kMachNone;
+}
+
+template <>
+inline MachineType MachineTypeForC<int8_t>() {
+  return kMachInt8;
+}
+
+template <>
+inline MachineType MachineTypeForC<uint8_t>() {
+  return kMachUint8;
+}
+
+template <>
+inline MachineType MachineTypeForC<int16_t>() {
+  return kMachInt16;
+}
+
+template <>
+inline MachineType MachineTypeForC<uint16_t>() {
+  return kMachUint16;
+}
+
+template <>
+inline MachineType MachineTypeForC<int32_t>() {
+  return kMachInt32;
+}
+
+template <>
+inline MachineType MachineTypeForC<uint32_t>() {
+  return kMachUint32;
+}
+
+template <>
+inline MachineType MachineTypeForC<int64_t>() {
+  return kMachInt64;
+}
+
+template <>
+inline MachineType MachineTypeForC<uint64_t>() {
+  return kMachUint64;
+}
+
+template <>
+inline MachineType MachineTypeForC<double>() {
+  return kMachFloat64;
+}
+
+template <>
+inline MachineType MachineTypeForC<Object*>() {
+  return kMachAnyTagged;
+}
+
+template <typename Ret, uint16_t kParamCount>
+class CSignatureOf : public MachineSignature {
+ protected:
+  MachineType storage_[1 + kParamCount];
+
+  CSignatureOf()
+      : MachineSignature(MachineTypeForC<Ret>() != kMachNone ? 1 : 0,
+                         kParamCount,
+                         reinterpret_cast<MachineType*>(&storage_)) {
+    if (return_count_ == 1) storage_[0] = MachineTypeForC<Ret>();
+  }
+  void Set(int index, MachineType type) {
+    DCHECK(index >= 0 && index < kParamCount);
+    reps_[return_count_ + index] = type;
+  }
+};
+
+// Helper classes for instantiating Signature objects to be callable from C.
+template <typename Ret>
+class CSignature0 : public CSignatureOf<Ret, 0> {
+ public:
+  CSignature0() : CSignatureOf<Ret, 0>() {}
+};
+
+template <typename Ret, typename P1>
+class CSignature1 : public CSignatureOf<Ret, 1> {
+ public:
+  CSignature1() : CSignatureOf<Ret, 1>() {
+    this->Set(0, MachineTypeForC<P1>());
+  }
+};
+
+template <typename Ret, typename P1, typename P2>
+class CSignature2 : public CSignatureOf<Ret, 2> {
+ public:
+  CSignature2() : CSignatureOf<Ret, 2>() {
+    this->Set(0, MachineTypeForC<P1>());
+    this->Set(1, MachineTypeForC<P2>());
+  }
+};
+
+template <typename Ret, typename P1, typename P2, typename P3>
+class CSignature3 : public CSignatureOf<Ret, 3> {
+ public:
+  CSignature3() : CSignatureOf<Ret, 3>() {
+    this->Set(0, MachineTypeForC<P1>());
+    this->Set(1, MachineTypeForC<P2>());
+    this->Set(2, MachineTypeForC<P3>());
+  }
+};
+
+static const CSignature2<int32_t, int32_t, int32_t> int32_int32_to_int32;
+static const CSignature2<uint32_t, uint32_t, uint32_t> uint32_uint32_to_uint32;
+static const CSignature2<double, double, double> float64_float64_to_float64;
+}
+}
+}  // namespace v8::internal::compiler
+
+#endif  // V8_COMPILER_C_SIGNATURE_H_
diff --git a/test/cctest/compiler/call-tester.h b/test/cctest/compiler/call-tester.h
new file mode 100644
index 0000000..e864160
--- /dev/null
+++ b/test/cctest/compiler/call-tester.h
@@ -0,0 +1,393 @@
+// 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_CCTEST_COMPILER_CALL_TESTER_H_
+#define V8_CCTEST_COMPILER_CALL_TESTER_H_
+
+#include "src/v8.h"
+
+#include "src/simulator.h"
+
+#if V8_TARGET_ARCH_IA32
+#if __GNUC__
+#define V8_CDECL __attribute__((cdecl))
+#else
+#define V8_CDECL __cdecl
+#endif
+#else
+#define V8_CDECL
+#endif
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// TODO(titzer): use c-signature.h instead of ReturnValueTraits
+template <typename R>
+struct ReturnValueTraits {
+  static R Cast(uintptr_t r) { return reinterpret_cast<R>(r); }
+  static MachineType Representation() {
+    // TODO(dcarney): detect when R is of a subclass of Object* instead of this
+    // type check.
+    while (false) {
+      *(static_cast<Object* volatile*>(0)) = static_cast<R>(0);
+    }
+    return kMachAnyTagged;
+  }
+};
+
+template <>
+struct ReturnValueTraits<int32_t*> {
+  static int32_t* Cast(uintptr_t r) { return reinterpret_cast<int32_t*>(r); }
+  static MachineType Representation() { return kMachPtr; }
+};
+
+template <>
+struct ReturnValueTraits<void> {
+  static void Cast(uintptr_t r) {}
+  static MachineType Representation() { return kMachPtr; }
+};
+
+template <>
+struct ReturnValueTraits<bool> {
+  static bool Cast(uintptr_t r) { return static_cast<bool>(r); }
+  static MachineType Representation() { return kRepBit; }
+};
+
+template <>
+struct ReturnValueTraits<int32_t> {
+  static int32_t Cast(uintptr_t r) { return static_cast<int32_t>(r); }
+  static MachineType Representation() { return kMachInt32; }
+};
+
+template <>
+struct ReturnValueTraits<uint32_t> {
+  static uint32_t Cast(uintptr_t r) { return static_cast<uint32_t>(r); }
+  static MachineType Representation() { return kMachUint32; }
+};
+
+template <>
+struct ReturnValueTraits<int64_t> {
+  static int64_t Cast(uintptr_t r) { return static_cast<int64_t>(r); }
+  static MachineType Representation() { return kMachInt64; }
+};
+
+template <>
+struct ReturnValueTraits<uint64_t> {
+  static uint64_t Cast(uintptr_t r) { return static_cast<uint64_t>(r); }
+  static MachineType Representation() { return kMachUint64; }
+};
+
+template <>
+struct ReturnValueTraits<int16_t> {
+  static int16_t Cast(uintptr_t r) { return static_cast<int16_t>(r); }
+  static MachineType Representation() { return kMachInt16; }
+};
+
+template <>
+struct ReturnValueTraits<uint16_t> {
+  static uint16_t Cast(uintptr_t r) { return static_cast<uint16_t>(r); }
+  static MachineType Representation() { return kMachUint16; }
+};
+
+template <>
+struct ReturnValueTraits<int8_t> {
+  static int8_t Cast(uintptr_t r) { return static_cast<int8_t>(r); }
+  static MachineType Representation() { return kMachInt8; }
+};
+
+template <>
+struct ReturnValueTraits<uint8_t> {
+  static uint8_t Cast(uintptr_t r) { return static_cast<uint8_t>(r); }
+  static MachineType Representation() { return kMachUint8; }
+};
+
+template <>
+struct ReturnValueTraits<double> {
+  static double Cast(uintptr_t r) {
+    UNREACHABLE();
+    return 0.0;
+  }
+  static MachineType Representation() { return kMachFloat64; }
+};
+
+
+template <typename R>
+struct ParameterTraits {
+  static uintptr_t Cast(R r) { return static_cast<uintptr_t>(r); }
+};
+
+template <>
+struct ParameterTraits<int*> {
+  static uintptr_t Cast(int* r) { return reinterpret_cast<uintptr_t>(r); }
+};
+
+template <typename T>
+struct ParameterTraits<T*> {
+  static uintptr_t Cast(void* r) { return reinterpret_cast<uintptr_t>(r); }
+};
+
+class CallHelper {
+ public:
+  explicit CallHelper(Isolate* isolate, MachineSignature* machine_sig)
+      : machine_sig_(machine_sig), isolate_(isolate) {
+    USE(isolate_);
+  }
+  virtual ~CallHelper() {}
+
+  static MachineSignature* MakeMachineSignature(
+      Zone* zone, MachineType return_type, MachineType p0 = kMachNone,
+      MachineType p1 = kMachNone, MachineType p2 = kMachNone,
+      MachineType p3 = kMachNone, MachineType p4 = kMachNone) {
+    // Count the number of parameters.
+    size_t param_count = 5;
+    MachineType types[] = {p0, p1, p2, p3, p4};
+    while (param_count > 0 && types[param_count - 1] == kMachNone)
+      param_count--;
+    size_t return_count = return_type == kMachNone ? 0 : 1;
+
+    // Build the machine signature.
+    MachineSignature::Builder builder(zone, return_count, param_count);
+    if (return_count > 0) builder.AddReturn(return_type);
+    for (size_t i = 0; i < param_count; i++) {
+      builder.AddParam(types[i]);
+    }
+    return builder.Build();
+  }
+
+ protected:
+  MachineSignature* machine_sig_;
+  void VerifyParameters(size_t parameter_count, MachineType* parameter_types) {
+    CHECK(machine_sig_->parameter_count() == parameter_count);
+    for (size_t i = 0; i < parameter_count; i++) {
+      CHECK_EQ(machine_sig_->GetParam(i), parameter_types[i]);
+    }
+  }
+  virtual byte* Generate() = 0;
+
+ private:
+#if USE_SIMULATOR && V8_TARGET_ARCH_ARM64
+  uintptr_t CallSimulator(byte* f, Simulator::CallArgument* args) {
+    Simulator* simulator = Simulator::current(isolate_);
+    return static_cast<uintptr_t>(simulator->CallInt64(f, args));
+  }
+
+  template <typename R, typename F>
+  R DoCall(F* f) {
+    Simulator::CallArgument args[] = {Simulator::CallArgument::End()};
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
+  }
+  template <typename R, typename F, typename P1>
+  R DoCall(F* f, P1 p1) {
+    Simulator::CallArgument args[] = {Simulator::CallArgument(p1),
+                                      Simulator::CallArgument::End()};
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
+  }
+  template <typename R, typename F, typename P1, typename P2>
+  R DoCall(F* f, P1 p1, P2 p2) {
+    Simulator::CallArgument args[] = {Simulator::CallArgument(p1),
+                                      Simulator::CallArgument(p2),
+                                      Simulator::CallArgument::End()};
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3) {
+    Simulator::CallArgument args[] = {
+        Simulator::CallArgument(p1), Simulator::CallArgument(p2),
+        Simulator::CallArgument(p3), Simulator::CallArgument::End()};
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3,
+            typename P4>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) {
+    Simulator::CallArgument args[] = {
+        Simulator::CallArgument(p1), Simulator::CallArgument(p2),
+        Simulator::CallArgument(p3), Simulator::CallArgument(p4),
+        Simulator::CallArgument::End()};
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f), args));
+  }
+#elif USE_SIMULATOR && V8_TARGET_ARCH_ARM
+  uintptr_t CallSimulator(byte* f, int32_t p1 = 0, int32_t p2 = 0,
+                          int32_t p3 = 0, int32_t p4 = 0) {
+    Simulator* simulator = Simulator::current(isolate_);
+    return static_cast<uintptr_t>(simulator->Call(f, 4, p1, p2, p3, p4));
+  }
+  template <typename R, typename F>
+  R DoCall(F* f) {
+    return ReturnValueTraits<R>::Cast(CallSimulator(FUNCTION_ADDR(f)));
+  }
+  template <typename R, typename F, typename P1>
+  R DoCall(F* f, P1 p1) {
+    return ReturnValueTraits<R>::Cast(
+        CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1)));
+  }
+  template <typename R, typename F, typename P1, typename P2>
+  R DoCall(F* f, P1 p1, P2 p2) {
+    return ReturnValueTraits<R>::Cast(
+        CallSimulator(FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+                      ParameterTraits<P2>::Cast(p2)));
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3) {
+    return ReturnValueTraits<R>::Cast(CallSimulator(
+        FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+        ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3)));
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3,
+            typename P4>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) {
+    return ReturnValueTraits<R>::Cast(CallSimulator(
+        FUNCTION_ADDR(f), ParameterTraits<P1>::Cast(p1),
+        ParameterTraits<P2>::Cast(p2), ParameterTraits<P3>::Cast(p3),
+        ParameterTraits<P4>::Cast(p4)));
+  }
+#else
+  template <typename R, typename F>
+  R DoCall(F* f) {
+    return f();
+  }
+  template <typename R, typename F, typename P1>
+  R DoCall(F* f, P1 p1) {
+    return f(p1);
+  }
+  template <typename R, typename F, typename P1, typename P2>
+  R DoCall(F* f, P1 p1, P2 p2) {
+    return f(p1, p2);
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3) {
+    return f(p1, p2, p3);
+  }
+  template <typename R, typename F, typename P1, typename P2, typename P3,
+            typename P4>
+  R DoCall(F* f, P1 p1, P2 p2, P3 p3, P4 p4) {
+    return f(p1, p2, p3, p4);
+  }
+#endif
+
+#ifndef DEBUG
+  void VerifyParameters0() {}
+
+  template <typename P1>
+  void VerifyParameters1() {}
+
+  template <typename P1, typename P2>
+  void VerifyParameters2() {}
+
+  template <typename P1, typename P2, typename P3>
+  void VerifyParameters3() {}
+
+  template <typename P1, typename P2, typename P3, typename P4>
+  void VerifyParameters4() {}
+#else
+  void VerifyParameters0() { VerifyParameters(0, NULL); }
+
+  template <typename P1>
+  void VerifyParameters1() {
+    MachineType parameters[] = {ReturnValueTraits<P1>::Representation()};
+    VerifyParameters(arraysize(parameters), parameters);
+  }
+
+  template <typename P1, typename P2>
+  void VerifyParameters2() {
+    MachineType parameters[] = {ReturnValueTraits<P1>::Representation(),
+                                ReturnValueTraits<P2>::Representation()};
+    VerifyParameters(arraysize(parameters), parameters);
+  }
+
+  template <typename P1, typename P2, typename P3>
+  void VerifyParameters3() {
+    MachineType parameters[] = {ReturnValueTraits<P1>::Representation(),
+                                ReturnValueTraits<P2>::Representation(),
+                                ReturnValueTraits<P3>::Representation()};
+    VerifyParameters(arraysize(parameters), parameters);
+  }
+
+  template <typename P1, typename P2, typename P3, typename P4>
+  void VerifyParameters4() {
+    MachineType parameters[] = {ReturnValueTraits<P1>::Representation(),
+                                ReturnValueTraits<P2>::Representation(),
+                                ReturnValueTraits<P3>::Representation(),
+                                ReturnValueTraits<P4>::Representation()};
+    VerifyParameters(arraysize(parameters), parameters);
+  }
+#endif
+
+  // TODO(dcarney): replace Call() in CallHelper2 with these.
+  template <typename R>
+  R Call0() {
+    typedef R V8_CDECL FType();
+    VerifyParameters0();
+    return DoCall<R>(FUNCTION_CAST<FType*>(Generate()));
+  }
+
+  template <typename R, typename P1>
+  R Call1(P1 p1) {
+    typedef R V8_CDECL FType(P1);
+    VerifyParameters1<P1>();
+    return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1);
+  }
+
+  template <typename R, typename P1, typename P2>
+  R Call2(P1 p1, P2 p2) {
+    typedef R V8_CDECL FType(P1, P2);
+    VerifyParameters2<P1, P2>();
+    return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2);
+  }
+
+  template <typename R, typename P1, typename P2, typename P3>
+  R Call3(P1 p1, P2 p2, P3 p3) {
+    typedef R V8_CDECL FType(P1, P2, P3);
+    VerifyParameters3<P1, P2, P3>();
+    return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2, p3);
+  }
+
+  template <typename R, typename P1, typename P2, typename P3, typename P4>
+  R Call4(P1 p1, P2 p2, P3 p3, P4 p4) {
+    typedef R V8_CDECL FType(P1, P2, P3, P4);
+    VerifyParameters4<P1, P2, P3, P4>();
+    return DoCall<R>(FUNCTION_CAST<FType*>(Generate()), p1, p2, p3, p4);
+  }
+
+  template <typename R, typename C>
+  friend class CallHelper2;
+  Isolate* isolate_;
+};
+
+
+// TODO(dcarney): replace CallHelper with CallHelper2 and rename.
+template <typename R, typename C>
+class CallHelper2 {
+ public:
+  R Call() { return helper()->template Call0<R>(); }
+
+  template <typename P1>
+  R Call(P1 p1) {
+    return helper()->template Call1<R>(p1);
+  }
+
+  template <typename P1, typename P2>
+  R Call(P1 p1, P2 p2) {
+    return helper()->template Call2<R>(p1, p2);
+  }
+
+  template <typename P1, typename P2, typename P3>
+  R Call(P1 p1, P2 p2, P3 p3) {
+    return helper()->template Call3<R>(p1, p2, p3);
+  }
+
+  template <typename P1, typename P2, typename P3, typename P4>
+  R Call(P1 p1, P2 p2, P3 p3, P4 p4) {
+    return helper()->template Call4<R>(p1, p2, p3, p4);
+  }
+
+ private:
+  CallHelper* helper() { return static_cast<C*>(this); }
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_CALL_TESTER_H_
diff --git a/test/cctest/compiler/codegen-tester.cc b/test/cctest/compiler/codegen-tester.cc
new file mode 100644
index 0000000..b1874f5
--- /dev/null
+++ b/test/cctest/compiler/codegen-tester.cc
@@ -0,0 +1,577 @@
+// 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/v8.h"
+
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(CompareWrapper) {
+  // Who tests the testers?
+  // If CompareWrapper is broken, then test expectations will be broken.
+  RawMachineAssemblerTester<int32_t> m;
+  CompareWrapper wWord32Equal(IrOpcode::kWord32Equal);
+  CompareWrapper wInt32LessThan(IrOpcode::kInt32LessThan);
+  CompareWrapper wInt32LessThanOrEqual(IrOpcode::kInt32LessThanOrEqual);
+  CompareWrapper wUint32LessThan(IrOpcode::kUint32LessThan);
+  CompareWrapper wUint32LessThanOrEqual(IrOpcode::kUint32LessThanOrEqual);
+
+  {
+    FOR_INT32_INPUTS(pl) {
+      FOR_INT32_INPUTS(pr) {
+        int32_t a = *pl;
+        int32_t b = *pr;
+        CHECK_EQ(a == b, wWord32Equal.Int32Compare(a, b));
+        CHECK_EQ(a < b, wInt32LessThan.Int32Compare(a, b));
+        CHECK_EQ(a <= b, wInt32LessThanOrEqual.Int32Compare(a, b));
+      }
+    }
+  }
+
+  {
+    FOR_UINT32_INPUTS(pl) {
+      FOR_UINT32_INPUTS(pr) {
+        uint32_t a = *pl;
+        uint32_t b = *pr;
+        CHECK_EQ(a == b, wWord32Equal.Int32Compare(a, b));
+        CHECK_EQ(a < b, wUint32LessThan.Int32Compare(a, b));
+        CHECK_EQ(a <= b, wUint32LessThanOrEqual.Int32Compare(a, b));
+      }
+    }
+  }
+
+  CHECK_EQ(true, wWord32Equal.Int32Compare(0, 0));
+  CHECK_EQ(true, wWord32Equal.Int32Compare(257, 257));
+  CHECK_EQ(true, wWord32Equal.Int32Compare(65539, 65539));
+  CHECK_EQ(true, wWord32Equal.Int32Compare(-1, -1));
+  CHECK_EQ(true, wWord32Equal.Int32Compare(0xffffffff, 0xffffffff));
+
+  CHECK_EQ(false, wWord32Equal.Int32Compare(0, 1));
+  CHECK_EQ(false, wWord32Equal.Int32Compare(257, 256));
+  CHECK_EQ(false, wWord32Equal.Int32Compare(65539, 65537));
+  CHECK_EQ(false, wWord32Equal.Int32Compare(-1, -2));
+  CHECK_EQ(false, wWord32Equal.Int32Compare(0xffffffff, 0xfffffffe));
+
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(0, 0));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(357, 357));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(75539, 75539));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(-1, -1));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(0xffffffff, 0xffffffff));
+
+  CHECK_EQ(true, wInt32LessThan.Int32Compare(0, 1));
+  CHECK_EQ(true, wInt32LessThan.Int32Compare(456, 457));
+  CHECK_EQ(true, wInt32LessThan.Int32Compare(85537, 85539));
+  CHECK_EQ(true, wInt32LessThan.Int32Compare(-2, -1));
+  CHECK_EQ(true, wInt32LessThan.Int32Compare(0xfffffffe, 0xffffffff));
+
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(1, 0));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(457, 456));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(85539, 85537));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(-1, -2));
+  CHECK_EQ(false, wInt32LessThan.Int32Compare(0xffffffff, 0xfffffffe));
+
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(0, 0));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(357, 357));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(75539, 75539));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(-1, -1));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(0xffffffff, 0xffffffff));
+
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(0, 1));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(456, 457));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(85537, 85539));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(-2, -1));
+  CHECK_EQ(true, wInt32LessThanOrEqual.Int32Compare(0xfffffffe, 0xffffffff));
+
+  CHECK_EQ(false, wInt32LessThanOrEqual.Int32Compare(1, 0));
+  CHECK_EQ(false, wInt32LessThanOrEqual.Int32Compare(457, 456));
+  CHECK_EQ(false, wInt32LessThanOrEqual.Int32Compare(85539, 85537));
+  CHECK_EQ(false, wInt32LessThanOrEqual.Int32Compare(-1, -2));
+  CHECK_EQ(false, wInt32LessThanOrEqual.Int32Compare(0xffffffff, 0xfffffffe));
+
+  // Unsigned comparisons.
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(0, 0));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(357, 357));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(75539, 75539));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(-1, -1));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(0xffffffff, 0xffffffff));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(0xffffffff, 0));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(-2999, 0));
+
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(0, 1));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(456, 457));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(85537, 85539));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(-11, -10));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(0xfffffffe, 0xffffffff));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(0, 0xffffffff));
+  CHECK_EQ(true, wUint32LessThan.Int32Compare(0, -2996));
+
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(1, 0));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(457, 456));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(85539, 85537));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(-10, -21));
+  CHECK_EQ(false, wUint32LessThan.Int32Compare(0xffffffff, 0xfffffffe));
+
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(0, 0));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(357, 357));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(75539, 75539));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(-1, -1));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(0xffffffff, 0xffffffff));
+
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(0, 1));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(456, 457));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(85537, 85539));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(-300, -299));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(-300, -300));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(0xfffffffe, 0xffffffff));
+  CHECK_EQ(true, wUint32LessThanOrEqual.Int32Compare(0, -2995));
+
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(1, 0));
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(457, 456));
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(85539, 85537));
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(-130, -170));
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(0xffffffff, 0xfffffffe));
+  CHECK_EQ(false, wUint32LessThanOrEqual.Int32Compare(-2997, 0));
+
+  CompareWrapper wFloat64Equal(IrOpcode::kFloat64Equal);
+  CompareWrapper wFloat64LessThan(IrOpcode::kFloat64LessThan);
+  CompareWrapper wFloat64LessThanOrEqual(IrOpcode::kFloat64LessThanOrEqual);
+
+  // Check NaN handling.
+  double nan = v8::base::OS::nan_value();
+  double inf = V8_INFINITY;
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, 0.0));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, 1.0));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, -inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, nan));
+
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(0.0, nan));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(1.0, nan));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(inf, nan));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-inf, nan));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(nan, nan));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, 0.0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, 1.0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, -inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, nan));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(0.0, nan));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(1.0, nan));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, nan));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(-inf, nan));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(nan, nan));
+
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, 0.0));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, 1.0));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, inf));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, -inf));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, nan));
+
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(0.0, nan));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(1.0, nan));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(inf, nan));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(-inf, nan));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(nan, nan));
+
+  // Check inf handling.
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(inf, 0.0));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(inf, 1.0));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(inf, inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(inf, -inf));
+
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(0.0, inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(1.0, inf));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(inf, inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-inf, inf));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, 0.0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, 1.0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, -inf));
+
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(0.0, inf));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(1.0, inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, inf));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(-inf, inf));
+
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(inf, 0.0));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(inf, 1.0));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(inf, inf));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(inf, -inf));
+
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(0.0, inf));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(1.0, inf));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(inf, inf));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, inf));
+
+  // Check -inf handling.
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-inf, 0.0));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-inf, 1.0));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-inf, inf));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(-inf, -inf));
+
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(0.0, -inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(1.0, -inf));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(inf, -inf));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(-inf, -inf));
+
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(-inf, 0.0));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(-inf, 1.0));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(-inf, inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(-inf, -inf));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(0.0, -inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(1.0, -inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(inf, -inf));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(-inf, -inf));
+
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, 0.0));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, 1.0));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, inf));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, -inf));
+
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(0.0, -inf));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(1.0, -inf));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(inf, -inf));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-inf, -inf));
+
+  // Check basic values.
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(0, 0));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(257.1, 257.1));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(65539.1, 65539.1));
+  CHECK_EQ(true, wFloat64Equal.Float64Compare(-1.1, -1.1));
+
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(0, 1));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(257.2, 256.2));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(65539.2, 65537.2));
+  CHECK_EQ(false, wFloat64Equal.Float64Compare(-1.2, -2.2));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(0, 0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(357.3, 357.3));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(75539.3, 75539.3));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(-1.3, -1.3));
+
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(0, 1));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(456.4, 457.4));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(85537.4, 85539.4));
+  CHECK_EQ(true, wFloat64LessThan.Float64Compare(-2.4, -1.4));
+
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(1, 0));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(457.5, 456.5));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(85539.5, 85537.5));
+  CHECK_EQ(false, wFloat64LessThan.Float64Compare(-1.5, -2.5));
+
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(0, 0));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(357.6, 357.6));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(75539.6, 75539.6));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-1.6, -1.6));
+
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(0, 1));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(456.7, 457.7));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(85537.7, 85539.7));
+  CHECK_EQ(true, wFloat64LessThanOrEqual.Float64Compare(-2.7, -1.7));
+
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(1, 0));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(457.8, 456.8));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(85539.8, 85537.8));
+  CHECK_EQ(false, wFloat64LessThanOrEqual.Float64Compare(-1.8, -2.8));
+}
+
+
+void Int32BinopInputShapeTester::TestAllInputShapes() {
+  std::vector<int32_t> inputs = ValueHelper::int32_vector();
+  int num_int_inputs = static_cast<int>(inputs.size());
+  if (num_int_inputs > 16) num_int_inputs = 16;  // limit to 16 inputs
+
+  for (int i = -2; i < num_int_inputs; i++) {    // for all left shapes
+    for (int j = -2; j < num_int_inputs; j++) {  // for all right shapes
+      if (i >= 0 && j >= 0) break;               // No constant/constant combos
+      RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+      Node* p0 = m.Parameter(0);
+      Node* p1 = m.Parameter(1);
+      Node* n0;
+      Node* n1;
+
+      // left = Parameter | Load | Constant
+      if (i == -2) {
+        n0 = p0;
+      } else if (i == -1) {
+        n0 = m.LoadFromPointer(&input_a, kMachInt32);
+      } else {
+        n0 = m.Int32Constant(inputs[i]);
+      }
+
+      // right = Parameter | Load | Constant
+      if (j == -2) {
+        n1 = p1;
+      } else if (j == -1) {
+        n1 = m.LoadFromPointer(&input_b, kMachInt32);
+      } else {
+        n1 = m.Int32Constant(inputs[j]);
+      }
+
+      gen->gen(&m, n0, n1);
+
+      if (false) printf("Int32BinopInputShapeTester i=%d, j=%d\n", i, j);
+      if (i >= 0) {
+        input_a = inputs[i];
+        RunRight(&m);
+      } else if (j >= 0) {
+        input_b = inputs[j];
+        RunLeft(&m);
+      } else {
+        Run(&m);
+      }
+    }
+  }
+}
+
+
+void Int32BinopInputShapeTester::Run(RawMachineAssemblerTester<int32_t>* m) {
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      input_a = *pl;
+      input_b = *pr;
+      int32_t expect = gen->expected(input_a, input_b);
+      if (false) printf("  cmp(a=%d, b=%d) ?== %d\n", input_a, input_b, expect);
+      CHECK_EQ(expect, m->Call(input_a, input_b));
+    }
+  }
+}
+
+
+void Int32BinopInputShapeTester::RunLeft(
+    RawMachineAssemblerTester<int32_t>* m) {
+  FOR_UINT32_INPUTS(i) {
+    input_a = *i;
+    int32_t expect = gen->expected(input_a, input_b);
+    if (false) printf("  cmp(a=%d, b=%d) ?== %d\n", input_a, input_b, expect);
+    CHECK_EQ(expect, m->Call(input_a, input_b));
+  }
+}
+
+
+void Int32BinopInputShapeTester::RunRight(
+    RawMachineAssemblerTester<int32_t>* m) {
+  FOR_UINT32_INPUTS(i) {
+    input_b = *i;
+    int32_t expect = gen->expected(input_a, input_b);
+    if (false) printf("  cmp(a=%d, b=%d) ?== %d\n", input_a, input_b, expect);
+    CHECK_EQ(expect, m->Call(input_a, input_b));
+  }
+}
+
+
+#if V8_TURBOFAN_TARGET
+
+TEST(ParametersEqual) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+  Node* p1 = m.Parameter(1);
+  CHECK_NE(NULL, p1);
+  Node* p0 = m.Parameter(0);
+  CHECK_NE(NULL, p0);
+  CHECK_EQ(p0, m.Parameter(0));
+  CHECK_EQ(p1, m.Parameter(1));
+}
+
+
+void RunSmiConstant(int32_t v) {
+// TODO(dcarney): on x64 Smis are generated with the SmiConstantRegister
+#if !V8_TARGET_ARCH_X64
+  if (Smi::IsValid(v)) {
+    RawMachineAssemblerTester<Object*> m;
+    m.Return(m.NumberConstant(v));
+    CHECK_EQ(Smi::FromInt(v), m.Call());
+  }
+#endif
+}
+
+
+void RunNumberConstant(double v) {
+  RawMachineAssemblerTester<Object*> m;
+#if V8_TARGET_ARCH_X64
+  // TODO(dcarney): on x64 Smis are generated with the SmiConstantRegister
+  Handle<Object> number = m.isolate()->factory()->NewNumber(v);
+  if (number->IsSmi()) return;
+#endif
+  m.Return(m.NumberConstant(v));
+  Object* result = m.Call();
+  m.CheckNumber(v, result);
+}
+
+
+TEST(RunEmpty) {
+  RawMachineAssemblerTester<int32_t> m;
+  m.Return(m.Int32Constant(0));
+  CHECK_EQ(0, m.Call());
+}
+
+
+TEST(RunInt32Constants) {
+  FOR_INT32_INPUTS(i) {
+    RawMachineAssemblerTester<int32_t> m;
+    m.Return(m.Int32Constant(*i));
+    CHECK_EQ(*i, m.Call());
+  }
+}
+
+
+TEST(RunSmiConstants) {
+  for (int32_t i = 1; i < Smi::kMaxValue && i != 0; i = i << 1) {
+    RunSmiConstant(i);
+    RunSmiConstant(3 * i);
+    RunSmiConstant(5 * i);
+    RunSmiConstant(-i);
+    RunSmiConstant(i | 1);
+    RunSmiConstant(i | 3);
+  }
+  RunSmiConstant(Smi::kMaxValue);
+  RunSmiConstant(Smi::kMaxValue - 1);
+  RunSmiConstant(Smi::kMinValue);
+  RunSmiConstant(Smi::kMinValue + 1);
+
+  FOR_INT32_INPUTS(i) { RunSmiConstant(*i); }
+}
+
+
+TEST(RunNumberConstants) {
+  {
+    FOR_FLOAT64_INPUTS(i) { RunNumberConstant(*i); }
+  }
+  {
+    FOR_INT32_INPUTS(i) { RunNumberConstant(*i); }
+  }
+
+  for (int32_t i = 1; i < Smi::kMaxValue && i != 0; i = i << 1) {
+    RunNumberConstant(i);
+    RunNumberConstant(-i);
+    RunNumberConstant(i | 1);
+    RunNumberConstant(i | 3);
+  }
+  RunNumberConstant(Smi::kMaxValue);
+  RunNumberConstant(Smi::kMaxValue - 1);
+  RunNumberConstant(Smi::kMinValue);
+  RunNumberConstant(Smi::kMinValue + 1);
+}
+
+
+TEST(RunEmptyString) {
+  RawMachineAssemblerTester<Object*> m;
+  m.Return(m.StringConstant("empty"));
+  m.CheckString("empty", m.Call());
+}
+
+
+TEST(RunHeapConstant) {
+  RawMachineAssemblerTester<Object*> m;
+  m.Return(m.StringConstant("empty"));
+  m.CheckString("empty", m.Call());
+}
+
+
+TEST(RunHeapNumberConstant) {
+  RawMachineAssemblerTester<Object*> m;
+  Handle<Object> number = m.isolate()->factory()->NewHeapNumber(100.5);
+  m.Return(m.HeapConstant(number));
+  Object* result = m.Call();
+  CHECK_EQ(result, *number);
+}
+
+
+TEST(RunParam1) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  m.Return(m.Parameter(0));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t result = m.Call(*i);
+    CHECK_EQ(*i, result);
+  }
+}
+
+
+TEST(RunParam2_1) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+  Node* p0 = m.Parameter(0);
+  Node* p1 = m.Parameter(1);
+  m.Return(p0);
+  USE(p1);
+
+  FOR_INT32_INPUTS(i) {
+    int32_t result = m.Call(*i, -9999);
+    CHECK_EQ(*i, result);
+  }
+}
+
+
+TEST(RunParam2_2) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+  Node* p0 = m.Parameter(0);
+  Node* p1 = m.Parameter(1);
+  m.Return(p1);
+  USE(p0);
+
+  FOR_INT32_INPUTS(i) {
+    int32_t result = m.Call(-7777, *i);
+    CHECK_EQ(*i, result);
+  }
+}
+
+
+TEST(RunParam3) {
+  for (int i = 0; i < 3; i++) {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    Node* nodes[] = {m.Parameter(0), m.Parameter(1), m.Parameter(2)};
+    m.Return(nodes[i]);
+
+    int p[] = {-99, -77, -88};
+    FOR_INT32_INPUTS(j) {
+      p[i] = *j;
+      int32_t result = m.Call(p[0], p[1], p[2]);
+      CHECK_EQ(*j, result);
+    }
+  }
+}
+
+
+TEST(RunBinopTester) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(bt.param0);
+
+    FOR_INT32_INPUTS(i) { CHECK_EQ(*i, bt.call(*i, 777)); }
+  }
+
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(bt.param1);
+
+    FOR_INT32_INPUTS(i) { CHECK_EQ(*i, bt.call(666, *i)); }
+  }
+
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Float64BinopTester bt(&m);
+    bt.AddReturn(bt.param0);
+
+    FOR_FLOAT64_INPUTS(i) { CHECK_EQ(*i, bt.call(*i, 9.0)); }
+  }
+
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Float64BinopTester bt(&m);
+    bt.AddReturn(bt.param1);
+
+    FOR_FLOAT64_INPUTS(i) { CHECK_EQ(*i, bt.call(-11.25, *i)); }
+  }
+}
+
+#endif  // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/codegen-tester.h b/test/cctest/compiler/codegen-tester.h
new file mode 100644
index 0000000..6aa5bae
--- /dev/null
+++ b/test/cctest/compiler/codegen-tester.h
@@ -0,0 +1,338 @@
+// 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_CCTEST_COMPILER_CODEGEN_TESTER_H_
+#define V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
+
+#include "src/v8.h"
+
+#include "src/compiler/pipeline.h"
+#include "src/compiler/raw-machine-assembler.h"
+#include "src/simulator.h"
+#include "test/cctest/compiler/call-tester.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+template <typename MachineAssembler>
+class MachineAssemblerTester : public HandleAndZoneScope,
+                               public CallHelper,
+                               public MachineAssembler {
+ public:
+  MachineAssemblerTester(MachineType return_type, MachineType p0,
+                         MachineType p1, MachineType p2, MachineType p3,
+                         MachineType p4)
+      : HandleAndZoneScope(),
+        CallHelper(
+            main_isolate(),
+            MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4)),
+        MachineAssembler(
+            new (main_zone()) Graph(main_zone()),
+            MakeMachineSignature(main_zone(), return_type, p0, p1, p2, p3, p4),
+            kMachPtr) {}
+
+  Node* LoadFromPointer(void* address, MachineType rep, int32_t offset = 0) {
+    return this->Load(rep, this->PointerConstant(address),
+                      this->Int32Constant(offset));
+  }
+
+  void StoreToPointer(void* address, MachineType rep, Node* node) {
+    this->Store(rep, this->PointerConstant(address), node);
+  }
+
+  Node* StringConstant(const char* string) {
+    return this->HeapConstant(
+        this->isolate()->factory()->InternalizeUtf8String(string));
+  }
+
+  void CheckNumber(double expected, Object* number) {
+    CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
+  }
+
+  void CheckString(const char* expected, Object* string) {
+    CHECK(
+        this->isolate()->factory()->InternalizeUtf8String(expected)->SameValue(
+            string));
+  }
+
+  void GenerateCode() { Generate(); }
+
+ protected:
+  virtual byte* Generate() {
+    if (code_.is_null()) {
+      Schedule* schedule = this->Export();
+      CallDescriptor* call_descriptor = this->call_descriptor();
+      Graph* graph = this->graph();
+      CompilationInfo info(graph->zone()->isolate(), graph->zone());
+      Linkage linkage(&info, call_descriptor);
+      Pipeline pipeline(&info);
+      code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph, schedule);
+    }
+    return this->code_.ToHandleChecked()->entry();
+  }
+
+ private:
+  MaybeHandle<Code> code_;
+};
+
+
+template <typename ReturnType>
+class RawMachineAssemblerTester
+    : public MachineAssemblerTester<RawMachineAssembler>,
+      public CallHelper2<ReturnType, RawMachineAssemblerTester<ReturnType> > {
+ public:
+  RawMachineAssemblerTester(MachineType p0 = kMachNone,
+                            MachineType p1 = kMachNone,
+                            MachineType p2 = kMachNone,
+                            MachineType p3 = kMachNone,
+                            MachineType p4 = kMachNone)
+      : MachineAssemblerTester<RawMachineAssembler>(
+            ReturnValueTraits<ReturnType>::Representation(), p0, p1, p2, p3,
+            p4) {}
+
+  template <typename Ci, typename Fn>
+  void Run(const Ci& ci, const Fn& fn) {
+    typename Ci::const_iterator i;
+    for (i = ci.begin(); i != ci.end(); ++i) {
+      CHECK_EQ(fn(*i), this->Call(*i));
+    }
+  }
+
+  template <typename Ci, typename Cj, typename Fn>
+  void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
+    typename Ci::const_iterator i;
+    typename Cj::const_iterator j;
+    for (i = ci.begin(); i != ci.end(); ++i) {
+      for (j = cj.begin(); j != cj.end(); ++j) {
+        CHECK_EQ(fn(*i, *j), this->Call(*i, *j));
+      }
+    }
+  }
+};
+
+
+static const bool USE_RESULT_BUFFER = true;
+static const bool USE_RETURN_REGISTER = false;
+static const int32_t CHECK_VALUE = 0x99BEEDCE;
+
+
+// TODO(titzer): use the C-style calling convention, or any register-based
+// calling convention for binop tests.
+template <typename CType, MachineType rep, bool use_result_buffer>
+class BinopTester {
+ public:
+  explicit BinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : T(tester),
+        param0(T->LoadFromPointer(&p0, rep)),
+        param1(T->LoadFromPointer(&p1, rep)),
+        p0(static_cast<CType>(0)),
+        p1(static_cast<CType>(0)),
+        result(static_cast<CType>(0)) {}
+
+  RawMachineAssemblerTester<int32_t>* T;
+  Node* param0;
+  Node* param1;
+
+  CType call(CType a0, CType a1) {
+    p0 = a0;
+    p1 = a1;
+    if (use_result_buffer) {
+      CHECK_EQ(CHECK_VALUE, T->Call());
+      return result;
+    } else {
+      return T->Call();
+    }
+  }
+
+  void AddReturn(Node* val) {
+    if (use_result_buffer) {
+      T->Store(rep, T->PointerConstant(&result), T->Int32Constant(0), val);
+      T->Return(T->Int32Constant(CHECK_VALUE));
+    } else {
+      T->Return(val);
+    }
+  }
+
+  template <typename Ci, typename Cj, typename Fn>
+  void Run(const Ci& ci, const Cj& cj, const Fn& fn) {
+    typename Ci::const_iterator i;
+    typename Cj::const_iterator j;
+    for (i = ci.begin(); i != ci.end(); ++i) {
+      for (j = cj.begin(); j != cj.end(); ++j) {
+        CHECK_EQ(fn(*i, *j), this->call(*i, *j));
+      }
+    }
+  }
+
+ protected:
+  CType p0;
+  CType p1;
+  CType result;
+};
+
+
+// A helper class for testing code sequences that take two int parameters and
+// return an int value.
+class Int32BinopTester
+    : public BinopTester<int32_t, kMachInt32, USE_RETURN_REGISTER> {
+ public:
+  explicit Int32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : BinopTester<int32_t, kMachInt32, USE_RETURN_REGISTER>(tester) {}
+};
+
+
+// A helper class for testing code sequences that take two uint parameters and
+// return an uint value.
+class Uint32BinopTester
+    : public BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER> {
+ public:
+  explicit Uint32BinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : BinopTester<uint32_t, kMachUint32, USE_RETURN_REGISTER>(tester) {}
+
+  uint32_t call(uint32_t a0, uint32_t a1) {
+    p0 = a0;
+    p1 = a1;
+    return static_cast<uint32_t>(T->Call());
+  }
+};
+
+
+// A helper class for testing code sequences that take two double parameters and
+// return a double value.
+// TODO(titzer): figure out how to return doubles correctly on ia32.
+class Float64BinopTester
+    : public BinopTester<double, kMachFloat64, USE_RESULT_BUFFER> {
+ public:
+  explicit Float64BinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : BinopTester<double, kMachFloat64, USE_RESULT_BUFFER>(tester) {}
+};
+
+
+// A helper class for testing code sequences that take two pointer parameters
+// and return a pointer value.
+// TODO(titzer): pick word size of pointers based on V8_TARGET.
+template <typename Type>
+class PointerBinopTester
+    : public BinopTester<Type*, kMachPtr, USE_RETURN_REGISTER> {
+ public:
+  explicit PointerBinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : BinopTester<Type*, kMachPtr, USE_RETURN_REGISTER>(tester) {}
+};
+
+
+// A helper class for testing code sequences that take two tagged parameters and
+// return a tagged value.
+template <typename Type>
+class TaggedBinopTester
+    : public BinopTester<Type*, kMachAnyTagged, USE_RETURN_REGISTER> {
+ public:
+  explicit TaggedBinopTester(RawMachineAssemblerTester<int32_t>* tester)
+      : BinopTester<Type*, kMachAnyTagged, USE_RETURN_REGISTER>(tester) {}
+};
+
+// A helper class for testing compares. Wraps a machine opcode and provides
+// evaluation routines and the operators.
+class CompareWrapper {
+ public:
+  explicit CompareWrapper(IrOpcode::Value op) : opcode(op) {}
+
+  Node* MakeNode(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
+    return m->NewNode(op(m->machine()), a, b);
+  }
+
+  const Operator* op(MachineOperatorBuilder* machine) {
+    switch (opcode) {
+      case IrOpcode::kWord32Equal:
+        return machine->Word32Equal();
+      case IrOpcode::kInt32LessThan:
+        return machine->Int32LessThan();
+      case IrOpcode::kInt32LessThanOrEqual:
+        return machine->Int32LessThanOrEqual();
+      case IrOpcode::kUint32LessThan:
+        return machine->Uint32LessThan();
+      case IrOpcode::kUint32LessThanOrEqual:
+        return machine->Uint32LessThanOrEqual();
+      case IrOpcode::kFloat64Equal:
+        return machine->Float64Equal();
+      case IrOpcode::kFloat64LessThan:
+        return machine->Float64LessThan();
+      case IrOpcode::kFloat64LessThanOrEqual:
+        return machine->Float64LessThanOrEqual();
+      default:
+        UNREACHABLE();
+    }
+    return NULL;
+  }
+
+  bool Int32Compare(int32_t a, int32_t b) {
+    switch (opcode) {
+      case IrOpcode::kWord32Equal:
+        return a == b;
+      case IrOpcode::kInt32LessThan:
+        return a < b;
+      case IrOpcode::kInt32LessThanOrEqual:
+        return a <= b;
+      case IrOpcode::kUint32LessThan:
+        return static_cast<uint32_t>(a) < static_cast<uint32_t>(b);
+      case IrOpcode::kUint32LessThanOrEqual:
+        return static_cast<uint32_t>(a) <= static_cast<uint32_t>(b);
+      default:
+        UNREACHABLE();
+    }
+    return false;
+  }
+
+  bool Float64Compare(double a, double b) {
+    switch (opcode) {
+      case IrOpcode::kFloat64Equal:
+        return a == b;
+      case IrOpcode::kFloat64LessThan:
+        return a < b;
+      case IrOpcode::kFloat64LessThanOrEqual:
+        return a <= b;
+      default:
+        UNREACHABLE();
+    }
+    return false;
+  }
+
+  IrOpcode::Value opcode;
+};
+
+
+// A small closure class to generate code for a function of two inputs that
+// produces a single output so that it can be used in many different contexts.
+// The {expected()} method should compute the expected output for a given
+// pair of inputs.
+template <typename T>
+class BinopGen {
+ public:
+  virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) = 0;
+  virtual T expected(T a, T b) = 0;
+  virtual ~BinopGen() {}
+};
+
+// A helper class to generate various combination of input shape combinations
+// and run the generated code to ensure it produces the correct results.
+class Int32BinopInputShapeTester {
+ public:
+  explicit Int32BinopInputShapeTester(BinopGen<int32_t>* g) : gen(g) {}
+
+  void TestAllInputShapes();
+
+ private:
+  BinopGen<int32_t>* gen;
+  int32_t input_a;
+  int32_t input_b;
+
+  void Run(RawMachineAssemblerTester<int32_t>* m);
+  void RunLeft(RawMachineAssemblerTester<int32_t>* m);
+  void RunRight(RawMachineAssemblerTester<int32_t>* m);
+};
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_CODEGEN_TESTER_H_
diff --git a/test/cctest/compiler/function-tester.h b/test/cctest/compiler/function-tester.h
new file mode 100644
index 0000000..c869f00
--- /dev/null
+++ b/test/cctest/compiler/function-tester.h
@@ -0,0 +1,193 @@
+// 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_CCTEST_COMPILER_FUNCTION_TESTER_H_
+#define V8_CCTEST_COMPILER_FUNCTION_TESTER_H_
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler.h"
+#include "src/compiler/pipeline.h"
+#include "src/execution.h"
+#include "src/full-codegen.h"
+#include "src/handles.h"
+#include "src/objects-inl.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+
+#define USE_CRANKSHAFT 0
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class FunctionTester : public InitializedHandleScope {
+ public:
+  explicit FunctionTester(const char* source, uint32_t flags = 0)
+      : isolate(main_isolate()),
+        function((FLAG_allow_natives_syntax = true, NewFunction(source))),
+        flags_(flags) {
+    Compile(function);
+    const uint32_t supported_flags = CompilationInfo::kContextSpecializing |
+                                     CompilationInfo::kInliningEnabled |
+                                     CompilationInfo::kTypingEnabled;
+    CHECK_EQ(0, flags_ & ~supported_flags);
+  }
+
+  Isolate* isolate;
+  Handle<JSFunction> function;
+
+  Handle<JSFunction> Compile(Handle<JSFunction> function) {
+#if V8_TURBOFAN_TARGET
+    CompilationInfoWithZone info(function);
+
+    CHECK(Parser::Parse(&info));
+    info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+    if (flags_ & CompilationInfo::kContextSpecializing) {
+      info.MarkAsContextSpecializing();
+    }
+    if (flags_ & CompilationInfo::kInliningEnabled) {
+      info.MarkAsInliningEnabled();
+    }
+    if (flags_ & CompilationInfo::kTypingEnabled) {
+      info.MarkAsTypingEnabled();
+    }
+    CHECK(Rewriter::Rewrite(&info));
+    CHECK(Scope::Analyze(&info));
+    CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+    Pipeline pipeline(&info);
+    Handle<Code> code = pipeline.GenerateCode();
+    if (FLAG_turbo_deoptimization) {
+      info.context()->native_context()->AddOptimizedCode(*code);
+    }
+
+    CHECK(!code.is_null());
+    function->ReplaceCode(*code);
+#elif USE_CRANKSHAFT
+    Handle<Code> unoptimized = Handle<Code>(function->code());
+    Handle<Code> code = Compiler::GetOptimizedCode(function, unoptimized,
+                                                   Compiler::NOT_CONCURRENT);
+    CHECK(!code.is_null());
+#if ENABLE_DISASSEMBLER
+    if (FLAG_print_opt_code) {
+      CodeTracer::Scope tracing_scope(isolate->GetCodeTracer());
+      code->Disassemble("test code", tracing_scope.file());
+    }
+#endif
+    function->ReplaceCode(*code);
+#endif
+    return function;
+  }
+
+  MaybeHandle<Object> Call(Handle<Object> a, Handle<Object> b) {
+    Handle<Object> args[] = {a, b};
+    return Execution::Call(isolate, function, undefined(), 2, args, false);
+  }
+
+  void CheckThrows(Handle<Object> a, Handle<Object> b) {
+    TryCatch try_catch;
+    MaybeHandle<Object> no_result = Call(a, b);
+    CHECK(isolate->has_pending_exception());
+    CHECK(try_catch.HasCaught());
+    CHECK(no_result.is_null());
+    // TODO(mstarzinger): Temporary workaround for issue chromium:362388.
+    isolate->OptionalRescheduleException(true);
+  }
+
+  v8::Handle<v8::Message> CheckThrowsReturnMessage(Handle<Object> a,
+                                                   Handle<Object> b) {
+    TryCatch try_catch;
+    MaybeHandle<Object> no_result = Call(a, b);
+    CHECK(isolate->has_pending_exception());
+    CHECK(try_catch.HasCaught());
+    CHECK(no_result.is_null());
+    // TODO(mstarzinger): Calling OptionalRescheduleException is a dirty hack,
+    // it's the only way to make Message() not to assert because an external
+    // exception has been caught by the try_catch.
+    isolate->OptionalRescheduleException(true);
+    return try_catch.Message();
+  }
+
+  void CheckCall(Handle<Object> expected, Handle<Object> a, Handle<Object> b) {
+    Handle<Object> result = Call(a, b).ToHandleChecked();
+    CHECK(expected->SameValue(*result));
+  }
+
+  void CheckCall(Handle<Object> expected, Handle<Object> a) {
+    CheckCall(expected, a, undefined());
+  }
+
+  void CheckCall(Handle<Object> expected) {
+    CheckCall(expected, undefined(), undefined());
+  }
+
+  void CheckCall(double expected, double a, double b) {
+    CheckCall(Val(expected), Val(a), Val(b));
+  }
+
+  void CheckTrue(Handle<Object> a, Handle<Object> b) {
+    CheckCall(true_value(), a, b);
+  }
+
+  void CheckTrue(Handle<Object> a) { CheckCall(true_value(), a, undefined()); }
+
+  void CheckTrue(double a, double b) {
+    CheckCall(true_value(), Val(a), Val(b));
+  }
+
+  void CheckFalse(Handle<Object> a, Handle<Object> b) {
+    CheckCall(false_value(), a, b);
+  }
+
+  void CheckFalse(Handle<Object> a) {
+    CheckCall(false_value(), a, undefined());
+  }
+
+  void CheckFalse(double a, double b) {
+    CheckCall(false_value(), Val(a), Val(b));
+  }
+
+  Handle<JSFunction> NewFunction(const char* source) {
+    return v8::Utils::OpenHandle(
+        *v8::Handle<v8::Function>::Cast(CompileRun(source)));
+  }
+
+  Handle<JSObject> NewObject(const char* source) {
+    return v8::Utils::OpenHandle(
+        *v8::Handle<v8::Object>::Cast(CompileRun(source)));
+  }
+
+  Handle<String> Val(const char* string) {
+    return isolate->factory()->InternalizeUtf8String(string);
+  }
+
+  Handle<Object> Val(double value) {
+    return isolate->factory()->NewNumber(value);
+  }
+
+  Handle<Object> infinity() { return isolate->factory()->infinity_value(); }
+
+  Handle<Object> minus_infinity() { return Val(-V8_INFINITY); }
+
+  Handle<Object> nan() { return isolate->factory()->nan_value(); }
+
+  Handle<Object> undefined() { return isolate->factory()->undefined_value(); }
+
+  Handle<Object> null() { return isolate->factory()->null_value(); }
+
+  Handle<Object> true_value() { return isolate->factory()->true_value(); }
+
+  Handle<Object> false_value() { return isolate->factory()->false_value(); }
+
+ private:
+  uint32_t flags_;
+};
+}
+}
+}  // namespace v8::internal::compiler
+
+#endif  // V8_CCTEST_COMPILER_FUNCTION_TESTER_H_
diff --git a/test/cctest/compiler/graph-builder-tester.cc b/test/cctest/compiler/graph-builder-tester.cc
new file mode 100644
index 0000000..bfa8226
--- /dev/null
+++ b/test/cctest/compiler/graph-builder-tester.cc
@@ -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.
+
+#include "test/cctest/compiler/graph-builder-tester.h"
+
+#include "src/compiler/linkage.h"
+#include "src/compiler/pipeline.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+MachineCallHelper::MachineCallHelper(Zone* zone, MachineSignature* machine_sig)
+    : CallHelper(zone->isolate(), machine_sig),
+      parameters_(NULL),
+      graph_(NULL) {}
+
+
+void MachineCallHelper::InitParameters(GraphBuilder* builder,
+                                       CommonOperatorBuilder* common) {
+  DCHECK_EQ(NULL, parameters_);
+  graph_ = builder->graph();
+  int param_count = static_cast<int>(parameter_count());
+  if (param_count == 0) return;
+  parameters_ = graph_->zone()->NewArray<Node*>(param_count);
+  for (int i = 0; i < param_count; ++i) {
+    parameters_[i] = builder->NewNode(common->Parameter(i), graph_->start());
+  }
+}
+
+
+byte* MachineCallHelper::Generate() {
+  DCHECK(parameter_count() == 0 || parameters_ != NULL);
+  if (!Pipeline::SupportedBackend()) return NULL;
+  if (code_.is_null()) {
+    Zone* zone = graph_->zone();
+    CompilationInfo info(zone->isolate(), zone);
+    Linkage linkage(&info,
+                    Linkage::GetSimplifiedCDescriptor(zone, machine_sig_));
+    Pipeline pipeline(&info);
+    code_ = pipeline.GenerateCodeForMachineGraph(&linkage, graph_);
+  }
+  return code_.ToHandleChecked()->entry();
+}
+
+
+Node* MachineCallHelper::Parameter(size_t index) {
+  DCHECK_NE(NULL, parameters_);
+  DCHECK(index < parameter_count());
+  return parameters_[index];
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/test/cctest/compiler/graph-builder-tester.h b/test/cctest/compiler/graph-builder-tester.h
new file mode 100644
index 0000000..df79250
--- /dev/null
+++ b/test/cctest/compiler/graph-builder-tester.h
@@ -0,0 +1,107 @@
+// 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_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_
+#define V8_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph-builder.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/simplified-operator.h"
+#include "test/cctest/compiler/call-tester.h"
+#include "test/cctest/compiler/simplified-graph-builder.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// A class that just passes node creation on to the Graph.
+class DirectGraphBuilder : public GraphBuilder {
+ public:
+  explicit DirectGraphBuilder(Graph* graph) : GraphBuilder(graph) {}
+  virtual ~DirectGraphBuilder() {}
+
+ protected:
+  virtual Node* MakeNode(const Operator* op, int value_input_count,
+                         Node** value_inputs) FINAL {
+    return graph()->NewNode(op, value_input_count, value_inputs);
+  }
+};
+
+
+class MachineCallHelper : public CallHelper {
+ public:
+  MachineCallHelper(Zone* zone, MachineSignature* machine_sig);
+
+  Node* Parameter(size_t index);
+
+  void GenerateCode() { Generate(); }
+
+ protected:
+  virtual byte* Generate();
+  void InitParameters(GraphBuilder* builder, CommonOperatorBuilder* common);
+
+ protected:
+  size_t parameter_count() const { return machine_sig_->parameter_count(); }
+
+ private:
+  Node** parameters_;
+  // TODO(dcarney): shouldn't need graph stored.
+  Graph* graph_;
+  MaybeHandle<Code> code_;
+};
+
+
+class GraphAndBuilders {
+ public:
+  explicit GraphAndBuilders(Zone* zone)
+      : main_graph_(new (zone) Graph(zone)),
+        main_common_(zone),
+        main_simplified_(zone) {}
+
+ protected:
+  // Prefixed with main_ to avoid naiming conflicts.
+  Graph* main_graph_;
+  CommonOperatorBuilder main_common_;
+  MachineOperatorBuilder main_machine_;
+  SimplifiedOperatorBuilder main_simplified_;
+};
+
+
+template <typename ReturnType>
+class GraphBuilderTester
+    : public HandleAndZoneScope,
+      private GraphAndBuilders,
+      public MachineCallHelper,
+      public SimplifiedGraphBuilder,
+      public CallHelper2<ReturnType, GraphBuilderTester<ReturnType> > {
+ public:
+  explicit GraphBuilderTester(MachineType p0 = kMachNone,
+                              MachineType p1 = kMachNone,
+                              MachineType p2 = kMachNone,
+                              MachineType p3 = kMachNone,
+                              MachineType p4 = kMachNone)
+      : GraphAndBuilders(main_zone()),
+        MachineCallHelper(
+            main_zone(),
+            MakeMachineSignature(
+                main_zone(), ReturnValueTraits<ReturnType>::Representation(),
+                p0, p1, p2, p3, p4)),
+        SimplifiedGraphBuilder(main_graph_, &main_common_, &main_machine_,
+                               &main_simplified_) {
+    Begin(static_cast<int>(parameter_count()));
+    InitParameters(this, &main_common_);
+  }
+  virtual ~GraphBuilderTester() {}
+
+  Factory* factory() const { return isolate()->factory(); }
+};
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_GRAPH_BUILDER_TESTER_H_
diff --git a/test/cctest/compiler/graph-tester.h b/test/cctest/compiler/graph-tester.h
new file mode 100644
index 0000000..e569245
--- /dev/null
+++ b/test/cctest/compiler/graph-tester.h
@@ -0,0 +1,42 @@
+// 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_CCTEST_COMPILER_GRAPH_TESTER_H_
+#define V8_CCTEST_COMPILER_GRAPH_TESTER_H_
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class GraphTester : public HandleAndZoneScope, public Graph {
+ public:
+  GraphTester() : Graph(main_zone()) {}
+};
+
+
+class GraphWithStartNodeTester : public GraphTester {
+ public:
+  explicit GraphWithStartNodeTester(int num_parameters = 0)
+      : builder_(main_zone()),
+        start_node_(NewNode(builder_.Start(num_parameters))) {
+    SetStart(start_node_);
+  }
+
+  Node* start_node() { return start_node_; }
+
+ private:
+  CommonOperatorBuilder builder_;
+  Node* start_node_;
+};
+}
+}
+}  // namespace v8::internal::compiler
+
+#endif  // V8_CCTEST_COMPILER_GRAPH_TESTER_H_
diff --git a/test/cctest/compiler/instruction-selector-tester.h b/test/cctest/compiler/instruction-selector-tester.h
new file mode 100644
index 0000000..3a28b2e
--- /dev/null
+++ b/test/cctest/compiler/instruction-selector-tester.h
@@ -0,0 +1,127 @@
+// 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_CCTEST_COMPILER_INSTRUCTION_SELECTOR_TEST_H_
+#define V8_CCTEST_COMPILER_INSTRUCTION_SELECTOR_TEST_H_
+
+#include <deque>
+#include <set>
+
+#include "src/compiler/instruction-selector.h"
+#include "src/compiler/raw-machine-assembler.h"
+#include "src/ostreams.h"
+#include "test/cctest/cctest.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+typedef std::set<int> VirtualRegisterSet;
+
+enum InstructionSelectorTesterMode { kTargetMode, kInternalMode };
+
+class InstructionSelectorTester : public HandleAndZoneScope,
+                                  public RawMachineAssembler {
+ public:
+  enum Mode { kTargetMode, kInternalMode };
+
+  static const int kParameterCount = 3;
+  static MachineType* BuildParameterArray(Zone* zone) {
+    MachineType* array = zone->NewArray<MachineType>(kParameterCount);
+    for (int i = 0; i < kParameterCount; ++i) {
+      array[i] = kMachInt32;
+    }
+    return array;
+  }
+
+  InstructionSelectorTester()
+      : RawMachineAssembler(
+            new (main_zone()) Graph(main_zone()),
+            new (main_zone()) MachineCallDescriptorBuilder(
+                kMachInt32, kParameterCount, BuildParameterArray(main_zone())),
+            kMachPtr) {}
+
+  void SelectInstructions(CpuFeature feature) {
+    SelectInstructions(InstructionSelector::Features(feature));
+  }
+
+  void SelectInstructions(CpuFeature feature1, CpuFeature feature2) {
+    SelectInstructions(InstructionSelector::Features(feature1, feature2));
+  }
+
+  void SelectInstructions(Mode mode = kTargetMode) {
+    SelectInstructions(InstructionSelector::Features(), mode);
+  }
+
+  void SelectInstructions(InstructionSelector::Features features,
+                          Mode mode = kTargetMode) {
+    OFStream out(stdout);
+    Schedule* schedule = Export();
+    CHECK_NE(0, graph()->NodeCount());
+    CompilationInfo info(main_isolate(), main_zone());
+    Linkage linkage(&info, call_descriptor());
+    InstructionSequence sequence(&linkage, graph(), schedule);
+    SourcePositionTable source_positions(graph());
+    InstructionSelector selector(&sequence, &source_positions, features);
+    selector.SelectInstructions();
+    out << "--- Code sequence after instruction selection --- " << endl
+        << sequence;
+    for (InstructionSequence::const_iterator i = sequence.begin();
+         i != sequence.end(); ++i) {
+      Instruction* instr = *i;
+      if (instr->opcode() < 0) continue;
+      if (mode == kTargetMode) {
+        switch (ArchOpcodeField::decode(instr->opcode())) {
+#define CASE(Name) \
+  case k##Name:    \
+    break;
+          TARGET_ARCH_OPCODE_LIST(CASE)
+#undef CASE
+          default:
+            continue;
+        }
+      }
+      code.push_back(instr);
+    }
+    for (int vreg = 0; vreg < sequence.VirtualRegisterCount(); ++vreg) {
+      if (sequence.IsDouble(vreg)) {
+        CHECK(!sequence.IsReference(vreg));
+        doubles.insert(vreg);
+      }
+      if (sequence.IsReference(vreg)) {
+        CHECK(!sequence.IsDouble(vreg));
+        references.insert(vreg);
+      }
+    }
+    immediates.assign(sequence.immediates().begin(),
+                      sequence.immediates().end());
+  }
+
+  int32_t ToInt32(const InstructionOperand* operand) const {
+    size_t i = operand->index();
+    CHECK(i < immediates.size());
+    CHECK_EQ(InstructionOperand::IMMEDIATE, operand->kind());
+    return immediates[i].ToInt32();
+  }
+
+  std::deque<Instruction*> code;
+  VirtualRegisterSet doubles;
+  VirtualRegisterSet references;
+  std::deque<Constant> immediates;
+};
+
+
+static inline void CheckSameVreg(InstructionOperand* exp,
+                                 InstructionOperand* val) {
+  CHECK_EQ(InstructionOperand::UNALLOCATED, exp->kind());
+  CHECK_EQ(InstructionOperand::UNALLOCATED, val->kind());
+  CHECK_EQ(UnallocatedOperand::cast(exp)->virtual_register(),
+           UnallocatedOperand::cast(val)->virtual_register());
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_INSTRUCTION_SELECTOR_TEST_H_
diff --git a/test/cctest/compiler/simplified-graph-builder.cc b/test/cctest/compiler/simplified-graph-builder.cc
new file mode 100644
index 0000000..c44d5ed
--- /dev/null
+++ b/test/cctest/compiler/simplified-graph-builder.cc
@@ -0,0 +1,90 @@
+// 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 "test/cctest/compiler/simplified-graph-builder.h"
+
+#include "src/compiler/operator-properties.h"
+#include "src/compiler/operator-properties-inl.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+SimplifiedGraphBuilder::SimplifiedGraphBuilder(
+    Graph* graph, CommonOperatorBuilder* common,
+    MachineOperatorBuilder* machine, SimplifiedOperatorBuilder* simplified)
+    : GraphBuilder(graph),
+      effect_(NULL),
+      return_(NULL),
+      common_(common),
+      machine_(machine),
+      simplified_(simplified) {}
+
+
+void SimplifiedGraphBuilder::Begin(int num_parameters) {
+  DCHECK(graph()->start() == NULL);
+  Node* start = graph()->NewNode(common()->Start(num_parameters));
+  graph()->SetStart(start);
+  effect_ = start;
+}
+
+
+void SimplifiedGraphBuilder::Return(Node* value) {
+  return_ =
+      graph()->NewNode(common()->Return(), value, effect_, graph()->start());
+  effect_ = NULL;
+}
+
+
+void SimplifiedGraphBuilder::End() {
+  Node* end = graph()->NewNode(common()->End(), return_);
+  graph()->SetEnd(end);
+}
+
+
+Node* SimplifiedGraphBuilder::MakeNode(const Operator* op,
+                                       int value_input_count,
+                                       Node** value_inputs) {
+  DCHECK(op->InputCount() == value_input_count);
+
+  DCHECK(!OperatorProperties::HasContextInput(op));
+  DCHECK(!OperatorProperties::HasFrameStateInput(op));
+  bool has_control = OperatorProperties::GetControlInputCount(op) == 1;
+  bool has_effect = OperatorProperties::GetEffectInputCount(op) == 1;
+
+  DCHECK(OperatorProperties::GetControlInputCount(op) < 2);
+  DCHECK(OperatorProperties::GetEffectInputCount(op) < 2);
+
+  Node* result = NULL;
+  if (!has_control && !has_effect) {
+    result = graph()->NewNode(op, value_input_count, value_inputs);
+  } else {
+    int input_count_with_deps = value_input_count;
+    if (has_control) ++input_count_with_deps;
+    if (has_effect) ++input_count_with_deps;
+    Node** buffer = zone()->NewArray<Node*>(input_count_with_deps);
+    memcpy(buffer, value_inputs, kPointerSize * value_input_count);
+    Node** current_input = buffer + value_input_count;
+    if (has_effect) {
+      *current_input++ = effect_;
+    }
+    if (has_control) {
+      *current_input++ = graph()->start();
+    }
+    result = graph()->NewNode(op, input_count_with_deps, buffer);
+    if (has_effect) {
+      effect_ = result;
+    }
+    if (OperatorProperties::HasControlOutput(result->op())) {
+      // This graph builder does not support control flow.
+      UNREACHABLE();
+    }
+  }
+
+  return result;
+}
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
diff --git a/test/cctest/compiler/simplified-graph-builder.h b/test/cctest/compiler/simplified-graph-builder.h
new file mode 100644
index 0000000..1b637b7
--- /dev/null
+++ b/test/cctest/compiler/simplified-graph-builder.h
@@ -0,0 +1,156 @@
+// 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_CCTEST_COMPILER_SIMPLIFIED_GRAPH_BUILDER_H_
+#define V8_CCTEST_COMPILER_SIMPLIFIED_GRAPH_BUILDER_H_
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph-builder.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/simplified-operator.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/call-tester.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+class SimplifiedGraphBuilder : public GraphBuilder {
+ public:
+  SimplifiedGraphBuilder(Graph* graph, CommonOperatorBuilder* common,
+                         MachineOperatorBuilder* machine,
+                         SimplifiedOperatorBuilder* simplified);
+  virtual ~SimplifiedGraphBuilder() {}
+
+  Zone* zone() const { return graph()->zone(); }
+  Isolate* isolate() const { return zone()->isolate(); }
+  CommonOperatorBuilder* common() const { return common_; }
+  MachineOperatorBuilder* machine() const { return machine_; }
+  SimplifiedOperatorBuilder* simplified() const { return simplified_; }
+
+  // Initialize graph and builder.
+  void Begin(int num_parameters);
+
+  void Return(Node* value);
+
+  // Close the graph.
+  void End();
+
+  Node* PointerConstant(void* value) {
+    intptr_t intptr_value = reinterpret_cast<intptr_t>(value);
+    return kPointerSize == 8 ? NewNode(common()->Int64Constant(intptr_value))
+                             : Int32Constant(static_cast<int>(intptr_value));
+  }
+  Node* Int32Constant(int32_t value) {
+    return NewNode(common()->Int32Constant(value));
+  }
+  Node* HeapConstant(Handle<Object> object) {
+    Unique<Object> val = Unique<Object>::CreateUninitialized(object);
+    return NewNode(common()->HeapConstant(val));
+  }
+
+  Node* BooleanNot(Node* a) { return NewNode(simplified()->BooleanNot(), a); }
+
+  Node* NumberEqual(Node* a, Node* b) {
+    return NewNode(simplified()->NumberEqual(), a, b);
+  }
+  Node* NumberLessThan(Node* a, Node* b) {
+    return NewNode(simplified()->NumberLessThan(), a, b);
+  }
+  Node* NumberLessThanOrEqual(Node* a, Node* b) {
+    return NewNode(simplified()->NumberLessThanOrEqual(), a, b);
+  }
+  Node* NumberAdd(Node* a, Node* b) {
+    return NewNode(simplified()->NumberAdd(), a, b);
+  }
+  Node* NumberSubtract(Node* a, Node* b) {
+    return NewNode(simplified()->NumberSubtract(), a, b);
+  }
+  Node* NumberMultiply(Node* a, Node* b) {
+    return NewNode(simplified()->NumberMultiply(), a, b);
+  }
+  Node* NumberDivide(Node* a, Node* b) {
+    return NewNode(simplified()->NumberDivide(), a, b);
+  }
+  Node* NumberModulus(Node* a, Node* b) {
+    return NewNode(simplified()->NumberModulus(), a, b);
+  }
+  Node* NumberToInt32(Node* a) {
+    return NewNode(simplified()->NumberToInt32(), a);
+  }
+  Node* NumberToUint32(Node* a) {
+    return NewNode(simplified()->NumberToUint32(), a);
+  }
+
+  Node* StringEqual(Node* a, Node* b) {
+    return NewNode(simplified()->StringEqual(), a, b);
+  }
+  Node* StringLessThan(Node* a, Node* b) {
+    return NewNode(simplified()->StringLessThan(), a, b);
+  }
+  Node* StringLessThanOrEqual(Node* a, Node* b) {
+    return NewNode(simplified()->StringLessThanOrEqual(), a, b);
+  }
+  Node* StringAdd(Node* a, Node* b) {
+    return NewNode(simplified()->StringAdd(), a, b);
+  }
+
+  Node* ChangeTaggedToInt32(Node* a) {
+    return NewNode(simplified()->ChangeTaggedToInt32(), a);
+  }
+  Node* ChangeTaggedToUint32(Node* a) {
+    return NewNode(simplified()->ChangeTaggedToUint32(), a);
+  }
+  Node* ChangeTaggedToFloat64(Node* a) {
+    return NewNode(simplified()->ChangeTaggedToFloat64(), a);
+  }
+  Node* ChangeInt32ToTagged(Node* a) {
+    return NewNode(simplified()->ChangeInt32ToTagged(), a);
+  }
+  Node* ChangeUint32ToTagged(Node* a) {
+    return NewNode(simplified()->ChangeUint32ToTagged(), a);
+  }
+  Node* ChangeFloat64ToTagged(Node* a) {
+    return NewNode(simplified()->ChangeFloat64ToTagged(), a);
+  }
+  Node* ChangeBoolToBit(Node* a) {
+    return NewNode(simplified()->ChangeBoolToBit(), a);
+  }
+  Node* ChangeBitToBool(Node* a) {
+    return NewNode(simplified()->ChangeBitToBool(), a);
+  }
+
+  Node* LoadField(const FieldAccess& access, Node* object) {
+    return NewNode(simplified()->LoadField(access), object);
+  }
+  Node* StoreField(const FieldAccess& access, Node* object, Node* value) {
+    return NewNode(simplified()->StoreField(access), object, value);
+  }
+  Node* LoadElement(const ElementAccess& access, Node* object, Node* index,
+                    Node* length) {
+    return NewNode(simplified()->LoadElement(access), object, index, length);
+  }
+  Node* StoreElement(const ElementAccess& access, Node* object, Node* index,
+                     Node* length, Node* value) {
+    return NewNode(simplified()->StoreElement(access), object, index, length,
+                   value);
+  }
+
+ protected:
+  virtual Node* MakeNode(const Operator* op, int value_input_count,
+                         Node** value_inputs) FINAL;
+
+ private:
+  Node* effect_;
+  Node* return_;
+  CommonOperatorBuilder* common_;
+  MachineOperatorBuilder* machine_;
+  SimplifiedOperatorBuilder* simplified_;
+};
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_SIMPLIFIED_GRAPH_BUILDER_H_
diff --git a/test/cctest/compiler/test-branch-combine.cc b/test/cctest/compiler/test-branch-combine.cc
new file mode 100644
index 0000000..cd3472d
--- /dev/null
+++ b/test/cctest/compiler/test-branch-combine.cc
@@ -0,0 +1,462 @@
+// 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/v8.h"
+
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef RawMachineAssembler::Label MLabel;
+
+static IrOpcode::Value int32cmp_opcodes[] = {
+    IrOpcode::kWord32Equal, IrOpcode::kInt32LessThan,
+    IrOpcode::kInt32LessThanOrEqual, IrOpcode::kUint32LessThan,
+    IrOpcode::kUint32LessThanOrEqual};
+
+
+TEST(BranchCombineWord32EqualZero_1) {
+  // Test combining a branch with x == 0
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  int32_t eq_constant = -1033;
+  int32_t ne_constant = 825118;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Word32Equal(p0, m.Int32Constant(0)), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t a = *i;
+    int32_t expect = a == 0 ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineWord32EqualZero_chain) {
+  // Test combining a branch with a chain of x == 0 == 0 == 0 ...
+  int32_t eq_constant = -1133;
+  int32_t ne_constant = 815118;
+
+  for (int k = 0; k < 6; k++) {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32);
+    Node* p0 = m.Parameter(0);
+    MLabel blocka, blockb;
+    Node* cond = p0;
+    for (int j = 0; j < k; j++) {
+      cond = m.Word32Equal(cond, m.Int32Constant(0));
+    }
+    m.Branch(cond, &blocka, &blockb);
+    m.Bind(&blocka);
+    m.Return(m.Int32Constant(eq_constant));
+    m.Bind(&blockb);
+    m.Return(m.Int32Constant(ne_constant));
+
+    FOR_INT32_INPUTS(i) {
+      int32_t a = *i;
+      int32_t expect = (k & 1) == 1 ? (a == 0 ? eq_constant : ne_constant)
+                                    : (a == 0 ? ne_constant : eq_constant);
+      CHECK_EQ(expect, m.Call(a));
+    }
+  }
+}
+
+
+TEST(BranchCombineInt32LessThanZero_1) {
+  // Test combining a branch with x < 0
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  int32_t eq_constant = -1433;
+  int32_t ne_constant = 845118;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Int32LessThan(p0, m.Int32Constant(0)), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t a = *i;
+    int32_t expect = a < 0 ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineUint32LessThan100_1) {
+  // Test combining a branch with x < 100
+  RawMachineAssemblerTester<int32_t> m(kMachUint32);
+  int32_t eq_constant = 1471;
+  int32_t ne_constant = 88845718;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Uint32LessThan(p0, m.Int32Constant(100)), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_UINT32_INPUTS(i) {
+    uint32_t a = *i;
+    int32_t expect = a < 100 ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineUint32LessThanOrEqual100_1) {
+  // Test combining a branch with x <= 100
+  RawMachineAssemblerTester<int32_t> m(kMachUint32);
+  int32_t eq_constant = 1479;
+  int32_t ne_constant = 77845719;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Uint32LessThanOrEqual(p0, m.Int32Constant(100)), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_UINT32_INPUTS(i) {
+    uint32_t a = *i;
+    int32_t expect = a <= 100 ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineZeroLessThanInt32_1) {
+  // Test combining a branch with 0 < x
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  int32_t eq_constant = -2033;
+  int32_t ne_constant = 225118;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Int32LessThan(m.Int32Constant(0), p0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t a = *i;
+    int32_t expect = 0 < a ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineInt32GreaterThanZero_1) {
+  // Test combining a branch with x > 0
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  int32_t eq_constant = -1073;
+  int32_t ne_constant = 825178;
+  Node* p0 = m.Parameter(0);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Int32GreaterThan(p0, m.Int32Constant(0)), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t a = *i;
+    int32_t expect = a > 0 ? eq_constant : ne_constant;
+    CHECK_EQ(expect, m.Call(a));
+  }
+}
+
+
+TEST(BranchCombineWord32EqualP) {
+  // Test combining a branch with an Word32Equal.
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+  int32_t eq_constant = -1035;
+  int32_t ne_constant = 825018;
+  Node* p0 = m.Parameter(0);
+  Node* p1 = m.Parameter(1);
+
+  MLabel blocka, blockb;
+  m.Branch(m.Word32Equal(p0, p1), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(eq_constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(ne_constant));
+
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t a = *i;
+      int32_t b = *j;
+      int32_t expect = a == b ? eq_constant : ne_constant;
+      CHECK_EQ(expect, m.Call(a, b));
+    }
+  }
+}
+
+
+TEST(BranchCombineWord32EqualI) {
+  int32_t eq_constant = -1135;
+  int32_t ne_constant = 925718;
+
+  for (int left = 0; left < 2; left++) {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      int32_t a = *i;
+
+      Node* p0 = m.Int32Constant(a);
+      Node* p1 = m.Parameter(0);
+
+      MLabel blocka, blockb;
+      if (left == 1) m.Branch(m.Word32Equal(p0, p1), &blocka, &blockb);
+      if (left == 0) m.Branch(m.Word32Equal(p1, p0), &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(eq_constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(ne_constant));
+
+      FOR_INT32_INPUTS(j) {
+        int32_t b = *j;
+        int32_t expect = a == b ? eq_constant : ne_constant;
+        CHECK_EQ(expect, m.Call(b));
+      }
+    }
+  }
+}
+
+
+TEST(BranchCombineInt32CmpP) {
+  int32_t eq_constant = -1235;
+  int32_t ne_constant = 725018;
+
+  for (int op = 0; op < 2; op++) {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+    Node* p0 = m.Parameter(0);
+    Node* p1 = m.Parameter(1);
+
+    MLabel blocka, blockb;
+    if (op == 0) m.Branch(m.Int32LessThan(p0, p1), &blocka, &blockb);
+    if (op == 1) m.Branch(m.Int32LessThanOrEqual(p0, p1), &blocka, &blockb);
+    m.Bind(&blocka);
+    m.Return(m.Int32Constant(eq_constant));
+    m.Bind(&blockb);
+    m.Return(m.Int32Constant(ne_constant));
+
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t a = *i;
+        int32_t b = *j;
+        int32_t expect = 0;
+        if (op == 0) expect = a < b ? eq_constant : ne_constant;
+        if (op == 1) expect = a <= b ? eq_constant : ne_constant;
+        CHECK_EQ(expect, m.Call(a, b));
+      }
+    }
+  }
+}
+
+
+TEST(BranchCombineInt32CmpI) {
+  int32_t eq_constant = -1175;
+  int32_t ne_constant = 927711;
+
+  for (int op = 0; op < 2; op++) {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      int32_t a = *i;
+      Node* p0 = m.Int32Constant(a);
+      Node* p1 = m.Parameter(0);
+
+      MLabel blocka, blockb;
+      if (op == 0) m.Branch(m.Int32LessThan(p0, p1), &blocka, &blockb);
+      if (op == 1) m.Branch(m.Int32LessThanOrEqual(p0, p1), &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(eq_constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(ne_constant));
+
+      FOR_INT32_INPUTS(j) {
+        int32_t b = *j;
+        int32_t expect = 0;
+        if (op == 0) expect = a < b ? eq_constant : ne_constant;
+        if (op == 1) expect = a <= b ? eq_constant : ne_constant;
+        CHECK_EQ(expect, m.Call(b));
+      }
+    }
+  }
+}
+
+
+// Now come the sophisticated tests for many input shape combinations.
+
+// Materializes a boolean (1 or 0) from a comparison.
+class CmpMaterializeBoolGen : public BinopGen<int32_t> {
+ public:
+  CompareWrapper w;
+  bool invert;
+
+  CmpMaterializeBoolGen(IrOpcode::Value opcode, bool i)
+      : w(opcode), invert(i) {}
+
+  virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
+    Node* cond = w.MakeNode(m, a, b);
+    if (invert) cond = m->Word32Equal(cond, m->Int32Constant(0));
+    m->Return(cond);
+  }
+  virtual int32_t expected(int32_t a, int32_t b) {
+    if (invert) return !w.Int32Compare(a, b) ? 1 : 0;
+    return w.Int32Compare(a, b) ? 1 : 0;
+  }
+};
+
+
+// Generates a branch and return one of two values from a comparison.
+class CmpBranchGen : public BinopGen<int32_t> {
+ public:
+  CompareWrapper w;
+  bool invert;
+  bool true_first;
+  int32_t eq_constant;
+  int32_t ne_constant;
+
+  CmpBranchGen(IrOpcode::Value opcode, bool i, bool t, int32_t eq, int32_t ne)
+      : w(opcode), invert(i), true_first(t), eq_constant(eq), ne_constant(ne) {}
+
+  virtual void gen(RawMachineAssemblerTester<int32_t>* m, Node* a, Node* b) {
+    MLabel blocka, blockb;
+    Node* cond = w.MakeNode(m, a, b);
+    if (invert) cond = m->Word32Equal(cond, m->Int32Constant(0));
+    m->Branch(cond, &blocka, &blockb);
+    if (true_first) {
+      m->Bind(&blocka);
+      m->Return(m->Int32Constant(eq_constant));
+      m->Bind(&blockb);
+      m->Return(m->Int32Constant(ne_constant));
+    } else {
+      m->Bind(&blockb);
+      m->Return(m->Int32Constant(ne_constant));
+      m->Bind(&blocka);
+      m->Return(m->Int32Constant(eq_constant));
+    }
+  }
+  virtual int32_t expected(int32_t a, int32_t b) {
+    if (invert) return !w.Int32Compare(a, b) ? eq_constant : ne_constant;
+    return w.Int32Compare(a, b) ? eq_constant : ne_constant;
+  }
+};
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_materialized) {
+  for (size_t i = 0; i < arraysize(int32cmp_opcodes); i++) {
+    CmpMaterializeBoolGen gen(int32cmp_opcodes[i], false);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_inverted_materialized) {
+  for (size_t i = 0; i < arraysize(int32cmp_opcodes); i++) {
+    CmpMaterializeBoolGen gen(int32cmp_opcodes[i], true);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_branch_true) {
+  for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
+    CmpBranchGen gen(int32cmp_opcodes[i], false, false, 995 + i, -1011 - i);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_branch_false) {
+  for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
+    CmpBranchGen gen(int32cmp_opcodes[i], false, true, 795 + i, -2011 - i);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_true) {
+  for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
+    CmpBranchGen gen(int32cmp_opcodes[i], true, false, 695 + i, -3011 - i);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineInt32CmpAllInputShapes_inverse_branch_false) {
+  for (int i = 0; i < static_cast<int>(arraysize(int32cmp_opcodes)); i++) {
+    CmpBranchGen gen(int32cmp_opcodes[i], true, true, 595 + i, -4011 - i);
+    Int32BinopInputShapeTester tester(&gen);
+    tester.TestAllInputShapes();
+  }
+}
+
+
+TEST(BranchCombineFloat64Compares) {
+  double inf = V8_INFINITY;
+  double nan = v8::base::OS::nan_value();
+  double inputs[] = {0.0, 1.0, -1.0, -inf, inf, nan};
+
+  int32_t eq_constant = -1733;
+  int32_t ne_constant = 915118;
+
+  double input_a = 0.0;
+  double input_b = 0.0;
+
+  CompareWrapper cmps[] = {CompareWrapper(IrOpcode::kFloat64Equal),
+                           CompareWrapper(IrOpcode::kFloat64LessThan),
+                           CompareWrapper(IrOpcode::kFloat64LessThanOrEqual)};
+
+  for (size_t c = 0; c < arraysize(cmps); c++) {
+    CompareWrapper cmp = cmps[c];
+    for (int invert = 0; invert < 2; invert++) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+      Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+
+      MLabel blocka, blockb;
+      Node* cond = cmp.MakeNode(&m, a, b);
+      if (invert) cond = m.Word32Equal(cond, m.Int32Constant(0));
+      m.Branch(cond, &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(eq_constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(ne_constant));
+
+      for (size_t i = 0; i < arraysize(inputs); i++) {
+        for (size_t j = 0; j < arraysize(inputs); j += 2) {
+          input_a = inputs[i];
+          input_b = inputs[i];
+          int32_t expected =
+              invert ? (cmp.Float64Compare(input_a, input_b) ? ne_constant
+                                                             : eq_constant)
+                     : (cmp.Float64Compare(input_a, input_b) ? eq_constant
+                                                             : ne_constant);
+          CHECK_EQ(expected, m.Call());
+        }
+      }
+    }
+  }
+}
+#endif  // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-changes-lowering.cc b/test/cctest/compiler/test-changes-lowering.cc
new file mode 100644
index 0000000..06308a0
--- /dev/null
+++ b/test/cctest/compiler/test-changes-lowering.cc
@@ -0,0 +1,413 @@
+// 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 <limits>
+
+#include "src/compiler/change-lowering.h"
+#include "src/compiler/control-builders.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/pipeline.h"
+#include "src/compiler/typer.h"
+#include "src/compiler/verifier.h"
+#include "src/execution.h"
+#include "src/globals.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+template <typename ReturnType>
+class ChangesLoweringTester : public GraphBuilderTester<ReturnType> {
+ public:
+  explicit ChangesLoweringTester(MachineType p0 = kMachNone)
+      : GraphBuilderTester<ReturnType>(p0),
+        typer(this->zone()),
+        javascript(this->zone()),
+        jsgraph(this->graph(), this->common(), &javascript, &typer,
+                this->machine()),
+        function(Handle<JSFunction>::null()) {}
+
+  Typer typer;
+  JSOperatorBuilder javascript;
+  JSGraph jsgraph;
+  Handle<JSFunction> function;
+
+  Node* start() { return this->graph()->start(); }
+
+  template <typename T>
+  T* CallWithPotentialGC() {
+    // TODO(titzer): we need to wrap the code in a JSFunction and call it via
+    // Execution::Call() so that the GC knows about the frame, can walk it,
+    // relocate the code object if necessary, etc.
+    // This is pretty ugly and at the least should be moved up to helpers.
+    if (function.is_null()) {
+      function =
+          v8::Utils::OpenHandle(*v8::Handle<v8::Function>::Cast(CompileRun(
+              "(function() { 'use strict'; return 2.7123; })")));
+      CompilationInfoWithZone info(function);
+      CHECK(Parser::Parse(&info));
+      info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+      CHECK(Rewriter::Rewrite(&info));
+      CHECK(Scope::Analyze(&info));
+      CHECK_NE(NULL, info.scope());
+      Handle<ScopeInfo> scope_info =
+          ScopeInfo::Create(info.scope(), info.zone());
+      info.shared_info()->set_scope_info(*scope_info);
+      Pipeline pipeline(&info);
+      Linkage linkage(&info);
+      Handle<Code> code =
+          pipeline.GenerateCodeForMachineGraph(&linkage, this->graph());
+      CHECK(!code.is_null());
+      function->ReplaceCode(*code);
+    }
+    Handle<Object>* args = NULL;
+    MaybeHandle<Object> result =
+        Execution::Call(this->isolate(), function, factory()->undefined_value(),
+                        0, args, false);
+    return T::cast(*result.ToHandleChecked());
+  }
+
+  void StoreFloat64(Node* node, double* ptr) {
+    Node* ptr_node = this->PointerConstant(ptr);
+    this->Store(kMachFloat64, ptr_node, node);
+  }
+
+  Node* LoadInt32(int32_t* ptr) {
+    Node* ptr_node = this->PointerConstant(ptr);
+    return this->Load(kMachInt32, ptr_node);
+  }
+
+  Node* LoadUint32(uint32_t* ptr) {
+    Node* ptr_node = this->PointerConstant(ptr);
+    return this->Load(kMachUint32, ptr_node);
+  }
+
+  Node* LoadFloat64(double* ptr) {
+    Node* ptr_node = this->PointerConstant(ptr);
+    return this->Load(kMachFloat64, ptr_node);
+  }
+
+  void CheckNumber(double expected, Object* number) {
+    CHECK(this->isolate()->factory()->NewNumber(expected)->SameValue(number));
+  }
+
+  void BuildAndLower(const Operator* op) {
+    // We build a graph by hand here, because the raw machine assembler
+    // does not add the correct control and effect nodes.
+    Node* p0 = this->Parameter(0);
+    Node* change = this->graph()->NewNode(op, p0);
+    Node* ret = this->graph()->NewNode(this->common()->Return(), change,
+                                       this->start(), this->start());
+    Node* end = this->graph()->NewNode(this->common()->End(), ret);
+    this->graph()->SetEnd(end);
+    LowerChange(change);
+  }
+
+  void BuildStoreAndLower(const Operator* op, const Operator* store_op,
+                          void* location) {
+    // We build a graph by hand here, because the raw machine assembler
+    // does not add the correct control and effect nodes.
+    Node* p0 = this->Parameter(0);
+    Node* change = this->graph()->NewNode(op, p0);
+    Node* store = this->graph()->NewNode(
+        store_op, this->PointerConstant(location), this->Int32Constant(0),
+        change, this->start(), this->start());
+    Node* ret = this->graph()->NewNode(
+        this->common()->Return(), this->Int32Constant(0), store, this->start());
+    Node* end = this->graph()->NewNode(this->common()->End(), ret);
+    this->graph()->SetEnd(end);
+    LowerChange(change);
+  }
+
+  void BuildLoadAndLower(const Operator* op, const Operator* load_op,
+                         void* location) {
+    // We build a graph by hand here, because the raw machine assembler
+    // does not add the correct control and effect nodes.
+    Node* load =
+        this->graph()->NewNode(load_op, this->PointerConstant(location),
+                               this->Int32Constant(0), this->start());
+    Node* change = this->graph()->NewNode(op, load);
+    Node* ret = this->graph()->NewNode(this->common()->Return(), change,
+                                       this->start(), this->start());
+    Node* end = this->graph()->NewNode(this->common()->End(), ret);
+    this->graph()->SetEnd(end);
+    LowerChange(change);
+  }
+
+  void LowerChange(Node* change) {
+    // Run the graph reducer with changes lowering on a single node.
+    CompilationInfo info(this->isolate(), this->zone());
+    Linkage linkage(&info);
+    ChangeLowering lowering(&jsgraph, &linkage);
+    GraphReducer reducer(this->graph());
+    reducer.AddReducer(&lowering);
+    reducer.ReduceNode(change);
+    Verifier::Run(this->graph());
+  }
+
+  Factory* factory() { return this->isolate()->factory(); }
+  Heap* heap() { return this->isolate()->heap(); }
+};
+
+
+TEST(RunChangeTaggedToInt32) {
+  // Build and lower a graph by hand.
+  ChangesLoweringTester<int32_t> t(kMachAnyTagged);
+  t.BuildAndLower(t.simplified()->ChangeTaggedToInt32());
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_INT32_INPUTS(i) {
+      int32_t input = *i;
+
+      if (Smi::IsValid(input)) {
+        int32_t result = t.Call(Smi::FromInt(input));
+        CHECK_EQ(input, result);
+      }
+
+      {
+        Handle<Object> number = t.factory()->NewNumber(input);
+        int32_t result = t.Call(*number);
+        CHECK_EQ(input, result);
+      }
+
+      {
+        Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
+        int32_t result = t.Call(*number);
+        CHECK_EQ(input, result);
+      }
+    }
+  }
+}
+
+
+TEST(RunChangeTaggedToUint32) {
+  // Build and lower a graph by hand.
+  ChangesLoweringTester<uint32_t> t(kMachAnyTagged);
+  t.BuildAndLower(t.simplified()->ChangeTaggedToUint32());
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_UINT32_INPUTS(i) {
+      uint32_t input = *i;
+
+      if (Smi::IsValid(input)) {
+        uint32_t result = t.Call(Smi::FromInt(input));
+        CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
+      }
+
+      {
+        Handle<Object> number = t.factory()->NewNumber(input);
+        uint32_t result = t.Call(*number);
+        CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
+      }
+
+      {
+        Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
+        uint32_t result = t.Call(*number);
+        CHECK_EQ(static_cast<int32_t>(input), static_cast<int32_t>(result));
+      }
+    }
+  }
+}
+
+
+TEST(RunChangeTaggedToFloat64) {
+  ChangesLoweringTester<int32_t> t(kMachAnyTagged);
+  double result;
+
+  t.BuildStoreAndLower(
+      t.simplified()->ChangeTaggedToFloat64(),
+      t.machine()->Store(StoreRepresentation(kMachFloat64, kNoWriteBarrier)),
+      &result);
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_INT32_INPUTS(i) {
+      int32_t input = *i;
+
+      if (Smi::IsValid(input)) {
+        t.Call(Smi::FromInt(input));
+        CHECK_EQ(input, static_cast<int32_t>(result));
+      }
+
+      {
+        Handle<Object> number = t.factory()->NewNumber(input);
+        t.Call(*number);
+        CHECK_EQ(input, static_cast<int32_t>(result));
+      }
+
+      {
+        Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
+        t.Call(*number);
+        CHECK_EQ(input, static_cast<int32_t>(result));
+      }
+    }
+  }
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_FLOAT64_INPUTS(i) {
+      double input = *i;
+      {
+        Handle<Object> number = t.factory()->NewNumber(input);
+        t.Call(*number);
+        CHECK_EQ(input, result);
+      }
+
+      {
+        Handle<HeapNumber> number = t.factory()->NewHeapNumber(input);
+        t.Call(*number);
+        CHECK_EQ(input, result);
+      }
+    }
+  }
+}
+
+
+TEST(RunChangeBoolToBit) {
+  ChangesLoweringTester<int32_t> t(kMachAnyTagged);
+  t.BuildAndLower(t.simplified()->ChangeBoolToBit());
+
+  if (Pipeline::SupportedTarget()) {
+    Object* true_obj = t.heap()->true_value();
+    int32_t result = t.Call(true_obj);
+    CHECK_EQ(1, result);
+  }
+
+  if (Pipeline::SupportedTarget()) {
+    Object* false_obj = t.heap()->false_value();
+    int32_t result = t.Call(false_obj);
+    CHECK_EQ(0, result);
+  }
+}
+
+
+TEST(RunChangeBitToBool) {
+  ChangesLoweringTester<Object*> t(kMachInt32);
+  t.BuildAndLower(t.simplified()->ChangeBitToBool());
+
+  if (Pipeline::SupportedTarget()) {
+    Object* result = t.Call(1);
+    Object* true_obj = t.heap()->true_value();
+    CHECK_EQ(true_obj, result);
+  }
+
+  if (Pipeline::SupportedTarget()) {
+    Object* result = t.Call(0);
+    Object* false_obj = t.heap()->false_value();
+    CHECK_EQ(false_obj, result);
+  }
+}
+
+
+#if V8_TURBOFAN_BACKEND
+// TODO(titzer): disabled on ARM
+
+TEST(RunChangeInt32ToTaggedSmi) {
+  ChangesLoweringTester<Object*> t;
+  int32_t input;
+  t.BuildLoadAndLower(t.simplified()->ChangeInt32ToTagged(),
+                      t.machine()->Load(kMachInt32), &input);
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_INT32_INPUTS(i) {
+      input = *i;
+      if (!Smi::IsValid(input)) continue;
+      Object* result = t.Call();
+      t.CheckNumber(static_cast<double>(input), result);
+    }
+  }
+}
+
+
+TEST(RunChangeUint32ToTaggedSmi) {
+  ChangesLoweringTester<Object*> t;
+  uint32_t input;
+  t.BuildLoadAndLower(t.simplified()->ChangeUint32ToTagged(),
+                      t.machine()->Load(kMachUint32), &input);
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_UINT32_INPUTS(i) {
+      input = *i;
+      if (input > static_cast<uint32_t>(Smi::kMaxValue)) continue;
+      Object* result = t.Call();
+      double expected = static_cast<double>(input);
+      t.CheckNumber(expected, result);
+    }
+  }
+}
+
+
+TEST(RunChangeInt32ToTagged) {
+  ChangesLoweringTester<Object*> t;
+  int32_t input;
+  t.BuildLoadAndLower(t.simplified()->ChangeInt32ToTagged(),
+                      t.machine()->Load(kMachInt32), &input);
+
+  if (Pipeline::SupportedTarget()) {
+    for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
+      FOR_INT32_INPUTS(i) {
+        if (m == 0) CcTest::heap()->EnableInlineAllocation();
+        if (m == 1) CcTest::heap()->DisableInlineAllocation();
+        if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
+
+        input = *i;
+        Object* result = t.CallWithPotentialGC<Object>();
+        t.CheckNumber(static_cast<double>(input), result);
+      }
+    }
+  }
+}
+
+
+TEST(RunChangeUint32ToTagged) {
+  ChangesLoweringTester<Object*> t;
+  uint32_t input;
+  t.BuildLoadAndLower(t.simplified()->ChangeUint32ToTagged(),
+                      t.machine()->Load(kMachUint32), &input);
+
+  if (Pipeline::SupportedTarget()) {
+    for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
+      FOR_UINT32_INPUTS(i) {
+        if (m == 0) CcTest::heap()->EnableInlineAllocation();
+        if (m == 1) CcTest::heap()->DisableInlineAllocation();
+        if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
+
+        input = *i;
+        Object* result = t.CallWithPotentialGC<Object>();
+        double expected = static_cast<double>(input);
+        t.CheckNumber(expected, result);
+      }
+    }
+  }
+}
+
+
+TEST(RunChangeFloat64ToTagged) {
+  ChangesLoweringTester<Object*> t;
+  double input;
+  t.BuildLoadAndLower(t.simplified()->ChangeFloat64ToTagged(),
+                      t.machine()->Load(kMachFloat64), &input);
+
+  if (Pipeline::SupportedTarget()) {
+    for (int m = 0; m < 3; m++) {  // Try 3 GC modes.
+      FOR_FLOAT64_INPUTS(i) {
+        if (m == 0) CcTest::heap()->EnableInlineAllocation();
+        if (m == 1) CcTest::heap()->DisableInlineAllocation();
+        if (m == 2) SimulateFullSpace(CcTest::heap()->new_space());
+
+        input = *i;
+        Object* result = t.CallWithPotentialGC<Object>();
+        t.CheckNumber(input, result);
+      }
+    }
+  }
+}
+
+#endif  // V8_TURBOFAN_BACKEND
diff --git a/test/cctest/compiler/test-codegen-deopt.cc b/test/cctest/compiler/test-codegen-deopt.cc
new file mode 100644
index 0000000..8217229
--- /dev/null
+++ b/test/cctest/compiler/test-codegen-deopt.cc
@@ -0,0 +1,313 @@
+// 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/code-generator.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/instruction-selector.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/raw-machine-assembler.h"
+#include "src/compiler/register-allocator.h"
+#include "src/compiler/schedule.h"
+
+#include "src/full-codegen.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+
+#include "test/cctest/compiler/c-signature.h"
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+
+#if V8_TURBOFAN_TARGET
+
+typedef RawMachineAssembler::Label MLabel;
+
+static Handle<JSFunction> NewFunction(const char* source) {
+  return v8::Utils::OpenHandle(
+      *v8::Handle<v8::Function>::Cast(CompileRun(source)));
+}
+
+
+class DeoptCodegenTester {
+ public:
+  explicit DeoptCodegenTester(HandleAndZoneScope* scope, const char* src)
+      : scope_(scope),
+        function(NewFunction(src)),
+        info(function, scope->main_zone()),
+        bailout_id(-1) {
+    CHECK(Parser::Parse(&info));
+    info.SetOptimizing(BailoutId::None(), Handle<Code>(function->code()));
+    CHECK(Rewriter::Rewrite(&info));
+    CHECK(Scope::Analyze(&info));
+    CHECK(Compiler::EnsureDeoptimizationSupport(&info));
+
+    DCHECK(info.shared_info()->has_deoptimization_support());
+
+    graph = new (scope_->main_zone()) Graph(scope_->main_zone());
+  }
+
+  virtual ~DeoptCodegenTester() { delete code; }
+
+  void GenerateCodeFromSchedule(Schedule* schedule) {
+    OFStream os(stdout);
+    if (FLAG_trace_turbo) {
+      os << *schedule;
+    }
+
+    // Initialize the codegen and generate code.
+    Linkage* linkage = new (scope_->main_zone()) Linkage(&info);
+    code = new v8::internal::compiler::InstructionSequence(linkage, graph,
+                                                           schedule);
+    SourcePositionTable source_positions(graph);
+    InstructionSelector selector(code, &source_positions);
+    selector.SelectInstructions();
+
+    if (FLAG_trace_turbo) {
+      os << "----- Instruction sequence before register allocation -----\n"
+         << *code;
+    }
+
+    RegisterAllocator allocator(code);
+    CHECK(allocator.Allocate());
+
+    if (FLAG_trace_turbo) {
+      os << "----- Instruction sequence after register allocation -----\n"
+         << *code;
+    }
+
+    compiler::CodeGenerator generator(code);
+    result_code = generator.GenerateCode();
+
+#ifdef OBJECT_PRINT
+    if (FLAG_print_opt_code || FLAG_trace_turbo) {
+      result_code->Print();
+    }
+#endif
+  }
+
+  Zone* zone() { return scope_->main_zone(); }
+
+  HandleAndZoneScope* scope_;
+  Handle<JSFunction> function;
+  CompilationInfo info;
+  BailoutId bailout_id;
+  Handle<Code> result_code;
+  v8::internal::compiler::InstructionSequence* code;
+  Graph* graph;
+};
+
+
+class TrivialDeoptCodegenTester : public DeoptCodegenTester {
+ public:
+  explicit TrivialDeoptCodegenTester(HandleAndZoneScope* scope)
+      : DeoptCodegenTester(scope,
+                           "function foo() { deopt(); return 42; }; foo") {}
+
+  void GenerateCode() {
+    GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
+  }
+
+  Schedule* BuildGraphAndSchedule(Graph* graph) {
+    CommonOperatorBuilder common(zone());
+
+    // Manually construct a schedule for the function below:
+    // function foo() {
+    //   deopt();
+    // }
+
+    CSignature1<Object*, Object*> sig;
+    RawMachineAssembler m(graph, &sig);
+
+    Handle<JSFunction> deopt_function =
+        NewFunction("function deopt() { %DeoptimizeFunction(foo); }; deopt");
+    Unique<Object> deopt_fun_constant =
+        Unique<Object>::CreateUninitialized(deopt_function);
+    Node* deopt_fun_node = m.NewNode(common.HeapConstant(deopt_fun_constant));
+
+    Handle<Context> caller_context(function->context(), CcTest::i_isolate());
+    Unique<Object> caller_context_constant =
+        Unique<Object>::CreateUninitialized(caller_context);
+    Node* caller_context_node =
+        m.NewNode(common.HeapConstant(caller_context_constant));
+
+    bailout_id = GetCallBailoutId();
+    Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
+    Node* locals = m.NewNode(common.StateValues(0));
+    Node* stack = m.NewNode(common.StateValues(0));
+
+    Node* state_node = m.NewNode(
+        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
+        locals, stack, caller_context_node, m.UndefinedConstant());
+
+    Handle<Context> context(deopt_function->context(), CcTest::i_isolate());
+    Unique<Object> context_constant =
+        Unique<Object>::CreateUninitialized(context);
+    Node* context_node = m.NewNode(common.HeapConstant(context_constant));
+
+    m.CallJS0(deopt_fun_node, m.UndefinedConstant(), context_node, state_node);
+
+    m.Return(m.UndefinedConstant());
+
+    // Schedule the graph:
+    Schedule* schedule = m.Export();
+
+    return schedule;
+  }
+
+  BailoutId GetCallBailoutId() {
+    ZoneList<Statement*>* body = info.function()->body();
+    for (int i = 0; i < body->length(); i++) {
+      if (body->at(i)->IsExpressionStatement() &&
+          body->at(i)->AsExpressionStatement()->expression()->IsCall()) {
+        return body->at(i)->AsExpressionStatement()->expression()->id();
+      }
+    }
+    CHECK(false);
+    return BailoutId(-1);
+  }
+};
+
+
+TEST(TurboTrivialDeoptCodegen) {
+  HandleAndZoneScope scope;
+  InitializedHandleScope handles;
+
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  TrivialDeoptCodegenTester t(&scope);
+  t.GenerateCode();
+
+  DeoptimizationInputData* data =
+      DeoptimizationInputData::cast(t.result_code->deoptimization_data());
+
+  // TODO(jarin) Find a way to test the safepoint.
+
+  // Check that we deoptimize to the right AST id.
+  CHECK_EQ(1, data->DeoptCount());
+  CHECK_EQ(t.bailout_id.ToInt(), data->AstId(0).ToInt());
+}
+
+
+TEST(TurboTrivialDeoptCodegenAndRun) {
+  HandleAndZoneScope scope;
+  InitializedHandleScope handles;
+
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  TrivialDeoptCodegenTester t(&scope);
+  t.GenerateCode();
+
+  t.function->ReplaceCode(*t.result_code);
+  t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
+
+  Isolate* isolate = scope.main_isolate();
+  Handle<Object> result;
+  bool has_pending_exception =
+      !Execution::Call(isolate, t.function,
+                       isolate->factory()->undefined_value(), 0, NULL,
+                       false).ToHandle(&result);
+  CHECK(!has_pending_exception);
+  CHECK(result->SameValue(Smi::FromInt(42)));
+}
+
+
+class TrivialRuntimeDeoptCodegenTester : public DeoptCodegenTester {
+ public:
+  explicit TrivialRuntimeDeoptCodegenTester(HandleAndZoneScope* scope)
+      : DeoptCodegenTester(
+            scope,
+            "function foo() { %DeoptimizeFunction(foo); return 42; }; foo") {}
+
+  void GenerateCode() {
+    GenerateCodeFromSchedule(BuildGraphAndSchedule(graph));
+  }
+
+  Schedule* BuildGraphAndSchedule(Graph* graph) {
+    CommonOperatorBuilder common(zone());
+
+    // Manually construct a schedule for the function below:
+    // function foo() {
+    //   %DeoptimizeFunction(foo);
+    // }
+
+    CSignature1<Object*, Object*> sig;
+    RawMachineAssembler m(graph, &sig);
+
+    Unique<Object> this_fun_constant =
+        Unique<Object>::CreateUninitialized(function);
+    Node* this_fun_node = m.NewNode(common.HeapConstant(this_fun_constant));
+
+    Handle<Context> context(function->context(), CcTest::i_isolate());
+    Unique<Object> context_constant =
+        Unique<Object>::CreateUninitialized(context);
+    Node* context_node = m.NewNode(common.HeapConstant(context_constant));
+
+    bailout_id = GetCallBailoutId();
+    Node* parameters = m.NewNode(common.StateValues(1), m.UndefinedConstant());
+    Node* locals = m.NewNode(common.StateValues(0));
+    Node* stack = m.NewNode(common.StateValues(0));
+
+    Node* state_node = m.NewNode(
+        common.FrameState(JS_FRAME, bailout_id, kIgnoreOutput), parameters,
+        locals, stack, context_node, m.UndefinedConstant());
+
+    m.CallRuntime1(Runtime::kDeoptimizeFunction, this_fun_node, context_node,
+                   state_node);
+
+    m.Return(m.UndefinedConstant());
+
+    // Schedule the graph:
+    Schedule* schedule = m.Export();
+
+    return schedule;
+  }
+
+  BailoutId GetCallBailoutId() {
+    ZoneList<Statement*>* body = info.function()->body();
+    for (int i = 0; i < body->length(); i++) {
+      if (body->at(i)->IsExpressionStatement() &&
+          body->at(i)->AsExpressionStatement()->expression()->IsCallRuntime()) {
+        return body->at(i)->AsExpressionStatement()->expression()->id();
+      }
+    }
+    CHECK(false);
+    return BailoutId(-1);
+  }
+};
+
+
+TEST(TurboTrivialRuntimeDeoptCodegenAndRun) {
+  HandleAndZoneScope scope;
+  InitializedHandleScope handles;
+
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  TrivialRuntimeDeoptCodegenTester t(&scope);
+  t.GenerateCode();
+
+  t.function->ReplaceCode(*t.result_code);
+  t.info.context()->native_context()->AddOptimizedCode(*t.result_code);
+
+  Isolate* isolate = scope.main_isolate();
+  Handle<Object> result;
+  bool has_pending_exception =
+      !Execution::Call(isolate, t.function,
+                       isolate->factory()->undefined_value(), 0, NULL,
+                       false).ToHandle(&result);
+  CHECK(!has_pending_exception);
+  CHECK(result->SameValue(Smi::FromInt(42)));
+}
+
+#endif
diff --git a/test/cctest/compiler/test-gap-resolver.cc b/test/cctest/compiler/test-gap-resolver.cc
new file mode 100644
index 0000000..6239f2a
--- /dev/null
+++ b/test/cctest/compiler/test-gap-resolver.cc
@@ -0,0 +1,173 @@
+// 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/gap-resolver.h"
+
+#include "src/base/utils/random-number-generator.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+// The state of our move interpreter is the mapping of operands to values. Note
+// that the actual values don't really matter, all we care about is equality.
+class InterpreterState {
+ public:
+  typedef std::vector<MoveOperands> Moves;
+
+  void ExecuteInParallel(Moves moves) {
+    InterpreterState copy(*this);
+    for (Moves::iterator it = moves.begin(); it != moves.end(); ++it) {
+      if (!it->IsRedundant()) write(it->destination(), copy.read(it->source()));
+    }
+  }
+
+  bool operator==(const InterpreterState& other) const {
+    return values_ == other.values_;
+  }
+
+  bool operator!=(const InterpreterState& other) const {
+    return values_ != other.values_;
+  }
+
+ private:
+  // Internally, the state is a normalized permutation of (kind,index) pairs.
+  typedef std::pair<InstructionOperand::Kind, int> Key;
+  typedef Key Value;
+  typedef std::map<Key, Value> OperandMap;
+
+  Value read(const InstructionOperand* op) const {
+    OperandMap::const_iterator it = values_.find(KeyFor(op));
+    return (it == values_.end()) ? ValueFor(op) : it->second;
+  }
+
+  void write(const InstructionOperand* op, Value v) {
+    if (v == ValueFor(op)) {
+      values_.erase(KeyFor(op));
+    } else {
+      values_[KeyFor(op)] = v;
+    }
+  }
+
+  static Key KeyFor(const InstructionOperand* op) {
+    return Key(op->kind(), op->index());
+  }
+
+  static Value ValueFor(const InstructionOperand* op) {
+    return Value(op->kind(), op->index());
+  }
+
+  friend OStream& operator<<(OStream& os, const InterpreterState& is) {
+    for (OperandMap::const_iterator it = is.values_.begin();
+         it != is.values_.end(); ++it) {
+      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);
+    }
+    return os;
+  }
+
+  OperandMap values_;
+};
+
+
+// An abstract interpreter for moves, swaps and parallel moves.
+class MoveInterpreter : public GapResolver::Assembler {
+ public:
+  virtual void AssembleMove(InstructionOperand* source,
+                            InstructionOperand* destination) OVERRIDE {
+    InterpreterState::Moves moves;
+    moves.push_back(MoveOperands(source, destination));
+    state_.ExecuteInParallel(moves);
+  }
+
+  virtual void AssembleSwap(InstructionOperand* source,
+                            InstructionOperand* destination) OVERRIDE {
+    InterpreterState::Moves moves;
+    moves.push_back(MoveOperands(source, destination));
+    moves.push_back(MoveOperands(destination, source));
+    state_.ExecuteInParallel(moves);
+  }
+
+  void AssembleParallelMove(const ParallelMove* pm) {
+    InterpreterState::Moves moves(pm->move_operands()->begin(),
+                                  pm->move_operands()->end());
+    state_.ExecuteInParallel(moves);
+  }
+
+  InterpreterState state() const { return state_; }
+
+ private:
+  InterpreterState state_;
+};
+
+
+class ParallelMoveCreator : public HandleAndZoneScope {
+ public:
+  ParallelMoveCreator() : rng_(CcTest::random_number_generator()) {}
+
+  ParallelMove* Create(int size) {
+    ParallelMove* parallel_move = new (main_zone()) ParallelMove(main_zone());
+    std::set<InstructionOperand*, InstructionOperandComparator> seen;
+    for (int i = 0; i < size; ++i) {
+      MoveOperands mo(CreateRandomOperand(), CreateRandomOperand());
+      if (!mo.IsRedundant() && seen.find(mo.destination()) == seen.end()) {
+        parallel_move->AddMove(mo.source(), mo.destination(), main_zone());
+        seen.insert(mo.destination());
+      }
+    }
+    return parallel_move;
+  }
+
+ private:
+  struct InstructionOperandComparator {
+    bool operator()(const InstructionOperand* x,
+                    const InstructionOperand* y) const {
+      return (x->kind() < y->kind()) ||
+             (x->kind() == y->kind() && x->index() < y->index());
+    }
+  };
+
+  InstructionOperand* CreateRandomOperand() {
+    int index = rng_->NextInt(6);
+    switch (rng_->NextInt(5)) {
+      case 0:
+        return ConstantOperand::Create(index, main_zone());
+      case 1:
+        return StackSlotOperand::Create(index, main_zone());
+      case 2:
+        return DoubleStackSlotOperand::Create(index, main_zone());
+      case 3:
+        return RegisterOperand::Create(index, main_zone());
+      case 4:
+        return DoubleRegisterOperand::Create(index, main_zone());
+    }
+    UNREACHABLE();
+    return NULL;
+  }
+
+ private:
+  v8::base::RandomNumberGenerator* rng_;
+};
+
+
+TEST(FuzzResolver) {
+  ParallelMoveCreator pmc;
+  for (int size = 0; size < 20; ++size) {
+    for (int repeat = 0; repeat < 50; ++repeat) {
+      ParallelMove* pm = pmc.Create(size);
+
+      // Note: The gap resolver modifies the ParallelMove, so interpret first.
+      MoveInterpreter mi1;
+      mi1.AssembleParallelMove(pm);
+
+      MoveInterpreter mi2;
+      GapResolver resolver(&mi2);
+      resolver.Resolve(pm);
+
+      CHECK(mi1.state() == mi2.state());
+    }
+  }
+}
diff --git a/test/cctest/compiler/test-graph-reducer.cc b/test/cctest/compiler/test-graph-reducer.cc
new file mode 100644
index 0000000..eabfd22
--- /dev/null
+++ b/test/cctest/compiler/test-graph-reducer.cc
@@ -0,0 +1,661 @@
+// 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/v8.h"
+
+#include "graph-tester.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-reducer.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+const uint8_t OPCODE_A0 = 10;
+const uint8_t OPCODE_A1 = 11;
+const uint8_t OPCODE_A2 = 12;
+const uint8_t OPCODE_B0 = 20;
+const uint8_t OPCODE_B1 = 21;
+const uint8_t OPCODE_B2 = 22;
+const uint8_t OPCODE_C0 = 30;
+const uint8_t OPCODE_C1 = 31;
+const uint8_t OPCODE_C2 = 32;
+
+static SimpleOperator OPA0(OPCODE_A0, Operator::kNoWrite, 0, 0, "opa0");
+static SimpleOperator OPA1(OPCODE_A1, Operator::kNoWrite, 1, 0, "opa1");
+static SimpleOperator OPA2(OPCODE_A2, Operator::kNoWrite, 2, 0, "opa2");
+static SimpleOperator OPB0(OPCODE_B0, Operator::kNoWrite, 0, 0, "opa0");
+static SimpleOperator OPB1(OPCODE_B1, Operator::kNoWrite, 1, 0, "opa1");
+static SimpleOperator OPB2(OPCODE_B2, Operator::kNoWrite, 2, 0, "opa2");
+static SimpleOperator OPC0(OPCODE_C0, Operator::kNoWrite, 0, 0, "opc0");
+static SimpleOperator OPC1(OPCODE_C1, Operator::kNoWrite, 1, 0, "opc1");
+static SimpleOperator OPC2(OPCODE_C2, Operator::kNoWrite, 2, 0, "opc2");
+
+
+// Replaces all "A" operators with "B" operators without creating new nodes.
+class InPlaceABReducer : public Reducer {
+ public:
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_A0:
+        CHECK_EQ(0, node->InputCount());
+        node->set_op(&OPB0);
+        return Replace(node);
+      case OPCODE_A1:
+        CHECK_EQ(1, node->InputCount());
+        node->set_op(&OPB1);
+        return Replace(node);
+      case OPCODE_A2:
+        CHECK_EQ(2, node->InputCount());
+        node->set_op(&OPB2);
+        return Replace(node);
+    }
+    return NoChange();
+  }
+};
+
+
+// Replaces all "A" operators with "B" operators by allocating new nodes.
+class NewABReducer : public Reducer {
+ public:
+  explicit NewABReducer(Graph* graph) : graph_(graph) {}
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_A0:
+        CHECK_EQ(0, node->InputCount());
+        return Replace(graph_->NewNode(&OPB0));
+      case OPCODE_A1:
+        CHECK_EQ(1, node->InputCount());
+        return Replace(graph_->NewNode(&OPB1, node->InputAt(0)));
+      case OPCODE_A2:
+        CHECK_EQ(2, node->InputCount());
+        return Replace(
+            graph_->NewNode(&OPB2, node->InputAt(0), node->InputAt(1)));
+    }
+    return NoChange();
+  }
+  Graph* graph_;
+};
+
+
+// Replaces all "B" operators with "C" operators without creating new nodes.
+class InPlaceBCReducer : public Reducer {
+ public:
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_B0:
+        CHECK_EQ(0, node->InputCount());
+        node->set_op(&OPC0);
+        return Replace(node);
+      case OPCODE_B1:
+        CHECK_EQ(1, node->InputCount());
+        node->set_op(&OPC1);
+        return Replace(node);
+      case OPCODE_B2:
+        CHECK_EQ(2, node->InputCount());
+        node->set_op(&OPC2);
+        return Replace(node);
+    }
+    return NoChange();
+  }
+};
+
+
+// Wraps all "OPA0" nodes in "OPB1" operators by allocating new nodes.
+class A0Wrapper FINAL : public Reducer {
+ public:
+  explicit A0Wrapper(Graph* graph) : graph_(graph) {}
+  virtual Reduction Reduce(Node* node) OVERRIDE {
+    switch (node->op()->opcode()) {
+      case OPCODE_A0:
+        CHECK_EQ(0, node->InputCount());
+        return Replace(graph_->NewNode(&OPB1, node));
+    }
+    return NoChange();
+  }
+  Graph* graph_;
+};
+
+
+// Wraps all "OPB0" nodes in two "OPC1" operators by allocating new nodes.
+class B0Wrapper FINAL : public Reducer {
+ public:
+  explicit B0Wrapper(Graph* graph) : graph_(graph) {}
+  virtual Reduction Reduce(Node* node) OVERRIDE {
+    switch (node->op()->opcode()) {
+      case OPCODE_B0:
+        CHECK_EQ(0, node->InputCount());
+        return Replace(graph_->NewNode(&OPC1, graph_->NewNode(&OPC1, node)));
+    }
+    return NoChange();
+  }
+  Graph* graph_;
+};
+
+
+// Replaces all "OPA1" nodes with the first input.
+class A1Forwarder : public Reducer {
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_A1:
+        CHECK_EQ(1, node->InputCount());
+        return Replace(node->InputAt(0));
+    }
+    return NoChange();
+  }
+};
+
+
+// Replaces all "OPB1" nodes with the first input.
+class B1Forwarder : public Reducer {
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_B1:
+        CHECK_EQ(1, node->InputCount());
+        return Replace(node->InputAt(0));
+    }
+    return NoChange();
+  }
+};
+
+
+// Swaps the inputs to "OP2A" and "OP2B" nodes based on ids.
+class AB2Sorter : public Reducer {
+  virtual Reduction Reduce(Node* node) {
+    switch (node->op()->opcode()) {
+      case OPCODE_A2:
+      case OPCODE_B2:
+        CHECK_EQ(2, node->InputCount());
+        Node* x = node->InputAt(0);
+        Node* y = node->InputAt(1);
+        if (x->id() > y->id()) {
+          node->ReplaceInput(0, y);
+          node->ReplaceInput(1, x);
+          return Replace(node);
+        }
+    }
+    return NoChange();
+  }
+};
+
+
+// Simply records the nodes visited.
+class ReducerRecorder : public Reducer {
+ public:
+  explicit ReducerRecorder(Zone* zone)
+      : set(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
+  virtual Reduction Reduce(Node* node) {
+    set.insert(node);
+    return NoChange();
+  }
+  void CheckContains(Node* node) {
+    CHECK_EQ(1, static_cast<int>(set.count(node)));
+  }
+  NodeSet set;
+};
+
+
+TEST(ReduceGraphFromEnd1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* end = graph.NewNode(&OPA1, n1);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  ReducerRecorder recorder(graph.zone());
+  reducer.AddReducer(&recorder);
+  reducer.ReduceGraph();
+  recorder.CheckContains(n1);
+  recorder.CheckContains(end);
+}
+
+
+TEST(ReduceGraphFromEnd2) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* n2 = graph.NewNode(&OPA1, n1);
+  Node* n3 = graph.NewNode(&OPA1, n1);
+  Node* end = graph.NewNode(&OPA2, n2, n3);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  ReducerRecorder recorder(graph.zone());
+  reducer.AddReducer(&recorder);
+  reducer.ReduceGraph();
+  recorder.CheckContains(n1);
+  recorder.CheckContains(n2);
+  recorder.CheckContains(n3);
+  recorder.CheckContains(end);
+}
+
+
+TEST(ReduceInPlace1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* end = graph.NewNode(&OPA1, n1);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  InPlaceABReducer r;
+  reducer.AddReducer(&r);
+
+  // Tests A* => B* with in-place updates.
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPB0, n1->op());
+    CHECK_EQ(&OPB1, end->op());
+    CHECK_EQ(n1, end->InputAt(0));
+  }
+}
+
+
+TEST(ReduceInPlace2) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* n2 = graph.NewNode(&OPA1, n1);
+  Node* n3 = graph.NewNode(&OPA1, n1);
+  Node* end = graph.NewNode(&OPA2, n2, n3);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  InPlaceABReducer r;
+  reducer.AddReducer(&r);
+
+  // Tests A* => B* with in-place updates.
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPB0, n1->op());
+    CHECK_EQ(&OPB1, n2->op());
+    CHECK_EQ(n1, n2->InputAt(0));
+    CHECK_EQ(&OPB1, n3->op());
+    CHECK_EQ(n1, n3->InputAt(0));
+    CHECK_EQ(&OPB2, end->op());
+    CHECK_EQ(n2, end->InputAt(0));
+    CHECK_EQ(n3, end->InputAt(1));
+  }
+}
+
+
+TEST(ReduceNew1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* n2 = graph.NewNode(&OPA1, n1);
+  Node* n3 = graph.NewNode(&OPA1, n1);
+  Node* end = graph.NewNode(&OPA2, n2, n3);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  NewABReducer r(&graph);
+  reducer.AddReducer(&r);
+
+  // Tests A* => B* while creating new nodes.
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    if (i == 0) {
+      CHECK_NE(before, graph.NodeCount());
+    } else {
+      CHECK_EQ(before, graph.NodeCount());
+    }
+    Node* nend = graph.end();
+    CHECK_NE(end, nend);  // end() should be updated too.
+
+    Node* nn2 = nend->InputAt(0);
+    Node* nn3 = nend->InputAt(1);
+    Node* nn1 = nn2->InputAt(0);
+
+    CHECK_EQ(nn1, nn3->InputAt(0));
+
+    CHECK_EQ(&OPB0, nn1->op());
+    CHECK_EQ(&OPB1, nn2->op());
+    CHECK_EQ(&OPB1, nn3->op());
+    CHECK_EQ(&OPB2, nend->op());
+  }
+}
+
+
+TEST(Wrapping1) {
+  GraphTester graph;
+
+  Node* end = graph.NewNode(&OPA0);
+  graph.SetEnd(end);
+  CHECK_EQ(1, graph.NodeCount());
+
+  GraphReducer reducer(&graph);
+  A0Wrapper r(&graph);
+  reducer.AddReducer(&r);
+
+  reducer.ReduceGraph();
+  CHECK_EQ(2, graph.NodeCount());
+
+  Node* nend = graph.end();
+  CHECK_NE(end, nend);
+  CHECK_EQ(&OPB1, nend->op());
+  CHECK_EQ(1, nend->InputCount());
+  CHECK_EQ(end, nend->InputAt(0));
+}
+
+
+TEST(Wrapping2) {
+  GraphTester graph;
+
+  Node* end = graph.NewNode(&OPB0);
+  graph.SetEnd(end);
+  CHECK_EQ(1, graph.NodeCount());
+
+  GraphReducer reducer(&graph);
+  B0Wrapper r(&graph);
+  reducer.AddReducer(&r);
+
+  reducer.ReduceGraph();
+  CHECK_EQ(3, graph.NodeCount());
+
+  Node* nend = graph.end();
+  CHECK_NE(end, nend);
+  CHECK_EQ(&OPC1, nend->op());
+  CHECK_EQ(1, nend->InputCount());
+
+  Node* n1 = nend->InputAt(0);
+  CHECK_NE(end, n1);
+  CHECK_EQ(&OPC1, n1->op());
+  CHECK_EQ(1, n1->InputCount());
+  CHECK_EQ(end, n1->InputAt(0));
+}
+
+
+TEST(Forwarding1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* end = graph.NewNode(&OPA1, n1);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  A1Forwarder r;
+  reducer.AddReducer(&r);
+
+  // Tests A1(x) => x
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPA0, n1->op());
+    CHECK_EQ(n1, graph.end());
+  }
+}
+
+
+TEST(Forwarding2) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* n2 = graph.NewNode(&OPA1, n1);
+  Node* n3 = graph.NewNode(&OPA1, n1);
+  Node* end = graph.NewNode(&OPA2, n2, n3);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  A1Forwarder r;
+  reducer.AddReducer(&r);
+
+  // Tests reducing A2(A1(x), A1(y)) => A2(x, y).
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPA0, n1->op());
+    CHECK_EQ(n1, end->InputAt(0));
+    CHECK_EQ(n1, end->InputAt(1));
+    CHECK_EQ(&OPA2, end->op());
+    CHECK_EQ(0, n2->UseCount());
+    CHECK_EQ(0, n3->UseCount());
+  }
+}
+
+
+TEST(Forwarding3) {
+  // Tests reducing a chain of A1(A1(A1(A1(x)))) => x.
+  for (int i = 0; i < 8; i++) {
+    GraphTester graph;
+
+    Node* n1 = graph.NewNode(&OPA0);
+    Node* end = n1;
+    for (int j = 0; j < i; j++) {
+      end = graph.NewNode(&OPA1, end);
+    }
+    graph.SetEnd(end);
+
+    GraphReducer reducer(&graph);
+    A1Forwarder r;
+    reducer.AddReducer(&r);
+
+    for (int i = 0; i < 3; i++) {
+      int before = graph.NodeCount();
+      reducer.ReduceGraph();
+      CHECK_EQ(before, graph.NodeCount());
+      CHECK_EQ(&OPA0, n1->op());
+      CHECK_EQ(n1, graph.end());
+    }
+  }
+}
+
+
+TEST(ReduceForward1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* n2 = graph.NewNode(&OPA1, n1);
+  Node* n3 = graph.NewNode(&OPA1, n1);
+  Node* end = graph.NewNode(&OPA2, n2, n3);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  InPlaceABReducer r;
+  B1Forwarder f;
+  reducer.AddReducer(&r);
+  reducer.AddReducer(&f);
+
+  // Tests first reducing A => B, then B1(x) => x.
+  for (int i = 0; i < 3; i++) {
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPB0, n1->op());
+    CHECK(n2->IsDead());
+    CHECK_EQ(n1, end->InputAt(0));
+    CHECK(n3->IsDead());
+    CHECK_EQ(n1, end->InputAt(0));
+    CHECK_EQ(&OPB2, end->op());
+    CHECK_EQ(0, n2->UseCount());
+    CHECK_EQ(0, n3->UseCount());
+  }
+}
+
+
+TEST(Sorter1) {
+  HandleAndZoneScope scope;
+  AB2Sorter r;
+  for (int i = 0; i < 6; i++) {
+    GraphTester graph;
+
+    Node* n1 = graph.NewNode(&OPA0);
+    Node* n2 = graph.NewNode(&OPA1, n1);
+    Node* n3 = graph.NewNode(&OPA1, n1);
+    Node* end = NULL;  // Initialize to please the compiler.
+
+    if (i == 0) end = graph.NewNode(&OPA2, n2, n3);
+    if (i == 1) end = graph.NewNode(&OPA2, n3, n2);
+    if (i == 2) end = graph.NewNode(&OPA2, n2, n1);
+    if (i == 3) end = graph.NewNode(&OPA2, n1, n2);
+    if (i == 4) end = graph.NewNode(&OPA2, n3, n1);
+    if (i == 5) end = graph.NewNode(&OPA2, n1, n3);
+
+    graph.SetEnd(end);
+
+    GraphReducer reducer(&graph);
+    reducer.AddReducer(&r);
+
+    int before = graph.NodeCount();
+    reducer.ReduceGraph();
+    CHECK_EQ(before, graph.NodeCount());
+    CHECK_EQ(&OPA0, n1->op());
+    CHECK_EQ(&OPA1, n2->op());
+    CHECK_EQ(&OPA1, n3->op());
+    CHECK_EQ(&OPA2, end->op());
+    CHECK_EQ(end, graph.end());
+    CHECK(end->InputAt(0)->id() <= end->InputAt(1)->id());
+  }
+}
+
+
+// Generate a node graph with the given permutations.
+void GenDAG(Graph* graph, int* p3, int* p2, int* p1) {
+  Node* level4 = graph->NewNode(&OPA0);
+  Node* level3[] = {graph->NewNode(&OPA1, level4),
+                    graph->NewNode(&OPA1, level4)};
+
+  Node* level2[] = {graph->NewNode(&OPA1, level3[p3[0]]),
+                    graph->NewNode(&OPA1, level3[p3[1]]),
+                    graph->NewNode(&OPA1, level3[p3[0]]),
+                    graph->NewNode(&OPA1, level3[p3[1]])};
+
+  Node* level1[] = {graph->NewNode(&OPA2, level2[p2[0]], level2[p2[1]]),
+                    graph->NewNode(&OPA2, level2[p2[2]], level2[p2[3]])};
+
+  Node* end = graph->NewNode(&OPA2, level1[p1[0]], level1[p1[1]]);
+  graph->SetEnd(end);
+}
+
+
+TEST(SortForwardReduce) {
+  GraphTester graph;
+
+  // Tests combined reductions on a series of DAGs.
+  for (int j = 0; j < 2; j++) {
+    int p3[] = {j, 1 - j};
+    for (int m = 0; m < 2; m++) {
+      int p1[] = {m, 1 - m};
+      for (int k = 0; k < 24; k++) {  // All permutations of 0, 1, 2, 3
+        int p2[] = {-1, -1, -1, -1};
+        int n = k;
+        for (int d = 4; d >= 1; d--) {  // Construct permutation.
+          int p = n % d;
+          for (int z = 0; z < 4; z++) {
+            if (p2[z] == -1) {
+              if (p == 0) p2[z] = d - 1;
+              p--;
+            }
+          }
+          n = n / d;
+        }
+
+        GenDAG(&graph, p3, p2, p1);
+
+        GraphReducer reducer(&graph);
+        AB2Sorter r1;
+        A1Forwarder r2;
+        InPlaceABReducer r3;
+        reducer.AddReducer(&r1);
+        reducer.AddReducer(&r2);
+        reducer.AddReducer(&r3);
+
+        reducer.ReduceGraph();
+
+        Node* end = graph.end();
+        CHECK_EQ(&OPB2, end->op());
+        Node* n1 = end->InputAt(0);
+        Node* n2 = end->InputAt(1);
+        CHECK_NE(n1, n2);
+        CHECK(n1->id() < n2->id());
+        CHECK_EQ(&OPB2, n1->op());
+        CHECK_EQ(&OPB2, n2->op());
+        Node* n4 = n1->InputAt(0);
+        CHECK_EQ(&OPB0, n4->op());
+        CHECK_EQ(n4, n1->InputAt(1));
+        CHECK_EQ(n4, n2->InputAt(0));
+        CHECK_EQ(n4, n2->InputAt(1));
+      }
+    }
+  }
+}
+
+
+TEST(Order) {
+  // Test that the order of reducers doesn't matter, as they should be
+  // rerun for changed nodes.
+  for (int i = 0; i < 2; i++) {
+    GraphTester graph;
+
+    Node* n1 = graph.NewNode(&OPA0);
+    Node* end = graph.NewNode(&OPA1, n1);
+    graph.SetEnd(end);
+
+    GraphReducer reducer(&graph);
+    InPlaceABReducer abr;
+    InPlaceBCReducer bcr;
+    if (i == 0) {
+      reducer.AddReducer(&abr);
+      reducer.AddReducer(&bcr);
+    } else {
+      reducer.AddReducer(&bcr);
+      reducer.AddReducer(&abr);
+    }
+
+    // Tests A* => C* with in-place updates.
+    for (int i = 0; i < 3; i++) {
+      int before = graph.NodeCount();
+      reducer.ReduceGraph();
+      CHECK_EQ(before, graph.NodeCount());
+      CHECK_EQ(&OPC0, n1->op());
+      CHECK_EQ(&OPC1, end->op());
+      CHECK_EQ(n1, end->InputAt(0));
+    }
+  }
+}
+
+
+// Tests that a reducer is only applied once.
+class OneTimeReducer : public Reducer {
+ public:
+  OneTimeReducer(Reducer* reducer, Zone* zone)
+      : reducer_(reducer),
+        nodes_(NodeSet::key_compare(), NodeSet::allocator_type(zone)) {}
+  virtual Reduction Reduce(Node* node) {
+    CHECK_EQ(0, static_cast<int>(nodes_.count(node)));
+    nodes_.insert(node);
+    return reducer_->Reduce(node);
+  }
+  Reducer* reducer_;
+  NodeSet nodes_;
+};
+
+
+TEST(OneTimeReduce1) {
+  GraphTester graph;
+
+  Node* n1 = graph.NewNode(&OPA0);
+  Node* end = graph.NewNode(&OPA1, n1);
+  graph.SetEnd(end);
+
+  GraphReducer reducer(&graph);
+  InPlaceABReducer r;
+  OneTimeReducer once(&r, graph.zone());
+  reducer.AddReducer(&once);
+
+  // Tests A* => B* with in-place updates. Should only be applied once.
+  int before = graph.NodeCount();
+  reducer.ReduceGraph();
+  CHECK_EQ(before, graph.NodeCount());
+  CHECK_EQ(&OPB0, n1->op());
+  CHECK_EQ(&OPB1, end->op());
+  CHECK_EQ(n1, end->InputAt(0));
+}
diff --git a/test/cctest/compiler/test-instruction.cc b/test/cctest/compiler/test-instruction.cc
new file mode 100644
index 0000000..a9feaac
--- /dev/null
+++ b/test/cctest/compiler/test-instruction.cc
@@ -0,0 +1,350 @@
+// 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/code-generator.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/instruction.h"
+#include "src/compiler/linkage.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/lithium.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef v8::internal::compiler::Instruction TestInstr;
+typedef v8::internal::compiler::InstructionSequence TestInstrSeq;
+
+// A testing helper for the register code abstraction.
+class InstructionTester : public HandleAndZoneScope {
+ public:  // We're all friends here.
+  InstructionTester()
+      : isolate(main_isolate()),
+        graph(zone()),
+        schedule(zone()),
+        info(static_cast<HydrogenCodeStub*>(NULL), main_isolate()),
+        linkage(&info),
+        common(zone()),
+        code(NULL) {}
+
+  ~InstructionTester() { delete code; }
+
+  Isolate* isolate;
+  Graph graph;
+  Schedule schedule;
+  CompilationInfoWithZone info;
+  Linkage linkage;
+  CommonOperatorBuilder common;
+  MachineOperatorBuilder machine;
+  TestInstrSeq* code;
+
+  Zone* zone() { return main_zone(); }
+
+  void allocCode() {
+    if (schedule.rpo_order()->size() == 0) {
+      // Compute the RPO order.
+      Scheduler::ComputeSpecialRPO(&schedule);
+      DCHECK(schedule.rpo_order()->size() > 0);
+    }
+    code = new TestInstrSeq(&linkage, &graph, &schedule);
+  }
+
+  Node* Int32Constant(int32_t val) {
+    Node* node = graph.NewNode(common.Int32Constant(val));
+    schedule.AddNode(schedule.start(), node);
+    return node;
+  }
+
+  Node* Float64Constant(double val) {
+    Node* node = graph.NewNode(common.Float64Constant(val));
+    schedule.AddNode(schedule.start(), node);
+    return node;
+  }
+
+  Node* Parameter(int32_t which) {
+    Node* node = graph.NewNode(common.Parameter(which));
+    schedule.AddNode(schedule.start(), node);
+    return node;
+  }
+
+  Node* NewNode(BasicBlock* block) {
+    Node* node = graph.NewNode(common.Int32Constant(111));
+    schedule.AddNode(block, node);
+    return node;
+  }
+
+  int NewInstr(BasicBlock* block) {
+    InstructionCode opcode = static_cast<InstructionCode>(110);
+    TestInstr* instr = TestInstr::New(zone(), opcode);
+    return code->AddInstruction(instr, block);
+  }
+
+  UnallocatedOperand* NewUnallocated(int vreg) {
+    UnallocatedOperand* unallocated =
+        new (zone()) UnallocatedOperand(UnallocatedOperand::ANY);
+    unallocated->set_virtual_register(vreg);
+    return unallocated;
+  }
+};
+
+
+TEST(InstructionBasic) {
+  InstructionTester R;
+
+  for (int i = 0; i < 10; i++) {
+    R.Int32Constant(i);  // Add some nodes to the graph.
+  }
+
+  BasicBlock* last = R.schedule.start();
+  for (int i = 0; i < 5; i++) {
+    BasicBlock* block = R.schedule.NewBasicBlock();
+    R.schedule.AddGoto(last, block);
+    last = block;
+  }
+
+  R.allocCode();
+
+  CHECK_EQ(R.graph.NodeCount(), R.code->ValueCount());
+
+  BasicBlockVector* blocks = R.schedule.rpo_order();
+  CHECK_EQ(static_cast<int>(blocks->size()), R.code->BasicBlockCount());
+
+  int index = 0;
+  for (BasicBlockVectorIter i = blocks->begin(); i != blocks->end();
+       i++, index++) {
+    BasicBlock* block = *i;
+    CHECK_EQ(block, R.code->BlockAt(index));
+    CHECK_EQ(-1, R.code->GetLoopEnd(block));
+  }
+}
+
+
+TEST(InstructionGetBasicBlock) {
+  InstructionTester R;
+
+  BasicBlock* b0 = R.schedule.start();
+  BasicBlock* b1 = R.schedule.NewBasicBlock();
+  BasicBlock* b2 = R.schedule.NewBasicBlock();
+  BasicBlock* b3 = R.schedule.end();
+
+  R.schedule.AddGoto(b0, b1);
+  R.schedule.AddGoto(b1, b2);
+  R.schedule.AddGoto(b2, b3);
+
+  R.allocCode();
+
+  R.code->StartBlock(b0);
+  int i0 = R.NewInstr(b0);
+  int i1 = R.NewInstr(b0);
+  R.code->EndBlock(b0);
+  R.code->StartBlock(b1);
+  int i2 = R.NewInstr(b1);
+  int i3 = R.NewInstr(b1);
+  int i4 = R.NewInstr(b1);
+  int i5 = R.NewInstr(b1);
+  R.code->EndBlock(b1);
+  R.code->StartBlock(b2);
+  int i6 = R.NewInstr(b2);
+  int i7 = R.NewInstr(b2);
+  int i8 = R.NewInstr(b2);
+  R.code->EndBlock(b2);
+  R.code->StartBlock(b3);
+  R.code->EndBlock(b3);
+
+  CHECK_EQ(b0, R.code->GetBasicBlock(i0));
+  CHECK_EQ(b0, R.code->GetBasicBlock(i1));
+
+  CHECK_EQ(b1, R.code->GetBasicBlock(i2));
+  CHECK_EQ(b1, R.code->GetBasicBlock(i3));
+  CHECK_EQ(b1, R.code->GetBasicBlock(i4));
+  CHECK_EQ(b1, R.code->GetBasicBlock(i5));
+
+  CHECK_EQ(b2, R.code->GetBasicBlock(i6));
+  CHECK_EQ(b2, R.code->GetBasicBlock(i7));
+  CHECK_EQ(b2, R.code->GetBasicBlock(i8));
+
+  CHECK_EQ(b0, R.code->GetBasicBlock(b0->first_instruction_index()));
+  CHECK_EQ(b0, R.code->GetBasicBlock(b0->last_instruction_index()));
+
+  CHECK_EQ(b1, R.code->GetBasicBlock(b1->first_instruction_index()));
+  CHECK_EQ(b1, R.code->GetBasicBlock(b1->last_instruction_index()));
+
+  CHECK_EQ(b2, R.code->GetBasicBlock(b2->first_instruction_index()));
+  CHECK_EQ(b2, R.code->GetBasicBlock(b2->last_instruction_index()));
+
+  CHECK_EQ(b3, R.code->GetBasicBlock(b3->first_instruction_index()));
+  CHECK_EQ(b3, R.code->GetBasicBlock(b3->last_instruction_index()));
+}
+
+
+TEST(InstructionIsGapAt) {
+  InstructionTester R;
+
+  BasicBlock* b0 = R.schedule.start();
+  R.schedule.AddReturn(b0, R.Int32Constant(1));
+
+  R.allocCode();
+  TestInstr* i0 = TestInstr::New(R.zone(), 100);
+  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
+  R.code->StartBlock(b0);
+  R.code->AddInstruction(i0, b0);
+  R.code->AddInstruction(g, b0);
+  R.code->EndBlock(b0);
+
+  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
+
+  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
+  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
+  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
+  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(5));  // g
+}
+
+
+TEST(InstructionIsGapAt2) {
+  InstructionTester R;
+
+  BasicBlock* b0 = R.schedule.start();
+  BasicBlock* b1 = R.schedule.end();
+  R.schedule.AddGoto(b0, b1);
+  R.schedule.AddReturn(b1, R.Int32Constant(1));
+
+  R.allocCode();
+  TestInstr* i0 = TestInstr::New(R.zone(), 100);
+  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
+  R.code->StartBlock(b0);
+  R.code->AddInstruction(i0, b0);
+  R.code->AddInstruction(g, b0);
+  R.code->EndBlock(b0);
+
+  TestInstr* i1 = TestInstr::New(R.zone(), 102);
+  TestInstr* g1 = TestInstr::New(R.zone(), 104)->MarkAsControl();
+  R.code->StartBlock(b1);
+  R.code->AddInstruction(i1, b1);
+  R.code->AddInstruction(g1, b1);
+  R.code->EndBlock(b1);
+
+  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
+
+  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
+  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
+  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
+  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(5));  // g
+
+  CHECK_EQ(true, R.code->InstructionAt(6)->IsBlockStart());
+
+  CHECK_EQ(true, R.code->IsGapAt(6));    // Label
+  CHECK_EQ(true, R.code->IsGapAt(7));    // Gap
+  CHECK_EQ(false, R.code->IsGapAt(8));   // i1
+  CHECK_EQ(true, R.code->IsGapAt(9));    // Gap
+  CHECK_EQ(true, R.code->IsGapAt(10));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(11));  // g1
+}
+
+
+TEST(InstructionAddGapMove) {
+  InstructionTester R;
+
+  BasicBlock* b0 = R.schedule.start();
+  R.schedule.AddReturn(b0, R.Int32Constant(1));
+
+  R.allocCode();
+  TestInstr* i0 = TestInstr::New(R.zone(), 100);
+  TestInstr* g = TestInstr::New(R.zone(), 103)->MarkAsControl();
+  R.code->StartBlock(b0);
+  R.code->AddInstruction(i0, b0);
+  R.code->AddInstruction(g, b0);
+  R.code->EndBlock(b0);
+
+  CHECK_EQ(true, R.code->InstructionAt(0)->IsBlockStart());
+
+  CHECK_EQ(true, R.code->IsGapAt(0));   // Label
+  CHECK_EQ(true, R.code->IsGapAt(1));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(2));  // i0
+  CHECK_EQ(true, R.code->IsGapAt(3));   // Gap
+  CHECK_EQ(true, R.code->IsGapAt(4));   // Gap
+  CHECK_EQ(false, R.code->IsGapAt(5));  // g
+
+  int indexes[] = {0, 1, 3, 4, -1};
+  for (int i = 0; indexes[i] >= 0; i++) {
+    int index = indexes[i];
+
+    UnallocatedOperand* op1 = R.NewUnallocated(index + 6);
+    UnallocatedOperand* op2 = R.NewUnallocated(index + 12);
+
+    R.code->AddGapMove(index, op1, op2);
+    GapInstruction* gap = R.code->GapAt(index);
+    ParallelMove* move = gap->GetParallelMove(GapInstruction::START);
+    CHECK_NE(NULL, move);
+    const ZoneList<MoveOperands>* move_operands = move->move_operands();
+    CHECK_EQ(1, move_operands->length());
+    MoveOperands* cur = &move_operands->at(0);
+    CHECK_EQ(op1, cur->source());
+    CHECK_EQ(op2, cur->destination());
+  }
+}
+
+
+TEST(InstructionOperands) {
+  Zone zone(CcTest::InitIsolateOnce());
+
+  {
+    TestInstr* i = TestInstr::New(&zone, 101);
+    CHECK_EQ(0, static_cast<int>(i->OutputCount()));
+    CHECK_EQ(0, static_cast<int>(i->InputCount()));
+    CHECK_EQ(0, static_cast<int>(i->TempCount()));
+  }
+
+  InstructionOperand* outputs[] = {
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
+
+  InstructionOperand* inputs[] = {
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
+
+  InstructionOperand* temps[] = {
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER),
+      new (&zone) UnallocatedOperand(UnallocatedOperand::MUST_HAVE_REGISTER)};
+
+  for (size_t i = 0; i < arraysize(outputs); i++) {
+    for (size_t j = 0; j < arraysize(inputs); j++) {
+      for (size_t k = 0; k < arraysize(temps); k++) {
+        TestInstr* m =
+            TestInstr::New(&zone, 101, i, outputs, j, inputs, k, temps);
+        CHECK(i == m->OutputCount());
+        CHECK(j == m->InputCount());
+        CHECK(k == m->TempCount());
+
+        for (size_t z = 0; z < i; z++) {
+          CHECK_EQ(outputs[z], m->OutputAt(z));
+        }
+
+        for (size_t z = 0; z < j; z++) {
+          CHECK_EQ(inputs[z], m->InputAt(z));
+        }
+
+        for (size_t z = 0; z < k; z++) {
+          CHECK_EQ(temps[z], m->TempAt(z));
+        }
+      }
+    }
+  }
+}
diff --git a/test/cctest/compiler/test-js-constant-cache.cc b/test/cctest/compiler/test-js-constant-cache.cc
new file mode 100644
index 0000000..eb0975e
--- /dev/null
+++ b/test/cctest/compiler/test-js-constant-cache.cc
@@ -0,0 +1,291 @@
+// 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/v8.h"
+
+#include "src/compiler/js-graph.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/typer.h"
+#include "src/types.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/value-helper.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+class JSCacheTesterHelper {
+ protected:
+  explicit JSCacheTesterHelper(Zone* zone)
+      : main_graph_(zone),
+        main_common_(zone),
+        main_javascript_(zone),
+        main_typer_(zone),
+        main_machine_() {}
+  Graph main_graph_;
+  CommonOperatorBuilder main_common_;
+  JSOperatorBuilder main_javascript_;
+  Typer main_typer_;
+  MachineOperatorBuilder main_machine_;
+};
+
+
+class JSConstantCacheTester : public HandleAndZoneScope,
+                              public JSCacheTesterHelper,
+                              public JSGraph {
+ public:
+  JSConstantCacheTester()
+      : JSCacheTesterHelper(main_zone()),
+        JSGraph(&main_graph_, &main_common_, &main_javascript_, &main_typer_,
+                &main_machine_) {}
+
+  Type* upper(Node* node) { return NodeProperties::GetBounds(node).upper; }
+
+  Handle<Object> handle(Node* node) {
+    CHECK_EQ(IrOpcode::kHeapConstant, node->opcode());
+    return OpParameter<Unique<Object> >(node).handle();
+  }
+
+  Factory* factory() { return main_isolate()->factory(); }
+};
+
+
+TEST(ZeroConstant1) {
+  JSConstantCacheTester T;
+
+  Node* zero = T.ZeroConstant();
+
+  CHECK_EQ(IrOpcode::kNumberConstant, zero->opcode());
+  CHECK_EQ(zero, T.Constant(0));
+  CHECK_NE(zero, T.Constant(-0.0));
+  CHECK_NE(zero, T.Constant(1.0));
+  CHECK_NE(zero, T.Constant(v8::base::OS::nan_value()));
+  CHECK_NE(zero, T.Float64Constant(0));
+  CHECK_NE(zero, T.Int32Constant(0));
+
+  Type* t = T.upper(zero);
+
+  CHECK(t->Is(Type::Number()));
+  CHECK(t->Is(Type::Integral32()));
+  CHECK(t->Is(Type::Signed32()));
+  CHECK(t->Is(Type::Unsigned32()));
+  CHECK(t->Is(Type::SignedSmall()));
+  CHECK(t->Is(Type::UnsignedSmall()));
+}
+
+
+TEST(MinusZeroConstant) {
+  JSConstantCacheTester T;
+
+  Node* minus_zero = T.Constant(-0.0);
+  Node* zero = T.ZeroConstant();
+
+  CHECK_EQ(IrOpcode::kNumberConstant, minus_zero->opcode());
+  CHECK_EQ(minus_zero, T.Constant(-0.0));
+  CHECK_NE(zero, minus_zero);
+
+  Type* t = T.upper(minus_zero);
+
+  CHECK(t->Is(Type::Number()));
+  CHECK(t->Is(Type::MinusZero()));
+  CHECK(!t->Is(Type::Integral32()));
+  CHECK(!t->Is(Type::Signed32()));
+  CHECK(!t->Is(Type::Unsigned32()));
+  CHECK(!t->Is(Type::SignedSmall()));
+  CHECK(!t->Is(Type::UnsignedSmall()));
+
+  double zero_value = OpParameter<double>(zero);
+  double minus_zero_value = OpParameter<double>(minus_zero);
+
+  CHECK_EQ(0.0, zero_value);
+  CHECK_NE(-0.0, zero_value);
+  CHECK_EQ(-0.0, minus_zero_value);
+  CHECK_NE(0.0, minus_zero_value);
+}
+
+
+TEST(ZeroConstant2) {
+  JSConstantCacheTester T;
+
+  Node* zero = T.Constant(0);
+
+  CHECK_EQ(IrOpcode::kNumberConstant, zero->opcode());
+  CHECK_EQ(zero, T.ZeroConstant());
+  CHECK_NE(zero, T.Constant(-0.0));
+  CHECK_NE(zero, T.Constant(1.0));
+  CHECK_NE(zero, T.Constant(v8::base::OS::nan_value()));
+  CHECK_NE(zero, T.Float64Constant(0));
+  CHECK_NE(zero, T.Int32Constant(0));
+
+  Type* t = T.upper(zero);
+
+  CHECK(t->Is(Type::Number()));
+  CHECK(t->Is(Type::Integral32()));
+  CHECK(t->Is(Type::Signed32()));
+  CHECK(t->Is(Type::Unsigned32()));
+  CHECK(t->Is(Type::SignedSmall()));
+  CHECK(t->Is(Type::UnsignedSmall()));
+}
+
+
+TEST(OneConstant1) {
+  JSConstantCacheTester T;
+
+  Node* one = T.OneConstant();
+
+  CHECK_EQ(IrOpcode::kNumberConstant, one->opcode());
+  CHECK_EQ(one, T.Constant(1));
+  CHECK_EQ(one, T.Constant(1.0));
+  CHECK_NE(one, T.Constant(1.01));
+  CHECK_NE(one, T.Constant(-1.01));
+  CHECK_NE(one, T.Constant(v8::base::OS::nan_value()));
+  CHECK_NE(one, T.Float64Constant(1.0));
+  CHECK_NE(one, T.Int32Constant(1));
+
+  Type* t = T.upper(one);
+
+  CHECK(t->Is(Type::Number()));
+  CHECK(t->Is(Type::Integral32()));
+  CHECK(t->Is(Type::Signed32()));
+  CHECK(t->Is(Type::Unsigned32()));
+  CHECK(t->Is(Type::SignedSmall()));
+  CHECK(t->Is(Type::UnsignedSmall()));
+}
+
+
+TEST(OneConstant2) {
+  JSConstantCacheTester T;
+
+  Node* one = T.Constant(1);
+
+  CHECK_EQ(IrOpcode::kNumberConstant, one->opcode());
+  CHECK_EQ(one, T.OneConstant());
+  CHECK_EQ(one, T.Constant(1.0));
+  CHECK_NE(one, T.Constant(1.01));
+  CHECK_NE(one, T.Constant(-1.01));
+  CHECK_NE(one, T.Constant(v8::base::OS::nan_value()));
+  CHECK_NE(one, T.Float64Constant(1.0));
+  CHECK_NE(one, T.Int32Constant(1));
+
+  Type* t = T.upper(one);
+
+  CHECK(t->Is(Type::Number()));
+  CHECK(t->Is(Type::Integral32()));
+  CHECK(t->Is(Type::Signed32()));
+  CHECK(t->Is(Type::Unsigned32()));
+  CHECK(t->Is(Type::SignedSmall()));
+  CHECK(t->Is(Type::UnsignedSmall()));
+}
+
+
+TEST(Canonicalizations) {
+  JSConstantCacheTester T;
+
+  CHECK_EQ(T.ZeroConstant(), T.ZeroConstant());
+  CHECK_EQ(T.UndefinedConstant(), T.UndefinedConstant());
+  CHECK_EQ(T.TheHoleConstant(), T.TheHoleConstant());
+  CHECK_EQ(T.TrueConstant(), T.TrueConstant());
+  CHECK_EQ(T.FalseConstant(), T.FalseConstant());
+  CHECK_EQ(T.NullConstant(), T.NullConstant());
+  CHECK_EQ(T.ZeroConstant(), T.ZeroConstant());
+  CHECK_EQ(T.OneConstant(), T.OneConstant());
+  CHECK_EQ(T.NaNConstant(), T.NaNConstant());
+}
+
+
+TEST(NoAliasing) {
+  JSConstantCacheTester T;
+
+  Node* nodes[] = {T.UndefinedConstant(), T.TheHoleConstant(), T.TrueConstant(),
+                   T.FalseConstant(),     T.NullConstant(),    T.ZeroConstant(),
+                   T.OneConstant(),       T.NaNConstant(),     T.Constant(21),
+                   T.Constant(22.2)};
+
+  for (size_t i = 0; i < arraysize(nodes); i++) {
+    for (size_t j = 0; j < arraysize(nodes); j++) {
+      if (i != j) CHECK_NE(nodes[i], nodes[j]);
+    }
+  }
+}
+
+
+TEST(CanonicalizingNumbers) {
+  JSConstantCacheTester T;
+
+  FOR_FLOAT64_INPUTS(i) {
+    Node* node = T.Constant(*i);
+    for (int j = 0; j < 5; j++) {
+      CHECK_EQ(node, T.Constant(*i));
+    }
+  }
+}
+
+
+TEST(NumberTypes) {
+  JSConstantCacheTester T;
+
+  FOR_FLOAT64_INPUTS(i) {
+    double value = *i;
+    Node* node = T.Constant(value);
+    CHECK(T.upper(node)->Equals(Type::Of(value, T.main_zone())));
+  }
+}
+
+
+TEST(HeapNumbers) {
+  JSConstantCacheTester T;
+
+  FOR_FLOAT64_INPUTS(i) {
+    double value = *i;
+    Handle<Object> num = T.factory()->NewNumber(value);
+    Handle<HeapNumber> heap = T.factory()->NewHeapNumber(value);
+    Node* node1 = T.Constant(value);
+    Node* node2 = T.Constant(num);
+    Node* node3 = T.Constant(heap);
+    CHECK_EQ(node1, node2);
+    CHECK_EQ(node1, node3);
+  }
+}
+
+
+TEST(OddballHandle) {
+  JSConstantCacheTester T;
+
+  CHECK_EQ(T.UndefinedConstant(), T.Constant(T.factory()->undefined_value()));
+  CHECK_EQ(T.TheHoleConstant(), T.Constant(T.factory()->the_hole_value()));
+  CHECK_EQ(T.TrueConstant(), T.Constant(T.factory()->true_value()));
+  CHECK_EQ(T.FalseConstant(), T.Constant(T.factory()->false_value()));
+  CHECK_EQ(T.NullConstant(), T.Constant(T.factory()->null_value()));
+  CHECK_EQ(T.NaNConstant(), T.Constant(T.factory()->nan_value()));
+}
+
+
+TEST(OddballValues) {
+  JSConstantCacheTester T;
+
+  CHECK_EQ(*T.factory()->undefined_value(), *T.handle(T.UndefinedConstant()));
+  CHECK_EQ(*T.factory()->the_hole_value(), *T.handle(T.TheHoleConstant()));
+  CHECK_EQ(*T.factory()->true_value(), *T.handle(T.TrueConstant()));
+  CHECK_EQ(*T.factory()->false_value(), *T.handle(T.FalseConstant()));
+  CHECK_EQ(*T.factory()->null_value(), *T.handle(T.NullConstant()));
+}
+
+
+TEST(OddballTypes) {
+  JSConstantCacheTester T;
+
+  CHECK(T.upper(T.UndefinedConstant())->Is(Type::Undefined()));
+  // TODO(dcarney): figure this out.
+  // CHECK(T.upper(T.TheHoleConstant())->Is(Type::Internal()));
+  CHECK(T.upper(T.TrueConstant())->Is(Type::Boolean()));
+  CHECK(T.upper(T.FalseConstant())->Is(Type::Boolean()));
+  CHECK(T.upper(T.NullConstant())->Is(Type::Null()));
+  CHECK(T.upper(T.ZeroConstant())->Is(Type::Number()));
+  CHECK(T.upper(T.OneConstant())->Is(Type::Number()));
+  CHECK(T.upper(T.NaNConstant())->Is(Type::NaN()));
+}
+
+
+TEST(ExternalReferences) {
+  // TODO(titzer): test canonicalization of external references.
+}
diff --git a/test/cctest/compiler/test-js-context-specialization.cc b/test/cctest/compiler/test-js-context-specialization.cc
new file mode 100644
index 0000000..47c660a
--- /dev/null
+++ b/test/cctest/compiler/test-js-context-specialization.cc
@@ -0,0 +1,307 @@
+// 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/js-context-specialization.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/source-position.h"
+#include "src/compiler/typer.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/function-tester.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+class ContextSpecializationTester : public HandleAndZoneScope,
+                                    public DirectGraphBuilder {
+ public:
+  ContextSpecializationTester()
+      : DirectGraphBuilder(new (main_zone()) Graph(main_zone())),
+        common_(main_zone()),
+        javascript_(main_zone()),
+        machine_(),
+        simplified_(main_zone()),
+        typer_(main_zone()),
+        jsgraph_(graph(), common(), &javascript_, &typer_, &machine_),
+        info_(main_isolate(), main_zone()) {}
+
+  Factory* factory() { return main_isolate()->factory(); }
+  CommonOperatorBuilder* common() { return &common_; }
+  JSOperatorBuilder* javascript() { return &javascript_; }
+  SimplifiedOperatorBuilder* simplified() { return &simplified_; }
+  JSGraph* jsgraph() { return &jsgraph_; }
+  CompilationInfo* info() { return &info_; }
+
+ private:
+  CommonOperatorBuilder common_;
+  JSOperatorBuilder javascript_;
+  MachineOperatorBuilder machine_;
+  SimplifiedOperatorBuilder simplified_;
+  Typer typer_;
+  JSGraph jsgraph_;
+  CompilationInfo info_;
+};
+
+
+TEST(ReduceJSLoadContext) {
+  ContextSpecializationTester t;
+
+  Node* start = t.NewNode(t.common()->Start(0));
+  t.graph()->SetStart(start);
+
+  // Make a context and initialize it a bit for this test.
+  Handle<Context> native = t.factory()->NewNativeContext();
+  Handle<Context> subcontext1 = t.factory()->NewNativeContext();
+  Handle<Context> subcontext2 = t.factory()->NewNativeContext();
+  subcontext2->set_previous(*subcontext1);
+  subcontext1->set_previous(*native);
+  Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
+  const int slot = Context::GLOBAL_OBJECT_INDEX;
+  native->set(slot, *expected);
+
+  Node* const_context = t.jsgraph()->Constant(native);
+  Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
+  Node* param_context = t.NewNode(t.common()->Parameter(0), start);
+  JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
+
+  {
+    // Mutable slot, constant context, depth = 0 => do nothing.
+    Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
+                           const_context, const_context, start);
+    Reduction r = spec.ReduceJSLoadContext(load);
+    CHECK(!r.Changed());
+  }
+
+  {
+    // Mutable slot, non-constant context, depth = 0 => do nothing.
+    Node* load = t.NewNode(t.javascript()->LoadContext(0, 0, false),
+                           param_context, param_context, start);
+    Reduction r = spec.ReduceJSLoadContext(load);
+    CHECK(!r.Changed());
+  }
+
+  {
+    // Mutable slot, constant context, depth > 0 => fold-in parent context.
+    Node* load = t.NewNode(
+        t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false),
+        deep_const_context, deep_const_context, start);
+    Reduction r = spec.ReduceJSLoadContext(load);
+    CHECK(r.Changed());
+    Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0);
+    CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
+    HeapObjectMatcher<Context> match(new_context_input);
+    CHECK_EQ(*native, *match.Value().handle());
+    ContextAccess access = OpParameter<ContextAccess>(r.replacement());
+    CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
+    CHECK_EQ(0, access.depth());
+    CHECK_EQ(false, access.immutable());
+  }
+
+  {
+    // Immutable slot, constant context, depth = 0 => specialize.
+    Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
+                           const_context, const_context, start);
+    Reduction r = spec.ReduceJSLoadContext(load);
+    CHECK(r.Changed());
+    CHECK(r.replacement() != load);
+
+    HeapObjectMatcher<Object> match(r.replacement());
+    CHECK(match.HasValue());
+    CHECK_EQ(*expected, *match.Value().handle());
+  }
+
+  // TODO(titzer): test with other kinds of contexts, e.g. a function context.
+  // TODO(sigurds): test that loads below create context are not optimized
+}
+
+
+TEST(ReduceJSStoreContext) {
+  ContextSpecializationTester t;
+
+  Node* start = t.NewNode(t.common()->Start(0));
+  t.graph()->SetStart(start);
+
+  // Make a context and initialize it a bit for this test.
+  Handle<Context> native = t.factory()->NewNativeContext();
+  Handle<Context> subcontext1 = t.factory()->NewNativeContext();
+  Handle<Context> subcontext2 = t.factory()->NewNativeContext();
+  subcontext2->set_previous(*subcontext1);
+  subcontext1->set_previous(*native);
+  Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
+  const int slot = Context::GLOBAL_OBJECT_INDEX;
+  native->set(slot, *expected);
+
+  Node* const_context = t.jsgraph()->Constant(native);
+  Node* deep_const_context = t.jsgraph()->Constant(subcontext2);
+  Node* param_context = t.NewNode(t.common()->Parameter(0), start);
+  JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
+
+  {
+    // Mutable slot, constant context, depth = 0 => do nothing.
+    Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), const_context,
+                           const_context, start);
+    Reduction r = spec.ReduceJSStoreContext(load);
+    CHECK(!r.Changed());
+  }
+
+  {
+    // Mutable slot, non-constant context, depth = 0 => do nothing.
+    Node* load = t.NewNode(t.javascript()->StoreContext(0, 0), param_context,
+                           param_context, start);
+    Reduction r = spec.ReduceJSStoreContext(load);
+    CHECK(!r.Changed());
+  }
+
+  {
+    // Immutable slot, constant context, depth = 0 => do nothing.
+    Node* load = t.NewNode(t.javascript()->StoreContext(0, slot), const_context,
+                           const_context, start);
+    Reduction r = spec.ReduceJSStoreContext(load);
+    CHECK(!r.Changed());
+  }
+
+  {
+    // Mutable slot, constant context, depth > 0 => fold-in parent context.
+    Node* load = t.NewNode(
+        t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX),
+        deep_const_context, deep_const_context, start);
+    Reduction r = spec.ReduceJSStoreContext(load);
+    CHECK(r.Changed());
+    Node* new_context_input = NodeProperties::GetValueInput(r.replacement(), 0);
+    CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
+    HeapObjectMatcher<Context> match(new_context_input);
+    CHECK_EQ(*native, *match.Value().handle());
+    ContextAccess access = OpParameter<ContextAccess>(r.replacement());
+    CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, access.index());
+    CHECK_EQ(0, access.depth());
+    CHECK_EQ(false, access.immutable());
+  }
+}
+
+
+// TODO(titzer): factor out common code with effects checking in typed lowering.
+static void CheckEffectInput(Node* effect, Node* use) {
+  CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
+}
+
+
+TEST(SpecializeToContext) {
+  ContextSpecializationTester t;
+
+  Node* start = t.NewNode(t.common()->Start(0));
+  t.graph()->SetStart(start);
+
+  // Make a context and initialize it a bit for this test.
+  Handle<Context> native = t.factory()->NewNativeContext();
+  Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
+  const int slot = Context::GLOBAL_OBJECT_INDEX;
+  native->set(slot, *expected);
+  t.info()->SetContext(native);
+
+  Node* const_context = t.jsgraph()->Constant(native);
+  Node* param_context = t.NewNode(t.common()->Parameter(0), start);
+  JSContextSpecializer spec(t.info(), t.jsgraph(), const_context);
+
+  {
+    // Check that SpecializeToContext() replaces values and forwards effects
+    // correctly, and folds values from constant and non-constant contexts
+    Node* effect_in = start;
+    Node* load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
+                           const_context, const_context, effect_in);
+
+
+    Node* value_use = t.NewNode(t.simplified()->ChangeTaggedToInt32(), load);
+    Node* other_load = t.NewNode(t.javascript()->LoadContext(0, slot, true),
+                                 param_context, param_context, load);
+    Node* effect_use = other_load;
+    Node* other_use =
+        t.NewNode(t.simplified()->ChangeTaggedToInt32(), other_load);
+
+    Node* add = t.NewNode(t.javascript()->Add(), value_use, other_use,
+                          param_context, other_load, start);
+
+    Node* ret = t.NewNode(t.common()->Return(), add, effect_use, start);
+    Node* end = t.NewNode(t.common()->End(), ret);
+    USE(end);
+    t.graph()->SetEnd(end);
+
+    // Double check the above graph is what we expect, or the test is broken.
+    CheckEffectInput(effect_in, load);
+    CheckEffectInput(load, effect_use);
+
+    // Perform the substitution on the entire graph.
+    spec.SpecializeToContext();
+
+    // Effects should have been forwarded (not replaced with a value).
+    CheckEffectInput(effect_in, effect_use);
+
+    // Use of {other_load} should not have been replaced.
+    CHECK_EQ(other_load, other_use->InputAt(0));
+
+    Node* replacement = value_use->InputAt(0);
+    HeapObjectMatcher<Object> match(replacement);
+    CHECK(match.HasValue());
+    CHECK_EQ(*expected, *match.Value().handle());
+  }
+  // TODO(titzer): clean up above test and test more complicated effects.
+}
+
+
+TEST(SpecializeJSFunction_ToConstant1) {
+  FunctionTester T(
+      "(function() { var x = 1; function inc(a)"
+      " { return a + x; } return inc; })()");
+
+  T.CheckCall(1.0, 0.0, 0.0);
+  T.CheckCall(2.0, 1.0, 0.0);
+  T.CheckCall(2.1, 1.1, 0.0);
+}
+
+
+TEST(SpecializeJSFunction_ToConstant2) {
+  FunctionTester T(
+      "(function() { var x = 1.5; var y = 2.25; var z = 3.75;"
+      " function f(a) { return a - x + y - z; } return f; })()");
+
+  T.CheckCall(-3.0, 0.0, 0.0);
+  T.CheckCall(-2.0, 1.0, 0.0);
+  T.CheckCall(-1.9, 1.1, 0.0);
+}
+
+
+TEST(SpecializeJSFunction_ToConstant3) {
+  FunctionTester T(
+      "(function() { var x = -11.5; function inc()"
+      " { return (function(a) { return a + x; }); }"
+      " return inc(); })()");
+
+  T.CheckCall(-11.5, 0.0, 0.0);
+  T.CheckCall(-10.5, 1.0, 0.0);
+  T.CheckCall(-10.4, 1.1, 0.0);
+}
+
+
+TEST(SpecializeJSFunction_ToConstant_uninit) {
+  {
+    FunctionTester T(
+        "(function() { if (false) { var x = 1; } function inc(a)"
+        " { return x; } return inc; })()");  // x is undefined!
+
+    CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsUndefined());
+    CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsUndefined());
+    CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsUndefined());
+  }
+
+  {
+    FunctionTester T(
+        "(function() { if (false) { var x = 1; } function inc(a)"
+        " { return a + x; } return inc; })()");  // x is undefined!
+
+    CHECK(T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsNaN());
+    CHECK(T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsNaN());
+    CHECK(T.Call(T.Val(-2.1), T.Val(0.0)).ToHandleChecked()->IsNaN());
+  }
+}
diff --git a/test/cctest/compiler/test-js-typed-lowering.cc b/test/cctest/compiler/test-js-typed-lowering.cc
new file mode 100644
index 0000000..cf126c2
--- /dev/null
+++ b/test/cctest/compiler/test-js-typed-lowering.cc
@@ -0,0 +1,1385 @@
+// 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/js-typed-lowering.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/opcodes.h"
+#include "src/compiler/typer.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+class JSTypedLoweringTester : public HandleAndZoneScope {
+ public:
+  explicit JSTypedLoweringTester(int num_parameters = 0)
+      : isolate(main_isolate()),
+        binop(NULL),
+        unop(NULL),
+        javascript(main_zone()),
+        simplified(main_zone()),
+        common(main_zone()),
+        graph(main_zone()),
+        typer(main_zone()),
+        context_node(NULL) {
+    typer.DecorateGraph(&graph);
+    Node* s = graph.NewNode(common.Start(num_parameters));
+    graph.SetStart(s);
+  }
+
+  Isolate* isolate;
+  const Operator* binop;
+  const Operator* unop;
+  JSOperatorBuilder javascript;
+  MachineOperatorBuilder machine;
+  SimplifiedOperatorBuilder simplified;
+  CommonOperatorBuilder common;
+  Graph graph;
+  Typer typer;
+  Node* context_node;
+
+  Node* Parameter(Type* t, int32_t index = 0) {
+    Node* n = graph.NewNode(common.Parameter(index), graph.start());
+    NodeProperties::SetBounds(n, Bounds(Type::None(), t));
+    return n;
+  }
+
+  Node* UndefinedConstant() {
+    Unique<Object> unique =
+        Unique<Object>::CreateImmovable(isolate->factory()->undefined_value());
+    return graph.NewNode(common.HeapConstant(unique));
+  }
+
+  Node* HeapConstant(Handle<Object> constant) {
+    Unique<Object> unique = Unique<Object>::CreateUninitialized(constant);
+    return graph.NewNode(common.HeapConstant(unique));
+  }
+
+  Node* EmptyFrameState(Node* context) {
+    Node* parameters = graph.NewNode(common.StateValues(0));
+    Node* locals = graph.NewNode(common.StateValues(0));
+    Node* stack = graph.NewNode(common.StateValues(0));
+
+    Node* state_node =
+        graph.NewNode(common.FrameState(JS_FRAME, BailoutId(0), kIgnoreOutput),
+                      parameters, locals, stack, context, UndefinedConstant());
+
+    return state_node;
+  }
+
+  Node* reduce(Node* node) {
+    JSGraph jsgraph(&graph, &common, &javascript, &typer, &machine);
+    JSTypedLowering reducer(&jsgraph);
+    Reduction reduction = reducer.Reduce(node);
+    if (reduction.Changed()) return reduction.replacement();
+    return node;
+  }
+
+  Node* start() { return graph.start(); }
+
+  Node* context() {
+    if (context_node == NULL) {
+      context_node = graph.NewNode(common.Parameter(-1), graph.start());
+    }
+    return context_node;
+  }
+
+  Node* control() { return start(); }
+
+  void CheckPureBinop(IrOpcode::Value expected, Node* node) {
+    CHECK_EQ(expected, node->opcode());
+    CHECK_EQ(2, node->InputCount());  // should not have context, effect, etc.
+  }
+
+  void CheckPureBinop(const Operator* expected, Node* node) {
+    CHECK_EQ(expected->opcode(), node->op()->opcode());
+    CHECK_EQ(2, node->InputCount());  // should not have context, effect, etc.
+  }
+
+  Node* ReduceUnop(const Operator* op, Type* input_type) {
+    return reduce(Unop(op, Parameter(input_type)));
+  }
+
+  Node* ReduceBinop(const Operator* op, Type* left_type, Type* right_type) {
+    return reduce(Binop(op, Parameter(left_type, 0), Parameter(right_type, 1)));
+  }
+
+  Node* Binop(const Operator* op, Node* left, Node* right) {
+    // JS binops also require context, effect, and control
+    return graph.NewNode(op, left, right, context(), start(), control());
+  }
+
+  Node* Unop(const Operator* op, Node* input) {
+    // JS unops also require context, effect, and control
+    return graph.NewNode(op, input, context(), start(), control());
+  }
+
+  Node* UseForEffect(Node* node) {
+    // TODO(titzer): use EffectPhi after fixing EffectCount
+    return graph.NewNode(javascript.ToNumber(), node, context(), node,
+                         control());
+  }
+
+  void CheckEffectInput(Node* effect, Node* use) {
+    CHECK_EQ(effect, NodeProperties::GetEffectInput(use));
+  }
+
+  void CheckInt32Constant(int32_t expected, Node* result) {
+    CHECK_EQ(IrOpcode::kInt32Constant, result->opcode());
+    CHECK_EQ(expected, OpParameter<int32_t>(result));
+  }
+
+  void CheckNumberConstant(double expected, Node* result) {
+    CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
+    CHECK_EQ(expected, OpParameter<double>(result));
+  }
+
+  void CheckNaN(Node* result) {
+    CHECK_EQ(IrOpcode::kNumberConstant, result->opcode());
+    double value = OpParameter<double>(result);
+    CHECK(std::isnan(value));
+  }
+
+  void CheckTrue(Node* result) {
+    CheckHandle(isolate->factory()->true_value(), result);
+  }
+
+  void CheckFalse(Node* result) {
+    CheckHandle(isolate->factory()->false_value(), result);
+  }
+
+  void CheckHandle(Handle<Object> expected, Node* result) {
+    CHECK_EQ(IrOpcode::kHeapConstant, result->opcode());
+    Handle<Object> value = OpParameter<Unique<Object> >(result).handle();
+    CHECK_EQ(*expected, *value);
+  }
+};
+
+static Type* kStringTypes[] = {Type::InternalizedString(), Type::OtherString(),
+                               Type::String()};
+
+
+static Type* kInt32Types[] = {
+    Type::UnsignedSmall(),   Type::OtherSignedSmall(), Type::OtherUnsigned31(),
+    Type::OtherUnsigned32(), Type::OtherSigned32(),    Type::SignedSmall(),
+    Type::Signed32(),        Type::Unsigned32(),       Type::Integral32()};
+
+
+static Type* kNumberTypes[] = {
+    Type::UnsignedSmall(),   Type::OtherSignedSmall(), Type::OtherUnsigned31(),
+    Type::OtherUnsigned32(), Type::OtherSigned32(),    Type::SignedSmall(),
+    Type::Signed32(),        Type::Unsigned32(),       Type::Integral32(),
+    Type::MinusZero(),       Type::NaN(),              Type::OtherNumber(),
+    Type::OrderedNumber(),   Type::Number()};
+
+
+static Type* kJSTypes[] = {Type::Undefined(), Type::Null(),   Type::Boolean(),
+                           Type::Number(),    Type::String(), Type::Object()};
+
+
+static Type* I32Type(bool is_signed) {
+  return is_signed ? Type::Signed32() : Type::Unsigned32();
+}
+
+
+static IrOpcode::Value NumberToI32(bool is_signed) {
+  return is_signed ? IrOpcode::kNumberToInt32 : IrOpcode::kNumberToUint32;
+}
+
+
+// TODO(turbofan): Lowering of StringAdd is disabled for now.
+#if 0
+TEST(StringBinops) {
+  JSTypedLoweringTester R;
+
+  for (size_t i = 0; i < arraysize(kStringTypes); ++i) {
+    Node* p0 = R.Parameter(kStringTypes[i], 0);
+
+    for (size_t j = 0; j < arraysize(kStringTypes); ++j) {
+      Node* p1 = R.Parameter(kStringTypes[j], 1);
+
+      Node* add = R.Binop(R.javascript.Add(), p0, p1);
+      Node* r = R.reduce(add);
+
+      R.CheckPureBinop(IrOpcode::kStringAdd, r);
+      CHECK_EQ(p0, r->InputAt(0));
+      CHECK_EQ(p1, r->InputAt(1));
+    }
+  }
+}
+#endif
+
+
+TEST(AddNumber1) {
+  JSTypedLoweringTester R;
+  for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
+    Node* p0 = R.Parameter(kNumberTypes[i], 0);
+    Node* p1 = R.Parameter(kNumberTypes[i], 1);
+    Node* add = R.Binop(R.javascript.Add(), p0, p1);
+    Node* r = R.reduce(add);
+
+    R.CheckPureBinop(IrOpcode::kNumberAdd, r);
+    CHECK_EQ(p0, r->InputAt(0));
+    CHECK_EQ(p1, r->InputAt(1));
+  }
+}
+
+
+TEST(NumberBinops) {
+  JSTypedLoweringTester R;
+  const Operator* ops[] = {
+      R.javascript.Add(),      R.simplified.NumberAdd(),
+      R.javascript.Subtract(), R.simplified.NumberSubtract(),
+      R.javascript.Multiply(), R.simplified.NumberMultiply(),
+      R.javascript.Divide(),   R.simplified.NumberDivide(),
+      R.javascript.Modulus(),  R.simplified.NumberModulus(),
+  };
+
+  for (size_t i = 0; i < arraysize(kNumberTypes); ++i) {
+    Node* p0 = R.Parameter(kNumberTypes[i], 0);
+
+    for (size_t j = 0; j < arraysize(kNumberTypes); ++j) {
+      Node* p1 = R.Parameter(kNumberTypes[j], 1);
+
+      for (size_t k = 0; k < arraysize(ops); k += 2) {
+        Node* add = R.Binop(ops[k], p0, p1);
+        Node* r = R.reduce(add);
+
+        R.CheckPureBinop(ops[k + 1], r);
+        CHECK_EQ(p0, r->InputAt(0));
+        CHECK_EQ(p1, r->InputAt(1));
+      }
+    }
+  }
+}
+
+
+static void CheckToI32(Node* old_input, Node* new_input, bool is_signed) {
+  Type* old_type = NodeProperties::GetBounds(old_input).upper;
+  Type* expected_type = I32Type(is_signed);
+  if (old_type->Is(expected_type)) {
+    CHECK_EQ(old_input, new_input);
+  } else if (new_input->opcode() == IrOpcode::kNumberConstant) {
+    CHECK(NodeProperties::GetBounds(new_input).upper->Is(expected_type));
+    double v = OpParameter<double>(new_input);
+    double e = static_cast<double>(is_signed ? FastD2I(v) : FastD2UI(v));
+    CHECK_EQ(e, v);
+  } else {
+    CHECK_EQ(NumberToI32(is_signed), new_input->opcode());
+  }
+}
+
+
+// A helper class for testing lowering of bitwise shift operators.
+class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
+ public:
+  static const int kNumberOps = 6;
+  const Operator* ops[kNumberOps];
+  bool signedness[kNumberOps];
+
+  JSBitwiseShiftTypedLoweringTester() {
+    int i = 0;
+    set(i++, javascript.ShiftLeft(), true);
+    set(i++, machine.Word32Shl(), false);
+    set(i++, javascript.ShiftRight(), true);
+    set(i++, machine.Word32Sar(), false);
+    set(i++, javascript.ShiftRightLogical(), false);
+    set(i++, machine.Word32Shr(), false);
+  }
+
+ private:
+  void set(int idx, const Operator* op, bool s) {
+    ops[idx] = op;
+    signedness[idx] = s;
+  }
+};
+
+
+TEST(Int32BitwiseShifts) {
+  JSBitwiseShiftTypedLoweringTester R;
+
+  Type* types[] = {
+      Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
+      Type::Unsigned32(),  Type::Signed32(),      Type::MinusZero(),
+      Type::NaN(),         Type::OtherNumber(),   Type::Undefined(),
+      Type::Null(),        Type::Boolean(),       Type::Number(),
+      Type::String(),      Type::Object()};
+
+  for (size_t i = 0; i < arraysize(types); ++i) {
+    Node* p0 = R.Parameter(types[i], 0);
+
+    for (size_t j = 0; j < arraysize(types); ++j) {
+      Node* p1 = R.Parameter(types[j], 1);
+
+      for (int k = 0; k < R.kNumberOps; k += 2) {
+        Node* add = R.Binop(R.ops[k], p0, p1);
+        Node* r = R.reduce(add);
+
+        R.CheckPureBinop(R.ops[k + 1], r);
+        Node* r0 = r->InputAt(0);
+        Node* r1 = r->InputAt(1);
+
+        CheckToI32(p0, r0, R.signedness[k]);
+
+        R.CheckPureBinop(IrOpcode::kWord32And, r1);
+        CheckToI32(p1, r1->InputAt(0), R.signedness[k + 1]);
+        R.CheckInt32Constant(0x1F, r1->InputAt(1));
+      }
+    }
+  }
+}
+
+
+// A helper class for testing lowering of bitwise operators.
+class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
+ public:
+  static const int kNumberOps = 6;
+  const Operator* ops[kNumberOps];
+  bool signedness[kNumberOps];
+
+  JSBitwiseTypedLoweringTester() {
+    int i = 0;
+    set(i++, javascript.BitwiseOr(), true);
+    set(i++, machine.Word32Or(), true);
+    set(i++, javascript.BitwiseXor(), true);
+    set(i++, machine.Word32Xor(), true);
+    set(i++, javascript.BitwiseAnd(), true);
+    set(i++, machine.Word32And(), true);
+  }
+
+ private:
+  void set(int idx, const Operator* op, bool s) {
+    ops[idx] = op;
+    signedness[idx] = s;
+  }
+};
+
+
+TEST(Int32BitwiseBinops) {
+  JSBitwiseTypedLoweringTester R;
+
+  Type* types[] = {
+      Type::SignedSmall(), Type::UnsignedSmall(), Type::OtherSigned32(),
+      Type::Unsigned32(),  Type::Signed32(),      Type::MinusZero(),
+      Type::NaN(),         Type::OtherNumber(),   Type::Undefined(),
+      Type::Null(),        Type::Boolean(),       Type::Number(),
+      Type::String(),      Type::Object()};
+
+  for (size_t i = 0; i < arraysize(types); ++i) {
+    Node* p0 = R.Parameter(types[i], 0);
+
+    for (size_t j = 0; j < arraysize(types); ++j) {
+      Node* p1 = R.Parameter(types[j], 1);
+
+      for (int k = 0; k < R.kNumberOps; k += 2) {
+        Node* add = R.Binop(R.ops[k], p0, p1);
+        Node* r = R.reduce(add);
+
+        R.CheckPureBinop(R.ops[k + 1], r);
+
+        CheckToI32(p0, r->InputAt(0), R.signedness[k]);
+        CheckToI32(p1, r->InputAt(1), R.signedness[k + 1]);
+      }
+    }
+  }
+}
+
+
+TEST(JSToNumber1) {
+  JSTypedLoweringTester R;
+  const Operator* ton = R.javascript.ToNumber();
+
+  for (size_t i = 0; i < arraysize(kNumberTypes); i++) {  // ToNumber(number)
+    Node* r = R.ReduceUnop(ton, kNumberTypes[i]);
+    CHECK_EQ(IrOpcode::kParameter, r->opcode());
+  }
+
+  {  // ToNumber(undefined)
+    Node* r = R.ReduceUnop(ton, Type::Undefined());
+    R.CheckNaN(r);
+  }
+
+  {  // ToNumber(null)
+    Node* r = R.ReduceUnop(ton, Type::Null());
+    R.CheckNumberConstant(0.0, r);
+  }
+}
+
+
+TEST(JSToNumber_replacement) {
+  JSTypedLoweringTester R;
+
+  Type* types[] = {Type::Null(), Type::Undefined(), Type::Number()};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* n = R.Parameter(types[i]);
+    Node* c = R.graph.NewNode(R.javascript.ToNumber(), n, R.context(),
+                              R.start(), R.start());
+    Node* effect_use = R.UseForEffect(c);
+    Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
+
+    R.CheckEffectInput(c, effect_use);
+    Node* r = R.reduce(c);
+
+    if (types[i]->Is(Type::Number())) {
+      CHECK_EQ(n, r);
+    } else {
+      CHECK_EQ(IrOpcode::kNumberConstant, r->opcode());
+    }
+
+    CHECK_EQ(n, add->InputAt(0));
+    CHECK_EQ(r, add->InputAt(1));
+    R.CheckEffectInput(R.start(), effect_use);
+  }
+}
+
+
+TEST(JSToNumberOfConstant) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.common.NumberConstant(0), R.common.NumberConstant(-1),
+      R.common.NumberConstant(0.1), R.common.Int32Constant(1177),
+      R.common.Float64Constant(0.99)};
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    Node* n = R.graph.NewNode(ops[i]);
+    Node* convert = R.Unop(R.javascript.ToNumber(), n);
+    Node* r = R.reduce(convert);
+    // Note that either outcome below is correct. It only depends on whether
+    // the types of constants are eagerly computed or only computed by the
+    // typing pass.
+    if (NodeProperties::GetBounds(n).upper->Is(Type::Number())) {
+      // If number constants are eagerly typed, then reduction should
+      // remove the ToNumber.
+      CHECK_EQ(n, r);
+    } else {
+      // Otherwise, type-based lowering should only look at the type, and
+      // *not* try to constant fold.
+      CHECK_EQ(convert, r);
+    }
+  }
+}
+
+
+TEST(JSToNumberOfNumberOrOtherPrimitive) {
+  JSTypedLoweringTester R;
+  Type* others[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
+                    Type::String()};
+
+  for (size_t i = 0; i < arraysize(others); i++) {
+    Type* t = Type::Union(Type::Number(), others[i], R.main_zone());
+    Node* r = R.ReduceUnop(R.javascript.ToNumber(), t);
+    CHECK_EQ(IrOpcode::kJSToNumber, r->opcode());
+  }
+}
+
+
+TEST(JSToBoolean) {
+  JSTypedLoweringTester R;
+  const Operator* op = R.javascript.ToBoolean();
+
+  {  // ToBoolean(undefined)
+    Node* r = R.ReduceUnop(op, Type::Undefined());
+    R.CheckFalse(r);
+  }
+
+  {  // ToBoolean(null)
+    Node* r = R.ReduceUnop(op, Type::Null());
+    R.CheckFalse(r);
+  }
+
+  {  // ToBoolean(boolean)
+    Node* r = R.ReduceUnop(op, Type::Boolean());
+    CHECK_EQ(IrOpcode::kParameter, r->opcode());
+  }
+
+  {  // ToBoolean(ordered-number)
+    Node* r = R.ReduceUnop(op, Type::OrderedNumber());
+    CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
+    Node* i = r->InputAt(0);
+    CHECK_EQ(IrOpcode::kNumberEqual, i->opcode());
+    // ToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
+  }
+
+  {  // ToBoolean(string)
+    Node* r = R.ReduceUnop(op, Type::String());
+    // TODO(titzer): test will break with better js-typed-lowering
+    CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
+  }
+
+  {  // ToBoolean(object)
+    Node* r = R.ReduceUnop(op, Type::DetectableObject());
+    R.CheckTrue(r);
+  }
+
+  {  // ToBoolean(undetectable)
+    Node* r = R.ReduceUnop(op, Type::Undetectable());
+    R.CheckFalse(r);
+  }
+
+  {  // ToBoolean(object)
+    Node* r = R.ReduceUnop(op, Type::Object());
+    CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
+  }
+}
+
+
+TEST(JSToBoolean_replacement) {
+  JSTypedLoweringTester R;
+
+  Type* types[] = {Type::Null(),             Type::Undefined(),
+                   Type::Boolean(),          Type::OrderedNumber(),
+                   Type::DetectableObject(), Type::Undetectable()};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* n = R.Parameter(types[i]);
+    Node* c = R.graph.NewNode(R.javascript.ToBoolean(), n, R.context(),
+                              R.start(), R.start());
+    Node* effect_use = R.UseForEffect(c);
+    Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
+
+    R.CheckEffectInput(c, effect_use);
+    Node* r = R.reduce(c);
+
+    if (types[i]->Is(Type::Boolean())) {
+      CHECK_EQ(n, r);
+    } else if (types[i]->Is(Type::OrderedNumber())) {
+      CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
+    } else {
+      CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
+    }
+
+    CHECK_EQ(n, add->InputAt(0));
+    CHECK_EQ(r, add->InputAt(1));
+    R.CheckEffectInput(R.start(), effect_use);
+  }
+}
+
+
+TEST(JSToString1) {
+  JSTypedLoweringTester R;
+
+  for (size_t i = 0; i < arraysize(kStringTypes); i++) {
+    Node* r = R.ReduceUnop(R.javascript.ToString(), kStringTypes[i]);
+    CHECK_EQ(IrOpcode::kParameter, r->opcode());
+  }
+
+  const Operator* op = R.javascript.ToString();
+
+  {  // ToString(undefined) => "undefined"
+    Node* r = R.ReduceUnop(op, Type::Undefined());
+    R.CheckHandle(R.isolate->factory()->undefined_string(), r);
+  }
+
+  {  // ToString(null) => "null"
+    Node* r = R.ReduceUnop(op, Type::Null());
+    R.CheckHandle(R.isolate->factory()->null_string(), r);
+  }
+
+  {  // ToString(boolean)
+    Node* r = R.ReduceUnop(op, Type::Boolean());
+    // TODO(titzer): could be a branch
+    CHECK_EQ(IrOpcode::kJSToString, r->opcode());
+  }
+
+  {  // ToString(number)
+    Node* r = R.ReduceUnop(op, Type::Number());
+    // TODO(titzer): could remove effects
+    CHECK_EQ(IrOpcode::kJSToString, r->opcode());
+  }
+
+  {  // ToString(string)
+    Node* r = R.ReduceUnop(op, Type::String());
+    CHECK_EQ(IrOpcode::kParameter, r->opcode());  // No-op
+  }
+
+  {  // ToString(object)
+    Node* r = R.ReduceUnop(op, Type::Object());
+    CHECK_EQ(IrOpcode::kJSToString, r->opcode());  // No reduction.
+  }
+}
+
+
+TEST(JSToString_replacement) {
+  JSTypedLoweringTester R;
+
+  Type* types[] = {Type::Null(), Type::Undefined(), Type::String()};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* n = R.Parameter(types[i]);
+    Node* c = R.graph.NewNode(R.javascript.ToString(), n, R.context(),
+                              R.start(), R.start());
+    Node* effect_use = R.UseForEffect(c);
+    Node* add = R.graph.NewNode(R.simplified.ReferenceEqual(Type::Any()), n, c);
+
+    R.CheckEffectInput(c, effect_use);
+    Node* r = R.reduce(c);
+
+    if (types[i]->Is(Type::String())) {
+      CHECK_EQ(n, r);
+    } else {
+      CHECK_EQ(IrOpcode::kHeapConstant, r->opcode());
+    }
+
+    CHECK_EQ(n, add->InputAt(0));
+    CHECK_EQ(r, add->InputAt(1));
+    R.CheckEffectInput(R.start(), effect_use);
+  }
+}
+
+
+TEST(StringComparison) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.LessThan(),           R.simplified.StringLessThan(),
+      R.javascript.LessThanOrEqual(),    R.simplified.StringLessThanOrEqual(),
+      R.javascript.GreaterThan(),        R.simplified.StringLessThan(),
+      R.javascript.GreaterThanOrEqual(), R.simplified.StringLessThanOrEqual()};
+
+  for (size_t i = 0; i < arraysize(kStringTypes); i++) {
+    Node* p0 = R.Parameter(kStringTypes[i], 0);
+    for (size_t j = 0; j < arraysize(kStringTypes); j++) {
+      Node* p1 = R.Parameter(kStringTypes[j], 1);
+
+      for (size_t k = 0; k < arraysize(ops); k += 2) {
+        Node* cmp = R.Binop(ops[k], p0, p1);
+        Node* r = R.reduce(cmp);
+
+        R.CheckPureBinop(ops[k + 1], r);
+        if (k >= 4) {
+          // GreaterThan and GreaterThanOrEqual commute the inputs
+          // and use the LessThan and LessThanOrEqual operators.
+          CHECK_EQ(p1, r->InputAt(0));
+          CHECK_EQ(p0, r->InputAt(1));
+        } else {
+          CHECK_EQ(p0, r->InputAt(0));
+          CHECK_EQ(p1, r->InputAt(1));
+        }
+      }
+    }
+  }
+}
+
+
+static void CheckIsConvertedToNumber(Node* val, Node* converted) {
+  if (NodeProperties::GetBounds(val).upper->Is(Type::Number())) {
+    CHECK_EQ(val, converted);
+  } else if (NodeProperties::GetBounds(val).upper->Is(Type::Boolean())) {
+    CHECK_EQ(IrOpcode::kBooleanToNumber, converted->opcode());
+    CHECK_EQ(val, converted->InputAt(0));
+  } else {
+    if (converted->opcode() == IrOpcode::kNumberConstant) return;
+    CHECK_EQ(IrOpcode::kJSToNumber, converted->opcode());
+    CHECK_EQ(val, converted->InputAt(0));
+  }
+}
+
+
+TEST(NumberComparison) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.LessThan(),           R.simplified.NumberLessThan(),
+      R.javascript.LessThanOrEqual(),    R.simplified.NumberLessThanOrEqual(),
+      R.javascript.GreaterThan(),        R.simplified.NumberLessThan(),
+      R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual()};
+
+  for (size_t i = 0; i < arraysize(kJSTypes); i++) {
+    Type* t0 = kJSTypes[i];
+    // Skip Type::String and Type::Receiver which might coerce into a string.
+    if (t0->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
+    Node* p0 = R.Parameter(t0, 0);
+
+    for (size_t j = 0; j < arraysize(kJSTypes); j++) {
+      Type* t1 = kJSTypes[j];
+      // Skip Type::String and Type::Receiver which might coerce into a string.
+      if (t1->Is(Type::String()) || t0->Is(Type::Receiver())) continue;
+      Node* p1 = R.Parameter(t1, 1);
+
+      for (size_t k = 0; k < arraysize(ops); k += 2) {
+        Node* cmp = R.Binop(ops[k], p0, p1);
+        Node* r = R.reduce(cmp);
+
+        R.CheckPureBinop(ops[k + 1], r);
+        if (k >= 4) {
+          // GreaterThan and GreaterThanOrEqual commute the inputs
+          // and use the LessThan and LessThanOrEqual operators.
+          CheckIsConvertedToNumber(p1, r->InputAt(0));
+          CheckIsConvertedToNumber(p0, r->InputAt(1));
+        } else {
+          CheckIsConvertedToNumber(p0, r->InputAt(0));
+          CheckIsConvertedToNumber(p1, r->InputAt(1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(MixedComparison1) {
+  JSTypedLoweringTester R;
+
+  Type* types[] = {Type::Number(), Type::String(),
+                   Type::Union(Type::Number(), Type::String(), R.main_zone())};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* p0 = R.Parameter(types[i], 0);
+
+    for (size_t j = 0; j < arraysize(types); j++) {
+      Node* p1 = R.Parameter(types[j], 1);
+      {
+        Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
+        Node* r = R.reduce(cmp);
+
+        if (!types[i]->Maybe(Type::String()) ||
+            !types[j]->Maybe(Type::String())) {
+          if (types[i]->Is(Type::String()) && types[j]->Is(Type::String())) {
+            R.CheckPureBinop(R.simplified.StringLessThan(), r);
+          } else {
+            R.CheckPureBinop(R.simplified.NumberLessThan(), r);
+          }
+        } else {
+          CHECK_EQ(cmp, r);  // No reduction of mixed types.
+        }
+      }
+    }
+  }
+}
+
+
+TEST(ObjectComparison) {
+  JSTypedLoweringTester R;
+
+  Node* p0 = R.Parameter(Type::Number(), 0);
+  Node* p1 = R.Parameter(Type::Object(), 1);
+
+  Node* cmp = R.Binop(R.javascript.LessThan(), p0, p1);
+  Node* effect_use = R.UseForEffect(cmp);
+
+  R.CheckEffectInput(R.start(), cmp);
+  R.CheckEffectInput(cmp, effect_use);
+
+  Node* r = R.reduce(cmp);
+
+  R.CheckPureBinop(R.simplified.NumberLessThan(), r);
+
+  Node* i0 = r->InputAt(0);
+  Node* i1 = r->InputAt(1);
+
+  CHECK_EQ(p0, i0);
+  CHECK_NE(p1, i1);
+  CHECK_EQ(IrOpcode::kParameter, i0->opcode());
+  CHECK_EQ(IrOpcode::kJSToNumber, i1->opcode());
+
+  // Check effect chain is correct.
+  R.CheckEffectInput(R.start(), i1);
+  R.CheckEffectInput(i1, effect_use);
+}
+
+
+TEST(UnaryNot) {
+  JSTypedLoweringTester R;
+  const Operator* opnot = R.javascript.UnaryNot();
+
+  for (size_t i = 0; i < arraysize(kJSTypes); i++) {
+    Node* orig = R.Unop(opnot, R.Parameter(kJSTypes[i]));
+    Node* use = R.graph.NewNode(R.common.Return(), orig);
+    Node* r = R.reduce(orig);
+    // TODO(titzer): test will break if/when js-typed-lowering constant folds.
+    CHECK_EQ(IrOpcode::kBooleanNot, use->InputAt(0)->opcode());
+
+    if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
+      // The original node was turned into a ToBoolean.
+      CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
+    } else {
+      CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
+    }
+  }
+}
+
+
+TEST(RemoveToNumberEffects) {
+  FLAG_turbo_deoptimization = true;
+
+  JSTypedLoweringTester R;
+
+  Node* effect_use = NULL;
+  for (int i = 0; i < 10; i++) {
+    Node* p0 = R.Parameter(Type::Number());
+    Node* ton = R.Unop(R.javascript.ToNumber(), p0);
+    Node* frame_state = R.EmptyFrameState(R.context());
+    effect_use = NULL;
+
+    switch (i) {
+      case 0:
+        effect_use = R.graph.NewNode(R.javascript.ToNumber(), p0, R.context(),
+                                     ton, R.start());
+        break;
+      case 1:
+        effect_use = R.graph.NewNode(R.javascript.ToNumber(), ton, R.context(),
+                                     ton, R.start());
+        break;
+      case 2:
+        effect_use = R.graph.NewNode(R.common.EffectPhi(1), ton, R.start());
+      case 3:
+        effect_use = R.graph.NewNode(R.javascript.Add(), ton, ton, R.context(),
+                                     frame_state, ton, R.start());
+        break;
+      case 4:
+        effect_use = R.graph.NewNode(R.javascript.Add(), p0, p0, R.context(),
+                                     frame_state, ton, R.start());
+        break;
+      case 5:
+        effect_use = R.graph.NewNode(R.common.Return(), p0, ton, R.start());
+        break;
+      case 6:
+        effect_use = R.graph.NewNode(R.common.Return(), ton, ton, R.start());
+    }
+
+    R.CheckEffectInput(R.start(), ton);
+    if (effect_use != NULL) R.CheckEffectInput(ton, effect_use);
+
+    Node* r = R.reduce(ton);
+    CHECK_EQ(p0, r);
+    CHECK_NE(R.start(), r);
+
+    if (effect_use != NULL) {
+      R.CheckEffectInput(R.start(), effect_use);
+      // Check that value uses of ToNumber() do not go to start().
+      for (int i = 0; i < effect_use->op()->InputCount(); i++) {
+        CHECK_NE(R.start(), effect_use->InputAt(i));
+      }
+    }
+  }
+
+  CHECK_EQ(NULL, effect_use);  // should have done all cases above.
+}
+
+
+// Helper class for testing the reduction of a single binop.
+class BinopEffectsTester {
+ public:
+  explicit BinopEffectsTester(const Operator* op, Type* t0, Type* t1)
+      : R(),
+        p0(R.Parameter(t0, 0)),
+        p1(R.Parameter(t1, 1)),
+        binop(R.Binop(op, p0, p1)),
+        effect_use(R.graph.NewNode(R.common.EffectPhi(1), binop, R.start())) {
+    // Effects should be ordered start -> binop -> effect_use
+    R.CheckEffectInput(R.start(), binop);
+    R.CheckEffectInput(binop, effect_use);
+    result = R.reduce(binop);
+  }
+
+  JSTypedLoweringTester R;
+  Node* p0;
+  Node* p1;
+  Node* binop;
+  Node* effect_use;
+  Node* result;
+
+  void CheckEffectsRemoved() { R.CheckEffectInput(R.start(), effect_use); }
+
+  void CheckEffectOrdering(Node* n0) {
+    R.CheckEffectInput(R.start(), n0);
+    R.CheckEffectInput(n0, effect_use);
+  }
+
+  void CheckEffectOrdering(Node* n0, Node* n1) {
+    R.CheckEffectInput(R.start(), n0);
+    R.CheckEffectInput(n0, n1);
+    R.CheckEffectInput(n1, effect_use);
+  }
+
+  Node* CheckConvertedInput(IrOpcode::Value opcode, int which, bool effects) {
+    return CheckConverted(opcode, result->InputAt(which), effects);
+  }
+
+  Node* CheckConverted(IrOpcode::Value opcode, Node* node, bool effects) {
+    CHECK_EQ(opcode, node->opcode());
+    if (effects) {
+      CHECK_LT(0, OperatorProperties::GetEffectInputCount(node->op()));
+    } else {
+      CHECK_EQ(0, OperatorProperties::GetEffectInputCount(node->op()));
+    }
+    return node;
+  }
+
+  Node* CheckNoOp(int which) {
+    CHECK_EQ(which == 0 ? p0 : p1, result->InputAt(which));
+    return result->InputAt(which);
+  }
+};
+
+
+// Helper function for strict and non-strict equality reductions.
+void CheckEqualityReduction(JSTypedLoweringTester* R, bool strict, Node* l,
+                            Node* r, IrOpcode::Value expected) {
+  for (int j = 0; j < 2; j++) {
+    Node* p0 = j == 0 ? l : r;
+    Node* p1 = j == 1 ? l : r;
+
+    {
+      Node* eq = strict ? R->graph.NewNode(R->javascript.StrictEqual(), p0, p1)
+                        : R->Binop(R->javascript.Equal(), p0, p1);
+      Node* r = R->reduce(eq);
+      R->CheckPureBinop(expected, r);
+    }
+
+    {
+      Node* ne = strict
+                     ? R->graph.NewNode(R->javascript.StrictNotEqual(), p0, p1)
+                     : R->Binop(R->javascript.NotEqual(), p0, p1);
+      Node* n = R->reduce(ne);
+      CHECK_EQ(IrOpcode::kBooleanNot, n->opcode());
+      Node* r = n->InputAt(0);
+      R->CheckPureBinop(expected, r);
+    }
+  }
+}
+
+
+TEST(EqualityForNumbers) {
+  JSTypedLoweringTester R;
+
+  Type* simple_number_types[] = {Type::UnsignedSmall(), Type::SignedSmall(),
+                                 Type::Signed32(), Type::Unsigned32(),
+                                 Type::Number()};
+
+
+  for (size_t i = 0; i < arraysize(simple_number_types); ++i) {
+    Node* p0 = R.Parameter(simple_number_types[i], 0);
+
+    for (size_t j = 0; j < arraysize(simple_number_types); ++j) {
+      Node* p1 = R.Parameter(simple_number_types[j], 1);
+
+      CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kNumberEqual);
+      CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kNumberEqual);
+    }
+  }
+}
+
+
+TEST(StrictEqualityForRefEqualTypes) {
+  JSTypedLoweringTester R;
+
+  Type* types[] = {Type::Undefined(), Type::Null(), Type::Boolean(),
+                   Type::Object(), Type::Receiver()};
+
+  Node* p0 = R.Parameter(Type::Any());
+  for (size_t i = 0; i < arraysize(types); i++) {
+    Node* p1 = R.Parameter(types[i]);
+    CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kReferenceEqual);
+  }
+  // TODO(titzer): Equal(RefEqualTypes)
+}
+
+
+TEST(StringEquality) {
+  JSTypedLoweringTester R;
+  Node* p0 = R.Parameter(Type::String());
+  Node* p1 = R.Parameter(Type::String());
+
+  CheckEqualityReduction(&R, true, p0, p1, IrOpcode::kStringEqual);
+  CheckEqualityReduction(&R, false, p0, p1, IrOpcode::kStringEqual);
+}
+
+
+TEST(RemovePureNumberBinopEffects) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.Equal(),           R.simplified.NumberEqual(),
+      R.javascript.Add(),             R.simplified.NumberAdd(),
+      R.javascript.Subtract(),        R.simplified.NumberSubtract(),
+      R.javascript.Multiply(),        R.simplified.NumberMultiply(),
+      R.javascript.Divide(),          R.simplified.NumberDivide(),
+      R.javascript.Modulus(),         R.simplified.NumberModulus(),
+      R.javascript.LessThan(),        R.simplified.NumberLessThan(),
+      R.javascript.LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
+  };
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Number(), Type::Number());
+    CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    B.CheckNoOp(0);
+    B.CheckNoOp(1);
+
+    B.CheckEffectsRemoved();
+  }
+}
+
+
+TEST(OrderNumberBinopEffects1) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.Subtract(), R.simplified.NumberSubtract(),
+      R.javascript.Multiply(), R.simplified.NumberMultiply(),
+      R.javascript.Divide(),   R.simplified.NumberDivide(),
+      R.javascript.Modulus(),  R.simplified.NumberModulus(),
+  };
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Object(), Type::String());
+    CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
+
+    Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
+    Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
+
+    CHECK_EQ(B.p0, i0->InputAt(0));
+    CHECK_EQ(B.p1, i1->InputAt(0));
+
+    // Effects should be ordered start -> i0 -> i1 -> effect_use
+    B.CheckEffectOrdering(i0, i1);
+  }
+}
+
+
+TEST(OrderNumberBinopEffects2) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.Add(),      R.simplified.NumberAdd(),
+      R.javascript.Subtract(), R.simplified.NumberSubtract(),
+      R.javascript.Multiply(), R.simplified.NumberMultiply(),
+      R.javascript.Divide(),   R.simplified.NumberDivide(),
+      R.javascript.Modulus(),  R.simplified.NumberModulus(),
+  };
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
+
+    Node* i0 = B.CheckNoOp(0);
+    Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
+
+    CHECK_EQ(B.p0, i0);
+    CHECK_EQ(B.p1, i1->InputAt(0));
+
+    // Effects should be ordered start -> i1 -> effect_use
+    B.CheckEffectOrdering(i1);
+  }
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
+
+    Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
+    Node* i1 = B.CheckNoOp(1);
+
+    CHECK_EQ(B.p0, i0->InputAt(0));
+    CHECK_EQ(B.p1, i1);
+
+    // Effects should be ordered start -> i0 -> effect_use
+    B.CheckEffectOrdering(i0);
+  }
+}
+
+
+TEST(OrderCompareEffects) {
+  JSTypedLoweringTester R;
+
+  const Operator* ops[] = {
+      R.javascript.GreaterThan(), R.simplified.NumberLessThan(),
+      R.javascript.GreaterThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
+  };
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Symbol(), Type::String());
+    CHECK_EQ(ops[j + 1]->opcode(), B.result->op()->opcode());
+
+    Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
+    Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
+
+    // Inputs should be commuted.
+    CHECK_EQ(B.p1, i0->InputAt(0));
+    CHECK_EQ(B.p0, i1->InputAt(0));
+
+    // But effects should be ordered start -> i1 -> i0 -> effect_use
+    B.CheckEffectOrdering(i1, i0);
+  }
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Number(), Type::Symbol());
+
+    Node* i0 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 0, true);
+    Node* i1 = B.result->InputAt(1);
+
+    CHECK_EQ(B.p1, i0->InputAt(0));  // Should be commuted.
+    CHECK_EQ(B.p0, i1);
+
+    // Effects should be ordered start -> i1 -> effect_use
+    B.CheckEffectOrdering(i0);
+  }
+
+  for (size_t j = 0; j < arraysize(ops); j += 2) {
+    BinopEffectsTester B(ops[j], Type::Symbol(), Type::Number());
+
+    Node* i0 = B.result->InputAt(0);
+    Node* i1 = B.CheckConvertedInput(IrOpcode::kJSToNumber, 1, true);
+
+    CHECK_EQ(B.p1, i0);  // Should be commuted.
+    CHECK_EQ(B.p0, i1->InputAt(0));
+
+    // Effects should be ordered start -> i0 -> effect_use
+    B.CheckEffectOrdering(i1);
+  }
+}
+
+
+TEST(Int32BinopEffects) {
+  JSBitwiseTypedLoweringTester R;
+
+  for (int j = 0; j < R.kNumberOps; j += 2) {
+    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
+    BinopEffectsTester B(R.ops[j], I32Type(signed_left), I32Type(signed_right));
+    CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    B.CheckNoOp(0);
+    B.CheckNoOp(1);
+
+    B.CheckEffectsRemoved();
+  }
+
+  for (int j = 0; j < R.kNumberOps; j += 2) {
+    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
+    BinopEffectsTester B(R.ops[j], Type::Number(), Type::Number());
+    CHECK_EQ(R.ops[j + 1]->opcode(), B.result->op()->opcode());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
+    B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
+
+    B.CheckEffectsRemoved();
+  }
+
+  for (int j = 0; j < R.kNumberOps; j += 2) {
+    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
+    BinopEffectsTester B(R.ops[j], Type::Number(), Type::Object());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
+    Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
+
+    CHECK_EQ(B.p0, i0->InputAt(0));
+    Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
+
+    CHECK_EQ(B.p1, ii1->InputAt(0));
+
+    B.CheckEffectOrdering(ii1);
+  }
+
+  for (int j = 0; j < R.kNumberOps; j += 2) {
+    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
+    BinopEffectsTester B(R.ops[j], Type::Object(), Type::Number());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
+    Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
+
+    Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
+    CHECK_EQ(B.p1, i1->InputAt(0));
+
+    CHECK_EQ(B.p0, ii0->InputAt(0));
+
+    B.CheckEffectOrdering(ii0);
+  }
+
+  for (int j = 0; j < R.kNumberOps; j += 2) {
+    bool signed_left = R.signedness[j], signed_right = R.signedness[j + 1];
+    BinopEffectsTester B(R.ops[j], Type::Object(), Type::Object());
+
+    B.R.CheckPureBinop(B.result->opcode(), B.result);
+
+    Node* i0 = B.CheckConvertedInput(NumberToI32(signed_left), 0, false);
+    Node* i1 = B.CheckConvertedInput(NumberToI32(signed_right), 1, false);
+
+    Node* ii0 = B.CheckConverted(IrOpcode::kJSToNumber, i0->InputAt(0), true);
+    Node* ii1 = B.CheckConverted(IrOpcode::kJSToNumber, i1->InputAt(0), true);
+
+    CHECK_EQ(B.p0, ii0->InputAt(0));
+    CHECK_EQ(B.p1, ii1->InputAt(0));
+
+    B.CheckEffectOrdering(ii0, ii1);
+  }
+}
+
+
+TEST(UnaryNotEffects) {
+  JSTypedLoweringTester R;
+  const Operator* opnot = R.javascript.UnaryNot();
+
+  for (size_t i = 0; i < arraysize(kJSTypes); i++) {
+    Node* p0 = R.Parameter(kJSTypes[i], 0);
+    Node* orig = R.Unop(opnot, p0);
+    Node* effect_use = R.UseForEffect(orig);
+    Node* value_use = R.graph.NewNode(R.common.Return(), orig);
+    Node* r = R.reduce(orig);
+    // TODO(titzer): test will break if/when js-typed-lowering constant folds.
+    CHECK_EQ(IrOpcode::kBooleanNot, value_use->InputAt(0)->opcode());
+
+    if (r == orig && orig->opcode() == IrOpcode::kJSToBoolean) {
+      // The original node was turned into a ToBoolean, which has an effect.
+      CHECK_EQ(IrOpcode::kJSToBoolean, r->opcode());
+      R.CheckEffectInput(R.start(), orig);
+      R.CheckEffectInput(orig, effect_use);
+    } else {
+      // effect should have been removed from this node.
+      CHECK_EQ(IrOpcode::kBooleanNot, r->opcode());
+      R.CheckEffectInput(R.start(), effect_use);
+    }
+  }
+}
+
+
+TEST(Int32AddNarrowing) {
+  {
+    JSBitwiseTypedLoweringTester R;
+
+    for (int o = 0; o < R.kNumberOps; o += 2) {
+      for (size_t i = 0; i < arraysize(kInt32Types); i++) {
+        Node* n0 = R.Parameter(kInt32Types[i]);
+        for (size_t j = 0; j < arraysize(kInt32Types); j++) {
+          Node* n1 = R.Parameter(kInt32Types[j]);
+          Node* one = R.graph.NewNode(R.common.NumberConstant(1));
+
+          for (int l = 0; l < 2; l++) {
+            Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
+            Node* or_node =
+                R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
+            Node* r = R.reduce(or_node);
+
+            CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
+            CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
+            bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
+
+            Type* add_type = NodeProperties::GetBounds(add_node).upper;
+            CHECK(add_type->Is(I32Type(is_signed)));
+          }
+        }
+      }
+    }
+  }
+  {
+    JSBitwiseShiftTypedLoweringTester R;
+
+    for (int o = 0; o < R.kNumberOps; o += 2) {
+      for (size_t i = 0; i < arraysize(kInt32Types); i++) {
+        Node* n0 = R.Parameter(kInt32Types[i]);
+        for (size_t j = 0; j < arraysize(kInt32Types); j++) {
+          Node* n1 = R.Parameter(kInt32Types[j]);
+          Node* one = R.graph.NewNode(R.common.NumberConstant(1));
+
+          for (int l = 0; l < 2; l++) {
+            Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
+            Node* or_node =
+                R.Binop(R.ops[o], l ? add_node : one, l ? one : add_node);
+            Node* r = R.reduce(or_node);
+
+            CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
+            CHECK_EQ(IrOpcode::kInt32Add, add_node->opcode());
+            bool is_signed = l ? R.signedness[o] : R.signedness[o + 1];
+
+            Type* add_type = NodeProperties::GetBounds(add_node).upper;
+            CHECK(add_type->Is(I32Type(is_signed)));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(Int32AddNarrowingNotOwned) {
+  JSBitwiseTypedLoweringTester R;
+
+  for (int o = 0; o < R.kNumberOps; o += 2) {
+    Node* n0 = R.Parameter(I32Type(R.signedness[o]));
+    Node* n1 = R.Parameter(I32Type(R.signedness[o + 1]));
+    Node* one = R.graph.NewNode(R.common.NumberConstant(1));
+
+    Node* add_node = R.Binop(R.simplified.NumberAdd(), n0, n1);
+    Node* or_node = R.Binop(R.ops[o], add_node, one);
+    Node* other_use = R.Binop(R.simplified.NumberAdd(), add_node, one);
+    Node* r = R.reduce(or_node);
+    CHECK_EQ(R.ops[o + 1]->opcode(), r->op()->opcode());
+    // Should not be reduced to Int32Add because of the other number add.
+    CHECK_EQ(IrOpcode::kNumberAdd, add_node->opcode());
+    // Conversion to int32 should be done.
+    CheckToI32(add_node, r->InputAt(0), R.signedness[o]);
+    CheckToI32(one, r->InputAt(1), R.signedness[o + 1]);
+    // The other use should also not be touched.
+    CHECK_EQ(add_node, other_use->InputAt(0));
+    CHECK_EQ(one, other_use->InputAt(1));
+  }
+}
+
+
+TEST(Int32Comparisons) {
+  JSTypedLoweringTester R;
+
+  struct Entry {
+    const Operator* js_op;
+    const Operator* uint_op;
+    const Operator* int_op;
+    const Operator* num_op;
+    bool commute;
+  };
+
+  Entry ops[] = {
+      {R.javascript.LessThan(), R.machine.Uint32LessThan(),
+       R.machine.Int32LessThan(), R.simplified.NumberLessThan(), false},
+      {R.javascript.LessThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
+       R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
+       false},
+      {R.javascript.GreaterThan(), R.machine.Uint32LessThan(),
+       R.machine.Int32LessThan(), R.simplified.NumberLessThan(), true},
+      {R.javascript.GreaterThanOrEqual(), R.machine.Uint32LessThanOrEqual(),
+       R.machine.Int32LessThanOrEqual(), R.simplified.NumberLessThanOrEqual(),
+       true}};
+
+  for (size_t o = 0; o < arraysize(ops); o++) {
+    for (size_t i = 0; i < arraysize(kNumberTypes); i++) {
+      Type* t0 = kNumberTypes[i];
+      Node* p0 = R.Parameter(t0, 0);
+
+      for (size_t j = 0; j < arraysize(kNumberTypes); j++) {
+        Type* t1 = kNumberTypes[j];
+        Node* p1 = R.Parameter(t1, 1);
+
+        Node* cmp = R.Binop(ops[o].js_op, p0, p1);
+        Node* r = R.reduce(cmp);
+
+        const Operator* expected;
+        if (t0->Is(Type::Unsigned32()) && t1->Is(Type::Unsigned32())) {
+          expected = ops[o].uint_op;
+        } else if (t0->Is(Type::Signed32()) && t1->Is(Type::Signed32())) {
+          expected = ops[o].int_op;
+        } else {
+          expected = ops[o].num_op;
+        }
+        R.CheckPureBinop(expected, r);
+        if (ops[o].commute) {
+          CHECK_EQ(p1, r->InputAt(0));
+          CHECK_EQ(p0, r->InputAt(1));
+        } else {
+          CHECK_EQ(p0, r->InputAt(0));
+          CHECK_EQ(p1, r->InputAt(1));
+        }
+      }
+    }
+  }
+}
diff --git a/test/cctest/compiler/test-linkage.cc b/test/cctest/compiler/test-linkage.cc
new file mode 100644
index 0000000..ff65d6e
--- /dev/null
+++ b/test/cctest/compiler/test-linkage.cc
@@ -0,0 +1,113 @@
+// 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/v8.h"
+
+#include "src/compiler.h"
+#include "src/zone.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/linkage.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/pipeline.h"
+#include "src/compiler/schedule.h"
+#include "test/cctest/cctest.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+                                     0, 0, "dummy");
+
+// So we can get a real JS function.
+static Handle<JSFunction> Compile(const char* source) {
+  Isolate* isolate = CcTest::i_isolate();
+  Handle<String> source_code = isolate->factory()
+                                   ->NewStringFromUtf8(CStrVector(source))
+                                   .ToHandleChecked();
+  Handle<SharedFunctionInfo> shared_function = Compiler::CompileScript(
+      source_code, Handle<String>(), 0, 0, false,
+      Handle<Context>(isolate->native_context()), NULL, NULL,
+      v8::ScriptCompiler::kNoCompileOptions, NOT_NATIVES_CODE);
+  return isolate->factory()->NewFunctionFromSharedFunctionInfo(
+      shared_function, isolate->native_context());
+}
+
+
+TEST(TestLinkageCreate) {
+  InitializedHandleScope handles;
+  Handle<JSFunction> function = Compile("a + b");
+  CompilationInfoWithZone info(function);
+  Linkage linkage(&info);
+}
+
+
+TEST(TestLinkageJSFunctionIncoming) {
+  InitializedHandleScope handles;
+
+  const char* sources[] = {"(function() { })", "(function(a) { })",
+                           "(function(a,b) { })", "(function(a,b,c) { })"};
+
+  for (int i = 0; i < 3; i++) {
+    i::HandleScope handles(CcTest::i_isolate());
+    Handle<JSFunction> function = v8::Utils::OpenHandle(
+        *v8::Handle<v8::Function>::Cast(CompileRun(sources[i])));
+    CompilationInfoWithZone info(function);
+    Linkage linkage(&info);
+
+    CallDescriptor* descriptor = linkage.GetIncomingDescriptor();
+    CHECK_NE(NULL, descriptor);
+
+    CHECK_EQ(1 + i, descriptor->JSParameterCount());
+    CHECK_EQ(1, descriptor->ReturnCount());
+    CHECK_EQ(Operator::kNoProperties, descriptor->properties());
+    CHECK_EQ(true, descriptor->IsJSFunctionCall());
+  }
+}
+
+
+TEST(TestLinkageCodeStubIncoming) {
+  Isolate* isolate = CcTest::InitIsolateOnce();
+  CompilationInfoWithZone info(static_cast<HydrogenCodeStub*>(NULL), isolate);
+  Linkage linkage(&info);
+  // TODO(titzer): test linkage creation with a bonafide code stub.
+  // this just checks current behavior.
+  CHECK_EQ(NULL, linkage.GetIncomingDescriptor());
+}
+
+
+TEST(TestLinkageJSCall) {
+  HandleAndZoneScope handles;
+  Handle<JSFunction> function = Compile("a + c");
+  CompilationInfoWithZone info(function);
+  Linkage linkage(&info);
+
+  for (int i = 0; i < 32; i++) {
+    CallDescriptor* descriptor = linkage.GetJSCallDescriptor(i);
+    CHECK_NE(NULL, descriptor);
+    CHECK_EQ(i, descriptor->JSParameterCount());
+    CHECK_EQ(1, descriptor->ReturnCount());
+    CHECK_EQ(Operator::kNoProperties, descriptor->properties());
+    CHECK_EQ(true, descriptor->IsJSFunctionCall());
+  }
+}
+
+
+TEST(TestLinkageRuntimeCall) {
+  // TODO(titzer): test linkage creation for outgoing runtime calls.
+}
+
+
+TEST(TestLinkageStubCall) {
+  // TODO(titzer): test linkage creation for outgoing stub calls.
+}
+
+
+#endif  // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-machine-operator-reducer.cc b/test/cctest/compiler/test-machine-operator-reducer.cc
new file mode 100644
index 0000000..eca1f3c
--- /dev/null
+++ b/test/cctest/compiler/test-machine-operator-reducer.cc
@@ -0,0 +1,809 @@
+// 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 "test/cctest/cctest.h"
+
+#include "src/base/utils/random-number-generator.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/js-graph.h"
+#include "src/compiler/machine-operator-reducer.h"
+#include "src/compiler/typer.h"
+#include "test/cctest/compiler/value-helper.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+template <typename T>
+const Operator* NewConstantOperator(CommonOperatorBuilder* common,
+                                    volatile T value);
+
+template <>
+const Operator* NewConstantOperator<int32_t>(CommonOperatorBuilder* common,
+                                             volatile int32_t value) {
+  return common->Int32Constant(value);
+}
+
+template <>
+const Operator* NewConstantOperator<double>(CommonOperatorBuilder* common,
+                                            volatile double value) {
+  return common->Float64Constant(value);
+}
+
+
+template <typename T>
+T ValueOfOperator(const Operator* op);
+
+template <>
+int32_t ValueOfOperator<int32_t>(const Operator* op) {
+  CHECK_EQ(IrOpcode::kInt32Constant, op->opcode());
+  return OpParameter<int32_t>(op);
+}
+
+template <>
+double ValueOfOperator<double>(const Operator* op) {
+  CHECK_EQ(IrOpcode::kFloat64Constant, op->opcode());
+  return OpParameter<double>(op);
+}
+
+
+class ReducerTester : public HandleAndZoneScope {
+ public:
+  explicit ReducerTester(int num_parameters = 0)
+      : isolate(main_isolate()),
+        binop(NULL),
+        unop(NULL),
+        common(main_zone()),
+        graph(main_zone()),
+        javascript(main_zone()),
+        typer(main_zone()),
+        jsgraph(&graph, &common, &javascript, &typer, &machine),
+        maxuint32(Constant<int32_t>(kMaxUInt32)) {
+    Node* s = graph.NewNode(common.Start(num_parameters));
+    graph.SetStart(s);
+  }
+
+  Isolate* isolate;
+  const Operator* binop;
+  const Operator* unop;
+  MachineOperatorBuilder machine;
+  CommonOperatorBuilder common;
+  Graph graph;
+  JSOperatorBuilder javascript;
+  Typer typer;
+  JSGraph jsgraph;
+  Node* maxuint32;
+
+  template <typename T>
+  Node* Constant(volatile T value) {
+    return graph.NewNode(NewConstantOperator<T>(&common, value));
+  }
+
+  template <typename T>
+  const T ValueOf(const Operator* op) {
+    return ValueOfOperator<T>(op);
+  }
+
+  // Check that the reduction of this binop applied to constants {a} and {b}
+  // yields the {expect} value.
+  template <typename T>
+  void CheckFoldBinop(volatile T expect, volatile T a, volatile T b) {
+    CheckFoldBinop<T>(expect, Constant<T>(a), Constant<T>(b));
+  }
+
+  // Check that the reduction of this binop applied to {a} and {b} yields
+  // the {expect} value.
+  template <typename T>
+  void CheckFoldBinop(volatile T expect, Node* a, Node* b) {
+    CHECK_NE(NULL, binop);
+    Node* n = graph.NewNode(binop, a, b);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction reduction = reducer.Reduce(n);
+    CHECK(reduction.Changed());
+    CHECK_NE(n, reduction.replacement());
+    CHECK_EQ(expect, ValueOf<T>(reduction.replacement()->op()));
+  }
+
+  // Check that the reduction of this binop applied to {a} and {b} yields
+  // the {expect} node.
+  void CheckBinop(Node* expect, Node* a, Node* b) {
+    CHECK_NE(NULL, binop);
+    Node* n = graph.NewNode(binop, a, b);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction reduction = reducer.Reduce(n);
+    CHECK(reduction.Changed());
+    CHECK_EQ(expect, reduction.replacement());
+  }
+
+  // Check that the reduction of this binop applied to {left} and {right} yields
+  // this binop applied to {left_expect} and {right_expect}.
+  void CheckFoldBinop(Node* left_expect, Node* right_expect, Node* left,
+                      Node* right) {
+    CHECK_NE(NULL, binop);
+    Node* n = graph.NewNode(binop, left, right);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction reduction = reducer.Reduce(n);
+    CHECK(reduction.Changed());
+    CHECK_EQ(binop, reduction.replacement()->op());
+    CHECK_EQ(left_expect, reduction.replacement()->InputAt(0));
+    CHECK_EQ(right_expect, reduction.replacement()->InputAt(1));
+  }
+
+  // Check that the reduction of this binop applied to {left} and {right} yields
+  // the {op_expect} applied to {left_expect} and {right_expect}.
+  template <typename T>
+  void CheckFoldBinop(volatile T left_expect, const Operator* op_expect,
+                      Node* right_expect, Node* left, Node* right) {
+    CHECK_NE(NULL, binop);
+    Node* n = graph.NewNode(binop, left, right);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction r = reducer.Reduce(n);
+    CHECK(r.Changed());
+    CHECK_EQ(op_expect->opcode(), r.replacement()->op()->opcode());
+    CHECK_EQ(left_expect, ValueOf<T>(r.replacement()->InputAt(0)->op()));
+    CHECK_EQ(right_expect, r.replacement()->InputAt(1));
+  }
+
+  // Check that the reduction of this binop applied to {left} and {right} yields
+  // the {op_expect} applied to {left_expect} and {right_expect}.
+  template <typename T>
+  void CheckFoldBinop(Node* left_expect, const Operator* op_expect,
+                      volatile T right_expect, Node* left, Node* right) {
+    CHECK_NE(NULL, binop);
+    Node* n = graph.NewNode(binop, left, right);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction r = reducer.Reduce(n);
+    CHECK(r.Changed());
+    CHECK_EQ(op_expect->opcode(), r.replacement()->op()->opcode());
+    CHECK_EQ(left_expect, r.replacement()->InputAt(0));
+    CHECK_EQ(right_expect, ValueOf<T>(r.replacement()->InputAt(1)->op()));
+  }
+
+  // Check that if the given constant appears on the left, the reducer will
+  // swap it to be on the right.
+  template <typename T>
+  void CheckPutConstantOnRight(volatile T constant) {
+    // TODO(titzer): CHECK(binop->HasProperty(Operator::kCommutative));
+    Node* p = Parameter();
+    Node* k = Constant<T>(constant);
+    {
+      Node* n = graph.NewNode(binop, k, p);
+      MachineOperatorReducer reducer(&jsgraph);
+      Reduction reduction = reducer.Reduce(n);
+      CHECK(!reduction.Changed() || reduction.replacement() == n);
+      CHECK_EQ(p, n->InputAt(0));
+      CHECK_EQ(k, n->InputAt(1));
+    }
+    {
+      Node* n = graph.NewNode(binop, p, k);
+      MachineOperatorReducer reducer(&jsgraph);
+      Reduction reduction = reducer.Reduce(n);
+      CHECK(!reduction.Changed());
+      CHECK_EQ(p, n->InputAt(0));
+      CHECK_EQ(k, n->InputAt(1));
+    }
+  }
+
+  // Check that if the given constant appears on the left, the reducer will
+  // *NOT* swap it to be on the right.
+  template <typename T>
+  void CheckDontPutConstantOnRight(volatile T constant) {
+    CHECK(!binop->HasProperty(Operator::kCommutative));
+    Node* p = Parameter();
+    Node* k = Constant<T>(constant);
+    Node* n = graph.NewNode(binop, k, p);
+    MachineOperatorReducer reducer(&jsgraph);
+    Reduction reduction = reducer.Reduce(n);
+    CHECK(!reduction.Changed());
+    CHECK_EQ(k, n->InputAt(0));
+    CHECK_EQ(p, n->InputAt(1));
+  }
+
+  Node* Parameter(int32_t index = 0) {
+    return graph.NewNode(common.Parameter(index), graph.start());
+  }
+};
+
+
+TEST(ReduceWord32And) {
+  ReducerTester R;
+  R.binop = R.machine.Word32And();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x & y, x, y);
+    }
+  }
+
+  R.CheckPutConstantOnRight(33);
+  R.CheckPutConstantOnRight(44000);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+  Node* minus_1 = R.Constant<int32_t>(-1);
+
+  R.CheckBinop(zero, x, zero);  // x  & 0  => 0
+  R.CheckBinop(zero, zero, x);  // 0  & x  => 0
+  R.CheckBinop(x, x, minus_1);  // x  & -1 => 0
+  R.CheckBinop(x, minus_1, x);  // -1 & x  => 0
+  R.CheckBinop(x, x, x);        // x  & x  => x
+}
+
+
+TEST(ReduceWord32Or) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Or();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x | y, x, y);
+    }
+  }
+
+  R.CheckPutConstantOnRight(36);
+  R.CheckPutConstantOnRight(44001);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+  Node* minus_1 = R.Constant<int32_t>(-1);
+
+  R.CheckBinop(x, x, zero);           // x  & 0  => x
+  R.CheckBinop(x, zero, x);           // 0  & x  => x
+  R.CheckBinop(minus_1, x, minus_1);  // x  & -1 => -1
+  R.CheckBinop(minus_1, minus_1, x);  // -1 & x  => -1
+  R.CheckBinop(x, x, x);              // x  & x  => x
+}
+
+
+TEST(ReduceWord32Xor) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Xor();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x ^ y, x, y);
+    }
+  }
+
+  R.CheckPutConstantOnRight(39);
+  R.CheckPutConstantOnRight(4403);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);            // x ^ 0  => x
+  R.CheckBinop(x, zero, x);            // 0 ^ x  => x
+  R.CheckFoldBinop<int32_t>(0, x, x);  // x ^ x  => 0
+}
+
+
+TEST(ReduceWord32Shl) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Shl();
+
+  // TODO(titzer): out of range shifts
+  FOR_INT32_INPUTS(i) {
+    for (int y = 0; y < 32; y++) {
+      int32_t x = *i;
+      R.CheckFoldBinop<int32_t>(x << y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(44);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);  // x << 0  => x
+}
+
+
+TEST(ReduceWord32Shr) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Shr();
+
+  // TODO(titzer): test out of range shifts
+  FOR_UINT32_INPUTS(i) {
+    for (uint32_t y = 0; y < 32; y++) {
+      uint32_t x = *i;
+      R.CheckFoldBinop<int32_t>(x >> y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(44);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);  // x >>> 0  => x
+}
+
+
+TEST(ReduceWord32Sar) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Sar();
+
+  // TODO(titzer): test out of range shifts
+  FOR_INT32_INPUTS(i) {
+    for (int32_t y = 0; y < 32; y++) {
+      int32_t x = *i;
+      R.CheckFoldBinop<int32_t>(x >> y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(44);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);  // x >> 0  => x
+}
+
+
+TEST(ReduceWord32Equal) {
+  ReducerTester R;
+  R.binop = R.machine.Word32Equal();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x == y ? 1 : 0, x, y);
+    }
+  }
+
+  R.CheckPutConstantOnRight(48);
+  R.CheckPutConstantOnRight(-48);
+
+  Node* x = R.Parameter(0);
+  Node* y = R.Parameter(1);
+  Node* zero = R.Constant<int32_t>(0);
+  Node* sub = R.graph.NewNode(R.machine.Int32Sub(), x, y);
+
+  R.CheckFoldBinop<int32_t>(1, x, x);  // x == x  => 1
+  R.CheckFoldBinop(x, y, sub, zero);   // x - y == 0  => x == y
+  R.CheckFoldBinop(x, y, zero, sub);   // 0 == x - y  => x == y
+}
+
+
+TEST(ReduceInt32Add) {
+  ReducerTester R;
+  R.binop = R.machine.Int32Add();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x + y, x, y);  // TODO(titzer): signed overflow
+    }
+  }
+
+  R.CheckPutConstantOnRight(41);
+  R.CheckPutConstantOnRight(4407);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);  // x + 0  => x
+  R.CheckBinop(x, zero, x);  // 0 + x  => x
+}
+
+
+TEST(ReduceInt32Sub) {
+  ReducerTester R;
+  R.binop = R.machine.Int32Sub();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x - y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(412);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckBinop(x, x, zero);  // x - 0  => x
+}
+
+
+TEST(ReduceInt32Mul) {
+  ReducerTester R;
+  R.binop = R.machine.Int32Mul();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x * y, x, y);  // TODO(titzer): signed overflow
+    }
+  }
+
+  R.CheckPutConstantOnRight(4111);
+  R.CheckPutConstantOnRight(-4407);
+
+  Node* x = R.Parameter();
+  Node* zero = R.Constant<int32_t>(0);
+  Node* one = R.Constant<int32_t>(1);
+  Node* minus_one = R.Constant<int32_t>(-1);
+
+  R.CheckBinop(zero, x, zero);  // x * 0  => 0
+  R.CheckBinop(zero, zero, x);  // 0 * x  => 0
+  R.CheckBinop(x, x, one);      // x * 1  => x
+  R.CheckBinop(x, one, x);      // 1 * x  => x
+  R.CheckFoldBinop<int32_t>(0, R.machine.Int32Sub(), x, minus_one,
+                            x);  // -1 * x  => 0 - x
+  R.CheckFoldBinop<int32_t>(0, R.machine.Int32Sub(), x, x,
+                            minus_one);  // x * -1  => 0 - x
+
+  for (int32_t n = 1; n < 31; ++n) {
+    Node* multiplier = R.Constant<int32_t>(1 << n);
+    R.CheckFoldBinop<int32_t>(x, R.machine.Word32Shl(), n, x,
+                              multiplier);  // x * 2^n => x << n
+    R.CheckFoldBinop<int32_t>(x, R.machine.Word32Shl(), n, multiplier,
+                              x);  // 2^n * x => x << n
+  }
+}
+
+
+TEST(ReduceInt32Div) {
+  ReducerTester R;
+  R.binop = R.machine.Int32Div();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      if (y == 0) continue;              // TODO(titzer): test / 0
+      int32_t r = y == -1 ? -x : x / y;  // INT_MIN / -1 may explode in C
+      R.CheckFoldBinop<int32_t>(r, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(41111);
+  R.CheckDontPutConstantOnRight(-44071);
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<int32_t>(1);
+  Node* minus_one = R.Constant<int32_t>(-1);
+
+  R.CheckBinop(x, x, one);  // x / 1  => x
+  // TODO(titzer):                          // 0 / x  => 0 if x != 0
+  // TODO(titzer):                          // x / 2^n => x >> n and round
+  R.CheckFoldBinop<int32_t>(0, R.machine.Int32Sub(), x, x,
+                            minus_one);  // x / -1  => 0 - x
+}
+
+
+TEST(ReduceInt32UDiv) {
+  ReducerTester R;
+  R.binop = R.machine.Int32UDiv();
+
+  FOR_UINT32_INPUTS(pl) {
+    FOR_UINT32_INPUTS(pr) {
+      uint32_t x = *pl, y = *pr;
+      if (y == 0) continue;  // TODO(titzer): test / 0
+      R.CheckFoldBinop<int32_t>(x / y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(41311);
+  R.CheckDontPutConstantOnRight(-44371);
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<int32_t>(1);
+
+  R.CheckBinop(x, x, one);  // x / 1  => x
+  // TODO(titzer):                            // 0 / x  => 0 if x != 0
+
+  for (uint32_t n = 1; n < 32; ++n) {
+    Node* divisor = R.Constant<int32_t>(1u << n);
+    R.CheckFoldBinop<int32_t>(x, R.machine.Word32Shr(), n, x,
+                              divisor);  // x / 2^n => x >> n
+  }
+}
+
+
+TEST(ReduceInt32Mod) {
+  ReducerTester R;
+  R.binop = R.machine.Int32Mod();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      if (y == 0) continue;             // TODO(titzer): test % 0
+      int32_t r = y == -1 ? 0 : x % y;  // INT_MIN % -1 may explode in C
+      R.CheckFoldBinop<int32_t>(r, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(413);
+  R.CheckDontPutConstantOnRight(-4401);
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<int32_t>(1);
+
+  R.CheckFoldBinop<int32_t>(0, x, one);  // x % 1  => 0
+  // TODO(titzer):                       // x % 2^n => x & 2^n-1 and round
+}
+
+
+TEST(ReduceInt32UMod) {
+  ReducerTester R;
+  R.binop = R.machine.Int32UMod();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      uint32_t x = *pl, y = *pr;
+      if (y == 0) continue;  // TODO(titzer): test x % 0
+      R.CheckFoldBinop<int32_t>(x % y, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(417);
+  R.CheckDontPutConstantOnRight(-4371);
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<int32_t>(1);
+
+  R.CheckFoldBinop<int32_t>(0, x, one);  // x % 1  => 0
+
+  for (uint32_t n = 1; n < 32; ++n) {
+    Node* divisor = R.Constant<int32_t>(1u << n);
+    R.CheckFoldBinop<int32_t>(x, R.machine.Word32And(), (1u << n) - 1, x,
+                              divisor);  // x % 2^n => x & 2^n-1
+  }
+}
+
+
+TEST(ReduceInt32LessThan) {
+  ReducerTester R;
+  R.binop = R.machine.Int32LessThan();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x < y ? 1 : 0, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(41399);
+  R.CheckDontPutConstantOnRight(-440197);
+
+  Node* x = R.Parameter(0);
+  Node* y = R.Parameter(1);
+  Node* zero = R.Constant<int32_t>(0);
+  Node* sub = R.graph.NewNode(R.machine.Int32Sub(), x, y);
+
+  R.CheckFoldBinop<int32_t>(0, x, x);  // x < x  => 0
+  R.CheckFoldBinop(x, y, sub, zero);   // x - y < 0 => x < y
+  R.CheckFoldBinop(y, x, zero, sub);   // 0 < x - y => y < x
+}
+
+
+TEST(ReduceInt32LessThanOrEqual) {
+  ReducerTester R;
+  R.binop = R.machine.Int32LessThanOrEqual();
+
+  FOR_INT32_INPUTS(pl) {
+    FOR_INT32_INPUTS(pr) {
+      int32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x <= y ? 1 : 0, x, y);
+    }
+  }
+
+  FOR_INT32_INPUTS(i) { R.CheckDontPutConstantOnRight<int32_t>(*i); }
+
+  Node* x = R.Parameter(0);
+  Node* y = R.Parameter(1);
+  Node* zero = R.Constant<int32_t>(0);
+  Node* sub = R.graph.NewNode(R.machine.Int32Sub(), x, y);
+
+  R.CheckFoldBinop<int32_t>(1, x, x);  // x <= x => 1
+  R.CheckFoldBinop(x, y, sub, zero);   // x - y <= 0 => x <= y
+  R.CheckFoldBinop(y, x, zero, sub);   // 0 <= x - y => y <= x
+}
+
+
+TEST(ReduceUint32LessThan) {
+  ReducerTester R;
+  R.binop = R.machine.Uint32LessThan();
+
+  FOR_UINT32_INPUTS(pl) {
+    FOR_UINT32_INPUTS(pr) {
+      uint32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x < y ? 1 : 0, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(41399);
+  R.CheckDontPutConstantOnRight(-440197);
+
+  Node* x = R.Parameter();
+  Node* max = R.maxuint32;
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckFoldBinop<int32_t>(0, max, x);   // M < x  => 0
+  R.CheckFoldBinop<int32_t>(0, x, zero);  // x < 0  => 0
+  R.CheckFoldBinop<int32_t>(0, x, x);     // x < x  => 0
+}
+
+
+TEST(ReduceUint32LessThanOrEqual) {
+  ReducerTester R;
+  R.binop = R.machine.Uint32LessThanOrEqual();
+
+  FOR_UINT32_INPUTS(pl) {
+    FOR_UINT32_INPUTS(pr) {
+      uint32_t x = *pl, y = *pr;
+      R.CheckFoldBinop<int32_t>(x <= y ? 1 : 0, x, y);
+    }
+  }
+
+  R.CheckDontPutConstantOnRight(41399);
+  R.CheckDontPutConstantOnRight(-440197);
+
+  Node* x = R.Parameter();
+  Node* max = R.maxuint32;
+  Node* zero = R.Constant<int32_t>(0);
+
+  R.CheckFoldBinop<int32_t>(1, x, max);   // x <= M  => 1
+  R.CheckFoldBinop<int32_t>(1, zero, x);  // 0 <= x  => 1
+  R.CheckFoldBinop<int32_t>(1, x, x);     // x <= x  => 1
+}
+
+
+TEST(ReduceLoadStore) {
+  ReducerTester R;
+
+  Node* base = R.Constant<int32_t>(11);
+  Node* index = R.Constant<int32_t>(4);
+  Node* load = R.graph.NewNode(R.machine.Load(kMachInt32), base, index);
+
+  {
+    MachineOperatorReducer reducer(&R.jsgraph);
+    Reduction reduction = reducer.Reduce(load);
+    CHECK(!reduction.Changed());  // loads should not be reduced.
+  }
+
+  {
+    Node* store = R.graph.NewNode(
+        R.machine.Store(StoreRepresentation(kMachInt32, kNoWriteBarrier)), base,
+        index, load);
+    MachineOperatorReducer reducer(&R.jsgraph);
+    Reduction reduction = reducer.Reduce(store);
+    CHECK(!reduction.Changed());  // stores should not be reduced.
+  }
+}
+
+
+static void CheckNans(ReducerTester* R) {
+  Node* x = R->Parameter();
+  std::vector<double> nans = ValueHelper::nan_vector();
+  for (std::vector<double>::const_iterator pl = nans.begin(); pl != nans.end();
+       ++pl) {
+    for (std::vector<double>::const_iterator pr = nans.begin();
+         pr != nans.end(); ++pr) {
+      Node* nan1 = R->Constant<double>(*pl);
+      Node* nan2 = R->Constant<double>(*pr);
+      R->CheckBinop(nan1, x, nan1);     // x % NaN => NaN
+      R->CheckBinop(nan1, nan1, x);     // NaN % x => NaN
+      R->CheckBinop(nan1, nan2, nan1);  // NaN % NaN => NaN
+    }
+  }
+}
+
+
+TEST(ReduceFloat64Add) {
+  ReducerTester R;
+  R.binop = R.machine.Float64Add();
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double x = *pl, y = *pr;
+      R.CheckFoldBinop<double>(x + y, x, y);
+    }
+  }
+
+  FOR_FLOAT64_INPUTS(i) { R.CheckPutConstantOnRight(*i); }
+  // TODO(titzer): CheckNans(&R);
+}
+
+
+TEST(ReduceFloat64Sub) {
+  ReducerTester R;
+  R.binop = R.machine.Float64Sub();
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double x = *pl, y = *pr;
+      R.CheckFoldBinop<double>(x - y, x, y);
+    }
+  }
+  // TODO(titzer): CheckNans(&R);
+}
+
+
+TEST(ReduceFloat64Mul) {
+  ReducerTester R;
+  R.binop = R.machine.Float64Mul();
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double x = *pl, y = *pr;
+      R.CheckFoldBinop<double>(x * y, x, y);
+    }
+  }
+
+  double inf = V8_INFINITY;
+  R.CheckPutConstantOnRight(-inf);
+  R.CheckPutConstantOnRight(-0.1);
+  R.CheckPutConstantOnRight(0.1);
+  R.CheckPutConstantOnRight(inf);
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<double>(1.0);
+
+  R.CheckBinop(x, x, one);  // x * 1.0 => x
+  R.CheckBinop(x, one, x);  // 1.0 * x => x
+
+  CheckNans(&R);
+}
+
+
+TEST(ReduceFloat64Div) {
+  ReducerTester R;
+  R.binop = R.machine.Float64Div();
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double x = *pl, y = *pr;
+      R.CheckFoldBinop<double>(x / y, x, y);
+    }
+  }
+
+  Node* x = R.Parameter();
+  Node* one = R.Constant<double>(1.0);
+
+  R.CheckBinop(x, x, one);  // x / 1.0 => x
+
+  CheckNans(&R);
+}
+
+
+TEST(ReduceFloat64Mod) {
+  ReducerTester R;
+  R.binop = R.machine.Float64Mod();
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double x = *pl, y = *pr;
+      R.CheckFoldBinop<double>(modulo(x, y), x, y);
+    }
+  }
+
+  CheckNans(&R);
+}
+
+
+// TODO(titzer): test MachineOperatorReducer for Word64And
+// TODO(titzer): test MachineOperatorReducer for Word64Or
+// TODO(titzer): test MachineOperatorReducer for Word64Xor
+// TODO(titzer): test MachineOperatorReducer for Word64Shl
+// TODO(titzer): test MachineOperatorReducer for Word64Shr
+// TODO(titzer): test MachineOperatorReducer for Word64Sar
+// TODO(titzer): test MachineOperatorReducer for Word64Equal
+// TODO(titzer): test MachineOperatorReducer for Word64Not
+// TODO(titzer): test MachineOperatorReducer for Int64Add
+// TODO(titzer): test MachineOperatorReducer for Int64Sub
+// TODO(titzer): test MachineOperatorReducer for Int64Mul
+// TODO(titzer): test MachineOperatorReducer for Int64UMul
+// TODO(titzer): test MachineOperatorReducer for Int64Div
+// TODO(titzer): test MachineOperatorReducer for Int64UDiv
+// TODO(titzer): test MachineOperatorReducer for Int64Mod
+// TODO(titzer): test MachineOperatorReducer for Int64UMod
+// TODO(titzer): test MachineOperatorReducer for Int64Neg
+// TODO(titzer): test MachineOperatorReducer for ChangeInt32ToFloat64
+// TODO(titzer): test MachineOperatorReducer for ChangeFloat64ToInt32
+// TODO(titzer): test MachineOperatorReducer for Float64Compare
diff --git a/test/cctest/compiler/test-node-algorithm.cc b/test/cctest/compiler/test-node-algorithm.cc
new file mode 100644
index 0000000..10f98a6
--- /dev/null
+++ b/test/cctest/compiler/test-node-algorithm.cc
@@ -0,0 +1,330 @@
+// 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.
+
+#include <vector>
+
+#include "src/v8.h"
+
+#include "graph-tester.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/generic-node.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/operator.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+                                     0, 0, "dummy");
+
+class PreNodeVisitor : public NullNodeVisitor {
+ public:
+  GenericGraphVisit::Control Pre(Node* node) {
+    printf("NODE ID: %d\n", node->id());
+    nodes_.push_back(node);
+    return GenericGraphVisit::CONTINUE;
+  }
+  std::vector<Node*> nodes_;
+};
+
+
+class PostNodeVisitor : public NullNodeVisitor {
+ public:
+  GenericGraphVisit::Control Post(Node* node) {
+    printf("NODE ID: %d\n", node->id());
+    nodes_.push_back(node);
+    return GenericGraphVisit::CONTINUE;
+  }
+  std::vector<Node*> nodes_;
+};
+
+
+TEST(TestUseNodeVisitEmpty) {
+  GraphWithStartNodeTester graph;
+
+  PreNodeVisitor node_visitor;
+  graph.VisitNodeUsesFromStart(&node_visitor);
+
+  CHECK_EQ(1, static_cast<int>(node_visitor.nodes_.size()));
+}
+
+
+TEST(TestUseNodePreOrderVisitSimple) {
+  GraphWithStartNodeTester graph;
+  Node* n2 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n3 = graph.NewNode(&dummy_operator, n2);
+  Node* n4 = graph.NewNode(&dummy_operator, n2, n3);
+  Node* n5 = graph.NewNode(&dummy_operator, n4, n2);
+  graph.SetEnd(n5);
+
+  PreNodeVisitor node_visitor;
+  graph.VisitNodeUsesFromStart(&node_visitor);
+
+  CHECK_EQ(5, static_cast<int>(node_visitor.nodes_.size()));
+  CHECK(graph.start()->id() == node_visitor.nodes_[0]->id());
+  CHECK(n2->id() == node_visitor.nodes_[1]->id());
+  CHECK(n3->id() == node_visitor.nodes_[2]->id());
+  CHECK(n4->id() == node_visitor.nodes_[3]->id());
+  CHECK(n5->id() == node_visitor.nodes_[4]->id());
+}
+
+
+TEST(TestInputNodePreOrderVisitSimple) {
+  GraphWithStartNodeTester graph;
+  Node* n2 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n3 = graph.NewNode(&dummy_operator, n2);
+  Node* n4 = graph.NewNode(&dummy_operator, n2, n3);
+  Node* n5 = graph.NewNode(&dummy_operator, n4, n2);
+  graph.SetEnd(n5);
+
+  PreNodeVisitor node_visitor;
+  graph.VisitNodeInputsFromEnd(&node_visitor);
+  CHECK_EQ(5, static_cast<int>(node_visitor.nodes_.size()));
+  CHECK(n5->id() == node_visitor.nodes_[0]->id());
+  CHECK(n4->id() == node_visitor.nodes_[1]->id());
+  CHECK(n2->id() == node_visitor.nodes_[2]->id());
+  CHECK(graph.start()->id() == node_visitor.nodes_[3]->id());
+  CHECK(n3->id() == node_visitor.nodes_[4]->id());
+}
+
+
+TEST(TestUseNodePostOrderVisitSimple) {
+  GraphWithStartNodeTester graph;
+  Node* n2 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n3 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n4 = graph.NewNode(&dummy_operator, n2);
+  Node* n5 = graph.NewNode(&dummy_operator, n2);
+  Node* n6 = graph.NewNode(&dummy_operator, n2);
+  Node* n7 = graph.NewNode(&dummy_operator, n3);
+  Node* end_dependencies[4] = {n4, n5, n6, n7};
+  Node* n8 = graph.NewNode(&dummy_operator, 4, end_dependencies);
+  graph.SetEnd(n8);
+
+  PostNodeVisitor node_visitor;
+  graph.VisitNodeUsesFromStart(&node_visitor);
+
+  CHECK_EQ(8, static_cast<int>(node_visitor.nodes_.size()));
+  CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
+  CHECK(n4->id() == node_visitor.nodes_[1]->id());
+  CHECK(n5->id() == node_visitor.nodes_[2]->id());
+  CHECK(n6->id() == node_visitor.nodes_[3]->id());
+  CHECK(n2->id() == node_visitor.nodes_[4]->id());
+  CHECK(n7->id() == node_visitor.nodes_[5]->id());
+  CHECK(n3->id() == node_visitor.nodes_[6]->id());
+  CHECK(graph.start()->id() == node_visitor.nodes_[7]->id());
+}
+
+
+TEST(TestUseNodePostOrderVisitLong) {
+  GraphWithStartNodeTester graph;
+  Node* n2 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n3 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n4 = graph.NewNode(&dummy_operator, n2);
+  Node* n5 = graph.NewNode(&dummy_operator, n2);
+  Node* n6 = graph.NewNode(&dummy_operator, n3);
+  Node* n7 = graph.NewNode(&dummy_operator, n3);
+  Node* n8 = graph.NewNode(&dummy_operator, n5);
+  Node* n9 = graph.NewNode(&dummy_operator, n5);
+  Node* n10 = graph.NewNode(&dummy_operator, n9);
+  Node* n11 = graph.NewNode(&dummy_operator, n9);
+  Node* end_dependencies[6] = {n4, n8, n10, n11, n6, n7};
+  Node* n12 = graph.NewNode(&dummy_operator, 6, end_dependencies);
+  graph.SetEnd(n12);
+
+  PostNodeVisitor node_visitor;
+  graph.VisitNodeUsesFromStart(&node_visitor);
+
+  CHECK_EQ(12, static_cast<int>(node_visitor.nodes_.size()));
+  CHECK(graph.end()->id() == node_visitor.nodes_[0]->id());
+  CHECK(n4->id() == node_visitor.nodes_[1]->id());
+  CHECK(n8->id() == node_visitor.nodes_[2]->id());
+  CHECK(n10->id() == node_visitor.nodes_[3]->id());
+  CHECK(n11->id() == node_visitor.nodes_[4]->id());
+  CHECK(n9->id() == node_visitor.nodes_[5]->id());
+  CHECK(n5->id() == node_visitor.nodes_[6]->id());
+  CHECK(n2->id() == node_visitor.nodes_[7]->id());
+  CHECK(n6->id() == node_visitor.nodes_[8]->id());
+  CHECK(n7->id() == node_visitor.nodes_[9]->id());
+  CHECK(n3->id() == node_visitor.nodes_[10]->id());
+  CHECK(graph.start()->id() == node_visitor.nodes_[11]->id());
+}
+
+
+TEST(TestUseNodePreOrderVisitCycle) {
+  GraphWithStartNodeTester graph;
+  Node* n0 = graph.start_node();
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n1);
+  n0->AppendInput(graph.main_zone(), n2);
+  graph.SetStart(n0);
+  graph.SetEnd(n2);
+
+  PreNodeVisitor node_visitor;
+  graph.VisitNodeUsesFromStart(&node_visitor);
+
+  CHECK_EQ(3, static_cast<int>(node_visitor.nodes_.size()));
+  CHECK(n0->id() == node_visitor.nodes_[0]->id());
+  CHECK(n1->id() == node_visitor.nodes_[1]->id());
+  CHECK(n2->id() == node_visitor.nodes_[2]->id());
+}
+
+
+struct ReenterNodeVisitor : NullNodeVisitor {
+  GenericGraphVisit::Control Pre(Node* node) {
+    printf("[%d] PRE NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
+    nodes_.push_back(node->id());
+    int size = static_cast<int>(nodes_.size());
+    switch (node->id()) {
+      case 0:
+        return size < 6 ? GenericGraphVisit::REENTER : GenericGraphVisit::SKIP;
+      case 1:
+        return size < 4 ? GenericGraphVisit::DEFER
+                        : GenericGraphVisit::CONTINUE;
+      default:
+        return GenericGraphVisit::REENTER;
+    }
+  }
+
+  GenericGraphVisit::Control Post(Node* node) {
+    printf("[%d] POST NODE: %d\n", static_cast<int>(nodes_.size()), node->id());
+    nodes_.push_back(-node->id());
+    return node->id() == 4 ? GenericGraphVisit::REENTER
+                           : GenericGraphVisit::CONTINUE;
+  }
+
+  void PreEdge(Node* from, int index, Node* to) {
+    printf("[%d] PRE EDGE: %d-%d\n", static_cast<int>(edges_.size()),
+           from->id(), to->id());
+    edges_.push_back(std::make_pair(from->id(), to->id()));
+  }
+
+  void PostEdge(Node* from, int index, Node* to) {
+    printf("[%d] POST EDGE: %d-%d\n", static_cast<int>(edges_.size()),
+           from->id(), to->id());
+    edges_.push_back(std::make_pair(-from->id(), -to->id()));
+  }
+
+  std::vector<int> nodes_;
+  std::vector<std::pair<int, int> > edges_;
+};
+
+
+TEST(TestUseNodeReenterVisit) {
+  GraphWithStartNodeTester graph;
+  Node* n0 = graph.start_node();
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator, n2);
+  Node* n4 = graph.NewNode(&dummy_operator, n0);
+  Node* n5 = graph.NewNode(&dummy_operator, n4);
+  n0->AppendInput(graph.main_zone(), n3);
+  graph.SetStart(n0);
+  graph.SetEnd(n5);
+
+  ReenterNodeVisitor visitor;
+  graph.VisitNodeUsesFromStart(&visitor);
+
+  CHECK_EQ(22, static_cast<int>(visitor.nodes_.size()));
+  CHECK_EQ(24, static_cast<int>(visitor.edges_.size()));
+
+  CHECK(n0->id() == visitor.nodes_[0]);
+  CHECK(n0->id() == visitor.edges_[0].first);
+  CHECK(n1->id() == visitor.edges_[0].second);
+  CHECK(n1->id() == visitor.nodes_[1]);
+  // N1 is deferred.
+  CHECK(-n1->id() == visitor.edges_[1].second);
+  CHECK(-n0->id() == visitor.edges_[1].first);
+  CHECK(n0->id() == visitor.edges_[2].first);
+  CHECK(n2->id() == visitor.edges_[2].second);
+  CHECK(n2->id() == visitor.nodes_[2]);
+  CHECK(n2->id() == visitor.edges_[3].first);
+  CHECK(n3->id() == visitor.edges_[3].second);
+  CHECK(n3->id() == visitor.nodes_[3]);
+  // Circle back to N0, which we may reenter for now.
+  CHECK(n3->id() == visitor.edges_[4].first);
+  CHECK(n0->id() == visitor.edges_[4].second);
+  CHECK(n0->id() == visitor.nodes_[4]);
+  CHECK(n0->id() == visitor.edges_[5].first);
+  CHECK(n1->id() == visitor.edges_[5].second);
+  CHECK(n1->id() == visitor.nodes_[5]);
+  // This time N1 is no longer deferred.
+  CHECK(-n1->id() == visitor.nodes_[6]);
+  CHECK(-n1->id() == visitor.edges_[6].second);
+  CHECK(-n0->id() == visitor.edges_[6].first);
+  CHECK(n0->id() == visitor.edges_[7].first);
+  CHECK(n2->id() == visitor.edges_[7].second);
+  CHECK(n2->id() == visitor.nodes_[7]);
+  CHECK(n2->id() == visitor.edges_[8].first);
+  CHECK(n3->id() == visitor.edges_[8].second);
+  CHECK(n3->id() == visitor.nodes_[8]);
+  CHECK(n3->id() == visitor.edges_[9].first);
+  CHECK(n0->id() == visitor.edges_[9].second);
+  CHECK(n0->id() == visitor.nodes_[9]);
+  // This time we break at N0 and skip it.
+  CHECK(-n0->id() == visitor.edges_[10].second);
+  CHECK(-n3->id() == visitor.edges_[10].first);
+  CHECK(-n3->id() == visitor.nodes_[10]);
+  CHECK(-n3->id() == visitor.edges_[11].second);
+  CHECK(-n2->id() == visitor.edges_[11].first);
+  CHECK(-n2->id() == visitor.nodes_[11]);
+  CHECK(-n2->id() == visitor.edges_[12].second);
+  CHECK(-n0->id() == visitor.edges_[12].first);
+  CHECK(n0->id() == visitor.edges_[13].first);
+  CHECK(n4->id() == visitor.edges_[13].second);
+  CHECK(n4->id() == visitor.nodes_[12]);
+  CHECK(n4->id() == visitor.edges_[14].first);
+  CHECK(n5->id() == visitor.edges_[14].second);
+  CHECK(n5->id() == visitor.nodes_[13]);
+  CHECK(-n5->id() == visitor.nodes_[14]);
+  CHECK(-n5->id() == visitor.edges_[15].second);
+  CHECK(-n4->id() == visitor.edges_[15].first);
+  CHECK(-n4->id() == visitor.nodes_[15]);
+  CHECK(-n4->id() == visitor.edges_[16].second);
+  CHECK(-n0->id() == visitor.edges_[16].first);
+  CHECK(-n0->id() == visitor.nodes_[16]);
+  CHECK(-n0->id() == visitor.edges_[17].second);
+  CHECK(-n3->id() == visitor.edges_[17].first);
+  CHECK(-n3->id() == visitor.nodes_[17]);
+  CHECK(-n3->id() == visitor.edges_[18].second);
+  CHECK(-n2->id() == visitor.edges_[18].first);
+  CHECK(-n2->id() == visitor.nodes_[18]);
+  CHECK(-n2->id() == visitor.edges_[19].second);
+  CHECK(-n0->id() == visitor.edges_[19].first);
+  // N4 may be reentered.
+  CHECK(n0->id() == visitor.edges_[20].first);
+  CHECK(n4->id() == visitor.edges_[20].second);
+  CHECK(n4->id() == visitor.nodes_[19]);
+  CHECK(n4->id() == visitor.edges_[21].first);
+  CHECK(n5->id() == visitor.edges_[21].second);
+  CHECK(-n5->id() == visitor.edges_[22].second);
+  CHECK(-n4->id() == visitor.edges_[22].first);
+  CHECK(-n4->id() == visitor.nodes_[20]);
+  CHECK(-n4->id() == visitor.edges_[23].second);
+  CHECK(-n0->id() == visitor.edges_[23].first);
+  CHECK(-n0->id() == visitor.nodes_[21]);
+}
+
+
+TEST(TestPrintNodeGraphToNodeGraphviz) {
+  GraphWithStartNodeTester graph;
+  Node* n2 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n3 = graph.NewNode(&dummy_operator, graph.start());
+  Node* n4 = graph.NewNode(&dummy_operator, n2);
+  Node* n5 = graph.NewNode(&dummy_operator, n2);
+  Node* n6 = graph.NewNode(&dummy_operator, n3);
+  Node* n7 = graph.NewNode(&dummy_operator, n3);
+  Node* n8 = graph.NewNode(&dummy_operator, n5);
+  Node* n9 = graph.NewNode(&dummy_operator, n5);
+  Node* n10 = graph.NewNode(&dummy_operator, n9);
+  Node* n11 = graph.NewNode(&dummy_operator, n9);
+  Node* end_dependencies[6] = {n4, n8, n10, n11, n6, n7};
+  Node* n12 = graph.NewNode(&dummy_operator, 6, end_dependencies);
+  graph.SetEnd(n12);
+
+  OFStream os(stdout);
+  os << AsDOT(graph);
+}
diff --git a/test/cctest/compiler/test-node-cache.cc b/test/cctest/compiler/test-node-cache.cc
new file mode 100644
index 0000000..3569386
--- /dev/null
+++ b/test/cctest/compiler/test-node-cache.cc
@@ -0,0 +1,160 @@
+// 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/v8.h"
+
+#include "graph-tester.h"
+#include "src/compiler/common-operator.h"
+#include "src/compiler/node-cache.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(Int32Constant_back_to_back) {
+  GraphTester graph;
+  Int32NodeCache cache;
+
+  for (int i = -2000000000; i < 2000000000; i += 3315177) {
+    Node** pos = cache.Find(graph.zone(), i);
+    CHECK_NE(NULL, pos);
+    for (int j = 0; j < 3; j++) {
+      Node** npos = cache.Find(graph.zone(), i);
+      CHECK_EQ(pos, npos);
+    }
+  }
+}
+
+
+TEST(Int32Constant_five) {
+  GraphTester graph;
+  Int32NodeCache cache;
+  CommonOperatorBuilder common(graph.zone());
+
+  int32_t constants[] = {static_cast<int32_t>(0x80000000), -77, 0, 1, -1};
+
+  Node* nodes[arraysize(constants)];
+
+  for (size_t i = 0; i < arraysize(constants); i++) {
+    int32_t k = constants[i];
+    Node* node = graph.NewNode(common.Int32Constant(k));
+    *cache.Find(graph.zone(), k) = nodes[i] = node;
+  }
+
+  for (size_t i = 0; i < arraysize(constants); i++) {
+    int32_t k = constants[i];
+    CHECK_EQ(nodes[i], *cache.Find(graph.zone(), k));
+  }
+}
+
+
+TEST(Int32Constant_hits) {
+  GraphTester graph;
+  Int32NodeCache cache;
+  const int32_t kSize = 1500;
+  Node** nodes = graph.zone()->NewArray<Node*>(kSize);
+  CommonOperatorBuilder common(graph.zone());
+
+  for (int i = 0; i < kSize; i++) {
+    int32_t v = i * -55;
+    nodes[i] = graph.NewNode(common.Int32Constant(v));
+    *cache.Find(graph.zone(), v) = nodes[i];
+  }
+
+  int hits = 0;
+  for (int i = 0; i < kSize; i++) {
+    int32_t v = i * -55;
+    Node** pos = cache.Find(graph.zone(), v);
+    if (*pos != NULL) {
+      CHECK_EQ(nodes[i], *pos);
+      hits++;
+    }
+  }
+  CHECK_LT(4, hits);
+}
+
+
+TEST(Int64Constant_back_to_back) {
+  GraphTester graph;
+  Int64NodeCache cache;
+
+  for (int64_t i = -2000000000; i < 2000000000; i += 3315177) {
+    Node** pos = cache.Find(graph.zone(), i);
+    CHECK_NE(NULL, pos);
+    for (int j = 0; j < 3; j++) {
+      Node** npos = cache.Find(graph.zone(), i);
+      CHECK_EQ(pos, npos);
+    }
+  }
+}
+
+
+TEST(Int64Constant_hits) {
+  GraphTester graph;
+  Int64NodeCache cache;
+  const int32_t kSize = 1500;
+  Node** nodes = graph.zone()->NewArray<Node*>(kSize);
+  CommonOperatorBuilder common(graph.zone());
+
+  for (int i = 0; i < kSize; i++) {
+    int64_t v = static_cast<int64_t>(i) * static_cast<int64_t>(5003001);
+    nodes[i] = graph.NewNode(common.Int32Constant(i));
+    *cache.Find(graph.zone(), v) = nodes[i];
+  }
+
+  int hits = 0;
+  for (int i = 0; i < kSize; i++) {
+    int64_t v = static_cast<int64_t>(i) * static_cast<int64_t>(5003001);
+    Node** pos = cache.Find(graph.zone(), v);
+    if (*pos != NULL) {
+      CHECK_EQ(nodes[i], *pos);
+      hits++;
+    }
+  }
+  CHECK_LT(4, hits);
+}
+
+
+TEST(PtrConstant_back_to_back) {
+  GraphTester graph;
+  PtrNodeCache cache;
+  int32_t buffer[50];
+
+  for (int32_t* p = buffer;
+       (p - buffer) < static_cast<ptrdiff_t>(arraysize(buffer)); p++) {
+    Node** pos = cache.Find(graph.zone(), p);
+    CHECK_NE(NULL, pos);
+    for (int j = 0; j < 3; j++) {
+      Node** npos = cache.Find(graph.zone(), p);
+      CHECK_EQ(pos, npos);
+    }
+  }
+}
+
+
+TEST(PtrConstant_hits) {
+  GraphTester graph;
+  PtrNodeCache cache;
+  const int32_t kSize = 50;
+  int32_t buffer[kSize];
+  Node* nodes[kSize];
+  CommonOperatorBuilder common(graph.zone());
+
+  for (size_t i = 0; i < arraysize(buffer); i++) {
+    int k = static_cast<int>(i);
+    int32_t* p = &buffer[i];
+    nodes[i] = graph.NewNode(common.Int32Constant(k));
+    *cache.Find(graph.zone(), p) = nodes[i];
+  }
+
+  int hits = 0;
+  for (size_t i = 0; i < arraysize(buffer); i++) {
+    int32_t* p = &buffer[i];
+    Node** pos = cache.Find(graph.zone(), p);
+    if (*pos != NULL) {
+      CHECK_EQ(nodes[i], *pos);
+      hits++;
+    }
+  }
+  CHECK_LT(4, hits);
+}
diff --git a/test/cctest/compiler/test-node.cc b/test/cctest/compiler/test-node.cc
new file mode 100644
index 0000000..28d807e
--- /dev/null
+++ b/test/cctest/compiler/test-node.cc
@@ -0,0 +1,841 @@
+// 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.
+
+#include <functional>
+
+#include "src/v8.h"
+
+#include "graph-tester.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+                                     0, 0, "dummy");
+
+TEST(NodeAllocation) {
+  GraphTester graph;
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator);
+  CHECK(n2->id() != n1->id());
+}
+
+
+TEST(NodeWithOpcode) {
+  GraphTester graph;
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator);
+  CHECK(n1->op() == &dummy_operator);
+  CHECK(n2->op() == &dummy_operator);
+}
+
+
+TEST(NodeInputs1) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  CHECK_EQ(1, n2->InputCount());
+  CHECK(n0 == n2->InputAt(0));
+}
+
+
+TEST(NodeInputs2) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+  CHECK_EQ(2, n2->InputCount());
+  CHECK(n0 == n2->InputAt(0));
+  CHECK(n1 == n2->InputAt(1));
+}
+
+
+TEST(NodeInputs3) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1, n1);
+  CHECK_EQ(3, n2->InputCount());
+  CHECK(n0 == n2->InputAt(0));
+  CHECK(n1 == n2->InputAt(1));
+  CHECK(n1 == n2->InputAt(2));
+}
+
+
+TEST(NodeInputIteratorEmpty) {
+  GraphTester graph;
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node::Inputs::iterator i(n1->inputs().begin());
+  int input_count = 0;
+  for (; i != n1->inputs().end(); ++i) {
+    input_count++;
+  }
+  CHECK_EQ(0, input_count);
+}
+
+
+TEST(NodeInputIteratorOne) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node::Inputs::iterator i(n1->inputs().begin());
+  CHECK_EQ(1, n1->InputCount());
+  CHECK_EQ(n0, *i);
+  ++i;
+  CHECK(n1->inputs().end() == i);
+}
+
+
+TEST(NodeUseIteratorEmpty) {
+  GraphTester graph;
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node::Uses::iterator i(n1->uses().begin());
+  int use_count = 0;
+  for (; i != n1->uses().end(); ++i) {
+    Node::Edge edge(i.edge());
+    USE(edge);
+    use_count++;
+  }
+  CHECK_EQ(0, use_count);
+}
+
+
+TEST(NodeUseIteratorOne) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node::Uses::iterator i(n0->uses().begin());
+  CHECK_EQ(n1, *i);
+  ++i;
+  CHECK(n0->uses().end() == i);
+}
+
+
+TEST(NodeUseIteratorReplaceNoUses) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator);
+  Node* n3 = graph.NewNode(&dummy_operator, n2);
+  n0->ReplaceUses(n1);
+  CHECK(n0->uses().begin() == n0->uses().end());
+  n0->ReplaceUses(n2);
+  CHECK(n0->uses().begin() == n0->uses().end());
+  USE(n3);
+}
+
+
+TEST(NodeUseIteratorReplaceUses) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator);
+  Node::Uses::iterator i1(n0->uses().begin());
+  CHECK_EQ(n1, *i1);
+  ++i1;
+  CHECK_EQ(n2, *i1);
+  n0->ReplaceUses(n3);
+  Node::Uses::iterator i2(n3->uses().begin());
+  CHECK_EQ(n1, *i2);
+  ++i2;
+  CHECK_EQ(n2, *i2);
+  Node::Inputs::iterator i3(n1->inputs().begin());
+  CHECK_EQ(n3, *i3);
+  ++i3;
+  CHECK(n1->inputs().end() == i3);
+  Node::Inputs::iterator i4(n2->inputs().begin());
+  CHECK_EQ(n3, *i4);
+  ++i4;
+  CHECK(n2->inputs().end() == i4);
+}
+
+
+TEST(NodeUseIteratorReplaceUsesSelf) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator);
+
+  n1->ReplaceInput(0, n1);  // Create self-reference.
+
+  Node::Uses::iterator i1(n1->uses().begin());
+  CHECK_EQ(n1, *i1);
+
+  n1->ReplaceUses(n3);
+
+  CHECK(n1->uses().begin() == n1->uses().end());
+
+  Node::Uses::iterator i2(n3->uses().begin());
+  CHECK_EQ(n1, *i2);
+  ++i2;
+  CHECK(n1->uses().end() == i2);
+}
+
+
+TEST(ReplaceInput) {
+  GraphTester graph;
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator);
+  Node* n3 = graph.NewNode(&dummy_operator, n0, n1, n2);
+  Node::Inputs::iterator i1(n3->inputs().begin());
+  CHECK(n0 == *i1);
+  CHECK_EQ(n0, n3->InputAt(0));
+  ++i1;
+  CHECK_EQ(n1, *i1);
+  CHECK_EQ(n1, n3->InputAt(1));
+  ++i1;
+  CHECK_EQ(n2, *i1);
+  CHECK_EQ(n2, n3->InputAt(2));
+  ++i1;
+  CHECK(i1 == n3->inputs().end());
+
+  Node::Uses::iterator i2(n1->uses().begin());
+  CHECK_EQ(n3, *i2);
+  ++i2;
+  CHECK(i2 == n1->uses().end());
+
+  Node* n4 = graph.NewNode(&dummy_operator);
+  Node::Uses::iterator i3(n4->uses().begin());
+  CHECK(i3 == n4->uses().end());
+
+  n3->ReplaceInput(1, n4);
+
+  Node::Uses::iterator i4(n1->uses().begin());
+  CHECK(i4 == n1->uses().end());
+
+  Node::Uses::iterator i5(n4->uses().begin());
+  CHECK_EQ(n3, *i5);
+  ++i5;
+  CHECK(i5 == n4->uses().end());
+
+  Node::Inputs::iterator i6(n3->inputs().begin());
+  CHECK(n0 == *i6);
+  CHECK_EQ(n0, n3->InputAt(0));
+  ++i6;
+  CHECK_EQ(n4, *i6);
+  CHECK_EQ(n4, n3->InputAt(1));
+  ++i6;
+  CHECK_EQ(n2, *i6);
+  CHECK_EQ(n2, n3->InputAt(2));
+  ++i6;
+  CHECK(i6 == n3->inputs().end());
+}
+
+
+TEST(OwnedBy) {
+  GraphTester graph;
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+
+    CHECK(!n0->OwnedBy(n1));
+    CHECK(!n1->OwnedBy(n0));
+
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    CHECK(n0->OwnedBy(n2));
+    CHECK(!n2->OwnedBy(n0));
+
+    Node* n3 = graph.NewNode(&dummy_operator, n0);
+    CHECK(!n0->OwnedBy(n2));
+    CHECK(!n0->OwnedBy(n3));
+    CHECK(!n2->OwnedBy(n0));
+    CHECK(!n3->OwnedBy(n0));
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator, n0);
+    CHECK(n0->OwnedBy(n1));
+    CHECK(!n1->OwnedBy(n0));
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    CHECK(!n0->OwnedBy(n1));
+    CHECK(!n0->OwnedBy(n2));
+    CHECK(!n1->OwnedBy(n0));
+    CHECK(!n1->OwnedBy(n2));
+    CHECK(!n2->OwnedBy(n0));
+    CHECK(!n2->OwnedBy(n1));
+
+    Node* n3 = graph.NewNode(&dummy_operator);
+    n2->ReplaceInput(0, n3);
+
+    CHECK(n0->OwnedBy(n1));
+    CHECK(!n1->OwnedBy(n0));
+    CHECK(!n1->OwnedBy(n0));
+    CHECK(!n1->OwnedBy(n2));
+    CHECK(!n2->OwnedBy(n0));
+    CHECK(!n2->OwnedBy(n1));
+    CHECK(n3->OwnedBy(n2));
+    CHECK(!n2->OwnedBy(n3));
+  }
+}
+
+
+TEST(Uses) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  CHECK_EQ(1, n0->UseCount());
+  printf("A: %d vs %d\n", n0->UseAt(0)->id(), n1->id());
+  CHECK(n0->UseAt(0) == n1);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  CHECK_EQ(2, n0->UseCount());
+  printf("B: %d vs %d\n", n0->UseAt(1)->id(), n2->id());
+  CHECK(n0->UseAt(1) == n2);
+  Node* n3 = graph.NewNode(&dummy_operator, n0);
+  CHECK_EQ(3, n0->UseCount());
+  CHECK(n0->UseAt(2) == n3);
+}
+
+
+TEST(Inputs) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator, n0, n1, n2);
+  CHECK_EQ(3, n3->InputCount());
+  CHECK(n3->InputAt(0) == n0);
+  CHECK(n3->InputAt(1) == n1);
+  CHECK(n3->InputAt(2) == n2);
+  Node* n4 = graph.NewNode(&dummy_operator, n0, n1, n2);
+  n3->AppendInput(graph.zone(), n4);
+  CHECK_EQ(4, n3->InputCount());
+  CHECK(n3->InputAt(0) == n0);
+  CHECK(n3->InputAt(1) == n1);
+  CHECK(n3->InputAt(2) == n2);
+  CHECK(n3->InputAt(3) == n4);
+  Node* n5 = graph.NewNode(&dummy_operator, n4);
+  n3->AppendInput(graph.zone(), n4);
+  CHECK_EQ(5, n3->InputCount());
+  CHECK(n3->InputAt(0) == n0);
+  CHECK(n3->InputAt(1) == n1);
+  CHECK(n3->InputAt(2) == n2);
+  CHECK(n3->InputAt(3) == n4);
+  CHECK(n3->InputAt(4) == n4);
+
+  // Make sure uses have been hooked op correctly.
+  Node::Uses uses(n4->uses());
+  Node::Uses::iterator current = uses.begin();
+  CHECK(current != uses.end());
+  CHECK(*current == n3);
+  ++current;
+  CHECK(current != uses.end());
+  CHECK(*current == n5);
+  ++current;
+  CHECK(current != uses.end());
+  CHECK(*current == n3);
+  ++current;
+  CHECK(current == uses.end());
+}
+
+
+TEST(RemoveInput) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+
+  n1->RemoveInput(0);
+  CHECK_EQ(0, n1->InputCount());
+  CHECK_EQ(1, n0->UseCount());
+
+  n2->RemoveInput(0);
+  CHECK_EQ(1, n2->InputCount());
+  CHECK_EQ(0, n0->UseCount());
+  CHECK_EQ(1, n1->UseCount());
+
+  n2->RemoveInput(0);
+  CHECK_EQ(0, n2->InputCount());
+}
+
+
+TEST(AppendInputsAndIterator) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+
+  Node::Inputs inputs(n2->inputs());
+  Node::Inputs::iterator current = inputs.begin();
+  CHECK(current != inputs.end());
+  CHECK(*current == n0);
+  ++current;
+  CHECK(current != inputs.end());
+  CHECK(*current == n1);
+  ++current;
+  CHECK(current == inputs.end());
+
+  Node* n3 = graph.NewNode(&dummy_operator);
+  n2->AppendInput(graph.zone(), n3);
+  inputs = n2->inputs();
+  current = inputs.begin();
+  CHECK(current != inputs.end());
+  CHECK(*current == n0);
+  CHECK_EQ(0, current.index());
+  ++current;
+  CHECK(current != inputs.end());
+  CHECK(*current == n1);
+  CHECK_EQ(1, current.index());
+  ++current;
+  CHECK(current != inputs.end());
+  CHECK(*current == n3);
+  CHECK_EQ(2, current.index());
+  ++current;
+  CHECK(current == inputs.end());
+}
+
+
+TEST(NullInputsSimple) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+  CHECK_EQ(2, n2->InputCount());
+
+  CHECK(n0 == n2->InputAt(0));
+  CHECK(n1 == n2->InputAt(1));
+  CHECK_EQ(2, n0->UseCount());
+  n2->ReplaceInput(0, NULL);
+  CHECK(NULL == n2->InputAt(0));
+  CHECK(n1 == n2->InputAt(1));
+  CHECK_EQ(1, n0->UseCount());
+}
+
+
+TEST(NullInputsAppended) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator, n0);
+  n3->AppendInput(graph.zone(), n1);
+  n3->AppendInput(graph.zone(), n2);
+  CHECK_EQ(3, n3->InputCount());
+
+  CHECK(n0 == n3->InputAt(0));
+  CHECK(n1 == n3->InputAt(1));
+  CHECK(n2 == n3->InputAt(2));
+  CHECK_EQ(1, n1->UseCount());
+  n3->ReplaceInput(1, NULL);
+  CHECK(n0 == n3->InputAt(0));
+  CHECK(NULL == n3->InputAt(1));
+  CHECK(n2 == n3->InputAt(2));
+  CHECK_EQ(0, n1->UseCount());
+}
+
+
+TEST(ReplaceUsesFromAppendedInputs) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator);
+  n2->AppendInput(graph.zone(), n1);
+  n2->AppendInput(graph.zone(), n0);
+  CHECK_EQ(0, n3->UseCount());
+  CHECK_EQ(3, n0->UseCount());
+  n0->ReplaceUses(n3);
+  CHECK_EQ(0, n0->UseCount());
+  CHECK_EQ(3, n3->UseCount());
+
+  Node::Uses uses(n3->uses());
+  Node::Uses::iterator current = uses.begin();
+  CHECK(current != uses.end());
+  CHECK(*current == n1);
+  ++current;
+  CHECK(current != uses.end());
+  CHECK(*current == n2);
+  ++current;
+  CHECK(current != uses.end());
+  CHECK(*current == n2);
+  ++current;
+  CHECK(current == uses.end());
+}
+
+
+template <bool result>
+struct FixedPredicate {
+  bool operator()(const Node* node) const { return result; }
+};
+
+
+TEST(ReplaceUsesIfWithFixedPredicate) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  Node* n3 = graph.NewNode(&dummy_operator);
+
+  CHECK_EQ(0, n2->UseCount());
+  n2->ReplaceUsesIf(FixedPredicate<true>(), n1);
+  CHECK_EQ(0, n2->UseCount());
+  n2->ReplaceUsesIf(FixedPredicate<false>(), n1);
+  CHECK_EQ(0, n2->UseCount());
+
+  CHECK_EQ(0, n3->UseCount());
+  n3->ReplaceUsesIf(FixedPredicate<true>(), n1);
+  CHECK_EQ(0, n3->UseCount());
+  n3->ReplaceUsesIf(FixedPredicate<false>(), n1);
+  CHECK_EQ(0, n3->UseCount());
+
+  CHECK_EQ(2, n0->UseCount());
+  CHECK_EQ(0, n1->UseCount());
+  n0->ReplaceUsesIf(FixedPredicate<false>(), n1);
+  CHECK_EQ(2, n0->UseCount());
+  CHECK_EQ(0, n1->UseCount());
+  n0->ReplaceUsesIf(FixedPredicate<true>(), n1);
+  CHECK_EQ(0, n0->UseCount());
+  CHECK_EQ(2, n1->UseCount());
+
+  n1->AppendInput(graph.zone(), n1);
+  CHECK_EQ(3, n1->UseCount());
+  n1->AppendInput(graph.zone(), n3);
+  CHECK_EQ(1, n3->UseCount());
+  n3->ReplaceUsesIf(FixedPredicate<true>(), n1);
+  CHECK_EQ(4, n1->UseCount());
+  CHECK_EQ(0, n3->UseCount());
+  n1->ReplaceUsesIf(FixedPredicate<false>(), n3);
+  CHECK_EQ(4, n1->UseCount());
+  CHECK_EQ(0, n3->UseCount());
+}
+
+
+TEST(ReplaceUsesIfWithEqualTo) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator, n0);
+  Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+
+  CHECK_EQ(0, n2->UseCount());
+  n2->ReplaceUsesIf(std::bind1st(std::equal_to<Node*>(), n1), n0);
+  CHECK_EQ(0, n2->UseCount());
+
+  CHECK_EQ(2, n0->UseCount());
+  CHECK_EQ(1, n1->UseCount());
+  n1->ReplaceUsesIf(std::bind1st(std::equal_to<Node*>(), n0), n0);
+  CHECK_EQ(2, n0->UseCount());
+  CHECK_EQ(1, n1->UseCount());
+  n0->ReplaceUsesIf(std::bind2nd(std::equal_to<Node*>(), n2), n1);
+  CHECK_EQ(1, n0->UseCount());
+  CHECK_EQ(2, n1->UseCount());
+}
+
+
+TEST(ReplaceInputMultipleUses) {
+  GraphTester graph;
+
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+  Node* n2 = graph.NewNode(&dummy_operator, n0);
+  n2->ReplaceInput(0, n1);
+  CHECK_EQ(0, n0->UseCount());
+  CHECK_EQ(1, n1->UseCount());
+
+  Node* n3 = graph.NewNode(&dummy_operator, n0);
+  n3->ReplaceInput(0, n1);
+  CHECK_EQ(0, n0->UseCount());
+  CHECK_EQ(2, n1->UseCount());
+}
+
+
+TEST(TrimInputCountInline) {
+  GraphTester graph;
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator, n0);
+    n1->TrimInputCount(1);
+    CHECK_EQ(1, n1->InputCount());
+    CHECK_EQ(n0, n1->InputAt(0));
+    CHECK_EQ(1, n0->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator, n0);
+    n1->TrimInputCount(0);
+    CHECK_EQ(0, n1->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+    n2->TrimInputCount(2);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(1, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0, n1);
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0, n0);
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0, n0);
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+}
+
+
+TEST(TrimInputCountOutOfLine1) {
+  GraphTester graph;
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    n1->AppendInput(graph.zone(), n0);
+    n1->TrimInputCount(1);
+    CHECK_EQ(1, n1->InputCount());
+    CHECK_EQ(n0, n1->InputAt(0));
+    CHECK_EQ(1, n0->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    n1->AppendInput(graph.zone(), n0);
+    CHECK_EQ(1, n1->InputCount());
+    n1->TrimInputCount(0);
+    CHECK_EQ(0, n1->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator);
+    n2->AppendInput(graph.zone(), n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(2);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(n0, n2->InputAt(0));
+    CHECK_EQ(n1, n2->InputAt(1));
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(1, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator);
+    n2->AppendInput(graph.zone(), n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(n0, n2->InputAt(0));
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator);
+    n2->AppendInput(graph.zone(), n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator);
+    n2->AppendInput(graph.zone(), n0);
+    n2->AppendInput(graph.zone(), n0);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(2, n0->UseCount());
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator);
+    n2->AppendInput(graph.zone(), n0);
+    n2->AppendInput(graph.zone(), n0);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(2, n0->UseCount());
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+}
+
+
+TEST(TrimInputCountOutOfLine2) {
+  GraphTester graph;
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(2);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(n0, n2->InputAt(0));
+    CHECK_EQ(n1, n2->InputAt(1));
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(1, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(n0, n2->InputAt(0));
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    n2->AppendInput(graph.zone(), n1);
+    CHECK_EQ(2, n2->InputCount());
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    n2->AppendInput(graph.zone(), n0);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(2, n0->UseCount());
+    n2->TrimInputCount(1);
+    CHECK_EQ(1, n2->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n2 = graph.NewNode(&dummy_operator, n0);
+    n2->AppendInput(graph.zone(), n0);
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(2, n0->UseCount());
+    n2->TrimInputCount(0);
+    CHECK_EQ(0, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n2->UseCount());
+  }
+}
+
+
+TEST(RemoveAllInputs) {
+  GraphTester graph;
+
+  for (int i = 0; i < 2; i++) {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator, n0);
+    Node* n2;
+    if (i == 0) {
+      n2 = graph.NewNode(&dummy_operator, n0, n1);
+    } else {
+      n2 = graph.NewNode(&dummy_operator, n0);
+      n2->AppendInput(graph.zone(), n1);  // with out-of-line input.
+    }
+
+    n0->RemoveAllInputs();
+    CHECK_EQ(0, n0->InputCount());
+
+    CHECK_EQ(2, n0->UseCount());
+    n1->RemoveAllInputs();
+    CHECK_EQ(1, n1->InputCount());
+    CHECK_EQ(1, n0->UseCount());
+    CHECK_EQ(NULL, n1->InputAt(0));
+
+    CHECK_EQ(1, n1->UseCount());
+    n2->RemoveAllInputs();
+    CHECK_EQ(2, n2->InputCount());
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(NULL, n2->InputAt(0));
+    CHECK_EQ(NULL, n2->InputAt(1));
+  }
+
+  {
+    Node* n0 = graph.NewNode(&dummy_operator);
+    Node* n1 = graph.NewNode(&dummy_operator, n0);
+    n1->ReplaceInput(0, n1);  // self-reference.
+
+    CHECK_EQ(0, n0->UseCount());
+    CHECK_EQ(1, n1->UseCount());
+    n1->RemoveAllInputs();
+    CHECK_EQ(1, n1->InputCount());
+    CHECK_EQ(0, n1->UseCount());
+    CHECK_EQ(NULL, n1->InputAt(0));
+  }
+}
diff --git a/test/cctest/compiler/test-operator.cc b/test/cctest/compiler/test-operator.cc
new file mode 100644
index 0000000..af75d67
--- /dev/null
+++ b/test/cctest/compiler/test-operator.cc
@@ -0,0 +1,244 @@
+// 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.
+
+#include "src/v8.h"
+
+#include "src/compiler/operator.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+#define NaN (v8::base::OS::nan_value())
+#define Infinity (std::numeric_limits<double>::infinity())
+
+TEST(TestOperatorMnemonic) {
+  SimpleOperator op1(10, Operator::kNoProperties, 0, 0, "ThisOne");
+  CHECK_EQ(0, strcmp(op1.mnemonic(), "ThisOne"));
+
+  SimpleOperator op2(11, Operator::kNoProperties, 0, 0, "ThatOne");
+  CHECK_EQ(0, strcmp(op2.mnemonic(), "ThatOne"));
+
+  Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "Mnemonic1", 12333);
+  CHECK_EQ(0, strcmp(op3.mnemonic(), "Mnemonic1"));
+
+  Operator1<double> op4(13, Operator::kNoProperties, 0, 1, "TheOther", 99.9);
+  CHECK_EQ(0, strcmp(op4.mnemonic(), "TheOther"));
+}
+
+
+TEST(TestSimpleOperatorHash) {
+  SimpleOperator op1(17, Operator::kNoProperties, 0, 0, "Another");
+  CHECK_EQ(17, op1.HashCode());
+
+  SimpleOperator op2(18, Operator::kNoProperties, 0, 0, "Falsch");
+  CHECK_EQ(18, op2.HashCode());
+}
+
+
+TEST(TestSimpleOperatorEquals) {
+  SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
+  SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+
+  CHECK(op1a.Equals(&op1a));
+  CHECK(op1a.Equals(&op1b));
+  CHECK(op1b.Equals(&op1a));
+  CHECK(op1b.Equals(&op1b));
+
+  SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Falsch1");
+  SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Falsch2");
+
+  CHECK(op2a.Equals(&op2a));
+  CHECK(op2a.Equals(&op2b));
+  CHECK(op2b.Equals(&op2a));
+  CHECK(op2b.Equals(&op2b));
+
+  CHECK(!op1a.Equals(&op2a));
+  CHECK(!op1a.Equals(&op2b));
+  CHECK(!op1b.Equals(&op2a));
+  CHECK(!op1b.Equals(&op2b));
+
+  CHECK(!op2a.Equals(&op1a));
+  CHECK(!op2a.Equals(&op1b));
+  CHECK(!op2b.Equals(&op1a));
+  CHECK(!op2b.Equals(&op1b));
+}
+
+
+static SmartArrayPointer<const char> OperatorToString(Operator* op) {
+  OStringStream os;
+  os << *op;
+  return SmartArrayPointer<const char>(StrDup(os.c_str()));
+}
+
+
+TEST(TestSimpleOperatorPrint) {
+  SimpleOperator op1a(19, Operator::kNoProperties, 0, 0, "Another1");
+  SimpleOperator op1b(19, Operator::kFoldable, 2, 2, "Another2");
+
+  CHECK_EQ("Another1", OperatorToString(&op1a).get());
+  CHECK_EQ("Another2", OperatorToString(&op1b).get());
+
+  SimpleOperator op2a(20, Operator::kNoProperties, 0, 0, "Flog1");
+  SimpleOperator op2b(20, Operator::kFoldable, 1, 1, "Flog2");
+
+  CHECK_EQ("Flog1", OperatorToString(&op2a).get());
+  CHECK_EQ("Flog2", OperatorToString(&op2b).get());
+}
+
+
+TEST(TestOperator1intHash) {
+  Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11);
+  Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11);
+
+  CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+
+  Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", 3);
+  Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", 4);
+
+  CHECK_NE(op1a.HashCode(), op2a.HashCode());
+  CHECK_NE(op2a.HashCode(), op2b.HashCode());
+}
+
+
+TEST(TestOperator1intEquals) {
+  Operator1<int> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11);
+  Operator1<int> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11);
+
+  CHECK(op1a.Equals(&op1a));
+  CHECK(op1a.Equals(&op1b));
+  CHECK(op1b.Equals(&op1a));
+  CHECK(op1b.Equals(&op1b));
+
+  Operator1<int> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3);
+  Operator1<int> op2b(24, Operator::kNoProperties, 0, 0, "Im", 4);
+
+  CHECK(op2a.Equals(&op2a));
+  CHECK(!op2a.Equals(&op2b));
+  CHECK(!op2b.Equals(&op2a));
+  CHECK(op2b.Equals(&op2b));
+
+  CHECK(!op1a.Equals(&op2a));
+  CHECK(!op1a.Equals(&op2b));
+  CHECK(!op1b.Equals(&op2a));
+  CHECK(!op1b.Equals(&op2b));
+
+  CHECK(!op2a.Equals(&op1a));
+  CHECK(!op2a.Equals(&op1b));
+  CHECK(!op2b.Equals(&op1a));
+  CHECK(!op2b.Equals(&op1b));
+
+  SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+
+  CHECK(!op1a.Equals(&op3));
+  CHECK(!op1b.Equals(&op3));
+  CHECK(!op2a.Equals(&op3));
+  CHECK(!op2b.Equals(&op3));
+
+  CHECK(!op3.Equals(&op1a));
+  CHECK(!op3.Equals(&op1b));
+  CHECK(!op3.Equals(&op2a));
+  CHECK(!op3.Equals(&op2b));
+}
+
+
+TEST(TestOperator1intPrint) {
+  Operator1<int> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
+  CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
+
+  Operator1<int> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 66666666);
+  CHECK_EQ("Op1Test[66666666]", OperatorToString(&op2).get());
+
+  Operator1<int> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2347);
+  CHECK_EQ("FooBar[2347]", OperatorToString(&op3).get());
+
+  Operator1<int> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", -879);
+  CHECK_EQ("BarFoo[-879]", OperatorToString(&op4).get());
+}
+
+
+TEST(TestOperator1doubleHash) {
+  Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Wolfie", 11.77);
+  Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Doggie", 11.77);
+
+  CHECK_EQ(op1a.HashCode(), op1b.HashCode());
+
+  Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Arfie", -6.7);
+  Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Arfie", -6.8);
+
+  CHECK_NE(op1a.HashCode(), op2a.HashCode());
+  CHECK_NE(op2a.HashCode(), op2b.HashCode());
+}
+
+
+TEST(TestOperator1doubleEquals) {
+  Operator1<double> op1a(23, Operator::kNoProperties, 0, 0, "Scratchy", 11.77);
+  Operator1<double> op1b(23, Operator::kFoldable, 2, 2, "Scratchy", 11.77);
+
+  CHECK(op1a.Equals(&op1a));
+  CHECK(op1a.Equals(&op1b));
+  CHECK(op1b.Equals(&op1a));
+  CHECK(op1b.Equals(&op1b));
+
+  Operator1<double> op2a(24, Operator::kNoProperties, 0, 0, "Im", 3.1);
+  Operator1<double> op2b(24, Operator::kNoProperties, 0, 0, "Im", 3.2);
+
+  CHECK(op2a.Equals(&op2a));
+  CHECK(!op2a.Equals(&op2b));
+  CHECK(!op2b.Equals(&op2a));
+  CHECK(op2b.Equals(&op2b));
+
+  CHECK(!op1a.Equals(&op2a));
+  CHECK(!op1a.Equals(&op2b));
+  CHECK(!op1b.Equals(&op2a));
+  CHECK(!op1b.Equals(&op2b));
+
+  CHECK(!op2a.Equals(&op1a));
+  CHECK(!op2a.Equals(&op1b));
+  CHECK(!op2b.Equals(&op1a));
+  CHECK(!op2b.Equals(&op1b));
+
+  SimpleOperator op3(25, Operator::kNoProperties, 0, 0, "Weepy");
+
+  CHECK(!op1a.Equals(&op3));
+  CHECK(!op1b.Equals(&op3));
+  CHECK(!op2a.Equals(&op3));
+  CHECK(!op2b.Equals(&op3));
+
+  CHECK(!op3.Equals(&op1a));
+  CHECK(!op3.Equals(&op1b));
+  CHECK(!op3.Equals(&op2a));
+  CHECK(!op3.Equals(&op2b));
+
+  Operator1<double> op4a(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
+  Operator1<double> op4b(24, Operator::kNoProperties, 0, 0, "Bashful", NaN);
+
+  CHECK(op4a.Equals(&op4a));
+  CHECK(op4a.Equals(&op4b));
+  CHECK(op4b.Equals(&op4a));
+  CHECK(op4b.Equals(&op4b));
+
+  CHECK(!op3.Equals(&op4a));
+  CHECK(!op3.Equals(&op4b));
+  CHECK(!op3.Equals(&op4a));
+  CHECK(!op3.Equals(&op4b));
+}
+
+
+TEST(TestOperator1doublePrint) {
+  Operator1<double> op1(12, Operator::kNoProperties, 0, 1, "Op1Test", 0);
+  CHECK_EQ("Op1Test[0]", OperatorToString(&op1).get());
+
+  Operator1<double> op2(12, Operator::kNoProperties, 0, 1, "Op1Test", 7.3);
+  CHECK_EQ("Op1Test[7.3]", OperatorToString(&op2).get());
+
+  Operator1<double> op3(12, Operator::kNoProperties, 0, 1, "FooBar", 2e+123);
+  CHECK_EQ("FooBar[2e+123]", OperatorToString(&op3).get());
+
+  Operator1<double> op4(12, Operator::kNoProperties, 0, 1, "BarFoo", Infinity);
+  CHECK_EQ("BarFoo[inf]", OperatorToString(&op4).get());
+
+  Operator1<double> op5(12, Operator::kNoProperties, 0, 1, "BarFoo", NaN);
+  CHECK_EQ("BarFoo[nan]", OperatorToString(&op5).get());
+}
diff --git a/test/cctest/compiler/test-phi-reducer.cc b/test/cctest/compiler/test-phi-reducer.cc
new file mode 100644
index 0000000..7d2fab6
--- /dev/null
+++ b/test/cctest/compiler/test-phi-reducer.cc
@@ -0,0 +1,230 @@
+// 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/graph-inl.h"
+#include "src/compiler/phi-reducer.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+class PhiReducerTester : HandleAndZoneScope {
+ public:
+  explicit PhiReducerTester(int num_parameters = 0)
+      : isolate(main_isolate()),
+        common(main_zone()),
+        graph(main_zone()),
+        self(graph.NewNode(common.Start(num_parameters))),
+        dead(graph.NewNode(common.Dead())) {
+    graph.SetStart(self);
+  }
+
+  Isolate* isolate;
+  CommonOperatorBuilder common;
+  Graph graph;
+  Node* self;
+  Node* dead;
+
+  void CheckReduce(Node* expect, Node* phi) {
+    PhiReducer reducer;
+    Reduction reduction = reducer.Reduce(phi);
+    if (expect == phi) {
+      CHECK(!reduction.Changed());
+    } else {
+      CHECK(reduction.Changed());
+      CHECK_EQ(expect, reduction.replacement());
+    }
+  }
+
+  Node* Int32Constant(int32_t val) {
+    return graph.NewNode(common.Int32Constant(val));
+  }
+
+  Node* Float64Constant(double val) {
+    return graph.NewNode(common.Float64Constant(val));
+  }
+
+  Node* Parameter(int32_t index = 0) {
+    return graph.NewNode(common.Parameter(index), graph.start());
+  }
+
+  Node* Phi(Node* a) {
+    return SetSelfReferences(graph.NewNode(common.Phi(kMachAnyTagged, 1), a));
+  }
+
+  Node* Phi(Node* a, Node* b) {
+    return SetSelfReferences(
+        graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b));
+  }
+
+  Node* Phi(Node* a, Node* b, Node* c) {
+    return SetSelfReferences(
+        graph.NewNode(common.Phi(kMachAnyTagged, 3), a, b, c));
+  }
+
+  Node* Phi(Node* a, Node* b, Node* c, Node* d) {
+    return SetSelfReferences(
+        graph.NewNode(common.Phi(kMachAnyTagged, 4), a, b, c, d));
+  }
+
+  Node* PhiWithControl(Node* a, Node* control) {
+    return SetSelfReferences(
+        graph.NewNode(common.Phi(kMachAnyTagged, 1), a, control));
+  }
+
+  Node* PhiWithControl(Node* a, Node* b, Node* control) {
+    return SetSelfReferences(
+        graph.NewNode(common.Phi(kMachAnyTagged, 2), a, b, control));
+  }
+
+  Node* SetSelfReferences(Node* node) {
+    Node::Inputs inputs = node->inputs();
+    for (Node::Inputs::iterator iter(inputs.begin()); iter != inputs.end();
+         ++iter) {
+      Node* input = *iter;
+      if (input == self) node->ReplaceInput(iter.index(), node);
+    }
+    return node;
+  }
+};
+
+
+TEST(PhiReduce1) {
+  PhiReducerTester R;
+  Node* zero = R.Int32Constant(0);
+  Node* one = R.Int32Constant(1);
+  Node* oneish = R.Float64Constant(1.1);
+  Node* param = R.Parameter();
+
+  Node* singles[] = {zero, one, oneish, param};
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    R.CheckReduce(singles[i], R.Phi(singles[i]));
+  }
+}
+
+
+TEST(PhiReduce2) {
+  PhiReducerTester R;
+  Node* zero = R.Int32Constant(0);
+  Node* one = R.Int32Constant(1);
+  Node* oneish = R.Float64Constant(1.1);
+  Node* param = R.Parameter();
+
+  Node* singles[] = {zero, one, oneish, param};
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(a, a));
+  }
+
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(R.self, a));
+    R.CheckReduce(a, R.Phi(a, R.self));
+  }
+
+  for (size_t i = 1; i < arraysize(singles); i++) {
+    Node* a = singles[i], *b = singles[0];
+    Node* phi1 = R.Phi(b, a);
+    R.CheckReduce(phi1, phi1);
+
+    Node* phi2 = R.Phi(a, b);
+    R.CheckReduce(phi2, phi2);
+  }
+}
+
+
+TEST(PhiReduce3) {
+  PhiReducerTester R;
+  Node* zero = R.Int32Constant(0);
+  Node* one = R.Int32Constant(1);
+  Node* oneish = R.Float64Constant(1.1);
+  Node* param = R.Parameter();
+
+  Node* singles[] = {zero, one, oneish, param};
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(a, a, a));
+  }
+
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(R.self, a, a));
+    R.CheckReduce(a, R.Phi(a, R.self, a));
+    R.CheckReduce(a, R.Phi(a, a, R.self));
+  }
+
+  for (size_t i = 1; i < arraysize(singles); i++) {
+    Node* a = singles[i], *b = singles[0];
+    Node* phi1 = R.Phi(b, a, a);
+    R.CheckReduce(phi1, phi1);
+
+    Node* phi2 = R.Phi(a, b, a);
+    R.CheckReduce(phi2, phi2);
+
+    Node* phi3 = R.Phi(a, a, b);
+    R.CheckReduce(phi3, phi3);
+  }
+}
+
+
+TEST(PhiReduce4) {
+  PhiReducerTester R;
+  Node* zero = R.Int32Constant(0);
+  Node* one = R.Int32Constant(1);
+  Node* oneish = R.Float64Constant(1.1);
+  Node* param = R.Parameter();
+
+  Node* singles[] = {zero, one, oneish, param};
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(a, a, a, a));
+  }
+
+  for (size_t i = 0; i < arraysize(singles); i++) {
+    Node* a = singles[i];
+    R.CheckReduce(a, R.Phi(R.self, a, a, a));
+    R.CheckReduce(a, R.Phi(a, R.self, a, a));
+    R.CheckReduce(a, R.Phi(a, a, R.self, a));
+    R.CheckReduce(a, R.Phi(a, a, a, R.self));
+
+    R.CheckReduce(a, R.Phi(R.self, R.self, a, a));
+    R.CheckReduce(a, R.Phi(a, R.self, R.self, a));
+    R.CheckReduce(a, R.Phi(a, a, R.self, R.self));
+    R.CheckReduce(a, R.Phi(R.self, a, a, R.self));
+  }
+
+  for (size_t i = 1; i < arraysize(singles); i++) {
+    Node* a = singles[i], *b = singles[0];
+    Node* phi1 = R.Phi(b, a, a, a);
+    R.CheckReduce(phi1, phi1);
+
+    Node* phi2 = R.Phi(a, b, a, a);
+    R.CheckReduce(phi2, phi2);
+
+    Node* phi3 = R.Phi(a, a, b, a);
+    R.CheckReduce(phi3, phi3);
+
+    Node* phi4 = R.Phi(a, a, a, b);
+    R.CheckReduce(phi4, phi4);
+  }
+}
+
+
+TEST(PhiReduceShouldIgnoreControlNodes) {
+  PhiReducerTester R;
+  Node* zero = R.Int32Constant(0);
+  Node* one = R.Int32Constant(1);
+  Node* oneish = R.Float64Constant(1.1);
+  Node* param = R.Parameter();
+
+  Node* singles[] = {zero, one, oneish, param};
+  for (size_t i = 0; i < arraysize(singles); ++i) {
+    R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.dead));
+    R.CheckReduce(singles[i], R.PhiWithControl(R.self, singles[i], R.dead));
+    R.CheckReduce(singles[i], R.PhiWithControl(singles[i], R.self, R.dead));
+  }
+}
diff --git a/test/cctest/compiler/test-pipeline.cc b/test/cctest/compiler/test-pipeline.cc
new file mode 100644
index 0000000..f0b750a
--- /dev/null
+++ b/test/cctest/compiler/test-pipeline.cc
@@ -0,0 +1,38 @@
+// 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.
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler.h"
+#include "src/compiler/pipeline.h"
+#include "src/handles.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(PipelineAdd) {
+  InitializedHandleScope handles;
+  const char* source = "(function(a,b) { return a + b; })";
+  Handle<JSFunction> function = v8::Utils::OpenHandle(
+      *v8::Handle<v8::Function>::Cast(CompileRun(source)));
+  CompilationInfoWithZone info(function);
+
+  CHECK(Parser::Parse(&info));
+  CHECK(Rewriter::Rewrite(&info));
+  CHECK(Scope::Analyze(&info));
+  CHECK_NE(NULL, info.scope());
+
+  Pipeline pipeline(&info);
+#if V8_TURBOFAN_TARGET
+  Handle<Code> code = pipeline.GenerateCode();
+  CHECK(Pipeline::SupportedTarget());
+  CHECK(!code.is_null());
+#else
+  USE(pipeline);
+#endif
+}
diff --git a/test/cctest/compiler/test-representation-change.cc b/test/cctest/compiler/test-representation-change.cc
new file mode 100644
index 0000000..6c9026b
--- /dev/null
+++ b/test/cctest/compiler/test-representation-change.cc
@@ -0,0 +1,305 @@
+// 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 <limits>
+
+#include "src/v8.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/representation-change.h"
+#include "src/compiler/typer.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+namespace v8 {  // for friendiness.
+namespace internal {
+namespace compiler {
+
+class RepresentationChangerTester : public HandleAndZoneScope,
+                                    public GraphAndBuilders {
+ public:
+  explicit RepresentationChangerTester(int num_parameters = 0)
+      : GraphAndBuilders(main_zone()),
+        typer_(main_zone()),
+        javascript_(main_zone()),
+        jsgraph_(main_graph_, &main_common_, &javascript_, &typer_,
+                 &main_machine_),
+        changer_(&jsgraph_, &main_simplified_, main_isolate()) {
+    Node* s = graph()->NewNode(common()->Start(num_parameters));
+    graph()->SetStart(s);
+  }
+
+  Typer typer_;
+  JSOperatorBuilder javascript_;
+  JSGraph jsgraph_;
+  RepresentationChanger changer_;
+
+  Isolate* isolate() { return main_isolate(); }
+  Graph* graph() { return main_graph_; }
+  CommonOperatorBuilder* common() { return &main_common_; }
+  JSGraph* jsgraph() { return &jsgraph_; }
+  RepresentationChanger* changer() { return &changer_; }
+
+  // TODO(titzer): use ValueChecker / ValueUtil
+  void CheckInt32Constant(Node* n, int32_t expected) {
+    Int32Matcher m(n);
+    CHECK(m.HasValue());
+    CHECK_EQ(expected, m.Value());
+  }
+
+  void CheckHeapConstant(Node* n, HeapObject* expected) {
+    HeapObjectMatcher<HeapObject> m(n);
+    CHECK(m.HasValue());
+    CHECK_EQ(expected, *m.Value().handle());
+  }
+
+  void CheckNumberConstant(Node* n, double expected) {
+    NumberMatcher m(n);
+    CHECK_EQ(IrOpcode::kNumberConstant, n->opcode());
+    CHECK(m.HasValue());
+    CHECK_EQ(expected, m.Value());
+  }
+
+  Node* Parameter(int index = 0) {
+    return graph()->NewNode(common()->Parameter(index), graph()->start());
+  }
+
+  void CheckTypeError(MachineTypeUnion from, MachineTypeUnion to) {
+    changer()->testing_type_errors_ = true;
+    changer()->type_error_ = false;
+    Node* n = Parameter(0);
+    Node* c = changer()->GetRepresentationFor(n, from, to);
+    CHECK(changer()->type_error_);
+    CHECK_EQ(n, c);
+  }
+
+  void CheckNop(MachineTypeUnion from, MachineTypeUnion to) {
+    Node* n = Parameter(0);
+    Node* c = changer()->GetRepresentationFor(n, from, to);
+    CHECK_EQ(n, c);
+  }
+};
+}
+}
+}  // namespace v8::internal::compiler
+
+
+// TODO(titzer): add kRepFloat32 when fully supported.
+static const MachineType all_reps[] = {kRepBit, kRepWord32, kRepWord64,
+                                       kRepFloat64, kRepTagged};
+
+
+// TODO(titzer): lift this to ValueHelper
+static const double double_inputs[] = {
+    0.0,   -0.0,    1.0,    -1.0,        0.1,         1.4,    -1.7,
+    2,     5,       6,      982983,      888,         -999.8, 3.1e7,
+    -2e66, 2.3e124, -12e73, V8_INFINITY, -V8_INFINITY};
+
+
+static const int32_t int32_inputs[] = {
+    0,      1,                                -1,
+    2,      5,                                6,
+    982983, 888,                              -999,
+    65535,  static_cast<int32_t>(0xFFFFFFFF), static_cast<int32_t>(0x80000000)};
+
+
+static const uint32_t uint32_inputs[] = {
+    0,      1,   static_cast<uint32_t>(-1),   2,     5,          6,
+    982983, 888, static_cast<uint32_t>(-999), 65535, 0xFFFFFFFF, 0x80000000};
+
+
+TEST(BoolToBit_constant) {
+  RepresentationChangerTester r;
+
+  Node* true_node = r.jsgraph()->TrueConstant();
+  Node* true_bit =
+      r.changer()->GetRepresentationFor(true_node, kRepTagged, kRepBit);
+  r.CheckInt32Constant(true_bit, 1);
+
+  Node* false_node = r.jsgraph()->FalseConstant();
+  Node* false_bit =
+      r.changer()->GetRepresentationFor(false_node, kRepTagged, kRepBit);
+  r.CheckInt32Constant(false_bit, 0);
+}
+
+
+TEST(BitToBool_constant) {
+  RepresentationChangerTester r;
+
+  for (int i = -5; i < 5; i++) {
+    Node* node = r.jsgraph()->Int32Constant(i);
+    Node* val = r.changer()->GetRepresentationFor(node, kRepBit, kRepTagged);
+    r.CheckHeapConstant(val, i == 0 ? r.isolate()->heap()->false_value()
+                                    : r.isolate()->heap()->true_value());
+  }
+}
+
+
+TEST(ToTagged_constant) {
+  RepresentationChangerTester r;
+
+  for (size_t i = 0; i < arraysize(double_inputs); i++) {
+    Node* n = r.jsgraph()->Float64Constant(double_inputs[i]);
+    Node* c = r.changer()->GetRepresentationFor(n, kRepFloat64, kRepTagged);
+    r.CheckNumberConstant(c, double_inputs[i]);
+  }
+
+  for (size_t i = 0; i < arraysize(int32_inputs); i++) {
+    Node* n = r.jsgraph()->Int32Constant(int32_inputs[i]);
+    Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeInt32,
+                                                kRepTagged);
+    r.CheckNumberConstant(c, static_cast<double>(int32_inputs[i]));
+  }
+
+  for (size_t i = 0; i < arraysize(uint32_inputs); i++) {
+    Node* n = r.jsgraph()->Int32Constant(uint32_inputs[i]);
+    Node* c = r.changer()->GetRepresentationFor(n, kRepWord32 | kTypeUint32,
+                                                kRepTagged);
+    r.CheckNumberConstant(c, static_cast<double>(uint32_inputs[i]));
+  }
+}
+
+
+static void CheckChange(IrOpcode::Value expected, MachineTypeUnion from,
+                        MachineTypeUnion to) {
+  RepresentationChangerTester r;
+
+  Node* n = r.Parameter();
+  Node* c = r.changer()->GetRepresentationFor(n, from, to);
+
+  CHECK_NE(c, n);
+  CHECK_EQ(expected, c->opcode());
+  CHECK_EQ(n, c->InputAt(0));
+}
+
+
+TEST(SingleChanges) {
+  CheckChange(IrOpcode::kChangeBoolToBit, kRepTagged, kRepBit);
+  CheckChange(IrOpcode::kChangeBitToBool, kRepBit, kRepTagged);
+
+  CheckChange(IrOpcode::kChangeInt32ToTagged, kRepWord32 | kTypeInt32,
+              kRepTagged);
+  CheckChange(IrOpcode::kChangeUint32ToTagged, kRepWord32 | kTypeUint32,
+              kRepTagged);
+  CheckChange(IrOpcode::kChangeFloat64ToTagged, kRepFloat64, kRepTagged);
+
+  CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged | kTypeInt32,
+              kRepWord32);
+  CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged | kTypeUint32,
+              kRepWord32);
+  CheckChange(IrOpcode::kChangeTaggedToFloat64, kRepTagged, kRepFloat64);
+
+  // Int32,Uint32 <-> Float64 are actually machine conversions.
+  CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32 | kTypeInt32,
+              kRepFloat64);
+  CheckChange(IrOpcode::kChangeUint32ToFloat64, kRepWord32 | kTypeUint32,
+              kRepFloat64);
+  CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64 | kTypeInt32,
+              kRepWord32);
+  CheckChange(IrOpcode::kChangeFloat64ToUint32, kRepFloat64 | kTypeUint32,
+              kRepWord32);
+}
+
+
+TEST(SignednessInWord32) {
+  RepresentationChangerTester r;
+
+  // TODO(titzer): assume that uses of a word32 without a sign mean kTypeInt32.
+  CheckChange(IrOpcode::kChangeTaggedToInt32, kRepTagged,
+              kRepWord32 | kTypeInt32);
+  CheckChange(IrOpcode::kChangeTaggedToUint32, kRepTagged,
+              kRepWord32 | kTypeUint32);
+  CheckChange(IrOpcode::kChangeInt32ToFloat64, kRepWord32, kRepFloat64);
+  CheckChange(IrOpcode::kChangeFloat64ToInt32, kRepFloat64, kRepWord32);
+}
+
+
+TEST(Nops) {
+  RepresentationChangerTester r;
+
+  // X -> X is always a nop for any single representation X.
+  for (size_t i = 0; i < arraysize(all_reps); i++) {
+    r.CheckNop(all_reps[i], all_reps[i]);
+  }
+
+  // 32-bit floats.
+  r.CheckNop(kRepFloat32, kRepFloat32);
+  r.CheckNop(kRepFloat32 | kTypeNumber, kRepFloat32);
+  r.CheckNop(kRepFloat32, kRepFloat32 | kTypeNumber);
+
+  // 32-bit or 64-bit words can be used as branch conditions (kRepBit).
+  r.CheckNop(kRepWord32, kRepBit);
+  r.CheckNop(kRepWord32, kRepBit | kTypeBool);
+  r.CheckNop(kRepWord64, kRepBit);
+  r.CheckNop(kRepWord64, kRepBit | kTypeBool);
+
+  // 32-bit words can be used as smaller word sizes and vice versa, because
+  // loads from memory implicitly sign or zero extend the value to the
+  // full machine word size, and stores implicitly truncate.
+  r.CheckNop(kRepWord32, kRepWord8);
+  r.CheckNop(kRepWord32, kRepWord16);
+  r.CheckNop(kRepWord32, kRepWord32);
+  r.CheckNop(kRepWord8, kRepWord32);
+  r.CheckNop(kRepWord16, kRepWord32);
+
+  // kRepBit (result of comparison) is implicitly a wordish thing.
+  r.CheckNop(kRepBit, kRepWord8);
+  r.CheckNop(kRepBit | kTypeBool, kRepWord8);
+  r.CheckNop(kRepBit, kRepWord16);
+  r.CheckNop(kRepBit | kTypeBool, kRepWord16);
+  r.CheckNop(kRepBit, kRepWord32);
+  r.CheckNop(kRepBit | kTypeBool, kRepWord32);
+  r.CheckNop(kRepBit, kRepWord64);
+  r.CheckNop(kRepBit | kTypeBool, kRepWord64);
+}
+
+
+TEST(TypeErrors) {
+  RepresentationChangerTester r;
+
+  // Floats cannot be implicitly converted to/from comparison conditions.
+  r.CheckTypeError(kRepFloat64, kRepBit);
+  r.CheckTypeError(kRepFloat64, kRepBit | kTypeBool);
+  r.CheckTypeError(kRepBit, kRepFloat64);
+  r.CheckTypeError(kRepBit | kTypeBool, kRepFloat64);
+
+  // Floats cannot be implicitly converted to/from comparison conditions.
+  r.CheckTypeError(kRepFloat32, kRepBit);
+  r.CheckTypeError(kRepFloat32, kRepBit | kTypeBool);
+  r.CheckTypeError(kRepBit, kRepFloat32);
+  r.CheckTypeError(kRepBit | kTypeBool, kRepFloat32);
+
+  // Word64 is internal and shouldn't be implicitly converted.
+  r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
+  r.CheckTypeError(kRepWord64, kRepTagged);
+  r.CheckTypeError(kRepWord64, kRepTagged | kTypeBool);
+  r.CheckTypeError(kRepTagged, kRepWord64);
+  r.CheckTypeError(kRepTagged | kTypeBool, kRepWord64);
+
+  // Word64 / Word32 shouldn't be implicitly converted.
+  r.CheckTypeError(kRepWord64, kRepWord32);
+  r.CheckTypeError(kRepWord32, kRepWord64);
+  r.CheckTypeError(kRepWord64, kRepWord32 | kTypeInt32);
+  r.CheckTypeError(kRepWord32 | kTypeInt32, kRepWord64);
+  r.CheckTypeError(kRepWord64, kRepWord32 | kTypeUint32);
+  r.CheckTypeError(kRepWord32 | kTypeUint32, kRepWord64);
+
+  for (size_t i = 0; i < arraysize(all_reps); i++) {
+    for (size_t j = 0; j < arraysize(all_reps); j++) {
+      if (i == j) continue;
+      // Only a single from representation is allowed.
+      r.CheckTypeError(all_reps[i] | all_reps[j], kRepTagged);
+    }
+  }
+
+  // TODO(titzer): Float32 representation changes trigger type errors now.
+  // Enforce current behavior to test all paths through representation changer.
+  for (size_t i = 0; i < arraysize(all_reps); i++) {
+    r.CheckTypeError(all_reps[i], kRepFloat32);
+    r.CheckTypeError(kRepFloat32, all_reps[i]);
+  }
+}
diff --git a/test/cctest/compiler/test-run-deopt.cc b/test/cctest/compiler/test-run-deopt.cc
new file mode 100644
index 0000000..14c024c
--- /dev/null
+++ b/test/cctest/compiler/test-run-deopt.cc
@@ -0,0 +1,76 @@
+// 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/v8.h"
+
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8;
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+#if V8_TURBOFAN_TARGET
+
+static void IsOptimized(const FunctionCallbackInfo<v8::Value>& args) {
+  JavaScriptFrameIterator it(CcTest::i_isolate());
+  JavaScriptFrame* frame = it.frame();
+  return args.GetReturnValue().Set(frame->is_optimized());
+}
+
+
+static void InstallIsOptimizedHelper(v8::Isolate* isolate) {
+  Local<v8::Context> context = isolate->GetCurrentContext();
+  Local<v8::FunctionTemplate> t = FunctionTemplate::New(isolate, IsOptimized);
+  context->Global()->Set(v8_str("IsOptimized"), t->GetFunction());
+}
+
+
+TEST(TurboSimpleDeopt) {
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  FunctionTester T(
+      "(function f(a) {"
+      "var b = 1;"
+      "if (!IsOptimized()) return 0;"
+      "%DeoptimizeFunction(f);"
+      "if (IsOptimized()) return 0;"
+      "return a + b; })");
+
+  InstallIsOptimizedHelper(CcTest::isolate());
+  T.CheckCall(T.Val(2), T.Val(1));
+}
+
+
+TEST(TurboSimpleDeoptInExpr) {
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  FunctionTester T(
+      "(function f(a) {"
+      "var b = 1;"
+      "var c = 2;"
+      "if (!IsOptimized()) return 0;"
+      "var d = b + (%DeoptimizeFunction(f), c);"
+      "if (IsOptimized()) return 0;"
+      "return d + a; })");
+
+  InstallIsOptimizedHelper(CcTest::isolate());
+  T.CheckCall(T.Val(6), T.Val(3));
+}
+
+#endif
+
+TEST(TurboTrivialDeopt) {
+  FLAG_allow_natives_syntax = true;
+  FLAG_turbo_deoptimization = true;
+
+  FunctionTester T(
+      "(function foo() {"
+      "%DeoptimizeFunction(foo);"
+      "return 1; })");
+
+  T.CheckCall(T.Val(1));
+}
diff --git a/test/cctest/compiler/test-run-inlining.cc b/test/cctest/compiler/test-run-inlining.cc
new file mode 100644
index 0000000..ad82fec
--- /dev/null
+++ b/test/cctest/compiler/test-run-inlining.cc
@@ -0,0 +1,353 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+// Helper to determine inline count via JavaScriptFrame::GetInlineCount.
+// Note that a count of 1 indicates that no inlining has occured.
+static void AssertInlineCount(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  StackTraceFrameIterator it(CcTest::i_isolate());
+  int frames_seen = 0;
+  JavaScriptFrame* topmost = it.frame();
+  while (!it.done()) {
+    JavaScriptFrame* frame = it.frame();
+    PrintF("%d %s, inline count: %d\n", frames_seen,
+           frame->function()->shared()->DebugName()->ToCString().get(),
+           frame->GetInlineCount());
+    frames_seen++;
+    it.Advance();
+  }
+  CHECK_EQ(args[0]->ToInt32()->Value(), topmost->GetInlineCount());
+}
+
+
+static void InstallAssertInlineCountHelper(v8::Isolate* isolate) {
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  v8::Local<v8::FunctionTemplate> t =
+      v8::FunctionTemplate::New(isolate, AssertInlineCount);
+  context->Global()->Set(v8_str("AssertInlineCount"), t->GetFunction());
+}
+
+
+TEST(SimpleInlining) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function(){"
+      "function foo(s) { AssertInlineCount(2); return s; };"
+      "function bar(s, t) { return foo(s); };"
+      "return bar;})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
+}
+
+
+TEST(SimpleInliningDeopt) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function(){"
+      "function foo(s) { %DeoptimizeFunction(bar); return "
+      "s; };"
+      "function bar(s, t) { return foo(s); };"
+      "return bar;})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(1), T.Val(1), T.Val(2));
+}
+
+
+TEST(SimpleInliningContext) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "function foo(s) { AssertInlineCount(2); var x = 12; return s + x; };"
+      "function bar(s, t) { return foo(s); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
+}
+
+
+TEST(SimpleInliningContextDeopt) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "function foo(s) { "
+      "  AssertInlineCount(2); %DeoptimizeFunction(bar); var x = 12;"
+      "  return s + x;"
+      "};"
+      "function bar(s, t) { return foo(s); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(13), T.Val(1), T.Val(2));
+}
+
+
+TEST(CaptureContext) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "var f = (function () {"
+      "var x = 42;"
+      "function bar(s) { return x + s; };"
+      "return (function (s) { return bar(s); });"
+      "})();"
+      "(function (s) { return f(s)})",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
+}
+
+
+// TODO(sigurds) For now we do not inline any native functions. If we do at
+// some point, change this test.
+TEST(DontInlineEval) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "var x = 42;"
+      "(function () {"
+      "function bar(s, t) { return eval(\"AssertInlineCount(1); x\") };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
+}
+
+
+TEST(InlineOmitArguments) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 42;"
+      "function bar(s, t, u, v) { AssertInlineCount(2); return x + s; };"
+      "return (function (s,t) { return bar(s); });"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
+}
+
+
+TEST(InlineOmitArgumentsDeopt) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "function foo(s,t,u,v) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
+      "return baz(); };"
+      "function bar() { return foo(11); };"
+      "function baz() { return foo.arguments.length == 1 && "
+      "                        foo.arguments[0] == 11 ; }"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
+
+TEST(InlineSurplusArguments) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 42;"
+      "function foo(s) { AssertInlineCount(2); return x + s; };"
+      "function bar(s,t) { return foo(s,t,13); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42 + 12), T.Val(12), T.undefined());
+}
+
+
+TEST(InlineSurplusArgumentsDeopt) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "function foo(s) { AssertInlineCount(2); %DeoptimizeFunction(bar); "
+      "return baz(); };"
+      "function bar() { return foo(13, 14, 15); };"
+      "function baz() { return foo.arguments.length == 3 && "
+      "                        foo.arguments[0] == 13 && "
+      "                        foo.arguments[1] == 14 && "
+      "                        foo.arguments[2] == 15; }"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.true_value(), T.Val(12), T.Val(14));
+}
+
+
+TEST(InlineTwice) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 42;"
+      "function bar(s) { AssertInlineCount(2); return x + s; };"
+      "return (function (s,t) { return bar(s) + bar(t); });"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(2 * 42 + 12 + 4), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineTwiceDependent) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 42;"
+      "function foo(s) { AssertInlineCount(2); return x + s; };"
+      "function bar(s,t) { return foo(foo(s)); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42 + 42 + 12), T.Val(12), T.Val(4));
+}
+
+
+TEST(InlineTwiceDependentDiamond) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 41;"
+      "function foo(s) { AssertInlineCount(2); if (s % 2 == 0) {"
+      "                  return x - s } else { return x + s; } };"
+      "function bar(s,t) { return foo(foo(s)); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(-11), T.Val(11), T.Val(4));
+}
+
+
+TEST(InlineTwiceDependentDiamondDifferent) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 41;"
+      "function foo(s,t) { AssertInlineCount(2); if (s % 2 == 0) {"
+      "                    return x - s * t } else { return x + s * t; } };"
+      "function bar(s,t) { return foo(foo(s, 3), 5); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(-329), T.Val(11), T.Val(4));
+}
+
+
+TEST(InlineLoop) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = 41;"
+      "function foo(s) { AssertInlineCount(2); while (s > 0) {"
+      "                  s = s - 1; }; return s; };"
+      "function bar(s,t) { return foo(foo(s)); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(0.0), T.Val(11), T.Val(4));
+}
+
+
+TEST(InlineStrictIntoNonStrict) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = Object.create({}, { y: { value:42, writable:false } });"
+      "function foo(s) { 'use strict';"
+      "                   x.y = 9; };"
+      "function bar(s,t) { return foo(s); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckThrows(T.undefined(), T.undefined());
+}
+
+
+TEST(InlineNonStrictIntoStrict) {
+  FLAG_turbo_deoptimization = true;
+  FunctionTester T(
+      "(function () {"
+      "var x = Object.create({}, { y: { value:42, writable:false } });"
+      "function foo(s) { x.y = 9; return x.y; };"
+      "function bar(s,t) { \'use strict\'; return foo(s); };"
+      "return bar;"
+      "})();",
+      CompilationInfo::kInliningEnabled |
+          CompilationInfo::kContextSpecializing |
+          CompilationInfo::kTypingEnabled);
+
+  InstallAssertInlineCountHelper(CcTest::isolate());
+  T.CheckCall(T.Val(42), T.undefined(), T.undefined());
+}
+
+
+#endif  // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-intrinsics.cc b/test/cctest/compiler/test-run-intrinsics.cc
new file mode 100644
index 0000000..a1b5676
--- /dev/null
+++ b/test/cctest/compiler/test-run-intrinsics.cc
@@ -0,0 +1,211 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+
+TEST(IsSmi) {
+  FunctionTester T("(function(a) { return %_IsSmi(a); })");
+
+  T.CheckTrue(T.Val(1));
+  T.CheckFalse(T.Val(1.1));
+  T.CheckFalse(T.Val(-0.0));
+  T.CheckTrue(T.Val(-2));
+  T.CheckFalse(T.Val(-2.3));
+  T.CheckFalse(T.undefined());
+}
+
+
+TEST(IsNonNegativeSmi) {
+  FunctionTester T("(function(a) { return %_IsNonNegativeSmi(a); })");
+
+  T.CheckTrue(T.Val(1));
+  T.CheckFalse(T.Val(1.1));
+  T.CheckFalse(T.Val(-0.0));
+  T.CheckFalse(T.Val(-2));
+  T.CheckFalse(T.Val(-2.3));
+  T.CheckFalse(T.undefined());
+}
+
+
+TEST(IsMinusZero) {
+  FunctionTester T("(function(a) { return %_IsMinusZero(a); })");
+
+  T.CheckFalse(T.Val(1));
+  T.CheckFalse(T.Val(1.1));
+  T.CheckTrue(T.Val(-0.0));
+  T.CheckFalse(T.Val(-2));
+  T.CheckFalse(T.Val(-2.3));
+  T.CheckFalse(T.undefined());
+}
+
+
+TEST(IsArray) {
+  FunctionTester T("(function(a) { return %_IsArray(a); })");
+
+  T.CheckFalse(T.NewObject("(function() {})"));
+  T.CheckTrue(T.NewObject("([1])"));
+  T.CheckFalse(T.NewObject("({})"));
+  T.CheckFalse(T.NewObject("(/x/)"));
+  T.CheckFalse(T.undefined());
+  T.CheckFalse(T.null());
+  T.CheckFalse(T.Val("x"));
+  T.CheckFalse(T.Val(1));
+}
+
+
+TEST(IsObject) {
+  FunctionTester T("(function(a) { return %_IsObject(a); })");
+
+  T.CheckFalse(T.NewObject("(function() {})"));
+  T.CheckTrue(T.NewObject("([1])"));
+  T.CheckTrue(T.NewObject("({})"));
+  T.CheckTrue(T.NewObject("(/x/)"));
+  T.CheckFalse(T.undefined());
+  T.CheckTrue(T.null());
+  T.CheckFalse(T.Val("x"));
+  T.CheckFalse(T.Val(1));
+}
+
+
+TEST(IsFunction) {
+  FunctionTester T("(function(a) { return %_IsFunction(a); })");
+
+  T.CheckTrue(T.NewObject("(function() {})"));
+  T.CheckFalse(T.NewObject("([1])"));
+  T.CheckFalse(T.NewObject("({})"));
+  T.CheckFalse(T.NewObject("(/x/)"));
+  T.CheckFalse(T.undefined());
+  T.CheckFalse(T.null());
+  T.CheckFalse(T.Val("x"));
+  T.CheckFalse(T.Val(1));
+}
+
+
+TEST(IsRegExp) {
+  FunctionTester T("(function(a) { return %_IsRegExp(a); })");
+
+  T.CheckFalse(T.NewObject("(function() {})"));
+  T.CheckFalse(T.NewObject("([1])"));
+  T.CheckFalse(T.NewObject("({})"));
+  T.CheckTrue(T.NewObject("(/x/)"));
+  T.CheckFalse(T.undefined());
+  T.CheckFalse(T.null());
+  T.CheckFalse(T.Val("x"));
+  T.CheckFalse(T.Val(1));
+}
+
+
+TEST(ClassOf) {
+  FunctionTester T("(function(a) { return %_ClassOf(a); })");
+
+  T.CheckCall(T.Val("Function"), T.NewObject("(function() {})"));
+  T.CheckCall(T.Val("Array"), T.NewObject("([1])"));
+  T.CheckCall(T.Val("Object"), T.NewObject("({})"));
+  T.CheckCall(T.Val("RegExp"), T.NewObject("(/x/)"));
+  T.CheckCall(T.null(), T.undefined());
+  T.CheckCall(T.null(), T.null());
+  T.CheckCall(T.null(), T.Val("x"));
+  T.CheckCall(T.null(), T.Val(1));
+}
+
+
+TEST(ObjectEquals) {
+  FunctionTester T("(function(a,b) { return %_ObjectEquals(a,b); })");
+  CompileRun("var o = {}");
+
+  T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)"));
+  T.CheckTrue(T.Val("internal"), T.Val("internal"));
+  T.CheckTrue(T.true_value(), T.true_value());
+  T.CheckFalse(T.true_value(), T.false_value());
+  T.CheckFalse(T.NewObject("({})"), T.NewObject("({})"));
+  T.CheckFalse(T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ValueOf) {
+  FunctionTester T("(function(a) { return %_ValueOf(a); })");
+
+  T.CheckCall(T.Val("a"), T.Val("a"));
+  T.CheckCall(T.Val("b"), T.NewObject("(new String('b'))"));
+  T.CheckCall(T.Val(123), T.Val(123));
+  T.CheckCall(T.Val(456), T.NewObject("(new Number(456))"));
+}
+
+
+TEST(SetValueOf) {
+  FunctionTester T("(function(a,b) { return %_SetValueOf(a,b); })");
+
+  T.CheckCall(T.Val("a"), T.NewObject("(new String)"), T.Val("a"));
+  T.CheckCall(T.Val(123), T.NewObject("(new Number)"), T.Val(123));
+  T.CheckCall(T.Val("x"), T.undefined(), T.Val("x"));
+}
+
+
+TEST(StringCharFromCode) {
+  FunctionTester T("(function(a) { return %_StringCharFromCode(a); })");
+
+  T.CheckCall(T.Val("a"), T.Val(97));
+  T.CheckCall(T.Val("\xE2\x9D\x8A"), T.Val(0x274A));
+  T.CheckCall(T.Val(""), T.undefined());
+}
+
+
+TEST(StringCharAt) {
+  FunctionTester T("(function(a,b) { return %_StringCharAt(a,b); })");
+
+  T.CheckCall(T.Val("e"), T.Val("huge fan!"), T.Val(3));
+  T.CheckCall(T.Val("f"), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
+  T.CheckCall(T.Val(""), T.Val("not a fan!"), T.Val(23));
+}
+
+
+TEST(StringCharCodeAt) {
+  FunctionTester T("(function(a,b) { return %_StringCharCodeAt(a,b); })");
+
+  T.CheckCall(T.Val('e'), T.Val("huge fan!"), T.Val(3));
+  T.CheckCall(T.Val('f'), T.Val("\xE2\x9D\x8A fan!"), T.Val(2));
+  T.CheckCall(T.nan(), T.Val("not a fan!"), T.Val(23));
+}
+
+
+TEST(StringAdd) {
+  FunctionTester T("(function(a,b) { return %_StringAdd(a,b); })");
+
+  T.CheckCall(T.Val("aaabbb"), T.Val("aaa"), T.Val("bbb"));
+  T.CheckCall(T.Val("aaa"), T.Val("aaa"), T.Val(""));
+  T.CheckCall(T.Val("bbb"), T.Val(""), T.Val("bbb"));
+}
+
+
+TEST(StringSubString) {
+  FunctionTester T("(function(a,b) { return %_SubString(a,b,b+3); })");
+
+  T.CheckCall(T.Val("aaa"), T.Val("aaabbb"), T.Val(0.0));
+  T.CheckCall(T.Val("abb"), T.Val("aaabbb"), T.Val(2));
+  T.CheckCall(T.Val("aaa"), T.Val("aaa"), T.Val(0.0));
+}
+
+
+TEST(StringCompare) {
+  FunctionTester T("(function(a,b) { return %_StringCompare(a,b); })");
+
+  T.CheckCall(T.Val(-1), T.Val("aaa"), T.Val("bbb"));
+  T.CheckCall(T.Val(0.0), T.Val("bbb"), T.Val("bbb"));
+  T.CheckCall(T.Val(+1), T.Val("ccc"), T.Val("bbb"));
+}
+
+
+TEST(CallFunction) {
+  FunctionTester T("(function(a,b) { return %_CallFunction(a, 1, 2, 3, b); })");
+  CompileRun("function f(a,b,c) { return a + b + c + this.d; }");
+
+  T.CheckCall(T.Val(129), T.NewObject("({d:123})"), T.NewObject("f"));
+  T.CheckCall(T.Val("6x"), T.NewObject("({d:'x'})"), T.NewObject("f"));
+}
diff --git a/test/cctest/compiler/test-run-jsbranches.cc b/test/cctest/compiler/test-run-jsbranches.cc
new file mode 100644
index 0000000..df2fcdc
--- /dev/null
+++ b/test/cctest/compiler/test-run-jsbranches.cc
@@ -0,0 +1,282 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(Conditional) {
+  FunctionTester T("(function(a) { return a ? 23 : 42; })");
+
+  T.CheckCall(T.Val(23), T.true_value(), T.undefined());
+  T.CheckCall(T.Val(42), T.false_value(), T.undefined());
+  T.CheckCall(T.Val(42), T.undefined(), T.undefined());
+  T.CheckCall(T.Val(42), T.Val(0.0), T.undefined());
+  T.CheckCall(T.Val(23), T.Val(999), T.undefined());
+  T.CheckCall(T.Val(23), T.Val("x"), T.undefined());
+}
+
+
+TEST(LogicalAnd) {
+  FunctionTester T("(function(a,b) { return a && b; })");
+
+  T.CheckCall(T.true_value(), T.true_value(), T.true_value());
+  T.CheckCall(T.false_value(), T.false_value(), T.true_value());
+  T.CheckCall(T.false_value(), T.true_value(), T.false_value());
+  T.CheckCall(T.false_value(), T.false_value(), T.false_value());
+
+  T.CheckCall(T.Val(999), T.Val(777), T.Val(999));
+  T.CheckCall(T.Val(0.0), T.Val(0.0), T.Val(999));
+  T.CheckCall(T.Val("b"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(LogicalOr) {
+  FunctionTester T("(function(a,b) { return a || b; })");
+
+  T.CheckCall(T.true_value(), T.true_value(), T.true_value());
+  T.CheckCall(T.true_value(), T.false_value(), T.true_value());
+  T.CheckCall(T.true_value(), T.true_value(), T.false_value());
+  T.CheckCall(T.false_value(), T.false_value(), T.false_value());
+
+  T.CheckCall(T.Val(777), T.Val(777), T.Val(999));
+  T.CheckCall(T.Val(999), T.Val(0.0), T.Val(999));
+  T.CheckCall(T.Val("a"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(LogicalEffect) {
+  FunctionTester T("(function(a,b) { a && (b = a); return b; })");
+
+  T.CheckCall(T.true_value(), T.true_value(), T.true_value());
+  T.CheckCall(T.true_value(), T.false_value(), T.true_value());
+  T.CheckCall(T.true_value(), T.true_value(), T.false_value());
+  T.CheckCall(T.false_value(), T.false_value(), T.false_value());
+
+  T.CheckCall(T.Val(777), T.Val(777), T.Val(999));
+  T.CheckCall(T.Val(999), T.Val(0.0), T.Val(999));
+  T.CheckCall(T.Val("a"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(IfStatement) {
+  FunctionTester T("(function(a) { if (a) { return 1; } else { return 2; } })");
+
+  T.CheckCall(T.Val(1), T.true_value(), T.undefined());
+  T.CheckCall(T.Val(2), T.false_value(), T.undefined());
+  T.CheckCall(T.Val(2), T.undefined(), T.undefined());
+  T.CheckCall(T.Val(2), T.Val(0.0), T.undefined());
+  T.CheckCall(T.Val(1), T.Val(999), T.undefined());
+  T.CheckCall(T.Val(1), T.Val("x"), T.undefined());
+}
+
+
+TEST(DoWhileStatement) {
+  FunctionTester T("(function(a,b) { do { a+=23; } while(a < b) return a; })");
+
+  T.CheckCall(T.Val(24), T.Val(1), T.Val(1));
+  T.CheckCall(T.Val(24), T.Val(1), T.Val(23));
+  T.CheckCall(T.Val(47), T.Val(1), T.Val(25));
+  T.CheckCall(T.Val("str23"), T.Val("str"), T.Val("str"));
+}
+
+
+TEST(WhileStatement) {
+  FunctionTester T("(function(a,b) { while(a < b) { a+=23; } return a; })");
+
+  T.CheckCall(T.Val(1), T.Val(1), T.Val(1));
+  T.CheckCall(T.Val(24), T.Val(1), T.Val(23));
+  T.CheckCall(T.Val(47), T.Val(1), T.Val(25));
+  T.CheckCall(T.Val("str"), T.Val("str"), T.Val("str"));
+}
+
+
+TEST(ForStatement) {
+  FunctionTester T("(function(a,b) { for (; a < b; a+=23) {} return a; })");
+
+  T.CheckCall(T.Val(1), T.Val(1), T.Val(1));
+  T.CheckCall(T.Val(24), T.Val(1), T.Val(23));
+  T.CheckCall(T.Val(47), T.Val(1), T.Val(25));
+  T.CheckCall(T.Val("str"), T.Val("str"), T.Val("str"));
+}
+
+
+static void TestForIn(const char* code) {
+  FunctionTester T(code);
+  T.CheckCall(T.undefined(), T.undefined());
+  T.CheckCall(T.undefined(), T.null());
+  T.CheckCall(T.undefined(), T.NewObject("({})"));
+  T.CheckCall(T.undefined(), T.Val(1));
+  T.CheckCall(T.Val("2"), T.Val("str"));
+  T.CheckCall(T.Val("a"), T.NewObject("({'a' : 1})"));
+  T.CheckCall(T.Val("2"), T.NewObject("([1, 2, 3])"));
+  T.CheckCall(T.Val("a"), T.NewObject("({'a' : 1, 'b' : 1})"), T.Val("b"));
+  T.CheckCall(T.Val("1"), T.NewObject("([1, 2, 3])"), T.Val("2"));
+}
+
+
+TEST(ForInStatement) {
+  // Variable assignment.
+  TestForIn(
+      "(function(a, b) {"
+      "var last;"
+      "for (var x in a) {"
+      "  if (b) { delete a[b]; b = undefined; }"
+      "  last = x;"
+      "}"
+      "return last;})");
+  // Indexed assignment.
+  TestForIn(
+      "(function(a, b) {"
+      "var array = [0, 1, undefined];"
+      "for (array[2] in a) {"
+      "  if (b) { delete a[b]; b = undefined; }"
+      "}"
+      "return array[2];})");
+  // Named assignment.
+  TestForIn(
+      "(function(a, b) {"
+      "var obj = {'a' : undefined};"
+      "for (obj.a in a) {"
+      "  if (b) { delete a[b]; b = undefined; }"
+      "}"
+      "return obj.a;})");
+}
+
+
+TEST(ForInContinueStatement) {
+  const char* src =
+      "(function(a,b) {"
+      "  var r = '-';"
+      "  for (var x in a) {"
+      "    r += 'A-';"
+      "    if (b) continue;"
+      "    r += 'B-';"
+      "  }"
+      "  return r;"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val("-A-B-"), T.NewObject("({x:1})"), T.false_value());
+  T.CheckCall(T.Val("-A-B-A-B-"), T.NewObject("({x:1,y:2})"), T.false_value());
+  T.CheckCall(T.Val("-A-"), T.NewObject("({x:1})"), T.true_value());
+  T.CheckCall(T.Val("-A-A-"), T.NewObject("({x:1,y:2})"), T.true_value());
+}
+
+
+TEST(SwitchStatement) {
+  const char* src =
+      "(function(a,b) {"
+      "  var r = '-';"
+      "  switch (a) {"
+      "    case 'x'    : r += 'X-';"
+      "    case b + 'b': r += 'B-';"
+      "    default     : r += 'D-';"
+      "    case 'y'    : r += 'Y-';"
+      "  }"
+      "  return r;"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val("-X-B-D-Y-"), T.Val("x"), T.Val("B"));
+  T.CheckCall(T.Val("-B-D-Y-"), T.Val("Bb"), T.Val("B"));
+  T.CheckCall(T.Val("-D-Y-"), T.Val("z"), T.Val("B"));
+  T.CheckCall(T.Val("-Y-"), T.Val("y"), T.Val("B"));
+
+  CompileRun("var c = 0; var o = { toString:function(){return c++} };");
+  T.CheckCall(T.Val("-D-Y-"), T.Val("1b"), T.NewObject("o"));
+  T.CheckCall(T.Val("-B-D-Y-"), T.Val("1b"), T.NewObject("o"));
+  T.CheckCall(T.Val("-D-Y-"), T.Val("1b"), T.NewObject("o"));
+}
+
+
+TEST(BlockBreakStatement) {
+  FunctionTester T("(function(a,b) { L:{ if (a) break L; b=1; } return b; })");
+
+  T.CheckCall(T.Val(7), T.true_value(), T.Val(7));
+  T.CheckCall(T.Val(1), T.false_value(), T.Val(7));
+}
+
+
+TEST(BlockReturnStatement) {
+  FunctionTester T("(function(a,b) { L:{ if (a) b=1; return b; } })");
+
+  T.CheckCall(T.Val(1), T.true_value(), T.Val(7));
+  T.CheckCall(T.Val(7), T.false_value(), T.Val(7));
+}
+
+
+TEST(NestedIfConditional) {
+  FunctionTester T("(function(a,b) { if (a) { b = (b?b:7) + 1; } return b; })");
+
+  T.CheckCall(T.Val(4), T.false_value(), T.Val(4));
+  T.CheckCall(T.Val(6), T.true_value(), T.Val(5));
+  T.CheckCall(T.Val(8), T.true_value(), T.undefined());
+}
+
+
+TEST(NestedIfLogical) {
+  const char* src =
+      "(function(a,b) {"
+      "  if (a || b) { return 1; } else { return 2; }"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val(1), T.true_value(), T.true_value());
+  T.CheckCall(T.Val(1), T.false_value(), T.true_value());
+  T.CheckCall(T.Val(1), T.true_value(), T.false_value());
+  T.CheckCall(T.Val(2), T.false_value(), T.false_value());
+  T.CheckCall(T.Val(1), T.Val(1.0), T.Val(1.0));
+  T.CheckCall(T.Val(1), T.Val(0.0), T.Val(1.0));
+  T.CheckCall(T.Val(1), T.Val(1.0), T.Val(0.0));
+  T.CheckCall(T.Val(2), T.Val(0.0), T.Val(0.0));
+}
+
+
+TEST(NestedIfElseFor) {
+  const char* src =
+      "(function(a,b) {"
+      "  if (!a) { return b - 3; } else { for (; a < b; a++); }"
+      "  return a;"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val(1), T.false_value(), T.Val(4));
+  T.CheckCall(T.Val(2), T.true_value(), T.Val(2));
+  T.CheckCall(T.Val(3), T.Val(3), T.Val(1));
+}
+
+
+TEST(NestedWhileWhile) {
+  const char* src =
+      "(function(a) {"
+      "  var i = a; while (false) while(false) return i;"
+      "  return i;"
+      "})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val(2.0), T.Val(2.0), T.Val(-1.0));
+  T.CheckCall(T.Val(65.0), T.Val(65.0), T.Val(-1.0));
+}
+
+
+TEST(NestedForIf) {
+  FunctionTester T("(function(a,b) { for (; a > 1; a--) if (b) return 1; })");
+
+  T.CheckCall(T.Val(1), T.Val(3), T.true_value());
+  T.CheckCall(T.undefined(), T.Val(2), T.false_value());
+  T.CheckCall(T.undefined(), T.Val(1), T.null());
+}
+
+
+TEST(NestedForConditional) {
+  FunctionTester T("(function(a,b) { for (; a > 1; a--) return b ? 1 : 2; })");
+
+  T.CheckCall(T.Val(1), T.Val(3), T.true_value());
+  T.CheckCall(T.Val(2), T.Val(2), T.false_value());
+  T.CheckCall(T.undefined(), T.Val(1), T.null());
+}
diff --git a/test/cctest/compiler/test-run-jscalls.cc b/test/cctest/compiler/test-run-jscalls.cc
new file mode 100644
index 0000000..dec7194
--- /dev/null
+++ b/test/cctest/compiler/test-run-jscalls.cc
@@ -0,0 +1,289 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(SimpleCall) {
+  FunctionTester T("(function(foo,a) { return foo(a); })");
+  Handle<JSFunction> foo = T.NewFunction("(function(a) { return a; })");
+
+  T.CheckCall(T.Val(3), foo, T.Val(3));
+  T.CheckCall(T.Val(3.1), foo, T.Val(3.1));
+  T.CheckCall(foo, foo, foo);
+  T.CheckCall(T.Val("Abba"), foo, T.Val("Abba"));
+}
+
+
+TEST(SimpleCall2) {
+  FunctionTester T("(function(foo,a) { return foo(a); })");
+  Handle<JSFunction> foo = T.NewFunction("(function(a) { return a; })");
+  T.Compile(foo);
+
+  T.CheckCall(T.Val(3), foo, T.Val(3));
+  T.CheckCall(T.Val(3.1), foo, T.Val(3.1));
+  T.CheckCall(foo, foo, foo);
+  T.CheckCall(T.Val("Abba"), foo, T.Val("Abba"));
+}
+
+
+TEST(ConstCall) {
+  FunctionTester T("(function(foo,a) { return foo(a,3); })");
+  Handle<JSFunction> foo = T.NewFunction("(function(a,b) { return a + b; })");
+  T.Compile(foo);
+
+  T.CheckCall(T.Val(6), foo, T.Val(3));
+  T.CheckCall(T.Val(6.1), foo, T.Val(3.1));
+  T.CheckCall(T.Val("function (a,b) { return a + b; }3"), foo, foo);
+  T.CheckCall(T.Val("Abba3"), foo, T.Val("Abba"));
+}
+
+
+TEST(ConstCall2) {
+  FunctionTester T("(function(foo,a) { return foo(a,\"3\"); })");
+  Handle<JSFunction> foo = T.NewFunction("(function(a,b) { return a + b; })");
+  T.Compile(foo);
+
+  T.CheckCall(T.Val("33"), foo, T.Val(3));
+  T.CheckCall(T.Val("3.13"), foo, T.Val(3.1));
+  T.CheckCall(T.Val("function (a,b) { return a + b; }3"), foo, foo);
+  T.CheckCall(T.Val("Abba3"), foo, T.Val("Abba"));
+}
+
+
+TEST(PropertyNamedCall) {
+  FunctionTester T("(function(a,b) { return a.foo(b,23); })");
+  CompileRun("function foo(y,z) { return this.x + y + z; }");
+
+  T.CheckCall(T.Val(32), T.NewObject("({ foo:foo, x:4 })"), T.Val(5));
+  T.CheckCall(T.Val("xy23"), T.NewObject("({ foo:foo, x:'x' })"), T.Val("y"));
+  T.CheckCall(T.nan(), T.NewObject("({ foo:foo, y:0 })"), T.Val(3));
+}
+
+
+TEST(PropertyKeyedCall) {
+  FunctionTester T("(function(a,b) { var f = 'foo'; return a[f](b,23); })");
+  CompileRun("function foo(y,z) { return this.x + y + z; }");
+
+  T.CheckCall(T.Val(32), T.NewObject("({ foo:foo, x:4 })"), T.Val(5));
+  T.CheckCall(T.Val("xy23"), T.NewObject("({ foo:foo, x:'x' })"), T.Val("y"));
+  T.CheckCall(T.nan(), T.NewObject("({ foo:foo, y:0 })"), T.Val(3));
+}
+
+
+TEST(GlobalCall) {
+  FunctionTester T("(function(a,b) { return foo(a,b); })");
+  CompileRun("function foo(a,b) { return a + b + this.c; }");
+  CompileRun("var c = 23;");
+
+  T.CheckCall(T.Val(32), T.Val(4), T.Val(5));
+  T.CheckCall(T.Val("xy23"), T.Val("x"), T.Val("y"));
+  T.CheckCall(T.nan(), T.undefined(), T.Val(3));
+}
+
+
+TEST(LookupCall) {
+  FunctionTester T("(function(a,b) { with (a) { return foo(a,b); } })");
+
+  CompileRun("function f1(a,b) { return a.val + b; }");
+  T.CheckCall(T.Val(5), T.NewObject("({ foo:f1, val:2 })"), T.Val(3));
+  T.CheckCall(T.Val("xy"), T.NewObject("({ foo:f1, val:'x' })"), T.Val("y"));
+
+  CompileRun("function f2(a,b) { return this.val + b; }");
+  T.CheckCall(T.Val(9), T.NewObject("({ foo:f2, val:4 })"), T.Val(5));
+  T.CheckCall(T.Val("xy"), T.NewObject("({ foo:f2, val:'x' })"), T.Val("y"));
+}
+
+
+TEST(MismatchCallTooFew) {
+  FunctionTester T("(function(a,b) { return foo(a,b); })");
+  CompileRun("function foo(a,b,c) { return a + b + c; }");
+
+  T.CheckCall(T.nan(), T.Val(23), T.Val(42));
+  T.CheckCall(T.nan(), T.Val(4.2), T.Val(2.3));
+  T.CheckCall(T.Val("abundefined"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(MismatchCallTooMany) {
+  FunctionTester T("(function(a,b) { return foo(a,b); })");
+  CompileRun("function foo(a) { return a; }");
+
+  T.CheckCall(T.Val(23), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val(4.2), T.Val(4.2), T.Val(2.3));
+  T.CheckCall(T.Val("a"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ConstructorCall) {
+  FunctionTester T("(function(a,b) { return new foo(a,b).value; })");
+  CompileRun("function foo(a,b) { return { value: a + b + this.c }; }");
+  CompileRun("foo.prototype.c = 23;");
+
+  T.CheckCall(T.Val(32), T.Val(4), T.Val(5));
+  T.CheckCall(T.Val("xy23"), T.Val("x"), T.Val("y"));
+  T.CheckCall(T.nan(), T.undefined(), T.Val(3));
+}
+
+
+// TODO(titzer): factor these out into test-runtime-calls.cc
+TEST(RuntimeCallCPP1) {
+  FLAG_allow_natives_syntax = true;
+  FunctionTester T("(function(a) { return %ToBool(a); })");
+
+  T.CheckCall(T.true_value(), T.Val(23), T.undefined());
+  T.CheckCall(T.true_value(), T.Val(4.2), T.undefined());
+  T.CheckCall(T.true_value(), T.Val("str"), T.undefined());
+  T.CheckCall(T.true_value(), T.true_value(), T.undefined());
+  T.CheckCall(T.false_value(), T.false_value(), T.undefined());
+  T.CheckCall(T.false_value(), T.undefined(), T.undefined());
+  T.CheckCall(T.false_value(), T.Val(0.0), T.undefined());
+}
+
+
+TEST(RuntimeCallCPP2) {
+  FLAG_allow_natives_syntax = true;
+  FunctionTester T("(function(a,b) { return %NumberAdd(a, b); })");
+
+  T.CheckCall(T.Val(65), T.Val(42), T.Val(23));
+  T.CheckCall(T.Val(19), T.Val(42), T.Val(-23));
+  T.CheckCall(T.Val(6.5), T.Val(4.2), T.Val(2.3));
+}
+
+
+TEST(RuntimeCallJS) {
+  FLAG_allow_natives_syntax = true;
+  FunctionTester T("(function(a) { return %ToString(a); })");
+
+  T.CheckCall(T.Val("23"), T.Val(23), T.undefined());
+  T.CheckCall(T.Val("4.2"), T.Val(4.2), T.undefined());
+  T.CheckCall(T.Val("str"), T.Val("str"), T.undefined());
+  T.CheckCall(T.Val("true"), T.true_value(), T.undefined());
+  T.CheckCall(T.Val("false"), T.false_value(), T.undefined());
+  T.CheckCall(T.Val("undefined"), T.undefined(), T.undefined());
+}
+
+
+TEST(RuntimeCallInline) {
+  FLAG_allow_natives_syntax = true;
+  FunctionTester T("(function(a) { return %_IsObject(a); })");
+
+  T.CheckCall(T.false_value(), T.Val(23), T.undefined());
+  T.CheckCall(T.false_value(), T.Val(4.2), T.undefined());
+  T.CheckCall(T.false_value(), T.Val("str"), T.undefined());
+  T.CheckCall(T.false_value(), T.true_value(), T.undefined());
+  T.CheckCall(T.false_value(), T.false_value(), T.undefined());
+  T.CheckCall(T.false_value(), T.undefined(), T.undefined());
+  T.CheckCall(T.true_value(), T.NewObject("({})"), T.undefined());
+  T.CheckCall(T.true_value(), T.NewObject("([])"), T.undefined());
+}
+
+
+TEST(RuntimeCallBooleanize) {
+  // TODO(turbofan): %Booleanize will disappear, don't hesitate to remove this
+  // test case, two-argument case is covered by the above test already.
+  FLAG_allow_natives_syntax = true;
+  FunctionTester T("(function(a,b) { return %Booleanize(a, b); })");
+
+  T.CheckCall(T.true_value(), T.Val(-1), T.Val(Token::LT));
+  T.CheckCall(T.false_value(), T.Val(-1), T.Val(Token::EQ));
+  T.CheckCall(T.false_value(), T.Val(-1), T.Val(Token::GT));
+
+  T.CheckCall(T.false_value(), T.Val(0.0), T.Val(Token::LT));
+  T.CheckCall(T.true_value(), T.Val(0.0), T.Val(Token::EQ));
+  T.CheckCall(T.false_value(), T.Val(0.0), T.Val(Token::GT));
+
+  T.CheckCall(T.false_value(), T.Val(1), T.Val(Token::LT));
+  T.CheckCall(T.false_value(), T.Val(1), T.Val(Token::EQ));
+  T.CheckCall(T.true_value(), T.Val(1), T.Val(Token::GT));
+}
+
+
+TEST(EvalCall) {
+  FunctionTester T("(function(a,b) { return eval(a); })");
+  Handle<JSObject> g(T.function->context()->global_object()->global_proxy());
+
+  T.CheckCall(T.Val(23), T.Val("17 + 6"), T.undefined());
+  T.CheckCall(T.Val("'Y'; a"), T.Val("'Y'; a"), T.Val("b-val"));
+  T.CheckCall(T.Val("b-val"), T.Val("'Y'; b"), T.Val("b-val"));
+  T.CheckCall(g, T.Val("this"), T.undefined());
+  T.CheckCall(g, T.Val("'use strict'; this"), T.undefined());
+
+  CompileRun("eval = function(x) { return x; }");
+  T.CheckCall(T.Val("17 + 6"), T.Val("17 + 6"), T.undefined());
+
+  CompileRun("eval = function(x) { return this; }");
+  T.CheckCall(g, T.Val("17 + 6"), T.undefined());
+
+  CompileRun("eval = function(x) { 'use strict'; return this; }");
+  T.CheckCall(T.undefined(), T.Val("17 + 6"), T.undefined());
+}
+
+
+TEST(ReceiverPatching) {
+  // TODO(turbofan): Note that this test only checks that the function prologue
+  // patches an undefined receiver to the global receiver. If this starts to
+  // fail once we fix the calling protocol, just remove this test.
+  FunctionTester T("(function(a) { return this; })");
+  Handle<JSObject> g(T.function->context()->global_object()->global_proxy());
+  T.CheckCall(g, T.undefined());
+}
+
+
+TEST(CallEval) {
+  FunctionTester T(
+      "var x = 42;"
+      "(function () {"
+      "function bar() { return eval('x') };"
+      "return bar;"
+      "})();");
+
+  T.CheckCall(T.Val(42), T.Val("x"), T.undefined());
+}
+
+
+TEST(ContextLoadedFromActivation) {
+  const char* script =
+      "var x = 42;"
+      "(function() {"
+      "  return function () { return x };"
+      "})()";
+
+  // Disable context specialization.
+  FunctionTester T(script);
+  v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
+  v8::Context::Scope scope(context);
+  v8::Local<v8::Value> value = CompileRun(script);
+  i::Handle<i::Object> ofun = v8::Utils::OpenHandle(*value);
+  i::Handle<i::JSFunction> jsfun = Handle<JSFunction>::cast(ofun);
+  jsfun->set_code(T.function->code());
+  context->Global()->Set(v8_str("foo"), v8::Utils::ToLocal(jsfun));
+  CompileRun("var x = 24;");
+  ExpectInt32("foo();", 24);
+}
+
+
+TEST(BuiltinLoadedFromActivation) {
+  const char* script =
+      "var x = 42;"
+      "(function() {"
+      "  return function () { return this; };"
+      "})()";
+
+  // Disable context specialization.
+  FunctionTester T(script);
+  v8::Local<v8::Context> context = v8::Context::New(CcTest::isolate());
+  v8::Context::Scope scope(context);
+  v8::Local<v8::Value> value = CompileRun(script);
+  i::Handle<i::Object> ofun = v8::Utils::OpenHandle(*value);
+  i::Handle<i::JSFunction> jsfun = Handle<JSFunction>::cast(ofun);
+  jsfun->set_code(T.function->code());
+  context->Global()->Set(v8_str("foo"), v8::Utils::ToLocal(jsfun));
+  CompileRun("var x = 24;");
+  ExpectObject("foo()", context->Global());
+}
diff --git a/test/cctest/compiler/test-run-jsexceptions.cc b/test/cctest/compiler/test-run-jsexceptions.cc
new file mode 100644
index 0000000..0712ab6
--- /dev/null
+++ b/test/cctest/compiler/test-run-jsexceptions.cc
@@ -0,0 +1,45 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(Throw) {
+  FunctionTester T("(function(a,b) { if (a) { throw b; } else { return b; }})");
+
+  T.CheckThrows(T.true_value(), T.NewObject("new Error"));
+  T.CheckCall(T.Val(23), T.false_value(), T.Val(23));
+}
+
+
+TEST(ThrowSourcePosition) {
+  static const char* src =
+      "(function(a, b) {        \n"
+      "  if (a == 1) throw 1;   \n"
+      "  if (a == 2) {throw 2}  \n"
+      "  if (a == 3) {0;throw 3}\n"
+      "  throw 4;               \n"
+      "})                       ";
+  FunctionTester T(src);
+  v8::Handle<v8::Message> message;
+
+  message = T.CheckThrowsReturnMessage(T.Val(1), T.undefined());
+  CHECK(!message.IsEmpty());
+  CHECK_EQ(2, message->GetLineNumber());
+  CHECK_EQ(40, message->GetStartPosition());
+
+  message = T.CheckThrowsReturnMessage(T.Val(2), T.undefined());
+  CHECK(!message.IsEmpty());
+  CHECK_EQ(3, message->GetLineNumber());
+  CHECK_EQ(67, message->GetStartPosition());
+
+  message = T.CheckThrowsReturnMessage(T.Val(3), T.undefined());
+  CHECK(!message.IsEmpty());
+  CHECK_EQ(4, message->GetLineNumber());
+  CHECK_EQ(95, message->GetStartPosition());
+}
diff --git a/test/cctest/compiler/test-run-jsops.cc b/test/cctest/compiler/test-run-jsops.cc
new file mode 100644
index 0000000..eb39760
--- /dev/null
+++ b/test/cctest/compiler/test-run-jsops.cc
@@ -0,0 +1,524 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+TEST(BinopAdd) {
+  FunctionTester T("(function(a,b) { return a + b; })");
+
+  T.CheckCall(3, 1, 2);
+  T.CheckCall(-11, -2, -9);
+  T.CheckCall(-11, -1.5, -9.5);
+  T.CheckCall(T.Val("AB"), T.Val("A"), T.Val("B"));
+  T.CheckCall(T.Val("A11"), T.Val("A"), T.Val(11));
+  T.CheckCall(T.Val("12B"), T.Val(12), T.Val("B"));
+  T.CheckCall(T.Val("38"), T.Val("3"), T.Val("8"));
+  T.CheckCall(T.Val("31"), T.Val("3"), T.NewObject("([1])"));
+  T.CheckCall(T.Val("3[object Object]"), T.Val("3"), T.NewObject("({})"));
+}
+
+
+TEST(BinopSubtract) {
+  FunctionTester T("(function(a,b) { return a - b; })");
+
+  T.CheckCall(3, 4, 1);
+  T.CheckCall(3.0, 4.5, 1.5);
+  T.CheckCall(T.Val(-9), T.Val("0"), T.Val(9));
+  T.CheckCall(T.Val(-9), T.Val(0.0), T.Val("9"));
+  T.CheckCall(T.Val(1), T.Val("3"), T.Val("2"));
+  T.CheckCall(T.nan(), T.Val("3"), T.Val("B"));
+  T.CheckCall(T.Val(2), T.Val("3"), T.NewObject("([1])"));
+  T.CheckCall(T.nan(), T.Val("3"), T.NewObject("({})"));
+}
+
+
+TEST(BinopMultiply) {
+  FunctionTester T("(function(a,b) { return a * b; })");
+
+  T.CheckCall(6, 3, 2);
+  T.CheckCall(4.5, 2.0, 2.25);
+  T.CheckCall(T.Val(6), T.Val("3"), T.Val(2));
+  T.CheckCall(T.Val(4.5), T.Val(2.0), T.Val("2.25"));
+  T.CheckCall(T.Val(6), T.Val("3"), T.Val("2"));
+  T.CheckCall(T.nan(), T.Val("3"), T.Val("B"));
+  T.CheckCall(T.Val(3), T.Val("3"), T.NewObject("([1])"));
+  T.CheckCall(T.nan(), T.Val("3"), T.NewObject("({})"));
+}
+
+
+TEST(BinopDivide) {
+  FunctionTester T("(function(a,b) { return a / b; })");
+
+  T.CheckCall(2, 8, 4);
+  T.CheckCall(2.1, 8.4, 4);
+  T.CheckCall(V8_INFINITY, 8, 0);
+  T.CheckCall(-V8_INFINITY, -8, 0);
+  T.CheckCall(T.infinity(), T.Val(8), T.Val("0"));
+  T.CheckCall(T.minus_infinity(), T.Val("-8"), T.Val(0.0));
+  T.CheckCall(T.Val(1.5), T.Val("3"), T.Val("2"));
+  T.CheckCall(T.nan(), T.Val("3"), T.Val("B"));
+  T.CheckCall(T.Val(1.5), T.Val("3"), T.NewObject("([2])"));
+  T.CheckCall(T.nan(), T.Val("3"), T.NewObject("({})"));
+}
+
+
+TEST(BinopModulus) {
+  FunctionTester T("(function(a,b) { return a % b; })");
+
+  T.CheckCall(3, 8, 5);
+  T.CheckCall(T.Val(3), T.Val("8"), T.Val(5));
+  T.CheckCall(T.Val(3), T.Val(8), T.Val("5"));
+  T.CheckCall(T.Val(1), T.Val("3"), T.Val("2"));
+  T.CheckCall(T.nan(), T.Val("3"), T.Val("B"));
+  T.CheckCall(T.Val(1), T.Val("3"), T.NewObject("([2])"));
+  T.CheckCall(T.nan(), T.Val("3"), T.NewObject("({})"));
+}
+
+
+TEST(BinopShiftLeft) {
+  FunctionTester T("(function(a,b) { return a << b; })");
+
+  T.CheckCall(4, 2, 1);
+  T.CheckCall(T.Val(4), T.Val("2"), T.Val(1));
+  T.CheckCall(T.Val(4), T.Val(2), T.Val("1"));
+}
+
+
+TEST(BinopShiftRight) {
+  FunctionTester T("(function(a,b) { return a >> b; })");
+
+  T.CheckCall(4, 8, 1);
+  T.CheckCall(-4, -8, 1);
+  T.CheckCall(T.Val(4), T.Val("8"), T.Val(1));
+  T.CheckCall(T.Val(4), T.Val(8), T.Val("1"));
+}
+
+
+TEST(BinopShiftRightLogical) {
+  FunctionTester T("(function(a,b) { return a >>> b; })");
+
+  T.CheckCall(4, 8, 1);
+  T.CheckCall(0x7ffffffc, -8, 1);
+  T.CheckCall(T.Val(4), T.Val("8"), T.Val(1));
+  T.CheckCall(T.Val(4), T.Val(8), T.Val("1"));
+}
+
+
+TEST(BinopAnd) {
+  FunctionTester T("(function(a,b) { return a & b; })");
+
+  T.CheckCall(7, 7, 15);
+  T.CheckCall(7, 15, 7);
+  T.CheckCall(T.Val(7), T.Val("15"), T.Val(7));
+  T.CheckCall(T.Val(7), T.Val(15), T.Val("7"));
+}
+
+
+TEST(BinopOr) {
+  FunctionTester T("(function(a,b) { return a | b; })");
+
+  T.CheckCall(6, 4, 2);
+  T.CheckCall(6, 2, 4);
+  T.CheckCall(T.Val(6), T.Val("2"), T.Val(4));
+  T.CheckCall(T.Val(6), T.Val(2), T.Val("4"));
+}
+
+
+TEST(BinopXor) {
+  FunctionTester T("(function(a,b) { return a ^ b; })");
+
+  T.CheckCall(7, 15, 8);
+  T.CheckCall(7, 8, 15);
+  T.CheckCall(T.Val(7), T.Val("8"), T.Val(15));
+  T.CheckCall(T.Val(7), T.Val(8), T.Val("15"));
+}
+
+
+TEST(BinopStrictEqual) {
+  FunctionTester T("(function(a,b) { return a === b; })");
+
+  T.CheckTrue(7, 7);
+  T.CheckFalse(7, 8);
+  T.CheckTrue(7.1, 7.1);
+  T.CheckFalse(7.1, 8.1);
+
+  T.CheckTrue(T.Val("7.1"), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("7.1"));
+  T.CheckFalse(T.Val(7), T.undefined());
+  T.CheckFalse(T.undefined(), T.Val(7));
+
+  CompileRun("var o = { desc : 'I am a singleton' }");
+  T.CheckFalse(T.NewObject("([1])"), T.NewObject("([1])"));
+  T.CheckFalse(T.NewObject("({})"), T.NewObject("({})"));
+  T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)"));
+}
+
+
+TEST(BinopEqual) {
+  FunctionTester T("(function(a,b) { return a == b; })");
+
+  T.CheckTrue(7, 7);
+  T.CheckFalse(7, 8);
+  T.CheckTrue(7.1, 7.1);
+  T.CheckFalse(7.1, 8.1);
+
+  T.CheckTrue(T.Val("7.1"), T.Val("7.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("7.1"));
+
+  CompileRun("var o = { desc : 'I am a singleton' }");
+  T.CheckFalse(T.NewObject("([1])"), T.NewObject("([1])"));
+  T.CheckFalse(T.NewObject("({})"), T.NewObject("({})"));
+  T.CheckTrue(T.NewObject("(o)"), T.NewObject("(o)"));
+}
+
+
+TEST(BinopNotEqual) {
+  FunctionTester T("(function(a,b) { return a != b; })");
+
+  T.CheckFalse(7, 7);
+  T.CheckTrue(7, 8);
+  T.CheckFalse(7.1, 7.1);
+  T.CheckTrue(7.1, 8.1);
+
+  T.CheckFalse(T.Val("7.1"), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("7.1"));
+
+  CompileRun("var o = { desc : 'I am a singleton' }");
+  T.CheckTrue(T.NewObject("([1])"), T.NewObject("([1])"));
+  T.CheckTrue(T.NewObject("({})"), T.NewObject("({})"));
+  T.CheckFalse(T.NewObject("(o)"), T.NewObject("(o)"));
+}
+
+
+TEST(BinopLessThan) {
+  FunctionTester T("(function(a,b) { return a < b; })");
+
+  T.CheckTrue(7, 8);
+  T.CheckFalse(8, 7);
+  T.CheckTrue(-8.1, -8);
+  T.CheckFalse(-8, -8.1);
+  T.CheckFalse(0.111, 0.111);
+
+  T.CheckFalse(T.Val("7.1"), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("6.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("7.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("8.1"));
+}
+
+
+TEST(BinopLessThanEqual) {
+  FunctionTester T("(function(a,b) { return a <= b; })");
+
+  T.CheckTrue(7, 8);
+  T.CheckFalse(8, 7);
+  T.CheckTrue(-8.1, -8);
+  T.CheckFalse(-8, -8.1);
+  T.CheckTrue(0.111, 0.111);
+
+  T.CheckTrue(T.Val("7.1"), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("6.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("7.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("8.1"));
+}
+
+
+TEST(BinopGreaterThan) {
+  FunctionTester T("(function(a,b) { return a > b; })");
+
+  T.CheckFalse(7, 8);
+  T.CheckTrue(8, 7);
+  T.CheckFalse(-8.1, -8);
+  T.CheckTrue(-8, -8.1);
+  T.CheckFalse(0.111, 0.111);
+
+  T.CheckFalse(T.Val("7.1"), T.Val("7.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("6.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("8.1"));
+}
+
+
+TEST(BinopGreaterThanOrEqual) {
+  FunctionTester T("(function(a,b) { return a >= b; })");
+
+  T.CheckFalse(7, 8);
+  T.CheckTrue(8, 7);
+  T.CheckFalse(-8.1, -8);
+  T.CheckTrue(-8, -8.1);
+  T.CheckTrue(0.111, 0.111);
+
+  T.CheckTrue(T.Val("7.1"), T.Val("7.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("6.1"));
+  T.CheckTrue(T.Val(7.1), T.Val("7.1"));
+  T.CheckFalse(T.Val(7.1), T.Val("8.1"));
+}
+
+
+TEST(BinopIn) {
+  FunctionTester T("(function(a,b) { return a in b; })");
+
+  T.CheckTrue(T.Val("x"), T.NewObject("({x:23})"));
+  T.CheckFalse(T.Val("y"), T.NewObject("({x:42})"));
+  T.CheckFalse(T.Val(123), T.NewObject("({x:65})"));
+  T.CheckTrue(T.Val(1), T.NewObject("([1,2,3])"));
+}
+
+
+TEST(BinopInstanceOf) {
+  FunctionTester T("(function(a,b) { return a instanceof b; })");
+
+  T.CheckTrue(T.NewObject("(new Number(23))"), T.NewObject("Number"));
+  T.CheckFalse(T.NewObject("(new Number(23))"), T.NewObject("String"));
+  T.CheckFalse(T.NewObject("(new String('a'))"), T.NewObject("Number"));
+  T.CheckTrue(T.NewObject("(new String('b'))"), T.NewObject("String"));
+  T.CheckFalse(T.Val(1), T.NewObject("Number"));
+  T.CheckFalse(T.Val("abc"), T.NewObject("String"));
+
+  CompileRun("var bound = (function() {}).bind(undefined)");
+  T.CheckTrue(T.NewObject("(new bound())"), T.NewObject("bound"));
+  T.CheckTrue(T.NewObject("(new bound())"), T.NewObject("Object"));
+  T.CheckFalse(T.NewObject("(new bound())"), T.NewObject("Number"));
+}
+
+
+TEST(UnopNot) {
+  FunctionTester T("(function(a) { return !a; })");
+
+  T.CheckCall(T.true_value(), T.false_value(), T.undefined());
+  T.CheckCall(T.false_value(), T.true_value(), T.undefined());
+  T.CheckCall(T.true_value(), T.Val(0.0), T.undefined());
+  T.CheckCall(T.false_value(), T.Val(123), T.undefined());
+  T.CheckCall(T.false_value(), T.Val("x"), T.undefined());
+  T.CheckCall(T.true_value(), T.undefined(), T.undefined());
+  T.CheckCall(T.true_value(), T.nan(), T.undefined());
+}
+
+
+TEST(UnopCountPost) {
+  FunctionTester T("(function(a) { return a++; })");
+
+  T.CheckCall(T.Val(0.0), T.Val(0.0), T.undefined());
+  T.CheckCall(T.Val(2.3), T.Val(2.3), T.undefined());
+  T.CheckCall(T.Val(123), T.Val(123), T.undefined());
+  T.CheckCall(T.Val(7), T.Val("7"), T.undefined());
+  T.CheckCall(T.nan(), T.Val("x"), T.undefined());
+  T.CheckCall(T.nan(), T.undefined(), T.undefined());
+  T.CheckCall(T.Val(1.0), T.true_value(), T.undefined());
+  T.CheckCall(T.Val(0.0), T.false_value(), T.undefined());
+  T.CheckCall(T.nan(), T.nan(), T.undefined());
+}
+
+
+TEST(UnopCountPre) {
+  FunctionTester T("(function(a) { return ++a; })");
+
+  T.CheckCall(T.Val(1.0), T.Val(0.0), T.undefined());
+  T.CheckCall(T.Val(3.3), T.Val(2.3), T.undefined());
+  T.CheckCall(T.Val(124), T.Val(123), T.undefined());
+  T.CheckCall(T.Val(8), T.Val("7"), T.undefined());
+  T.CheckCall(T.nan(), T.Val("x"), T.undefined());
+  T.CheckCall(T.nan(), T.undefined(), T.undefined());
+  T.CheckCall(T.Val(2.0), T.true_value(), T.undefined());
+  T.CheckCall(T.Val(1.0), T.false_value(), T.undefined());
+  T.CheckCall(T.nan(), T.nan(), T.undefined());
+}
+
+
+TEST(PropertyNamedLoad) {
+  FunctionTester T("(function(a,b) { return a.x; })");
+
+  T.CheckCall(T.Val(23), T.NewObject("({x:23})"), T.undefined());
+  T.CheckCall(T.undefined(), T.NewObject("({y:23})"), T.undefined());
+}
+
+
+TEST(PropertyKeyedLoad) {
+  FunctionTester T("(function(a,b) { return a[b]; })");
+
+  T.CheckCall(T.Val(23), T.NewObject("({x:23})"), T.Val("x"));
+  T.CheckCall(T.Val(42), T.NewObject("([23,42,65])"), T.Val(1));
+  T.CheckCall(T.undefined(), T.NewObject("({x:23})"), T.Val("y"));
+  T.CheckCall(T.undefined(), T.NewObject("([23,42,65])"), T.Val(4));
+}
+
+
+TEST(PropertyNamedStore) {
+  FunctionTester T("(function(a) { a.x = 7; return a.x; })");
+
+  T.CheckCall(T.Val(7), T.NewObject("({})"), T.undefined());
+  T.CheckCall(T.Val(7), T.NewObject("({x:23})"), T.undefined());
+}
+
+
+TEST(PropertyKeyedStore) {
+  FunctionTester T("(function(a,b) { a[b] = 7; return a.x; })");
+
+  T.CheckCall(T.Val(7), T.NewObject("({})"), T.Val("x"));
+  T.CheckCall(T.Val(7), T.NewObject("({x:23})"), T.Val("x"));
+  T.CheckCall(T.Val(9), T.NewObject("({x:9})"), T.Val("y"));
+}
+
+
+TEST(PropertyNamedDelete) {
+  FunctionTester T("(function(a) { return delete a.x; })");
+
+  CompileRun("var o = Object.create({}, { x: { value:23 } });");
+  T.CheckTrue(T.NewObject("({x:42})"), T.undefined());
+  T.CheckTrue(T.NewObject("({})"), T.undefined());
+  T.CheckFalse(T.NewObject("(o)"), T.undefined());
+}
+
+
+TEST(PropertyKeyedDelete) {
+  FunctionTester T("(function(a, b) { return delete a[b]; })");
+
+  CompileRun("function getX() { return 'x'; }");
+  CompileRun("var o = Object.create({}, { x: { value:23 } });");
+  T.CheckTrue(T.NewObject("({x:42})"), T.Val("x"));
+  T.CheckFalse(T.NewObject("(o)"), T.Val("x"));
+  T.CheckFalse(T.NewObject("(o)"), T.NewObject("({toString:getX})"));
+}
+
+
+TEST(GlobalLoad) {
+  FunctionTester T("(function() { return g; })");
+
+  T.CheckThrows(T.undefined(), T.undefined());
+  CompileRun("var g = 23;");
+  T.CheckCall(T.Val(23));
+}
+
+
+TEST(GlobalStoreSloppy) {
+  FunctionTester T("(function(a,b) { g = a + b; return g; })");
+
+  T.CheckCall(T.Val(33), T.Val(22), T.Val(11));
+  CompileRun("delete g");
+  CompileRun("const g = 23");
+  T.CheckCall(T.Val(23), T.Val(55), T.Val(44));
+}
+
+
+TEST(GlobalStoreStrict) {
+  FunctionTester T("(function(a,b) { 'use strict'; g = a + b; return g; })");
+
+  T.CheckThrows(T.Val(22), T.Val(11));
+  CompileRun("var g = 'a global variable';");
+  T.CheckCall(T.Val(33), T.Val(22), T.Val(11));
+}
+
+
+TEST(ContextLoad) {
+  FunctionTester T("(function(a,b) { (function(){a}); return a + b; })");
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ContextStore) {
+  FunctionTester T("(function(a,b) { (function(){x}); var x = a; return x; })");
+
+  T.CheckCall(T.Val(23), T.Val(23), T.undefined());
+  T.CheckCall(T.Val("a"), T.Val("a"), T.undefined());
+}
+
+
+TEST(LookupLoad) {
+  FunctionTester T("(function(a,b) { with(a) { return x + b; } })");
+
+  T.CheckCall(T.Val(24), T.NewObject("({x:23})"), T.Val(1));
+  T.CheckCall(T.Val(32), T.NewObject("({x:23, b:9})"), T.Val(2));
+  T.CheckCall(T.Val(45), T.NewObject("({__proto__:{x:42}})"), T.Val(3));
+  T.CheckCall(T.Val(69), T.NewObject("({get x() { return 65; }})"), T.Val(4));
+}
+
+
+TEST(LookupStore) {
+  FunctionTester T("(function(a,b) { var x; with(a) { x = b; } return x; })");
+
+  T.CheckCall(T.undefined(), T.NewObject("({x:23})"), T.Val(1));
+  T.CheckCall(T.Val(2), T.NewObject("({y:23})"), T.Val(2));
+  T.CheckCall(T.Val(23), T.NewObject("({b:23})"), T.Val(3));
+  T.CheckCall(T.undefined(), T.NewObject("({__proto__:{x:42}})"), T.Val(4));
+}
+
+
+TEST(BlockLoadStore) {
+  FLAG_harmony_scoping = true;
+  FunctionTester T("(function(a) { 'use strict'; { let x = a+a; return x; }})");
+
+  T.CheckCall(T.Val(46), T.Val(23));
+  T.CheckCall(T.Val("aa"), T.Val("a"));
+}
+
+
+TEST(BlockLoadStoreNested) {
+  FLAG_harmony_scoping = true;
+  const char* src =
+      "(function(a,b) {"
+      "'use strict';"
+      "{ let x = a, y = a;"
+      "  { let y = b;"
+      "    return x + y;"
+      "  }"
+      "}})";
+  FunctionTester T(src);
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ObjectLiteralComputed) {
+  FunctionTester T("(function(a,b) { o = { x:a+b }; return o.x; })");
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ObjectLiteralNonString) {
+  FunctionTester T("(function(a,b) { o = { 7:a+b }; return o[7]; })");
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(ObjectLiteralPrototype) {
+  FunctionTester T("(function(a) { o = { __proto__:a }; return o.x; })");
+
+  T.CheckCall(T.Val(23), T.NewObject("({x:23})"), T.undefined());
+  T.CheckCall(T.undefined(), T.NewObject("({y:42})"), T.undefined());
+}
+
+
+TEST(ObjectLiteralGetter) {
+  FunctionTester T("(function(a) { o = { get x() {return a} }; return o.x; })");
+
+  T.CheckCall(T.Val(23), T.Val(23), T.undefined());
+  T.CheckCall(T.Val("x"), T.Val("x"), T.undefined());
+}
+
+
+TEST(ArrayLiteral) {
+  FunctionTester T("(function(a,b) { o = [1, a + b, 3]; return o[1]; })");
+
+  T.CheckCall(T.Val(65), T.Val(23), T.Val(42));
+  T.CheckCall(T.Val("ab"), T.Val("a"), T.Val("b"));
+}
+
+
+TEST(RegExpLiteral) {
+  FunctionTester T("(function(a) { o = /b/; return o.test(a); })");
+
+  T.CheckTrue(T.Val("abc"));
+  T.CheckFalse(T.Val("xyz"));
+}
diff --git a/test/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc
new file mode 100644
index 0000000..985e0f8
--- /dev/null
+++ b/test/cctest/compiler/test-run-machops.cc
@@ -0,0 +1,4245 @@
+// 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 <functional>
+#include <limits>
+
+#include "src/base/bits.h"
+#include "src/compiler/generic-node-inl.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+#if V8_TURBOFAN_TARGET
+
+using namespace v8::base;
+
+#define CHECK_UINT32_EQ(x, y) \
+  CHECK_EQ(static_cast<int32_t>(x), static_cast<int32_t>(y))
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+typedef RawMachineAssembler::Label MLabel;
+
+TEST(RunInt32Add) {
+  RawMachineAssemblerTester<int32_t> m;
+  Node* add = m.Int32Add(m.Int32Constant(0), m.Int32Constant(1));
+  m.Return(add);
+  CHECK_EQ(1, m.Call());
+}
+
+
+static Node* Int32Input(RawMachineAssemblerTester<int32_t>* m, int index) {
+  switch (index) {
+    case 0:
+      return m->Parameter(0);
+    case 1:
+      return m->Parameter(1);
+    case 2:
+      return m->Int32Constant(0);
+    case 3:
+      return m->Int32Constant(1);
+    case 4:
+      return m->Int32Constant(-1);
+    case 5:
+      return m->Int32Constant(0xff);
+    case 6:
+      return m->Int32Constant(0x01234567);
+    case 7:
+      return m->Load(kMachInt32, m->PointerConstant(NULL));
+    default:
+      return NULL;
+  }
+}
+
+
+TEST(CodeGenInt32Binop) {
+  RawMachineAssemblerTester<void> m;
+
+  const Operator* ops[] = {
+      m.machine()->Word32And(),      m.machine()->Word32Or(),
+      m.machine()->Word32Xor(),      m.machine()->Word32Shl(),
+      m.machine()->Word32Shr(),      m.machine()->Word32Sar(),
+      m.machine()->Word32Equal(),    m.machine()->Int32Add(),
+      m.machine()->Int32Sub(),       m.machine()->Int32Mul(),
+      m.machine()->Int32Div(),       m.machine()->Int32UDiv(),
+      m.machine()->Int32Mod(),       m.machine()->Int32UMod(),
+      m.machine()->Int32LessThan(),  m.machine()->Int32LessThanOrEqual(),
+      m.machine()->Uint32LessThan(), m.machine()->Uint32LessThanOrEqual(),
+      NULL};
+
+  for (int i = 0; ops[i] != NULL; i++) {
+    for (int j = 0; j < 8; j++) {
+      for (int k = 0; k < 8; k++) {
+        RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+        Node* a = Int32Input(&m, j);
+        Node* b = Int32Input(&m, k);
+        m.Return(m.NewNode(ops[i], a, b));
+        m.GenerateCode();
+      }
+    }
+  }
+}
+
+
+TEST(RunGoto) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 99999;
+
+  MLabel next;
+  m.Goto(&next);
+  m.Bind(&next);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunGotoMultiple) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 9999977;
+
+  MLabel labels[10];
+  for (size_t i = 0; i < arraysize(labels); i++) {
+    m.Goto(&labels[i]);
+    m.Bind(&labels[i]);
+  }
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunBranch) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 999777;
+
+  MLabel blocka, blockb;
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(0 - constant));
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunRedundantBranch1) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 944777;
+
+  MLabel blocka;
+  m.Branch(m.Int32Constant(0), &blocka, &blocka);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunRedundantBranch2) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 955777;
+
+  MLabel blocka, blockb;
+  m.Branch(m.Int32Constant(0), &blocka, &blocka);
+  m.Bind(&blockb);
+  m.Goto(&blocka);
+  m.Bind(&blocka);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunRedundantBranch3) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 966777;
+
+  MLabel blocka, blockb, blockc;
+  m.Branch(m.Int32Constant(0), &blocka, &blockc);
+  m.Bind(&blocka);
+  m.Branch(m.Int32Constant(0), &blockb, &blockb);
+  m.Bind(&blockc);
+  m.Goto(&blockb);
+  m.Bind(&blockb);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunDiamond2) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  int constant = 995666;
+
+  MLabel blocka, blockb, end;
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&end);
+  m.Bind(&blockb);
+  m.Goto(&end);
+  m.Bind(&end);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+TEST(RunLoop) {
+  RawMachineAssemblerTester<int32_t> m;
+  int constant = 999555;
+
+  MLabel header, body, exit;
+  m.Goto(&header);
+  m.Bind(&header);
+  m.Branch(m.Int32Constant(0), &body, &exit);
+  m.Bind(&body);
+  m.Goto(&header);
+  m.Bind(&exit);
+  m.Return(m.Int32Constant(constant));
+
+  CHECK_EQ(constant, m.Call());
+}
+
+
+template <typename R>
+static void BuildDiamondPhi(RawMachineAssemblerTester<R>* m, Node* cond_node,
+                            MachineType type, Node* true_node,
+                            Node* false_node) {
+  MLabel blocka, blockb;
+  MLabel* end = m->Exit();
+  m->Branch(cond_node, &blocka, &blockb);
+  m->Bind(&blocka);
+  m->Goto(end);
+  m->Bind(&blockb);
+  m->Goto(end);
+
+  m->Bind(end);
+  Node* phi = m->Phi(type, true_node, false_node);
+  m->Return(phi);
+}
+
+
+TEST(RunDiamondPhiConst) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  int false_val = 0xFF666;
+  int true_val = 0x00DDD;
+  Node* true_node = m.Int32Constant(true_val);
+  Node* false_node = m.Int32Constant(false_val);
+  BuildDiamondPhi(&m, m.Parameter(0), kMachInt32, true_node, false_node);
+  CHECK_EQ(false_val, m.Call(0));
+  CHECK_EQ(true_val, m.Call(1));
+}
+
+
+TEST(RunDiamondPhiNumber) {
+  RawMachineAssemblerTester<Object*> m(kMachInt32);
+  double false_val = -11.1;
+  double true_val = 200.1;
+  Node* true_node = m.NumberConstant(true_val);
+  Node* false_node = m.NumberConstant(false_val);
+  BuildDiamondPhi(&m, m.Parameter(0), kMachAnyTagged, true_node, false_node);
+  m.CheckNumber(false_val, m.Call(0));
+  m.CheckNumber(true_val, m.Call(1));
+}
+
+
+TEST(RunDiamondPhiString) {
+  RawMachineAssemblerTester<Object*> m(kMachInt32);
+  const char* false_val = "false";
+  const char* true_val = "true";
+  Node* true_node = m.StringConstant(true_val);
+  Node* false_node = m.StringConstant(false_val);
+  BuildDiamondPhi(&m, m.Parameter(0), kMachAnyTagged, true_node, false_node);
+  m.CheckString(false_val, m.Call(0));
+  m.CheckString(true_val, m.Call(1));
+}
+
+
+TEST(RunDiamondPhiParam) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+  BuildDiamondPhi(&m, m.Parameter(0), kMachInt32, m.Parameter(1),
+                  m.Parameter(2));
+  int32_t c1 = 0x260cb75a;
+  int32_t c2 = 0xcd3e9c8b;
+  int result = m.Call(0, c1, c2);
+  CHECK_EQ(c2, result);
+  result = m.Call(1, c1, c2);
+  CHECK_EQ(c1, result);
+}
+
+
+TEST(RunLoopPhiConst) {
+  RawMachineAssemblerTester<int32_t> m;
+  int true_val = 0x44000;
+  int false_val = 0x00888;
+
+  Node* cond_node = m.Int32Constant(0);
+  Node* true_node = m.Int32Constant(true_val);
+  Node* false_node = m.Int32Constant(false_val);
+
+  // x = false_val; while(false) { x = true_val; } return x;
+  MLabel body, header;
+  MLabel* end = m.Exit();
+
+  m.Goto(&header);
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, false_node, true_node);
+  m.Branch(cond_node, &body, end);
+  m.Bind(&body);
+  m.Goto(&header);
+  m.Bind(end);
+  m.Return(phi);
+
+  CHECK_EQ(false_val, m.Call());
+}
+
+
+TEST(RunLoopPhiParam) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+
+  MLabel blocka, blockb;
+  MLabel* end = m.Exit();
+
+  m.Goto(&blocka);
+
+  m.Bind(&blocka);
+  Node* phi = m.Phi(kMachInt32, m.Parameter(1), m.Parameter(2));
+  Node* cond = m.Phi(kMachInt32, m.Parameter(0), m.Int32Constant(0));
+  m.Branch(cond, &blockb, end);
+
+  m.Bind(&blockb);
+  m.Goto(&blocka);
+
+  m.Bind(end);
+  m.Return(phi);
+
+  int32_t c1 = 0xa81903b4;
+  int32_t c2 = 0x5a1207da;
+  int result = m.Call(0, c1, c2);
+  CHECK_EQ(c1, result);
+  result = m.Call(1, c1, c2);
+  CHECK_EQ(c2, result);
+}
+
+
+TEST(RunLoopPhiInduction) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  int false_val = 0x10777;
+
+  // x = false_val; while(false) { x++; } return x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+  Node* false_node = m.Int32Constant(false_val);
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, false_node, false_node);
+  m.Branch(m.Int32Constant(0), &body, end);
+
+  m.Bind(&body);
+  Node* add = m.Int32Add(phi, m.Int32Constant(1));
+  phi->ReplaceInput(1, add);
+  m.Goto(&header);
+
+  m.Bind(end);
+  m.Return(phi);
+
+  CHECK_EQ(false_val, m.Call());
+}
+
+
+TEST(RunLoopIncrement) {
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+
+  // x = 0; while(x ^ param) { x++; } return x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+  Node* zero = m.Int32Constant(0);
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, zero, zero);
+  m.Branch(m.WordXor(phi, bt.param0), &body, end);
+
+  m.Bind(&body);
+  phi->ReplaceInput(1, m.Int32Add(phi, m.Int32Constant(1)));
+  m.Goto(&header);
+
+  m.Bind(end);
+  bt.AddReturn(phi);
+
+  CHECK_EQ(11, bt.call(11, 0));
+  CHECK_EQ(110, bt.call(110, 0));
+  CHECK_EQ(176, bt.call(176, 0));
+}
+
+
+TEST(RunLoopIncrement2) {
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+
+  // x = 0; while(x < param) { x++; } return x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+  Node* zero = m.Int32Constant(0);
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, zero, zero);
+  m.Branch(m.Int32LessThan(phi, bt.param0), &body, end);
+
+  m.Bind(&body);
+  phi->ReplaceInput(1, m.Int32Add(phi, m.Int32Constant(1)));
+  m.Goto(&header);
+
+  m.Bind(end);
+  bt.AddReturn(phi);
+
+  CHECK_EQ(11, bt.call(11, 0));
+  CHECK_EQ(110, bt.call(110, 0));
+  CHECK_EQ(176, bt.call(176, 0));
+  CHECK_EQ(0, bt.call(-200, 0));
+}
+
+
+TEST(RunLoopIncrement3) {
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+
+  // x = 0; while(x < param) { x++; } return x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+  Node* zero = m.Int32Constant(0);
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, zero, zero);
+  m.Branch(m.Uint32LessThan(phi, bt.param0), &body, end);
+
+  m.Bind(&body);
+  phi->ReplaceInput(1, m.Int32Add(phi, m.Int32Constant(1)));
+  m.Goto(&header);
+
+  m.Bind(end);
+  bt.AddReturn(phi);
+
+  CHECK_EQ(11, bt.call(11, 0));
+  CHECK_EQ(110, bt.call(110, 0));
+  CHECK_EQ(176, bt.call(176, 0));
+  CHECK_EQ(200, bt.call(200, 0));
+}
+
+
+TEST(RunLoopDecrement) {
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+
+  // x = param; while(x) { x--; } return x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, bt.param0, m.Int32Constant(0));
+  m.Branch(phi, &body, end);
+
+  m.Bind(&body);
+  phi->ReplaceInput(1, m.Int32Sub(phi, m.Int32Constant(1)));
+  m.Goto(&header);
+
+  m.Bind(end);
+  bt.AddReturn(phi);
+
+  CHECK_EQ(0, bt.call(11, 0));
+  CHECK_EQ(0, bt.call(110, 0));
+  CHECK_EQ(0, bt.call(197, 0));
+}
+
+
+TEST(RunLoopIncrementFloat64) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  // x = -3.0; while(x < 10) { x = x + 0.5; } return (int) x;
+  MLabel header, body;
+  MLabel* end = m.Exit();
+  Node* minus_3 = m.Float64Constant(-3.0);
+  Node* ten = m.Float64Constant(10.0);
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachFloat64, minus_3, ten);
+  m.Branch(m.Float64LessThan(phi, ten), &body, end);
+
+  m.Bind(&body);
+  phi->ReplaceInput(1, m.Float64Add(phi, m.Float64Constant(0.5)));
+  m.Goto(&header);
+
+  m.Bind(end);
+  m.Return(m.ChangeFloat64ToInt32(phi));
+
+  CHECK_EQ(10, m.Call());
+}
+
+
+TEST(RunLoadInt32) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  int32_t p1 = 0;  // loads directly from this location.
+  m.Return(m.LoadFromPointer(&p1, kMachInt32));
+
+  FOR_INT32_INPUTS(i) {
+    p1 = *i;
+    CHECK_EQ(p1, m.Call());
+  }
+}
+
+
+TEST(RunLoadInt32Offset) {
+  int32_t p1 = 0;  // loads directly from this location.
+
+  int32_t offsets[] = {-2000000, -100, -101, 1,          3,
+                       7,        120,  2000, 2000000000, 0xff};
+
+  for (size_t i = 0; i < arraysize(offsets); i++) {
+    RawMachineAssemblerTester<int32_t> m;
+    int32_t offset = offsets[i];
+    byte* pointer = reinterpret_cast<byte*>(&p1) - offset;
+    // generate load [#base + #index]
+    m.Return(m.LoadFromPointer(pointer, kMachInt32, offset));
+
+    FOR_INT32_INPUTS(j) {
+      p1 = *j;
+      CHECK_EQ(p1, m.Call());
+    }
+  }
+}
+
+
+TEST(RunLoadStoreFloat64Offset) {
+  double p1 = 0;  // loads directly from this location.
+  double p2 = 0;  // and stores directly into this location.
+
+  FOR_INT32_INPUTS(i) {
+    int32_t magic = 0x2342aabb + *i * 3;
+    RawMachineAssemblerTester<int32_t> m;
+    int32_t offset = *i;
+    byte* from = reinterpret_cast<byte*>(&p1) - offset;
+    byte* to = reinterpret_cast<byte*>(&p2) - offset;
+    // generate load [#base + #index]
+    Node* load =
+        m.Load(kMachFloat64, m.PointerConstant(from), m.Int32Constant(offset));
+    m.Store(kMachFloat64, m.PointerConstant(to), m.Int32Constant(offset), load);
+    m.Return(m.Int32Constant(magic));
+
+    FOR_FLOAT64_INPUTS(j) {
+      p1 = *j;
+      p2 = *j - 5;
+      CHECK_EQ(magic, m.Call());
+      CHECK_EQ(p1, p2);
+    }
+  }
+}
+
+
+TEST(RunInt32AddP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+
+  bt.AddReturn(m.Int32Add(bt.param0, bt.param1));
+
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      // Use uint32_t because signed overflow is UB in C.
+      int expected = static_cast<int32_t>(*i + *j);
+      CHECK_EQ(expected, bt.call(*i, *j));
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32SarP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
+    m.Return(m.Int32Add(m.Parameter(0),
+                        m.Word32Sar(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = *i + (*j >> shift);
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Add(m.Word32Sar(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = (*i >> shift) + *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32ShlP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
+    m.Return(m.Int32Add(m.Parameter(0),
+                        m.Word32Shl(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = *i + (*j << shift);
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Add(m.Word32Shl(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = (*i << shift) + *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddAndWord32ShrP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Add(m.Parameter(0),
+                        m.Word32Shr(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = *i + (*j >> shift);
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Add(m.Word32Shr(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = (*i >> shift) + *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddInBranch) {
+  static const int32_t constant = 987654321;
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32Equal(m.Int32Add(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i + *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32NotEqual(m.Int32Add(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i + *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Int32Add(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i + *j) == 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32NotEqual(m.Int32Add(m.Int32Constant(*i), m.Parameter(0)),
+                                m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i + *j) != 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Int32Add(m.Parameter(0),
+                                        m.NewNode(shops[n], m.Parameter(1),
+                                                  m.Parameter(2))),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = ((*i + right) == 0) ? constant : 0 - constant;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32AddInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Add(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i + *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Int32Add(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i + *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Int32Add(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i + *j) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Int32Add(m.Parameter(0), m.Int32Constant(*i)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*j + *i) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      m.Return(m.Word32Equal(
+          m.Int32Add(m.Parameter(0),
+                     m.NewNode(shops[n], m.Parameter(1), m.Parameter(2))),
+          m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = (*i + right) == 0;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Uint32BinopTester bt(&m);
+
+  m.Return(m.Int32Sub(bt.param0, bt.param1));
+
+  FOR_UINT32_INPUTS(i) {
+    FOR_UINT32_INPUTS(j) {
+      uint32_t expected = static_cast<int32_t>(*i - *j);
+      CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+    }
+  }
+}
+
+
+TEST(RunInt32SubImm) {
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Int32Sub(m.Int32Constant(*i), m.Parameter(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i - *j;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Int32Sub(m.Parameter(0), m.Int32Constant(*i)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *j - *i;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubAndWord32SarP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
+    m.Return(m.Int32Sub(m.Parameter(0),
+                        m.Word32Sar(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          int32_t expected = *i - (*j >> shift);
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Sub(m.Word32Sar(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          int32_t expected = (*i >> shift) - *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubAndWord32ShlP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachUint32);
+    m.Return(m.Int32Sub(m.Parameter(0),
+                        m.Word32Shl(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          int32_t expected = *i - (*j << shift);
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachUint32, kMachUint32);
+    m.Return(m.Int32Sub(m.Word32Shl(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = (*i << shift) - *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubAndWord32ShrP) {
+  {
+    RawMachineAssemblerTester<uint32_t> m(kMachUint32, kMachUint32,
+                                          kMachUint32);
+    m.Return(m.Int32Sub(m.Parameter(0),
+                        m.Word32Shr(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = *i - (*j >> shift);
+          CHECK_UINT32_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<uint32_t> m(kMachUint32, kMachUint32,
+                                          kMachUint32);
+    m.Return(m.Int32Sub(m.Word32Shr(m.Parameter(0), m.Parameter(1)),
+                        m.Parameter(2)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          // Use uint32_t because signed overflow is UB in C.
+          int32_t expected = (*i >> shift) - *k;
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubInBranch) {
+  static const int constant = 987654321;
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32Equal(m.Int32Sub(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i - *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32NotEqual(m.Int32Sub(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i - *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Int32Sub(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i - *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32NotEqual(m.Int32Sub(m.Int32Constant(*i), m.Parameter(0)),
+                                m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i - *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Int32Sub(m.Parameter(0),
+                                        m.NewNode(shops[n], m.Parameter(1),
+                                                  m.Parameter(2))),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = ((*i - right) == 0) ? constant : 0 - constant;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32SubInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Sub(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i - *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Int32Sub(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i - *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Int32Sub(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i - *j) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Int32Sub(m.Parameter(0), m.Int32Constant(*i)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*j - *i) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      m.Return(m.Word32Equal(
+          m.Int32Sub(m.Parameter(0),
+                     m.NewNode(shops[n], m.Parameter(1), m.Parameter(2))),
+          m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = (*i - right) == 0;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32MulP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Mul(bt.param0, bt.param1));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int expected = static_cast<int32_t>(*i * *j);
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Mul(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i * *j;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32MulImm) {
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Int32Mul(m.Int32Constant(*i), m.Parameter(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i * *j;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Int32Mul(m.Parameter(0), m.Int32Constant(*i)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *j * *i;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32MulAndInt32AddP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(
+        m.Int32Add(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          int32_t p0 = *i;
+          int32_t p1 = *j;
+          int32_t p2 = *k;
+          int expected = p0 + static_cast<int32_t>(p1 * p2);
+          CHECK_EQ(expected, m.Call(p0, p1, p2));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachInt32);
+    m.Return(
+        m.Int32Add(m.Int32Mul(m.Parameter(0), m.Parameter(1)), m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          int32_t p0 = *i;
+          int32_t p1 = *j;
+          int32_t p2 = *k;
+          int expected = static_cast<int32_t>(p0 * p1) + p2;
+          CHECK_EQ(expected, m.Call(p0, p1, p2));
+        }
+      }
+    }
+  }
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m;
+      Int32BinopTester bt(&m);
+      bt.AddReturn(
+          m.Int32Add(m.Int32Constant(*i), m.Int32Mul(bt.param0, bt.param1)));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          int32_t p0 = *j;
+          int32_t p1 = *k;
+          int expected = *i + static_cast<int32_t>(p0 * p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32MulAndInt32SubP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32, kMachInt32);
+    m.Return(
+        m.Int32Sub(m.Parameter(0), m.Int32Mul(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          uint32_t p0 = *i;
+          int32_t p1 = *j;
+          int32_t p2 = *k;
+          // Use uint32_t because signed overflow is UB in C.
+          int expected = p0 - static_cast<uint32_t>(p1 * p2);
+          CHECK_EQ(expected, m.Call(p0, p1, p2));
+        }
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m;
+      Int32BinopTester bt(&m);
+      bt.AddReturn(
+          m.Int32Sub(m.Int32Constant(*i), m.Int32Mul(bt.param0, bt.param1)));
+      FOR_INT32_INPUTS(j) {
+        FOR_INT32_INPUTS(k) {
+          int32_t p0 = *j;
+          int32_t p1 = *k;
+          // Use uint32_t because signed overflow is UB in C.
+          int expected = *i - static_cast<uint32_t>(p0 * p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32DivP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Div(bt.param0, bt.param1));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int p0 = *i;
+        int p1 = *j;
+        if (p1 != 0 && (static_cast<uint32_t>(p0) != 0x80000000 || p1 != -1)) {
+          int expected = static_cast<int32_t>(p0 / p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Add(bt.param0, m.Int32Div(bt.param0, bt.param1)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int p0 = *i;
+        int p1 = *j;
+        if (p1 != 0 && (static_cast<uint32_t>(p0) != 0x80000000 || p1 != -1)) {
+          int expected = static_cast<int32_t>(p0 + (p0 / p1));
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32UDivP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32UDiv(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t p0 = *i;
+        uint32_t p1 = *j;
+        if (p1 != 0) {
+          uint32_t expected = static_cast<uint32_t>(p0 / p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Add(bt.param0, m.Int32UDiv(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t p0 = *i;
+        uint32_t p1 = *j;
+        if (p1 != 0) {
+          uint32_t expected = static_cast<uint32_t>(p0 + (p0 / p1));
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32ModP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Mod(bt.param0, bt.param1));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int p0 = *i;
+        int p1 = *j;
+        if (p1 != 0 && (static_cast<uint32_t>(p0) != 0x80000000 || p1 != -1)) {
+          int expected = static_cast<int32_t>(p0 % p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Add(bt.param0, m.Int32Mod(bt.param0, bt.param1)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int p0 = *i;
+        int p1 = *j;
+        if (p1 != 0 && (static_cast<uint32_t>(p0) != 0x80000000 || p1 != -1)) {
+          int expected = static_cast<int32_t>(p0 + (p0 % p1));
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunInt32UModP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32UMod(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t p0 = *i;
+        uint32_t p1 = *j;
+        if (p1 != 0) {
+          uint32_t expected = static_cast<uint32_t>(p0 % p1);
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Int32Add(bt.param0, m.Int32UMod(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t p0 = *i;
+        uint32_t p1 = *j;
+        if (p1 != 0) {
+          uint32_t expected = static_cast<uint32_t>(p0 + (p0 % p1));
+          CHECK_EQ(expected, bt.call(p0, p1));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32And(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i & *j;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32And(bt.param0, m.Word32Not(bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i & ~(*j);
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32And(m.Word32Not(bt.param0), bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = ~(*i) & *j;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndAndWord32ShlP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Shl(bt.param0, m.Word32And(bt.param1, m.Int32Constant(0x1f))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i << (*j & 0x1f);
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Shl(bt.param0, m.Word32And(m.Int32Constant(0x1f), bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i << (0x1f & *j);
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndAndWord32ShrP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Shr(bt.param0, m.Word32And(bt.param1, m.Int32Constant(0x1f))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i >> (*j & 0x1f);
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Shr(bt.param0, m.Word32And(m.Int32Constant(0x1f), bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i >> (0x1f & *j);
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndAndWord32SarP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Sar(bt.param0, m.Word32And(bt.param1, m.Int32Constant(0x1f))));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = *i >> (*j & 0x1f);
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Sar(bt.param0, m.Word32And(m.Int32Constant(0x1f), bt.param1)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        uint32_t expected = *i >> (0x1f & *j);
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndImm) {
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32And(m.Int32Constant(*i), m.Parameter(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i & *j;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32And(m.Int32Constant(*i), m.Word32Not(m.Parameter(0))));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i & ~(*j);
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndInBranch) {
+  static const int constant = 987654321;
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32Equal(m.Word32And(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i & *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32NotEqual(m.Word32And(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i & *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32And(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i & *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(
+          m.Word32NotEqual(m.Word32And(m.Int32Constant(*i), m.Parameter(0)),
+                           m.Int32Constant(0)),
+          &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i & *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32And(m.Parameter(0),
+                                         m.NewNode(shops[n], m.Parameter(1),
+                                                   m.Parameter(2))),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = ((*i & right) == 0) ? constant : 0 - constant;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32AndInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32And(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i & *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32And(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i & *j) == 0;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Word32And(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i & *j) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Word32And(m.Parameter(0), m.Int32Constant(*i)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*j & *i) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32OrP) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Or(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i | *j;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Or(bt.param0, m.Word32Not(bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i | ~(*j);
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Or(m.Word32Not(bt.param0), bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = ~(*i) | *j;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32OrImm) {
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Or(m.Int32Constant(*i), m.Parameter(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i | *j;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Or(m.Int32Constant(*i), m.Word32Not(m.Parameter(0))));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i | ~(*j);
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32OrInBranch) {
+  static const int constant = 987654321;
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32Equal(m.Word32Or(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = (*i | *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32NotEqual(m.Word32Or(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = (*i | *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32Or(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = (*i | *j) == 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_INT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32NotEqual(m.Word32Or(m.Int32Constant(*i), m.Parameter(0)),
+                                m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = (*i | *j) != 0 ? constant : 0 - constant;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32Or(m.Parameter(0),
+                                        m.NewNode(shops[n], m.Parameter(1),
+                                                  m.Parameter(2))),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = ((*i | right) == 0) ? constant : 0 - constant;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32OrInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32Or(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i | *j) == 0;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32Or(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = (*i | *j) == 0;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Word32Or(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i | *j) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Equal(m.Word32Or(m.Parameter(0), m.Int32Constant(*i)),
+                             m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*j | *i) == 0;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32XorP) {
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(m.Word32Xor(m.Int32Constant(*i), m.Parameter(0)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i ^ *j;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Xor(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = *i ^ *j;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Xor(bt.param0, m.Word32Not(bt.param1)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = *i ^ ~(*j);
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Xor(m.Word32Not(bt.param0), bt.param1));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = ~(*i) ^ *j;
+        CHECK_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Xor(m.Int32Constant(*i), m.Word32Not(m.Parameter(0))));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *i ^ ~(*j);
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32XorInBranch) {
+  static const uint32_t constant = 987654321;
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32Equal(m.Word32Xor(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i ^ *j) == 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    MLabel blocka, blockb;
+    m.Branch(
+        m.Word32NotEqual(m.Word32Xor(bt.param0, bt.param1), m.Int32Constant(0)),
+        &blocka, &blockb);
+    m.Bind(&blocka);
+    bt.AddReturn(m.Int32Constant(constant));
+    m.Bind(&blockb);
+    bt.AddReturn(m.Int32Constant(0 - constant));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i ^ *j) != 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, bt.call(*i, *j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32Xor(m.Int32Constant(*i), m.Parameter(0)),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i ^ *j) == 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    FOR_UINT32_INPUTS(i) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(
+          m.Word32NotEqual(m.Word32Xor(m.Int32Constant(*i), m.Parameter(0)),
+                           m.Int32Constant(0)),
+          &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = (*i ^ *j) != 0 ? constant : 0 - constant;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<void> m;
+    const Operator* shops[] = {m.machine()->Word32Sar(),
+                               m.machine()->Word32Shl(),
+                               m.machine()->Word32Shr()};
+    for (size_t n = 0; n < arraysize(shops); n++) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachInt32,
+                                           kMachUint32);
+      MLabel blocka, blockb;
+      m.Branch(m.Word32Equal(m.Word32Xor(m.Parameter(0),
+                                         m.NewNode(shops[n], m.Parameter(1),
+                                                   m.Parameter(2))),
+                             m.Int32Constant(0)),
+               &blocka, &blockb);
+      m.Bind(&blocka);
+      m.Return(m.Int32Constant(constant));
+      m.Bind(&blockb);
+      m.Return(m.Int32Constant(0 - constant));
+      FOR_UINT32_INPUTS(i) {
+        FOR_INT32_INPUTS(j) {
+          FOR_UINT32_SHIFTS(shift) {
+            int32_t right;
+            switch (shops[n]->opcode()) {
+              default:
+                UNREACHABLE();
+              case IrOpcode::kWord32Sar:
+                right = *j >> shift;
+                break;
+              case IrOpcode::kWord32Shl:
+                right = *j << shift;
+                break;
+              case IrOpcode::kWord32Shr:
+                right = static_cast<uint32_t>(*j) >> shift;
+                break;
+            }
+            int32_t expected = ((*i ^ right) == 0) ? constant : 0 - constant;
+            CHECK_EQ(expected, m.Call(*i, *j, shift));
+          }
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32ShlP) {
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Shl(m.Parameter(0), m.Int32Constant(shift)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *j << shift;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Shl(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = *i << shift;
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32ShlInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32Shl(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == (*i << shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32Shl(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == (*i << shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Int32Constant(0),
+                        m.Word32Shl(m.Parameter(0), m.Int32Constant(shift))));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == (*i << shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Word32Shl(m.Parameter(0), m.Int32Constant(shift)),
+                        m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == (*i << shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32ShrP) {
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<uint32_t> m(kMachUint32);
+      m.Return(m.Word32Shr(m.Parameter(0), m.Int32Constant(shift)));
+      FOR_UINT32_INPUTS(j) {
+        uint32_t expected = *j >> shift;
+        CHECK_UINT32_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Shr(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = *i >> shift;
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+    CHECK_EQ(0x00010000, bt.call(0x80000000, 15));
+  }
+}
+
+
+TEST(RunWord32ShrInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32Shr(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == (*i >> shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32Shr(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == (*i >> shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Int32Constant(0),
+                        m.Word32Shr(m.Parameter(0), m.Int32Constant(shift))));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == (*i >> shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Word32Shr(m.Parameter(0), m.Int32Constant(shift)),
+                        m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == (*i >> shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32SarP) {
+  {
+    FOR_INT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      m.Return(m.Word32Sar(m.Parameter(0), m.Int32Constant(shift)));
+      FOR_INT32_INPUTS(j) {
+        int32_t expected = *j >> shift;
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Sar(bt.param0, bt.param1));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_SHIFTS(shift) {
+        int32_t expected = *i >> shift;
+        CHECK_EQ(expected, bt.call(*i, shift));
+      }
+    }
+    CHECK_EQ(0xFFFF0000, bt.call(0x80000000, 15));
+  }
+}
+
+
+TEST(RunWord32SarInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32Sar(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_SHIFTS(shift) {
+        int32_t expected = 0 == (*i >> shift);
+        CHECK_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Int32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32Sar(bt.param0, bt.param1)));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_SHIFTS(shift) {
+        int32_t expected = 0 == (*i >> shift);
+        CHECK_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    FOR_INT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      m.Return(
+          m.Word32Equal(m.Int32Constant(0),
+                        m.Word32Sar(m.Parameter(0), m.Int32Constant(shift))));
+      FOR_INT32_INPUTS(i) {
+        int32_t expected = 0 == (*i >> shift);
+        CHECK_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+  {
+    FOR_INT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      m.Return(
+          m.Word32Equal(m.Word32Sar(m.Parameter(0), m.Int32Constant(shift)),
+                        m.Int32Constant(0)));
+      FOR_INT32_INPUTS(i) {
+        uint32_t expected = 0 == (*i >> shift);
+        CHECK_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32RorP) {
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(m.Word32Ror(m.Parameter(0), m.Int32Constant(shift)));
+      FOR_UINT32_INPUTS(j) {
+        int32_t expected = bits::RotateRight32(*j, shift);
+        CHECK_EQ(expected, m.Call(*j));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(m.Word32Ror(bt.param0, bt.param1));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = bits::RotateRight32(*i, shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32RorInComparison) {
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Word32Ror(bt.param0, bt.param1), m.Int32Constant(0)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == bits::RotateRight32(*i, shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Uint32BinopTester bt(&m);
+    bt.AddReturn(
+        m.Word32Equal(m.Int32Constant(0), m.Word32Ror(bt.param0, bt.param1)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        uint32_t expected = 0 == bits::RotateRight32(*i, shift);
+        CHECK_UINT32_EQ(expected, bt.call(*i, shift));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Int32Constant(0),
+                        m.Word32Ror(m.Parameter(0), m.Int32Constant(shift))));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == bits::RotateRight32(*i, shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+  {
+    FOR_UINT32_SHIFTS(shift) {
+      RawMachineAssemblerTester<int32_t> m(kMachUint32);
+      m.Return(
+          m.Word32Equal(m.Word32Ror(m.Parameter(0), m.Int32Constant(shift)),
+                        m.Int32Constant(0)));
+      FOR_UINT32_INPUTS(i) {
+        uint32_t expected = 0 == bits::RotateRight32(*i, shift);
+        CHECK_UINT32_EQ(expected, m.Call(*i));
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32NotP) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  m.Return(m.Word32Not(m.Parameter(0)));
+  FOR_INT32_INPUTS(i) {
+    int expected = ~(*i);
+    CHECK_EQ(expected, m.Call(*i));
+  }
+}
+
+
+TEST(RunInt32NegP) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  m.Return(m.Int32Neg(m.Parameter(0)));
+  FOR_INT32_INPUTS(i) {
+    int expected = -*i;
+    CHECK_EQ(expected, m.Call(*i));
+  }
+}
+
+
+TEST(RunWord32EqualAndWord32SarP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32, kMachUint32);
+    m.Return(m.Word32Equal(m.Parameter(0),
+                           m.Word32Sar(m.Parameter(1), m.Parameter(2))));
+    FOR_INT32_INPUTS(i) {
+      FOR_INT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          int32_t expected = (*i == (*j >> shift));
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachUint32, kMachInt32);
+    m.Return(m.Word32Equal(m.Word32Sar(m.Parameter(0), m.Parameter(1)),
+                           m.Parameter(2)));
+    FOR_INT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_INT32_INPUTS(k) {
+          int32_t expected = ((*i >> shift) == *k);
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32EqualAndWord32ShlP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Word32Equal(m.Parameter(0),
+                           m.Word32Shl(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          int32_t expected = (*i == (*j << shift));
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Word32Equal(m.Word32Shl(m.Parameter(0), m.Parameter(1)),
+                           m.Parameter(2)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          int32_t expected = ((*i << shift) == *k);
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunWord32EqualAndWord32ShrP) {
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Word32Equal(m.Parameter(0),
+                           m.Word32Shr(m.Parameter(1), m.Parameter(2))));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_INPUTS(j) {
+        FOR_UINT32_SHIFTS(shift) {
+          int32_t expected = (*i == (*j >> shift));
+          CHECK_EQ(expected, m.Call(*i, *j, shift));
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m(kMachUint32, kMachUint32, kMachUint32);
+    m.Return(m.Word32Equal(m.Word32Shr(m.Parameter(0), m.Parameter(1)),
+                           m.Parameter(2)));
+    FOR_UINT32_INPUTS(i) {
+      FOR_UINT32_SHIFTS(shift) {
+        FOR_UINT32_INPUTS(k) {
+          int32_t expected = ((*i >> shift) == *k);
+          CHECK_EQ(expected, m.Call(*i, shift, *k));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunDeadNodes) {
+  for (int i = 0; true; i++) {
+    RawMachineAssemblerTester<int32_t> m(i == 5 ? kMachInt32 : kMachNone);
+    int constant = 0x55 + i;
+    switch (i) {
+      case 0:
+        m.Int32Constant(44);
+        break;
+      case 1:
+        m.StringConstant("unused");
+        break;
+      case 2:
+        m.NumberConstant(11.1);
+        break;
+      case 3:
+        m.PointerConstant(&constant);
+        break;
+      case 4:
+        m.LoadFromPointer(&constant, kMachInt32);
+        break;
+      case 5:
+        m.Parameter(0);
+        break;
+      default:
+        return;
+    }
+    m.Return(m.Int32Constant(constant));
+    if (i != 5) {
+      CHECK_EQ(constant, m.Call());
+    } else {
+      CHECK_EQ(constant, m.Call(0));
+    }
+  }
+}
+
+
+TEST(RunDeadInt32Binops) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const Operator* ops[] = {
+      m.machine()->Word32And(),             m.machine()->Word32Or(),
+      m.machine()->Word32Xor(),             m.machine()->Word32Shl(),
+      m.machine()->Word32Shr(),             m.machine()->Word32Sar(),
+      m.machine()->Word32Ror(),             m.machine()->Word32Equal(),
+      m.machine()->Int32Add(),              m.machine()->Int32Sub(),
+      m.machine()->Int32Mul(),              m.machine()->Int32Div(),
+      m.machine()->Int32UDiv(),             m.machine()->Int32Mod(),
+      m.machine()->Int32UMod(),             m.machine()->Int32LessThan(),
+      m.machine()->Int32LessThanOrEqual(),  m.machine()->Uint32LessThan(),
+      m.machine()->Uint32LessThanOrEqual(), NULL};
+
+  for (int i = 0; ops[i] != NULL; i++) {
+    RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+    int constant = 0x55555 + i;
+    m.NewNode(ops[i], m.Parameter(0), m.Parameter(1));
+    m.Return(m.Int32Constant(constant));
+
+    CHECK_EQ(constant, m.Call(1, 1));
+  }
+}
+
+
+template <typename Type>
+static void RunLoadImmIndex(MachineType rep) {
+  const int kNumElems = 3;
+  Type buffer[kNumElems];
+
+  // initialize the buffer with raw data.
+  byte* raw = reinterpret_cast<byte*>(buffer);
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
+  }
+
+  // Test with various large and small offsets.
+  for (int offset = -1; offset <= 200000; offset *= -5) {
+    for (int i = 0; i < kNumElems; i++) {
+      RawMachineAssemblerTester<Type> m;
+      Node* base = m.PointerConstant(buffer - offset);
+      Node* index = m.Int32Constant((offset + i) * sizeof(buffer[0]));
+      m.Return(m.Load(rep, base, index));
+
+      Type expected = buffer[i];
+      Type actual = m.Call();
+      CHECK(expected == actual);
+    }
+  }
+}
+
+
+TEST(RunLoadImmIndex) {
+  RunLoadImmIndex<int8_t>(kMachInt8);
+  RunLoadImmIndex<uint8_t>(kMachUint8);
+  RunLoadImmIndex<int16_t>(kMachInt16);
+  RunLoadImmIndex<uint16_t>(kMachUint16);
+  RunLoadImmIndex<int32_t>(kMachInt32);
+  RunLoadImmIndex<uint32_t>(kMachUint32);
+  RunLoadImmIndex<int32_t*>(kMachAnyTagged);
+
+  // TODO(titzer): test kRepBit loads
+  // TODO(titzer): test kMachFloat64 loads
+  // TODO(titzer): test various indexing modes.
+}
+
+
+template <typename CType>
+static void RunLoadStore(MachineType rep) {
+  const int kNumElems = 4;
+  CType buffer[kNumElems];
+
+  for (int32_t x = 0; x < kNumElems; x++) {
+    int32_t y = kNumElems - x - 1;
+    // initialize the buffer with raw data.
+    byte* raw = reinterpret_cast<byte*>(buffer);
+    for (size_t i = 0; i < sizeof(buffer); i++) {
+      raw[i] = static_cast<byte>((i + sizeof(buffer)) ^ 0xAA);
+    }
+
+    RawMachineAssemblerTester<int32_t> m;
+    int32_t OK = 0x29000 + x;
+    Node* base = m.PointerConstant(buffer);
+    Node* index0 = m.Int32Constant(x * sizeof(buffer[0]));
+    Node* load = m.Load(rep, base, index0);
+    Node* index1 = m.Int32Constant(y * sizeof(buffer[0]));
+    m.Store(rep, base, index1, load);
+    m.Return(m.Int32Constant(OK));
+
+    CHECK(buffer[x] != buffer[y]);
+    CHECK_EQ(OK, m.Call());
+    CHECK(buffer[x] == buffer[y]);
+  }
+}
+
+
+TEST(RunLoadStore) {
+  RunLoadStore<int8_t>(kMachInt8);
+  RunLoadStore<uint8_t>(kMachUint8);
+  RunLoadStore<int16_t>(kMachInt16);
+  RunLoadStore<uint16_t>(kMachUint16);
+  RunLoadStore<int32_t>(kMachInt32);
+  RunLoadStore<uint32_t>(kMachUint32);
+  RunLoadStore<void*>(kMachAnyTagged);
+  RunLoadStore<float>(kMachFloat32);
+  RunLoadStore<double>(kMachFloat64);
+}
+
+
+TEST(RunFloat64Binop) {
+  RawMachineAssemblerTester<int32_t> m;
+  double result;
+
+  const Operator* ops[] = {m.machine()->Float64Add(), m.machine()->Float64Sub(),
+                           m.machine()->Float64Mul(), m.machine()->Float64Div(),
+                           m.machine()->Float64Mod(), NULL};
+
+  double inf = V8_INFINITY;
+  const Operator* inputs[] = {
+      m.common()->Float64Constant(0),     m.common()->Float64Constant(1),
+      m.common()->Float64Constant(1),     m.common()->Float64Constant(0),
+      m.common()->Float64Constant(0),     m.common()->Float64Constant(-1),
+      m.common()->Float64Constant(-1),    m.common()->Float64Constant(0),
+      m.common()->Float64Constant(0.22),  m.common()->Float64Constant(-1.22),
+      m.common()->Float64Constant(-1.22), m.common()->Float64Constant(0.22),
+      m.common()->Float64Constant(inf),   m.common()->Float64Constant(0.22),
+      m.common()->Float64Constant(inf),   m.common()->Float64Constant(-inf),
+      NULL};
+
+  for (int i = 0; ops[i] != NULL; i++) {
+    for (int j = 0; inputs[j] != NULL; j += 2) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* a = m.NewNode(inputs[j]);
+      Node* b = m.NewNode(inputs[j + 1]);
+      Node* binop = m.NewNode(ops[i], a, b);
+      Node* base = m.PointerConstant(&result);
+      Node* zero = m.Int32Constant(0);
+      m.Store(kMachFloat64, base, zero, binop);
+      m.Return(m.Int32Constant(i + j));
+      CHECK_EQ(i + j, m.Call());
+    }
+  }
+}
+
+
+TEST(RunDeadFloat64Binops) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const Operator* ops[] = {m.machine()->Float64Add(), m.machine()->Float64Sub(),
+                           m.machine()->Float64Mul(), m.machine()->Float64Div(),
+                           m.machine()->Float64Mod(), NULL};
+
+  for (int i = 0; ops[i] != NULL; i++) {
+    RawMachineAssemblerTester<int32_t> m;
+    int constant = 0x53355 + i;
+    m.NewNode(ops[i], m.Float64Constant(0.1), m.Float64Constant(1.11));
+    m.Return(m.Int32Constant(constant));
+    CHECK_EQ(constant, m.Call());
+  }
+}
+
+
+TEST(RunFloat64AddP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Float64BinopTester bt(&m);
+
+  bt.AddReturn(m.Float64Add(bt.param0, bt.param1));
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double expected = *pl + *pr;
+      CHECK_EQ(expected, bt.call(*pl, *pr));
+    }
+  }
+}
+
+
+TEST(RunFloat64SubP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Float64BinopTester bt(&m);
+
+  bt.AddReturn(m.Float64Sub(bt.param0, bt.param1));
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double expected = *pl - *pr;
+      CHECK_EQ(expected, bt.call(*pl, *pr));
+    }
+  }
+}
+
+
+TEST(RunFloat64SubImm1) {
+  double input = 0.0;
+  double output = 0.0;
+
+  FOR_FLOAT64_INPUTS(i) {
+    RawMachineAssemblerTester<int32_t> m;
+    Node* t0 = m.LoadFromPointer(&input, kMachFloat64);
+    Node* t1 = m.Float64Sub(m.Float64Constant(*i), t0);
+    m.StoreToPointer(&output, kMachFloat64, t1);
+    m.Return(m.Int32Constant(0));
+    FOR_FLOAT64_INPUTS(j) {
+      input = *j;
+      double expected = *i - input;
+      CHECK_EQ(0, m.Call());
+      CHECK_EQ(expected, output);
+    }
+  }
+}
+
+
+TEST(RunFloat64SubImm2) {
+  double input = 0.0;
+  double output = 0.0;
+
+  FOR_FLOAT64_INPUTS(i) {
+    RawMachineAssemblerTester<int32_t> m;
+    Node* t0 = m.LoadFromPointer(&input, kMachFloat64);
+    Node* t1 = m.Float64Sub(t0, m.Float64Constant(*i));
+    m.StoreToPointer(&output, kMachFloat64, t1);
+    m.Return(m.Int32Constant(0));
+    FOR_FLOAT64_INPUTS(j) {
+      input = *j;
+      double expected = input - *i;
+      CHECK_EQ(0, m.Call());
+      CHECK_EQ(expected, output);
+    }
+  }
+}
+
+
+TEST(RunFloat64MulP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Float64BinopTester bt(&m);
+
+  bt.AddReturn(m.Float64Mul(bt.param0, bt.param1));
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double expected = *pl * *pr;
+      CHECK_EQ(expected, bt.call(*pl, *pr));
+    }
+  }
+}
+
+
+TEST(RunFloat64MulAndFloat64AddP) {
+  double input_a = 0.0;
+  double input_b = 0.0;
+  double input_c = 0.0;
+  double output = 0.0;
+
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+    Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+    Node* c = m.LoadFromPointer(&input_c, kMachFloat64);
+    m.StoreToPointer(&output, kMachFloat64,
+                     m.Float64Add(m.Float64Mul(a, b), c));
+    m.Return(m.Int32Constant(0));
+    FOR_FLOAT64_INPUTS(i) {
+      FOR_FLOAT64_INPUTS(j) {
+        FOR_FLOAT64_INPUTS(k) {
+          input_a = *i;
+          input_b = *j;
+          input_c = *k;
+          volatile double temp = input_a * input_b;
+          volatile double expected = temp + input_c;
+          CHECK_EQ(0, m.Call());
+          CHECK_EQ(expected, output);
+        }
+      }
+    }
+  }
+  {
+    RawMachineAssemblerTester<int32_t> m;
+    Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+    Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+    Node* c = m.LoadFromPointer(&input_c, kMachFloat64);
+    m.StoreToPointer(&output, kMachFloat64,
+                     m.Float64Add(a, m.Float64Mul(b, c)));
+    m.Return(m.Int32Constant(0));
+    FOR_FLOAT64_INPUTS(i) {
+      FOR_FLOAT64_INPUTS(j) {
+        FOR_FLOAT64_INPUTS(k) {
+          input_a = *i;
+          input_b = *j;
+          input_c = *k;
+          volatile double temp = input_b * input_c;
+          volatile double expected = input_a + temp;
+          CHECK_EQ(0, m.Call());
+          CHECK_EQ(expected, output);
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunFloat64MulAndFloat64SubP) {
+  double input_a = 0.0;
+  double input_b = 0.0;
+  double input_c = 0.0;
+  double output = 0.0;
+
+  RawMachineAssemblerTester<int32_t> m;
+  Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+  Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+  Node* c = m.LoadFromPointer(&input_c, kMachFloat64);
+  m.StoreToPointer(&output, kMachFloat64, m.Float64Sub(a, m.Float64Mul(b, c)));
+  m.Return(m.Int32Constant(0));
+
+  FOR_FLOAT64_INPUTS(i) {
+    FOR_FLOAT64_INPUTS(j) {
+      FOR_FLOAT64_INPUTS(k) {
+        input_a = *i;
+        input_b = *j;
+        input_c = *k;
+        volatile double temp = input_b * input_c;
+        volatile double expected = input_a - temp;
+        CHECK_EQ(0, m.Call());
+        CHECK_EQ(expected, output);
+      }
+    }
+  }
+}
+
+
+TEST(RunFloat64MulImm) {
+  double input = 0.0;
+  double output = 0.0;
+
+  {
+    FOR_FLOAT64_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* t0 = m.LoadFromPointer(&input, kMachFloat64);
+      Node* t1 = m.Float64Mul(m.Float64Constant(*i), t0);
+      m.StoreToPointer(&output, kMachFloat64, t1);
+      m.Return(m.Int32Constant(0));
+      FOR_FLOAT64_INPUTS(j) {
+        input = *j;
+        double expected = *i * input;
+        CHECK_EQ(0, m.Call());
+        CHECK_EQ(expected, output);
+      }
+    }
+  }
+  {
+    FOR_FLOAT64_INPUTS(i) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* t0 = m.LoadFromPointer(&input, kMachFloat64);
+      Node* t1 = m.Float64Mul(t0, m.Float64Constant(*i));
+      m.StoreToPointer(&output, kMachFloat64, t1);
+      m.Return(m.Int32Constant(0));
+      FOR_FLOAT64_INPUTS(j) {
+        input = *j;
+        double expected = input * *i;
+        CHECK_EQ(0, m.Call());
+        CHECK_EQ(expected, output);
+      }
+    }
+  }
+}
+
+
+TEST(RunFloat64DivP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Float64BinopTester bt(&m);
+
+  bt.AddReturn(m.Float64Div(bt.param0, bt.param1));
+
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      double expected = *pl / *pr;
+      CHECK_EQ(expected, bt.call(*pl, *pr));
+    }
+  }
+}
+
+
+TEST(RunFloat64ModP) {
+  RawMachineAssemblerTester<int32_t> m;
+  Float64BinopTester bt(&m);
+
+  bt.AddReturn(m.Float64Mod(bt.param0, bt.param1));
+
+  FOR_FLOAT64_INPUTS(i) {
+    FOR_FLOAT64_INPUTS(j) {
+      double expected = modulo(*i, *j);
+      double found = bt.call(*i, *j);
+      CHECK_EQ(expected, found);
+    }
+  }
+}
+
+
+TEST(RunChangeInt32ToFloat64_A) {
+  RawMachineAssemblerTester<int32_t> m;
+  int32_t magic = 0x986234;
+  double result = 0;
+
+  Node* convert = m.ChangeInt32ToFloat64(m.Int32Constant(magic));
+  m.Store(kMachFloat64, m.PointerConstant(&result), m.Int32Constant(0),
+          convert);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK_EQ(static_cast<double>(magic), result);
+}
+
+
+TEST(RunChangeInt32ToFloat64_B) {
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  double output = 0;
+
+  Node* convert = m.ChangeInt32ToFloat64(m.Parameter(0));
+  m.Store(kMachFloat64, m.PointerConstant(&output), m.Int32Constant(0),
+          convert);
+  m.Return(m.Parameter(0));
+
+  FOR_INT32_INPUTS(i) {
+    int32_t expect = *i;
+    CHECK_EQ(expect, m.Call(expect));
+    CHECK_EQ(static_cast<double>(expect), output);
+  }
+}
+
+
+TEST(RunChangeUint32ToFloat64_B) {
+  RawMachineAssemblerTester<int32_t> m(kMachUint32);
+  double output = 0;
+
+  Node* convert = m.ChangeUint32ToFloat64(m.Parameter(0));
+  m.Store(kMachFloat64, m.PointerConstant(&output), m.Int32Constant(0),
+          convert);
+  m.Return(m.Parameter(0));
+
+  FOR_UINT32_INPUTS(i) {
+    uint32_t expect = *i;
+    CHECK_EQ(expect, m.Call(expect));
+    CHECK_EQ(static_cast<double>(expect), output);
+  }
+}
+
+
+TEST(RunChangeFloat64ToInt32_A) {
+  RawMachineAssemblerTester<int32_t> m;
+  int32_t magic = 0x786234;
+  double input = 11.1;
+  int32_t result = 0;
+
+  m.Store(kMachInt32, m.PointerConstant(&result), m.Int32Constant(0),
+          m.ChangeFloat64ToInt32(m.Float64Constant(input)));
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK_EQ(static_cast<int32_t>(input), result);
+}
+
+
+TEST(RunChangeFloat64ToInt32_B) {
+  RawMachineAssemblerTester<int32_t> m;
+  double input = 0;
+  int32_t output = 0;
+
+  Node* load =
+      m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(0));
+  Node* convert = m.ChangeFloat64ToInt32(load);
+  m.Store(kMachInt32, m.PointerConstant(&output), m.Int32Constant(0), convert);
+  m.Return(convert);
+
+  {
+    FOR_INT32_INPUTS(i) {
+      input = *i;
+      int32_t expect = *i;
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+  }
+
+  // Check various powers of 2.
+  for (int32_t n = 1; n < 31; ++n) {
+    {
+      input = 1 << n;
+      int32_t expect = static_cast<int32_t>(input);
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+
+    {
+      input = 3 << n;
+      int32_t expect = static_cast<int32_t>(input);
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+  }
+  // Note we don't check fractional inputs, because these Convert operators
+  // really should be Change operators.
+}
+
+
+TEST(RunChangeFloat64ToUint32_B) {
+  RawMachineAssemblerTester<int32_t> m;
+  double input = 0;
+  int32_t output = 0;
+
+  Node* load =
+      m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(0));
+  Node* convert = m.ChangeFloat64ToUint32(load);
+  m.Store(kMachInt32, m.PointerConstant(&output), m.Int32Constant(0), convert);
+  m.Return(convert);
+
+  {
+    FOR_UINT32_INPUTS(i) {
+      input = *i;
+      // TODO(titzer): add a CheckEqualsHelper overload for uint32_t.
+      int32_t expect = static_cast<int32_t>(*i);
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+  }
+
+  // Check various powers of 2.
+  for (int32_t n = 1; n < 31; ++n) {
+    {
+      input = 1u << n;
+      int32_t expect = static_cast<int32_t>(static_cast<uint32_t>(input));
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+
+    {
+      input = 3u << n;
+      int32_t expect = static_cast<int32_t>(static_cast<uint32_t>(input));
+      CHECK_EQ(expect, m.Call());
+      CHECK_EQ(expect, output);
+    }
+  }
+  // Note we don't check fractional inputs, because these Convert operators
+  // really should be Change operators.
+}
+
+
+TEST(RunChangeFloat64ToInt32_spilled) {
+  RawMachineAssemblerTester<int32_t> m;
+  const int kNumInputs = 32;
+  int32_t magic = 0x786234;
+  double input[kNumInputs];
+  int32_t result[kNumInputs];
+  Node* input_node[kNumInputs];
+
+  for (int i = 0; i < kNumInputs; i++) {
+    input_node[i] =
+        m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(i * 8));
+  }
+
+  for (int i = 0; i < kNumInputs; i++) {
+    m.Store(kMachInt32, m.PointerConstant(&result), m.Int32Constant(i * 4),
+            m.ChangeFloat64ToInt32(input_node[i]));
+  }
+
+  m.Return(m.Int32Constant(magic));
+
+  for (int i = 0; i < kNumInputs; i++) {
+    input[i] = 100.9 + i;
+  }
+
+  CHECK_EQ(magic, m.Call());
+
+  for (int i = 0; i < kNumInputs; i++) {
+    CHECK_EQ(result[i], 100 + i);
+  }
+}
+
+
+TEST(RunChangeFloat64ToUint32_spilled) {
+  RawMachineAssemblerTester<uint32_t> m;
+  const int kNumInputs = 32;
+  int32_t magic = 0x786234;
+  double input[kNumInputs];
+  uint32_t result[kNumInputs];
+  Node* input_node[kNumInputs];
+
+  for (int i = 0; i < kNumInputs; i++) {
+    input_node[i] =
+        m.Load(kMachFloat64, m.PointerConstant(&input), m.Int32Constant(i * 8));
+  }
+
+  for (int i = 0; i < kNumInputs; i++) {
+    m.Store(kMachUint32, m.PointerConstant(&result), m.Int32Constant(i * 4),
+            m.ChangeFloat64ToUint32(input_node[i]));
+  }
+
+  m.Return(m.Int32Constant(magic));
+
+  for (int i = 0; i < kNumInputs; i++) {
+    if (i % 2) {
+      input[i] = 100 + i + 2147483648u;
+    } else {
+      input[i] = 100 + i;
+    }
+  }
+
+  CHECK_EQ(magic, m.Call());
+
+  for (int i = 0; i < kNumInputs; i++) {
+    if (i % 2) {
+      CHECK_UINT32_EQ(result[i], static_cast<uint32_t>(100 + i + 2147483648u));
+    } else {
+      CHECK_UINT32_EQ(result[i], static_cast<uint32_t>(100 + i));
+    }
+  }
+}
+
+
+TEST(RunDeadChangeFloat64ToInt32) {
+  RawMachineAssemblerTester<int32_t> m;
+  const int magic = 0x88abcda4;
+  m.ChangeFloat64ToInt32(m.Float64Constant(999.78));
+  m.Return(m.Int32Constant(magic));
+  CHECK_EQ(magic, m.Call());
+}
+
+
+TEST(RunDeadChangeInt32ToFloat64) {
+  RawMachineAssemblerTester<int32_t> m;
+  const int magic = 0x8834abcd;
+  m.ChangeInt32ToFloat64(m.Int32Constant(magic - 6888));
+  m.Return(m.Int32Constant(magic));
+  CHECK_EQ(magic, m.Call());
+}
+
+
+TEST(RunLoopPhiInduction2) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  int false_val = 0x10777;
+
+  // x = false_val; while(false) { x++; } return x;
+  MLabel header, body, end;
+  Node* false_node = m.Int32Constant(false_val);
+  m.Goto(&header);
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachInt32, false_node, false_node);
+  m.Branch(m.Int32Constant(0), &body, &end);
+  m.Bind(&body);
+  Node* add = m.Int32Add(phi, m.Int32Constant(1));
+  phi->ReplaceInput(1, add);
+  m.Goto(&header);
+  m.Bind(&end);
+  m.Return(phi);
+
+  CHECK_EQ(false_val, m.Call());
+}
+
+
+TEST(RunDoubleDiamond) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const int magic = 99645;
+  double buffer = 0.1;
+  double constant = 99.99;
+
+  MLabel blocka, blockb, end;
+  Node* k1 = m.Float64Constant(constant);
+  Node* k2 = m.Float64Constant(0 - constant);
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&end);
+  m.Bind(&blockb);
+  m.Goto(&end);
+  m.Bind(&end);
+  Node* phi = m.Phi(kMachFloat64, k2, k1);
+  m.Store(kMachFloat64, m.PointerConstant(&buffer), m.Int32Constant(0), phi);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK_EQ(constant, buffer);
+}
+
+
+TEST(RunRefDiamond) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const int magic = 99644;
+  Handle<String> rexpected =
+      CcTest::i_isolate()->factory()->InternalizeUtf8String("A");
+  String* buffer;
+
+  MLabel blocka, blockb, end;
+  Node* k1 = m.StringConstant("A");
+  Node* k2 = m.StringConstant("B");
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&end);
+  m.Bind(&blockb);
+  m.Goto(&end);
+  m.Bind(&end);
+  Node* phi = m.Phi(kMachAnyTagged, k2, k1);
+  m.Store(kMachAnyTagged, m.PointerConstant(&buffer), m.Int32Constant(0), phi);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK(rexpected->SameValue(buffer));
+}
+
+
+TEST(RunDoubleRefDiamond) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const int magic = 99648;
+  double dbuffer = 0.1;
+  double dconstant = 99.99;
+  Handle<String> rexpected =
+      CcTest::i_isolate()->factory()->InternalizeUtf8String("AX");
+  String* rbuffer;
+
+  MLabel blocka, blockb, end;
+  Node* d1 = m.Float64Constant(dconstant);
+  Node* d2 = m.Float64Constant(0 - dconstant);
+  Node* r1 = m.StringConstant("AX");
+  Node* r2 = m.StringConstant("BX");
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&end);
+  m.Bind(&blockb);
+  m.Goto(&end);
+  m.Bind(&end);
+  Node* dphi = m.Phi(kMachFloat64, d2, d1);
+  Node* rphi = m.Phi(kMachAnyTagged, r2, r1);
+  m.Store(kMachFloat64, m.PointerConstant(&dbuffer), m.Int32Constant(0), dphi);
+  m.Store(kMachAnyTagged, m.PointerConstant(&rbuffer), m.Int32Constant(0),
+          rphi);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK_EQ(dconstant, dbuffer);
+  CHECK(rexpected->SameValue(rbuffer));
+}
+
+
+TEST(RunDoubleRefDoubleDiamond) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const int magic = 99649;
+  double dbuffer = 0.1;
+  double dconstant = 99.997;
+  Handle<String> rexpected =
+      CcTest::i_isolate()->factory()->InternalizeUtf8String("AD");
+  String* rbuffer;
+
+  MLabel blocka, blockb, mid, blockd, blocke, end;
+  Node* d1 = m.Float64Constant(dconstant);
+  Node* d2 = m.Float64Constant(0 - dconstant);
+  Node* r1 = m.StringConstant("AD");
+  Node* r2 = m.StringConstant("BD");
+  m.Branch(m.Int32Constant(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&mid);
+  m.Bind(&blockb);
+  m.Goto(&mid);
+  m.Bind(&mid);
+  Node* dphi1 = m.Phi(kMachFloat64, d2, d1);
+  Node* rphi1 = m.Phi(kMachAnyTagged, r2, r1);
+  m.Branch(m.Int32Constant(0), &blockd, &blocke);
+
+  m.Bind(&blockd);
+  m.Goto(&end);
+  m.Bind(&blocke);
+  m.Goto(&end);
+  m.Bind(&end);
+  Node* dphi2 = m.Phi(kMachFloat64, d1, dphi1);
+  Node* rphi2 = m.Phi(kMachAnyTagged, r1, rphi1);
+
+  m.Store(kMachFloat64, m.PointerConstant(&dbuffer), m.Int32Constant(0), dphi2);
+  m.Store(kMachAnyTagged, m.PointerConstant(&rbuffer), m.Int32Constant(0),
+          rphi2);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+  CHECK_EQ(dconstant, dbuffer);
+  CHECK(rexpected->SameValue(rbuffer));
+}
+
+
+TEST(RunDoubleLoopPhi) {
+  RawMachineAssemblerTester<int32_t> m;
+  MLabel header, body, end;
+
+  int magic = 99773;
+  double buffer = 0.99;
+  double dconstant = 777.1;
+
+  Node* zero = m.Int32Constant(0);
+  Node* dk = m.Float64Constant(dconstant);
+
+  m.Goto(&header);
+  m.Bind(&header);
+  Node* phi = m.Phi(kMachFloat64, dk, dk);
+  phi->ReplaceInput(1, phi);
+  m.Branch(zero, &body, &end);
+  m.Bind(&body);
+  m.Goto(&header);
+  m.Bind(&end);
+  m.Store(kMachFloat64, m.PointerConstant(&buffer), m.Int32Constant(0), phi);
+  m.Return(m.Int32Constant(magic));
+
+  CHECK_EQ(magic, m.Call());
+}
+
+
+TEST(RunCountToTenAccRaw) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  Node* zero = m.Int32Constant(0);
+  Node* ten = m.Int32Constant(10);
+  Node* one = m.Int32Constant(1);
+
+  MLabel header, body, body_cont, end;
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* i = m.Phi(kMachInt32, zero, zero);
+  Node* j = m.Phi(kMachInt32, zero, zero);
+  m.Goto(&body);
+
+  m.Bind(&body);
+  Node* next_i = m.Int32Add(i, one);
+  Node* next_j = m.Int32Add(j, one);
+  m.Branch(m.Word32Equal(next_i, ten), &end, &body_cont);
+
+  m.Bind(&body_cont);
+  i->ReplaceInput(1, next_i);
+  j->ReplaceInput(1, next_j);
+  m.Goto(&header);
+
+  m.Bind(&end);
+  m.Return(ten);
+
+  CHECK_EQ(10, m.Call());
+}
+
+
+TEST(RunCountToTenAccRaw2) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  Node* zero = m.Int32Constant(0);
+  Node* ten = m.Int32Constant(10);
+  Node* one = m.Int32Constant(1);
+
+  MLabel header, body, body_cont, end;
+
+  m.Goto(&header);
+
+  m.Bind(&header);
+  Node* i = m.Phi(kMachInt32, zero, zero);
+  Node* j = m.Phi(kMachInt32, zero, zero);
+  Node* k = m.Phi(kMachInt32, zero, zero);
+  m.Goto(&body);
+
+  m.Bind(&body);
+  Node* next_i = m.Int32Add(i, one);
+  Node* next_j = m.Int32Add(j, one);
+  Node* next_k = m.Int32Add(j, one);
+  m.Branch(m.Word32Equal(next_i, ten), &end, &body_cont);
+
+  m.Bind(&body_cont);
+  i->ReplaceInput(1, next_i);
+  j->ReplaceInput(1, next_j);
+  k->ReplaceInput(1, next_k);
+  m.Goto(&header);
+
+  m.Bind(&end);
+  m.Return(ten);
+
+  CHECK_EQ(10, m.Call());
+}
+
+
+TEST(RunAddTree) {
+  RawMachineAssemblerTester<int32_t> m;
+  int32_t inputs[] = {11, 12, 13, 14, 15, 16, 17, 18};
+
+  Node* base = m.PointerConstant(inputs);
+  Node* n0 = m.Load(kMachInt32, base, m.Int32Constant(0 * sizeof(int32_t)));
+  Node* n1 = m.Load(kMachInt32, base, m.Int32Constant(1 * sizeof(int32_t)));
+  Node* n2 = m.Load(kMachInt32, base, m.Int32Constant(2 * sizeof(int32_t)));
+  Node* n3 = m.Load(kMachInt32, base, m.Int32Constant(3 * sizeof(int32_t)));
+  Node* n4 = m.Load(kMachInt32, base, m.Int32Constant(4 * sizeof(int32_t)));
+  Node* n5 = m.Load(kMachInt32, base, m.Int32Constant(5 * sizeof(int32_t)));
+  Node* n6 = m.Load(kMachInt32, base, m.Int32Constant(6 * sizeof(int32_t)));
+  Node* n7 = m.Load(kMachInt32, base, m.Int32Constant(7 * sizeof(int32_t)));
+
+  Node* i1 = m.Int32Add(n0, n1);
+  Node* i2 = m.Int32Add(n2, n3);
+  Node* i3 = m.Int32Add(n4, n5);
+  Node* i4 = m.Int32Add(n6, n7);
+
+  Node* i5 = m.Int32Add(i1, i2);
+  Node* i6 = m.Int32Add(i3, i4);
+
+  Node* i7 = m.Int32Add(i5, i6);
+
+  m.Return(i7);
+
+  CHECK_EQ(116, m.Call());
+}
+
+
+static const int kFloat64CompareHelperTestCases = 15;
+static const int kFloat64CompareHelperNodeType = 4;
+
+static int Float64CompareHelper(RawMachineAssemblerTester<int32_t>* m,
+                                int test_case, int node_type, double x,
+                                double y) {
+  static double buffer[2];
+  buffer[0] = x;
+  buffer[1] = y;
+  CHECK(0 <= test_case && test_case < kFloat64CompareHelperTestCases);
+  CHECK(0 <= node_type && node_type < kFloat64CompareHelperNodeType);
+  CHECK(x < y);
+  bool load_a = node_type / 2 == 1;
+  bool load_b = node_type % 2 == 1;
+  Node* a = load_a ? m->Load(kMachFloat64, m->PointerConstant(&buffer[0]))
+                   : m->Float64Constant(x);
+  Node* b = load_b ? m->Load(kMachFloat64, m->PointerConstant(&buffer[1]))
+                   : m->Float64Constant(y);
+  Node* cmp = NULL;
+  bool expected = false;
+  switch (test_case) {
+    // Equal tests.
+    case 0:
+      cmp = m->Float64Equal(a, b);
+      expected = false;
+      break;
+    case 1:
+      cmp = m->Float64Equal(a, a);
+      expected = true;
+      break;
+    // LessThan tests.
+    case 2:
+      cmp = m->Float64LessThan(a, b);
+      expected = true;
+      break;
+    case 3:
+      cmp = m->Float64LessThan(b, a);
+      expected = false;
+      break;
+    case 4:
+      cmp = m->Float64LessThan(a, a);
+      expected = false;
+      break;
+    // LessThanOrEqual tests.
+    case 5:
+      cmp = m->Float64LessThanOrEqual(a, b);
+      expected = true;
+      break;
+    case 6:
+      cmp = m->Float64LessThanOrEqual(b, a);
+      expected = false;
+      break;
+    case 7:
+      cmp = m->Float64LessThanOrEqual(a, a);
+      expected = true;
+      break;
+    // NotEqual tests.
+    case 8:
+      cmp = m->Float64NotEqual(a, b);
+      expected = true;
+      break;
+    case 9:
+      cmp = m->Float64NotEqual(b, a);
+      expected = true;
+      break;
+    case 10:
+      cmp = m->Float64NotEqual(a, a);
+      expected = false;
+      break;
+    // GreaterThan tests.
+    case 11:
+      cmp = m->Float64GreaterThan(a, a);
+      expected = false;
+      break;
+    case 12:
+      cmp = m->Float64GreaterThan(a, b);
+      expected = false;
+      break;
+    // GreaterThanOrEqual tests.
+    case 13:
+      cmp = m->Float64GreaterThanOrEqual(a, a);
+      expected = true;
+      break;
+    case 14:
+      cmp = m->Float64GreaterThanOrEqual(b, a);
+      expected = true;
+      break;
+    default:
+      UNREACHABLE();
+  }
+  m->Return(cmp);
+  return expected;
+}
+
+
+TEST(RunFloat64Compare) {
+  double inf = V8_INFINITY;
+  // All pairs (a1, a2) are of the form a1 < a2.
+  double inputs[] = {0.0,  1.0,  -1.0, 0.22, -1.22, 0.22,
+                     -inf, 0.22, 0.22, inf,  -inf,  inf};
+
+  for (int test = 0; test < kFloat64CompareHelperTestCases; test++) {
+    for (int node_type = 0; node_type < kFloat64CompareHelperNodeType;
+         node_type++) {
+      for (size_t input = 0; input < arraysize(inputs); input += 2) {
+        RawMachineAssemblerTester<int32_t> m;
+        int expected = Float64CompareHelper(&m, test, node_type, inputs[input],
+                                            inputs[input + 1]);
+        CHECK_EQ(expected, m.Call());
+      }
+    }
+  }
+}
+
+
+TEST(RunFloat64UnorderedCompare) {
+  RawMachineAssemblerTester<int32_t> m;
+
+  const Operator* operators[] = {m.machine()->Float64Equal(),
+                                 m.machine()->Float64LessThan(),
+                                 m.machine()->Float64LessThanOrEqual()};
+
+  double nan = v8::base::OS::nan_value();
+
+  FOR_FLOAT64_INPUTS(i) {
+    for (size_t o = 0; o < arraysize(operators); ++o) {
+      for (int j = 0; j < 2; j++) {
+        RawMachineAssemblerTester<int32_t> m;
+        Node* a = m.Float64Constant(*i);
+        Node* b = m.Float64Constant(nan);
+        if (j == 1) std::swap(a, b);
+        m.Return(m.NewNode(operators[o], a, b));
+        CHECK_EQ(0, m.Call());
+      }
+    }
+  }
+}
+
+
+TEST(RunFloat64Equal) {
+  double input_a = 0.0;
+  double input_b = 0.0;
+
+  RawMachineAssemblerTester<int32_t> m;
+  Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+  Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+  m.Return(m.Float64Equal(a, b));
+
+  CompareWrapper cmp(IrOpcode::kFloat64Equal);
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      input_a = *pl;
+      input_b = *pr;
+      int32_t expected = cmp.Float64Compare(input_a, input_b) ? 1 : 0;
+      CHECK_EQ(expected, m.Call());
+    }
+  }
+}
+
+
+TEST(RunFloat64LessThan) {
+  double input_a = 0.0;
+  double input_b = 0.0;
+
+  RawMachineAssemblerTester<int32_t> m;
+  Node* a = m.LoadFromPointer(&input_a, kMachFloat64);
+  Node* b = m.LoadFromPointer(&input_b, kMachFloat64);
+  m.Return(m.Float64LessThan(a, b));
+
+  CompareWrapper cmp(IrOpcode::kFloat64LessThan);
+  FOR_FLOAT64_INPUTS(pl) {
+    FOR_FLOAT64_INPUTS(pr) {
+      input_a = *pl;
+      input_b = *pr;
+      int32_t expected = cmp.Float64Compare(input_a, input_b) ? 1 : 0;
+      CHECK_EQ(expected, m.Call());
+    }
+  }
+}
+
+
+template <typename IntType, MachineType kRepresentation>
+static void LoadStoreTruncation() {
+  IntType input;
+
+  RawMachineAssemblerTester<int32_t> m;
+  Node* a = m.LoadFromPointer(&input, kRepresentation);
+  Node* ap1 = m.Int32Add(a, m.Int32Constant(1));
+  m.StoreToPointer(&input, kRepresentation, ap1);
+  m.Return(ap1);
+
+  const IntType max = std::numeric_limits<IntType>::max();
+  const IntType min = std::numeric_limits<IntType>::min();
+
+  // Test upper bound.
+  input = max;
+  CHECK_EQ(max + 1, m.Call());
+  CHECK_EQ(min, input);
+
+  // Test lower bound.
+  input = min;
+  CHECK_EQ(static_cast<IntType>(max + 2), m.Call());
+  CHECK_EQ(min + 1, input);
+
+  // Test all one byte values that are not one byte bounds.
+  for (int i = -127; i < 127; i++) {
+    input = i;
+    int expected = i >= 0 ? i + 1 : max + (i - min) + 2;
+    CHECK_EQ(static_cast<IntType>(expected), m.Call());
+    CHECK_EQ(static_cast<IntType>(i + 1), input);
+  }
+}
+
+
+TEST(RunLoadStoreTruncation) {
+  LoadStoreTruncation<int8_t, kMachInt8>();
+  LoadStoreTruncation<int16_t, kMachInt16>();
+}
+
+
+static void IntPtrCompare(intptr_t left, intptr_t right) {
+  for (int test = 0; test < 7; test++) {
+    RawMachineAssemblerTester<bool> m(kMachPtr, kMachPtr);
+    Node* p0 = m.Parameter(0);
+    Node* p1 = m.Parameter(1);
+    Node* res = NULL;
+    bool expected = false;
+    switch (test) {
+      case 0:
+        res = m.IntPtrLessThan(p0, p1);
+        expected = true;
+        break;
+      case 1:
+        res = m.IntPtrLessThanOrEqual(p0, p1);
+        expected = true;
+        break;
+      case 2:
+        res = m.IntPtrEqual(p0, p1);
+        expected = false;
+        break;
+      case 3:
+        res = m.IntPtrGreaterThanOrEqual(p0, p1);
+        expected = false;
+        break;
+      case 4:
+        res = m.IntPtrGreaterThan(p0, p1);
+        expected = false;
+        break;
+      case 5:
+        res = m.IntPtrEqual(p0, p0);
+        expected = true;
+        break;
+      case 6:
+        res = m.IntPtrNotEqual(p0, p1);
+        expected = true;
+        break;
+      default:
+        UNREACHABLE();
+        break;
+    }
+    m.Return(res);
+    CHECK_EQ(expected, m.Call(reinterpret_cast<int32_t*>(left),
+                              reinterpret_cast<int32_t*>(right)));
+  }
+}
+
+
+TEST(RunIntPtrCompare) {
+  intptr_t min = std::numeric_limits<intptr_t>::min();
+  intptr_t max = std::numeric_limits<intptr_t>::max();
+  // An ascending chain of intptr_t
+  intptr_t inputs[] = {min, min / 2, -1, 0, 1, max / 2, max};
+  for (size_t i = 0; i < arraysize(inputs) - 1; i++) {
+    IntPtrCompare(inputs[i], inputs[i + 1]);
+  }
+}
+
+
+TEST(RunTestIntPtrArithmetic) {
+  static const int kInputSize = 10;
+  int32_t inputs[kInputSize];
+  int32_t outputs[kInputSize];
+  for (int i = 0; i < kInputSize; i++) {
+    inputs[i] = i;
+    outputs[i] = -1;
+  }
+  RawMachineAssemblerTester<int32_t*> m;
+  Node* input = m.PointerConstant(&inputs[0]);
+  Node* output = m.PointerConstant(&outputs[kInputSize - 1]);
+  Node* elem_size = m.ConvertInt32ToIntPtr(m.Int32Constant(sizeof(inputs[0])));
+  for (int i = 0; i < kInputSize; i++) {
+    m.Store(kMachInt32, output, m.Load(kMachInt32, input));
+    input = m.IntPtrAdd(input, elem_size);
+    output = m.IntPtrSub(output, elem_size);
+  }
+  m.Return(input);
+  CHECK_EQ(&inputs[kInputSize], m.Call());
+  for (int i = 0; i < kInputSize; i++) {
+    CHECK_EQ(i, inputs[i]);
+    CHECK_EQ(kInputSize - i - 1, outputs[i]);
+  }
+}
+
+
+TEST(RunSpillLotsOfThings) {
+  static const int kInputSize = 1000;
+  RawMachineAssemblerTester<void> m;
+  Node* accs[kInputSize];
+  int32_t outputs[kInputSize];
+  Node* one = m.Int32Constant(1);
+  Node* acc = one;
+  for (int i = 0; i < kInputSize; i++) {
+    acc = m.Int32Add(acc, one);
+    accs[i] = acc;
+  }
+  for (int i = 0; i < kInputSize; i++) {
+    m.StoreToPointer(&outputs[i], kMachInt32, accs[i]);
+  }
+  m.Return(one);
+  m.Call();
+  for (int i = 0; i < kInputSize; i++) {
+    CHECK_EQ(outputs[i], i + 2);
+  }
+}
+
+
+TEST(RunSpillConstantsAndParameters) {
+  static const int kInputSize = 1000;
+  static const int32_t kBase = 987;
+  RawMachineAssemblerTester<int32_t> m(kMachInt32, kMachInt32);
+  int32_t outputs[kInputSize];
+  Node* csts[kInputSize];
+  Node* accs[kInputSize];
+  Node* acc = m.Int32Constant(0);
+  for (int i = 0; i < kInputSize; i++) {
+    csts[i] = m.Int32Constant(static_cast<int32_t>(kBase + i));
+  }
+  for (int i = 0; i < kInputSize; i++) {
+    acc = m.Int32Add(acc, csts[i]);
+    accs[i] = acc;
+  }
+  for (int i = 0; i < kInputSize; i++) {
+    m.StoreToPointer(&outputs[i], kMachInt32, accs[i]);
+  }
+  m.Return(m.Int32Add(acc, m.Int32Add(m.Parameter(0), m.Parameter(1))));
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t expected = *i + *j;
+      for (int k = 0; k < kInputSize; k++) {
+        expected += kBase + k;
+      }
+      CHECK_EQ(expected, m.Call(*i, *j));
+      expected = 0;
+      for (int k = 0; k < kInputSize; k++) {
+        expected += kBase + k;
+        CHECK_EQ(expected, outputs[k]);
+      }
+    }
+  }
+}
+
+
+TEST(RunNewSpaceConstantsInPhi) {
+  RawMachineAssemblerTester<Object*> m(kMachInt32);
+
+  Isolate* isolate = CcTest::i_isolate();
+  Handle<HeapNumber> true_val = isolate->factory()->NewHeapNumber(11.2);
+  Handle<HeapNumber> false_val = isolate->factory()->NewHeapNumber(11.3);
+  Node* true_node = m.HeapConstant(true_val);
+  Node* false_node = m.HeapConstant(false_val);
+
+  MLabel blocka, blockb, end;
+  m.Branch(m.Parameter(0), &blocka, &blockb);
+  m.Bind(&blocka);
+  m.Goto(&end);
+  m.Bind(&blockb);
+  m.Goto(&end);
+
+  m.Bind(&end);
+  Node* phi = m.Phi(kMachAnyTagged, true_node, false_node);
+  m.Return(phi);
+
+  CHECK_EQ(*false_val, m.Call(0));
+  CHECK_EQ(*true_val, m.Call(1));
+}
+
+
+TEST(RunInt32AddWithOverflowP) {
+  int32_t actual_val = -1;
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+  Node* add = m.Int32AddWithOverflow(bt.param0, bt.param1);
+  Node* val = m.Projection(0, add);
+  Node* ovf = m.Projection(1, add);
+  m.StoreToPointer(&actual_val, kMachInt32, val);
+  bt.AddReturn(ovf);
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t expected_val;
+      int expected_ovf = bits::SignedAddOverflow32(*i, *j, &expected_val);
+      CHECK_EQ(expected_ovf, bt.call(*i, *j));
+      CHECK_EQ(expected_val, actual_val);
+    }
+  }
+}
+
+
+TEST(RunInt32AddWithOverflowImm) {
+  int32_t actual_val = -1, expected_val = 0;
+  FOR_INT32_INPUTS(i) {
+    {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      Node* add = m.Int32AddWithOverflow(m.Int32Constant(*i), m.Parameter(0));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      FOR_INT32_INPUTS(j) {
+        int expected_ovf = bits::SignedAddOverflow32(*i, *j, &expected_val);
+        CHECK_EQ(expected_ovf, m.Call(*j));
+        CHECK_EQ(expected_val, actual_val);
+      }
+    }
+    {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      Node* add = m.Int32AddWithOverflow(m.Parameter(0), m.Int32Constant(*i));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      FOR_INT32_INPUTS(j) {
+        int expected_ovf = bits::SignedAddOverflow32(*i, *j, &expected_val);
+        CHECK_EQ(expected_ovf, m.Call(*j));
+        CHECK_EQ(expected_val, actual_val);
+      }
+    }
+    FOR_INT32_INPUTS(j) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* add =
+          m.Int32AddWithOverflow(m.Int32Constant(*i), m.Int32Constant(*j));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      int expected_ovf = bits::SignedAddOverflow32(*i, *j, &expected_val);
+      CHECK_EQ(expected_ovf, m.Call());
+      CHECK_EQ(expected_val, actual_val);
+    }
+  }
+}
+
+
+TEST(RunInt32AddWithOverflowInBranchP) {
+  int constant = 911777;
+  MLabel blocka, blockb;
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+  Node* add = m.Int32AddWithOverflow(bt.param0, bt.param1);
+  Node* ovf = m.Projection(1, add);
+  m.Branch(ovf, &blocka, &blockb);
+  m.Bind(&blocka);
+  bt.AddReturn(m.Int32Constant(constant));
+  m.Bind(&blockb);
+  Node* val = m.Projection(0, add);
+  bt.AddReturn(val);
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t expected;
+      if (bits::SignedAddOverflow32(*i, *j, &expected)) expected = constant;
+      CHECK_EQ(expected, bt.call(*i, *j));
+    }
+  }
+}
+
+
+TEST(RunInt32SubWithOverflowP) {
+  int32_t actual_val = -1;
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+  Node* add = m.Int32SubWithOverflow(bt.param0, bt.param1);
+  Node* val = m.Projection(0, add);
+  Node* ovf = m.Projection(1, add);
+  m.StoreToPointer(&actual_val, kMachInt32, val);
+  bt.AddReturn(ovf);
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t expected_val;
+      int expected_ovf = bits::SignedSubOverflow32(*i, *j, &expected_val);
+      CHECK_EQ(expected_ovf, bt.call(*i, *j));
+      CHECK_EQ(expected_val, actual_val);
+    }
+  }
+}
+
+
+TEST(RunInt32SubWithOverflowImm) {
+  int32_t actual_val = -1, expected_val = 0;
+  FOR_INT32_INPUTS(i) {
+    {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      Node* add = m.Int32SubWithOverflow(m.Int32Constant(*i), m.Parameter(0));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      FOR_INT32_INPUTS(j) {
+        int expected_ovf = bits::SignedSubOverflow32(*i, *j, &expected_val);
+        CHECK_EQ(expected_ovf, m.Call(*j));
+        CHECK_EQ(expected_val, actual_val);
+      }
+    }
+    {
+      RawMachineAssemblerTester<int32_t> m(kMachInt32);
+      Node* add = m.Int32SubWithOverflow(m.Parameter(0), m.Int32Constant(*i));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      FOR_INT32_INPUTS(j) {
+        int expected_ovf = bits::SignedSubOverflow32(*j, *i, &expected_val);
+        CHECK_EQ(expected_ovf, m.Call(*j));
+        CHECK_EQ(expected_val, actual_val);
+      }
+    }
+    FOR_INT32_INPUTS(j) {
+      RawMachineAssemblerTester<int32_t> m;
+      Node* add =
+          m.Int32SubWithOverflow(m.Int32Constant(*i), m.Int32Constant(*j));
+      Node* val = m.Projection(0, add);
+      Node* ovf = m.Projection(1, add);
+      m.StoreToPointer(&actual_val, kMachInt32, val);
+      m.Return(ovf);
+      int expected_ovf = bits::SignedSubOverflow32(*i, *j, &expected_val);
+      CHECK_EQ(expected_ovf, m.Call());
+      CHECK_EQ(expected_val, actual_val);
+    }
+  }
+}
+
+
+TEST(RunInt32SubWithOverflowInBranchP) {
+  int constant = 911999;
+  MLabel blocka, blockb;
+  RawMachineAssemblerTester<int32_t> m;
+  Int32BinopTester bt(&m);
+  Node* sub = m.Int32SubWithOverflow(bt.param0, bt.param1);
+  Node* ovf = m.Projection(1, sub);
+  m.Branch(ovf, &blocka, &blockb);
+  m.Bind(&blocka);
+  bt.AddReturn(m.Int32Constant(constant));
+  m.Bind(&blockb);
+  Node* val = m.Projection(0, sub);
+  bt.AddReturn(val);
+  FOR_INT32_INPUTS(i) {
+    FOR_INT32_INPUTS(j) {
+      int32_t expected;
+      if (bits::SignedSubOverflow32(*i, *j, &expected)) expected = constant;
+      CHECK_EQ(expected, bt.call(*i, *j));
+    }
+  }
+}
+
+
+TEST(RunChangeInt32ToInt64P) {
+  if (kPointerSize < 8) return;
+  int64_t actual = -1;
+  RawMachineAssemblerTester<int32_t> m(kMachInt32);
+  m.StoreToPointer(&actual, kMachInt64, m.ChangeInt32ToInt64(m.Parameter(0)));
+  m.Return(m.Int32Constant(0));
+  FOR_INT32_INPUTS(i) {
+    int64_t expected = *i;
+    CHECK_EQ(0, m.Call(*i));
+    CHECK_EQ(expected, actual);
+  }
+}
+
+
+TEST(RunChangeUint32ToUint64P) {
+  if (kPointerSize < 8) return;
+  int64_t actual = -1;
+  RawMachineAssemblerTester<int32_t> m(kMachUint32);
+  m.StoreToPointer(&actual, kMachUint64,
+                   m.ChangeUint32ToUint64(m.Parameter(0)));
+  m.Return(m.Int32Constant(0));
+  FOR_UINT32_INPUTS(i) {
+    int64_t expected = static_cast<uint64_t>(*i);
+    CHECK_EQ(0, m.Call(*i));
+    CHECK_EQ(expected, actual);
+  }
+}
+
+
+TEST(RunTruncateInt64ToInt32P) {
+  if (kPointerSize < 8) return;
+  int64_t expected = -1;
+  RawMachineAssemblerTester<int32_t> m;
+  m.Return(m.TruncateInt64ToInt32(m.LoadFromPointer(&expected, kMachInt64)));
+  FOR_UINT32_INPUTS(i) {
+    FOR_UINT32_INPUTS(j) {
+      expected = (static_cast<uint64_t>(*j) << 32) | *i;
+      CHECK_UINT32_EQ(expected, m.Call());
+    }
+  }
+}
+
+
+TEST(RunTruncateFloat64ToInt32P) {
+  struct {
+    double from;
+    double raw;
+  } kValues[] = {{0, 0},
+                 {0.5, 0},
+                 {-0.5, 0},
+                 {1.5, 1},
+                 {-1.5, -1},
+                 {5.5, 5},
+                 {-5.0, -5},
+                 {v8::base::OS::nan_value(), 0},
+                 {std::numeric_limits<double>::infinity(), 0},
+                 {-v8::base::OS::nan_value(), 0},
+                 {-std::numeric_limits<double>::infinity(), 0},
+                 {4.94065645841e-324, 0},
+                 {-4.94065645841e-324, 0},
+                 {0.9999999999999999, 0},
+                 {-0.9999999999999999, 0},
+                 {4294967296.0, 0},
+                 {-4294967296.0, 0},
+                 {9223372036854775000.0, 4294966272.0},
+                 {-9223372036854775000.0, -4294966272.0},
+                 {4.5036e+15, 372629504},
+                 {-4.5036e+15, -372629504},
+                 {287524199.5377777, 0x11234567},
+                 {-287524199.5377777, -0x11234567},
+                 {2300193596.302222, 2300193596.0},
+                 {-2300193596.302222, -2300193596.0},
+                 {4600387192.604444, 305419896},
+                 {-4600387192.604444, -305419896},
+                 {4823855600872397.0, 1737075661},
+                 {-4823855600872397.0, -1737075661},
+                 {4503603922337791.0, -1},
+                 {-4503603922337791.0, 1},
+                 {4503601774854143.0, 2147483647},
+                 {-4503601774854143.0, -2147483647},
+                 {9007207844675582.0, -2},
+                 {-9007207844675582.0, 2},
+                 {2.4178527921507624e+24, -536870912},
+                 {-2.4178527921507624e+24, 536870912},
+                 {2.417853945072267e+24, -536870912},
+                 {-2.417853945072267e+24, 536870912},
+                 {4.8357055843015248e+24, -1073741824},
+                 {-4.8357055843015248e+24, 1073741824},
+                 {4.8357078901445341e+24, -1073741824},
+                 {-4.8357078901445341e+24, 1073741824},
+                 {2147483647.0, 2147483647.0},
+                 {-2147483648.0, -2147483648.0},
+                 {9.6714111686030497e+24, -2147483648.0},
+                 {-9.6714111686030497e+24, -2147483648.0},
+                 {9.6714157802890681e+24, -2147483648.0},
+                 {-9.6714157802890681e+24, -2147483648.0},
+                 {1.9342813113834065e+25, 2147483648.0},
+                 {-1.9342813113834065e+25, 2147483648.0},
+                 {3.868562622766813e+25, 0},
+                 {-3.868562622766813e+25, 0},
+                 {1.7976931348623157e+308, 0},
+                 {-1.7976931348623157e+308, 0}};
+  double input = -1.0;
+  RawMachineAssemblerTester<int32_t> m;
+  m.Return(m.TruncateFloat64ToInt32(m.LoadFromPointer(&input, kMachFloat64)));
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    input = kValues[i].from;
+    uint64_t expected = static_cast<int64_t>(kValues[i].raw);
+    CHECK_EQ(static_cast<int>(expected), m.Call());
+  }
+}
+
+#endif  // V8_TURBOFAN_TARGET
diff --git a/test/cctest/compiler/test-run-properties.cc b/test/cctest/compiler/test-run-properties.cc
new file mode 100644
index 0000000..d4442f7
--- /dev/null
+++ b/test/cctest/compiler/test-run-properties.cc
@@ -0,0 +1,141 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+template <typename U>
+static void TypedArrayLoadHelper(const char* array_type) {
+  static const uint32_t kValues[] = {
+      0x00000000, 0x00000001, 0x00000023, 0x00000042, 0x12345678, 0x87654321,
+      0x0000003f, 0x0000007f, 0x00003fff, 0x00007fff, 0x3fffffff, 0x7fffffff,
+      0x000000ff, 0x00000080, 0x0000ffff, 0x00008000, 0xffffffff, 0x80000000};
+  EmbeddedVector<char, 1024> values_buffer;
+  StringBuilder values_builder(values_buffer.start(), values_buffer.length());
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    values_builder.AddFormatted("a[%d] = 0x%08x;", i, kValues[i]);
+  }
+
+  // Note that below source creates two different typed arrays with distinct
+  // elements kind to get coverage for both access patterns:
+  // - IsFixedTypedArrayElementsKind(x)
+  // - IsExternalArrayElementsKind(y)
+  const char* source =
+      "(function(a) {"
+      "  var x = (a = new %sArray(%d)); %s;"
+      "  var y = (a = new %sArray(%d)); %s; %%TypedArrayGetBuffer(y);"
+      "  if (!%%HasFixed%sElements(x)) %%AbortJS('x');"
+      "  if (!%%HasExternal%sElements(y)) %%AbortJS('y');"
+      "  function f(a,b) {"
+      "    a = a | 0; b = b | 0;"
+      "    return x[a] + y[b];"
+      "  }"
+      "  return f;"
+      "})()";
+  EmbeddedVector<char, 1024> source_buffer;
+  SNPrintF(source_buffer, source, array_type, arraysize(kValues),
+           values_buffer.start(), array_type, arraysize(kValues),
+           values_buffer.start(), array_type, array_type);
+
+  FunctionTester T(
+      source_buffer.start(),
+      CompilationInfo::kContextSpecializing | CompilationInfo::kTypingEnabled);
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    for (size_t j = 0; j < arraysize(kValues); ++j) {
+      volatile U value_a = static_cast<U>(kValues[i]);
+      volatile U value_b = static_cast<U>(kValues[j]);
+      double expected =
+          static_cast<double>(value_a) + static_cast<double>(value_b);
+      T.CheckCall(T.Val(expected), T.Val(static_cast<double>(i)),
+                  T.Val(static_cast<double>(j)));
+    }
+  }
+}
+
+
+TEST(TypedArrayLoad) {
+  FLAG_typed_array_max_size_in_heap = 256;
+  TypedArrayLoadHelper<int8_t>("Int8");
+  TypedArrayLoadHelper<uint8_t>("Uint8");
+  TypedArrayLoadHelper<int16_t>("Int16");
+  TypedArrayLoadHelper<uint16_t>("Uint16");
+  TypedArrayLoadHelper<int32_t>("Int32");
+  TypedArrayLoadHelper<uint32_t>("Uint32");
+  TypedArrayLoadHelper<float>("Float32");
+  TypedArrayLoadHelper<double>("Float64");
+  // TODO(mstarzinger): Add tests for ClampedUint8.
+}
+
+
+template <typename U>
+static void TypedArrayStoreHelper(const char* array_type) {
+  static const uint32_t kValues[] = {
+      0x00000000, 0x00000001, 0x00000023, 0x00000042, 0x12345678, 0x87654321,
+      0x0000003f, 0x0000007f, 0x00003fff, 0x00007fff, 0x3fffffff, 0x7fffffff,
+      0x000000ff, 0x00000080, 0x0000ffff, 0x00008000, 0xffffffff, 0x80000000};
+  EmbeddedVector<char, 1024> values_buffer;
+  StringBuilder values_builder(values_buffer.start(), values_buffer.length());
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    values_builder.AddFormatted("a[%d] = 0x%08x;", i, kValues[i]);
+  }
+
+  // Note that below source creates two different typed arrays with distinct
+  // elements kind to get coverage for both access patterns:
+  // - IsFixedTypedArrayElementsKind(x)
+  // - IsExternalArrayElementsKind(y)
+  const char* source =
+      "(function(a) {"
+      "  var x = (a = new %sArray(%d)); %s;"
+      "  var y = (a = new %sArray(%d)); %s; %%TypedArrayGetBuffer(y);"
+      "  if (!%%HasFixed%sElements(x)) %%AbortJS('x');"
+      "  if (!%%HasExternal%sElements(y)) %%AbortJS('y');"
+      "  function f(a,b) {"
+      "    a = a | 0; b = b | 0;"
+      "    var t = x[a];"
+      "    x[a] = y[b];"
+      "    y[b] = t;"
+      "    t = y[b];"
+      "    y[b] = x[a];"
+      "    x[a] = t;"
+      "    return x[a] + y[b];"
+      "  }"
+      "  return f;"
+      "})()";
+  EmbeddedVector<char, 2048> source_buffer;
+  SNPrintF(source_buffer, source, array_type, arraysize(kValues),
+           values_buffer.start(), array_type, arraysize(kValues),
+           values_buffer.start(), array_type, array_type);
+
+  FunctionTester T(
+      source_buffer.start(),
+      CompilationInfo::kContextSpecializing | CompilationInfo::kTypingEnabled);
+  for (size_t i = 0; i < arraysize(kValues); ++i) {
+    for (size_t j = 0; j < arraysize(kValues); ++j) {
+      volatile U value_a = static_cast<U>(kValues[i]);
+      volatile U value_b = static_cast<U>(kValues[j]);
+      double expected =
+          static_cast<double>(value_a) + static_cast<double>(value_b);
+      T.CheckCall(T.Val(expected), T.Val(static_cast<double>(i)),
+                  T.Val(static_cast<double>(j)));
+    }
+  }
+}
+
+
+TEST(TypedArrayStore) {
+  FLAG_typed_array_max_size_in_heap = 256;
+  TypedArrayStoreHelper<int8_t>("Int8");
+  TypedArrayStoreHelper<uint8_t>("Uint8");
+  TypedArrayStoreHelper<int16_t>("Int16");
+  TypedArrayStoreHelper<uint16_t>("Uint16");
+  TypedArrayStoreHelper<int32_t>("Int32");
+  TypedArrayStoreHelper<uint32_t>("Uint32");
+  TypedArrayStoreHelper<float>("Float32");
+  TypedArrayStoreHelper<double>("Float64");
+  // TODO(mstarzinger): Add tests for ClampedUint8.
+}
diff --git a/test/cctest/compiler/test-run-variables.cc b/test/cctest/compiler/test-run-variables.cc
new file mode 100644
index 0000000..bf86e0d
--- /dev/null
+++ b/test/cctest/compiler/test-run-variables.cc
@@ -0,0 +1,121 @@
+// 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/v8.h"
+
+#include "test/cctest/compiler/function-tester.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static const char* throws = NULL;
+
+static const char* load_tests[] = {
+    "var x = a; r = x",                       "123",       "0",
+    "var x = (r = x)",                        "undefined", "undefined",
+    "var x = (a?1:2); r = x",                 "1",         "2",
+    "const x = a; r = x",                     "123",       "0",
+    "const x = (r = x)",                      "undefined", "undefined",
+    "const x = (a?3:4); r = x",               "3",         "4",
+    "'use strict'; const x = a; r = x",       "123",       "0",
+    "'use strict'; const x = (r = x)",        throws,      throws,
+    "'use strict'; const x = (a?5:6); r = x", "5",         "6",
+    "'use strict'; let x = a; r = x",         "123",       "0",
+    "'use strict'; let x = (r = x)",          throws,      throws,
+    "'use strict'; let x = (a?7:8); r = x",   "7",         "8",
+    NULL};
+
+static const char* store_tests[] = {
+    "var x = 1; x = a; r = x",                     "123",  "0",
+    "var x = (a?(x=4,2):3); r = x",                "2",    "3",
+    "var x = (a?4:5); x = a; r = x",               "123",  "0",
+    "const x = 1; x = a; r = x",                   "1",    "1",
+    "const x = (a?(x=4,2):3); r = x",              "2",    "3",
+    "const x = (a?4:5); x = a; r = x",             "4",    "5",
+    // Assignments to 'const' are SyntaxErrors, handled by the parser,
+    // hence we cannot test them here because they are early errors.
+    "'use strict'; let x = 1; x = a; r = x",       "123",  "0",
+    "'use strict'; let x = (a?(x=4,2):3); r = x",  throws, "3",
+    "'use strict'; let x = (a?4:5); x = a; r = x", "123",  "0",
+    NULL};
+
+static const char* bind_tests[] = {
+    "if (a) { const x = a }; r = x;",            "123", "undefined",
+    "for (; a > 0; a--) { const x = a }; r = x", "123", "undefined",
+    // Re-initialization of variables other than legacy 'const' is not
+    // possible due to sane variable scoping, hence no tests here.
+    NULL};
+
+
+static void RunVariableTests(const char* source, const char* tests[]) {
+  FLAG_harmony_scoping = true;
+  EmbeddedVector<char, 512> buffer;
+
+  for (int i = 0; tests[i] != NULL; i += 3) {
+    SNPrintF(buffer, source, tests[i]);
+    PrintF("#%d: %s\n", i / 3, buffer.start());
+    FunctionTester T(buffer.start());
+
+    // Check function with non-falsey parameter.
+    if (tests[i + 1] != throws) {
+      Handle<Object> r = v8::Utils::OpenHandle(*CompileRun(tests[i + 1]));
+      T.CheckCall(r, T.Val(123), T.Val("result"));
+    } else {
+      T.CheckThrows(T.Val(123), T.Val("result"));
+    }
+
+    // Check function with falsey parameter.
+    if (tests[i + 2] != throws) {
+      Handle<Object> r = v8::Utils::OpenHandle(*CompileRun(tests[i + 2]));
+      T.CheckCall(r, T.Val(0.0), T.Val("result"));
+    } else {
+      T.CheckThrows(T.Val(0.0), T.Val("result"));
+    }
+  }
+}
+
+
+TEST(StackLoadVariables) {
+  const char* source = "(function(a,r) { %s; return r; })";
+  RunVariableTests(source, load_tests);
+}
+
+
+TEST(ContextLoadVariables) {
+  const char* source = "(function(a,r) { %s; function f() {x} return r; })";
+  RunVariableTests(source, load_tests);
+}
+
+
+TEST(StackStoreVariables) {
+  const char* source = "(function(a,r) { %s; return r; })";
+  RunVariableTests(source, store_tests);
+}
+
+
+TEST(ContextStoreVariables) {
+  const char* source = "(function(a,r) { %s; function f() {x} return r; })";
+  RunVariableTests(source, store_tests);
+}
+
+
+TEST(StackInitializeVariables) {
+  const char* source = "(function(a,r) { %s; return r; })";
+  RunVariableTests(source, bind_tests);
+}
+
+
+TEST(ContextInitializeVariables) {
+  const char* source = "(function(a,r) { %s; function f() {x} return r; })";
+  RunVariableTests(source, bind_tests);
+}
+
+
+TEST(SelfReferenceVariable) {
+  FunctionTester T("(function self() { return self; })");
+
+  T.CheckCall(T.function);
+  CompileRun("var self = 'not a function'");
+  T.CheckCall(T.function);
+}
diff --git a/test/cctest/compiler/test-schedule.cc b/test/cctest/compiler/test-schedule.cc
new file mode 100644
index 0000000..6c05c05
--- /dev/null
+++ b/test/cctest/compiler/test-schedule.cc
@@ -0,0 +1,145 @@
+// 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.
+
+#include "src/v8.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "test/cctest/cctest.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+static SimpleOperator dummy_operator(IrOpcode::kParameter, Operator::kNoWrite,
+                                     0, 0, "dummy");
+
+TEST(TestScheduleAllocation) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  CHECK_NE(NULL, schedule.start());
+  CHECK_EQ(schedule.start(), *(schedule.all_blocks().begin()));
+}
+
+
+TEST(TestScheduleAddNode) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* n1 = graph.NewNode(&dummy_operator);
+
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* entry = schedule.start();
+  schedule.AddNode(entry, n0);
+  schedule.AddNode(entry, n1);
+
+  CHECK_EQ(entry, schedule.block(n0));
+  CHECK_EQ(entry, schedule.block(n1));
+  CHECK(schedule.SameBasicBlock(n0, n1));
+
+  Node* n2 = graph.NewNode(&dummy_operator);
+  CHECK_EQ(NULL, schedule.block(n2));
+}
+
+
+TEST(TestScheduleAddGoto) {
+  HandleAndZoneScope scope;
+
+  Schedule schedule(scope.main_zone());
+  BasicBlock* entry = schedule.start();
+  BasicBlock* next = schedule.NewBasicBlock();
+
+  schedule.AddGoto(entry, next);
+
+  CHECK_EQ(0, entry->PredecessorCount());
+  CHECK_EQ(1, entry->SuccessorCount());
+  CHECK_EQ(next, entry->SuccessorAt(0));
+
+  CHECK_EQ(1, next->PredecessorCount());
+  CHECK_EQ(entry, next->PredecessorAt(0));
+  CHECK_EQ(0, next->SuccessorCount());
+}
+
+
+TEST(TestScheduleAddBranch) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* entry = schedule.start();
+  BasicBlock* tblock = schedule.NewBasicBlock();
+  BasicBlock* fblock = schedule.NewBasicBlock();
+
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+  Node* n0 = graph.NewNode(&dummy_operator);
+  Node* b = graph.NewNode(common.Branch(), n0);
+
+  schedule.AddBranch(entry, b, tblock, fblock);
+
+  CHECK_EQ(0, entry->PredecessorCount());
+  CHECK_EQ(2, entry->SuccessorCount());
+  CHECK_EQ(tblock, entry->SuccessorAt(0));
+  CHECK_EQ(fblock, entry->SuccessorAt(1));
+
+  CHECK_EQ(1, tblock->PredecessorCount());
+  CHECK_EQ(entry, tblock->PredecessorAt(0));
+  CHECK_EQ(0, tblock->SuccessorCount());
+
+  CHECK_EQ(1, fblock->PredecessorCount());
+  CHECK_EQ(entry, fblock->PredecessorAt(0));
+  CHECK_EQ(0, fblock->SuccessorCount());
+}
+
+
+TEST(TestScheduleAddReturn) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  Graph graph(scope.main_zone());
+  Node* n0 = graph.NewNode(&dummy_operator);
+  BasicBlock* entry = schedule.start();
+  schedule.AddReturn(entry, n0);
+
+  CHECK_EQ(0, entry->PredecessorCount());
+  CHECK_EQ(1, entry->SuccessorCount());
+  CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
+}
+
+
+TEST(TestScheduleAddThrow) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  Graph graph(scope.main_zone());
+  Node* n0 = graph.NewNode(&dummy_operator);
+  BasicBlock* entry = schedule.start();
+  schedule.AddThrow(entry, n0);
+
+  CHECK_EQ(0, entry->PredecessorCount());
+  CHECK_EQ(1, entry->SuccessorCount());
+  CHECK_EQ(schedule.end(), entry->SuccessorAt(0));
+}
+
+
+TEST(BuildMulNodeGraph) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+  MachineOperatorBuilder machine;
+
+  Node* start = graph.NewNode(common.Start(0));
+  graph.SetStart(start);
+  Node* param0 = graph.NewNode(common.Parameter(0), graph.start());
+  Node* param1 = graph.NewNode(common.Parameter(1), graph.start());
+
+  Node* mul = graph.NewNode(machine.Int32Mul(), param0, param1);
+  Node* ret = graph.NewNode(common.Return(), mul, start);
+
+  USE(ret);
+}
diff --git a/test/cctest/compiler/test-scheduler.cc b/test/cctest/compiler/test-scheduler.cc
new file mode 100644
index 0000000..cf33123
--- /dev/null
+++ b/test/cctest/compiler/test-scheduler.cc
@@ -0,0 +1,1713 @@
+// 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/v8.h"
+#include "test/cctest/cctest.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/generic-node.h"
+#include "src/compiler/graph.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/js-operator.h"
+#include "src/compiler/machine-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/operator.h"
+#include "src/compiler/schedule.h"
+#include "src/compiler/scheduler.h"
+#include "src/compiler/verifier.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+// TODO(titzer): pull RPO tests out to their own file.
+struct TestLoop {
+  int count;
+  BasicBlock** nodes;
+  BasicBlock* header() { return nodes[0]; }
+  BasicBlock* last() { return nodes[count - 1]; }
+  ~TestLoop() { delete[] nodes; }
+};
+
+
+static TestLoop* CreateLoop(Schedule* schedule, int count) {
+  TestLoop* loop = new TestLoop();
+  loop->count = count;
+  loop->nodes = new BasicBlock* [count];
+  for (int i = 0; i < count; i++) {
+    loop->nodes[i] = schedule->NewBasicBlock();
+    if (i > 0) schedule->AddSuccessor(loop->nodes[i - 1], loop->nodes[i]);
+  }
+  schedule->AddSuccessor(loop->nodes[count - 1], loop->nodes[0]);
+  return loop;
+}
+
+
+static void CheckRPONumbers(BasicBlockVector* order, int expected,
+                            bool loops_allowed) {
+  CHECK_EQ(expected, static_cast<int>(order->size()));
+  for (int i = 0; i < static_cast<int>(order->size()); i++) {
+    CHECK(order->at(i)->rpo_number_ == i);
+    if (!loops_allowed) CHECK_LT(order->at(i)->loop_end_, 0);
+  }
+}
+
+
+static void CheckLoopContains(BasicBlock** blocks, int body_size) {
+  BasicBlock* header = blocks[0];
+  CHECK_GT(header->loop_end_, 0);
+  CHECK_EQ(body_size, (header->loop_end_ - header->rpo_number_));
+  for (int i = 0; i < body_size; i++) {
+    int num = blocks[i]->rpo_number_;
+    CHECK(num >= header->rpo_number_ && num < header->loop_end_);
+    CHECK(header->LoopContains(blocks[i]));
+    CHECK(header->IsLoopHeader() || blocks[i]->loop_header_ == header);
+  }
+}
+
+
+static int GetScheduledNodeCount(Schedule* schedule) {
+  int node_count = 0;
+  for (BasicBlockVectorIter i = schedule->rpo_order()->begin();
+       i != schedule->rpo_order()->end(); ++i) {
+    BasicBlock* block = *i;
+    for (BasicBlock::const_iterator j = block->begin(); j != block->end();
+         ++j) {
+      ++node_count;
+    }
+    BasicBlock::Control control = block->control_;
+    if (control != BasicBlock::kNone) {
+      ++node_count;
+    }
+  }
+  return node_count;
+}
+
+
+static Schedule* ComputeAndVerifySchedule(int expected, Graph* graph) {
+  if (FLAG_trace_turbo) {
+    OFStream os(stdout);
+    os << AsDOT(*graph);
+  }
+
+  Schedule* schedule = Scheduler::ComputeSchedule(graph);
+
+  if (FLAG_trace_turbo_scheduler) {
+    OFStream os(stdout);
+    os << *schedule << endl;
+  }
+  ScheduleVerifier::Run(schedule);
+  CHECK_EQ(expected, GetScheduledNodeCount(schedule));
+  return schedule;
+}
+
+
+TEST(RPODegenerate1) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 1, false);
+  CHECK_EQ(schedule.start(), order->at(0));
+}
+
+
+TEST(RPODegenerate2) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  schedule.AddGoto(schedule.start(), schedule.end());
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 2, false);
+  CHECK_EQ(schedule.start(), order->at(0));
+  CHECK_EQ(schedule.end(), order->at(1));
+}
+
+
+TEST(RPOLine) {
+  HandleAndZoneScope scope;
+
+  for (int i = 0; i < 10; i++) {
+    Schedule schedule(scope.main_zone());
+
+    BasicBlock* last = schedule.start();
+    for (int j = 0; j < i; j++) {
+      BasicBlock* block = schedule.NewBasicBlock();
+      schedule.AddGoto(last, block);
+      last = block;
+    }
+    BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+    CheckRPONumbers(order, 1 + i, false);
+
+    Schedule::BasicBlocks blocks(schedule.all_blocks());
+    for (Schedule::BasicBlocks::iterator iter = blocks.begin();
+         iter != blocks.end(); ++iter) {
+      BasicBlock* block = *iter;
+      if (block->rpo_number_ >= 0 && block->SuccessorCount() == 1) {
+        CHECK(block->rpo_number_ + 1 == block->SuccessorAt(0)->rpo_number_);
+      }
+    }
+  }
+}
+
+
+TEST(RPOSelfLoop) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  schedule.AddSuccessor(schedule.start(), schedule.start());
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 1, true);
+  BasicBlock* loop[] = {schedule.start()};
+  CheckLoopContains(loop, 1);
+}
+
+
+TEST(RPOEntryLoop) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  schedule.AddSuccessor(schedule.start(), schedule.end());
+  schedule.AddSuccessor(schedule.end(), schedule.start());
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 2, true);
+  BasicBlock* loop[] = {schedule.start(), schedule.end()};
+  CheckLoopContains(loop, 2);
+}
+
+
+TEST(RPOEndLoop) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
+  schedule.AddSuccessor(schedule.start(), loop1->header());
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 3, true);
+  CheckLoopContains(loop1->nodes, loop1->count);
+}
+
+
+TEST(RPOEndLoopNested) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+  SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 2));
+  schedule.AddSuccessor(schedule.start(), loop1->header());
+  schedule.AddSuccessor(loop1->last(), schedule.start());
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 3, true);
+  CheckLoopContains(loop1->nodes, loop1->count);
+}
+
+
+TEST(RPODiamond) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(A, C);
+  schedule.AddSuccessor(B, D);
+  schedule.AddSuccessor(C, D);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 4, false);
+
+  CHECK_EQ(0, A->rpo_number_);
+  CHECK((B->rpo_number_ == 1 && C->rpo_number_ == 2) ||
+        (B->rpo_number_ == 2 && C->rpo_number_ == 1));
+  CHECK_EQ(3, D->rpo_number_);
+}
+
+
+TEST(RPOLoop1) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, C);
+  schedule.AddSuccessor(C, B);
+  schedule.AddSuccessor(C, D);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 4, true);
+  BasicBlock* loop[] = {B, C};
+  CheckLoopContains(loop, 2);
+}
+
+
+TEST(RPOLoop2) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, C);
+  schedule.AddSuccessor(C, B);
+  schedule.AddSuccessor(B, D);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 4, true);
+  BasicBlock* loop[] = {B, C};
+  CheckLoopContains(loop, 2);
+}
+
+
+TEST(RPOLoopN) {
+  HandleAndZoneScope scope;
+
+  for (int i = 0; i < 11; i++) {
+    Schedule schedule(scope.main_zone());
+    BasicBlock* A = schedule.start();
+    BasicBlock* B = schedule.NewBasicBlock();
+    BasicBlock* C = schedule.NewBasicBlock();
+    BasicBlock* D = schedule.NewBasicBlock();
+    BasicBlock* E = schedule.NewBasicBlock();
+    BasicBlock* F = schedule.NewBasicBlock();
+    BasicBlock* G = schedule.end();
+
+    schedule.AddSuccessor(A, B);
+    schedule.AddSuccessor(B, C);
+    schedule.AddSuccessor(C, D);
+    schedule.AddSuccessor(D, E);
+    schedule.AddSuccessor(E, F);
+    schedule.AddSuccessor(F, B);
+    schedule.AddSuccessor(B, G);
+
+    // Throw in extra backedges from time to time.
+    if (i == 1) schedule.AddSuccessor(B, B);
+    if (i == 2) schedule.AddSuccessor(C, B);
+    if (i == 3) schedule.AddSuccessor(D, B);
+    if (i == 4) schedule.AddSuccessor(E, B);
+    if (i == 5) schedule.AddSuccessor(F, B);
+
+    // Throw in extra loop exits from time to time.
+    if (i == 6) schedule.AddSuccessor(B, G);
+    if (i == 7) schedule.AddSuccessor(C, G);
+    if (i == 8) schedule.AddSuccessor(D, G);
+    if (i == 9) schedule.AddSuccessor(E, G);
+    if (i == 10) schedule.AddSuccessor(F, G);
+
+    BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+    CheckRPONumbers(order, 7, true);
+    BasicBlock* loop[] = {B, C, D, E, F};
+    CheckLoopContains(loop, 5);
+  }
+}
+
+
+TEST(RPOLoopNest1) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.NewBasicBlock();
+  BasicBlock* E = schedule.NewBasicBlock();
+  BasicBlock* F = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, C);
+  schedule.AddSuccessor(C, D);
+  schedule.AddSuccessor(D, C);
+  schedule.AddSuccessor(D, E);
+  schedule.AddSuccessor(E, B);
+  schedule.AddSuccessor(E, F);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 6, true);
+  BasicBlock* loop1[] = {B, C, D, E};
+  CheckLoopContains(loop1, 4);
+
+  BasicBlock* loop2[] = {C, D};
+  CheckLoopContains(loop2, 2);
+}
+
+
+TEST(RPOLoopNest2) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.NewBasicBlock();
+  BasicBlock* E = schedule.NewBasicBlock();
+  BasicBlock* F = schedule.NewBasicBlock();
+  BasicBlock* G = schedule.NewBasicBlock();
+  BasicBlock* H = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, C);
+  schedule.AddSuccessor(C, D);
+  schedule.AddSuccessor(D, E);
+  schedule.AddSuccessor(E, F);
+  schedule.AddSuccessor(F, G);
+  schedule.AddSuccessor(G, H);
+
+  schedule.AddSuccessor(E, D);
+  schedule.AddSuccessor(F, C);
+  schedule.AddSuccessor(G, B);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 8, true);
+  BasicBlock* loop1[] = {B, C, D, E, F, G};
+  CheckLoopContains(loop1, 6);
+
+  BasicBlock* loop2[] = {C, D, E, F};
+  CheckLoopContains(loop2, 4);
+
+  BasicBlock* loop3[] = {D, E};
+  CheckLoopContains(loop3, 2);
+}
+
+
+TEST(RPOLoopFollow1) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 1));
+  SmartPointer<TestLoop> loop2(CreateLoop(&schedule, 1));
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* E = schedule.end();
+
+  schedule.AddSuccessor(A, loop1->header());
+  schedule.AddSuccessor(loop1->header(), loop2->header());
+  schedule.AddSuccessor(loop2->last(), E);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+
+  CheckLoopContains(loop1->nodes, loop1->count);
+
+  CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
+  CheckLoopContains(loop1->nodes, loop1->count);
+  CheckLoopContains(loop2->nodes, loop2->count);
+}
+
+
+TEST(RPOLoopFollow2) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 1));
+  SmartPointer<TestLoop> loop2(CreateLoop(&schedule, 1));
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* S = schedule.NewBasicBlock();
+  BasicBlock* E = schedule.end();
+
+  schedule.AddSuccessor(A, loop1->header());
+  schedule.AddSuccessor(loop1->header(), S);
+  schedule.AddSuccessor(S, loop2->header());
+  schedule.AddSuccessor(loop2->last(), E);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+
+  CheckLoopContains(loop1->nodes, loop1->count);
+
+  CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
+  CheckLoopContains(loop1->nodes, loop1->count);
+  CheckLoopContains(loop2->nodes, loop2->count);
+}
+
+
+TEST(RPOLoopFollowN) {
+  HandleAndZoneScope scope;
+
+  for (int size = 1; size < 5; size++) {
+    for (int exit = 0; exit < size; exit++) {
+      Schedule schedule(scope.main_zone());
+      SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
+      SmartPointer<TestLoop> loop2(CreateLoop(&schedule, size));
+      BasicBlock* A = schedule.start();
+      BasicBlock* E = schedule.end();
+
+      schedule.AddSuccessor(A, loop1->header());
+      schedule.AddSuccessor(loop1->nodes[exit], loop2->header());
+      schedule.AddSuccessor(loop2->nodes[exit], E);
+      BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+      CheckLoopContains(loop1->nodes, loop1->count);
+
+      CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
+      CheckLoopContains(loop1->nodes, loop1->count);
+      CheckLoopContains(loop2->nodes, loop2->count);
+    }
+  }
+}
+
+
+TEST(RPONestedLoopFollow1) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  SmartPointer<TestLoop> loop1(CreateLoop(&schedule, 1));
+  SmartPointer<TestLoop> loop2(CreateLoop(&schedule, 1));
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* E = schedule.end();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, loop1->header());
+  schedule.AddSuccessor(loop1->header(), loop2->header());
+  schedule.AddSuccessor(loop2->last(), C);
+  schedule.AddSuccessor(C, E);
+  schedule.AddSuccessor(C, B);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+
+  CheckLoopContains(loop1->nodes, loop1->count);
+
+  CHECK_EQ(schedule.BasicBlockCount(), static_cast<int>(order->size()));
+  CheckLoopContains(loop1->nodes, loop1->count);
+  CheckLoopContains(loop2->nodes, loop2->count);
+
+  BasicBlock* loop3[] = {B, loop1->nodes[0], loop2->nodes[0], C};
+  CheckLoopContains(loop3, 4);
+}
+
+
+TEST(RPOLoopBackedges1) {
+  HandleAndZoneScope scope;
+
+  int size = 8;
+  for (int i = 0; i < size; i++) {
+    for (int j = 0; j < size; j++) {
+      Schedule schedule(scope.main_zone());
+      BasicBlock* A = schedule.start();
+      BasicBlock* E = schedule.end();
+
+      SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
+      schedule.AddSuccessor(A, loop1->header());
+      schedule.AddSuccessor(loop1->last(), E);
+
+      schedule.AddSuccessor(loop1->nodes[i], loop1->header());
+      schedule.AddSuccessor(loop1->nodes[j], E);
+
+      BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+      CheckRPONumbers(order, schedule.BasicBlockCount(), true);
+      CheckLoopContains(loop1->nodes, loop1->count);
+    }
+  }
+}
+
+
+TEST(RPOLoopOutedges1) {
+  HandleAndZoneScope scope;
+
+  int size = 8;
+  for (int i = 0; i < size; i++) {
+    for (int j = 0; j < size; j++) {
+      Schedule schedule(scope.main_zone());
+      BasicBlock* A = schedule.start();
+      BasicBlock* D = schedule.NewBasicBlock();
+      BasicBlock* E = schedule.end();
+
+      SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
+      schedule.AddSuccessor(A, loop1->header());
+      schedule.AddSuccessor(loop1->last(), E);
+
+      schedule.AddSuccessor(loop1->nodes[i], loop1->header());
+      schedule.AddSuccessor(loop1->nodes[j], D);
+      schedule.AddSuccessor(D, E);
+
+      BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+      CheckRPONumbers(order, schedule.BasicBlockCount(), true);
+      CheckLoopContains(loop1->nodes, loop1->count);
+    }
+  }
+}
+
+
+TEST(RPOLoopOutedges2) {
+  HandleAndZoneScope scope;
+
+  int size = 8;
+  for (int i = 0; i < size; i++) {
+    Schedule schedule(scope.main_zone());
+    BasicBlock* A = schedule.start();
+    BasicBlock* E = schedule.end();
+
+    SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
+    schedule.AddSuccessor(A, loop1->header());
+    schedule.AddSuccessor(loop1->last(), E);
+
+    for (int j = 0; j < size; j++) {
+      BasicBlock* O = schedule.NewBasicBlock();
+      schedule.AddSuccessor(loop1->nodes[j], O);
+      schedule.AddSuccessor(O, E);
+    }
+
+    BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+    CheckRPONumbers(order, schedule.BasicBlockCount(), true);
+    CheckLoopContains(loop1->nodes, loop1->count);
+  }
+}
+
+
+TEST(RPOLoopOutloops1) {
+  HandleAndZoneScope scope;
+
+  int size = 8;
+  for (int i = 0; i < size; i++) {
+    Schedule schedule(scope.main_zone());
+    BasicBlock* A = schedule.start();
+    BasicBlock* E = schedule.end();
+    SmartPointer<TestLoop> loop1(CreateLoop(&schedule, size));
+    schedule.AddSuccessor(A, loop1->header());
+    schedule.AddSuccessor(loop1->last(), E);
+
+    TestLoop** loopN = new TestLoop* [size];
+    for (int j = 0; j < size; j++) {
+      loopN[j] = CreateLoop(&schedule, 2);
+      schedule.AddSuccessor(loop1->nodes[j], loopN[j]->header());
+      schedule.AddSuccessor(loopN[j]->last(), E);
+    }
+
+    BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+    CheckRPONumbers(order, schedule.BasicBlockCount(), true);
+    CheckLoopContains(loop1->nodes, loop1->count);
+
+    for (int j = 0; j < size; j++) {
+      CheckLoopContains(loopN[j]->nodes, loopN[j]->count);
+      delete loopN[j];
+    }
+    delete[] loopN;
+  }
+}
+
+
+TEST(RPOLoopMultibackedge) {
+  HandleAndZoneScope scope;
+  Schedule schedule(scope.main_zone());
+
+  BasicBlock* A = schedule.start();
+  BasicBlock* B = schedule.NewBasicBlock();
+  BasicBlock* C = schedule.NewBasicBlock();
+  BasicBlock* D = schedule.end();
+  BasicBlock* E = schedule.NewBasicBlock();
+
+  schedule.AddSuccessor(A, B);
+  schedule.AddSuccessor(B, C);
+  schedule.AddSuccessor(B, D);
+  schedule.AddSuccessor(B, E);
+  schedule.AddSuccessor(C, B);
+  schedule.AddSuccessor(D, B);
+  schedule.AddSuccessor(E, B);
+
+  BasicBlockVector* order = Scheduler::ComputeSpecialRPO(&schedule);
+  CheckRPONumbers(order, 5, true);
+
+  BasicBlock* loop1[] = {B, C, D, E};
+  CheckLoopContains(loop1, 4);
+}
+
+
+TEST(BuildScheduleEmpty) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder builder(scope.main_zone());
+  graph.SetStart(graph.NewNode(builder.Start(0)));
+  graph.SetEnd(graph.NewNode(builder.End(), graph.start()));
+
+  USE(Scheduler::ComputeSchedule(&graph));
+}
+
+
+TEST(BuildScheduleOneParameter) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder builder(scope.main_zone());
+  graph.SetStart(graph.NewNode(builder.Start(0)));
+
+  Node* p1 = graph.NewNode(builder.Parameter(0), graph.start());
+  Node* ret = graph.NewNode(builder.Return(), p1, graph.start(), graph.start());
+
+  graph.SetEnd(graph.NewNode(builder.End(), ret));
+
+  USE(Scheduler::ComputeSchedule(&graph));
+}
+
+
+TEST(BuildScheduleIfSplit) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  graph.SetStart(graph.NewNode(builder.Start(3)));
+
+  Node* p1 = graph.NewNode(builder.Parameter(0), graph.start());
+  Node* p2 = graph.NewNode(builder.Parameter(1), graph.start());
+  Node* p3 = graph.NewNode(builder.Parameter(2), graph.start());
+  Node* p4 = graph.NewNode(builder.Parameter(3), graph.start());
+  Node* p5 = graph.NewNode(builder.Parameter(4), graph.start());
+  Node* cmp = graph.NewNode(js_builder.LessThanOrEqual(), p1, p2, p3,
+                            graph.start(), graph.start());
+  Node* branch = graph.NewNode(builder.Branch(), cmp, graph.start());
+  Node* true_branch = graph.NewNode(builder.IfTrue(), branch);
+  Node* false_branch = graph.NewNode(builder.IfFalse(), branch);
+
+  Node* ret1 = graph.NewNode(builder.Return(), p4, graph.start(), true_branch);
+  Node* ret2 = graph.NewNode(builder.Return(), p5, graph.start(), false_branch);
+  Node* merge = graph.NewNode(builder.Merge(2), ret1, ret2);
+  graph.SetEnd(graph.NewNode(builder.End(), merge));
+
+  ComputeAndVerifySchedule(13, &graph);
+}
+
+
+TEST(BuildScheduleIfSplitWithEffects) {
+  HandleAndZoneScope scope;
+  Isolate* isolate = scope.main_isolate();
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common_builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  const Operator* op;
+
+  Handle<Object> object =
+      Handle<Object>(isolate->heap()->undefined_value(), isolate);
+  Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+
+  // Manually transcripted code for:
+  // function turbo_fan_test(a, b, c, y) {
+  //   if (a < b) {
+  //     return a + b - c * c - a + y;
+  //   } else {
+  //     return c * c - a;
+  //   }
+  // }
+  op = common_builder.Start(0);
+  Node* n0 = graph.NewNode(op);
+  USE(n0);
+  Node* nil = graph.NewNode(common_builder.Dead());
+  op = common_builder.End();
+  Node* n23 = graph.NewNode(op, nil);
+  USE(n23);
+  op = common_builder.Merge(2);
+  Node* n22 = graph.NewNode(op, nil, nil);
+  USE(n22);
+  op = common_builder.Return();
+  Node* n16 = graph.NewNode(op, nil, nil, nil);
+  USE(n16);
+  op = js_builder.Add();
+  Node* n15 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n15);
+  op = js_builder.Subtract();
+  Node* n14 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n14);
+  op = js_builder.Subtract();
+  Node* n13 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n13);
+  op = js_builder.Add();
+  Node* n11 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n11);
+  op = common_builder.Parameter(0);
+  Node* n2 = graph.NewNode(op, n0);
+  USE(n2);
+  n11->ReplaceInput(0, n2);
+  op = common_builder.Parameter(0);
+  Node* n3 = graph.NewNode(op, n0);
+  USE(n3);
+  n11->ReplaceInput(1, n3);
+  op = common_builder.HeapConstant(unique_constant);
+  Node* n7 = graph.NewNode(op);
+  USE(n7);
+  n11->ReplaceInput(2, n7);
+  op = js_builder.LessThan();
+  Node* n8 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n8);
+  n8->ReplaceInput(0, n2);
+  n8->ReplaceInput(1, n3);
+  n8->ReplaceInput(2, n7);
+  n8->ReplaceInput(3, n0);
+  n8->ReplaceInput(4, n0);
+  n11->ReplaceInput(3, n8);
+  op = common_builder.IfTrue();
+  Node* n10 = graph.NewNode(op, nil);
+  USE(n10);
+  op = common_builder.Branch();
+  Node* n9 = graph.NewNode(op, nil, nil);
+  USE(n9);
+  n9->ReplaceInput(0, n8);
+  n9->ReplaceInput(1, n0);
+  n10->ReplaceInput(0, n9);
+  n11->ReplaceInput(4, n10);
+  n13->ReplaceInput(0, n11);
+  op = js_builder.Multiply();
+  Node* n12 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n12);
+  op = common_builder.Parameter(0);
+  Node* n4 = graph.NewNode(op, n0);
+  USE(n4);
+  n12->ReplaceInput(0, n4);
+  n12->ReplaceInput(1, n4);
+  n12->ReplaceInput(2, n7);
+  n12->ReplaceInput(3, n11);
+  n12->ReplaceInput(4, n10);
+  n13->ReplaceInput(1, n12);
+  n13->ReplaceInput(2, n7);
+  n13->ReplaceInput(3, n12);
+  n13->ReplaceInput(4, n10);
+  n14->ReplaceInput(0, n13);
+  n14->ReplaceInput(1, n2);
+  n14->ReplaceInput(2, n7);
+  n14->ReplaceInput(3, n13);
+  n14->ReplaceInput(4, n10);
+  n15->ReplaceInput(0, n14);
+  op = common_builder.Parameter(0);
+  Node* n5 = graph.NewNode(op, n0);
+  USE(n5);
+  n15->ReplaceInput(1, n5);
+  n15->ReplaceInput(2, n7);
+  n15->ReplaceInput(3, n14);
+  n15->ReplaceInput(4, n10);
+  n16->ReplaceInput(0, n15);
+  n16->ReplaceInput(1, n15);
+  n16->ReplaceInput(2, n10);
+  n22->ReplaceInput(0, n16);
+  op = common_builder.Return();
+  Node* n21 = graph.NewNode(op, nil, nil, nil);
+  USE(n21);
+  op = js_builder.Subtract();
+  Node* n20 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n20);
+  op = js_builder.Multiply();
+  Node* n19 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n19);
+  n19->ReplaceInput(0, n4);
+  n19->ReplaceInput(1, n4);
+  n19->ReplaceInput(2, n7);
+  n19->ReplaceInput(3, n8);
+  op = common_builder.IfFalse();
+  Node* n18 = graph.NewNode(op, nil);
+  USE(n18);
+  n18->ReplaceInput(0, n9);
+  n19->ReplaceInput(4, n18);
+  n20->ReplaceInput(0, n19);
+  n20->ReplaceInput(1, n2);
+  n20->ReplaceInput(2, n7);
+  n20->ReplaceInput(3, n19);
+  n20->ReplaceInput(4, n18);
+  n21->ReplaceInput(0, n20);
+  n21->ReplaceInput(1, n20);
+  n21->ReplaceInput(2, n18);
+  n22->ReplaceInput(1, n21);
+  n23->ReplaceInput(0, n22);
+
+  graph.SetStart(n0);
+  graph.SetEnd(n23);
+
+  ComputeAndVerifySchedule(20, &graph);
+}
+
+
+TEST(BuildScheduleSimpleLoop) {
+  HandleAndZoneScope scope;
+  Isolate* isolate = scope.main_isolate();
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common_builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  const Operator* op;
+
+  Handle<Object> object =
+      Handle<Object>(isolate->heap()->undefined_value(), isolate);
+  Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+
+  // Manually transcripted code for:
+  // function turbo_fan_test(a, b) {
+  //   while (a < b) {
+  //     a++;
+  //   }
+  //   return a;
+  // }
+  op = common_builder.Start(0);
+  Node* n0 = graph.NewNode(op);
+  USE(n0);
+  Node* nil = graph.NewNode(common_builder.Dead());
+  op = common_builder.End();
+  Node* n20 = graph.NewNode(op, nil);
+  USE(n20);
+  op = common_builder.Return();
+  Node* n19 = graph.NewNode(op, nil, nil, nil);
+  USE(n19);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n8 = graph.NewNode(op, nil, nil, nil);
+  USE(n8);
+  op = common_builder.Parameter(0);
+  Node* n2 = graph.NewNode(op, n0);
+  USE(n2);
+  n8->ReplaceInput(0, n2);
+  op = js_builder.Add();
+  Node* n18 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n18);
+  op = js_builder.ToNumber();
+  Node* n16 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n16);
+  n16->ReplaceInput(0, n8);
+  op = common_builder.HeapConstant(unique_constant);
+  Node* n5 = graph.NewNode(op);
+  USE(n5);
+  n16->ReplaceInput(1, n5);
+  op = js_builder.LessThan();
+  Node* n12 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n12);
+  n12->ReplaceInput(0, n8);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n9 = graph.NewNode(op, nil, nil, nil);
+  USE(n9);
+  op = common_builder.Parameter(0);
+  Node* n3 = graph.NewNode(op, n0);
+  USE(n3);
+  n9->ReplaceInput(0, n3);
+  n9->ReplaceInput(1, n9);
+  op = common_builder.Loop(2);
+  Node* n6 = graph.NewNode(op, nil, nil);
+  USE(n6);
+  n6->ReplaceInput(0, n0);
+  op = common_builder.IfTrue();
+  Node* n14 = graph.NewNode(op, nil);
+  USE(n14);
+  op = common_builder.Branch();
+  Node* n13 = graph.NewNode(op, nil, nil);
+  USE(n13);
+  n13->ReplaceInput(0, n12);
+  n13->ReplaceInput(1, n6);
+  n14->ReplaceInput(0, n13);
+  n6->ReplaceInput(1, n14);
+  n9->ReplaceInput(2, n6);
+  n12->ReplaceInput(1, n9);
+  n12->ReplaceInput(2, n5);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n10 = graph.NewNode(op, nil, nil, nil);
+  USE(n10);
+  n10->ReplaceInput(0, n0);
+  n10->ReplaceInput(1, n18);
+  n10->ReplaceInput(2, n6);
+  n12->ReplaceInput(3, n10);
+  n12->ReplaceInput(4, n6);
+  n16->ReplaceInput(2, n12);
+  n16->ReplaceInput(3, n14);
+  n18->ReplaceInput(0, n16);
+  op = common_builder.NumberConstant(0);
+  Node* n17 = graph.NewNode(op);
+  USE(n17);
+  n18->ReplaceInput(1, n17);
+  n18->ReplaceInput(2, n5);
+  n18->ReplaceInput(3, n16);
+  n18->ReplaceInput(4, n14);
+  n8->ReplaceInput(1, n18);
+  n8->ReplaceInput(2, n6);
+  n19->ReplaceInput(0, n8);
+  n19->ReplaceInput(1, n12);
+  op = common_builder.IfFalse();
+  Node* n15 = graph.NewNode(op, nil);
+  USE(n15);
+  n15->ReplaceInput(0, n13);
+  n19->ReplaceInput(2, n15);
+  n20->ReplaceInput(0, n19);
+
+  graph.SetStart(n0);
+  graph.SetEnd(n20);
+
+  ComputeAndVerifySchedule(19, &graph);
+}
+
+
+TEST(BuildScheduleComplexLoops) {
+  HandleAndZoneScope scope;
+  Isolate* isolate = scope.main_isolate();
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common_builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  const Operator* op;
+
+  Handle<Object> object =
+      Handle<Object>(isolate->heap()->undefined_value(), isolate);
+  Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+
+  // Manually transcripted code for:
+  // function turbo_fan_test(a, b, c) {
+  //   while (a < b) {
+  //     a++;
+  //     while (c < b) {
+  //       c++;
+  //     }
+  //   }
+  //   while (a < b) {
+  //     a += 2;
+  //   }
+  //   return a;
+  // }
+  op = common_builder.Start(0);
+  Node* n0 = graph.NewNode(op);
+  USE(n0);
+  Node* nil = graph.NewNode(common_builder.Dead());
+  op = common_builder.End();
+  Node* n46 = graph.NewNode(op, nil);
+  USE(n46);
+  op = common_builder.Return();
+  Node* n45 = graph.NewNode(op, nil, nil, nil);
+  USE(n45);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n35 = graph.NewNode(op, nil, nil, nil);
+  USE(n35);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n9 = graph.NewNode(op, nil, nil, nil);
+  USE(n9);
+  op = common_builder.Parameter(0);
+  Node* n2 = graph.NewNode(op, n0);
+  USE(n2);
+  n9->ReplaceInput(0, n2);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n23 = graph.NewNode(op, nil, nil, nil);
+  USE(n23);
+  op = js_builder.Add();
+  Node* n20 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n20);
+  op = js_builder.ToNumber();
+  Node* n18 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n18);
+  n18->ReplaceInput(0, n9);
+  op = common_builder.HeapConstant(unique_constant);
+  Node* n6 = graph.NewNode(op);
+  USE(n6);
+  n18->ReplaceInput(1, n6);
+  op = js_builder.LessThan();
+  Node* n14 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n14);
+  n14->ReplaceInput(0, n9);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n10 = graph.NewNode(op, nil, nil, nil);
+  USE(n10);
+  op = common_builder.Parameter(0);
+  Node* n3 = graph.NewNode(op, n0);
+  USE(n3);
+  n10->ReplaceInput(0, n3);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n24 = graph.NewNode(op, nil, nil, nil);
+  USE(n24);
+  n24->ReplaceInput(0, n10);
+  n24->ReplaceInput(1, n24);
+  op = common_builder.Loop(2);
+  Node* n21 = graph.NewNode(op, nil, nil);
+  USE(n21);
+  op = common_builder.IfTrue();
+  Node* n16 = graph.NewNode(op, nil);
+  USE(n16);
+  op = common_builder.Branch();
+  Node* n15 = graph.NewNode(op, nil, nil);
+  USE(n15);
+  n15->ReplaceInput(0, n14);
+  op = common_builder.Loop(2);
+  Node* n7 = graph.NewNode(op, nil, nil);
+  USE(n7);
+  n7->ReplaceInput(0, n0);
+  op = common_builder.IfFalse();
+  Node* n30 = graph.NewNode(op, nil);
+  USE(n30);
+  op = common_builder.Branch();
+  Node* n28 = graph.NewNode(op, nil, nil);
+  USE(n28);
+  op = js_builder.LessThan();
+  Node* n27 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n27);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n25 = graph.NewNode(op, nil, nil, nil);
+  USE(n25);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n11 = graph.NewNode(op, nil, nil, nil);
+  USE(n11);
+  op = common_builder.Parameter(0);
+  Node* n4 = graph.NewNode(op, n0);
+  USE(n4);
+  n11->ReplaceInput(0, n4);
+  n11->ReplaceInput(1, n25);
+  n11->ReplaceInput(2, n7);
+  n25->ReplaceInput(0, n11);
+  op = js_builder.Add();
+  Node* n32 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n32);
+  op = js_builder.ToNumber();
+  Node* n31 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n31);
+  n31->ReplaceInput(0, n25);
+  n31->ReplaceInput(1, n6);
+  n31->ReplaceInput(2, n27);
+  op = common_builder.IfTrue();
+  Node* n29 = graph.NewNode(op, nil);
+  USE(n29);
+  n29->ReplaceInput(0, n28);
+  n31->ReplaceInput(3, n29);
+  n32->ReplaceInput(0, n31);
+  op = common_builder.NumberConstant(0);
+  Node* n19 = graph.NewNode(op);
+  USE(n19);
+  n32->ReplaceInput(1, n19);
+  n32->ReplaceInput(2, n6);
+  n32->ReplaceInput(3, n31);
+  n32->ReplaceInput(4, n29);
+  n25->ReplaceInput(1, n32);
+  n25->ReplaceInput(2, n21);
+  n27->ReplaceInput(0, n25);
+  n27->ReplaceInput(1, n24);
+  n27->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n26 = graph.NewNode(op, nil, nil, nil);
+  USE(n26);
+  n26->ReplaceInput(0, n20);
+  n26->ReplaceInput(1, n32);
+  n26->ReplaceInput(2, n21);
+  n27->ReplaceInput(3, n26);
+  n27->ReplaceInput(4, n21);
+  n28->ReplaceInput(0, n27);
+  n28->ReplaceInput(1, n21);
+  n30->ReplaceInput(0, n28);
+  n7->ReplaceInput(1, n30);
+  n15->ReplaceInput(1, n7);
+  n16->ReplaceInput(0, n15);
+  n21->ReplaceInput(0, n16);
+  n21->ReplaceInput(1, n29);
+  n24->ReplaceInput(2, n21);
+  n10->ReplaceInput(1, n24);
+  n10->ReplaceInput(2, n7);
+  n14->ReplaceInput(1, n10);
+  n14->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n12 = graph.NewNode(op, nil, nil, nil);
+  USE(n12);
+  n12->ReplaceInput(0, n0);
+  n12->ReplaceInput(1, n27);
+  n12->ReplaceInput(2, n7);
+  n14->ReplaceInput(3, n12);
+  n14->ReplaceInput(4, n7);
+  n18->ReplaceInput(2, n14);
+  n18->ReplaceInput(3, n16);
+  n20->ReplaceInput(0, n18);
+  n20->ReplaceInput(1, n19);
+  n20->ReplaceInput(2, n6);
+  n20->ReplaceInput(3, n18);
+  n20->ReplaceInput(4, n16);
+  n23->ReplaceInput(0, n20);
+  n23->ReplaceInput(1, n23);
+  n23->ReplaceInput(2, n21);
+  n9->ReplaceInput(1, n23);
+  n9->ReplaceInput(2, n7);
+  n35->ReplaceInput(0, n9);
+  op = js_builder.Add();
+  Node* n44 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n44);
+  n44->ReplaceInput(0, n35);
+  op = common_builder.NumberConstant(0);
+  Node* n43 = graph.NewNode(op);
+  USE(n43);
+  n44->ReplaceInput(1, n43);
+  n44->ReplaceInput(2, n6);
+  op = js_builder.LessThan();
+  Node* n39 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n39);
+  n39->ReplaceInput(0, n35);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n36 = graph.NewNode(op, nil, nil, nil);
+  USE(n36);
+  n36->ReplaceInput(0, n10);
+  n36->ReplaceInput(1, n36);
+  op = common_builder.Loop(2);
+  Node* n33 = graph.NewNode(op, nil, nil);
+  USE(n33);
+  op = common_builder.IfFalse();
+  Node* n17 = graph.NewNode(op, nil);
+  USE(n17);
+  n17->ReplaceInput(0, n15);
+  n33->ReplaceInput(0, n17);
+  op = common_builder.IfTrue();
+  Node* n41 = graph.NewNode(op, nil);
+  USE(n41);
+  op = common_builder.Branch();
+  Node* n40 = graph.NewNode(op, nil, nil);
+  USE(n40);
+  n40->ReplaceInput(0, n39);
+  n40->ReplaceInput(1, n33);
+  n41->ReplaceInput(0, n40);
+  n33->ReplaceInput(1, n41);
+  n36->ReplaceInput(2, n33);
+  n39->ReplaceInput(1, n36);
+  n39->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n38 = graph.NewNode(op, nil, nil, nil);
+  USE(n38);
+  n38->ReplaceInput(0, n14);
+  n38->ReplaceInput(1, n44);
+  n38->ReplaceInput(2, n33);
+  n39->ReplaceInput(3, n38);
+  n39->ReplaceInput(4, n33);
+  n44->ReplaceInput(3, n39);
+  n44->ReplaceInput(4, n41);
+  n35->ReplaceInput(1, n44);
+  n35->ReplaceInput(2, n33);
+  n45->ReplaceInput(0, n35);
+  n45->ReplaceInput(1, n39);
+  op = common_builder.IfFalse();
+  Node* n42 = graph.NewNode(op, nil);
+  USE(n42);
+  n42->ReplaceInput(0, n40);
+  n45->ReplaceInput(2, n42);
+  n46->ReplaceInput(0, n45);
+
+  graph.SetStart(n0);
+  graph.SetEnd(n46);
+
+  ComputeAndVerifySchedule(46, &graph);
+}
+
+
+TEST(BuildScheduleBreakAndContinue) {
+  HandleAndZoneScope scope;
+  Isolate* isolate = scope.main_isolate();
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common_builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  const Operator* op;
+
+  Handle<Object> object =
+      Handle<Object>(isolate->heap()->undefined_value(), isolate);
+  Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+
+  // Manually transcripted code for:
+  // function turbo_fan_test(a, b, c) {
+  //   var d = 0;
+  //   while (a < b) {
+  //     a++;
+  //     while (c < b) {
+  //       c++;
+  //       if (d == 0) break;
+  //       a++;
+  //     }
+  //     if (a == 1) continue;
+  //     d++;
+  //   }
+  //   return a + d;
+  // }
+  op = common_builder.Start(0);
+  Node* n0 = graph.NewNode(op);
+  USE(n0);
+  Node* nil = graph.NewNode(common_builder.Dead());
+  op = common_builder.End();
+  Node* n58 = graph.NewNode(op, nil);
+  USE(n58);
+  op = common_builder.Return();
+  Node* n57 = graph.NewNode(op, nil, nil, nil);
+  USE(n57);
+  op = js_builder.Add();
+  Node* n56 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n56);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n10 = graph.NewNode(op, nil, nil, nil);
+  USE(n10);
+  op = common_builder.Parameter(0);
+  Node* n2 = graph.NewNode(op, n0);
+  USE(n2);
+  n10->ReplaceInput(0, n2);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n25 = graph.NewNode(op, nil, nil, nil);
+  USE(n25);
+  op = js_builder.Add();
+  Node* n22 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n22);
+  op = js_builder.ToNumber();
+  Node* n20 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n20);
+  n20->ReplaceInput(0, n10);
+  op = common_builder.HeapConstant(unique_constant);
+  Node* n6 = graph.NewNode(op);
+  USE(n6);
+  n20->ReplaceInput(1, n6);
+  op = js_builder.LessThan();
+  Node* n16 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n16);
+  n16->ReplaceInput(0, n10);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n11 = graph.NewNode(op, nil, nil, nil);
+  USE(n11);
+  op = common_builder.Parameter(0);
+  Node* n3 = graph.NewNode(op, n0);
+  USE(n3);
+  n11->ReplaceInput(0, n3);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n26 = graph.NewNode(op, nil, nil, nil);
+  USE(n26);
+  n26->ReplaceInput(0, n11);
+  n26->ReplaceInput(1, n26);
+  op = common_builder.Loop(2);
+  Node* n23 = graph.NewNode(op, nil, nil);
+  USE(n23);
+  op = common_builder.IfTrue();
+  Node* n18 = graph.NewNode(op, nil);
+  USE(n18);
+  op = common_builder.Branch();
+  Node* n17 = graph.NewNode(op, nil, nil);
+  USE(n17);
+  n17->ReplaceInput(0, n16);
+  op = common_builder.Loop(2);
+  Node* n8 = graph.NewNode(op, nil, nil);
+  USE(n8);
+  n8->ReplaceInput(0, n0);
+  op = common_builder.Merge(2);
+  Node* n53 = graph.NewNode(op, nil, nil);
+  USE(n53);
+  op = common_builder.IfTrue();
+  Node* n49 = graph.NewNode(op, nil);
+  USE(n49);
+  op = common_builder.Branch();
+  Node* n48 = graph.NewNode(op, nil, nil);
+  USE(n48);
+  op = js_builder.Equal();
+  Node* n47 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n47);
+  n47->ReplaceInput(0, n25);
+  op = common_builder.NumberConstant(0);
+  Node* n46 = graph.NewNode(op);
+  USE(n46);
+  n47->ReplaceInput(1, n46);
+  n47->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n42 = graph.NewNode(op, nil, nil, nil);
+  USE(n42);
+  op = js_builder.LessThan();
+  Node* n30 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n30);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n27 = graph.NewNode(op, nil, nil, nil);
+  USE(n27);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n12 = graph.NewNode(op, nil, nil, nil);
+  USE(n12);
+  op = common_builder.Parameter(0);
+  Node* n4 = graph.NewNode(op, n0);
+  USE(n4);
+  n12->ReplaceInput(0, n4);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n41 = graph.NewNode(op, nil, nil, nil);
+  USE(n41);
+  n41->ReplaceInput(0, n27);
+  op = js_builder.Add();
+  Node* n35 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n35);
+  op = js_builder.ToNumber();
+  Node* n34 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n34);
+  n34->ReplaceInput(0, n27);
+  n34->ReplaceInput(1, n6);
+  n34->ReplaceInput(2, n30);
+  op = common_builder.IfTrue();
+  Node* n32 = graph.NewNode(op, nil);
+  USE(n32);
+  op = common_builder.Branch();
+  Node* n31 = graph.NewNode(op, nil, nil);
+  USE(n31);
+  n31->ReplaceInput(0, n30);
+  n31->ReplaceInput(1, n23);
+  n32->ReplaceInput(0, n31);
+  n34->ReplaceInput(3, n32);
+  n35->ReplaceInput(0, n34);
+  op = common_builder.NumberConstant(0);
+  Node* n21 = graph.NewNode(op);
+  USE(n21);
+  n35->ReplaceInput(1, n21);
+  n35->ReplaceInput(2, n6);
+  n35->ReplaceInput(3, n34);
+  n35->ReplaceInput(4, n32);
+  n41->ReplaceInput(1, n35);
+  op = common_builder.Merge(2);
+  Node* n40 = graph.NewNode(op, nil, nil);
+  USE(n40);
+  op = common_builder.IfFalse();
+  Node* n33 = graph.NewNode(op, nil);
+  USE(n33);
+  n33->ReplaceInput(0, n31);
+  n40->ReplaceInput(0, n33);
+  op = common_builder.IfTrue();
+  Node* n39 = graph.NewNode(op, nil);
+  USE(n39);
+  op = common_builder.Branch();
+  Node* n38 = graph.NewNode(op, nil, nil);
+  USE(n38);
+  op = js_builder.Equal();
+  Node* n37 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n37);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n28 = graph.NewNode(op, nil, nil, nil);
+  USE(n28);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n13 = graph.NewNode(op, nil, nil, nil);
+  USE(n13);
+  op = common_builder.NumberConstant(0);
+  Node* n7 = graph.NewNode(op);
+  USE(n7);
+  n13->ReplaceInput(0, n7);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n54 = graph.NewNode(op, nil, nil, nil);
+  USE(n54);
+  n54->ReplaceInput(0, n28);
+  op = js_builder.Add();
+  Node* n52 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n52);
+  op = js_builder.ToNumber();
+  Node* n51 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n51);
+  n51->ReplaceInput(0, n28);
+  n51->ReplaceInput(1, n6);
+  n51->ReplaceInput(2, n47);
+  op = common_builder.IfFalse();
+  Node* n50 = graph.NewNode(op, nil);
+  USE(n50);
+  n50->ReplaceInput(0, n48);
+  n51->ReplaceInput(3, n50);
+  n52->ReplaceInput(0, n51);
+  n52->ReplaceInput(1, n21);
+  n52->ReplaceInput(2, n6);
+  n52->ReplaceInput(3, n51);
+  n52->ReplaceInput(4, n50);
+  n54->ReplaceInput(1, n52);
+  n54->ReplaceInput(2, n53);
+  n13->ReplaceInput(1, n54);
+  n13->ReplaceInput(2, n8);
+  n28->ReplaceInput(0, n13);
+  n28->ReplaceInput(1, n28);
+  n28->ReplaceInput(2, n23);
+  n37->ReplaceInput(0, n28);
+  op = common_builder.NumberConstant(0);
+  Node* n36 = graph.NewNode(op);
+  USE(n36);
+  n37->ReplaceInput(1, n36);
+  n37->ReplaceInput(2, n6);
+  n37->ReplaceInput(3, n35);
+  n37->ReplaceInput(4, n32);
+  n38->ReplaceInput(0, n37);
+  n38->ReplaceInput(1, n32);
+  n39->ReplaceInput(0, n38);
+  n40->ReplaceInput(1, n39);
+  n41->ReplaceInput(2, n40);
+  n12->ReplaceInput(1, n41);
+  n12->ReplaceInput(2, n8);
+  n27->ReplaceInput(0, n12);
+  n27->ReplaceInput(1, n35);
+  n27->ReplaceInput(2, n23);
+  n30->ReplaceInput(0, n27);
+  n30->ReplaceInput(1, n26);
+  n30->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n29 = graph.NewNode(op, nil, nil, nil);
+  USE(n29);
+  n29->ReplaceInput(0, n22);
+  op = js_builder.Add();
+  Node* n45 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n45);
+  op = js_builder.ToNumber();
+  Node* n44 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n44);
+  n44->ReplaceInput(0, n25);
+  n44->ReplaceInput(1, n6);
+  n44->ReplaceInput(2, n37);
+  op = common_builder.IfFalse();
+  Node* n43 = graph.NewNode(op, nil);
+  USE(n43);
+  n43->ReplaceInput(0, n38);
+  n44->ReplaceInput(3, n43);
+  n45->ReplaceInput(0, n44);
+  n45->ReplaceInput(1, n21);
+  n45->ReplaceInput(2, n6);
+  n45->ReplaceInput(3, n44);
+  n45->ReplaceInput(4, n43);
+  n29->ReplaceInput(1, n45);
+  n29->ReplaceInput(2, n23);
+  n30->ReplaceInput(3, n29);
+  n30->ReplaceInput(4, n23);
+  n42->ReplaceInput(0, n30);
+  n42->ReplaceInput(1, n37);
+  n42->ReplaceInput(2, n40);
+  n47->ReplaceInput(3, n42);
+  n47->ReplaceInput(4, n40);
+  n48->ReplaceInput(0, n47);
+  n48->ReplaceInput(1, n40);
+  n49->ReplaceInput(0, n48);
+  n53->ReplaceInput(0, n49);
+  n53->ReplaceInput(1, n50);
+  n8->ReplaceInput(1, n53);
+  n17->ReplaceInput(1, n8);
+  n18->ReplaceInput(0, n17);
+  n23->ReplaceInput(0, n18);
+  n23->ReplaceInput(1, n43);
+  n26->ReplaceInput(2, n23);
+  n11->ReplaceInput(1, n26);
+  n11->ReplaceInput(2, n8);
+  n16->ReplaceInput(1, n11);
+  n16->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n14 = graph.NewNode(op, nil, nil, nil);
+  USE(n14);
+  n14->ReplaceInput(0, n0);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n55 = graph.NewNode(op, nil, nil, nil);
+  USE(n55);
+  n55->ReplaceInput(0, n47);
+  n55->ReplaceInput(1, n52);
+  n55->ReplaceInput(2, n53);
+  n14->ReplaceInput(1, n55);
+  n14->ReplaceInput(2, n8);
+  n16->ReplaceInput(3, n14);
+  n16->ReplaceInput(4, n8);
+  n20->ReplaceInput(2, n16);
+  n20->ReplaceInput(3, n18);
+  n22->ReplaceInput(0, n20);
+  n22->ReplaceInput(1, n21);
+  n22->ReplaceInput(2, n6);
+  n22->ReplaceInput(3, n20);
+  n22->ReplaceInput(4, n18);
+  n25->ReplaceInput(0, n22);
+  n25->ReplaceInput(1, n45);
+  n25->ReplaceInput(2, n23);
+  n10->ReplaceInput(1, n25);
+  n10->ReplaceInput(2, n8);
+  n56->ReplaceInput(0, n10);
+  n56->ReplaceInput(1, n13);
+  n56->ReplaceInput(2, n6);
+  n56->ReplaceInput(3, n16);
+  op = common_builder.IfFalse();
+  Node* n19 = graph.NewNode(op, nil);
+  USE(n19);
+  n19->ReplaceInput(0, n17);
+  n56->ReplaceInput(4, n19);
+  n57->ReplaceInput(0, n56);
+  n57->ReplaceInput(1, n56);
+  n57->ReplaceInput(2, n19);
+  n58->ReplaceInput(0, n57);
+
+  graph.SetStart(n0);
+  graph.SetEnd(n58);
+
+  ComputeAndVerifySchedule(62, &graph);
+}
+
+
+TEST(BuildScheduleSimpleLoopWithCodeMotion) {
+  HandleAndZoneScope scope;
+  Isolate* isolate = scope.main_isolate();
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common_builder(scope.main_zone());
+  JSOperatorBuilder js_builder(scope.main_zone());
+  MachineOperatorBuilder machine_builder;
+  const Operator* op;
+
+  Handle<Object> object =
+      Handle<Object>(isolate->heap()->undefined_value(), isolate);
+  Unique<Object> unique_constant = Unique<Object>::CreateUninitialized(object);
+
+  // Manually transcripted code for:
+  // function turbo_fan_test(a, b, c) {
+  //   while (a < b) {
+  //     a += b + c;
+  //   }
+  //   return a;
+  // }
+  op = common_builder.Start(0);
+  Node* n0 = graph.NewNode(op);
+  USE(n0);
+  Node* nil = graph.NewNode(common_builder.Dead());
+  op = common_builder.End();
+  Node* n22 = graph.NewNode(op, nil);
+  USE(n22);
+  op = common_builder.Return();
+  Node* n21 = graph.NewNode(op, nil, nil, nil);
+  USE(n21);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n9 = graph.NewNode(op, nil, nil, nil);
+  USE(n9);
+  op = common_builder.Parameter(0);
+  Node* n2 = graph.NewNode(op, n0);
+  USE(n2);
+  n9->ReplaceInput(0, n2);
+  op = js_builder.Add();
+  Node* n20 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n20);
+  n20->ReplaceInput(0, n9);
+  op = machine_builder.Int32Add();
+  Node* n19 = graph.NewNode(op, nil, nil);
+  USE(n19);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n10 = graph.NewNode(op, nil, nil, nil);
+  USE(n10);
+  op = common_builder.Parameter(0);
+  Node* n3 = graph.NewNode(op, n0);
+  USE(n3);
+  n10->ReplaceInput(0, n3);
+  n10->ReplaceInput(1, n10);
+  op = common_builder.Loop(2);
+  Node* n7 = graph.NewNode(op, nil, nil);
+  USE(n7);
+  n7->ReplaceInput(0, n0);
+  op = common_builder.IfTrue();
+  Node* n17 = graph.NewNode(op, nil);
+  USE(n17);
+  op = common_builder.Branch();
+  Node* n16 = graph.NewNode(op, nil, nil);
+  USE(n16);
+  op = js_builder.ToBoolean();
+  Node* n15 = graph.NewNode(op, nil, nil, nil, nil);
+  USE(n15);
+  op = js_builder.LessThan();
+  Node* n14 = graph.NewNode(op, nil, nil, nil, nil, nil);
+  USE(n14);
+  n14->ReplaceInput(0, n9);
+  n14->ReplaceInput(1, n10);
+  op = common_builder.HeapConstant(unique_constant);
+  Node* n6 = graph.NewNode(op);
+  USE(n6);
+  n14->ReplaceInput(2, n6);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n12 = graph.NewNode(op, nil, nil, nil);
+  USE(n12);
+  n12->ReplaceInput(0, n0);
+  n12->ReplaceInput(1, n20);
+  n12->ReplaceInput(2, n7);
+  n14->ReplaceInput(3, n12);
+  n14->ReplaceInput(4, n7);
+  n15->ReplaceInput(0, n14);
+  n15->ReplaceInput(1, n6);
+  n15->ReplaceInput(2, n14);
+  n15->ReplaceInput(3, n7);
+  n16->ReplaceInput(0, n15);
+  n16->ReplaceInput(1, n7);
+  n17->ReplaceInput(0, n16);
+  n7->ReplaceInput(1, n17);
+  n10->ReplaceInput(2, n7);
+  n19->ReplaceInput(0, n2);
+  op = common_builder.Phi(kMachAnyTagged, 2);
+  Node* n11 = graph.NewNode(op, nil, nil, nil);
+  USE(n11);
+  op = common_builder.Parameter(0);
+  Node* n4 = graph.NewNode(op, n0);
+  USE(n4);
+  n11->ReplaceInput(0, n4);
+  n11->ReplaceInput(1, n11);
+  n11->ReplaceInput(2, n7);
+  n19->ReplaceInput(1, n3);
+  n20->ReplaceInput(1, n19);
+  n20->ReplaceInput(2, n6);
+  n20->ReplaceInput(3, n19);
+  n20->ReplaceInput(4, n17);
+  n9->ReplaceInput(1, n20);
+  n9->ReplaceInput(2, n7);
+  n21->ReplaceInput(0, n9);
+  n21->ReplaceInput(1, n15);
+  op = common_builder.IfFalse();
+  Node* n18 = graph.NewNode(op, nil);
+  USE(n18);
+  n18->ReplaceInput(0, n16);
+  n21->ReplaceInput(2, n18);
+  n22->ReplaceInput(0, n21);
+
+  graph.SetStart(n0);
+  graph.SetEnd(n22);
+
+  Schedule* schedule = ComputeAndVerifySchedule(19, &graph);
+  // Make sure the integer-only add gets hoisted to a different block that the
+  // JSAdd.
+  CHECK(schedule->block(n19) != schedule->block(n20));
+}
+
+
+#if V8_TURBOFAN_TARGET
+
+static Node* CreateDiamond(Graph* graph, CommonOperatorBuilder* common,
+                           Node* cond) {
+  Node* tv = graph->NewNode(common->Int32Constant(6));
+  Node* fv = graph->NewNode(common->Int32Constant(7));
+  Node* br = graph->NewNode(common->Branch(), cond, graph->start());
+  Node* t = graph->NewNode(common->IfTrue(), br);
+  Node* f = graph->NewNode(common->IfFalse(), br);
+  Node* m = graph->NewNode(common->Merge(2), t, f);
+  Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), tv, fv, m);
+  return phi;
+}
+
+
+TEST(FloatingDiamond1) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+
+  Node* start = graph.NewNode(common.Start(1));
+  graph.SetStart(start);
+
+  Node* p0 = graph.NewNode(common.Parameter(0), start);
+  Node* d1 = CreateDiamond(&graph, &common, p0);
+  Node* ret = graph.NewNode(common.Return(), d1, start, start);
+  Node* end = graph.NewNode(common.End(), ret, start);
+
+  graph.SetEnd(end);
+
+  ComputeAndVerifySchedule(13, &graph);
+}
+
+
+TEST(FloatingDiamond2) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+  MachineOperatorBuilder machine;
+
+  Node* start = graph.NewNode(common.Start(2));
+  graph.SetStart(start);
+
+  Node* p0 = graph.NewNode(common.Parameter(0), start);
+  Node* p1 = graph.NewNode(common.Parameter(1), start);
+  Node* d1 = CreateDiamond(&graph, &common, p0);
+  Node* d2 = CreateDiamond(&graph, &common, p1);
+  Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+  Node* ret = graph.NewNode(common.Return(), add, start, start);
+  Node* end = graph.NewNode(common.End(), ret, start);
+
+  graph.SetEnd(end);
+
+  ComputeAndVerifySchedule(24, &graph);
+}
+
+
+TEST(FloatingDiamond3) {
+  HandleAndZoneScope scope;
+  Graph graph(scope.main_zone());
+  CommonOperatorBuilder common(scope.main_zone());
+  MachineOperatorBuilder machine;
+
+  Node* start = graph.NewNode(common.Start(2));
+  graph.SetStart(start);
+
+  Node* p0 = graph.NewNode(common.Parameter(0), start);
+  Node* p1 = graph.NewNode(common.Parameter(1), start);
+  Node* d1 = CreateDiamond(&graph, &common, p0);
+  Node* d2 = CreateDiamond(&graph, &common, p1);
+  Node* add = graph.NewNode(machine.Int32Add(), d1, d2);
+  Node* d3 = CreateDiamond(&graph, &common, add);
+  Node* ret = graph.NewNode(common.Return(), d3, start, start);
+  Node* end = graph.NewNode(common.End(), ret, start);
+
+  graph.SetEnd(end);
+
+  ComputeAndVerifySchedule(33, &graph);
+}
+
+#endif
diff --git a/test/cctest/compiler/test-simplified-lowering.cc b/test/cctest/compiler/test-simplified-lowering.cc
new file mode 100644
index 0000000..96fb965
--- /dev/null
+++ b/test/cctest/compiler/test-simplified-lowering.cc
@@ -0,0 +1,1560 @@
+// 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 <limits>
+
+#include "src/compiler/access-builder.h"
+#include "src/compiler/control-builders.h"
+#include "src/compiler/generic-node-inl.h"
+#include "src/compiler/graph-visualizer.h"
+#include "src/compiler/node-properties-inl.h"
+#include "src/compiler/pipeline.h"
+#include "src/compiler/representation-change.h"
+#include "src/compiler/simplified-lowering.h"
+#include "src/compiler/typer.h"
+#include "src/compiler/verifier.h"
+#include "src/execution.h"
+#include "src/parser.h"
+#include "src/rewriter.h"
+#include "src/scopes.h"
+#include "test/cctest/cctest.h"
+#include "test/cctest/compiler/codegen-tester.h"
+#include "test/cctest/compiler/graph-builder-tester.h"
+#include "test/cctest/compiler/value-helper.h"
+
+using namespace v8::internal;
+using namespace v8::internal::compiler;
+
+template <typename ReturnType>
+class SimplifiedLoweringTester : public GraphBuilderTester<ReturnType> {
+ public:
+  SimplifiedLoweringTester(MachineType p0 = kMachNone,
+                           MachineType p1 = kMachNone,
+                           MachineType p2 = kMachNone,
+                           MachineType p3 = kMachNone,
+                           MachineType p4 = kMachNone)
+      : GraphBuilderTester<ReturnType>(p0, p1, p2, p3, p4),
+        typer(this->zone()),
+        javascript(this->zone()),
+        jsgraph(this->graph(), this->common(), &javascript, &typer,
+                this->machine()),
+        lowering(&jsgraph) {}
+
+  Typer typer;
+  JSOperatorBuilder javascript;
+  JSGraph jsgraph;
+  SimplifiedLowering lowering;
+
+  void LowerAllNodes() {
+    this->End();
+    lowering.LowerAllNodes();
+  }
+
+  Factory* factory() { return this->isolate()->factory(); }
+  Heap* heap() { return this->isolate()->heap(); }
+};
+
+
+#ifndef V8_TARGET_ARCH_ARM64
+// TODO(titzer): these result in a stub call that doesn't work on ARM64.
+// TODO(titzer): factor these tests out to test-run-simplifiedops.cc.
+// TODO(titzer): test tagged representation for input to NumberToInt32.
+TEST(RunNumberToInt32_float64) {
+  // TODO(titzer): explicit load/stores here are only because of representations
+  double input;
+  int32_t result;
+  SimplifiedLoweringTester<Object*> t;
+  FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
+                      kMachFloat64};
+  Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+  Node* convert = t.NumberToInt32(loaded);
+  FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Signed32(),
+                       kMachInt32};
+  t.StoreField(store, t.PointerConstant(&result), convert);
+  t.Return(t.jsgraph.TrueConstant());
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_FLOAT64_INPUTS(i) {
+      input = *i;
+      int32_t expected = DoubleToInt32(*i);
+      t.Call();
+      CHECK_EQ(expected, result);
+    }
+  }
+}
+
+
+// TODO(titzer): test tagged representation for input to NumberToUint32.
+TEST(RunNumberToUint32_float64) {
+  // TODO(titzer): explicit load/stores here are only because of representations
+  double input;
+  uint32_t result;
+  SimplifiedLoweringTester<Object*> t;
+  FieldAccess load = {kUntaggedBase, 0, Handle<Name>(), Type::Number(),
+                      kMachFloat64};
+  Node* loaded = t.LoadField(load, t.PointerConstant(&input));
+  Node* convert = t.NumberToUint32(loaded);
+  FieldAccess store = {kUntaggedBase, 0, Handle<Name>(), Type::Unsigned32(),
+                       kMachUint32};
+  t.StoreField(store, t.PointerConstant(&result), convert);
+  t.Return(t.jsgraph.TrueConstant());
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    FOR_FLOAT64_INPUTS(i) {
+      input = *i;
+      uint32_t expected = DoubleToUint32(*i);
+      t.Call();
+      CHECK_EQ(static_cast<int32_t>(expected), static_cast<int32_t>(result));
+    }
+  }
+}
+#endif
+
+
+// Create a simple JSObject with a unique map.
+static Handle<JSObject> TestObject() {
+  static int index = 0;
+  char buffer[50];
+  v8::base::OS::SNPrintF(buffer, 50, "({'a_%d':1})", index++);
+  return Handle<JSObject>::cast(v8::Utils::OpenHandle(*CompileRun(buffer)));
+}
+
+
+TEST(RunLoadMap) {
+  SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+  FieldAccess access = AccessBuilder::ForMap();
+  Node* load = t.LoadField(access, t.Parameter(0));
+  t.Return(load);
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<JSObject> src = TestObject();
+    Handle<Map> src_map(src->map());
+    Object* result = t.Call(*src);  // TODO(titzer): raw pointers in call
+    CHECK_EQ(*src_map, result);
+  }
+}
+
+
+TEST(RunStoreMap) {
+  SimplifiedLoweringTester<int32_t> t(kMachAnyTagged, kMachAnyTagged);
+  FieldAccess access = AccessBuilder::ForMap();
+  t.StoreField(access, t.Parameter(1), t.Parameter(0));
+  t.Return(t.jsgraph.TrueConstant());
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<JSObject> src = TestObject();
+    Handle<Map> src_map(src->map());
+    Handle<JSObject> dst = TestObject();
+    CHECK(src->map() != dst->map());
+    t.Call(*src_map, *dst);  // TODO(titzer): raw pointers in call
+    CHECK(*src_map == dst->map());
+  }
+}
+
+
+TEST(RunLoadProperties) {
+  SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+  FieldAccess access = AccessBuilder::ForJSObjectProperties();
+  Node* load = t.LoadField(access, t.Parameter(0));
+  t.Return(load);
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<JSObject> src = TestObject();
+    Handle<FixedArray> src_props(src->properties());
+    Object* result = t.Call(*src);  // TODO(titzer): raw pointers in call
+    CHECK_EQ(*src_props, result);
+  }
+}
+
+
+TEST(RunLoadStoreMap) {
+  SimplifiedLoweringTester<Object*> t(kMachAnyTagged, kMachAnyTagged);
+  FieldAccess access = AccessBuilder::ForMap();
+  Node* load = t.LoadField(access, t.Parameter(0));
+  t.StoreField(access, t.Parameter(1), load);
+  t.Return(load);
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<JSObject> src = TestObject();
+    Handle<Map> src_map(src->map());
+    Handle<JSObject> dst = TestObject();
+    CHECK(src->map() != dst->map());
+    Object* result = t.Call(*src, *dst);  // TODO(titzer): raw pointers in call
+    CHECK(result->IsMap());
+    CHECK_EQ(*src_map, result);
+    CHECK(*src_map == dst->map());
+  }
+}
+
+
+TEST(RunLoadStoreFixedArrayIndex) {
+  SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+  ElementAccess access = AccessBuilder::ForFixedArrayElement();
+  Node* load = t.LoadElement(access, t.Parameter(0), t.Int32Constant(0),
+                             t.Int32Constant(2));
+  t.StoreElement(access, t.Parameter(0), t.Int32Constant(1), t.Int32Constant(2),
+                 load);
+  t.Return(load);
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<FixedArray> array = t.factory()->NewFixedArray(2);
+    Handle<JSObject> src = TestObject();
+    Handle<JSObject> dst = TestObject();
+    array->set(0, *src);
+    array->set(1, *dst);
+    Object* result = t.Call(*array);
+    CHECK_EQ(*src, result);
+    CHECK_EQ(*src, array->get(0));
+    CHECK_EQ(*src, array->get(1));
+  }
+}
+
+
+TEST(RunLoadStoreArrayBuffer) {
+  SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+  const int index = 12;
+  const int array_length = 2 * index;
+  ElementAccess buffer_access =
+      AccessBuilder::ForBackingStoreElement(kMachInt8);
+  Node* backing_store = t.LoadField(
+      AccessBuilder::ForJSArrayBufferBackingStore(), t.Parameter(0));
+  Node* load =
+      t.LoadElement(buffer_access, backing_store, t.Int32Constant(index),
+                    t.Int32Constant(array_length));
+  t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1),
+                 t.Int32Constant(array_length), load);
+  t.Return(t.jsgraph.TrueConstant());
+
+  t.LowerAllNodes();
+  t.GenerateCode();
+
+  if (Pipeline::SupportedTarget()) {
+    Handle<JSArrayBuffer> array = t.factory()->NewJSArrayBuffer();
+    Runtime::SetupArrayBufferAllocatingData(t.isolate(), array, array_length);
+    uint8_t* data = reinterpret_cast<uint8_t*>(array->backing_store());
+    for (int i = 0; i < array_length; i++) {
+      data[i] = i;
+    }
+
+    // TODO(titzer): raw pointers in call
+    Object* result = t.Call(*array);
+    CHECK_EQ(t.isolate()->heap()->true_value(), result);
+    for (int i = 0; i < array_length; i++) {
+      uint8_t expected = i;
+      if (i == (index + 1)) expected = index;
+      CHECK_EQ(data[i], expected);
+    }
+  }
+}
+
+
+TEST(RunLoadFieldFromUntaggedBase) {
+  Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3)};
+
+  for (size_t i = 0; i < arraysize(smis); i++) {
+    int offset = static_cast<int>(i * sizeof(Smi*));
+    FieldAccess access = {kUntaggedBase, offset, Handle<Name>(),
+                          Type::Integral32(), kMachAnyTagged};
+
+    SimplifiedLoweringTester<Object*> t;
+    Node* load = t.LoadField(access, t.PointerConstant(smis));
+    t.Return(load);
+    t.LowerAllNodes();
+
+    if (!Pipeline::SupportedTarget()) continue;
+
+    for (int j = -5; j <= 5; j++) {
+      Smi* expected = Smi::FromInt(j);
+      smis[i] = expected;
+      CHECK_EQ(expected, t.Call());
+    }
+  }
+}
+
+
+TEST(RunStoreFieldToUntaggedBase) {
+  Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3)};
+
+  for (size_t i = 0; i < arraysize(smis); i++) {
+    int offset = static_cast<int>(i * sizeof(Smi*));
+    FieldAccess access = {kUntaggedBase, offset, Handle<Name>(),
+                          Type::Integral32(), kMachAnyTagged};
+
+    SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+    Node* p0 = t.Parameter(0);
+    t.StoreField(access, t.PointerConstant(smis), p0);
+    t.Return(p0);
+    t.LowerAllNodes();
+
+    if (!Pipeline::SupportedTarget()) continue;
+
+    for (int j = -5; j <= 5; j++) {
+      Smi* expected = Smi::FromInt(j);
+      smis[i] = Smi::FromInt(-100);
+      CHECK_EQ(expected, t.Call(expected));
+      CHECK_EQ(expected, smis[i]);
+    }
+  }
+}
+
+
+TEST(RunLoadElementFromUntaggedBase) {
+  Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3),
+                 Smi::FromInt(4), Smi::FromInt(5)};
+
+  for (size_t i = 0; i < arraysize(smis); i++) {    // for header sizes
+    for (size_t j = 0; (i + j) < arraysize(smis); j++) {  // for element index
+      int offset = static_cast<int>(i * sizeof(Smi*));
+      ElementAccess access = {kUntaggedBase, offset, Type::Integral32(),
+                              kMachAnyTagged};
+
+      SimplifiedLoweringTester<Object*> t;
+      Node* load = t.LoadElement(
+          access, t.PointerConstant(smis), t.Int32Constant(static_cast<int>(j)),
+          t.Int32Constant(static_cast<int>(arraysize(smis))));
+      t.Return(load);
+      t.LowerAllNodes();
+
+      if (!Pipeline::SupportedTarget()) continue;
+
+      for (int k = -5; k <= 5; k++) {
+        Smi* expected = Smi::FromInt(k);
+        smis[i + j] = expected;
+        CHECK_EQ(expected, t.Call());
+      }
+    }
+  }
+}
+
+
+TEST(RunStoreElementFromUntaggedBase) {
+  Smi* smis[] = {Smi::FromInt(1), Smi::FromInt(2), Smi::FromInt(3),
+                 Smi::FromInt(4), Smi::FromInt(5)};
+
+  for (size_t i = 0; i < arraysize(smis); i++) {    // for header sizes
+    for (size_t j = 0; (i + j) < arraysize(smis); j++) {  // for element index
+      int offset = static_cast<int>(i * sizeof(Smi*));
+      ElementAccess access = {kUntaggedBase, offset, Type::Integral32(),
+                              kMachAnyTagged};
+
+      SimplifiedLoweringTester<Object*> t(kMachAnyTagged);
+      Node* p0 = t.Parameter(0);
+      t.StoreElement(access, t.PointerConstant(smis),
+                     t.Int32Constant(static_cast<int>(j)),
+                     t.Int32Constant(static_cast<int>(arraysize(smis))), p0);
+      t.Return(p0);
+      t.LowerAllNodes();
+
+      if (!Pipeline::SupportedTarget()) continue;
+
+      for (int k = -5; k <= 5; k++) {
+        Smi* expected = Smi::FromInt(k);
+        smis[i + j] = Smi::FromInt(-100);
+        CHECK_EQ(expected, t.Call(expected));
+        CHECK_EQ(expected, smis[i + j]);
+      }
+
+      // TODO(titzer): assert the contents of the array.
+    }
+  }
+}
+
+
+// A helper class for accessing fields and elements of various types, on both
+// tagged and untagged base pointers. Contains both tagged and untagged buffers
+// for testing direct memory access from generated code.
+template <typename E>
+class AccessTester : public HandleAndZoneScope {
+ public:
+  bool tagged;
+  MachineType rep;
+  E* original_elements;
+  size_t num_elements;
+  E* untagged_array;
+  Handle<ByteArray> tagged_array;  // TODO(titzer): use FixedArray for tagged.
+
+  AccessTester(bool t, MachineType r, E* orig, size_t num)
+      : tagged(t),
+        rep(r),
+        original_elements(orig),
+        num_elements(num),
+        untagged_array(static_cast<E*>(malloc(ByteSize()))),
+        tagged_array(main_isolate()->factory()->NewByteArray(
+            static_cast<int>(ByteSize()))) {
+    Reinitialize();
+  }
+
+  ~AccessTester() { free(untagged_array); }
+
+  size_t ByteSize() { return num_elements * sizeof(E); }
+
+  // Nuke both {untagged_array} and {tagged_array} with {original_elements}.
+  void Reinitialize() {
+    memcpy(untagged_array, original_elements, ByteSize());
+    CHECK_EQ(static_cast<int>(ByteSize()), tagged_array->length());
+    E* raw = reinterpret_cast<E*>(tagged_array->GetDataStartAddress());
+    memcpy(raw, original_elements, ByteSize());
+  }
+
+  // Create and run code that copies the element in either {untagged_array}
+  // or {tagged_array} at index {from_index} to index {to_index}.
+  void RunCopyElement(int from_index, int to_index) {
+    // TODO(titzer): test element and field accesses where the base is not
+    // a constant in the code.
+    BoundsCheck(from_index);
+    BoundsCheck(to_index);
+    ElementAccess access = GetElementAccess();
+
+    SimplifiedLoweringTester<Object*> t;
+    Node* ptr = GetBaseNode(&t);
+    Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index),
+                               t.Int32Constant(static_cast<int>(num_elements)));
+    t.StoreElement(access, ptr, t.Int32Constant(to_index),
+                   t.Int32Constant(static_cast<int>(num_elements)), load);
+    t.Return(t.jsgraph.TrueConstant());
+    t.LowerAllNodes();
+    t.GenerateCode();
+
+    if (Pipeline::SupportedTarget()) {
+      Object* result = t.Call();
+      CHECK_EQ(t.isolate()->heap()->true_value(), result);
+    }
+  }
+
+  // Create and run code that copies the field in either {untagged_array}
+  // or {tagged_array} at index {from_index} to index {to_index}.
+  void RunCopyField(int from_index, int to_index) {
+    BoundsCheck(from_index);
+    BoundsCheck(to_index);
+    FieldAccess from_access = GetFieldAccess(from_index);
+    FieldAccess to_access = GetFieldAccess(to_index);
+
+    SimplifiedLoweringTester<Object*> t;
+    Node* ptr = GetBaseNode(&t);
+    Node* load = t.LoadField(from_access, ptr);
+    t.StoreField(to_access, ptr, load);
+    t.Return(t.jsgraph.TrueConstant());
+    t.LowerAllNodes();
+    t.GenerateCode();
+
+    if (Pipeline::SupportedTarget()) {
+      Object* result = t.Call();
+      CHECK_EQ(t.isolate()->heap()->true_value(), result);
+    }
+  }
+
+  // Create and run code that copies the elements from {this} to {that}.
+  void RunCopyElements(AccessTester<E>* that) {
+// TODO(titzer): Rewrite this test without StructuredGraphBuilder support.
+#if 0
+    SimplifiedLoweringTester<Object*> t;
+
+    Node* one = t.Int32Constant(1);
+    Node* index = t.Int32Constant(0);
+    Node* limit = t.Int32Constant(static_cast<int>(num_elements));
+    t.environment()->Push(index);
+    Node* src = this->GetBaseNode(&t);
+    Node* dst = that->GetBaseNode(&t);
+    {
+      LoopBuilder loop(&t);
+      loop.BeginLoop();
+      // Loop exit condition
+      index = t.environment()->Top();
+      Node* condition = t.Int32LessThan(index, limit);
+      loop.BreakUnless(condition);
+      // dst[index] = src[index]
+      index = t.environment()->Pop();
+      Node* load = t.LoadElement(this->GetElementAccess(), src, index);
+      t.StoreElement(that->GetElementAccess(), dst, index, load);
+      // index++
+      index = t.Int32Add(index, one);
+      t.environment()->Push(index);
+      // continue
+      loop.EndBody();
+      loop.EndLoop();
+    }
+    index = t.environment()->Pop();
+    t.Return(t.jsgraph.TrueConstant());
+    t.LowerAllNodes();
+    t.GenerateCode();
+
+    if (Pipeline::SupportedTarget()) {
+      Object* result = t.Call();
+      CHECK_EQ(t.isolate()->heap()->true_value(), result);
+    }
+#endif
+  }
+
+  E GetElement(int index) {
+    BoundsCheck(index);
+    if (tagged) {
+      E* raw = reinterpret_cast<E*>(tagged_array->GetDataStartAddress());
+      return raw[index];
+    } else {
+      return untagged_array[index];
+    }
+  }
+
+ private:
+  ElementAccess GetElementAccess() {
+    ElementAccess access = {tagged ? kTaggedBase : kUntaggedBase,
+                            tagged ? FixedArrayBase::kHeaderSize : 0,
+                            Type::Any(), rep};
+    return access;
+  }
+
+  FieldAccess GetFieldAccess(int field) {
+    int offset = field * sizeof(E);
+    FieldAccess access = {tagged ? kTaggedBase : kUntaggedBase,
+                          offset + (tagged ? FixedArrayBase::kHeaderSize : 0),
+                          Handle<Name>(), Type::Any(), rep};
+    return access;
+  }
+
+  template <typename T>
+  Node* GetBaseNode(SimplifiedLoweringTester<T>* t) {
+    return tagged ? t->HeapConstant(tagged_array)
+                  : t->PointerConstant(untagged_array);
+  }
+
+  void BoundsCheck(int index) {
+    CHECK_GE(index, 0);
+    CHECK_LT(index, static_cast<int>(num_elements));
+    CHECK_EQ(static_cast<int>(ByteSize()), tagged_array->length());
+  }
+};
+
+
+template <typename E>
+static void RunAccessTest(MachineType rep, E* original_elements, size_t num) {
+  int num_elements = static_cast<int>(num);
+
+  for (int taggedness = 0; taggedness < 2; taggedness++) {
+    AccessTester<E> a(taggedness == 1, rep, original_elements, num);
+    for (int field = 0; field < 2; field++) {
+      for (int i = 0; i < num_elements - 1; i++) {
+        a.Reinitialize();
+        if (field == 0) {
+          a.RunCopyField(i, i + 1);  // Test field read/write.
+        } else {
+          a.RunCopyElement(i, i + 1);  // Test element read/write.
+        }
+        if (Pipeline::SupportedTarget()) {  // verify.
+          for (int j = 0; j < num_elements; j++) {
+            E expect =
+                j == (i + 1) ? original_elements[i] : original_elements[j];
+            CHECK_EQ(expect, a.GetElement(j));
+          }
+        }
+      }
+    }
+  }
+  // Test array copy.
+  for (int tf = 0; tf < 2; tf++) {
+    for (int tt = 0; tt < 2; tt++) {
+      AccessTester<E> a(tf == 1, rep, original_elements, num);
+      AccessTester<E> b(tt == 1, rep, original_elements, num);
+      a.RunCopyElements(&b);
+      if (Pipeline::SupportedTarget()) {  // verify.
+        for (int i = 0; i < num_elements; i++) {
+          CHECK_EQ(a.GetElement(i), b.GetElement(i));
+        }
+      }
+    }
+  }
+}
+
+
+TEST(RunAccessTests_uint8) {
+  uint8_t data[] = {0x07, 0x16, 0x25, 0x34, 0x43, 0x99,
+                    0xab, 0x78, 0x89, 0x19, 0x2b, 0x38};
+  RunAccessTest<uint8_t>(kMachInt8, data, arraysize(data));
+}
+
+
+TEST(RunAccessTests_uint16) {
+  uint16_t data[] = {0x071a, 0x162b, 0x253c, 0x344d, 0x435e, 0x7777};
+  RunAccessTest<uint16_t>(kMachInt16, data, arraysize(data));
+}
+
+
+TEST(RunAccessTests_int32) {
+  int32_t data[] = {-211, 211, 628347, 2000000000, -2000000000, -1, -100000034};
+  RunAccessTest<int32_t>(kMachInt32, data, arraysize(data));
+}
+
+
+#define V8_2PART_INT64(a, b) (((static_cast<int64_t>(a) << 32) + 0x##b##u))
+
+
+TEST(RunAccessTests_int64) {
+  if (kPointerSize != 8) return;
+  int64_t data[] = {V8_2PART_INT64(0x10111213, 14151617),
+                    V8_2PART_INT64(0x20212223, 24252627),
+                    V8_2PART_INT64(0x30313233, 34353637),
+                    V8_2PART_INT64(0xa0a1a2a3, a4a5a6a7),
+                    V8_2PART_INT64(0xf0f1f2f3, f4f5f6f7)};
+  RunAccessTest<int64_t>(kMachInt64, data, arraysize(data));
+}
+
+
+TEST(RunAccessTests_float64) {
+  double data[] = {1.25, -1.25, 2.75, 11.0, 11100.8};
+  RunAccessTest<double>(kMachFloat64, data, arraysize(data));
+}
+
+
+TEST(RunAccessTests_Smi) {
+  Smi* data[] = {Smi::FromInt(-1),    Smi::FromInt(-9),
+                 Smi::FromInt(0),     Smi::FromInt(666),
+                 Smi::FromInt(77777), Smi::FromInt(Smi::kMaxValue)};
+  RunAccessTest<Smi*>(kMachAnyTagged, data, arraysize(data));
+}
+
+
+// Fills in most of the nodes of the graph in order to make tests shorter.
+class TestingGraph : public HandleAndZoneScope, public GraphAndBuilders {
+ public:
+  Typer typer;
+  JSOperatorBuilder javascript;
+  JSGraph jsgraph;
+  Node* p0;
+  Node* p1;
+  Node* p2;
+  Node* start;
+  Node* end;
+  Node* ret;
+
+  explicit TestingGraph(Type* p0_type, Type* p1_type = Type::None(),
+                        Type* p2_type = Type::None())
+      : GraphAndBuilders(main_zone()),
+        typer(main_zone()),
+        javascript(main_zone()),
+        jsgraph(graph(), common(), &javascript, &typer, machine()) {
+    start = graph()->NewNode(common()->Start(2));
+    graph()->SetStart(start);
+    ret =
+        graph()->NewNode(common()->Return(), jsgraph.Constant(0), start, start);
+    end = graph()->NewNode(common()->End(), ret);
+    graph()->SetEnd(end);
+    p0 = graph()->NewNode(common()->Parameter(0), start);
+    p1 = graph()->NewNode(common()->Parameter(1), start);
+    p2 = graph()->NewNode(common()->Parameter(2), start);
+    NodeProperties::SetBounds(p0, Bounds(p0_type));
+    NodeProperties::SetBounds(p1, Bounds(p1_type));
+    NodeProperties::SetBounds(p2, Bounds(p2_type));
+  }
+
+  void CheckLoweringBinop(IrOpcode::Value expected, const Operator* op) {
+    Node* node = Return(graph()->NewNode(op, p0, p1));
+    Lower();
+    CHECK_EQ(expected, node->opcode());
+  }
+
+  void CheckLoweringTruncatedBinop(IrOpcode::Value expected, const Operator* op,
+                                   const Operator* trunc) {
+    Node* node = graph()->NewNode(op, p0, p1);
+    Return(graph()->NewNode(trunc, node));
+    Lower();
+    CHECK_EQ(expected, node->opcode());
+  }
+
+  void Lower() {
+    SimplifiedLowering lowering(&jsgraph);
+    lowering.LowerAllNodes();
+  }
+
+  // Inserts the node as the return value of the graph.
+  Node* Return(Node* node) {
+    ret->ReplaceInput(0, node);
+    return node;
+  }
+
+  // Inserts the node as the effect input to the return of the graph.
+  void Effect(Node* node) { ret->ReplaceInput(1, node); }
+
+  Node* ExampleWithOutput(MachineType type) {
+    // TODO(titzer): use parameters with guaranteed representations.
+    if (type & kTypeInt32) {
+      return graph()->NewNode(machine()->Int32Add(), jsgraph.Int32Constant(1),
+                              jsgraph.Int32Constant(1));
+    } else if (type & kTypeUint32) {
+      return graph()->NewNode(machine()->Word32Shr(), jsgraph.Int32Constant(1),
+                              jsgraph.Int32Constant(1));
+    } else if (type & kRepFloat64) {
+      return graph()->NewNode(machine()->Float64Add(),
+                              jsgraph.Float64Constant(1),
+                              jsgraph.Float64Constant(1));
+    } else if (type & kRepBit) {
+      return graph()->NewNode(machine()->Word32Equal(),
+                              jsgraph.Int32Constant(1),
+                              jsgraph.Int32Constant(1));
+    } else if (type & kRepWord64) {
+      return graph()->NewNode(machine()->Int64Add(), Int64Constant(1),
+                              Int64Constant(1));
+    } else {
+      CHECK(type & kRepTagged);
+      return p0;
+    }
+  }
+
+  Node* Use(Node* node, MachineType type) {
+    if (type & kTypeInt32) {
+      return graph()->NewNode(machine()->Int32LessThan(), node,
+                              jsgraph.Int32Constant(1));
+    } else if (type & kTypeUint32) {
+      return graph()->NewNode(machine()->Uint32LessThan(), node,
+                              jsgraph.Int32Constant(1));
+    } else if (type & kRepFloat64) {
+      return graph()->NewNode(machine()->Float64Add(), node,
+                              jsgraph.Float64Constant(1));
+    } else if (type & kRepWord64) {
+      return graph()->NewNode(machine()->Int64LessThan(), node,
+                              Int64Constant(1));
+    } else {
+      return graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), node,
+                              jsgraph.TrueConstant());
+    }
+  }
+
+  Node* Branch(Node* cond) {
+    Node* br = graph()->NewNode(common()->Branch(), cond, start);
+    Node* tb = graph()->NewNode(common()->IfTrue(), br);
+    Node* fb = graph()->NewNode(common()->IfFalse(), br);
+    Node* m = graph()->NewNode(common()->Merge(2), tb, fb);
+    NodeProperties::ReplaceControlInput(ret, m);
+    return br;
+  }
+
+  Node* Int64Constant(int64_t v) {
+    return graph()->NewNode(common()->Int64Constant(v));
+  }
+
+  SimplifiedOperatorBuilder* simplified() { return &main_simplified_; }
+  MachineOperatorBuilder* machine() { return &main_machine_; }
+  CommonOperatorBuilder* common() { return &main_common_; }
+  Graph* graph() { return main_graph_; }
+};
+
+
+TEST(LowerBooleanNot_bit_bit) {
+  // BooleanNot(x: kRepBit) used as kRepBit
+  TestingGraph t(Type::Boolean());
+  Node* b = t.ExampleWithOutput(kRepBit);
+  Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b);
+  Node* use = t.Branch(inv);
+  t.Lower();
+  Node* cmp = use->InputAt(0);
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
+  Node* f = t.jsgraph.Int32Constant(0);
+  CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
+}
+
+
+TEST(LowerBooleanNot_bit_tagged) {
+  // BooleanNot(x: kRepBit) used as kRepTagged
+  TestingGraph t(Type::Boolean());
+  Node* b = t.ExampleWithOutput(kRepBit);
+  Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b);
+  Node* use = t.Use(inv, kRepTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
+  Node* cmp = use->InputAt(0)->InputAt(0);
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
+  Node* f = t.jsgraph.Int32Constant(0);
+  CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
+}
+
+
+TEST(LowerBooleanNot_tagged_bit) {
+  // BooleanNot(x: kRepTagged) used as kRepBit
+  TestingGraph t(Type::Boolean());
+  Node* b = t.p0;
+  Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b);
+  Node* use = t.Branch(inv);
+  t.Lower();
+  Node* cmp = use->InputAt(0);
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
+  Node* f = t.jsgraph.FalseConstant();
+  CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
+}
+
+
+TEST(LowerBooleanNot_tagged_tagged) {
+  // BooleanNot(x: kRepTagged) used as kRepTagged
+  TestingGraph t(Type::Boolean());
+  Node* b = t.p0;
+  Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b);
+  Node* use = t.Use(inv, kRepTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kChangeBitToBool, use->InputAt(0)->opcode());
+  Node* cmp = use->InputAt(0)->InputAt(0);
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cmp->opcode());
+  CHECK(b == cmp->InputAt(0) || b == cmp->InputAt(1));
+  Node* f = t.jsgraph.FalseConstant();
+  CHECK(f == cmp->InputAt(0) || f == cmp->InputAt(1));
+}
+
+
+TEST(LowerBooleanToNumber_bit_int32) {
+  // BooleanToNumber(x: kRepBit) used as kMachInt32
+  TestingGraph t(Type::Boolean());
+  Node* b = t.ExampleWithOutput(kRepBit);
+  Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b);
+  Node* use = t.Use(cnv, kMachInt32);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(b, use->InputAt(0));
+}
+
+
+TEST(LowerBooleanToNumber_tagged_int32) {
+  // BooleanToNumber(x: kRepTagged) used as kMachInt32
+  TestingGraph t(Type::Boolean());
+  Node* b = t.p0;
+  Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b);
+  Node* use = t.Use(cnv, kMachInt32);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cnv->opcode());
+  CHECK(b == cnv->InputAt(0) || b == cnv->InputAt(1));
+  Node* c = t.jsgraph.TrueConstant();
+  CHECK(c == cnv->InputAt(0) || c == cnv->InputAt(1));
+}
+
+
+TEST(LowerBooleanToNumber_bit_tagged) {
+  // BooleanToNumber(x: kRepBit) used as kMachAnyTagged
+  TestingGraph t(Type::Boolean());
+  Node* b = t.ExampleWithOutput(kRepBit);
+  Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b);
+  Node* use = t.Use(cnv, kMachAnyTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(b, use->InputAt(0)->InputAt(0));
+  CHECK_EQ(IrOpcode::kChangeInt32ToTagged, use->InputAt(0)->opcode());
+}
+
+
+TEST(LowerBooleanToNumber_tagged_tagged) {
+  // BooleanToNumber(x: kRepTagged) used as kMachAnyTagged
+  TestingGraph t(Type::Boolean());
+  Node* b = t.p0;
+  Node* cnv = t.graph()->NewNode(t.simplified()->BooleanToNumber(), b);
+  Node* use = t.Use(cnv, kMachAnyTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(cnv, use->InputAt(0)->InputAt(0));
+  CHECK_EQ(IrOpcode::kChangeInt32ToTagged, use->InputAt(0)->opcode());
+  CHECK_EQ(t.machine()->WordEqual()->opcode(), cnv->opcode());
+  CHECK(b == cnv->InputAt(0) || b == cnv->InputAt(1));
+  Node* c = t.jsgraph.TrueConstant();
+  CHECK(c == cnv->InputAt(0) || c == cnv->InputAt(1));
+}
+
+
+static Type* test_types[] = {Type::Signed32(), Type::Unsigned32(),
+                             Type::Number(), Type::Any()};
+
+
+TEST(LowerNumberCmp_to_int32) {
+  TestingGraph t(Type::Signed32(), Type::Signed32());
+
+  t.CheckLoweringBinop(IrOpcode::kWord32Equal, t.simplified()->NumberEqual());
+  t.CheckLoweringBinop(IrOpcode::kInt32LessThan,
+                       t.simplified()->NumberLessThan());
+  t.CheckLoweringBinop(IrOpcode::kInt32LessThanOrEqual,
+                       t.simplified()->NumberLessThanOrEqual());
+}
+
+
+TEST(LowerNumberCmp_to_uint32) {
+  TestingGraph t(Type::Unsigned32(), Type::Unsigned32());
+
+  t.CheckLoweringBinop(IrOpcode::kWord32Equal, t.simplified()->NumberEqual());
+  t.CheckLoweringBinop(IrOpcode::kUint32LessThan,
+                       t.simplified()->NumberLessThan());
+  t.CheckLoweringBinop(IrOpcode::kUint32LessThanOrEqual,
+                       t.simplified()->NumberLessThanOrEqual());
+}
+
+
+TEST(LowerNumberCmp_to_float64) {
+  static Type* types[] = {Type::Number(), Type::Any()};
+
+  for (size_t i = 0; i < arraysize(types); i++) {
+    TestingGraph t(types[i], types[i]);
+
+    t.CheckLoweringBinop(IrOpcode::kFloat64Equal,
+                         t.simplified()->NumberEqual());
+    t.CheckLoweringBinop(IrOpcode::kFloat64LessThan,
+                         t.simplified()->NumberLessThan());
+    t.CheckLoweringBinop(IrOpcode::kFloat64LessThanOrEqual,
+                         t.simplified()->NumberLessThanOrEqual());
+  }
+}
+
+
+TEST(LowerNumberAddSub_to_int32) {
+  TestingGraph t(Type::Signed32(), Type::Signed32());
+  t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+                                t.simplified()->NumberAdd(),
+                                t.simplified()->NumberToInt32());
+  t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+                                t.simplified()->NumberSubtract(),
+                                t.simplified()->NumberToInt32());
+}
+
+
+TEST(LowerNumberAddSub_to_uint32) {
+  TestingGraph t(Type::Unsigned32(), Type::Unsigned32());
+  t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Add,
+                                t.simplified()->NumberAdd(),
+                                t.simplified()->NumberToUint32());
+  t.CheckLoweringTruncatedBinop(IrOpcode::kInt32Sub,
+                                t.simplified()->NumberSubtract(),
+                                t.simplified()->NumberToUint32());
+}
+
+
+TEST(LowerNumberAddSub_to_float64) {
+  for (size_t i = 0; i < arraysize(test_types); i++) {
+    TestingGraph t(test_types[i], test_types[i]);
+
+    t.CheckLoweringBinop(IrOpcode::kFloat64Add, t.simplified()->NumberAdd());
+    t.CheckLoweringBinop(IrOpcode::kFloat64Sub,
+                         t.simplified()->NumberSubtract());
+  }
+}
+
+
+TEST(LowerNumberDivMod_to_float64) {
+  for (size_t i = 0; i < arraysize(test_types); i++) {
+    TestingGraph t(test_types[i], test_types[i]);
+
+    t.CheckLoweringBinop(IrOpcode::kFloat64Div, t.simplified()->NumberDivide());
+    t.CheckLoweringBinop(IrOpcode::kFloat64Mod,
+                         t.simplified()->NumberModulus());
+  }
+}
+
+
+static void CheckChangeOf(IrOpcode::Value change, Node* of, Node* node) {
+  CHECK_EQ(change, node->opcode());
+  CHECK_EQ(of, node->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_nop) {
+  // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepTagged
+  TestingGraph t(Type::Signed32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0);
+  Node* use = t.Use(trunc, kRepTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_ChangeTaggedToFloat64) {
+  // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepFloat64
+  TestingGraph t(Type::Signed32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0);
+  Node* use = t.Use(trunc, kRepFloat64);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_ChangeTaggedToInt32) {
+  // NumberToInt32(x: kRepTagged | kTypeInt32) used as kRepWord32
+  TestingGraph t(Type::Signed32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0);
+  Node* use = t.Use(trunc, kTypeInt32);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32) {
+  // NumberToInt32(x: kRepFloat64) used as kMachInt32
+  TestingGraph t(Type::Number());
+  Node* p0 = t.ExampleWithOutput(kMachFloat64);
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), p0);
+  Node* use = t.Use(trunc, kMachInt32);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32_with_change) {
+  // NumberToInt32(x: kTypeNumber | kRepTagged) used as kMachInt32
+  TestingGraph t(Type::Number());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0);
+  Node* use = t.Use(trunc, kMachInt32);
+  t.Return(use);
+  t.Lower();
+  Node* node = use->InputAt(0);
+  CHECK_EQ(IrOpcode::kTruncateFloat64ToInt32, node->opcode());
+  Node* of = node->InputAt(0);
+  CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, of->opcode());
+  CHECK_EQ(t.p0, of->InputAt(0));
+}
+
+
+TEST(LowerNumberToInt32_to_ChangeFloat64ToTagged) {
+  // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepTagged
+}
+
+
+TEST(LowerNumberToInt32_to_ChangeFloat64ToInt32) {
+  // TODO(titzer): NumberToInt32(x: kRepFloat64 | kTypeInt32) used as kRepWord32
+  // | kTypeInt32
+}
+
+
+TEST(LowerNumberToUint32_to_nop) {
+  // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepTagged
+  TestingGraph t(Type::Unsigned32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0);
+  Node* use = t.Use(trunc, kRepTagged);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToUint32_to_ChangeTaggedToFloat64) {
+  // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepWord32
+  TestingGraph t(Type::Unsigned32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0);
+  Node* use = t.Use(trunc, kRepFloat64);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToUint32_to_ChangeTaggedToUint32) {
+  // NumberToUint32(x: kRepTagged | kTypeUint32) used as kRepWord32
+  TestingGraph t(Type::Unsigned32());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0);
+  Node* use = t.Use(trunc, kTypeUint32);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kChangeTaggedToUint32, t.p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32) {
+  // NumberToUint32(x: kRepFloat64) used as kMachUint32
+  TestingGraph t(Type::Number());
+  Node* p0 = t.ExampleWithOutput(kMachFloat64);
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), p0);
+  Node* use = t.Use(trunc, kMachUint32);
+  t.Return(use);
+  t.Lower();
+  CheckChangeOf(IrOpcode::kTruncateFloat64ToInt32, p0, use->InputAt(0));
+}
+
+
+TEST(LowerNumberToUint32_to_TruncateFloat64ToInt32_with_change) {
+  // NumberToInt32(x: kTypeNumber | kRepTagged) used as kMachUint32
+  TestingGraph t(Type::Number());
+  Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0);
+  Node* use = t.Use(trunc, kMachUint32);
+  t.Return(use);
+  t.Lower();
+  Node* node = use->InputAt(0);
+  CHECK_EQ(IrOpcode::kTruncateFloat64ToInt32, node->opcode());
+  Node* of = node->InputAt(0);
+  CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, of->opcode());
+  CHECK_EQ(t.p0, of->InputAt(0));
+}
+
+
+TEST(LowerNumberToUint32_to_ChangeFloat64ToTagged) {
+  // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
+  // kRepTagged
+}
+
+
+TEST(LowerNumberToUint32_to_ChangeFloat64ToUint32) {
+  // TODO(titzer): NumberToUint32(x: kRepFloat64 | kTypeUint32) used as
+  // kRepWord32
+}
+
+
+TEST(LowerNumberToUint32_to_TruncateFloat64ToUint32) {
+  // TODO(titzer): NumberToUint32(x: kRepFloat64) used as kRepWord32
+}
+
+
+TEST(LowerReferenceEqual_to_wordeq) {
+  TestingGraph t(Type::Any(), Type::Any());
+  IrOpcode::Value opcode =
+      static_cast<IrOpcode::Value>(t.machine()->WordEqual()->opcode());
+  t.CheckLoweringBinop(opcode, t.simplified()->ReferenceEqual(Type::Any()));
+}
+
+
+TEST(LowerStringOps_to_call_and_compare) {
+  if (Pipeline::SupportedTarget()) {
+    // These tests need linkage for the calls.
+    TestingGraph t(Type::String(), Type::String());
+    IrOpcode::Value compare_eq =
+        static_cast<IrOpcode::Value>(t.machine()->WordEqual()->opcode());
+    IrOpcode::Value compare_lt =
+        static_cast<IrOpcode::Value>(t.machine()->IntLessThan()->opcode());
+    IrOpcode::Value compare_le = static_cast<IrOpcode::Value>(
+        t.machine()->IntLessThanOrEqual()->opcode());
+    t.CheckLoweringBinop(compare_eq, t.simplified()->StringEqual());
+    t.CheckLoweringBinop(compare_lt, t.simplified()->StringLessThan());
+    t.CheckLoweringBinop(compare_le, t.simplified()->StringLessThanOrEqual());
+    t.CheckLoweringBinop(IrOpcode::kCall, t.simplified()->StringAdd());
+  }
+}
+
+
+void CheckChangeInsertion(IrOpcode::Value expected, MachineType from,
+                          MachineType to) {
+  TestingGraph t(Type::Any());
+  Node* in = t.ExampleWithOutput(from);
+  Node* use = t.Use(in, to);
+  t.Return(use);
+  t.Lower();
+  CHECK_EQ(expected, use->InputAt(0)->opcode());
+  CHECK_EQ(in, use->InputAt(0)->InputAt(0));
+}
+
+
+TEST(InsertBasicChanges) {
+  CheckChangeInsertion(IrOpcode::kChangeFloat64ToInt32, kRepFloat64,
+                       kTypeInt32);
+  CheckChangeInsertion(IrOpcode::kChangeFloat64ToUint32, kRepFloat64,
+                       kTypeUint32);
+  CheckChangeInsertion(IrOpcode::kChangeTaggedToInt32, kRepTagged, kTypeInt32);
+  CheckChangeInsertion(IrOpcode::kChangeTaggedToUint32, kRepTagged,
+                       kTypeUint32);
+
+  CheckChangeInsertion(IrOpcode::kChangeFloat64ToTagged, kRepFloat64,
+                       kRepTagged);
+  CheckChangeInsertion(IrOpcode::kChangeTaggedToFloat64, kRepTagged,
+                       kRepFloat64);
+
+  CheckChangeInsertion(IrOpcode::kChangeInt32ToFloat64, kTypeInt32,
+                       kRepFloat64);
+  CheckChangeInsertion(IrOpcode::kChangeInt32ToTagged, kTypeInt32, kRepTagged);
+
+  CheckChangeInsertion(IrOpcode::kChangeUint32ToFloat64, kTypeUint32,
+                       kRepFloat64);
+  CheckChangeInsertion(IrOpcode::kChangeUint32ToTagged, kTypeUint32,
+                       kRepTagged);
+}
+
+
+static void CheckChangesAroundBinop(TestingGraph* t, const Operator* op,
+                                    IrOpcode::Value input_change,
+                                    IrOpcode::Value output_change) {
+  Node* binop = t->graph()->NewNode(op, t->p0, t->p1);
+  t->Return(binop);
+  t->Lower();
+  CHECK_EQ(input_change, binop->InputAt(0)->opcode());
+  CHECK_EQ(input_change, binop->InputAt(1)->opcode());
+  CHECK_EQ(t->p0, binop->InputAt(0)->InputAt(0));
+  CHECK_EQ(t->p1, binop->InputAt(1)->InputAt(0));
+  CHECK_EQ(output_change, t->ret->InputAt(0)->opcode());
+  CHECK_EQ(binop, t->ret->InputAt(0)->InputAt(0));
+}
+
+
+TEST(InsertChangesAroundInt32Binops) {
+  TestingGraph t(Type::Signed32(), Type::Signed32());
+
+  const Operator* ops[] = {t.machine()->Int32Add(),  t.machine()->Int32Sub(),
+                           t.machine()->Int32Mul(),  t.machine()->Int32Div(),
+                           t.machine()->Int32Mod(),  t.machine()->Word32And(),
+                           t.machine()->Word32Or(),  t.machine()->Word32Xor(),
+                           t.machine()->Word32Shl(), t.machine()->Word32Sar()};
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32,
+                            IrOpcode::kChangeInt32ToTagged);
+  }
+}
+
+
+TEST(InsertChangesAroundInt32Cmp) {
+  TestingGraph t(Type::Signed32(), Type::Signed32());
+
+  const Operator* ops[] = {t.machine()->Int32LessThan(),
+                           t.machine()->Int32LessThanOrEqual()};
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32,
+                            IrOpcode::kChangeBitToBool);
+  }
+}
+
+
+TEST(InsertChangesAroundUint32Cmp) {
+  TestingGraph t(Type::Unsigned32(), Type::Unsigned32());
+
+  const Operator* ops[] = {t.machine()->Uint32LessThan(),
+                           t.machine()->Uint32LessThanOrEqual()};
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToUint32,
+                            IrOpcode::kChangeBitToBool);
+  }
+}
+
+
+TEST(InsertChangesAroundFloat64Binops) {
+  TestingGraph t(Type::Number(), Type::Number());
+
+  const Operator* ops[] = {
+      t.machine()->Float64Add(), t.machine()->Float64Sub(),
+      t.machine()->Float64Mul(), t.machine()->Float64Div(),
+      t.machine()->Float64Mod(),
+  };
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64,
+                            IrOpcode::kChangeFloat64ToTagged);
+  }
+}
+
+
+TEST(InsertChangesAroundFloat64Cmp) {
+  TestingGraph t(Type::Number(), Type::Number());
+
+  const Operator* ops[] = {t.machine()->Float64Equal(),
+                           t.machine()->Float64LessThan(),
+                           t.machine()->Float64LessThanOrEqual()};
+
+  for (size_t i = 0; i < arraysize(ops); i++) {
+    CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64,
+                            IrOpcode::kChangeBitToBool);
+  }
+}
+
+
+void CheckFieldAccessArithmetic(FieldAccess access, Node* load_or_store) {
+  Int32Matcher index = Int32Matcher(load_or_store->InputAt(1));
+  CHECK(index.Is(access.offset - access.tag()));
+}
+
+
+Node* CheckElementAccessArithmetic(ElementAccess access, Node* load_or_store) {
+  Int32BinopMatcher index(load_or_store->InputAt(1));
+  CHECK_EQ(IrOpcode::kInt32Add, index.node()->opcode());
+  CHECK(index.right().Is(access.header_size - access.tag()));
+
+  int element_size = ElementSizeOf(access.machine_type);
+
+  if (element_size != 1) {
+    Int32BinopMatcher mul(index.left().node());
+    CHECK_EQ(IrOpcode::kInt32Mul, mul.node()->opcode());
+    CHECK(mul.right().Is(element_size));
+    return mul.left().node();
+  } else {
+    return index.left().node();
+  }
+}
+
+
+static const MachineType machine_reps[] = {
+    kRepBit,    kMachInt8,    kMachInt16,    kMachInt32,
+    kMachInt64, kMachFloat64, kMachAnyTagged};
+
+
+TEST(LowerLoadField_to_load) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+
+  for (size_t i = 0; i < arraysize(machine_reps); i++) {
+    FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                          Handle<Name>::null(), Type::Any(), machine_reps[i]};
+
+    Node* load =
+        t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
+    Node* use = t.Use(load, machine_reps[i]);
+    t.Return(use);
+    t.Lower();
+    CHECK_EQ(IrOpcode::kLoad, load->opcode());
+    CHECK_EQ(t.p0, load->InputAt(0));
+    CheckFieldAccessArithmetic(access, load);
+
+    MachineType rep = OpParameter<MachineType>(load);
+    CHECK_EQ(machine_reps[i], rep);
+  }
+}
+
+
+TEST(LowerStoreField_to_store) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+
+  for (size_t i = 0; i < arraysize(machine_reps); i++) {
+    FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                          Handle<Name>::null(), Type::Any(), machine_reps[i]};
+
+
+    Node* val = t.ExampleWithOutput(machine_reps[i]);
+    Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
+                                     val, t.start, t.start);
+    t.Effect(store);
+    t.Lower();
+    CHECK_EQ(IrOpcode::kStore, store->opcode());
+    CHECK_EQ(val, store->InputAt(2));
+    CheckFieldAccessArithmetic(access, store);
+
+    StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
+    if (machine_reps[i] & kRepTagged) {
+      CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
+    }
+    CHECK_EQ(machine_reps[i], rep.machine_type());
+  }
+}
+
+
+TEST(LowerLoadElement_to_load) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+
+  for (size_t i = 0; i < arraysize(machine_reps); i++) {
+    ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                            Type::Any(), machine_reps[i]};
+
+    Node* load =
+        t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0, t.p1,
+                           t.jsgraph.Int32Constant(1024), t.start);
+    Node* use = t.Use(load, machine_reps[i]);
+    t.Return(use);
+    t.Lower();
+    CHECK_EQ(IrOpcode::kLoad, load->opcode());
+    CHECK_EQ(t.p0, load->InputAt(0));
+    CheckElementAccessArithmetic(access, load);
+
+    MachineType rep = OpParameter<MachineType>(load);
+    CHECK_EQ(machine_reps[i], rep);
+  }
+}
+
+
+TEST(LowerStoreElement_to_store) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+
+  for (size_t i = 0; i < arraysize(machine_reps); i++) {
+    ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                            Type::Any(), machine_reps[i]};
+
+    Node* val = t.ExampleWithOutput(machine_reps[i]);
+    Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
+                                     t.p1, t.jsgraph.Int32Constant(1024), val,
+                                     t.start, t.start);
+    t.Effect(store);
+    t.Lower();
+    CHECK_EQ(IrOpcode::kStore, store->opcode());
+    CHECK_EQ(val, store->InputAt(2));
+    CheckElementAccessArithmetic(access, store);
+
+    StoreRepresentation rep = OpParameter<StoreRepresentation>(store);
+    if (machine_reps[i] & kRepTagged) {
+      CHECK_EQ(kFullWriteBarrier, rep.write_barrier_kind());
+    }
+    CHECK_EQ(machine_reps[i], rep.machine_type());
+  }
+}
+
+
+TEST(InsertChangeForLoadElementIndex) {
+  // LoadElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length) =>
+  //   Load(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k))
+  TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+  ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
+                          kMachAnyTagged};
+
+  Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
+                                  t.p1, t.p2, t.start);
+  t.Return(load);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kLoad, load->opcode());
+  CHECK_EQ(t.p0, load->InputAt(0));
+
+  Node* index = CheckElementAccessArithmetic(access, load);
+  CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p1, index);
+}
+
+
+TEST(InsertChangeForStoreElementIndex) {
+  // StoreElement(obj: Tagged, index: kTypeInt32 | kRepTagged, length, val) =>
+  //   Store(obj, Int32Add(Int32Mul(ChangeTaggedToInt32(index), #k), #k), val)
+  TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+  ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
+                          kMachAnyTagged};
+
+  Node* store =
+      t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0, t.p1, t.p2,
+                         t.jsgraph.TrueConstant(), t.start, t.start);
+  t.Effect(store);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kStore, store->opcode());
+  CHECK_EQ(t.p0, store->InputAt(0));
+
+  Node* index = CheckElementAccessArithmetic(access, store);
+  CheckChangeOf(IrOpcode::kChangeTaggedToInt32, t.p1, index);
+}
+
+
+TEST(InsertChangeForLoadElement) {
+  // TODO(titzer): test all load/store representation change insertions.
+  TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+  ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
+                          kMachFloat64};
+
+  Node* load = t.graph()->NewNode(t.simplified()->LoadElement(access), t.p0,
+                                  t.p1, t.p1, t.start);
+  t.Return(load);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kLoad, load->opcode());
+  CHECK_EQ(t.p0, load->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0));
+}
+
+
+TEST(InsertChangeForLoadField) {
+  // TODO(titzer): test all load/store representation change insertions.
+  TestingGraph t(Type::Any(), Type::Signed32());
+  FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                        Handle<Name>::null(), Type::Any(), kMachFloat64};
+
+  Node* load =
+      t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
+  t.Return(load);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kLoad, load->opcode());
+  CHECK_EQ(t.p0, load->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0));
+}
+
+
+TEST(InsertChangeForStoreElement) {
+  // TODO(titzer): test all load/store representation change insertions.
+  TestingGraph t(Type::Any(), Type::Signed32(), Type::Any());
+  ElementAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize, Type::Any(),
+                          kMachFloat64};
+
+  Node* store = t.graph()->NewNode(t.simplified()->StoreElement(access), t.p0,
+                                   t.jsgraph.Int32Constant(0), t.p2, t.p1,
+                                   t.start, t.start);
+  t.Effect(store);
+  t.Lower();
+
+  CHECK_EQ(IrOpcode::kStore, store->opcode());
+  CHECK_EQ(t.p0, store->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2));
+}
+
+
+TEST(InsertChangeForStoreField) {
+  // TODO(titzer): test all load/store representation change insertions.
+  TestingGraph t(Type::Any(), Type::Signed32());
+  FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                        Handle<Name>::null(), Type::Any(), kMachFloat64};
+
+  Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
+                                   t.p1, t.start, t.start);
+  t.Effect(store);
+  t.Lower();
+
+  CHECK_EQ(IrOpcode::kStore, store->opcode());
+  CHECK_EQ(t.p0, store->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2));
+}
+
+
+TEST(UpdatePhi) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+  static const MachineType kMachineTypes[] = {kMachInt32, kMachUint32,
+                                              kMachFloat64};
+
+  for (size_t i = 0; i < arraysize(kMachineTypes); i++) {
+    FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                          Handle<Name>::null(), Type::Any(), kMachineTypes[i]};
+
+    Node* load0 =
+        t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
+    Node* load1 =
+        t.graph()->NewNode(t.simplified()->LoadField(access), t.p1, t.start);
+    Node* phi = t.graph()->NewNode(t.common()->Phi(kMachAnyTagged, 2), load0,
+                                   load1, t.start);
+    t.Return(t.Use(phi, kMachineTypes[i]));
+    t.Lower();
+
+    CHECK_EQ(IrOpcode::kPhi, phi->opcode());
+    CHECK_EQ(RepresentationOf(kMachineTypes[i]),
+             RepresentationOf(OpParameter<MachineType>(phi)));
+  }
+}
+
+
+// TODO(titzer): this tests current behavior of assuming an implicit
+// representation change in loading float32s. Fix when float32 is fully
+// supported.
+TEST(ImplicitFloat32ToFloat64InLoads) {
+  TestingGraph t(Type::Any());
+
+  FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                        Handle<Name>::null(), Type::Any(), kMachFloat32};
+
+  Node* load =
+      t.graph()->NewNode(t.simplified()->LoadField(access), t.p0, t.start);
+  t.Return(load);
+  t.Lower();
+  CHECK_EQ(IrOpcode::kLoad, load->opcode());
+  CHECK_EQ(t.p0, load->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeFloat64ToTagged, load, t.ret->InputAt(0));
+}
+
+
+TEST(ImplicitFloat64ToFloat32InStores) {
+  TestingGraph t(Type::Any(), Type::Signed32());
+  FieldAccess access = {kTaggedBase, FixedArrayBase::kHeaderSize,
+                        Handle<Name>::null(), Type::Any(), kMachFloat32};
+
+  Node* store = t.graph()->NewNode(t.simplified()->StoreField(access), t.p0,
+                                   t.p1, t.start, t.start);
+  t.Effect(store);
+  t.Lower();
+
+  CHECK_EQ(IrOpcode::kStore, store->opcode());
+  CHECK_EQ(t.p0, store->InputAt(0));
+  CheckChangeOf(IrOpcode::kChangeTaggedToFloat64, t.p1, store->InputAt(2));
+}
diff --git a/test/cctest/compiler/value-helper.h b/test/cctest/compiler/value-helper.h
new file mode 100644
index 0000000..b5da982
--- /dev/null
+++ b/test/cctest/compiler/value-helper.h
@@ -0,0 +1,130 @@
+// 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_CCTEST_COMPILER_VALUE_HELPER_H_
+#define V8_CCTEST_COMPILER_VALUE_HELPER_H_
+
+#include "src/v8.h"
+
+#include "src/compiler/common-operator.h"
+#include "src/compiler/node.h"
+#include "src/compiler/node-matchers.h"
+#include "src/isolate.h"
+#include "src/objects.h"
+#include "test/cctest/cctest.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// A collection of utilities related to numerical and heap values, including
+// example input values of various types, including int32_t, uint32_t, double,
+// etc.
+class ValueHelper {
+ public:
+  Isolate* isolate_;
+
+  ValueHelper() : isolate_(CcTest::InitIsolateOnce()) {}
+
+  void CheckFloat64Constant(double expected, Node* node) {
+    CHECK_EQ(IrOpcode::kFloat64Constant, node->opcode());
+    CHECK_EQ(expected, OpParameter<double>(node));
+  }
+
+  void CheckNumberConstant(double expected, Node* node) {
+    CHECK_EQ(IrOpcode::kNumberConstant, node->opcode());
+    CHECK_EQ(expected, OpParameter<double>(node));
+  }
+
+  void CheckInt32Constant(int32_t expected, Node* node) {
+    CHECK_EQ(IrOpcode::kInt32Constant, node->opcode());
+    CHECK_EQ(expected, OpParameter<int32_t>(node));
+  }
+
+  void CheckUint32Constant(int32_t expected, Node* node) {
+    CHECK_EQ(IrOpcode::kInt32Constant, node->opcode());
+    CHECK_EQ(expected, OpParameter<uint32_t>(node));
+  }
+
+  void CheckHeapConstant(Object* expected, Node* node) {
+    CHECK_EQ(IrOpcode::kHeapConstant, node->opcode());
+    CHECK_EQ(expected, *OpParameter<Unique<Object> >(node).handle());
+  }
+
+  void CheckTrue(Node* node) {
+    CheckHeapConstant(isolate_->heap()->true_value(), node);
+  }
+
+  void CheckFalse(Node* node) {
+    CheckHeapConstant(isolate_->heap()->false_value(), node);
+  }
+
+  static std::vector<double> float64_vector() {
+    static const double nan = v8::base::OS::nan_value();
+    static const double values[] = {
+        0.125,           0.25,            0.375,          0.5,
+        1.25,            -1.75,           2,              5.125,
+        6.25,            0.0,             -0.0,           982983.25,
+        888,             2147483647.0,    -999.75,        3.1e7,
+        -2e66,           3e-88,           -2147483648.0,  V8_INFINITY,
+        -V8_INFINITY,    nan,             2147483647.375, 2147483647.75,
+        2147483648.0,    2147483648.25,   2147483649.25,  -2147483647.0,
+        -2147483647.125, -2147483647.875, -2147483648.25, -2147483649.5};
+    return std::vector<double>(&values[0], &values[arraysize(values)]);
+  }
+
+  static const std::vector<int32_t> int32_vector() {
+    std::vector<uint32_t> values = uint32_vector();
+    return std::vector<int32_t>(values.begin(), values.end());
+  }
+
+  static const std::vector<uint32_t> uint32_vector() {
+    static const uint32_t kValues[] = {
+        0x00000000, 0x00000001, 0xffffffff, 0x1b09788b, 0x04c5fce8, 0xcc0de5bf,
+        0x273a798e, 0x187937a3, 0xece3af83, 0x5495a16b, 0x0b668ecc, 0x11223344,
+        0x0000009e, 0x00000043, 0x0000af73, 0x0000116b, 0x00658ecc, 0x002b3b4c,
+        0x88776655, 0x70000000, 0x07200000, 0x7fffffff, 0x56123761, 0x7fffff00,
+        0x761c4761, 0x80000000, 0x88888888, 0xa0000000, 0xdddddddd, 0xe0000000,
+        0xeeeeeeee, 0xfffffffd, 0xf0000000, 0x007fffff, 0x003fffff, 0x001fffff,
+        0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, 0x0000ffff, 0x00007fff,
+        0x00003fff, 0x00001fff, 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff};
+    return std::vector<uint32_t>(&kValues[0], &kValues[arraysize(kValues)]);
+  }
+
+  static const std::vector<double> nan_vector(size_t limit = 0) {
+    static const double nan = v8::base::OS::nan_value();
+    static const double values[] = {-nan,               -V8_INFINITY * -0.0,
+                                    -V8_INFINITY * 0.0, V8_INFINITY * -0.0,
+                                    V8_INFINITY * 0.0,  nan};
+    return std::vector<double>(&values[0], &values[arraysize(values)]);
+  }
+
+  static const std::vector<uint32_t> ror_vector() {
+    static const uint32_t kValues[31] = {
+        1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16,
+        17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+    return std::vector<uint32_t>(&kValues[0], &kValues[arraysize(kValues)]);
+  }
+};
+
+// Helper macros that can be used in FOR_INT32_INPUTS(i) { ... *i ... }
+// Watch out, these macros aren't hygenic; they pollute your scope. Thanks STL.
+#define FOR_INPUTS(ctype, itype, var)                           \
+  std::vector<ctype> var##_vec = ValueHelper::itype##_vector(); \
+  for (std::vector<ctype>::iterator var = var##_vec.begin();    \
+       var != var##_vec.end(); ++var)
+
+#define FOR_INT32_INPUTS(var) FOR_INPUTS(int32_t, int32, var)
+#define FOR_UINT32_INPUTS(var) FOR_INPUTS(uint32_t, uint32, var)
+#define FOR_FLOAT64_INPUTS(var) FOR_INPUTS(double, float64, var)
+
+#define FOR_INT32_SHIFTS(var) for (int32_t var = 0; var < 32; var++)
+
+#define FOR_UINT32_SHIFTS(var) for (uint32_t var = 0; var < 32; var++)
+
+}  // namespace compiler
+}  // namespace internal
+}  // namespace v8
+
+#endif  // V8_CCTEST_COMPILER_VALUE_HELPER_H_