• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
41 using ::v8::internal::Deoptimizer;
42 using ::v8::internal::EmbeddedVector;
43 using ::v8::internal::Handle;
44 using ::v8::internal::Isolate;
45 using ::v8::internal::JSFunction;
46 using ::v8::internal::OS;
47 using ::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.
54 class AlwaysOptimizeAllowNativesSyntaxNoInlining {
55  public:
AlwaysOptimizeAllowNativesSyntaxNoInlining()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 
~AlwaysOptimizeAllowNativesSyntaxNoInlining()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.
80 class AllowNativesSyntaxNoInlining {
81  public:
AllowNativesSyntaxNoInlining()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 
~AllowNativesSyntaxNoInlining()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.
NonIncrementalGC()102 static void NonIncrementalGC() {
103   CcTest::heap()->CollectAllGarbage(i::Heap::kAbortIncrementalMarkingMask);
104 }
105 
106 
GetJSFunction(v8::Handle<v8::Object> obj,const char * property_name)107 static 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 
TEST(DeoptimizeSimple)115 TEST(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 
TEST(DeoptimizeSimpleWithArguments)153 TEST(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 
TEST(DeoptimizeSimpleNested)192 TEST(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 
TEST(DeoptimizeRecursive)217 TEST(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 
TEST(DeoptimizeMultiple)244 TEST(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 
TEST(DeoptimizeConstructor)272 TEST(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 
TEST(DeoptimizeConstructorMultiple)310 TEST(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 
TEST(DeoptimizeBinaryOperationADDString)339 TEST(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 
CompileConstructorWithDeoptimizingValueOf()386 static 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 
TestDeoptimizeBinaryOpHelper(LocalContext * env,const char * binary_op)397 static void TestDeoptimizeBinaryOpHelper(LocalContext* env,
398                                          const char* binary_op) {
399   EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> f_source_buffer;
400   OS::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 
TEST(DeoptimizeBinaryOperationADD)430 TEST(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 
TEST(DeoptimizeBinaryOperationSUB)443 TEST(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 
TEST(DeoptimizeBinaryOperationMUL)456 TEST(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 
TEST(DeoptimizeBinaryOperationDIV)469 TEST(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 
TEST(DeoptimizeBinaryOperationMOD)482 TEST(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 
TEST(DeoptimizeCompare)495 TEST(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 
TEST(DeoptimizeLoadICStoreIC)539 TEST(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   CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
617 }
618 
619 
TEST(DeoptimizeLoadICStoreICNested)620 TEST(DeoptimizeLoadICStoreICNested) {
621   i::FLAG_concurrent_recompilation = false;
622   LocalContext env;
623   v8::HandleScope scope(env->GetIsolate());
624 
625   // Functions to generate load/store/keyed load/keyed store IC calls.
626   const char* f1_source = "function f1(x) { return x.y; };";
627   const char* g1_source = "function g1(x) { x.y = 1; };";
628   const char* f2_source = "function f2(x, y) { return x[y]; };";
629   const char* g2_source = "function g2(x, y) { x[y] = 1; };";
630 
631   {
632     AllowNativesSyntaxNoInlining options;
633     // Compile functions and collect to type feedback to insert ic
634     // calls in the optimized code.
635     i::FLAG_prepare_always_opt = true;
636     CompileRun("var count = 0;"
637                "var result = 0;"
638                "var deopt = false;"
639                "function X() { };"
640                "X.prototype.__defineGetter__('y', function () {"
641                "  g1(this);"
642                "  return 13;"
643                "});"
644                "X.prototype.__defineSetter__('y', function () {"
645                "  f2(this, 'z');"
646                "});"
647                "X.prototype.__defineGetter__('z', function () {"
648                "  g2(this, 'z');"
649                "});"
650                "X.prototype.__defineSetter__('z', function () {"
651                "  if (deopt) {"
652                "    count++;"
653                "    %DeoptimizeFunction(f1);"
654                "    %DeoptimizeFunction(g1);"
655                "    %DeoptimizeFunction(f2);"
656                "    %DeoptimizeFunction(g2); };"
657                "});");
658     CompileRun(f1_source);
659     CompileRun(g1_source);
660     CompileRun(f2_source);
661     CompileRun(g2_source);
662     CompileRun("for (var i = 0; i < 5; i++) {"
663                "  f1(new X());"
664                "  g1(new X());"
665                "  f2(new X(), 'z');"
666                "  g2(new X(), 'z');"
667                "};");
668 
669     // Compile an optimized version of the functions.
670     i::FLAG_always_opt = true;
671     CompileRun(f1_source);
672     CompileRun(g1_source);
673     CompileRun(f2_source);
674     CompileRun(g2_source);
675     CompileRun("f1(new X());");
676     CompileRun("g1(new X());");
677     CompileRun("f2(new X(), 'z');");
678     CompileRun("g2(new X(), 'z');");
679     if (CcTest::i_isolate()->use_crankshaft()) {
680       CHECK(GetJSFunction(env->Global(), "f1")->IsOptimized());
681       CHECK(GetJSFunction(env->Global(), "g1")->IsOptimized());
682       CHECK(GetJSFunction(env->Global(), "f2")->IsOptimized());
683       CHECK(GetJSFunction(env->Global(), "g2")->IsOptimized());
684     }
685 
686     // Call functions and force deoptimization while processing the ics.
687     CompileRun("deopt = true;"
688                "var result = f1(new X());");
689   }
690   NonIncrementalGC();
691 
692   CHECK(!GetJSFunction(env->Global(), "f1")->IsOptimized());
693   CHECK(!GetJSFunction(env->Global(), "g1")->IsOptimized());
694   CHECK(!GetJSFunction(env->Global(), "f2")->IsOptimized());
695   CHECK(!GetJSFunction(env->Global(), "g2")->IsOptimized());
696   CHECK_EQ(1, env->Global()->Get(v8_str("count"))->Int32Value());
697   CHECK_EQ(13, env->Global()->Get(v8_str("result"))->Int32Value());
698   CHECK_EQ(0, Deoptimizer::GetDeoptimizedCodeCount(CcTest::i_isolate()));
699 }
700