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