Index: test/cctest/compiler/test-simplified-lowering.cc |
diff --git a/test/cctest/compiler/test-simplified-lowering.cc b/test/cctest/compiler/test-simplified-lowering.cc |
index bcf1531ca86ba357faac2de18177b16516a798cb..2230e83bdd9254144ea5885084744c61e7fcf715 100644 |
--- a/test/cctest/compiler/test-simplified-lowering.cc |
+++ b/test/cctest/compiler/test-simplified-lowering.cc |
@@ -6,8 +6,10 @@ |
#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/simplified-node-factory.h" |
#include "src/compiler/typer.h" |
@@ -44,37 +46,9 @@ class SimplifiedGraphBuilderTester : public GraphBuilderTester<ReturnType> { |
JSGraph jsgraph; |
SimplifiedLowering lowering; |
- // Close graph and lower one node. |
- void Lower(Node* node) { |
+ void LowerAllNodes() { |
this->End(); |
- if (node == NULL) { |
- lowering.LowerAllNodes(); |
- } else { |
- lowering.Lower(node); |
- } |
- } |
- |
- // Close graph and lower all nodes. |
- void LowerAllNodes() { Lower(NULL); } |
- |
- void StoreFloat64(Node* node, double* ptr) { |
- Node* ptr_node = this->PointerConstant(ptr); |
- this->Store(kMachineFloat64, ptr_node, node); |
- } |
- |
- Node* LoadInt32(int32_t* ptr) { |
- Node* ptr_node = this->PointerConstant(ptr); |
- return this->Load(kMachineWord32, ptr_node); |
- } |
- |
- Node* LoadUint32(uint32_t* ptr) { |
- Node* ptr_node = this->PointerConstant(ptr); |
- return this->Load(kMachineWord32, ptr_node); |
- } |
- |
- Node* LoadFloat64(double* ptr) { |
- Node* ptr_node = this->PointerConstant(ptr); |
- return this->Load(kMachineFloat64, ptr_node); |
+ lowering.LowerAllNodes(); |
} |
Factory* factory() { return this->isolate()->factory(); } |
@@ -142,12 +116,13 @@ TEST(RunLoadMap) { |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- Handle<JSObject> src = TestObject(); |
- Handle<Map> src_map(src->map()); |
- Object* result = t.Call(*src); |
- CHECK_EQ(*src_map, result); |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ 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); |
+ } |
} |
@@ -155,18 +130,19 @@ TEST(RunStoreMap) { |
SimplifiedGraphBuilderTester<int32_t> t(kMachineTagged, kMachineTagged); |
FieldAccess access = ForJSObjectMap(); |
t.StoreField(access, t.Parameter(1), t.Parameter(0)); |
- t.Return(t.Int32Constant(0)); |
+ t.Return(t.jsgraph.TrueConstant()); |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- Handle<JSObject> src = TestObject(); |
- Handle<Map> src_map(src->map()); |
- Handle<JSObject> dst = TestObject(); |
- CHECK(src->map() != dst->map()); |
- t.Call(*src_map, *dst); |
- CHECK(*src_map == dst->map()); |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ 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()); |
+ } |
} |
@@ -178,12 +154,13 @@ TEST(RunLoadProperties) { |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- Handle<JSObject> src = TestObject(); |
- Handle<FixedArray> src_props(src->properties()); |
- Object* result = t.Call(*src); |
- CHECK_EQ(*src_props, result); |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ 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); |
+ } |
} |
@@ -196,16 +173,17 @@ TEST(RunLoadStoreMap) { |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- 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); |
- CHECK(result->IsMap()); |
- CHECK_EQ(*src_map, result); |
- CHECK(*src_map == dst->map()); |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ 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()); |
+ } |
} |
@@ -218,101 +196,53 @@ TEST(RunLoadStoreFixedArrayIndex) { |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- 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)); |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ 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) { |
- SimplifiedGraphBuilderTester<int32_t> t(kMachineTagged); |
+ SimplifiedGraphBuilderTester<Object*> t(kMachineTagged); |
const int index = 12; |
- FieldAccess access = ForArrayBufferBackingStore(); |
- Node* backing_store = t.LoadField(access, t.Parameter(0)); |
ElementAccess buffer_access = ForBackingStoreElement(kMachineWord8); |
+ Node* backing_store = |
+ t.LoadField(ForArrayBufferBackingStore(), t.Parameter(0)); |
Node* load = |
t.LoadElement(buffer_access, backing_store, t.Int32Constant(index)); |
t.StoreElement(buffer_access, backing_store, t.Int32Constant(index + 1), |
load); |
- t.Return(load); |
+ t.Return(t.jsgraph.TrueConstant()); |
t.LowerAllNodes(); |
- if (!Pipeline::SupportedTarget()) return; |
- |
- Handle<JSArrayBuffer> array = t.factory()->NewJSArrayBuffer(); |
- const int array_length = 2 * index; |
- 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; |
- } |
- int32_t result = t.Call(*array); |
- CHECK_EQ(index, result); |
- for (int i = 0; i < array_length; i++) { |
- uint8_t expected = i; |
- if (i == (index + 1)) expected = result; |
- CHECK_EQ(data[i], expected); |
- } |
-} |
- |
- |
-TEST(RunCopyFixedArray) { |
- SimplifiedGraphBuilderTester<int32_t> t(kMachineTagged, kMachineTagged); |
- |
- const int kArraySize = 15; |
- Node* one = t.Int32Constant(1); |
- Node* index = t.Int32Constant(0); |
- Node* limit = t.Int32Constant(kArraySize); |
- t.environment()->Push(index); |
- { |
- LoopBuilder loop(&t); |
- loop.BeginLoop(); |
- // Loop exit condition. |
- index = t.environment()->Top(); |
- Node* condition = t.Int32LessThan(index, limit); |
- loop.BreakUnless(condition); |
- // src[index] = dst[index]. |
- index = t.environment()->Pop(); |
- ElementAccess access = ForFixedArrayElement(); |
- Node* src = t.Parameter(0); |
- Node* load = t.LoadElement(access, src, index); |
- Node* dst = t.Parameter(1); |
- t.StoreElement(access, dst, index, load); |
- // index++ |
- index = t.Int32Add(index, one); |
- t.environment()->Push(index); |
- // continue. |
- loop.EndBody(); |
- loop.EndLoop(); |
- } |
- index = t.environment()->Pop(); |
- t.Return(index); |
- |
- t.LowerAllNodes(); |
- |
- if (!Pipeline::SupportedTarget()) return; |
+ if (Pipeline::SupportedTarget()) { |
+ t.GenerateCode(); |
+ Handle<JSArrayBuffer> array = t.factory()->NewJSArrayBuffer(); |
+ const int array_length = 2 * index; |
+ 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; |
+ } |
- Handle<FixedArray> src = t.factory()->NewFixedArray(kArraySize); |
- Handle<FixedArray> src_copy = t.factory()->NewFixedArray(kArraySize); |
- Handle<FixedArray> dst = t.factory()->NewFixedArray(kArraySize); |
- for (int i = 0; i < kArraySize; i++) { |
- src->set(i, *TestObject()); |
- src_copy->set(i, src->get(i)); |
- dst->set(i, *TestObject()); |
- CHECK_NE(src_copy->get(i), dst->get(i)); |
- } |
- CHECK_EQ(kArraySize, t.Call(*src, *dst)); |
- for (int i = 0; i < kArraySize; i++) { |
- CHECK_EQ(src_copy->get(i), dst->get(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); |
+ } |
} |
} |
@@ -425,3 +355,766 @@ TEST(RunStoreElementFromUntaggedBase) { |
} |
} |
} |
+ |
+ |
+// 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; |
+ MachineRepresentation rep; |
+ E* original_elements; |
+ int num_elements; |
+ E* untagged_array; |
+ Handle<ByteArray> tagged_array; // TODO(titzer): use FixedArray for tagged. |
+ |
+ AccessTester(bool t, MachineRepresentation r, E* orig, int num) |
+ : tagged(t), |
+ rep(r), |
+ original_elements(orig), |
+ num_elements(num), |
+ untagged_array(static_cast<E*>(malloc(ByteSize()))), |
+ tagged_array(main_isolate()->factory()->NewByteArray(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(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(); |
+ |
+ SimplifiedGraphBuilderTester<Object*> t; |
+ Node* ptr = GetBaseNode(&t); |
+ Node* load = t.LoadElement(access, ptr, t.Int32Constant(from_index)); |
+ t.StoreElement(access, ptr, t.Int32Constant(to_index), 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); |
+ |
+ SimplifiedGraphBuilderTester<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) { |
+ SimplifiedGraphBuilderTester<Object*> t; |
+ |
+ Node* one = t.Int32Constant(1); |
+ Node* index = t.Int32Constant(0); |
+ Node* limit = t.Int32Constant(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); |
+ } |
+ } |
+ |
+ 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(SimplifiedGraphBuilderTester<T>* t) { |
+ return tagged ? t->HeapConstant(tagged_array) |
+ : t->PointerConstant(untagged_array); |
+ } |
+ |
+ void BoundsCheck(int index) { |
+ CHECK_GE(index, 0); |
+ CHECK_LT(index, num_elements); |
+ CHECK_EQ(ByteSize(), tagged_array->length()); |
+ } |
+}; |
+ |
+ |
+template <typename E> |
+static void RunAccessTest(MachineRepresentation 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_elements); |
+ 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_elements); |
+ AccessTester<E> b(tt == 1, rep, original_elements, num_elements); |
+ 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>(kMachineWord8, data, ARRAY_SIZE(data)); |
+} |
+ |
+ |
+TEST(RunAccessTests_uint16) { |
+ uint16_t data[] = {0x071a, 0x162b, 0x253c, 0x344d, 0x435e, 0x7777}; |
+ RunAccessTest<uint16_t>(kMachineWord16, data, ARRAY_SIZE(data)); |
+} |
+ |
+ |
+TEST(RunAccessTests_int32) { |
+ int32_t data[] = {0xf10733aa, 0xf21644bb, 0xf32555cc, |
+ 0xf43466dd, 0xf54377ee, 0x34455667}; |
+ RunAccessTest<int32_t>(kMachineWord32, data, ARRAY_SIZE(data)); |
+} |
+ |
+ |
+TEST(RunAccessTests_int64) { |
+ if (kPointerSize != 8) return; |
+ int64_t data[] = {0x1011121314151617L, 0x2021222324252627L, |
+ 0x3031323334353637L, 0xa0a1a2a3a4a5a6a7L, |
+ 0xf0f1f2f3f4f5f6f7L}; |
+ RunAccessTest<int64_t>(kMachineWord64, data, ARRAY_SIZE(data)); |
+} |
+ |
+ |
+TEST(RunAccessTests_float64) { |
+ double data[] = {1.25, -1.25, 2.75, 11.0, 11100.8}; |
+ RunAccessTest<double>(kMachineFloat64, data, ARRAY_SIZE(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*>(kMachineTagged, data, ARRAY_SIZE(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; |
+ JSGraph jsgraph; |
+ Node* p0; |
+ Node* p1; |
+ Node* start; |
+ Node* end; |
+ Node* ret; |
+ |
+ TestingGraph(Type* p0_type, Type* p1_type = Type::None()) |
+ : GraphAndBuilders(main_zone()), |
+ typer(main_zone()), |
+ jsgraph(graph(), common(), &typer) { |
+ 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); |
+ NodeProperties::SetBounds(p0, Bounds(p0_type)); |
+ NodeProperties::SetBounds(p1, Bounds(p1_type)); |
+ } |
+ |
+ void CheckLoweringBinop(IrOpcode::Value expected, Operator* op) { |
+ Node* node = Return(graph()->NewNode(op, p0, p1)); |
+ Lower(); |
+ CHECK_EQ(expected, node->opcode()); |
+ } |
+ |
+ void CheckLoweringTruncatedBinop(IrOpcode::Value expected, Operator* op, |
+ 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, NULL); |
+ lowering.LowerAllNodes(); |
+ } |
+ |
+ // Inserts the node as the return value of the graph. |
+ Node* Return(Node* node) { |
+ ret->ReplaceInput(0, node); |
+ return node; |
+ } |
+ |
+ Node* ExampleWithOutput(RepType type) { |
+ // TODO(titzer): use parameters with guaranteed representations. |
+ if (type & tInt32) { |
+ return graph()->NewNode(machine()->Int32Add(), jsgraph.Int32Constant(1), |
+ jsgraph.Int32Constant(1)); |
+ } else if (type & tUint32) { |
+ return graph()->NewNode(machine()->Word32Sar(), jsgraph.Int32Constant(1), |
+ jsgraph.Int32Constant(1)); |
+ } else if (type & rFloat64) { |
+ return graph()->NewNode(machine()->Float64Add(), |
+ jsgraph.Float64Constant(1), |
+ jsgraph.Float64Constant(1)); |
+ } else if (type & rBit) { |
+ return graph()->NewNode(machine()->Word32Equal(), |
+ jsgraph.Int32Constant(1), |
+ jsgraph.Int32Constant(1)); |
+ } else { |
+ CHECK_EQ(rTagged, type); |
+ return p0; |
+ } |
+ } |
+ |
+ Node* Use(Node* node, RepType type) { |
+ if (type & tInt32) { |
+ return graph()->NewNode(machine()->Int32LessThan(), node, |
+ jsgraph.Int32Constant(1)); |
+ } else if (type & tUint32) { |
+ return graph()->NewNode(machine()->Uint32LessThan(), node, |
+ jsgraph.Int32Constant(1)); |
+ } else if (type & rFloat64) { |
+ return graph()->NewNode(machine()->Float64Add(), node, |
+ jsgraph.Float64Constant(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); |
+ ret->ReplaceInput(NodeProperties::FirstControlIndex(ret), m); |
+ return br; |
+ } |
+ |
+ SimplifiedOperatorBuilder* simplified() { return &main_simplified_; } |
+ MachineOperatorBuilder* machine() { return &main_machine_; } |
+ CommonOperatorBuilder* common() { return &main_common_; } |
+ Graph* graph() { return main_graph_; } |
+}; |
+ |
+ |
+#define TODO_TITZER |
+ |
+TEST(LowerBooleanNot_bit_bit) { |
+ // BooleanNot(x: rBit) used as rBit |
+ TestingGraph t(Type::Boolean()); |
+ Node* b = t.ExampleWithOutput(rBit); |
+ 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: rBit) used as rTagged |
+ TestingGraph t(Type::Boolean()); |
+ Node* b = t.ExampleWithOutput(rBit); |
+ Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); |
+ Node* use = t.Use(inv, rTagged); |
+ 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: rTagged) used as rBit |
+ 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: rTagged) used as rTagged |
+ TestingGraph t(Type::Boolean()); |
+ Node* b = t.p0; |
+ Node* inv = t.graph()->NewNode(t.simplified()->BooleanNot(), b); |
+ Node* use = t.Use(inv, rTagged); |
+ 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)); |
+} |
+ |
+ |
+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 < ARRAY_SIZE(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 < ARRAY_SIZE(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 < ARRAY_SIZE(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()); |
+ } |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_nop) { |
+ // NumberToInt32(x: rTagged | tInt32) used as rTagged |
+ TestingGraph t(Type::Signed32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); |
+ Node* use = t.Use(trunc, rTagged); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(t.p0, use->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_ChangeTaggedToFloat64) { |
+ // NumberToInt32(x: rTagged | tInt32) used as rFloat64 |
+ TestingGraph t(Type::Signed32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); |
+ Node* use = t.Use(trunc, rFloat64); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, use->InputAt(0)->opcode()); |
+ CHECK_EQ(t.p0, use->InputAt(0)->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_ChangeTaggedToInt32) { |
+ // NumberToInt32(x: rTagged | tInt32) used as rWord32 |
+ TestingGraph t(Type::Signed32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToInt32(), t.p0); |
+ Node* use = t.Use(trunc, tInt32); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(IrOpcode::kChangeTaggedToInt32, use->InputAt(0)->opcode()); |
+ CHECK_EQ(t.p0, use->InputAt(0)->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_ChangeFloat64ToTagged) { |
+ // NumberToInt32(x: rFloat64 | tInt32) used as rTagged |
+ TODO_TITZER; |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_ChangeFloat64ToInt32) { |
+ // NumberToInt32(x: rFloat64 | tInt32) used as rWord32 | tInt32 |
+ TODO_TITZER; |
+} |
+ |
+ |
+TEST(LowerNumberToInt32_to_TruncateFloat64ToInt32) { |
+ // NumberToInt32(x: rFloat64) used as rWord32 | tUint32 |
+ TODO_TITZER; |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_nop) { |
+ // NumberToUint32(x: rTagged | tUint32) used as rTagged |
+ TestingGraph t(Type::Unsigned32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); |
+ Node* use = t.Use(trunc, rTagged); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(t.p0, use->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_ChangeTaggedToFloat64) { |
+ // NumberToUint32(x: rTagged | tUint32) used as rWord32 |
+ TestingGraph t(Type::Unsigned32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); |
+ Node* use = t.Use(trunc, rFloat64); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(IrOpcode::kChangeTaggedToFloat64, use->InputAt(0)->opcode()); |
+ CHECK_EQ(t.p0, use->InputAt(0)->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_ChangeTaggedToUint32) { |
+ // NumberToUint32(x: rTagged | tUint32) used as rWord32 |
+ TestingGraph t(Type::Unsigned32()); |
+ Node* trunc = t.graph()->NewNode(t.simplified()->NumberToUint32(), t.p0); |
+ Node* use = t.Use(trunc, tUint32); |
+ t.Return(use); |
+ t.Lower(); |
+ CHECK_EQ(IrOpcode::kChangeTaggedToUint32, use->InputAt(0)->opcode()); |
+ CHECK_EQ(t.p0, use->InputAt(0)->InputAt(0)); |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_ChangeFloat64ToTagged) { |
+ // NumberToUint32(x: rFloat64 | tUint32) used as rTagged |
+ TODO_TITZER; |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_ChangeFloat64ToUint32) { |
+ // NumberToUint32(x: rFloat64 | tUint32) used as rWord32 |
+ TODO_TITZER; |
+} |
+ |
+ |
+TEST(LowerNumberToUint32_to_TruncateFloat64ToUint32) { |
+ // NumberToUint32(x: rFloat64) used as rWord32 |
+ TODO_TITZER; |
+} |
+ |
+ |
+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_rtcalls) { |
+ if (false) { // TODO(titzer): lower StringOps to runtime calls |
+ TestingGraph t(Type::String(), Type::String()); |
+ t.CheckLoweringBinop(IrOpcode::kCall, t.simplified()->StringEqual()); |
+ t.CheckLoweringBinop(IrOpcode::kCall, t.simplified()->StringLessThan()); |
+ t.CheckLoweringBinop(IrOpcode::kCall, |
+ t.simplified()->StringLessThanOrEqual()); |
+ t.CheckLoweringBinop(IrOpcode::kCall, t.simplified()->StringAdd()); |
+ } |
+} |
+ |
+ |
+TEST(LowerLoadField_to_load) { TODO_TITZER; } |
+ |
+ |
+TEST(LowerStoreField_to_store) { TODO_TITZER; } |
+ |
+ |
+TEST(LowerStoreField_to_store_wb) { TODO_TITZER; } |
+ |
+ |
+TEST(LowerLoadElement_to_load) { TODO_TITZER; } |
+ |
+ |
+TEST(LowerStoreElement_to_store) { TODO_TITZER; } |
+ |
+ |
+TEST(LowerStoreElement_to_store_wb) { TODO_TITZER; } |
+ |
+ |
+void CheckChangeInsertion(IrOpcode::Value expected, RepType from, RepType 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, rFloat64, tInt32); |
+ CheckChangeInsertion(IrOpcode::kChangeFloat64ToUint32, rFloat64, tUint32); |
+ CheckChangeInsertion(IrOpcode::kChangeFloat64ToTagged, rFloat64, rTagged); |
+ |
+ CheckChangeInsertion(IrOpcode::kChangeTaggedToInt32, rTagged, tInt32); |
+ CheckChangeInsertion(IrOpcode::kChangeTaggedToUint32, rTagged, tUint32); |
+ CheckChangeInsertion(IrOpcode::kChangeTaggedToFloat64, rTagged, rFloat64); |
+ |
+ CheckChangeInsertion(IrOpcode::kChangeInt32ToFloat64, tInt32, rFloat64); |
+ CheckChangeInsertion(IrOpcode::kChangeInt32ToTagged, tInt32, rTagged); |
+ |
+ CheckChangeInsertion(IrOpcode::kChangeUint32ToFloat64, tUint32, rFloat64); |
+ CheckChangeInsertion(IrOpcode::kChangeUint32ToTagged, tUint32, rTagged); |
+} |
+ |
+ |
+static void CheckChangesAroundBinop(TestingGraph* t, 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()); |
+ |
+ 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 < ARRAY_SIZE(ops); i++) { |
+ CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32, |
+ IrOpcode::kChangeInt32ToTagged); |
+ } |
+} |
+ |
+ |
+TEST(InsertChangesAroundInt32Cmp) { |
+ TestingGraph t(Type::Signed32(), Type::Signed32()); |
+ |
+ Operator* ops[] = {t.machine()->Int32LessThan(), |
+ t.machine()->Int32LessThanOrEqual()}; |
+ |
+ for (size_t i = 0; i < ARRAY_SIZE(ops); i++) { |
+ CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToInt32, |
+ IrOpcode::kChangeBitToBool); |
+ } |
+} |
+ |
+ |
+TEST(InsertChangesAroundUint32Cmp) { |
+ TestingGraph t(Type::Unsigned32(), Type::Unsigned32()); |
+ |
+ Operator* ops[] = {t.machine()->Uint32LessThan(), |
+ t.machine()->Uint32LessThanOrEqual()}; |
+ |
+ for (size_t i = 0; i < ARRAY_SIZE(ops); i++) { |
+ CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToUint32, |
+ IrOpcode::kChangeBitToBool); |
+ } |
+} |
+ |
+ |
+TEST(InsertChangesAroundFloat64Binops) { |
+ TestingGraph t(Type::Number(), Type::Number()); |
+ |
+ Operator* ops[] = { |
+ t.machine()->Float64Add(), t.machine()->Float64Sub(), |
+ t.machine()->Float64Mul(), t.machine()->Float64Div(), |
+ t.machine()->Float64Mod(), |
+ }; |
+ |
+ for (size_t i = 0; i < ARRAY_SIZE(ops); i++) { |
+ CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64, |
+ IrOpcode::kChangeFloat64ToTagged); |
+ } |
+} |
+ |
+ |
+TEST(InsertChangesAroundFloat64Cmp) { |
+ TestingGraph t(Type::Number(), Type::Number()); |
+ |
+ Operator* ops[] = {t.machine()->Float64Equal(), |
+ t.machine()->Float64LessThan(), |
+ t.machine()->Float64LessThanOrEqual()}; |
+ |
+ for (size_t i = 0; i < ARRAY_SIZE(ops); i++) { |
+ CheckChangesAroundBinop(&t, ops[i], IrOpcode::kChangeTaggedToFloat64, |
+ IrOpcode::kChangeBitToBool); |
+ } |
+} |
+ |
+ |
+TEST(InsertChangesForElementAccessIndex) { TODO_TITZER; } |