test-js-context-specialization.cc 24.9 KB
Newer Older
1 2 3 4
// 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.

5
#include "src/codegen/tick-counter.h"
6
#include "src/compiler/compiler-source-position-table.h"
7
#include "src/compiler/js-context-specialization.h"
8
#include "src/compiler/js-graph.h"
9
#include "src/compiler/js-heap-broker.h"
10 11
#include "src/compiler/js-operator.h"
#include "src/compiler/node-matchers.h"
12
#include "src/compiler/node-properties.h"
13
#include "src/compiler/simplified-operator.h"
14
#include "src/heap/factory.h"
15
#include "src/objects/objects-inl.h"
16
#include "src/objects/property.h"
17 18 19
#include "test/cctest/cctest.h"
#include "test/cctest/compiler/function-tester.h"

20 21 22
namespace v8 {
namespace internal {
namespace compiler {
23

24
class ContextSpecializationTester : public HandleAndZoneScope {
25
 public:
26
  explicit ContextSpecializationTester(Maybe<OuterContext> context)
27 28
      : HandleAndZoneScope(kCompressGraphZone),
        canonical_(main_isolate()),
29
        graph_(main_zone()->New<Graph>(main_zone())),
30 31
        common_(main_zone()),
        javascript_(main_zone()),
32
        machine_(main_zone()),
33
        simplified_(main_zone()),
34 35
        jsgraph_(main_isolate(), graph(), common(), &javascript_, &simplified_,
                 &machine_),
36
        reducer_(main_zone(), graph(), &tick_counter_, &js_heap_broker_),
37
        js_heap_broker_(main_isolate(), main_zone()),
38 39
        spec_(&reducer_, jsgraph(), &js_heap_broker_, context,
              MaybeHandle<JSFunction>()) {}
40

41
  JSContextSpecialization* spec() { return &spec_; }
42 43 44 45 46
  Factory* factory() { return main_isolate()->factory(); }
  CommonOperatorBuilder* common() { return &common_; }
  JSOperatorBuilder* javascript() { return &javascript_; }
  SimplifiedOperatorBuilder* simplified() { return &simplified_; }
  JSGraph* jsgraph() { return &jsgraph_; }
47
  Graph* graph() { return graph_; }
48

49 50 51 52 53 54 55
  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);

56 57
  JSHeapBroker* broker() { return &js_heap_broker_; }

58
 private:
59
  TickCounter tick_counter_;
60
  CanonicalHandleScope canonical_;
61
  Graph* graph_;
62 63
  CommonOperatorBuilder common_;
  JSOperatorBuilder javascript_;
sigurds@chromium.org's avatar
sigurds@chromium.org committed
64
  MachineOperatorBuilder machine_;
65 66
  SimplifiedOperatorBuilder simplified_;
  JSGraph jsgraph_;
67
  GraphReducer reducer_;
68
  JSHeapBroker js_heap_broker_;
69
  JSContextSpecialization spec_;
70 71
};

72 73 74 75 76
void ContextSpecializationTester::CheckChangesToValue(
    Node* node, Handle<HeapObject> expected_value) {
  Reduction r = spec()->Reduce(node);
  CHECK(r.Changed());
  HeapObjectMatcher match(r.replacement());
77 78
  CHECK(match.HasResolvedValue());
  CHECK_EQ(*match.ResolvedValue(), *expected_value);
79 80 81 82 83
}

void ContextSpecializationTester::CheckContextInputAndDepthChanges(
    Node* node, Handle<Context> expected_new_context_object,
    size_t expected_new_depth) {
84
  ContextAccess access = ContextAccessOf(node->op());
85 86 87 88 89 90
  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);
91
  CHECK_EQ(Context::cast(*match.ResolvedValue()), *expected_new_context_object);
92

93
  ContextAccess new_access = ContextAccessOf(r.replacement()->op());
94 95 96 97 98 99 100
  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) {
101
  ContextAccess access = ContextAccessOf(node->op());
102 103 104 105 106
  Reduction r = spec()->Reduce(node);
  CHECK(r.Changed());

  Node* new_context = NodeProperties::GetContextInput(r.replacement());
  CHECK_EQ(new_context, expected_new_context);
107

108
  ContextAccess new_access = ContextAccessOf(r.replacement()->op());
109 110 111 112 113
  CHECK_EQ(new_access.depth(), expected_new_depth);
  CHECK_EQ(new_access.index(), access.index());
  CHECK_EQ(new_access.immutable(), access.immutable());
}

114
static const int slot_index = Context::PREVIOUS_INDEX;
115 116

TEST(ReduceJSLoadContext0) {
117
  ContextSpecializationTester t(Nothing<OuterContext>());
118

119
  Node* start = t.graph()->NewNode(t.common()->Start(0));
120 121 122 123
  t.graph()->SetStart(start);

  // Make a context and initialize it a bit for this test.
  Handle<Context> native = t.factory()->NewNativeContext();
124 125 126 127
  Handle<Context> subcontext1 = t.factory()->NewNativeContext();
  Handle<Context> subcontext2 = t.factory()->NewNativeContext();
  subcontext2->set_previous(*subcontext1);
  subcontext1->set_previous(*native);
128
  Handle<Object> expected = t.factory()->InternalizeUtf8String("gboy!");
129
  const int slot = Context::PREVIOUS_INDEX;
130 131
  native->set(slot, *expected);

132 133 134
  Node* const_context = t.jsgraph()->Constant(ObjectRef(t.broker(), native));
  Node* deep_const_context =
      t.jsgraph()->Constant(ObjectRef(t.broker(), subcontext2));
135
  Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start);
136 137 138

  {
    // Mutable slot, constant context, depth = 0 => do nothing.
139
    Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false),
140
                                    const_context, start);
141
    Reduction r = t.spec()->Reduce(load);
142 143 144 145 146
    CHECK(!r.Changed());
  }

  {
    // Mutable slot, non-constant context, depth = 0 => do nothing.
147
    Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, 0, false),
148
                                    param_context, start);
149
    Reduction r = t.spec()->Reduce(load);
150 151 152 153
    CHECK(!r.Changed());
  }

  {
154
    // Mutable slot, constant context, depth > 0 => fold-in parent context.
155
    Node* load = t.graph()->NewNode(
156
        t.javascript()->LoadContext(2, Context::GLOBAL_EVAL_FUN_INDEX, false),
157
        deep_const_context, start);
158
    Reduction r = t.spec()->Reduce(load);
159
    CHECK(r.Changed());
160
    Node* new_context_input = NodeProperties::GetContextInput(r.replacement());
161
    CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
162
    HeapObjectMatcher match(new_context_input);
163
    CHECK_EQ(*native, Context::cast(*match.ResolvedValue()));
164
    ContextAccess access = ContextAccessOf(r.replacement()->op());
165 166
    CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
    CHECK_EQ(0, static_cast<int>(access.depth()));
167 168 169 170
    CHECK_EQ(false, access.immutable());
  }

  {
171
    // Immutable slot, constant context, depth = 0 => specialize.
172
    Node* load = t.graph()->NewNode(t.javascript()->LoadContext(0, slot, true),
173
                                    const_context, start);
174
    Reduction r = t.spec()->Reduce(load);
175 176 177
    CHECK(r.Changed());
    CHECK(r.replacement() != load);

178
    HeapObjectMatcher match(r.replacement());
179 180
    CHECK(match.HasResolvedValue());
    CHECK_EQ(*expected, *match.ResolvedValue());
181
  }
182 183 184

  // Clean up so that verifiers don't complain.
  native->set(slot, Smi::zero());
185 186 187 188 189 190 191
}

TEST(ReduceJSLoadContext1) {
  // The graph's context chain ends in the incoming context parameter:
  //
  //   context2 <-- context1 <-- context0 (= Parameter(0))

192
  ContextSpecializationTester t(Nothing<OuterContext>());
193 194 195

  Node* start = t.graph()->NewNode(t.common()->Start(0));
  t.graph()->SetStart(start);
196
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
197
  const i::compiler::Operator* create_function_context =
198
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
199 200

  Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
201 202 203 204
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
205 206 207 208 209 210 211 212 213 214 215 216

  {
    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());
  }
217

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  {
    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);
  }
253 254
}

255 256
TEST(ReduceJSLoadContext2) {
  // The graph's context chain ends in a constant context (context_object1),
257
  // which has another outer context (context_object0).
258 259 260
  //
  //   context2 <-- context1 <-- context0 (= HeapConstant(context_object1))
  //   context_object1 <~~ context_object0
261

262
  ContextSpecializationTester t(Nothing<OuterContext>());
263 264 265

  Node* start = t.graph()->NewNode(t.common()->Start(0));
  t.graph()->SetStart(start);
266
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
267
  const i::compiler::Operator* create_function_context =
268
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
269 270 271 272 273 274 275

  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);
276 277
  context_object0->set(Context::EXTENSION_INDEX, *slot_value0);
  context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
278

279 280
  Node* context0 =
      t.jsgraph()->Constant(ObjectRef(t.broker(), context_object1));
281 282 283 284
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

  {
    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(
318 319
        t.javascript()->LoadContext(2, Context::EXTENSION_INDEX, true),
        context2, start);
320 321 322 323 324 325 326 327 328 329 330
    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(
331 332
        t.javascript()->LoadContext(3, Context::EXTENSION_INDEX, true),
        context2, start);
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    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);
353 354
  context_object0->set(Context::EXTENSION_INDEX, *slot_value0);
  context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
355

356
  ContextSpecializationTester t(Just(OuterContext(context_object1, 0)));
357 358 359

  Node* start = t.graph()->NewNode(t.common()->Start(2));
  t.graph()->SetStart(start);
360 361
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()),
                          handle_zone_scope.main_isolate());
362
  const i::compiler::Operator* create_function_context =
363
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
364 365

  Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
366 367 368 369
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402

  {
    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(
403 404
        t.javascript()->LoadContext(2, Context::EXTENSION_INDEX, true),
        context2, start);
405 406 407 408 409 410 411 412 413 414 415
    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(
416 417
        t.javascript()->LoadContext(3, Context::EXTENSION_INDEX, true),
        context2, start);
418 419 420 421 422
    t.CheckChangesToValue(load, slot_value0);
  }
}

TEST(ReduceJSStoreContext0) {
423
  ContextSpecializationTester t(Nothing<OuterContext>());
424

425
  Node* start = t.graph()->NewNode(t.common()->Start(0));
426 427 428 429 430 431 432 433 434
  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!");
435
  const int slot = Context::PREVIOUS_INDEX;
436 437
  native->set(slot, *expected);

438 439 440
  Node* const_context = t.jsgraph()->Constant(ObjectRef(t.broker(), native));
  Node* deep_const_context =
      t.jsgraph()->Constant(ObjectRef(t.broker(), subcontext2));
441
  Node* param_context = t.graph()->NewNode(t.common()->Parameter(0), start);
442

443
  {
444
    // Mutable slot, constant context, depth = 0 => do nothing.
445 446
    Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0),
                                    const_context, const_context, start, start);
447
    Reduction r = t.spec()->Reduce(load);
448 449
    CHECK(!r.Changed());
  }
450

451 452
  {
    // Mutable slot, non-constant context, depth = 0 => do nothing.
453 454
    Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, 0),
                                    param_context, param_context, start, start);
455
    Reduction r = t.spec()->Reduce(load);
456
    CHECK(!r.Changed());
457 458
  }

459 460
  {
    // Immutable slot, constant context, depth = 0 => do nothing.
461 462
    Node* load = t.graph()->NewNode(t.javascript()->StoreContext(0, slot),
                                    const_context, const_context, start, start);
463
    Reduction r = t.spec()->Reduce(load);
464 465 466 467 468
    CHECK(!r.Changed());
  }

  {
    // Mutable slot, constant context, depth > 0 => fold-in parent context.
469
    Node* load = t.graph()->NewNode(
470
        t.javascript()->StoreContext(2, Context::GLOBAL_EVAL_FUN_INDEX),
471
        deep_const_context, deep_const_context, start, start);
472
    Reduction r = t.spec()->Reduce(load);
473
    CHECK(r.Changed());
474
    Node* new_context_input = NodeProperties::GetContextInput(r.replacement());
475
    CHECK_EQ(IrOpcode::kHeapConstant, new_context_input->opcode());
476
    HeapObjectMatcher match(new_context_input);
477
    CHECK_EQ(*native, Context::cast(*match.ResolvedValue()));
478
    ContextAccess access = ContextAccessOf(r.replacement()->op());
479 480
    CHECK_EQ(Context::GLOBAL_EVAL_FUN_INDEX, static_cast<int>(access.index()));
    CHECK_EQ(0, static_cast<int>(access.depth()));
481 482
    CHECK_EQ(false, access.immutable());
  }
483 484 485

  // Clean up so that verifiers don't complain.
  native->set(slot, Smi::zero());
486 487
}

488
TEST(ReduceJSStoreContext1) {
489
  ContextSpecializationTester t(Nothing<OuterContext>());
490 491 492

  Node* start = t.graph()->NewNode(t.common()->Start(0));
  t.graph()->SetStart(start);
493
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
494
  const i::compiler::Operator* create_function_context =
495
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
496 497

  Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
498 499 500 501
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532

  {
    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) {
533
  ContextSpecializationTester t(Nothing<OuterContext>());
534 535 536

  Node* start = t.graph()->NewNode(t.common()->Start(0));
  t.graph()->SetStart(start);
537
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()), t.main_isolate());
538
  const i::compiler::Operator* create_function_context =
539
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
540 541 542 543 544 545 546

  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);
547 548
  context_object0->set(Context::EXTENSION_INDEX, *slot_value0);
  context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
549

550 551
  Node* context0 =
      t.jsgraph()->Constant(ObjectRef(t.broker(), context_object1));
552 553 554 555
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
556 557

  {
558 559 560
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(0, Context::EXTENSION_INDEX), context2,
        context2, start, start);
561 562 563 564
    CHECK(!t.spec()->Reduce(store).Changed());
  }

  {
565 566 567
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(1, Context::EXTENSION_INDEX), context2,
        context2, start, start);
568 569 570 571
    t.CheckContextInputAndDepthChanges(store, context1, 0);
  }

  {
572 573 574
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(2, Context::EXTENSION_INDEX), context2,
        context2, start, start);
575 576 577 578
    t.CheckContextInputAndDepthChanges(store, context0, 0);
  }

  {
579 580 581
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(3, Context::EXTENSION_INDEX), context2,
        context2, start, start);
582 583 584 585 586 587 588 589 590 591 592 593 594 595
    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);
596 597
  context_object0->set(Context::EXTENSION_INDEX, *slot_value0);
  context_object1->set(Context::EXTENSION_INDEX, *slot_value1);
598

599
  ContextSpecializationTester t(Just(OuterContext(context_object1, 0)));
600 601 602

  Node* start = t.graph()->NewNode(t.common()->Start(2));
  t.graph()->SetStart(start);
603 604
  Handle<ScopeInfo> empty(ScopeInfo::Empty(t.main_isolate()),
                          handle_zone_scope.main_isolate());
605
  const i::compiler::Operator* create_function_context =
606
      t.javascript()->CreateFunctionContext(empty, 42, FUNCTION_SCOPE);
607 608

  Node* context0 = t.graph()->NewNode(t.common()->Parameter(0), start);
609 610 611 612
  Node* context1 =
      t.graph()->NewNode(create_function_context, context0, start, start);
  Node* context2 =
      t.graph()->NewNode(create_function_context, context1, start, start);
613 614

  {
615 616 617
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(0, Context::EXTENSION_INDEX), context2,
        context2, start, start);
618 619 620 621
    CHECK(!t.spec()->Reduce(store).Changed());
  }

  {
622 623 624
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(1, Context::EXTENSION_INDEX), context2,
        context2, start, start);
625 626 627 628
    t.CheckContextInputAndDepthChanges(store, context1, 0);
  }

  {
629 630 631
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(2, Context::EXTENSION_INDEX), context2,
        context2, start, start);
632 633 634 635
    t.CheckContextInputAndDepthChanges(store, context_object1, 0);
  }

  {
636 637 638
    Node* store = t.graph()->NewNode(
        t.javascript()->StoreContext(3, Context::EXTENSION_INDEX), context2,
        context2, start, start);
639 640 641
    t.CheckContextInputAndDepthChanges(store, context_object0, 0);
  }
}
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681

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!
682 683 684 685 686 687 688 689
    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));
690 691 692 693 694 695 696 697 698 699 700 701
  }

  {
    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());
  }
}
702 703 704 705

}  // namespace compiler
}  // namespace internal
}  // namespace v8