f30b53bdae
... to OptimizedCompilationInfo, BytecodeGraphBuilder, and JSHeapBroker. Also add first uses of these flags in pipeline.cc by skipping certain phases when nci is enabled. With this change, tests in the NCI variant will start to fail since generic lowering is not fully implemented. These implementations will follow incrementally in the next days. Bug: v8:8888 Change-Id: I3f570fb92f09059d1f1f4015f88ffe80ccf746ad Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2239572 Commit-Queue: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#68339}
705 lines
25 KiB
C++
705 lines
25 KiB
C++
// 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/tick-counter.h"
|
|
#include "src/compiler/compiler-source-position-table.h"
|
|
#include "src/compiler/js-context-specialization.h"
|
|
#include "src/compiler/js-graph.h"
|
|
#include "src/compiler/js-heap-broker.h"
|
|
#include "src/compiler/js-operator.h"
|
|
#include "src/compiler/node-matchers.h"
|
|
#include "src/compiler/node-properties.h"
|
|
#include "src/compiler/simplified-operator.h"
|
|
#include "src/heap/factory.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#include "src/objects/property.h"
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/compiler/function-tester.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
class ContextSpecializationTester : public HandleAndZoneScope {
|
|
public:
|
|
explicit ContextSpecializationTester(Maybe<OuterContext> context)
|
|
: canonical_(main_isolate()),
|
|
graph_(new (main_zone()) Graph(main_zone())),
|
|
common_(main_zone()),
|
|
javascript_(main_zone()),
|
|
machine_(main_zone()),
|
|
simplified_(main_zone()),
|
|
jsgraph_(main_isolate(), graph(), common(), &javascript_, &simplified_,
|
|
&machine_),
|
|
reducer_(main_zone(), graph(), &tick_counter_),
|
|
js_heap_broker_(main_isolate(), main_zone()),
|
|
spec_(&reducer_, jsgraph(), &js_heap_broker_, context,
|
|
MaybeHandle<JSFunction>()) {}
|
|
|
|
JSContextSpecialization* spec() { return &spec_; }
|
|
Factory* factory() { return main_isolate()->factory(); }
|
|
CommonOperatorBuilder* common() { return &common_; }
|
|
JSOperatorBuilder* javascript() { return &javascript_; }
|
|
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
|
|
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);
|
|
|
|
JSHeapBroker* broker() { return &js_heap_broker_; }
|
|
|
|
private:
|
|
TickCounter tick_counter_;
|
|
CanonicalHandleScope canonical_;
|
|
Graph* graph_;
|
|
CommonOperatorBuilder common_;
|
|
JSOperatorBuilder javascript_;
|
|
MachineOperatorBuilder machine_;
|
|
SimplifiedOperatorBuilder simplified_;
|
|
JSGraph jsgraph_;
|
|
GraphReducer reducer_;
|
|
JSHeapBroker js_heap_broker_;
|
|
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 = ContextAccessOf(node->op());
|
|
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(Context::cast(*match.Value()), *expected_new_context_object);
|
|
|
|
ContextAccess new_access = ContextAccessOf(r.replacement()->op());
|
|
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 = ContextAccessOf(node->op());
|
|
Reduction r = spec()->Reduce(node);
|
|
CHECK(r.Changed());
|
|
|
|
Node* new_context = NodeProperties::GetContextInput(r.replacement());
|
|
CHECK_EQ(new_context, expected_new_context);
|
|
|
|
ContextAccess new_access = ContextAccessOf(r.replacement()->op());
|
|
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::PREVIOUS_INDEX;
|
|
|
|
TEST(ReduceJSLoadContext0) {
|
|
ContextSpecializationTester t(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->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::PREVIOUS_INDEX;
|
|
native->set(slot, *expected);
|
|
|
|
Node* const_context = t.jsgraph()->Constant(ObjectRef(t.broker(), native));
|
|
Node* deep_const_context =
|
|
t.jsgraph()->Constant(ObjectRef(t.broker(), subcontext2));
|
|
Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
|
|
{
|
|
// Mutable slot, constant context, depth = 0 => do nothing.
|
|
Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false),
|
|
const_context, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(!r.Changed());
|
|
}
|
|
|
|
{
|
|
// Mutable slot, non-constant context, depth = 0 => do nothing.
|
|
Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false),
|
|
param_context, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(!r.Changed());
|
|
}
|
|
|
|
{
|
|
// Mutable slot, constant context, depth > 0 => fold-in parent context.
|
|
Node* load = t.graph()->NewNode(
|
|
t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false),
|
|
deep_const_context, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(r.Changed());
|
|
Node* new_context_input = NodeProperties::GetContextInput(r.replacement());
|
|
CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
|
|
HeapObjectMatcher match(new_context_input);
|
|
CHECK_EQ(*native, Context::cast(*match.Value()));
|
|
ContextAccess access = ContextAccessOf(r.replacement()->op());
|
|
CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
|
|
CHECK_EQ(0, static_cast<int>(access.depth()));
|
|
CHECK_EQ(false, access.immutable());
|
|
}
|
|
|
|
{
|
|
// Immutable slot, constant context, depth = 0 => specialize.
|
|
Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true),
|
|
const_context, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(r.Changed());
|
|
CHECK(r.replacement() != load);
|
|
|
|
HeapObjectMatcher match(r.replacement());
|
|
CHECK(match.HasValue());
|
|
CHECK_EQ(*expected, *match.Value());
|
|
}
|
|
|
|
// Clean up so that verifiers don't complain.
|
|
native->set(slot, Smi::zero());
|
|
}
|
|
|
|
TEST(ReduceJSLoadContext1) {
|
|
// The graph's context chain ends in the incoming context parameter:
|
|
//
|
|
// context2 <-- context1 <-- context0 (= Parameter(0))
|
|
|
|
ContextSpecializationTester t(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(0));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
|
|
|
|
Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, 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.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 another outer context (context_object0).
|
|
//
|
|
// context2 <-- context1 <-- context0 (= HeapConstant(context_object1))
|
|
// context_object1 <~~ context_object0
|
|
|
|
ContextSpecializationTester t(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(0));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 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(Context::EXTENSION_INDEX, *slot_value0);
|
|
context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
|
|
|
|
Node* context0 =
|
|
t.jsgraph()->Constant(ObjectRef(t.broker(), context_object1));
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, 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, Context::EXTENSION_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, Context::EXTENSION_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(Context::EXTENSION_INDEX, *slot_value0);
|
|
context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
|
|
|
|
ContextSpecializationTester t(Just(OuterContext(context_object1, 0)));
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(2));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()),
|
|
handle_zone_scope.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
|
|
|
|
Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, 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, Context::EXTENSION_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, Context::EXTENSION_INDEX, true),
|
|
context2, start);
|
|
t.CheckChangesToValue(load, slot_value0);
|
|
}
|
|
}
|
|
|
|
TEST(ReduceJSStoreContext0) {
|
|
ContextSpecializationTester t(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->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::PREVIOUS_INDEX;
|
|
native->set(slot, *expected);
|
|
|
|
Node* const_context = t.jsgraph()->Constant(ObjectRef(t.broker(), native));
|
|
Node* deep_const_context =
|
|
t.jsgraph()->Constant(ObjectRef(t.broker(), subcontext2));
|
|
Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
|
|
{
|
|
// Mutable slot, constant context, depth = 0 => do nothing.
|
|
Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0),
|
|
const_context, const_context, start, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(!r.Changed());
|
|
}
|
|
|
|
{
|
|
// Mutable slot, non-constant context, depth = 0 => do nothing.
|
|
Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0),
|
|
param_context, param_context, start, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(!r.Changed());
|
|
}
|
|
|
|
{
|
|
// Immutable slot, constant context, depth = 0 => do nothing.
|
|
Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, slot),
|
|
const_context, const_context, start, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(!r.Changed());
|
|
}
|
|
|
|
{
|
|
// Mutable slot, constant context, depth > 0 => fold-in parent context.
|
|
Node* load = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX),
|
|
deep_const_context, deep_const_context, start, start);
|
|
Reduction r = t.spec()->Reduce(load);
|
|
CHECK(r.Changed());
|
|
Node* new_context_input = NodeProperties::GetContextInput(r.replacement());
|
|
CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
|
|
HeapObjectMatcher match(new_context_input);
|
|
CHECK_EQ(*native, Context::cast(*match.Value()));
|
|
ContextAccess access = ContextAccessOf(r.replacement()->op());
|
|
CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
|
|
CHECK_EQ(0, static_cast<int>(access.depth()));
|
|
CHECK_EQ(false, access.immutable());
|
|
}
|
|
|
|
// Clean up so that verifiers don't complain.
|
|
native->set(slot, Smi::zero());
|
|
}
|
|
|
|
TEST(ReduceJSStoreContext1) {
|
|
ContextSpecializationTester t(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(0));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
|
|
|
|
Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, 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(Nothing<OuterContext>());
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(0));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 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(Context::EXTENSION_INDEX, *slot_value0);
|
|
context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
|
|
|
|
Node* context0 =
|
|
t.jsgraph()->Constant(ObjectRef(t.broker(), context_object1));
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, context1, start, start);
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(0, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
CHECK(!t.spec()->Reduce(store).Changed());
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(1, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
t.CheckContextInputAndDepthChanges(store, context1, 0);
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(2, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
t.CheckContextInputAndDepthChanges(store, context0, 0);
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(3, Context::EXTENSION_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(Context::EXTENSION_INDEX, *slot_value0);
|
|
context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
|
|
|
|
ContextSpecializationTester t(Just(OuterContext(context_object1, 0)));
|
|
|
|
Node* start = t.graph()->NewNode(t.common()->Start(2));
|
|
t.graph()->SetStart(start);
|
|
Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()),
|
|
handle_zone_scope.main_isolate());
|
|
const i::compiler::Operator* create_function_context =
|
|
t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
|
|
|
|
Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
|
|
Node* context1 =
|
|
t.graph()->NewNode(create_function_context, context0, start, start);
|
|
Node* context2 =
|
|
t.graph()->NewNode(create_function_context, context1, start, start);
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(0, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
CHECK(!t.spec()->Reduce(store).Changed());
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(1, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
t.CheckContextInputAndDepthChanges(store, context1, 0);
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(2, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
t.CheckContextInputAndDepthChanges(store, context_object1, 0);
|
|
}
|
|
|
|
{
|
|
Node* store = t.graph()->NewNode(
|
|
t.javascript()->StoreContext(3, Context::EXTENSION_INDEX), context2,
|
|
context2, start, start);
|
|
t.CheckContextInputAndDepthChanges(store, context_object0, 0);
|
|
}
|
|
}
|
|
|
|
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!
|
|
i::Isolate* isolate = CcTest::i_isolate();
|
|
CHECK(
|
|
T.Call(T.Val(0.0), T.Val(0.0)).ToHandleChecked()->IsUndefined(isolate));
|
|
CHECK(
|
|
T.Call(T.Val(2.0), T.Val(0.0)).ToHandleChecked()->IsUndefined(isolate));
|
|
CHECK(T.Call(T.Val(-2.1), T.Val(0.0))
|
|
.ToHandleChecked()
|
|
->IsUndefined(isolate));
|
|
}
|
|
|
|
{
|
|
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());
|
|
}
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|