Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: src/x64/fast-codegen-x64.cc

Issue 549109: Rename the fast-codegen* files to full-codegen*. (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 10 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/ia32/full-codegen-ia32.cc ('k') | src/x64/full-codegen-x64.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « src/ia32/full-codegen-ia32.cc ('k') | src/x64/full-codegen-x64.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698