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_