OLD | NEW |
| (Empty) |
1 // Copyright 2009 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 "v8.h" | |
29 | |
30 #include "codegen-inl.h" | |
31 #include "compiler.h" | |
32 #include "debug.h" | |
33 #include "fast-codegen.h" | |
34 #include "parser.h" | |
35 | |
36 namespace v8 { | |
37 namespace internal { | |
38 | |
39 #define __ ACCESS_MASM(masm_) | |
40 | |
41 // Generate code for a JS function. On entry to the function the receiver | |
42 // and arguments have been pushed on the stack left to right, with the | |
43 // return address on top of them. The actual argument count matches the | |
44 // formal parameter count expected by the function. | |
45 // | |
46 // The live registers are: | |
47 // o rdi: the JS function object being called (ie, ourselves) | |
48 // o rsi: our context | |
49 // o rbp: our caller's frame pointer | |
50 // o rsp: stack pointer (pointing to return address) | |
51 // | |
52 // The function builds a JS frame. Please see JavaScriptFrameConstants in | |
53 // frames-x64.h for its layout. | |
54 void FullCodeGenerator::Generate(FunctionLiteral* fun) { | |
55 function_ = fun; | |
56 SetFunctionPosition(fun); | |
57 | |
58 __ push(rbp); // Caller's frame pointer. | |
59 __ movq(rbp, rsp); | |
60 __ push(rsi); // Callee's context. | |
61 __ push(rdi); // Callee's JS Function. | |
62 | |
63 { Comment cmnt(masm_, "[ Allocate locals"); | |
64 int locals_count = fun->scope()->num_stack_slots(); | |
65 if (locals_count == 1) { | |
66 __ PushRoot(Heap::kUndefinedValueRootIndex); | |
67 } else if (locals_count > 1) { | |
68 __ LoadRoot(rdx, Heap::kUndefinedValueRootIndex); | |
69 for (int i = 0; i < locals_count; i++) { | |
70 __ push(rdx); | |
71 } | |
72 } | |
73 } | |
74 | |
75 bool function_in_register = true; | |
76 | |
77 // Possibly allocate a local context. | |
78 if (fun->scope()->num_heap_slots() > 0) { | |
79 Comment cmnt(masm_, "[ Allocate local context"); | |
80 // Argument to NewContext is the function, which is still in rdi. | |
81 __ push(rdi); | |
82 __ CallRuntime(Runtime::kNewContext, 1); | |
83 function_in_register = false; | |
84 // Context is returned in both rax and rsi. It replaces the context | |
85 // passed to us. It's saved in the stack and kept live in rsi. | |
86 __ movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi); | |
87 | |
88 // Copy any necessary parameters into the context. | |
89 int num_parameters = fun->scope()->num_parameters(); | |
90 for (int i = 0; i < num_parameters; i++) { | |
91 Slot* slot = fun->scope()->parameter(i)->slot(); | |
92 if (slot != NULL && slot->type() == Slot::CONTEXT) { | |
93 int parameter_offset = StandardFrameConstants::kCallerSPOffset + | |
94 (num_parameters - 1 - i) * kPointerSize; | |
95 // Load parameter from stack. | |
96 __ movq(rax, Operand(rbp, parameter_offset)); | |
97 // Store it in the context | |
98 __ movq(Operand(rsi, Context::SlotOffset(slot->index())), rax); | |
99 } | |
100 } | |
101 } | |
102 | |
103 // Possibly allocate an arguments object. | |
104 Variable* arguments = fun->scope()->arguments()->AsVariable(); | |
105 if (arguments != NULL) { | |
106 // Arguments object must be allocated after the context object, in | |
107 // case the "arguments" or ".arguments" variables are in the context. | |
108 Comment cmnt(masm_, "[ Allocate arguments object"); | |
109 if (function_in_register) { | |
110 __ push(rdi); | |
111 } else { | |
112 __ push(Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
113 } | |
114 // The receiver is just before the parameters on the caller's stack. | |
115 __ lea(rdx, Operand(rbp, StandardFrameConstants::kCallerSPOffset + | |
116 fun->num_parameters() * kPointerSize)); | |
117 __ push(rdx); | |
118 __ Push(Smi::FromInt(fun->num_parameters())); | |
119 // Arguments to ArgumentsAccessStub: | |
120 // function, receiver address, parameter count. | |
121 // The stub will rewrite receiver and parameter count if the previous | |
122 // stack frame was an arguments adapter frame. | |
123 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT); | |
124 __ CallStub(&stub); | |
125 // Store new arguments object in both "arguments" and ".arguments" slots. | |
126 __ movq(rcx, rax); | |
127 Move(arguments->slot(), rax, rbx, rdx); | |
128 Slot* dot_arguments_slot = | |
129 fun->scope()->arguments_shadow()->AsVariable()->slot(); | |
130 Move(dot_arguments_slot, rcx, rbx, rdx); | |
131 } | |
132 | |
133 { Comment cmnt(masm_, "[ Declarations"); | |
134 VisitDeclarations(fun->scope()->declarations()); | |
135 } | |
136 | |
137 { Comment cmnt(masm_, "[ Stack check"); | |
138 Label ok; | |
139 __ CompareRoot(rsp, Heap::kStackLimitRootIndex); | |
140 __ j(above_equal, &ok); | |
141 StackCheckStub stub; | |
142 __ CallStub(&stub); | |
143 __ bind(&ok); | |
144 } | |
145 | |
146 if (FLAG_trace) { | |
147 __ CallRuntime(Runtime::kTraceEnter, 0); | |
148 } | |
149 | |
150 { Comment cmnt(masm_, "[ Body"); | |
151 ASSERT(loop_depth() == 0); | |
152 VisitStatements(fun->body()); | |
153 ASSERT(loop_depth() == 0); | |
154 } | |
155 | |
156 { Comment cmnt(masm_, "[ return <undefined>;"); | |
157 // Emit a 'return undefined' in case control fell off the end of the body. | |
158 __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); | |
159 EmitReturnSequence(function_->end_position()); | |
160 } | |
161 } | |
162 | |
163 | |
164 void FullCodeGenerator::EmitReturnSequence(int position) { | |
165 Comment cmnt(masm_, "[ Return sequence"); | |
166 if (return_label_.is_bound()) { | |
167 __ jmp(&return_label_); | |
168 } else { | |
169 __ bind(&return_label_); | |
170 if (FLAG_trace) { | |
171 __ push(rax); | |
172 __ CallRuntime(Runtime::kTraceExit, 1); | |
173 } | |
174 #ifdef DEBUG | |
175 // Add a label for checking the size of the code used for returning. | |
176 Label check_exit_codesize; | |
177 masm_->bind(&check_exit_codesize); | |
178 #endif | |
179 CodeGenerator::RecordPositions(masm_, position); | |
180 __ RecordJSReturn(); | |
181 // Do not use the leave instruction here because it is too short to | |
182 // patch with the code required by the debugger. | |
183 __ movq(rsp, rbp); | |
184 __ pop(rbp); | |
185 __ ret((function_->scope()->num_parameters() + 1) * kPointerSize); | |
186 #ifdef ENABLE_DEBUGGER_SUPPORT | |
187 // Add padding that will be overwritten by a debugger breakpoint. We | |
188 // have just generated "movq rsp, rbp; pop rbp; ret k" with length 7 | |
189 // (3 + 1 + 3). | |
190 const int kPadding = Assembler::kJSReturnSequenceLength - 7; | |
191 for (int i = 0; i < kPadding; ++i) { | |
192 masm_->int3(); | |
193 } | |
194 // Check that the size of the code used for returning matches what is | |
195 // expected by the debugger. | |
196 ASSERT_EQ(Assembler::kJSReturnSequenceLength, | |
197 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize)); | |
198 #endif | |
199 } | |
200 } | |
201 | |
202 | |
203 void FullCodeGenerator::Apply(Expression::Context context, Register reg) { | |
204 switch (context) { | |
205 case Expression::kUninitialized: | |
206 UNREACHABLE(); | |
207 | |
208 case Expression::kEffect: | |
209 // Nothing to do. | |
210 break; | |
211 | |
212 case Expression::kValue: | |
213 // Move value into place. | |
214 switch (location_) { | |
215 case kAccumulator: | |
216 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
217 break; | |
218 case kStack: | |
219 __ push(reg); | |
220 break; | |
221 } | |
222 break; | |
223 | |
224 case Expression::kTest: | |
225 // For simplicity we always test the accumulator register. | |
226 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
227 DoTest(context); | |
228 break; | |
229 | |
230 case Expression::kValueTest: | |
231 case Expression::kTestValue: | |
232 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
233 switch (location_) { | |
234 case kAccumulator: | |
235 break; | |
236 case kStack: | |
237 __ push(result_register()); | |
238 break; | |
239 } | |
240 DoTest(context); | |
241 break; | |
242 } | |
243 } | |
244 | |
245 | |
246 void FullCodeGenerator::Apply(Expression::Context context, Slot* slot) { | |
247 switch (context) { | |
248 case Expression::kUninitialized: | |
249 UNREACHABLE(); | |
250 case Expression::kEffect: | |
251 // Nothing to do. | |
252 break; | |
253 case Expression::kValue: { | |
254 MemOperand slot_operand = EmitSlotSearch(slot, result_register()); | |
255 switch (location_) { | |
256 case kAccumulator: | |
257 __ movq(result_register(), slot_operand); | |
258 break; | |
259 case kStack: | |
260 // Memory operands can be pushed directly. | |
261 __ push(slot_operand); | |
262 break; | |
263 } | |
264 break; | |
265 } | |
266 | |
267 case Expression::kTest: | |
268 Move(result_register(), slot); | |
269 DoTest(context); | |
270 break; | |
271 | |
272 case Expression::kValueTest: | |
273 case Expression::kTestValue: | |
274 Move(result_register(), slot); | |
275 switch (location_) { | |
276 case kAccumulator: | |
277 break; | |
278 case kStack: | |
279 __ push(result_register()); | |
280 break; | |
281 } | |
282 DoTest(context); | |
283 break; | |
284 } | |
285 } | |
286 | |
287 | |
288 void FullCodeGenerator::Apply(Expression::Context context, Literal* lit) { | |
289 switch (context) { | |
290 case Expression::kUninitialized: | |
291 UNREACHABLE(); | |
292 case Expression::kEffect: | |
293 // Nothing to do. | |
294 break; | |
295 case Expression::kValue: | |
296 switch (location_) { | |
297 case kAccumulator: | |
298 __ Move(result_register(), lit->handle()); | |
299 break; | |
300 case kStack: | |
301 __ Push(lit->handle()); | |
302 break; | |
303 } | |
304 break; | |
305 | |
306 case Expression::kTest: | |
307 __ Move(result_register(), lit->handle()); | |
308 DoTest(context); | |
309 break; | |
310 | |
311 case Expression::kValueTest: | |
312 case Expression::kTestValue: | |
313 __ Move(result_register(), lit->handle()); | |
314 switch (location_) { | |
315 case kAccumulator: | |
316 break; | |
317 case kStack: | |
318 __ push(result_register()); | |
319 break; | |
320 } | |
321 DoTest(context); | |
322 break; | |
323 } | |
324 } | |
325 | |
326 | |
327 void FullCodeGenerator::ApplyTOS(Expression::Context context) { | |
328 switch (context) { | |
329 case Expression::kUninitialized: | |
330 UNREACHABLE(); | |
331 | |
332 case Expression::kEffect: | |
333 __ Drop(1); | |
334 break; | |
335 | |
336 case Expression::kValue: | |
337 switch (location_) { | |
338 case kAccumulator: | |
339 __ pop(result_register()); | |
340 break; | |
341 case kStack: | |
342 break; | |
343 } | |
344 break; | |
345 | |
346 case Expression::kTest: | |
347 __ pop(result_register()); | |
348 DoTest(context); | |
349 break; | |
350 | |
351 case Expression::kValueTest: | |
352 case Expression::kTestValue: | |
353 switch (location_) { | |
354 case kAccumulator: | |
355 __ pop(result_register()); | |
356 break; | |
357 case kStack: | |
358 __ movq(result_register(), Operand(rsp, 0)); | |
359 break; | |
360 } | |
361 DoTest(context); | |
362 break; | |
363 } | |
364 } | |
365 | |
366 | |
367 void FullCodeGenerator::DropAndApply(int count, | |
368 Expression::Context context, | |
369 Register reg) { | |
370 ASSERT(count > 0); | |
371 ASSERT(!reg.is(rsp)); | |
372 switch (context) { | |
373 case Expression::kUninitialized: | |
374 UNREACHABLE(); | |
375 | |
376 case Expression::kEffect: | |
377 __ Drop(count); | |
378 break; | |
379 | |
380 case Expression::kValue: | |
381 switch (location_) { | |
382 case kAccumulator: | |
383 __ Drop(count); | |
384 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
385 break; | |
386 case kStack: | |
387 if (count > 1) __ Drop(count - 1); | |
388 __ movq(Operand(rsp, 0), reg); | |
389 break; | |
390 } | |
391 break; | |
392 | |
393 case Expression::kTest: | |
394 __ Drop(count); | |
395 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
396 DoTest(context); | |
397 break; | |
398 | |
399 case Expression::kValueTest: | |
400 case Expression::kTestValue: | |
401 switch (location_) { | |
402 case kAccumulator: | |
403 __ Drop(count); | |
404 if (!reg.is(result_register())) __ movq(result_register(), reg); | |
405 break; | |
406 case kStack: | |
407 if (count > 1) __ Drop(count - 1); | |
408 __ movq(result_register(), reg); | |
409 __ movq(Operand(rsp, 0), result_register()); | |
410 break; | |
411 } | |
412 DoTest(context); | |
413 break; | |
414 } | |
415 } | |
416 | |
417 | |
418 void FullCodeGenerator::Apply(Expression::Context context, | |
419 Label* materialize_true, | |
420 Label* materialize_false) { | |
421 switch (context) { | |
422 case Expression::kUninitialized: | |
423 | |
424 case Expression::kEffect: | |
425 ASSERT_EQ(materialize_true, materialize_false); | |
426 __ bind(materialize_true); | |
427 break; | |
428 | |
429 case Expression::kValue: { | |
430 Label done; | |
431 switch (location_) { | |
432 case kAccumulator: | |
433 __ bind(materialize_true); | |
434 __ Move(result_register(), Factory::true_value()); | |
435 __ jmp(&done); | |
436 __ bind(materialize_false); | |
437 __ Move(result_register(), Factory::false_value()); | |
438 break; | |
439 case kStack: | |
440 __ bind(materialize_true); | |
441 __ Push(Factory::true_value()); | |
442 __ jmp(&done); | |
443 __ bind(materialize_false); | |
444 __ Push(Factory::false_value()); | |
445 break; | |
446 } | |
447 __ bind(&done); | |
448 break; | |
449 } | |
450 | |
451 case Expression::kTest: | |
452 break; | |
453 | |
454 case Expression::kValueTest: | |
455 __ bind(materialize_true); | |
456 switch (location_) { | |
457 case kAccumulator: | |
458 __ Move(result_register(), Factory::true_value()); | |
459 break; | |
460 case kStack: | |
461 __ Push(Factory::true_value()); | |
462 break; | |
463 } | |
464 __ jmp(true_label_); | |
465 break; | |
466 | |
467 case Expression::kTestValue: | |
468 __ bind(materialize_false); | |
469 switch (location_) { | |
470 case kAccumulator: | |
471 __ Move(result_register(), Factory::false_value()); | |
472 break; | |
473 case kStack: | |
474 __ Push(Factory::false_value()); | |
475 break; | |
476 } | |
477 __ jmp(false_label_); | |
478 break; | |
479 } | |
480 } | |
481 | |
482 | |
483 void FullCodeGenerator::DoTest(Expression::Context context) { | |
484 // The value to test is in the accumulator. If the value might be needed | |
485 // on the stack (value/test and test/value contexts with a stack location | |
486 // desired), then the value is already duplicated on the stack. | |
487 ASSERT_NE(NULL, true_label_); | |
488 ASSERT_NE(NULL, false_label_); | |
489 | |
490 // In value/test and test/value expression contexts with stack as the | |
491 // desired location, there is already an extra value on the stack. Use a | |
492 // label to discard it if unneeded. | |
493 Label discard; | |
494 Label* if_true = true_label_; | |
495 Label* if_false = false_label_; | |
496 switch (context) { | |
497 case Expression::kUninitialized: | |
498 case Expression::kEffect: | |
499 case Expression::kValue: | |
500 UNREACHABLE(); | |
501 case Expression::kTest: | |
502 break; | |
503 case Expression::kValueTest: | |
504 switch (location_) { | |
505 case kAccumulator: | |
506 break; | |
507 case kStack: | |
508 if_false = &discard; | |
509 break; | |
510 } | |
511 break; | |
512 case Expression::kTestValue: | |
513 switch (location_) { | |
514 case kAccumulator: | |
515 break; | |
516 case kStack: | |
517 if_true = &discard; | |
518 break; | |
519 } | |
520 break; | |
521 } | |
522 | |
523 // Emit the inlined tests assumed by the stub. | |
524 __ CompareRoot(result_register(), Heap::kUndefinedValueRootIndex); | |
525 __ j(equal, if_false); | |
526 __ CompareRoot(result_register(), Heap::kTrueValueRootIndex); | |
527 __ j(equal, if_true); | |
528 __ CompareRoot(result_register(), Heap::kFalseValueRootIndex); | |
529 __ j(equal, if_false); | |
530 ASSERT_EQ(0, kSmiTag); | |
531 __ SmiCompare(result_register(), Smi::FromInt(0)); | |
532 __ j(equal, if_false); | |
533 Condition is_smi = masm_->CheckSmi(result_register()); | |
534 __ j(is_smi, if_true); | |
535 | |
536 // Save a copy of the value if it may be needed and isn't already saved. | |
537 switch (context) { | |
538 case Expression::kUninitialized: | |
539 case Expression::kEffect: | |
540 case Expression::kValue: | |
541 UNREACHABLE(); | |
542 case Expression::kTest: | |
543 break; | |
544 case Expression::kValueTest: | |
545 switch (location_) { | |
546 case kAccumulator: | |
547 __ push(result_register()); | |
548 break; | |
549 case kStack: | |
550 break; | |
551 } | |
552 break; | |
553 case Expression::kTestValue: | |
554 switch (location_) { | |
555 case kAccumulator: | |
556 __ push(result_register()); | |
557 break; | |
558 case kStack: | |
559 break; | |
560 } | |
561 break; | |
562 } | |
563 | |
564 // Call the ToBoolean stub for all other cases. | |
565 ToBooleanStub stub; | |
566 __ push(result_register()); | |
567 __ CallStub(&stub); | |
568 __ testq(rax, rax); | |
569 | |
570 // The stub returns nonzero for true. Complete based on the context. | |
571 switch (context) { | |
572 case Expression::kUninitialized: | |
573 case Expression::kEffect: | |
574 case Expression::kValue: | |
575 UNREACHABLE(); | |
576 | |
577 case Expression::kTest: | |
578 __ j(not_zero, true_label_); | |
579 __ jmp(false_label_); | |
580 break; | |
581 | |
582 case Expression::kValueTest: | |
583 switch (location_) { | |
584 case kAccumulator: | |
585 __ j(zero, &discard); | |
586 __ pop(result_register()); | |
587 __ jmp(true_label_); | |
588 break; | |
589 case kStack: | |
590 __ j(not_zero, true_label_); | |
591 break; | |
592 } | |
593 __ bind(&discard); | |
594 __ Drop(1); | |
595 __ jmp(false_label_); | |
596 break; | |
597 | |
598 case Expression::kTestValue: | |
599 switch (location_) { | |
600 case kAccumulator: | |
601 __ j(not_zero, &discard); | |
602 __ pop(result_register()); | |
603 __ jmp(false_label_); | |
604 break; | |
605 case kStack: | |
606 __ j(zero, false_label_); | |
607 break; | |
608 } | |
609 __ bind(&discard); | |
610 __ Drop(1); | |
611 __ jmp(true_label_); | |
612 break; | |
613 } | |
614 } | |
615 | |
616 | |
617 MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { | |
618 switch (slot->type()) { | |
619 case Slot::PARAMETER: | |
620 case Slot::LOCAL: | |
621 return Operand(rbp, SlotOffset(slot)); | |
622 case Slot::CONTEXT: { | |
623 int context_chain_length = | |
624 function_->scope()->ContextChainLength(slot->var()->scope()); | |
625 __ LoadContext(scratch, context_chain_length); | |
626 return CodeGenerator::ContextOperand(scratch, slot->index()); | |
627 } | |
628 case Slot::LOOKUP: | |
629 UNREACHABLE(); | |
630 } | |
631 UNREACHABLE(); | |
632 return Operand(rax, 0); | |
633 } | |
634 | |
635 | |
636 void FullCodeGenerator::Move(Register destination, Slot* source) { | |
637 MemOperand location = EmitSlotSearch(source, destination); | |
638 __ movq(destination, location); | |
639 } | |
640 | |
641 | |
642 void FullCodeGenerator::Move(Slot* dst, | |
643 Register src, | |
644 Register scratch1, | |
645 Register scratch2) { | |
646 ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented. | |
647 ASSERT(!scratch1.is(src) && !scratch2.is(src)); | |
648 MemOperand location = EmitSlotSearch(dst, scratch1); | |
649 __ movq(location, src); | |
650 // Emit the write barrier code if the location is in the heap. | |
651 if (dst->type() == Slot::CONTEXT) { | |
652 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; | |
653 __ RecordWrite(scratch1, offset, src, scratch2); | |
654 } | |
655 } | |
656 | |
657 | |
658 void FullCodeGenerator::VisitDeclaration(Declaration* decl) { | |
659 Comment cmnt(masm_, "[ Declaration"); | |
660 Variable* var = decl->proxy()->var(); | |
661 ASSERT(var != NULL); // Must have been resolved. | |
662 Slot* slot = var->slot(); | |
663 Property* prop = var->AsProperty(); | |
664 | |
665 if (slot != NULL) { | |
666 switch (slot->type()) { | |
667 case Slot::PARAMETER: | |
668 case Slot::LOCAL: | |
669 if (decl->mode() == Variable::CONST) { | |
670 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | |
671 __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); | |
672 } else if (decl->fun() != NULL) { | |
673 VisitForValue(decl->fun(), kAccumulator); | |
674 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | |
675 } | |
676 break; | |
677 | |
678 case Slot::CONTEXT: | |
679 // We bypass the general EmitSlotSearch because we know more about | |
680 // this specific context. | |
681 | |
682 // The variable in the decl always resides in the current context. | |
683 ASSERT_EQ(0, function_->scope()->ContextChainLength(var->scope())); | |
684 if (FLAG_debug_code) { | |
685 // Check if we have the correct context pointer. | |
686 __ movq(rbx, | |
687 CodeGenerator::ContextOperand(rsi, Context::FCONTEXT_INDEX)); | |
688 __ cmpq(rbx, rsi); | |
689 __ Check(equal, "Unexpected declaration in current context."); | |
690 } | |
691 if (decl->mode() == Variable::CONST) { | |
692 __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); | |
693 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | |
694 kScratchRegister); | |
695 // No write barrier since the hole value is in old space. | |
696 } else if (decl->fun() != NULL) { | |
697 VisitForValue(decl->fun(), kAccumulator); | |
698 __ movq(CodeGenerator::ContextOperand(rsi, slot->index()), | |
699 result_register()); | |
700 int offset = Context::SlotOffset(slot->index()); | |
701 __ RecordWrite(rsi, offset, result_register(), rcx); | |
702 } | |
703 break; | |
704 | |
705 case Slot::LOOKUP: { | |
706 __ push(rsi); | |
707 __ Push(var->name()); | |
708 // Declaration nodes are always introduced in one of two modes. | |
709 ASSERT(decl->mode() == Variable::VAR || | |
710 decl->mode() == Variable::CONST); | |
711 PropertyAttributes attr = | |
712 (decl->mode() == Variable::VAR) ? NONE : READ_ONLY; | |
713 __ Push(Smi::FromInt(attr)); | |
714 // Push initial value, if any. | |
715 // Note: For variables we must not push an initial value (such as | |
716 // 'undefined') because we may have a (legal) redeclaration and we | |
717 // must not destroy the current value. | |
718 if (decl->mode() == Variable::CONST) { | |
719 __ PushRoot(Heap::kTheHoleValueRootIndex); | |
720 } else if (decl->fun() != NULL) { | |
721 VisitForValue(decl->fun(), kStack); | |
722 } else { | |
723 __ Push(Smi::FromInt(0)); // no initial value! | |
724 } | |
725 __ CallRuntime(Runtime::kDeclareContextSlot, 4); | |
726 break; | |
727 } | |
728 } | |
729 | |
730 } else if (prop != NULL) { | |
731 if (decl->fun() != NULL || decl->mode() == Variable::CONST) { | |
732 // We are declaring a function or constant that rewrites to a | |
733 // property. Use (keyed) IC to set the initial value. | |
734 VisitForValue(prop->obj(), kStack); | |
735 VisitForValue(prop->key(), kStack); | |
736 | |
737 if (decl->fun() != NULL) { | |
738 VisitForValue(decl->fun(), kAccumulator); | |
739 } else { | |
740 __ LoadRoot(result_register(), Heap::kTheHoleValueRootIndex); | |
741 } | |
742 | |
743 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
744 __ call(ic, RelocInfo::CODE_TARGET); | |
745 // Absence of a test rax instruction following the call | |
746 // indicates that none of the load was inlined. | |
747 __ nop(); | |
748 | |
749 // Value in rax is ignored (declarations are statements). Receiver | |
750 // and key on stack are discarded. | |
751 __ Drop(2); | |
752 } | |
753 } | |
754 } | |
755 | |
756 | |
757 void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { | |
758 // Call the runtime to declare the globals. | |
759 __ push(rsi); // The context is the first argument. | |
760 __ Push(pairs); | |
761 __ Push(Smi::FromInt(is_eval_ ? 1 : 0)); | |
762 __ CallRuntime(Runtime::kDeclareGlobals, 3); | |
763 // Return value is ignored. | |
764 } | |
765 | |
766 | |
767 void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { | |
768 Comment cmnt(masm_, "[ FunctionLiteral"); | |
769 | |
770 // Build the function boilerplate and instantiate it. | |
771 Handle<JSFunction> boilerplate = | |
772 Compiler::BuildBoilerplate(expr, script_, this); | |
773 if (HasStackOverflow()) return; | |
774 | |
775 ASSERT(boilerplate->IsBoilerplate()); | |
776 | |
777 // Create a new closure. | |
778 __ push(rsi); | |
779 __ Push(boilerplate); | |
780 __ CallRuntime(Runtime::kNewClosure, 2); | |
781 Apply(context_, rax); | |
782 } | |
783 | |
784 | |
785 void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { | |
786 Comment cmnt(masm_, "[ VariableProxy"); | |
787 EmitVariableLoad(expr->var(), context_); | |
788 } | |
789 | |
790 | |
791 void FullCodeGenerator::EmitVariableLoad(Variable* var, | |
792 Expression::Context context) { | |
793 Expression* rewrite = var->rewrite(); | |
794 if (rewrite == NULL) { | |
795 ASSERT(var->is_global()); | |
796 Comment cmnt(masm_, "Global variable"); | |
797 // Use inline caching. Variable name is passed in rcx and the global | |
798 // object on the stack. | |
799 __ push(CodeGenerator::GlobalObject()); | |
800 __ Move(rcx, var->name()); | |
801 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | |
802 __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); | |
803 // A test rax instruction following the call is used by the IC to | |
804 // indicate that the inobject property case was inlined. Ensure there | |
805 // is no test rax instruction here. | |
806 __ nop(); | |
807 DropAndApply(1, context, rax); | |
808 } else if (rewrite->AsSlot() != NULL) { | |
809 Slot* slot = rewrite->AsSlot(); | |
810 if (FLAG_debug_code) { | |
811 switch (slot->type()) { | |
812 case Slot::PARAMETER: | |
813 case Slot::LOCAL: { | |
814 Comment cmnt(masm_, "Stack slot"); | |
815 break; | |
816 } | |
817 case Slot::CONTEXT: { | |
818 Comment cmnt(masm_, "Context slot"); | |
819 break; | |
820 } | |
821 case Slot::LOOKUP: | |
822 UNIMPLEMENTED(); | |
823 break; | |
824 } | |
825 } | |
826 Apply(context, slot); | |
827 } else { | |
828 Comment cmnt(masm_, "Variable rewritten to property"); | |
829 // A variable has been rewritten into an explicit access to an object | |
830 // property. | |
831 Property* property = rewrite->AsProperty(); | |
832 ASSERT_NOT_NULL(property); | |
833 | |
834 // The only property expressions that can occur are of the form | |
835 // "slot[literal]". | |
836 | |
837 // Assert that the object is in a slot. | |
838 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable(); | |
839 ASSERT_NOT_NULL(object_var); | |
840 Slot* object_slot = object_var->slot(); | |
841 ASSERT_NOT_NULL(object_slot); | |
842 | |
843 // Load the object. | |
844 MemOperand object_loc = EmitSlotSearch(object_slot, rax); | |
845 __ push(object_loc); | |
846 | |
847 // Assert that the key is a smi. | |
848 Literal* key_literal = property->key()->AsLiteral(); | |
849 ASSERT_NOT_NULL(key_literal); | |
850 ASSERT(key_literal->handle()->IsSmi()); | |
851 | |
852 // Load the key. | |
853 __ Push(key_literal->handle()); | |
854 | |
855 // Do a keyed property load. | |
856 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
857 __ call(ic, RelocInfo::CODE_TARGET); | |
858 // Notice: We must not have a "test rax, ..." instruction after the | |
859 // call. It is treated specially by the LoadIC code. | |
860 __ nop(); | |
861 // Drop key and object left on the stack by IC, and push the result. | |
862 DropAndApply(2, context, rax); | |
863 } | |
864 } | |
865 | |
866 | |
867 void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { | |
868 Comment cmnt(masm_, "[ RegExpLiteral"); | |
869 Label done; | |
870 // Registers will be used as follows: | |
871 // rdi = JS function. | |
872 // rbx = literals array. | |
873 // rax = regexp literal. | |
874 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
875 __ movq(rbx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); | |
876 int literal_offset = | |
877 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; | |
878 __ movq(rax, FieldOperand(rbx, literal_offset)); | |
879 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); | |
880 __ j(not_equal, &done); | |
881 // Create regexp literal using runtime function | |
882 // Result will be in rax. | |
883 __ push(rbx); | |
884 __ Push(Smi::FromInt(expr->literal_index())); | |
885 __ Push(expr->pattern()); | |
886 __ Push(expr->flags()); | |
887 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4); | |
888 __ bind(&done); | |
889 Apply(context_, rax); | |
890 } | |
891 | |
892 | |
893 void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { | |
894 Comment cmnt(masm_, "[ ObjectLiteral"); | |
895 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
896 __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset)); | |
897 __ Push(Smi::FromInt(expr->literal_index())); | |
898 __ Push(expr->constant_properties()); | |
899 if (expr->depth() > 1) { | |
900 __ CallRuntime(Runtime::kCreateObjectLiteral, 3); | |
901 } else { | |
902 __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3); | |
903 } | |
904 | |
905 // If result_saved is true the result is on top of the stack. If | |
906 // result_saved is false the result is in rax. | |
907 bool result_saved = false; | |
908 | |
909 for (int i = 0; i < expr->properties()->length(); i++) { | |
910 ObjectLiteral::Property* property = expr->properties()->at(i); | |
911 if (property->IsCompileTimeValue()) continue; | |
912 | |
913 Literal* key = property->key(); | |
914 Expression* value = property->value(); | |
915 if (!result_saved) { | |
916 __ push(rax); // Save result on the stack | |
917 result_saved = true; | |
918 } | |
919 switch (property->kind()) { | |
920 case ObjectLiteral::Property::CONSTANT: | |
921 UNREACHABLE(); | |
922 case ObjectLiteral::Property::MATERIALIZED_LITERAL: | |
923 ASSERT(!CompileTimeValue::IsCompileTimeValue(value)); | |
924 // Fall through. | |
925 case ObjectLiteral::Property::COMPUTED: | |
926 if (key->handle()->IsSymbol()) { | |
927 VisitForValue(value, kAccumulator); | |
928 __ Move(rcx, key->handle()); | |
929 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
930 __ call(ic, RelocInfo::CODE_TARGET); | |
931 __ nop(); | |
932 // StoreIC leaves the receiver on the stack. | |
933 break; | |
934 } | |
935 // Fall through. | |
936 case ObjectLiteral::Property::PROTOTYPE: | |
937 __ push(Operand(rsp, 0)); // Duplicate receiver. | |
938 VisitForValue(key, kStack); | |
939 VisitForValue(value, kStack); | |
940 __ CallRuntime(Runtime::kSetProperty, 3); | |
941 break; | |
942 case ObjectLiteral::Property::SETTER: | |
943 case ObjectLiteral::Property::GETTER: | |
944 __ push(Operand(rsp, 0)); // Duplicate receiver. | |
945 VisitForValue(key, kStack); | |
946 __ Push(property->kind() == ObjectLiteral::Property::SETTER ? | |
947 Smi::FromInt(1) : | |
948 Smi::FromInt(0)); | |
949 VisitForValue(value, kStack); | |
950 __ CallRuntime(Runtime::kDefineAccessor, 4); | |
951 break; | |
952 } | |
953 } | |
954 | |
955 if (result_saved) { | |
956 ApplyTOS(context_); | |
957 } else { | |
958 Apply(context_, rax); | |
959 } | |
960 } | |
961 | |
962 | |
963 void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { | |
964 Comment cmnt(masm_, "[ ArrayLiteral"); | |
965 __ movq(rbx, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
966 __ push(FieldOperand(rbx, JSFunction::kLiteralsOffset)); | |
967 __ Push(Smi::FromInt(expr->literal_index())); | |
968 __ Push(expr->constant_elements()); | |
969 if (expr->depth() > 1) { | |
970 __ CallRuntime(Runtime::kCreateArrayLiteral, 3); | |
971 } else { | |
972 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); | |
973 } | |
974 | |
975 bool result_saved = false; // Is the result saved to the stack? | |
976 | |
977 // Emit code to evaluate all the non-constant subexpressions and to store | |
978 // them into the newly cloned array. | |
979 ZoneList<Expression*>* subexprs = expr->values(); | |
980 for (int i = 0, len = subexprs->length(); i < len; i++) { | |
981 Expression* subexpr = subexprs->at(i); | |
982 // If the subexpression is a literal or a simple materialized literal it | |
983 // is already set in the cloned array. | |
984 if (subexpr->AsLiteral() != NULL || | |
985 CompileTimeValue::IsCompileTimeValue(subexpr)) { | |
986 continue; | |
987 } | |
988 | |
989 if (!result_saved) { | |
990 __ push(rax); | |
991 result_saved = true; | |
992 } | |
993 VisitForValue(subexpr, kAccumulator); | |
994 | |
995 // Store the subexpression value in the array's elements. | |
996 __ movq(rbx, Operand(rsp, 0)); // Copy of array literal. | |
997 __ movq(rbx, FieldOperand(rbx, JSObject::kElementsOffset)); | |
998 int offset = FixedArray::kHeaderSize + (i * kPointerSize); | |
999 __ movq(FieldOperand(rbx, offset), result_register()); | |
1000 | |
1001 // Update the write barrier for the array store. | |
1002 __ RecordWrite(rbx, offset, result_register(), rcx); | |
1003 } | |
1004 | |
1005 if (result_saved) { | |
1006 ApplyTOS(context_); | |
1007 } else { | |
1008 Apply(context_, rax); | |
1009 } | |
1010 } | |
1011 | |
1012 | |
1013 void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { | |
1014 SetSourcePosition(prop->position()); | |
1015 Literal* key = prop->key()->AsLiteral(); | |
1016 __ Move(rcx, key->handle()); | |
1017 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | |
1018 __ Call(ic, RelocInfo::CODE_TARGET); | |
1019 __ nop(); | |
1020 } | |
1021 | |
1022 | |
1023 void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { | |
1024 SetSourcePosition(prop->position()); | |
1025 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
1026 __ Call(ic, RelocInfo::CODE_TARGET); | |
1027 __ nop(); | |
1028 } | |
1029 | |
1030 | |
1031 void FullCodeGenerator::EmitBinaryOp(Token::Value op, | |
1032 Expression::Context context) { | |
1033 __ push(result_register()); | |
1034 GenericBinaryOpStub stub(op, | |
1035 NO_OVERWRITE, | |
1036 NO_GENERIC_BINARY_FLAGS); | |
1037 __ CallStub(&stub); | |
1038 Apply(context, rax); | |
1039 } | |
1040 | |
1041 | |
1042 void FullCodeGenerator::EmitVariableAssignment(Variable* var, | |
1043 Expression::Context context) { | |
1044 ASSERT(var != NULL); | |
1045 ASSERT(var->is_global() || var->slot() != NULL); | |
1046 if (var->is_global()) { | |
1047 // Assignment to a global variable. Use inline caching for the | |
1048 // assignment. Right-hand-side value is passed in rax, variable name in | |
1049 // rcx, and the global object on the stack. | |
1050 __ Move(rcx, var->name()); | |
1051 __ push(CodeGenerator::GlobalObject()); | |
1052 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
1053 __ Call(ic, RelocInfo::CODE_TARGET); | |
1054 // Overwrite the global object on the stack with the result if needed. | |
1055 DropAndApply(1, context, rax); | |
1056 | |
1057 } else if (var->slot() != NULL) { | |
1058 Slot* slot = var->slot(); | |
1059 switch (slot->type()) { | |
1060 case Slot::LOCAL: | |
1061 case Slot::PARAMETER: | |
1062 __ movq(Operand(rbp, SlotOffset(slot)), result_register()); | |
1063 break; | |
1064 | |
1065 case Slot::CONTEXT: { | |
1066 MemOperand target = EmitSlotSearch(slot, rcx); | |
1067 __ movq(target, result_register()); | |
1068 | |
1069 // RecordWrite may destroy all its register arguments. | |
1070 __ movq(rdx, result_register()); | |
1071 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; | |
1072 __ RecordWrite(rcx, offset, rdx, rbx); | |
1073 break; | |
1074 } | |
1075 | |
1076 case Slot::LOOKUP: | |
1077 UNREACHABLE(); | |
1078 break; | |
1079 } | |
1080 Apply(context, result_register()); | |
1081 } else { | |
1082 // Variables rewritten as properties are not treated as variables in | |
1083 // assignments. | |
1084 UNREACHABLE(); | |
1085 } | |
1086 } | |
1087 | |
1088 | |
1089 void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { | |
1090 // Assignment to a property, using a named store IC. | |
1091 Property* prop = expr->target()->AsProperty(); | |
1092 ASSERT(prop != NULL); | |
1093 ASSERT(prop->key()->AsLiteral() != NULL); | |
1094 | |
1095 // If the assignment starts a block of assignments to the same object, | |
1096 // change to slow case to avoid the quadratic behavior of repeatedly | |
1097 // adding fast properties. | |
1098 if (expr->starts_initialization_block()) { | |
1099 __ push(result_register()); | |
1100 __ push(Operand(rsp, kPointerSize)); // Receiver is now under value. | |
1101 __ CallRuntime(Runtime::kToSlowProperties, 1); | |
1102 __ pop(result_register()); | |
1103 } | |
1104 | |
1105 // Record source code position before IC call. | |
1106 SetSourcePosition(expr->position()); | |
1107 __ Move(rcx, prop->key()->AsLiteral()->handle()); | |
1108 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
1109 __ Call(ic, RelocInfo::CODE_TARGET); | |
1110 __ nop(); | |
1111 | |
1112 // If the assignment ends an initialization block, revert to fast case. | |
1113 if (expr->ends_initialization_block()) { | |
1114 __ push(rax); // Result of assignment, saved even if not needed. | |
1115 __ push(Operand(rsp, kPointerSize)); // Receiver is under value. | |
1116 __ CallRuntime(Runtime::kToFastProperties, 1); | |
1117 __ pop(rax); | |
1118 } | |
1119 | |
1120 DropAndApply(1, context_, rax); | |
1121 } | |
1122 | |
1123 | |
1124 void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { | |
1125 // Assignment to a property, using a keyed store IC. | |
1126 | |
1127 // If the assignment starts a block of assignments to the same object, | |
1128 // change to slow case to avoid the quadratic behavior of repeatedly | |
1129 // adding fast properties. | |
1130 if (expr->starts_initialization_block()) { | |
1131 __ push(result_register()); | |
1132 // Receiver is now under the key and value. | |
1133 __ push(Operand(rsp, 2 * kPointerSize)); | |
1134 __ CallRuntime(Runtime::kToSlowProperties, 1); | |
1135 __ pop(result_register()); | |
1136 } | |
1137 | |
1138 // Record source code position before IC call. | |
1139 SetSourcePosition(expr->position()); | |
1140 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
1141 __ Call(ic, RelocInfo::CODE_TARGET); | |
1142 // This nop signals to the IC that there is no inlined code at the call | |
1143 // site for it to patch. | |
1144 __ nop(); | |
1145 | |
1146 // If the assignment ends an initialization block, revert to fast case. | |
1147 if (expr->ends_initialization_block()) { | |
1148 __ push(rax); // Result of assignment, saved even if not needed. | |
1149 // Receiver is under the key and value. | |
1150 __ push(Operand(rsp, 2 * kPointerSize)); | |
1151 __ CallRuntime(Runtime::kToFastProperties, 1); | |
1152 __ pop(rax); | |
1153 } | |
1154 | |
1155 // Receiver and key are still on stack. | |
1156 DropAndApply(2, context_, rax); | |
1157 } | |
1158 | |
1159 | |
1160 void FullCodeGenerator::VisitProperty(Property* expr) { | |
1161 Comment cmnt(masm_, "[ Property"); | |
1162 Expression* key = expr->key(); | |
1163 | |
1164 // Evaluate receiver. | |
1165 VisitForValue(expr->obj(), kStack); | |
1166 | |
1167 if (key->IsPropertyName()) { | |
1168 EmitNamedPropertyLoad(expr); | |
1169 // Drop receiver left on the stack by IC. | |
1170 DropAndApply(1, context_, rax); | |
1171 } else { | |
1172 VisitForValue(expr->key(), kStack); | |
1173 EmitKeyedPropertyLoad(expr); | |
1174 // Drop key and receiver left on the stack by IC. | |
1175 DropAndApply(2, context_, rax); | |
1176 } | |
1177 } | |
1178 | |
1179 | |
1180 void FullCodeGenerator::EmitCallWithIC(Call* expr, | |
1181 Handle<Object> ignored, | |
1182 RelocInfo::Mode mode) { | |
1183 // Code common for calls using the IC. | |
1184 ZoneList<Expression*>* args = expr->arguments(); | |
1185 int arg_count = args->length(); | |
1186 for (int i = 0; i < arg_count; i++) { | |
1187 VisitForValue(args->at(i), kStack); | |
1188 } | |
1189 // Record source position for debugger. | |
1190 SetSourcePosition(expr->position()); | |
1191 // Call the IC initialization code. | |
1192 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; | |
1193 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, | |
1194 in_loop); | |
1195 __ Call(ic, mode); | |
1196 // Restore context register. | |
1197 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | |
1198 // Discard the function left on TOS. | |
1199 DropAndApply(1, context_, rax); | |
1200 } | |
1201 | |
1202 | |
1203 void FullCodeGenerator::EmitCallWithStub(Call* expr) { | |
1204 // Code common for calls using the call stub. | |
1205 ZoneList<Expression*>* args = expr->arguments(); | |
1206 int arg_count = args->length(); | |
1207 for (int i = 0; i < arg_count; i++) { | |
1208 VisitForValue(args->at(i), kStack); | |
1209 } | |
1210 // Record source position for debugger. | |
1211 SetSourcePosition(expr->position()); | |
1212 CallFunctionStub stub(arg_count, NOT_IN_LOOP, RECEIVER_MIGHT_BE_VALUE); | |
1213 __ CallStub(&stub); | |
1214 // Restore context register. | |
1215 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | |
1216 // Discard the function left on TOS. | |
1217 DropAndApply(1, context_, rax); | |
1218 } | |
1219 | |
1220 | |
1221 void FullCodeGenerator::VisitCall(Call* expr) { | |
1222 Comment cmnt(masm_, "[ Call"); | |
1223 Expression* fun = expr->expression(); | |
1224 Variable* var = fun->AsVariableProxy()->AsVariable(); | |
1225 | |
1226 if (var != NULL && var->is_possibly_eval()) { | |
1227 // Call to the identifier 'eval'. | |
1228 UNREACHABLE(); | |
1229 } else if (var != NULL && !var->is_this() && var->is_global()) { | |
1230 // Call to a global variable. | |
1231 __ Push(var->name()); | |
1232 // Push global object as receiver for the call IC lookup. | |
1233 __ push(CodeGenerator::GlobalObject()); | |
1234 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); | |
1235 } else if (var != NULL && var->slot() != NULL && | |
1236 var->slot()->type() == Slot::LOOKUP) { | |
1237 // Call to a lookup slot. | |
1238 UNREACHABLE(); | |
1239 } else if (fun->AsProperty() != NULL) { | |
1240 // Call to an object property. | |
1241 Property* prop = fun->AsProperty(); | |
1242 Literal* key = prop->key()->AsLiteral(); | |
1243 if (key != NULL && key->handle()->IsSymbol()) { | |
1244 // Call to a named property, use call IC. | |
1245 __ Push(key->handle()); | |
1246 VisitForValue(prop->obj(), kStack); | |
1247 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); | |
1248 } else { | |
1249 // Call to a keyed property, use keyed load IC followed by function | |
1250 // call. | |
1251 VisitForValue(prop->obj(), kStack); | |
1252 VisitForValue(prop->key(), kStack); | |
1253 // Record source code position for IC call. | |
1254 SetSourcePosition(prop->position()); | |
1255 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
1256 __ call(ic, RelocInfo::CODE_TARGET); | |
1257 // By emitting a nop we make sure that we do not have a "test rax,..." | |
1258 // instruction after the call it is treated specially by the LoadIC code. | |
1259 __ nop(); | |
1260 // Drop key left on the stack by IC. | |
1261 __ Drop(1); | |
1262 // Pop receiver. | |
1263 __ pop(rbx); | |
1264 // Push result (function). | |
1265 __ push(rax); | |
1266 // Push receiver object on stack. | |
1267 if (prop->is_synthetic()) { | |
1268 __ movq(rcx, CodeGenerator::GlobalObject()); | |
1269 __ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset)); | |
1270 } else { | |
1271 __ push(rbx); | |
1272 } | |
1273 EmitCallWithStub(expr); | |
1274 } | |
1275 } else { | |
1276 // Call to some other expression. If the expression is an anonymous | |
1277 // function literal not called in a loop, mark it as one that should | |
1278 // also use the fast code generator. | |
1279 FunctionLiteral* lit = fun->AsFunctionLiteral(); | |
1280 if (lit != NULL && | |
1281 lit->name()->Equals(Heap::empty_string()) && | |
1282 loop_depth() == 0) { | |
1283 lit->set_try_fast_codegen(true); | |
1284 } | |
1285 VisitForValue(fun, kStack); | |
1286 // Load global receiver object. | |
1287 __ movq(rbx, CodeGenerator::GlobalObject()); | |
1288 __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); | |
1289 // Emit function call. | |
1290 EmitCallWithStub(expr); | |
1291 } | |
1292 } | |
1293 | |
1294 | |
1295 void FullCodeGenerator::VisitCallNew(CallNew* expr) { | |
1296 Comment cmnt(masm_, "[ CallNew"); | |
1297 // According to ECMA-262, section 11.2.2, page 44, the function | |
1298 // expression in new calls must be evaluated before the | |
1299 // arguments. | |
1300 // Push function on the stack. | |
1301 VisitForValue(expr->expression(), kStack); | |
1302 | |
1303 // Push global object (receiver). | |
1304 __ push(CodeGenerator::GlobalObject()); | |
1305 | |
1306 // Push the arguments ("left-to-right") on the stack. | |
1307 ZoneList<Expression*>* args = expr->arguments(); | |
1308 int arg_count = args->length(); | |
1309 for (int i = 0; i < arg_count; i++) { | |
1310 VisitForValue(args->at(i), kStack); | |
1311 } | |
1312 | |
1313 // Call the construct call builtin that handles allocation and | |
1314 // constructor invocation. | |
1315 SetSourcePosition(expr->position()); | |
1316 | |
1317 // Load function, arg_count into rdi and rax. | |
1318 __ Set(rax, arg_count); | |
1319 // Function is in rsp[arg_count + 1]. | |
1320 __ movq(rdi, Operand(rsp, rax, times_pointer_size, kPointerSize)); | |
1321 | |
1322 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall)); | |
1323 __ Call(construct_builtin, RelocInfo::CONSTRUCT_CALL); | |
1324 | |
1325 // Replace function on TOS with result in rax, or pop it. | |
1326 DropAndApply(1, context_, rax); | |
1327 } | |
1328 | |
1329 | |
1330 void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { | |
1331 Comment cmnt(masm_, "[ CallRuntime"); | |
1332 ZoneList<Expression*>* args = expr->arguments(); | |
1333 | |
1334 if (expr->is_jsruntime()) { | |
1335 // Prepare for calling JS runtime function. | |
1336 __ Push(expr->name()); | |
1337 __ movq(rax, CodeGenerator::GlobalObject()); | |
1338 __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); | |
1339 } | |
1340 | |
1341 // Push the arguments ("left-to-right"). | |
1342 int arg_count = args->length(); | |
1343 for (int i = 0; i < arg_count; i++) { | |
1344 VisitForValue(args->at(i), kStack); | |
1345 } | |
1346 | |
1347 if (expr->is_jsruntime()) { | |
1348 // Call the JS runtime function. | |
1349 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, | |
1350 NOT_IN_LOOP); | |
1351 __ call(ic, RelocInfo::CODE_TARGET); | |
1352 // Restore context register. | |
1353 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); | |
1354 // Discard the function left on TOS. | |
1355 DropAndApply(1, context_, rax); | |
1356 } else { | |
1357 __ CallRuntime(expr->function(), arg_count); | |
1358 Apply(context_, rax); | |
1359 } | |
1360 } | |
1361 | |
1362 | |
1363 void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { | |
1364 switch (expr->op()) { | |
1365 case Token::VOID: { | |
1366 Comment cmnt(masm_, "[ UnaryOperation (VOID)"); | |
1367 VisitForEffect(expr->expression()); | |
1368 switch (context_) { | |
1369 case Expression::kUninitialized: | |
1370 UNREACHABLE(); | |
1371 break; | |
1372 case Expression::kEffect: | |
1373 break; | |
1374 case Expression::kValue: | |
1375 switch (location_) { | |
1376 case kAccumulator: | |
1377 __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex); | |
1378 break; | |
1379 case kStack: | |
1380 __ PushRoot(Heap::kUndefinedValueRootIndex); | |
1381 break; | |
1382 } | |
1383 break; | |
1384 case Expression::kTestValue: | |
1385 // Value is false so it's needed. | |
1386 switch (location_) { | |
1387 case kAccumulator: | |
1388 __ LoadRoot(result_register(), Heap::kUndefinedValueRootIndex); | |
1389 break; | |
1390 case kStack: | |
1391 __ PushRoot(Heap::kUndefinedValueRootIndex); | |
1392 break; | |
1393 } | |
1394 // Fall through. | |
1395 case Expression::kTest: | |
1396 case Expression::kValueTest: | |
1397 __ jmp(false_label_); | |
1398 break; | |
1399 } | |
1400 break; | |
1401 } | |
1402 | |
1403 case Token::NOT: { | |
1404 Comment cmnt(masm_, "[ UnaryOperation (NOT)"); | |
1405 Label materialize_true, materialize_false, done; | |
1406 // Initially assume a pure test context. Notice that the labels are | |
1407 // swapped. | |
1408 Label* if_true = false_label_; | |
1409 Label* if_false = true_label_; | |
1410 switch (context_) { | |
1411 case Expression::kUninitialized: | |
1412 UNREACHABLE(); | |
1413 break; | |
1414 case Expression::kEffect: | |
1415 if_true = &done; | |
1416 if_false = &done; | |
1417 break; | |
1418 case Expression::kValue: | |
1419 if_true = &materialize_false; | |
1420 if_false = &materialize_true; | |
1421 break; | |
1422 case Expression::kTest: | |
1423 break; | |
1424 case Expression::kValueTest: | |
1425 if_false = &materialize_true; | |
1426 break; | |
1427 case Expression::kTestValue: | |
1428 if_true = &materialize_false; | |
1429 break; | |
1430 } | |
1431 VisitForControl(expr->expression(), if_true, if_false); | |
1432 Apply(context_, if_false, if_true); // Labels swapped. | |
1433 break; | |
1434 } | |
1435 | |
1436 case Token::TYPEOF: { | |
1437 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)"); | |
1438 VariableProxy* proxy = expr->expression()->AsVariableProxy(); | |
1439 if (proxy != NULL && | |
1440 !proxy->var()->is_this() && | |
1441 proxy->var()->is_global()) { | |
1442 Comment cmnt(masm_, "Global variable"); | |
1443 __ push(CodeGenerator::GlobalObject()); | |
1444 __ Move(rcx, proxy->name()); | |
1445 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); | |
1446 // Use a regular load, not a contextual load, to avoid a reference | |
1447 // error. | |
1448 __ Call(ic, RelocInfo::CODE_TARGET); | |
1449 __ movq(Operand(rsp, 0), rax); | |
1450 } else if (proxy != NULL && | |
1451 proxy->var()->slot() != NULL && | |
1452 proxy->var()->slot()->type() == Slot::LOOKUP) { | |
1453 __ push(rsi); | |
1454 __ Push(proxy->name()); | |
1455 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); | |
1456 __ push(rax); | |
1457 } else { | |
1458 // This expression cannot throw a reference error at the top level. | |
1459 VisitForValue(expr->expression(), kStack); | |
1460 } | |
1461 | |
1462 __ CallRuntime(Runtime::kTypeof, 1); | |
1463 Apply(context_, rax); | |
1464 break; | |
1465 } | |
1466 | |
1467 default: | |
1468 UNREACHABLE(); | |
1469 } | |
1470 } | |
1471 | |
1472 | |
1473 void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { | |
1474 Comment cmnt(masm_, "[ CountOperation"); | |
1475 | |
1476 // Expression can only be a property, a global or a (parameter or local) | |
1477 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. | |
1478 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; | |
1479 LhsKind assign_type = VARIABLE; | |
1480 Property* prop = expr->expression()->AsProperty(); | |
1481 // In case of a property we use the uninitialized expression context | |
1482 // of the key to detect a named property. | |
1483 if (prop != NULL) { | |
1484 assign_type = | |
1485 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; | |
1486 } | |
1487 | |
1488 // Evaluate expression and get value. | |
1489 if (assign_type == VARIABLE) { | |
1490 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL); | |
1491 Location saved_location = location_; | |
1492 location_ = kStack; | |
1493 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(), | |
1494 Expression::kValue); | |
1495 location_ = saved_location; | |
1496 } else { | |
1497 // Reserve space for result of postfix operation. | |
1498 if (expr->is_postfix() && context_ != Expression::kEffect) { | |
1499 __ Push(Smi::FromInt(0)); | |
1500 } | |
1501 VisitForValue(prop->obj(), kStack); | |
1502 if (assign_type == NAMED_PROPERTY) { | |
1503 EmitNamedPropertyLoad(prop); | |
1504 } else { | |
1505 VisitForValue(prop->key(), kStack); | |
1506 EmitKeyedPropertyLoad(prop); | |
1507 } | |
1508 __ push(rax); | |
1509 } | |
1510 | |
1511 // Convert to number. | |
1512 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION); | |
1513 | |
1514 // Save result for postfix expressions. | |
1515 if (expr->is_postfix()) { | |
1516 switch (context_) { | |
1517 case Expression::kUninitialized: | |
1518 UNREACHABLE(); | |
1519 case Expression::kEffect: | |
1520 // Do not save result. | |
1521 break; | |
1522 case Expression::kValue: | |
1523 case Expression::kTest: | |
1524 case Expression::kValueTest: | |
1525 case Expression::kTestValue: | |
1526 // Save the result on the stack. If we have a named or keyed property | |
1527 // we store the result under the receiver that is currently on top | |
1528 // of the stack. | |
1529 switch (assign_type) { | |
1530 case VARIABLE: | |
1531 __ push(rax); | |
1532 break; | |
1533 case NAMED_PROPERTY: | |
1534 __ movq(Operand(rsp, kPointerSize), rax); | |
1535 break; | |
1536 case KEYED_PROPERTY: | |
1537 __ movq(Operand(rsp, 2 * kPointerSize), rax); | |
1538 break; | |
1539 } | |
1540 break; | |
1541 } | |
1542 } | |
1543 | |
1544 // Call stub for +1/-1. | |
1545 __ push(rax); | |
1546 __ Push(Smi::FromInt(1)); | |
1547 GenericBinaryOpStub stub(expr->binary_op(), | |
1548 NO_OVERWRITE, | |
1549 NO_GENERIC_BINARY_FLAGS); | |
1550 __ CallStub(&stub); | |
1551 | |
1552 // Store the value returned in rax. | |
1553 switch (assign_type) { | |
1554 case VARIABLE: | |
1555 if (expr->is_postfix()) { | |
1556 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | |
1557 Expression::kEffect); | |
1558 // For all contexts except kEffect: We have the result on | |
1559 // top of the stack. | |
1560 if (context_ != Expression::kEffect) { | |
1561 ApplyTOS(context_); | |
1562 } | |
1563 } else { | |
1564 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(), | |
1565 context_); | |
1566 } | |
1567 break; | |
1568 case NAMED_PROPERTY: { | |
1569 __ Move(rcx, prop->key()->AsLiteral()->handle()); | |
1570 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); | |
1571 __ call(ic, RelocInfo::CODE_TARGET); | |
1572 // This nop signals to the IC that there is no inlined code at the call | |
1573 // site for it to patch. | |
1574 __ nop(); | |
1575 if (expr->is_postfix()) { | |
1576 __ Drop(1); // Result is on the stack under the receiver. | |
1577 if (context_ != Expression::kEffect) { | |
1578 ApplyTOS(context_); | |
1579 } | |
1580 } else { | |
1581 DropAndApply(1, context_, rax); | |
1582 } | |
1583 break; | |
1584 } | |
1585 case KEYED_PROPERTY: { | |
1586 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); | |
1587 __ call(ic, RelocInfo::CODE_TARGET); | |
1588 // This nop signals to the IC that there is no inlined code at the call | |
1589 // site for it to patch. | |
1590 __ nop(); | |
1591 if (expr->is_postfix()) { | |
1592 __ Drop(2); // Result is on the stack under the key and the receiver. | |
1593 if (context_ != Expression::kEffect) { | |
1594 ApplyTOS(context_); | |
1595 } | |
1596 } else { | |
1597 DropAndApply(2, context_, rax); | |
1598 } | |
1599 break; | |
1600 } | |
1601 } | |
1602 } | |
1603 | |
1604 void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { | |
1605 Comment cmnt(masm_, "[ BinaryOperation"); | |
1606 switch (expr->op()) { | |
1607 case Token::COMMA: | |
1608 VisitForEffect(expr->left()); | |
1609 Visit(expr->right()); | |
1610 break; | |
1611 | |
1612 case Token::OR: | |
1613 case Token::AND: | |
1614 EmitLogicalOperation(expr); | |
1615 break; | |
1616 | |
1617 case Token::ADD: | |
1618 case Token::SUB: | |
1619 case Token::DIV: | |
1620 case Token::MOD: | |
1621 case Token::MUL: | |
1622 case Token::BIT_OR: | |
1623 case Token::BIT_AND: | |
1624 case Token::BIT_XOR: | |
1625 case Token::SHL: | |
1626 case Token::SHR: | |
1627 case Token::SAR: | |
1628 VisitForValue(expr->left(), kStack); | |
1629 VisitForValue(expr->right(), kAccumulator); | |
1630 EmitBinaryOp(expr->op(), context_); | |
1631 break; | |
1632 | |
1633 default: | |
1634 UNREACHABLE(); | |
1635 } | |
1636 } | |
1637 | |
1638 | |
1639 void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) { | |
1640 Comment cmnt(masm_, "[ CompareOperation"); | |
1641 | |
1642 // Always perform the comparison for its control flow. Pack the result | |
1643 // into the expression's context after the comparison is performed. | |
1644 Label materialize_true, materialize_false, done; | |
1645 // Initially assume we are in a test context. | |
1646 Label* if_true = true_label_; | |
1647 Label* if_false = false_label_; | |
1648 switch (context_) { | |
1649 case Expression::kUninitialized: | |
1650 UNREACHABLE(); | |
1651 break; | |
1652 case Expression::kEffect: | |
1653 if_true = &done; | |
1654 if_false = &done; | |
1655 break; | |
1656 case Expression::kValue: | |
1657 if_true = &materialize_true; | |
1658 if_false = &materialize_false; | |
1659 break; | |
1660 case Expression::kTest: | |
1661 break; | |
1662 case Expression::kValueTest: | |
1663 if_true = &materialize_true; | |
1664 break; | |
1665 case Expression::kTestValue: | |
1666 if_false = &materialize_false; | |
1667 break; | |
1668 } | |
1669 | |
1670 VisitForValue(expr->left(), kStack); | |
1671 switch (expr->op()) { | |
1672 case Token::IN: | |
1673 VisitForValue(expr->right(), kStack); | |
1674 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION); | |
1675 __ CompareRoot(rax, Heap::kTrueValueRootIndex); | |
1676 __ j(equal, if_true); | |
1677 __ jmp(if_false); | |
1678 break; | |
1679 | |
1680 case Token::INSTANCEOF: { | |
1681 VisitForValue(expr->right(), kStack); | |
1682 InstanceofStub stub; | |
1683 __ CallStub(&stub); | |
1684 __ testq(rax, rax); | |
1685 __ j(zero, if_true); // The stub returns 0 for true. | |
1686 __ jmp(if_false); | |
1687 break; | |
1688 } | |
1689 | |
1690 default: { | |
1691 VisitForValue(expr->right(), kAccumulator); | |
1692 Condition cc = no_condition; | |
1693 bool strict = false; | |
1694 switch (expr->op()) { | |
1695 case Token::EQ_STRICT: | |
1696 strict = true; | |
1697 // Fall through. | |
1698 case Token::EQ: | |
1699 cc = equal; | |
1700 __ pop(rdx); | |
1701 break; | |
1702 case Token::LT: | |
1703 cc = less; | |
1704 __ pop(rdx); | |
1705 break; | |
1706 case Token::GT: | |
1707 // Reverse left and right sizes to obtain ECMA-262 conversion order. | |
1708 cc = less; | |
1709 __ movq(rdx, result_register()); | |
1710 __ pop(rax); | |
1711 break; | |
1712 case Token::LTE: | |
1713 // Reverse left and right sizes to obtain ECMA-262 conversion order. | |
1714 cc = greater_equal; | |
1715 __ movq(rdx, result_register()); | |
1716 __ pop(rax); | |
1717 break; | |
1718 case Token::GTE: | |
1719 cc = greater_equal; | |
1720 __ pop(rdx); | |
1721 break; | |
1722 case Token::IN: | |
1723 case Token::INSTANCEOF: | |
1724 default: | |
1725 UNREACHABLE(); | |
1726 } | |
1727 | |
1728 // The comparison stub expects the smi vs. smi case to be handled | |
1729 // before it is called. | |
1730 Label slow_case; | |
1731 __ JumpIfNotBothSmi(rax, rdx, &slow_case); | |
1732 __ SmiCompare(rdx, rax); | |
1733 __ j(cc, if_true); | |
1734 __ jmp(if_false); | |
1735 | |
1736 __ bind(&slow_case); | |
1737 CompareStub stub(cc, strict); | |
1738 __ CallStub(&stub); | |
1739 __ testq(rax, rax); | |
1740 __ j(cc, if_true); | |
1741 __ jmp(if_false); | |
1742 } | |
1743 } | |
1744 | |
1745 // Convert the result of the comparison into one expected for this | |
1746 // expression's context. | |
1747 Apply(context_, if_true, if_false); | |
1748 } | |
1749 | |
1750 | |
1751 void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) { | |
1752 __ movq(rax, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
1753 Apply(context_, rax); | |
1754 } | |
1755 | |
1756 | |
1757 Register FullCodeGenerator::result_register() { return rax; } | |
1758 | |
1759 | |
1760 Register FullCodeGenerator::context_register() { return rsi; } | |
1761 | |
1762 | |
1763 void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) { | |
1764 ASSERT(IsAligned(frame_offset, kPointerSize)); | |
1765 __ movq(Operand(rbp, frame_offset), value); | |
1766 } | |
1767 | |
1768 | |
1769 void FullCodeGenerator::LoadContextField(Register dst, int context_index) { | |
1770 __ movq(dst, CodeGenerator::ContextOperand(rsi, context_index)); | |
1771 } | |
1772 | |
1773 | |
1774 // ---------------------------------------------------------------------------- | |
1775 // Non-local control flow support. | |
1776 | |
1777 | |
1778 void FullCodeGenerator::EnterFinallyBlock() { | |
1779 ASSERT(!result_register().is(rdx)); | |
1780 ASSERT(!result_register().is(rcx)); | |
1781 // Cook return address on top of stack (smi encoded Code* delta) | |
1782 __ movq(rdx, Operand(rsp, 0)); | |
1783 __ Move(rcx, masm_->CodeObject()); | |
1784 __ subq(rdx, rcx); | |
1785 __ Integer32ToSmi(rdx, rdx); | |
1786 __ movq(Operand(rsp, 0), rdx); | |
1787 // Store result register while executing finally block. | |
1788 __ push(result_register()); | |
1789 } | |
1790 | |
1791 | |
1792 void FullCodeGenerator::ExitFinallyBlock() { | |
1793 ASSERT(!result_register().is(rdx)); | |
1794 ASSERT(!result_register().is(rcx)); | |
1795 // Restore result register from stack. | |
1796 __ pop(result_register()); | |
1797 // Uncook return address. | |
1798 __ movq(rdx, Operand(rsp, 0)); | |
1799 __ SmiToInteger32(rdx, rdx); | |
1800 __ Move(rcx, masm_->CodeObject()); | |
1801 __ addq(rdx, rcx); | |
1802 __ movq(Operand(rsp, 0), rdx); | |
1803 // And return. | |
1804 __ ret(0); | |
1805 } | |
1806 | |
1807 | |
1808 #undef __ | |
1809 | |
1810 | |
1811 } } // namespace v8::internal | |
OLD | NEW |