| // 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/codegen/assembler.h" |
| #include "src/compiler/js-graph.h" |
| #include "src/compiler/node-properties.h" |
| #include "src/heap/factory-inl.h" |
| #include "test/cctest/cctest.h" |
| #include "test/cctest/compiler/value-helper.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace compiler { |
| |
| class JSCacheTesterHelper { |
| protected: |
| explicit JSCacheTesterHelper(Zone* zone) |
| : main_graph_(zone), |
| main_common_(zone), |
| main_javascript_(zone), |
| main_machine_(zone) {} |
| Graph main_graph_; |
| CommonOperatorBuilder main_common_; |
| JSOperatorBuilder main_javascript_; |
| MachineOperatorBuilder main_machine_; |
| }; |
| |
| |
| // TODO(dcarney): JSConstantCacheTester inherits from JSGraph??? |
| class JSConstantCacheTester : public HandleAndZoneScope, |
| public JSCacheTesterHelper, |
| public JSGraph { |
| public: |
| JSConstantCacheTester() |
| : JSCacheTesterHelper(main_zone()), |
| JSGraph(main_isolate(), &main_graph_, &main_common_, &main_javascript_, |
| nullptr, &main_machine_) { |
| main_graph_.SetStart(main_graph_.NewNode(common()->Start(0))); |
| main_graph_.SetEnd( |
| main_graph_.NewNode(common()->End(1), main_graph_.start())); |
| } |
| |
| Handle<HeapObject> handle(Node* node) { |
| CHECK_EQ(IrOpcode::kHeapConstant, node->opcode()); |
| return HeapConstantOf(node->op()); |
| } |
| |
| 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(std::numeric_limits<double>::quiet_NaN())); |
| CHECK_NE(zero, T.Float64Constant(0)); |
| CHECK_NE(zero, T.Int32Constant(0)); |
| } |
| |
| |
| 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); |
| |
| double zero_value = OpParameter<double>(zero->op()); |
| double minus_zero_value = OpParameter<double>(minus_zero->op()); |
| |
| CHECK(bit_cast<uint64_t>(0.0) == bit_cast<uint64_t>(zero_value)); |
| CHECK(bit_cast<uint64_t>(-0.0) != bit_cast<uint64_t>(zero_value)); |
| CHECK(bit_cast<uint64_t>(0.0) != bit_cast<uint64_t>(minus_zero_value)); |
| CHECK(bit_cast<uint64_t>(-0.0) == bit_cast<uint64_t>(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(std::numeric_limits<double>::quiet_NaN())); |
| CHECK_NE(zero, T.Float64Constant(0)); |
| CHECK_NE(zero, T.Int32Constant(0)); |
| } |
| |
| |
| 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(std::numeric_limits<double>::quiet_NaN())); |
| CHECK_NE(one, T.Float64Constant(1.0)); |
| CHECK_NE(one, T.Int32Constant(1)); |
| } |
| |
| |
| 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(std::numeric_limits<double>::quiet_NaN())); |
| CHECK_NE(one, T.Float64Constant(1.0)); |
| CHECK_NE(one, T.Int32Constant(1)); |
| } |
| |
| |
| 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(HeapNumbers) { |
| JSConstantCacheTester T; |
| |
| FOR_FLOAT64_INPUTS(value) { |
| 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(ExternalReferences) { |
| // TODO(titzer): test canonicalization of external references. |
| } |
| |
| |
| static bool Contains(NodeVector* nodes, Node* n) { |
| for (size_t i = 0; i < nodes->size(); i++) { |
| if (nodes->at(i) == n) return true; |
| } |
| return false; |
| } |
| |
| |
| static void CheckGetCachedNodesContains(JSConstantCacheTester* T, Node* n) { |
| NodeVector nodes(T->main_zone()); |
| T->GetCachedNodes(&nodes); |
| CHECK(Contains(&nodes, n)); |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes1) { |
| JSConstantCacheTester T; |
| CheckGetCachedNodesContains(&T, T.TrueConstant()); |
| CheckGetCachedNodesContains(&T, T.UndefinedConstant()); |
| CheckGetCachedNodesContains(&T, T.TheHoleConstant()); |
| CheckGetCachedNodesContains(&T, T.TrueConstant()); |
| CheckGetCachedNodesContains(&T, T.FalseConstant()); |
| CheckGetCachedNodesContains(&T, T.NullConstant()); |
| CheckGetCachedNodesContains(&T, T.ZeroConstant()); |
| CheckGetCachedNodesContains(&T, T.OneConstant()); |
| CheckGetCachedNodesContains(&T, T.NaNConstant()); |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_int32) { |
| JSConstantCacheTester T; |
| |
| int32_t constants[] = {0, 1, 1, 1, 1, 2, 3, 4, 11, 12, 13, |
| 14, 55, -55, -44, -33, -22, -11, 16, 16, 17, 17, |
| 18, 18, 19, 19, 20, 20, 21, 21, 22, 23, 24, |
| 25, 15, 30, 31, 45, 46, 47, 48}; |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| size_t count_before = T.graph()->NodeCount(); |
| NodeVector nodes_before(T.main_zone()); |
| T.GetCachedNodes(&nodes_before); |
| Node* n = T.Int32Constant(constants[i]); |
| if (n->id() < count_before) { |
| // An old ID indicates a cached node. It should have been in the set. |
| CHECK(Contains(&nodes_before, n)); |
| } |
| // Old or new, it should be in the cached set afterwards. |
| CheckGetCachedNodesContains(&T, n); |
| } |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_float64) { |
| JSConstantCacheTester T; |
| |
| double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4, |
| -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3, |
| 11, 11, -33.3, -33.3, -22, -11}; |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| size_t count_before = T.graph()->NodeCount(); |
| NodeVector nodes_before(T.main_zone()); |
| T.GetCachedNodes(&nodes_before); |
| Node* n = T.Float64Constant(constants[i]); |
| if (n->id() < count_before) { |
| // An old ID indicates a cached node. It should have been in the set. |
| CHECK(Contains(&nodes_before, n)); |
| } |
| // Old or new, it should be in the cached set afterwards. |
| CheckGetCachedNodesContains(&T, n); |
| } |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_int64) { |
| JSConstantCacheTester T; |
| |
| int32_t constants[] = {0, 11, 12, 13, 14, 55, -55, -44, -33, |
| -22, -11, 16, 16, 17, 17, 18, 18, 19, |
| 19, 20, 20, 21, 21, 22, 23, 24, 25}; |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| size_t count_before = T.graph()->NodeCount(); |
| NodeVector nodes_before(T.main_zone()); |
| T.GetCachedNodes(&nodes_before); |
| Node* n = T.Int64Constant(constants[i]); |
| if (n->id() < count_before) { |
| // An old ID indicates a cached node. It should have been in the set. |
| CHECK(Contains(&nodes_before, n)); |
| } |
| // Old or new, it should be in the cached set afterwards. |
| CheckGetCachedNodesContains(&T, n); |
| } |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_number) { |
| JSConstantCacheTester T; |
| |
| double constants[] = {0, 11.1, 12.2, 13, 14, 55.5, -55.5, -44.4, |
| -33, -22, -11, 0, 11.1, 11.1, 12.3, 12.3, |
| 11, 11, -33.3, -33.3, -22, -11}; |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| size_t count_before = T.graph()->NodeCount(); |
| NodeVector nodes_before(T.main_zone()); |
| T.GetCachedNodes(&nodes_before); |
| Node* n = T.Constant(constants[i]); |
| if (n->id() < count_before) { |
| // An old ID indicates a cached node. It should have been in the set. |
| CHECK(Contains(&nodes_before, n)); |
| } |
| // Old or new, it should be in the cached set afterwards. |
| CheckGetCachedNodesContains(&T, n); |
| } |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_external) { |
| JSConstantCacheTester T; |
| |
| ExternalReference constants[] = {ExternalReference::address_of_min_int(), |
| ExternalReference::address_of_min_int(), |
| ExternalReference::address_of_min_int(), |
| ExternalReference::address_of_one_half(), |
| ExternalReference::address_of_one_half(), |
| ExternalReference::address_of_min_int(), |
| ExternalReference::address_of_the_hole_nan(), |
| ExternalReference::address_of_one_half()}; |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| size_t count_before = T.graph()->NodeCount(); |
| NodeVector nodes_before(T.main_zone()); |
| T.GetCachedNodes(&nodes_before); |
| Node* n = T.ExternalConstant(constants[i]); |
| if (n->id() < count_before) { |
| // An old ID indicates a cached node. It should have been in the set. |
| CHECK(Contains(&nodes_before, n)); |
| } |
| // Old or new, it should be in the cached set afterwards. |
| CheckGetCachedNodesContains(&T, n); |
| } |
| } |
| |
| |
| TEST(JSGraph_GetCachedNodes_together) { |
| JSConstantCacheTester T; |
| |
| Node* constants[] = { |
| T.TrueConstant(), |
| T.UndefinedConstant(), |
| T.TheHoleConstant(), |
| T.TrueConstant(), |
| T.FalseConstant(), |
| T.NullConstant(), |
| T.ZeroConstant(), |
| T.OneConstant(), |
| T.NaNConstant(), |
| T.Int32Constant(0), |
| T.Int32Constant(1), |
| T.Int64Constant(-2), |
| T.Int64Constant(-4), |
| T.Float64Constant(0.9), |
| T.Float64Constant(V8_INFINITY), |
| T.Constant(0.99), |
| T.Constant(1.11), |
| T.ExternalConstant(ExternalReference::address_of_one_half())}; |
| |
| NodeVector nodes(T.main_zone()); |
| T.GetCachedNodes(&nodes); |
| |
| for (size_t i = 0; i < arraysize(constants); i++) { |
| CHECK(Contains(&nodes, constants[i])); |
| } |
| } |
| |
| } // namespace compiler |
| } // namespace internal |
| } // namespace v8 |