| Index: test/cctest/compiler/test-js-context-specialization.cc
|
| diff --git a/test/cctest/compiler/test-js-context-specialization.cc b/test/cctest/compiler/test-js-context-specialization.cc
|
| index d7812c6eb0842e62b427742b298cbde8c9a880c0..d8122c4fdbb9afc843049064462d696a2dd0e6e3 100644
|
| --- a/test/cctest/compiler/test-js-context-specialization.cc
|
| +++ b/test/cctest/compiler/test-js-context-specialization.cc
|
| @@ -18,7 +18,7 @@ namespace compiler {
|
|
|
| class ContextSpecializationTester : public HandleAndZoneScope {
|
| public:
|
| - ContextSpecializationTester()
|
| + explicit ContextSpecializationTester(MaybeHandle<Context> context)
|
| : graph_(new (main_zone()) Graph(main_zone())),
|
| common_(main_zone()),
|
| javascript_(main_zone()),
|
| @@ -27,7 +27,7 @@ class ContextSpecializationTester : public HandleAndZoneScope {
|
| jsgraph_(main_isolate(), graph(), common(), &javascript_, &simplified_,
|
| &machine_),
|
| reducer_(main_zone(), graph()),
|
| - spec_(&reducer_, jsgraph(), MaybeHandle<Context>()) {}
|
| + spec_(&reducer_, jsgraph(), context) {}
|
|
|
| JSContextSpecialization* spec() { return &spec_; }
|
| Factory* factory() { return main_isolate()->factory(); }
|
| @@ -37,6 +37,13 @@ class ContextSpecializationTester : public HandleAndZoneScope {
|
| JSGraph* jsgraph() { return &jsgraph_; }
|
| Graph* graph() { return graph_; }
|
|
|
| + void CheckChangesToValue(Node* node, Handle<HeapObject> expected_value);
|
| + void CheckContextInputAndDepthChanges(
|
| + Node* node, Handle<Context> expected_new_context_object,
|
| + size_t expected_new_depth);
|
| + void CheckContextInputAndDepthChanges(Node* node, Node* expected_new_context,
|
| + size_t expected_new_depth);
|
| +
|
| private:
|
| Graph* graph_;
|
| CommonOperatorBuilder common_;
|
| @@ -48,9 +55,52 @@ class ContextSpecializationTester : public HandleAndZoneScope {
|
| JSContextSpecialization spec_;
|
| };
|
|
|
| +void ContextSpecializationTester::CheckChangesToValue(
|
| + Node* node, Handle<HeapObject> expected_value) {
|
| + Reduction r = spec()->Reduce(node);
|
| + CHECK(r.Changed());
|
| + HeapObjectMatcher match(r.replacement());
|
| + CHECK(match.HasValue());
|
| + CHECK_EQ(*match.Value(), *expected_value);
|
| +}
|
| +
|
| +void ContextSpecializationTester::CheckContextInputAndDepthChanges(
|
| + Node* node, Handle<Context> expected_new_context_object,
|
| + size_t expected_new_depth) {
|
| + ContextAccess access = OpParameter<ContextAccess>(node);
|
| + Reduction r = spec()->Reduce(node);
|
| + CHECK(r.Changed());
|
| +
|
| + Node* new_context = NodeProperties::GetContextInput(r.replacement());
|
| + CHECK_EQ(IrOpcode::kHeapConstant, new_context->opcode());
|
| + HeapObjectMatcher match(new_context);
|
| + CHECK_EQ(*match.Value(), *expected_new_context_object);
|
| +
|
| + ContextAccess new_access = OpParameter<ContextAccess>(r.replacement());
|
| + CHECK_EQ(new_access.depth(), expected_new_depth);
|
| + CHECK_EQ(new_access.index(), access.index());
|
| + CHECK_EQ(new_access.immutable(), access.immutable());
|
| +}
|
| +
|
| +void ContextSpecializationTester::CheckContextInputAndDepthChanges(
|
| + Node* node, Node* expected_new_context, size_t expected_new_depth) {
|
| + ContextAccess access = OpParameter<ContextAccess>(node);
|
| + Reduction r = spec()->Reduce(node);
|
| + CHECK(r.Changed());
|
| +
|
| + Node* new_context = NodeProperties::GetContextInput(r.replacement());
|
| + CHECK_EQ(new_context, expected_new_context);
|
|
|
| -TEST(ReduceJSLoadContext) {
|
| - ContextSpecializationTester t;
|
| + ContextAccess new_access = OpParameter<ContextAccess>(r.replacement());
|
| + CHECK_EQ(new_access.depth(), expected_new_depth);
|
| + CHECK_EQ(new_access.index(), access.index());
|
| + CHECK_EQ(new_access.immutable(), access.immutable());
|
| +}
|
| +
|
| +static const int slot_index = Context::NATIVE_CONTEXT_INDEX;
|
| +
|
| +TEST(ReduceJSLoadContext0) {
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
|
|
| Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| t.graph()->SetStart(start);
|
| @@ -114,14 +164,239 @@ TEST(ReduceJSLoadContext) {
|
| CHECK(match.HasValue());
|
| CHECK_EQ(*expected, *match.Value());
|
| }
|
| +}
|
| +
|
| +TEST(ReduceJSLoadContext1) {
|
| + // The graph's context chain ends in the incoming context parameter:
|
| + //
|
| + // context2 <-- context1 <-- context0 (= Parameter(0))
|
| +
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, false), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, true), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
|
|
| - // TODO(titzer): test with other kinds of contexts, e.g. a function context.
|
| - // TODO(sigurds): test that loads below create context are not optimized
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, true), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, true), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context0, 1);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, true), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context0, 1);
|
| + }
|
| }
|
|
|
| +TEST(ReduceJSLoadContext2) {
|
| + // The graph's context chain ends in a constant context (context_object1),
|
| + // which has has another outer context (context_object0).
|
| + //
|
| + // context2 <-- context1 <-- context0 (= HeapConstant(context_object1))
|
| + // context_object1 <~~ context_object0
|
|
|
| -TEST(ReduceJSStoreContext) {
|
| - ContextSpecializationTester t;
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Handle<HeapObject> slot_value0 = t.factory()->InternalizeUtf8String("0");
|
| + Handle<HeapObject> slot_value1 = t.factory()->InternalizeUtf8String("1");
|
| +
|
| + Handle<Context> context_object0 = t.factory()->NewNativeContext();
|
| + Handle<Context> context_object1 = t.factory()->NewNativeContext();
|
| + context_object1->set_previous(*context_object0);
|
| + context_object0->set(slot_index, *slot_value0);
|
| + context_object1->set(slot_index, *slot_value1);
|
| +
|
| + Node* context0 = t.jsgraph()->Constant(context_object1);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, false), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, true), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, true), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, true), context2, start);
|
| + t.CheckChangesToValue(load, slot_value1);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context_object0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, true), context2, start);
|
| + t.CheckChangesToValue(load, slot_value0);
|
| + }
|
| +}
|
| +
|
| +TEST(ReduceJSLoadContext3) {
|
| + // Like in ReduceJSLoadContext1, the graph's context chain ends in the
|
| + // incoming context parameter. However, this time we provide a concrete
|
| + // context for this parameter as the "specialization context". We choose
|
| + // context_object2 from ReduceJSLoadContext2 for this, so almost all test
|
| + // expectations are the same as in ReduceJSLoadContext2.
|
| +
|
| + HandleAndZoneScope handle_zone_scope;
|
| + auto factory = handle_zone_scope.main_isolate()->factory();
|
| +
|
| + Handle<HeapObject> slot_value0 = factory->InternalizeUtf8String("0");
|
| + Handle<HeapObject> slot_value1 = factory->InternalizeUtf8String("1");
|
| +
|
| + Handle<Context> context_object0 = factory->NewNativeContext();
|
| + Handle<Context> context_object1 = factory->NewNativeContext();
|
| + context_object1->set_previous(*context_object0);
|
| + context_object0->set(slot_index, *slot_value0);
|
| + context_object1->set(slot_index, *slot_value1);
|
| +
|
| + ContextSpecializationTester t(context_object1);
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(2));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, false), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(0, slot_index, true), context2, start);
|
| + CHECK(!t.spec()->Reduce(load).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(1, slot_index, true), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context_object1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(2, slot_index, true), context2, start);
|
| + t.CheckChangesToValue(load, slot_value1);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, false), context2, start);
|
| + t.CheckContextInputAndDepthChanges(load, context_object0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* load = t.graph()->NewNode(
|
| + t.javascript()->LoadContext(3, slot_index, true), context2, start);
|
| + t.CheckChangesToValue(load, slot_value0);
|
| + }
|
| +}
|
| +
|
| +TEST(ReduceJSStoreContext0) {
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
|
|
| Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| t.graph()->SetStart(start);
|
| @@ -182,6 +457,158 @@ TEST(ReduceJSStoreContext) {
|
| }
|
| }
|
|
|
| +TEST(ReduceJSStoreContext1) {
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(0, slot_index),
|
| + context2, context2, start, start);
|
| + CHECK(!t.spec()->Reduce(store).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(1, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(2, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(3, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context0, 1);
|
| + }
|
| +}
|
| +
|
| +TEST(ReduceJSStoreContext2) {
|
| + ContextSpecializationTester t((MaybeHandle<Context>()));
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(0));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Handle<HeapObject> slot_value0 = t.factory()->InternalizeUtf8String("0");
|
| + Handle<HeapObject> slot_value1 = t.factory()->InternalizeUtf8String("1");
|
| +
|
| + Handle<Context> context_object0 = t.factory()->NewNativeContext();
|
| + Handle<Context> context_object1 = t.factory()->NewNativeContext();
|
| + context_object1->set_previous(*context_object0);
|
| + context_object0->set(slot_index, *slot_value0);
|
| + context_object1->set(slot_index, *slot_value1);
|
| +
|
| + Node* context0 = t.jsgraph()->Constant(context_object1);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(0, slot_index),
|
| + context2, context2, start, start);
|
| + CHECK(!t.spec()->Reduce(store).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(1, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(2, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context0, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(3, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context_object0, 0);
|
| + }
|
| +}
|
| +
|
| +TEST(ReduceJSStoreContext3) {
|
| + HandleAndZoneScope handle_zone_scope;
|
| + auto factory = handle_zone_scope.main_isolate()->factory();
|
| +
|
| + Handle<HeapObject> slot_value0 = factory->InternalizeUtf8String("0");
|
| + Handle<HeapObject> slot_value1 = factory->InternalizeUtf8String("1");
|
| +
|
| + Handle<Context> context_object0 = factory->NewNativeContext();
|
| + Handle<Context> context_object1 = factory->NewNativeContext();
|
| + context_object1->set_previous(*context_object0);
|
| + context_object0->set(slot_index, *slot_value0);
|
| + context_object1->set(slot_index, *slot_value1);
|
| +
|
| + ContextSpecializationTester t(context_object1);
|
| +
|
| + Node* start = t.graph()->NewNode(t.common()->Start(2));
|
| + t.graph()->SetStart(start);
|
| + Node* undefined = t.jsgraph()->Constant(t.factory()->undefined_value());
|
| + const i::compiler::Operator* create_function_context =
|
| + t.javascript()->CreateFunctionContext(42, FUNCTION_SCOPE);
|
| +
|
| + Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
| + Node* context1 = t.graph()->NewNode(create_function_context, undefined,
|
| + context0, start, start);
|
| + Node* context2 = t.graph()->NewNode(create_function_context, undefined,
|
| + context1, start, start);
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(0, slot_index),
|
| + context2, context2, start, start);
|
| + CHECK(!t.spec()->Reduce(store).Changed());
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(1, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(2, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context_object1, 0);
|
| + }
|
| +
|
| + {
|
| + Node* store =
|
| + t.graph()->NewNode(t.javascript()->StoreContext(3, slot_index),
|
| + context2, context2, start, start);
|
| + t.CheckContextInputAndDepthChanges(store, context_object0, 0);
|
| + }
|
| +}
|
|
|
| TEST(SpecializeJSFunction_ToConstant1) {
|
| FunctionTester T(
|
|
|