1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "src/v8.h"
31
32#include "src/api.h"
33#include "src/base/platform/platform.h"
34#include "src/compilation-cache.h"
35#include "src/debug.h"
36#include "src/deoptimizer.h"
37#include "src/isolate.h"
38#include "test/cctest/cctest.h"
39
40using ::v8::base::OS;
41using ::v8::internal::Deoptimizer;
42using ::v8::internal::EmbeddedVector;
43using ::v8::internal::Handle;
44using ::v8::internal::Isolate;
45using ::v8::internal::JSFunction;
46using ::v8::internal::Object;
47
48// Size of temp buffer for formatting small strings.
49#define SMALL_STRING_BUFFER_SIZE 80
50
51// Utility class to set --allow-natives-syntax --always-opt and --nouse-inlining
52// when constructed and return to their default state when destroyed.
53class AlwaysOptimizeAllowNativesSyntaxNoInlining {
54 public:
55  AlwaysOptimizeAllowNativesSyntaxNoInlining()
56      : always_opt_(i::FLAG_always_opt),
57        allow_natives_syntax_(i::FLAG_allow_natives_syntax),
58        use_inlining_(i::FLAG_use_inlining) {
59    i::FLAG_always_opt = true;
60    i::FLAG_allow_natives_syntax = true;
61    i::FLAG_use_inlining = false;
62  }
63
64  ~AlwaysOptimizeAllowNativesSyntaxNoInlining() {
65    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
66    i::FLAG_always_opt = always_opt_;
67    i::FLAG_use_inlining = use_inlining_;
68  }
69
70 private:
71  bool always_opt_;
72  bool allow_natives_syntax_;
73  bool use_inlining_;
74};
75
76
77// Utility class to set --allow-natives-syntax and --nouse-inlining when
78// constructed and return to their default state when destroyed.
79class AllowNativesSyntaxNoInlining {
80 public:
81  AllowNativesSyntaxNoInlining()
82      : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
83        use_inlining_(i::FLAG_use_inlining) {
84    i::FLAG_allow_natives_syntax = true;
85    i::FLAG_use_inlining = false;
86  }
87
88  ~AllowNativesSyntaxNoInlining() {
89    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
90    i::FLAG_use_inlining = use_inlining_;
91  }
92
93 private:
94  bool allow_natives_syntax_;
95  bool use_inlining_;
96};
97
98
99// Abort any ongoing incremental marking to make sure that all weak global
100// handle callbacks are processed.
101static void NonIncrementalGC(i::Isolate* isolate) {
102  isolate->heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
103}
104
105
106static Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
107                                        const char* property_name) {
108  v8::Local<v8::Function> fun =
109      v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name)));
110  return v8::Utils::OpenHandle(*fun);
111}
112
113
114TEST(DeoptimizeSimple) {
115  i::FLAG_turbo_deoptimization = true;
116
117  LocalContext env;
118  v8::HandleScope scope(env->GetIsolate());
119
120  // Test lazy deoptimization of a simple function.
121  {
122    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
123    CompileRun(
124        "var count = 0;"
125        "function h() { %DeoptimizeFunction(f); }"
126        "function g() { count++; h(); }"
127        "function f() { g(); };"
128        "f();");
129  }
130  NonIncrementalGC(CcTest::i_isolate());
131
132  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
133  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
134  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
135
136  // Test lazy deoptimization of a simple function. Call the function after the
137  // deoptimization while it is still activated further down the stack.
138  {
139    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
140    CompileRun(
141        "var count = 0;"
142        "function g() { count++; %DeoptimizeFunction(f); f(false); }"
143        "function f(x) { if (x) { g(); } else { return } };"
144        "f(true);");
145  }
146  NonIncrementalGC(CcTest::i_isolate());
147
148  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
149  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
150  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
151}
152
153
154TEST(DeoptimizeSimpleWithArguments) {
155  i::FLAG_turbo_deoptimization = true;
156
157  LocalContext env;
158  v8::HandleScope scope(env->GetIsolate());
159
160  // Test lazy deoptimization of a simple function with some arguments.
161  {
162    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
163    CompileRun(
164        "var count = 0;"
165        "function h(x) { %DeoptimizeFunction(f); }"
166        "function g(x, y) { count++; h(x); }"
167        "function f(x, y, z) { g(1,x); y+z; };"
168        "f(1, \"2\", false);");
169  }
170  NonIncrementalGC(CcTest::i_isolate());
171
172  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
173  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
174  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
175
176  // Test lazy deoptimization of a simple function with some arguments. Call the
177  // function after the deoptimization while it is still activated further down
178  // the stack.
179  {
180    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
181    CompileRun(
182        "var count = 0;"
183        "function g(x, y) { count++; %DeoptimizeFunction(f); f(false, 1, y); }"
184        "function f(x, y, z) { if (x) { g(x, y); } else { return y + z; } };"
185        "f(true, 1, \"2\");");
186  }
187  NonIncrementalGC(CcTest::i_isolate());
188
189  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
190  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
191  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
192}
193
194
195TEST(DeoptimizeSimpleNested) {
196  i::FLAG_turbo_deoptimization = true;
197
198  LocalContext env;
199  v8::HandleScope scope(env->GetIsolate());
200
201  // Test lazy deoptimization of a simple function. Have a nested function call
202  // do the deoptimization.
203  {
204    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
205    CompileRun(
206        "var count = 0;"
207        "var result = 0;"
208        "function h(x, y, z) { return x + y + z; }"
209        "function g(z) { count++; %DeoptimizeFunction(f); return z;}"
210        "function f(x,y,z) { return h(x, y, g(z)); };"
211        "result = f(1, 2, 3);");
212    NonIncrementalGC(CcTest::i_isolate());
213
214    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
215    CHECK_EQ(6, env->Global()->Get(v8_str("result"))->Int32Value());
216    CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
217    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
218  }
219}
220
221
222TEST(DeoptimizeRecursive) {
223  i::FLAG_turbo_deoptimization = true;
224  LocalContext env;
225  v8::HandleScope scope(env->GetIsolate());
226
227  {
228    // Test lazy deoptimization of a simple function called recursively. Call
229    // the function recursively a number of times before deoptimizing it.
230    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
231    CompileRun(
232        "var count = 0;"
233        "var calls = 0;"
234        "function g() { count++; %DeoptimizeFunction(f); }"
235        "function f(x) { calls++; if (x > 0) { f(x - 1); } else { g(); } };"
236        "f(10);");
237  }
238  NonIncrementalGC(CcTest::i_isolate());
239
240  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
241  CHECK_EQ(11, env->Global()->Get(v8_str("calls"))->Int32Value());
242  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
243
244  v8::Local<v8::Function> fun = v8::Local<v8::Function>::Cast(
245      env->Global()->Get(v8::String::NewFromUtf8(CcTest::isolate(), "f")));
246  CHECK(!fun.IsEmpty());
247}
248
249
250TEST(DeoptimizeMultiple) {
251  i::FLAG_turbo_deoptimization = true;
252  LocalContext env;
253  v8::HandleScope scope(env->GetIsolate());
254
255  {
256    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
257    CompileRun(
258        "var count = 0;"
259        "var result = 0;"
260        "function g() { count++;"
261        "               %DeoptimizeFunction(f1);"
262        "               %DeoptimizeFunction(f2);"
263        "               %DeoptimizeFunction(f3);"
264        "               %DeoptimizeFunction(f4);}"
265        "function f4(x) { g(); };"
266        "function f3(x, y, z) { f4(); return x + y + z; };"
267        "function f2(x, y) { return x + f3(y + 1, y + 1, y + 1) + y; };"
268        "function f1(x) { return f2(x + 1, x + 1) + x; };"
269        "result = f1(1);");
270  }
271  NonIncrementalGC(CcTest::i_isolate());
272
273  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
274  CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value());
275  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
276}
277
278
279TEST(DeoptimizeConstructor) {
280  i::FLAG_turbo_deoptimization = true;
281  LocalContext env;
282  v8::HandleScope scope(env->GetIsolate());
283
284  {
285    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
286    CompileRun(
287        "var count = 0;"
288        "function g() { count++;"
289        "               %DeoptimizeFunction(f); }"
290        "function f() {  g(); };"
291        "result = new f() instanceof f;");
292  }
293  NonIncrementalGC(CcTest::i_isolate());
294
295  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
296  CHECK(env->Global()->Get(v8_str("result"))->IsTrue());
297  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
298
299  {
300    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
301    CompileRun(
302        "var count = 0;"
303        "var result = 0;"
304        "function g() { count++;"
305        "               %DeoptimizeFunction(f); }"
306        "function f(x, y) { this.x = x; g(); this.y = y; };"
307        "result = new f(1, 2);"
308        "result = result.x + result.y;");
309  }
310  NonIncrementalGC(CcTest::i_isolate());
311
312  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
313  CHECK_EQ(3, env->Global()->Get(v8_str("result"))->Int32Value());
314  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
315}
316
317
318TEST(DeoptimizeConstructorMultiple) {
319  i::FLAG_turbo_deoptimization = true;
320  LocalContext env;
321  v8::HandleScope scope(env->GetIsolate());
322
323  {
324    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
325    CompileRun(
326        "var count = 0;"
327        "var result = 0;"
328        "function g() { count++;"
329        "               %DeoptimizeFunction(f1);"
330        "               %DeoptimizeFunction(f2);"
331        "               %DeoptimizeFunction(f3);"
332        "               %DeoptimizeFunction(f4);}"
333        "function f4(x) { this.result = x; g(); };"
334        "function f3(x, y, z) { this.result = new f4(x + y + z).result; };"
335        "function f2(x, y) {"
336        "    this.result = x + new f3(y + 1, y + 1, y + 1).result + y; };"
337        "function f1(x) { this.result = new f2(x + 1, x + 1).result + x; };"
338        "result = new f1(1).result;");
339  }
340  NonIncrementalGC(CcTest::i_isolate());
341
342  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
343  CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value());
344  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
345}
346
347
348UNINITIALIZED_TEST(DeoptimizeBinaryOperationADDString) {
349  i::FLAG_turbo_deoptimization = true;
350  i::FLAG_concurrent_recompilation = false;
351  AllowNativesSyntaxNoInlining options;
352  v8::Isolate* isolate = v8::Isolate::New();
353  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
354  isolate->Enter();
355  {
356    LocalContext env(isolate);
357    v8::HandleScope scope(env->GetIsolate());
358
359    const char* f_source = "function f(x, y) { return x + y; };";
360
361    {
362      // Compile function f and collect to type feedback to insert binary op
363      // stub call in the optimized code.
364      i::FLAG_prepare_always_opt = true;
365      CompileRun(
366          "var count = 0;"
367          "var result = 0;"
368          "var deopt = false;"
369          "function X() { };"
370          "X.prototype.toString = function () {"
371          "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'"
372          "};");
373      CompileRun(f_source);
374      CompileRun(
375          "for (var i = 0; i < 5; i++) {"
376          "  f('a+', new X());"
377          "};");
378
379      // Compile an optimized version of f.
380      i::FLAG_always_opt = true;
381      CompileRun(f_source);
382      CompileRun("f('a+', new X());");
383      CHECK(!i_isolate->use_crankshaft() ||
384            GetJSFunction(env->Global(), "f")->IsOptimized());
385
386      // Call f and force deoptimization while processing the binary operation.
387      CompileRun(
388          "deopt = true;"
389          "var result = f('a+', new X());");
390    }
391    NonIncrementalGC(i_isolate);
392
393    CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
394    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
395    v8::Handle<v8::Value> result = env->Global()->Get(v8_str("result"));
396    CHECK(result->IsString());
397    v8::String::Utf8Value utf8(result);
398    CHECK_EQ("a+an X", *utf8);
399    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
400  }
401  isolate->Exit();
402  isolate->Dispose();
403}
404
405
406static void CompileConstructorWithDeoptimizingValueOf() {
407  CompileRun("var count = 0;"
408             "var result = 0;"
409             "var deopt = false;"
410             "function X() { };"
411             "X.prototype.valueOf = function () {"
412             "  if (deopt) { count++; %DeoptimizeFunction(f); } return 8"
413             "};");
414}
415
416
417static void TestDeoptimizeBinaryOpHelper(LocalContext* env,
418                                         const char* binary_op) {
419  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>((*env)->GetIsolate());
420  EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer;
421  SNPrintF(f_source_buffer,
422           "function f(x, y) { return x %s y; };",
423           binary_op);
424  char* f_source = f_source_buffer.start();
425
426  AllowNativesSyntaxNoInlining options;
427  // Compile function f and collect to type feedback to insert binary op stub
428  // call in the optimized code.
429  i::FLAG_prepare_always_opt = true;
430  CompileConstructorWithDeoptimizingValueOf();
431  CompileRun(f_source);
432  CompileRun("for (var i = 0; i < 5; i++) {"
433             "  f(8, new X());"
434             "};");
435
436  // Compile an optimized version of f.
437  i::FLAG_always_opt = true;
438  CompileRun(f_source);
439  CompileRun("f(7, new X());");
440  CHECK(!i_isolate->use_crankshaft() ||
441        GetJSFunction((*env)->Global(), "f")->IsOptimized());
442
443  // Call f and force deoptimization while processing the binary operation.
444  CompileRun("deopt = true;"
445             "var result = f(7, new X());");
446  NonIncrementalGC(i_isolate);
447  CHECK(!GetJSFunction((*env)->Global(), "f")->IsOptimized());
448}
449
450
451UNINITIALIZED_TEST(DeoptimizeBinaryOperationADD) {
452  i::FLAG_turbo_deoptimization = true;
453  i::FLAG_concurrent_recompilation = false;
454  v8::Isolate* isolate = v8::Isolate::New();
455  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
456  isolate->Enter();
457  {
458    LocalContext env(isolate);
459    v8::HandleScope scope(env->GetIsolate());
460
461    TestDeoptimizeBinaryOpHelper(&env, "+");
462
463    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
464    CHECK_EQ(15, env->Global()->Get(v8_str("result"))->Int32Value());
465    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
466  }
467  isolate->Exit();
468  isolate->Dispose();
469}
470
471
472UNINITIALIZED_TEST(DeoptimizeBinaryOperationSUB) {
473  i::FLAG_turbo_deoptimization = true;
474  i::FLAG_concurrent_recompilation = false;
475  v8::Isolate* isolate = v8::Isolate::New();
476  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
477  isolate->Enter();
478  {
479    LocalContext env(isolate);
480    v8::HandleScope scope(env->GetIsolate());
481
482    TestDeoptimizeBinaryOpHelper(&env, "-");
483
484    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
485    CHECK_EQ(-1, env->Global()->Get(v8_str("result"))->Int32Value());
486    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
487  }
488  isolate->Exit();
489  isolate->Dispose();
490}
491
492
493UNINITIALIZED_TEST(DeoptimizeBinaryOperationMUL) {
494  i::FLAG_turbo_deoptimization = true;
495  i::FLAG_concurrent_recompilation = false;
496  v8::Isolate* isolate = v8::Isolate::New();
497  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
498  isolate->Enter();
499  {
500    LocalContext env(isolate);
501    v8::HandleScope scope(env->GetIsolate());
502
503    TestDeoptimizeBinaryOpHelper(&env, "*");
504
505    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
506    CHECK_EQ(56, env->Global()->Get(v8_str("result"))->Int32Value());
507    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
508  }
509  isolate->Exit();
510  isolate->Dispose();
511}
512
513
514UNINITIALIZED_TEST(DeoptimizeBinaryOperationDIV) {
515  i::FLAG_turbo_deoptimization = true;
516  i::FLAG_concurrent_recompilation = false;
517  v8::Isolate* isolate = v8::Isolate::New();
518  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
519  isolate->Enter();
520  {
521    LocalContext env(isolate);
522    v8::HandleScope scope(env->GetIsolate());
523
524    TestDeoptimizeBinaryOpHelper(&env, "/");
525
526    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
527    CHECK_EQ(0, env->Global()->Get(v8_str("result"))->Int32Value());
528    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
529  }
530  isolate->Exit();
531  isolate->Dispose();
532}
533
534
535UNINITIALIZED_TEST(DeoptimizeBinaryOperationMOD) {
536  i::FLAG_turbo_deoptimization = true;
537  i::FLAG_concurrent_recompilation = false;
538  v8::Isolate* isolate = v8::Isolate::New();
539  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
540  isolate->Enter();
541  {
542    LocalContext env(isolate);
543    v8::HandleScope scope(env->GetIsolate());
544
545    TestDeoptimizeBinaryOpHelper(&env, "%");
546
547    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
548    CHECK_EQ(7, env->Global()->Get(v8_str("result"))->Int32Value());
549    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
550  }
551  isolate->Exit();
552  isolate->Dispose();
553}
554
555
556UNINITIALIZED_TEST(DeoptimizeCompare) {
557  i::FLAG_turbo_deoptimization = true;
558  i::FLAG_concurrent_recompilation = false;
559  v8::Isolate* isolate = v8::Isolate::New();
560  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
561  isolate->Enter();
562  {
563    LocalContext env(isolate);
564    v8::HandleScope scope(env->GetIsolate());
565
566    const char* f_source = "function f(x, y) { return x < y; };";
567
568    {
569      AllowNativesSyntaxNoInlining options;
570      // Compile function f and collect to type feedback to insert compare ic
571      // call in the optimized code.
572      i::FLAG_prepare_always_opt = true;
573      CompileRun(
574          "var count = 0;"
575          "var result = 0;"
576          "var deopt = false;"
577          "function X() { };"
578          "X.prototype.toString = function () {"
579          "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'"
580          "};");
581      CompileRun(f_source);
582      CompileRun(
583          "for (var i = 0; i < 5; i++) {"
584          "  f('a', new X());"
585          "};");
586
587      // Compile an optimized version of f.
588      i::FLAG_always_opt = true;
589      CompileRun(f_source);
590      CompileRun("f('a', new X());");
591      CHECK(!i_isolate->use_crankshaft() ||
592            GetJSFunction(env->Global(), "f")->IsOptimized());
593
594      // Call f and force deoptimization while processing the comparison.
595      CompileRun(
596          "deopt = true;"
597          "var result = f('a', new X());");
598    }
599    NonIncrementalGC(i_isolate);
600
601    CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
602    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
603    CHECK_EQ(true, env->Global()->Get(v8_str("result"))->BooleanValue());
604    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(i_isolate));
605  }
606  isolate->Exit();
607  isolate->Dispose();
608}
609
610
611UNINITIALIZED_TEST(DeoptimizeLoadICStoreIC) {
612  i::FLAG_turbo_deoptimization = true;
613  i::FLAG_concurrent_recompilation = false;
614  v8::Isolate* isolate = v8::Isolate::New();
615  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
616  isolate->Enter();
617  {
618    LocalContext env(isolate);
619    v8::HandleScope scope(env->GetIsolate());
620
621    // Functions to generate load/store/keyed load/keyed store IC calls.
622    const char* f1_source = "function f1(x) { return x.y; };";
623    const char* g1_source = "function g1(x) { x.y = 1; };";
624    const char* f2_source = "function f2(x, y) { return x[y]; };";
625    const char* g2_source = "function g2(x, y) { x[y] = 1; };";
626
627    {
628      AllowNativesSyntaxNoInlining options;
629      // Compile functions and collect to type feedback to insert ic
630      // calls in the optimized code.
631      i::FLAG_prepare_always_opt = true;
632      CompileRun(
633          "var count = 0;"
634          "var result = 0;"
635          "var deopt = false;"
636          "function X() { };"
637          "X.prototype.__defineGetter__('y', function () {"
638          "  if (deopt) { count++; %DeoptimizeFunction(f1); };"
639          "  return 13;"
640          "});"
641          "X.prototype.__defineSetter__('y', function () {"
642          "  if (deopt) { count++; %DeoptimizeFunction(g1); };"
643          "});"
644          "X.prototype.__defineGetter__('z', function () {"
645          "  if (deopt) { count++; %DeoptimizeFunction(f2); };"
646          "  return 13;"
647          "});"
648          "X.prototype.__defineSetter__('z', function () {"
649          "  if (deopt) { count++; %DeoptimizeFunction(g2); };"
650          "});");
651      CompileRun(f1_source);
652      CompileRun(g1_source);
653      CompileRun(f2_source);
654      CompileRun(g2_source);
655      CompileRun(
656          "for (var i = 0; i < 5; i++) {"
657          "  f1(new X());"
658          "  g1(new X());"
659          "  f2(new X(), 'z');"
660          "  g2(new X(), 'z');"
661          "};");
662
663      // Compile an optimized version of the functions.
664      i::FLAG_always_opt = true;
665      CompileRun(f1_source);
666      CompileRun(g1_source);
667      CompileRun(f2_source);
668      CompileRun(g2_source);
669      CompileRun("f1(new X());");
670      CompileRun("g1(new X());");
671      CompileRun("f2(new X(), 'z');");
672      CompileRun("g2(new X(), 'z');");
673      if (i_isolate->use_crankshaft()) {
674        CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized());
675        CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized());
676        CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized());
677        CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized());
678      }
679
680      // Call functions and force deoptimization while processing the ics.
681      CompileRun(
682          "deopt = true;"
683          "var result = f1(new X());"
684          "g1(new X());"
685          "f2(new X(), 'z');"
686          "g2(new X(), 'z');");
687    }
688    NonIncrementalGC(i_isolate);
689
690    CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized());
691    CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized());
692    CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized());
693    CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized());
694    CHECK_EQ(4, env->Global()->Get(v8_str("count"))->Int32Value());
695    CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value());
696  }
697  isolate->Exit();
698  isolate->Dispose();
699}
700
701
702UNINITIALIZED_TEST(DeoptimizeLoadICStoreICNested) {
703  i::FLAG_turbo_deoptimization = true;
704  i::FLAG_concurrent_recompilation = false;
705  v8::Isolate* isolate = v8::Isolate::New();
706  i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
707  isolate->Enter();
708  {
709    LocalContext env(isolate);
710    v8::HandleScope scope(env->GetIsolate());
711
712    // Functions to generate load/store/keyed load/keyed store IC calls.
713    const char* f1_source = "function f1(x) { return x.y; };";
714    const char* g1_source = "function g1(x) { x.y = 1; };";
715    const char* f2_source = "function f2(x, y) { return x[y]; };";
716    const char* g2_source = "function g2(x, y) { x[y] = 1; };";
717
718    {
719      AllowNativesSyntaxNoInlining options;
720      // Compile functions and collect to type feedback to insert ic
721      // calls in the optimized code.
722      i::FLAG_prepare_always_opt = true;
723      CompileRun(
724          "var count = 0;"
725          "var result = 0;"
726          "var deopt = false;"
727          "function X() { };"
728          "X.prototype.__defineGetter__('y', function () {"
729          "  g1(this);"
730          "  return 13;"
731          "});"
732          "X.prototype.__defineSetter__('y', function () {"
733          "  f2(this, 'z');"
734          "});"
735          "X.prototype.__defineGetter__('z', function () {"
736          "  g2(this, 'z');"
737          "});"
738          "X.prototype.__defineSetter__('z', function () {"
739          "  if (deopt) {"
740          "    count++;"
741          "    %DeoptimizeFunction(f1);"
742          "    %DeoptimizeFunction(g1);"
743          "    %DeoptimizeFunction(f2);"
744          "    %DeoptimizeFunction(g2); };"
745          "});");
746      CompileRun(f1_source);
747      CompileRun(g1_source);
748      CompileRun(f2_source);
749      CompileRun(g2_source);
750      CompileRun(
751          "for (var i = 0; i < 5; i++) {"
752          "  f1(new X());"
753          "  g1(new X());"
754          "  f2(new X(), 'z');"
755          "  g2(new X(), 'z');"
756          "};");
757
758      // Compile an optimized version of the functions.
759      i::FLAG_always_opt = true;
760      CompileRun(f1_source);
761      CompileRun(g1_source);
762      CompileRun(f2_source);
763      CompileRun(g2_source);
764      CompileRun("f1(new X());");
765      CompileRun("g1(new X());");
766      CompileRun("f2(new X(), 'z');");
767      CompileRun("g2(new X(), 'z');");
768      if (i_isolate->use_crankshaft()) {
769        CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized());
770        CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized());
771        CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized());
772        CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized());
773      }
774
775      // Call functions and force deoptimization while processing the ics.
776      CompileRun(
777          "deopt = true;"
778          "var result = f1(new X());");
779    }
780    NonIncrementalGC(i_isolate);
781
782    CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized());
783    CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized());
784    CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized());
785    CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized());
786    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
787    CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value());
788  }
789  isolate->Exit();
790  isolate->Dispose();
791}
792