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