v8/test/cctest/compiler/test-js-context-specialization.cc
neis fd8cebb1a6 [compiler] Generalize JSContextSpecialization.
With this CL, context loads and stores are "strengthened" by reducing
the incoming context chain and decreasing the depth accordingly,
whenever possible.  This enables more opportunities for specialization
and will let us easily add module context specialization later.

BUG=

Review-Url: https://codereview.chromium.org/2559173003
Cr-Commit-Position: refs/heads/master@{#42334}
2017-01-13 14:44:57 +00:00

676 lines
24 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/compiler/compiler-source-position-table.h"
#include "src/compiler/js-context-specialization.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/function-tester.h"
#include "test/cctest/compiler/graph-builder-tester.h"
namespace v8 {
namespace internal {
namespace compiler {
class ContextSpecializationTester : public HandleAndZoneScope {
public:
explicit ContextSpecializationTester(MaybeHandle<Context> context)
: 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()),
spec_(&reducer_, jsgraph(), context) {}
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);
private:
Graph* graph_;
CommonOperatorBuilder common_;
JSOperatorBuilder javascript_;
MachineOperatorBuilder machine_;
SimplifiedOperatorBuilder simplified_;
JSGraph jsgraph_;
GraphReducer reducer_;
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);
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);
// 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::NATIVE_CONTEXT_INDEX;
native->set(slot, *expected);
Node* const_context = t.jsgraph()->Constant(native);
Node* deep_const_context = t.jsgraph()->Constant(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, *match.Value());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
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());
}
}
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());
}
{
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
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);
// 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::NATIVE_CONTEXT_INDEX;
native->set(slot, *expected);
Node* const_context = t.jsgraph()->Constant(native);
Node* deep_const_context = t.jsgraph()->Constant(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, *match.Value());
ContextAccess access = OpParameter<ContextAccess>(r.replacement());
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());
}
}
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(
"(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