test-deoptimization.cc revision 44f0eee88ff00398ff7f715fab053374d808c90d
1// Copyright 2007-2010 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 "v8.h"
31
32#include "api.h"
33#include "cctest.h"
34#include "compilation-cache.h"
35#include "debug.h"
36#include "deoptimizer.h"
37#include "isolate.h"
38#include "platform.h"
39#include "stub-cache.h"
40
41using ::v8::internal::Deoptimizer;
42using ::v8::internal::EmbeddedVector;
43using ::v8::internal::Handle;
44using ::v8::internal::Isolate;
45using ::v8::internal::JSFunction;
46using ::v8::internal::OS;
47using ::v8::internal::Object;
48
49// Size of temp buffer for formatting small strings.
50#define SMALL_STRING_BUFFER_SIZE 80
51
52// Utility class to set --allow-natives-syntax --always-opt and --nouse-inlining
53// when constructed and return to their default state when destroyed.
54class AlwaysOptimizeAllowNativesSyntaxNoInlining {
55 public:
56  AlwaysOptimizeAllowNativesSyntaxNoInlining()
57      : always_opt_(i::FLAG_always_opt),
58        allow_natives_syntax_(i::FLAG_allow_natives_syntax),
59        use_inlining_(i::FLAG_use_inlining) {
60    i::FLAG_always_opt = true;
61    i::FLAG_allow_natives_syntax = true;
62    i::FLAG_use_inlining = false;
63  }
64
65  ~AlwaysOptimizeAllowNativesSyntaxNoInlining() {
66    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
67    i::FLAG_always_opt = always_opt_;
68    i::FLAG_use_inlining = use_inlining_;
69  }
70
71 private:
72  bool always_opt_;
73  bool allow_natives_syntax_;
74  bool use_inlining_;
75};
76
77
78// Utility class to set --allow-natives-syntax and --nouse-inlining when
79// constructed and return to their default state when destroyed.
80class AllowNativesSyntaxNoInlining {
81 public:
82  AllowNativesSyntaxNoInlining()
83      : allow_natives_syntax_(i::FLAG_allow_natives_syntax),
84        use_inlining_(i::FLAG_use_inlining) {
85    i::FLAG_allow_natives_syntax = true;
86    i::FLAG_use_inlining = false;
87  }
88
89  ~AllowNativesSyntaxNoInlining() {
90    i::FLAG_allow_natives_syntax = allow_natives_syntax_;
91    i::FLAG_use_inlining = use_inlining_;
92  }
93
94 private:
95  bool allow_natives_syntax_;
96  bool use_inlining_;
97};
98
99
100Handle<JSFunction> GetJSFunction(v8::Handle<v8::Object> obj,
101                                 const char* property_name) {
102  v8::Local<v8::Function> fun =
103      v8::Local<v8::Function>::Cast(obj->Get(v8_str(property_name)));
104  return v8::Utils::OpenHandle(*fun);
105}
106
107
108TEST(DeoptimizeSimple) {
109  v8::HandleScope scope;
110  const char* extension_list[] = { "v8/gc" };
111  v8::ExtensionConfiguration extensions(1, extension_list);
112  LocalContext env(&extensions);
113
114  // Test lazy deoptimization of a simple function.
115  {
116    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
117    CompileRun(
118        "var count = 0;"
119        "function h() { %DeoptimizeFunction(f); }"
120        "function g() { count++; h(); }"
121        "function f() { g(); };"
122        "f();"
123        "gc(); gc()");
124  }
125
126  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
127  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
128  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
129
130  // Test lazy deoptimization of a simple function. Call the function after the
131  // deoptimization while it is still activated further down the stack.
132  {
133    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
134    CompileRun(
135        "var count = 0;"
136        "function g() { count++; %DeoptimizeFunction(f); f(false); }"
137        "function f(x) { if (x) { g(); } else { return } };"
138        "f(true);"
139        "gc(); gc()");
140  }
141
142  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
143  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
144  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
145}
146
147
148TEST(DeoptimizeSimpleWithArguments) {
149  v8::HandleScope scope;
150  const char* extension_list[] = { "v8/gc" };
151  v8::ExtensionConfiguration extensions(1, extension_list);
152  LocalContext env(&extensions);
153
154  // Test lazy deoptimization of a simple function with some arguments.
155  {
156    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
157    CompileRun(
158        "var count = 0;"
159        "function h(x) { %DeoptimizeFunction(f); }"
160        "function g(x, y) { count++; h(x); }"
161        "function f(x, y, z) { g(1,x); y+z; };"
162        "f(1, \"2\", false);"
163        "gc(); gc()");
164  }
165
166  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
167  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
168  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
169
170  // Test lazy deoptimization of a simple function with some arguments. Call the
171  // function after the deoptimization while it is still activated further down
172  // the stack.
173  {
174    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
175    CompileRun(
176        "var count = 0;"
177        "function g(x, y) { count++; %DeoptimizeFunction(f); f(false, 1, y); }"
178        "function f(x, y, z) { if (x) { g(x, y); } else { return y + z; } };"
179        "f(true, 1, \"2\");"
180        "gc(); gc()");
181  }
182
183  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
184  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
185  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
186}
187
188
189TEST(DeoptimizeSimpleNested) {
190  v8::HandleScope scope;
191  const char* extension_list[] = { "v8/gc" };
192  v8::ExtensionConfiguration extensions(1, extension_list);
193  LocalContext env(&extensions);
194
195  // Test lazy deoptimization of a simple function. Have a nested function call
196  // do the deoptimization.
197  {
198    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
199    CompileRun(
200        "var count = 0;"
201        "var result = 0;"
202        "function h(x, y, z) { return x + y + z; }"
203        "function g(z) { count++; %DeoptimizeFunction(f); return z;}"
204        "function f(x,y,z) { return h(x, y, g(z)); };"
205        "result = f(1, 2, 3);"
206        "gc(); gc()");
207
208    CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
209    CHECK_EQ(6, env->Global()->Get(v8_str("result"))->Int32Value());
210    CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
211    CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
212  }
213}
214
215
216TEST(DeoptimizeRecursive) {
217  v8::HandleScope scope;
218  const char* extension_list[] = { "v8/gc" };
219  v8::ExtensionConfiguration extensions(1, extension_list);
220  LocalContext env(&extensions);
221
222  {
223    // Test lazy deoptimization of a simple function called recursively. Call
224    // the function recursively a number of times before deoptimizing it.
225    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
226    CompileRun(
227        "var count = 0;"
228        "var calls = 0;"
229        "function g() { count++; %DeoptimizeFunction(f); }"
230        "function f(x) { calls++; if (x > 0) { f(x - 1); } else { g(); } };"
231        "f(10); gc(); gc()");
232  }
233
234  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
235  CHECK_EQ(11, env->Global()->Get(v8_str("calls"))->Int32Value());
236  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
237
238  v8::Local<v8::Function> fun =
239      v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
240  Handle<v8::internal::JSFunction> f = v8::Utils::OpenHandle(*fun);
241}
242
243
244TEST(DeoptimizeMultiple) {
245  v8::HandleScope scope;
246  const char* extension_list[] = { "v8/gc" };
247  v8::ExtensionConfiguration extensions(1, extension_list);
248  LocalContext env(&extensions);
249
250  {
251    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
252    CompileRun(
253        "var count = 0;"
254        "var result = 0;"
255        "function g() { count++;"
256        "               %DeoptimizeFunction(f1);"
257        "               %DeoptimizeFunction(f2);"
258        "               %DeoptimizeFunction(f3);"
259        "               %DeoptimizeFunction(f4);}"
260        "function f4(x) { g(); };"
261        "function f3(x, y, z) { f4(); return x + y + z; };"
262        "function f2(x, y) { return x + f3(y + 1, y + 1, y + 1) + y; };"
263        "function f1(x) { return f2(x + 1, x + 1) + x; };"
264        "result = f1(1);"
265        "gc(); gc()");
266  }
267
268  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
269  CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value());
270  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
271}
272
273
274TEST(DeoptimizeConstructor) {
275  v8::HandleScope scope;
276  const char* extension_list[] = { "v8/gc" };
277  v8::ExtensionConfiguration extensions(1, extension_list);
278  LocalContext env(&extensions);
279
280  {
281    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
282    CompileRun(
283        "var count = 0;"
284        "function g() { count++;"
285        "               %DeoptimizeFunction(f); }"
286        "function f() {  g(); };"
287        "result = new f() instanceof f;"
288        "gc(); gc()");
289  }
290
291  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
292  CHECK(env->Global()->Get(v8_str("result"))->IsTrue());
293  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
294
295  {
296    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
297    CompileRun(
298        "var count = 0;"
299        "var result = 0;"
300        "function g() { count++;"
301        "               %DeoptimizeFunction(f); }"
302        "function f(x, y) { this.x = x; g(); this.y = y; };"
303        "result = new f(1, 2);"
304        "result = result.x + result.y;"
305        "gc(); gc()");
306  }
307
308  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
309  CHECK_EQ(3, env->Global()->Get(v8_str("result"))->Int32Value());
310  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
311}
312
313
314TEST(DeoptimizeConstructorMultiple) {
315  v8::HandleScope scope;
316  const char* extension_list[] = { "v8/gc" };
317  v8::ExtensionConfiguration extensions(1, extension_list);
318  LocalContext env(&extensions);
319
320  {
321    AlwaysOptimizeAllowNativesSyntaxNoInlining options;
322    CompileRun(
323        "var count = 0;"
324        "var result = 0;"
325        "function g() { count++;"
326        "               %DeoptimizeFunction(f1);"
327        "               %DeoptimizeFunction(f2);"
328        "               %DeoptimizeFunction(f3);"
329        "               %DeoptimizeFunction(f4);}"
330        "function f4(x) { this.result = x; g(); };"
331        "function f3(x, y, z) { this.result = new f4(x + y + z).result; };"
332        "function f2(x, y) {"
333        "    this.result = x + new f3(y + 1, y + 1, y + 1).result + y; };"
334        "function f1(x) { this.result = new f2(x + 1, x + 1).result + x; };"
335        "result = new f1(1).result;"
336        "gc(); gc()");
337  }
338
339  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
340  CHECK_EQ(14, env->Global()->Get(v8_str("result"))->Int32Value());
341  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
342}
343
344
345TEST(DeoptimizeBinaryOperationADDString) {
346  v8::HandleScope scope;
347  const char* extension_list[] = { "v8/gc" };
348  v8::ExtensionConfiguration extensions(1, extension_list);
349  LocalContext env(&extensions);
350
351  const char* f_source = "function f(x, y) { return x + y; };";
352
353  {
354    AllowNativesSyntaxNoInlining options;
355    // Compile function f and collect to type feedback to insert binary op stub
356    // call in the optimized code.
357    i::FLAG_prepare_always_opt = true;
358    CompileRun("var count = 0;"
359               "var result = 0;"
360               "var deopt = false;"
361               "function X() { };"
362               "X.prototype.toString = function () {"
363               "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'an X'"
364               "};");
365    CompileRun(f_source);
366    CompileRun("for (var i = 0; i < 5; i++) {"
367               "  f('a+', new X());"
368               "};");
369
370    // Compile an optimized version of f.
371    i::FLAG_always_opt = true;
372    CompileRun(f_source);
373    CompileRun("f('a+', new X());");
374    CHECK(!i::V8::UseCrankshaft() ||
375          GetJSFunction(env->Global(), "f")->IsOptimized());
376
377    // Call f and force deoptimization while processing the binary operation.
378    CompileRun("deopt = true;"
379               "var result = f('a+', new X());"
380               "gc(); gc();");
381  }
382
383  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
384  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
385  v8::Handle<v8::Value> result = env->Global()->Get(v8_str("result"));
386  CHECK(result->IsString());
387  v8::String::AsciiValue ascii(result);
388  CHECK_EQ("a+an X", *ascii);
389  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
390}
391
392
393static void CompileConstructorWithDeoptimizingValueOf() {
394  CompileRun("var count = 0;"
395             "var result = 0;"
396             "var deopt = false;"
397             "function X() { };"
398             "X.prototype.valueOf = function () {"
399             "  if (deopt) { count++; %DeoptimizeFunction(f); } return 8"
400             "};");
401}
402
403
404static void TestDeoptimizeBinaryOpHelper(LocalContext* env,
405                                         const char* binary_op) {
406  EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer;
407  OS::SNPrintF(f_source_buffer,
408               "function f(x, y) { return x %s y; };",
409               binary_op);
410  char* f_source = f_source_buffer.start();
411
412  AllowNativesSyntaxNoInlining options;
413  // Compile function f and collect to type feedback to insert binary op stub
414  // call in the optimized code.
415  i::FLAG_prepare_always_opt = true;
416  CompileConstructorWithDeoptimizingValueOf();
417  CompileRun(f_source);
418  CompileRun("for (var i = 0; i < 5; i++) {"
419             "  f(8, new X());"
420             "};");
421
422  // Compile an optimized version of f.
423  i::FLAG_always_opt = true;
424  CompileRun(f_source);
425  CompileRun("f(7, new X());");
426  CHECK(!i::V8::UseCrankshaft() ||
427        GetJSFunction((*env)->Global(), "f")->IsOptimized());
428
429  // Call f and force deoptimization while processing the binary operation.
430  CompileRun("deopt = true;"
431             "var result = f(7, new X());"
432             "gc(); gc();");
433
434  CHECK(!GetJSFunction((*env)->Global(), "f")->IsOptimized());
435}
436
437
438TEST(DeoptimizeBinaryOperationADD) {
439  v8::HandleScope scope;
440  const char* extension_list[] = { "v8/gc" };
441  v8::ExtensionConfiguration extensions(1, extension_list);
442  LocalContext env(&extensions);
443
444  TestDeoptimizeBinaryOpHelper(&env, "+");
445
446  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
447  CHECK_EQ(15, env->Global()->Get(v8_str("result"))->Int32Value());
448  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
449}
450
451
452TEST(DeoptimizeBinaryOperationSUB) {
453  v8::HandleScope scope;
454  const char* extension_list[] = { "v8/gc" };
455  v8::ExtensionConfiguration extensions(1, extension_list);
456  LocalContext env(&extensions);
457
458  TestDeoptimizeBinaryOpHelper(&env, "-");
459
460  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
461  CHECK_EQ(-1, env->Global()->Get(v8_str("result"))->Int32Value());
462  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
463}
464
465
466TEST(DeoptimizeBinaryOperationMUL) {
467  v8::HandleScope scope;
468  const char* extension_list[] = { "v8/gc" };
469  v8::ExtensionConfiguration extensions(1, extension_list);
470  LocalContext env(&extensions);
471
472  TestDeoptimizeBinaryOpHelper(&env, "*");
473
474  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
475  CHECK_EQ(56, env->Global()->Get(v8_str("result"))->Int32Value());
476  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
477}
478
479
480TEST(DeoptimizeBinaryOperationDIV) {
481  v8::HandleScope scope;
482  const char* extension_list[] = { "v8/gc" };
483  v8::ExtensionConfiguration extensions(1, extension_list);
484  LocalContext env(&extensions);
485
486  TestDeoptimizeBinaryOpHelper(&env, "/");
487
488  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
489  CHECK_EQ(0, env->Global()->Get(v8_str("result"))->Int32Value());
490  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
491}
492
493
494TEST(DeoptimizeBinaryOperationMOD) {
495  v8::HandleScope scope;
496  const char* extension_list[] = { "v8/gc" };
497  v8::ExtensionConfiguration extensions(1, extension_list);
498  LocalContext env(&extensions);
499
500  TestDeoptimizeBinaryOpHelper(&env, "%");
501
502  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
503  CHECK_EQ(7, env->Global()->Get(v8_str("result"))->Int32Value());
504  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
505}
506
507
508TEST(DeoptimizeCompare) {
509  v8::HandleScope scope;
510  const char* extension_list[] = { "v8/gc" };
511  v8::ExtensionConfiguration extensions(1, extension_list);
512  LocalContext env(&extensions);
513
514  const char* f_source = "function f(x, y) { return x < y; };";
515
516  {
517    AllowNativesSyntaxNoInlining options;
518    // Compile function f and collect to type feedback to insert compare ic
519    // call in the optimized code.
520    i::FLAG_prepare_always_opt = true;
521    CompileRun("var count = 0;"
522               "var result = 0;"
523               "var deopt = false;"
524               "function X() { };"
525               "X.prototype.toString = function () {"
526               "  if (deopt) { count++; %DeoptimizeFunction(f); } return 'b'"
527               "};");
528    CompileRun(f_source);
529    CompileRun("for (var i = 0; i < 5; i++) {"
530               "  f('a', new X());"
531               "};");
532
533    // Compile an optimized version of f.
534    i::FLAG_always_opt = true;
535    CompileRun(f_source);
536    CompileRun("f('a', new X());");
537    CHECK(!i::V8::UseCrankshaft() ||
538          GetJSFunction(env->Global(), "f")->IsOptimized());
539
540    // Call f and force deoptimization while processing the comparison.
541    CompileRun("deopt = true;"
542               "var result = f('a', new X());"
543               "gc(); gc();");
544  }
545
546  CHECK(!GetJSFunction(env->Global(), "f")->IsOptimized());
547  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
548  CHECK_EQ(true, env->Global()->Get(v8_str("result"))->BooleanValue());
549  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
550}
551
552
553TEST(DeoptimizeLoadICStoreIC) {
554  v8::HandleScope scope;
555  const char* extension_list[] = { "v8/gc" };
556  v8::ExtensionConfiguration extensions(1, extension_list);
557  LocalContext env(&extensions);
558
559  // Functions to generate load/store/keyed load/keyed store IC calls.
560  const char* f1_source = "function f1(x) { return x.y; };";
561  const char* g1_source = "function g1(x) { x.y = 1; };";
562  const char* f2_source = "function f2(x, y) { return x[y]; };";
563  const char* g2_source = "function g2(x, y) { x[y] = 1; };";
564
565  {
566    AllowNativesSyntaxNoInlining options;
567    // Compile functions and collect to type feedback to insert ic
568    // calls in the optimized code.
569    i::FLAG_prepare_always_opt = true;
570    CompileRun("var count = 0;"
571               "var result = 0;"
572               "var deopt = false;"
573               "function X() { };"
574               "X.prototype.__defineGetter__('y', function () {"
575               "  if (deopt) { count++; %DeoptimizeFunction(f1); };"
576               "  return 13;"
577               "});"
578               "X.prototype.__defineSetter__('y', function () {"
579               "  if (deopt) { count++; %DeoptimizeFunction(g1); };"
580               "});"
581               "X.prototype.__defineGetter__('z', function () {"
582               "  if (deopt) { count++; %DeoptimizeFunction(f2); };"
583               "  return 13;"
584               "});"
585               "X.prototype.__defineSetter__('z', function () {"
586               "  if (deopt) { count++; %DeoptimizeFunction(g2); };"
587               "});");
588    CompileRun(f1_source);
589    CompileRun(g1_source);
590    CompileRun(f2_source);
591    CompileRun(g2_source);
592    CompileRun("for (var i = 0; i < 5; i++) {"
593               "  f1(new X());"
594               "  g1(new X());"
595               "  f2(new X(), 'z');"
596               "  g2(new X(), 'z');"
597               "};");
598
599    // Compile an optimized version of the functions.
600    i::FLAG_always_opt = true;
601    CompileRun(f1_source);
602    CompileRun(g1_source);
603    CompileRun(f2_source);
604    CompileRun(g2_source);
605    CompileRun("f1(new X());");
606    CompileRun("g1(new X());");
607    CompileRun("f2(new X(), 'z');");
608    CompileRun("g2(new X(), 'z');");
609    if (i::V8::UseCrankshaft()) {
610      CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized());
611      CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized());
612      CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized());
613      CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized());
614    }
615
616    // Call functions and force deoptimization while processing the ics.
617    CompileRun("deopt = true;"
618               "var result = f1(new X());"
619               "g1(new X());"
620               "f2(new X(), 'z');"
621               "g2(new X(), 'z');"
622               "gc(); gc();");
623  }
624
625  CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized());
626  CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized());
627  CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized());
628  CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized());
629  CHECK_EQ(4, env->Global()->Get(v8_str("count"))->Int32Value());
630  CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value());
631  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
632}
633
634
635TEST(DeoptimizeLoadICStoreICNested) {
636  v8::HandleScope scope;
637  const char* extension_list[] = { "v8/gc" };
638  v8::ExtensionConfiguration extensions(1, extension_list);
639  LocalContext env(&extensions);
640
641  // Functions to generate load/store/keyed load/keyed store IC calls.
642  const char* f1_source = "function f1(x) { return x.y; };";
643  const char* g1_source = "function g1(x) { x.y = 1; };";
644  const char* f2_source = "function f2(x, y) { return x[y]; };";
645  const char* g2_source = "function g2(x, y) { x[y] = 1; };";
646
647  {
648    AllowNativesSyntaxNoInlining options;
649    // Compile functions and collect to type feedback to insert ic
650    // calls in the optimized code.
651    i::FLAG_prepare_always_opt = true;
652    CompileRun("var count = 0;"
653               "var result = 0;"
654               "var deopt = false;"
655               "function X() { };"
656               "X.prototype.__defineGetter__('y', function () {"
657               "  g1(this);"
658               "  return 13;"
659               "});"
660               "X.prototype.__defineSetter__('y', function () {"
661               "  f2(this, 'z');"
662               "});"
663               "X.prototype.__defineGetter__('z', function () {"
664               "  g2(this, 'z');"
665               "});"
666               "X.prototype.__defineSetter__('z', function () {"
667               "  if (deopt) {"
668               "    count++;"
669               "    %DeoptimizeFunction(f1);"
670               "    %DeoptimizeFunction(g1);"
671               "    %DeoptimizeFunction(f2);"
672               "    %DeoptimizeFunction(g2); };"
673               "});");
674    CompileRun(f1_source);
675    CompileRun(g1_source);
676    CompileRun(f2_source);
677    CompileRun(g2_source);
678    CompileRun("for (var i = 0; i < 5; i++) {"
679               "  f1(new X());"
680               "  g1(new X());"
681               "  f2(new X(), 'z');"
682               "  g2(new X(), 'z');"
683               "};");
684
685    // Compile an optimized version of the functions.
686    i::FLAG_always_opt = true;
687    CompileRun(f1_source);
688    CompileRun(g1_source);
689    CompileRun(f2_source);
690    CompileRun(g2_source);
691    CompileRun("f1(new X());");
692    CompileRun("g1(new X());");
693    CompileRun("f2(new X(), 'z');");
694    CompileRun("g2(new X(), 'z');");
695    if (i::V8::UseCrankshaft()) {
696      CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized());
697      CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized());
698      CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized());
699      CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized());
700    }
701
702    // Call functions and force deoptimization while processing the ics.
703    CompileRun("deopt = true;"
704               "var result = f1(new X());"
705               "gc(); gc();");
706  }
707
708  CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized());
709  CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized());
710  CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized());
711  CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized());
712  CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
713  CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value());
714  CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(Isolate::Current()));
715}
716