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