• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 
GetJSFunction(v8::Handle<v8::Object> obj,const char * property_name)100 static Handle<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 
TEST(DeoptimizeSimple)108 TEST(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 
TEST(DeoptimizeSimpleWithArguments)148 TEST(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 
TEST(DeoptimizeSimpleNested)189 TEST(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 
TEST(DeoptimizeRecursive)216 TEST(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 
TEST(DeoptimizeMultiple)244 TEST(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 
TEST(DeoptimizeConstructor)274 TEST(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 
TEST(DeoptimizeConstructorMultiple)314 TEST(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 
TEST(DeoptimizeBinaryOperationADDString)345 TEST(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 
CompileConstructorWithDeoptimizingValueOf()393 static 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 
TestDeoptimizeBinaryOpHelper(LocalContext * env,const char * binary_op)404 static 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 
TEST(DeoptimizeBinaryOperationADD)438 TEST(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 
TEST(DeoptimizeBinaryOperationSUB)452 TEST(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 
TEST(DeoptimizeBinaryOperationMUL)466 TEST(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 
TEST(DeoptimizeBinaryOperationDIV)480 TEST(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 
TEST(DeoptimizeBinaryOperationMOD)494 TEST(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 
TEST(DeoptimizeCompare)508 TEST(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 
TEST(DeoptimizeLoadICStoreIC)553 TEST(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 
TEST(DeoptimizeLoadICStoreICNested)635 TEST(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