blob: 1b78772f746492c3ff50cb1aac087b555416202f [file] [log] [blame]
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001// Copyright 2010 the V8 project authors. All rights reserved.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002// 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
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000030#if defined(V8_TARGET_ARCH_IA32)
31
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000032#include "codegen-inl.h"
33#include "compiler.h"
34#include "debug.h"
35#include "full-codegen.h"
36#include "parser.h"
sgjesse@chromium.org833cdd72010-02-26 10:06:16 +000037#include "scopes.h"
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000038
39namespace v8 {
40namespace internal {
41
42#define __ ACCESS_MASM(masm_)
43
44// Generate code for a JS function. On entry to the function the receiver
45// and arguments have been pushed on the stack left to right, with the
46// return address on top of them. The actual argument count matches the
47// formal parameter count expected by the function.
48//
49// The live registers are:
50// o edi: the JS function object being called (ie, ourselves)
51// o esi: our context
52// o ebp: our caller's frame pointer
53// o esp: stack pointer (pointing to return address)
54//
55// The function builds a JS frame. Please see JavaScriptFrameConstants in
56// frames-ia32.h for its layout.
ager@chromium.org5c838252010-02-19 08:53:10 +000057void FullCodeGenerator::Generate(CompilationInfo* info, Mode mode) {
58 ASSERT(info_ == NULL);
59 info_ = info;
60 SetFunctionPosition(function());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000061 Comment cmnt(masm_, "[ function compiled by full code generator");
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000062
63 if (mode == PRIMARY) {
64 __ push(ebp); // Caller's frame pointer.
65 __ mov(ebp, esp);
66 __ push(esi); // Callee's context.
67 __ push(edi); // Callee's JS Function.
68
69 { Comment cmnt(masm_, "[ Allocate locals");
ager@chromium.org5c838252010-02-19 08:53:10 +000070 int locals_count = scope()->num_stack_slots();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000071 if (locals_count == 1) {
72 __ push(Immediate(Factory::undefined_value()));
73 } else if (locals_count > 1) {
74 __ mov(eax, Immediate(Factory::undefined_value()));
75 for (int i = 0; i < locals_count; i++) {
76 __ push(eax);
77 }
78 }
79 }
80
81 bool function_in_register = true;
82
83 // Possibly allocate a local context.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000084 int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
85 if (heap_slots > 0) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000086 Comment cmnt(masm_, "[ Allocate local context");
87 // Argument to NewContext is the function, which is still in edi.
88 __ push(edi);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000089 if (heap_slots <= FastNewContextStub::kMaximumSlots) {
90 FastNewContextStub stub(heap_slots);
91 __ CallStub(&stub);
92 } else {
93 __ CallRuntime(Runtime::kNewContext, 1);
94 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +000095 function_in_register = false;
96 // Context is returned in both eax and esi. It replaces the context
97 // passed to us. It's saved in the stack and kept live in esi.
98 __ mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
99
100 // Copy parameters into context if necessary.
ager@chromium.org5c838252010-02-19 08:53:10 +0000101 int num_parameters = scope()->num_parameters();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000102 for (int i = 0; i < num_parameters; i++) {
ager@chromium.org5c838252010-02-19 08:53:10 +0000103 Slot* slot = scope()->parameter(i)->slot();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000104 if (slot != NULL && slot->type() == Slot::CONTEXT) {
105 int parameter_offset = StandardFrameConstants::kCallerSPOffset +
106 (num_parameters - 1 - i) * kPointerSize;
107 // Load parameter from stack.
108 __ mov(eax, Operand(ebp, parameter_offset));
109 // Store it in the context.
110 int context_offset = Context::SlotOffset(slot->index());
111 __ mov(Operand(esi, context_offset), eax);
112 // Update the write barrier. This clobbers all involved
113 // registers, so we have use a third register to avoid
114 // clobbering esi.
115 __ mov(ecx, esi);
116 __ RecordWrite(ecx, context_offset, eax, ebx);
117 }
118 }
119 }
120
ager@chromium.org5c838252010-02-19 08:53:10 +0000121 Variable* arguments = scope()->arguments()->AsVariable();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000122 if (arguments != NULL) {
123 // Function uses arguments object.
124 Comment cmnt(masm_, "[ Allocate arguments object");
125 if (function_in_register) {
126 __ push(edi);
127 } else {
128 __ push(Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
129 }
130 // Receiver is just before the parameters on the caller's stack.
ager@chromium.org5c838252010-02-19 08:53:10 +0000131 int offset = scope()->num_parameters() * kPointerSize;
132 __ lea(edx,
133 Operand(ebp, StandardFrameConstants::kCallerSPOffset + offset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000134 __ push(edx);
ager@chromium.org5c838252010-02-19 08:53:10 +0000135 __ push(Immediate(Smi::FromInt(scope()->num_parameters())));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000136 // Arguments to ArgumentsAccessStub:
137 // function, receiver address, parameter count.
138 // The stub will rewrite receiver and parameter count if the previous
139 // stack frame was an arguments adapter frame.
140 ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
141 __ CallStub(&stub);
142 __ mov(ecx, eax); // Duplicate result.
143 Move(arguments->slot(), eax, ebx, edx);
144 Slot* dot_arguments_slot =
ager@chromium.org5c838252010-02-19 08:53:10 +0000145 scope()->arguments_shadow()->AsVariable()->slot();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000146 Move(dot_arguments_slot, ecx, ebx, edx);
147 }
148 }
149
150 { Comment cmnt(masm_, "[ Declarations");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000151 // For named function expressions, declare the function name as a
152 // constant.
153 if (scope()->is_function_scope() && scope()->function() != NULL) {
154 EmitDeclaration(scope()->function(), Variable::CONST, NULL);
155 }
156 // Visit all the explicit declarations unless there is an illegal
157 // redeclaration.
158 if (scope()->HasIllegalRedeclaration()) {
159 scope()->VisitIllegalRedeclaration(this);
160 } else {
161 VisitDeclarations(scope()->declarations());
162 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000163 }
164
165 { Comment cmnt(masm_, "[ Stack check");
166 Label ok;
167 ExternalReference stack_limit =
168 ExternalReference::address_of_stack_limit();
169 __ cmp(esp, Operand::StaticVariable(stack_limit));
170 __ j(above_equal, &ok, taken);
171 StackCheckStub stub;
172 __ CallStub(&stub);
173 __ bind(&ok);
174 }
175
176 if (FLAG_trace) {
177 __ CallRuntime(Runtime::kTraceEnter, 0);
178 }
179
180 { Comment cmnt(masm_, "[ Body");
181 ASSERT(loop_depth() == 0);
ager@chromium.org5c838252010-02-19 08:53:10 +0000182 VisitStatements(function()->body());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000183 ASSERT(loop_depth() == 0);
184 }
185
186 { Comment cmnt(masm_, "[ return <undefined>;");
187 // Emit a 'return undefined' in case control fell off the end of the body.
188 __ mov(eax, Factory::undefined_value());
ager@chromium.org5c838252010-02-19 08:53:10 +0000189 EmitReturnSequence(function()->end_position());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000190 }
191}
192
193
194void FullCodeGenerator::EmitReturnSequence(int position) {
195 Comment cmnt(masm_, "[ Return sequence");
196 if (return_label_.is_bound()) {
197 __ jmp(&return_label_);
198 } else {
199 // Common return label
200 __ bind(&return_label_);
201 if (FLAG_trace) {
202 __ push(eax);
203 __ CallRuntime(Runtime::kTraceExit, 1);
204 }
205#ifdef DEBUG
206 // Add a label for checking the size of the code used for returning.
207 Label check_exit_codesize;
208 masm_->bind(&check_exit_codesize);
209#endif
210 CodeGenerator::RecordPositions(masm_, position);
211 __ RecordJSReturn();
212 // Do not use the leave instruction here because it is too short to
213 // patch with the code required by the debugger.
214 __ mov(esp, ebp);
215 __ pop(ebp);
ager@chromium.org5c838252010-02-19 08:53:10 +0000216 __ ret((scope()->num_parameters() + 1) * kPointerSize);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000217#ifdef ENABLE_DEBUGGER_SUPPORT
218 // Check that the size of the code used for returning matches what is
219 // expected by the debugger.
220 ASSERT_EQ(Assembler::kJSReturnSequenceLength,
221 masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
222#endif
223 }
224}
225
226
227void FullCodeGenerator::Apply(Expression::Context context, Register reg) {
228 switch (context) {
229 case Expression::kUninitialized:
230 UNREACHABLE();
231
232 case Expression::kEffect:
233 // Nothing to do.
234 break;
235
236 case Expression::kValue:
237 // Move value into place.
238 switch (location_) {
239 case kAccumulator:
240 if (!reg.is(result_register())) __ mov(result_register(), reg);
241 break;
242 case kStack:
243 __ push(reg);
244 break;
245 }
246 break;
247
248 case Expression::kTest:
249 // For simplicity we always test the accumulator register.
250 if (!reg.is(result_register())) __ mov(result_register(), reg);
251 DoTest(context);
252 break;
253
254 case Expression::kValueTest:
255 case Expression::kTestValue:
256 if (!reg.is(result_register())) __ mov(result_register(), reg);
257 switch (location_) {
258 case kAccumulator:
259 break;
260 case kStack:
261 __ push(result_register());
262 break;
263 }
264 DoTest(context);
265 break;
266 }
267}
268
269
270void FullCodeGenerator::Apply(Expression::Context context, Slot* slot) {
271 switch (context) {
272 case Expression::kUninitialized:
273 UNREACHABLE();
274 case Expression::kEffect:
275 // Nothing to do.
276 break;
277 case Expression::kValue: {
278 MemOperand slot_operand = EmitSlotSearch(slot, result_register());
279 switch (location_) {
280 case kAccumulator:
281 __ mov(result_register(), slot_operand);
282 break;
283 case kStack:
284 // Memory operands can be pushed directly.
285 __ push(slot_operand);
286 break;
287 }
288 break;
289 }
290
291 case Expression::kTest:
292 // For simplicity we always test the accumulator register.
293 Move(result_register(), slot);
294 DoTest(context);
295 break;
296
297 case Expression::kValueTest:
298 case Expression::kTestValue:
299 Move(result_register(), slot);
300 switch (location_) {
301 case kAccumulator:
302 break;
303 case kStack:
304 __ push(result_register());
305 break;
306 }
307 DoTest(context);
308 break;
309 }
310}
311
312
313void FullCodeGenerator::Apply(Expression::Context context, Literal* lit) {
314 switch (context) {
315 case Expression::kUninitialized:
316 UNREACHABLE();
317 case Expression::kEffect:
318 // Nothing to do.
319 break;
320 case Expression::kValue:
321 switch (location_) {
322 case kAccumulator:
323 __ mov(result_register(), lit->handle());
324 break;
325 case kStack:
326 // Immediates can be pushed directly.
327 __ push(Immediate(lit->handle()));
328 break;
329 }
330 break;
331
332 case Expression::kTest:
333 // For simplicity we always test the accumulator register.
334 __ mov(result_register(), lit->handle());
335 DoTest(context);
336 break;
337
338 case Expression::kValueTest:
339 case Expression::kTestValue:
340 __ mov(result_register(), lit->handle());
341 switch (location_) {
342 case kAccumulator:
343 break;
344 case kStack:
345 __ push(result_register());
346 break;
347 }
348 DoTest(context);
349 break;
350 }
351}
352
353
354void FullCodeGenerator::ApplyTOS(Expression::Context context) {
355 switch (context) {
356 case Expression::kUninitialized:
357 UNREACHABLE();
358
359 case Expression::kEffect:
360 __ Drop(1);
361 break;
362
363 case Expression::kValue:
364 switch (location_) {
365 case kAccumulator:
366 __ pop(result_register());
367 break;
368 case kStack:
369 break;
370 }
371 break;
372
373 case Expression::kTest:
374 // For simplicity we always test the accumulator register.
375 __ pop(result_register());
376 DoTest(context);
377 break;
378
379 case Expression::kValueTest:
380 case Expression::kTestValue:
381 switch (location_) {
382 case kAccumulator:
383 __ pop(result_register());
384 break;
385 case kStack:
386 __ mov(result_register(), Operand(esp, 0));
387 break;
388 }
389 DoTest(context);
390 break;
391 }
392}
393
394
395void FullCodeGenerator::DropAndApply(int count,
396 Expression::Context context,
397 Register reg) {
398 ASSERT(count > 0);
399 ASSERT(!reg.is(esp));
400 switch (context) {
401 case Expression::kUninitialized:
402 UNREACHABLE();
403
404 case Expression::kEffect:
405 __ Drop(count);
406 break;
407
408 case Expression::kValue:
409 switch (location_) {
410 case kAccumulator:
411 __ Drop(count);
412 if (!reg.is(result_register())) __ mov(result_register(), reg);
413 break;
414 case kStack:
415 if (count > 1) __ Drop(count - 1);
416 __ mov(Operand(esp, 0), reg);
417 break;
418 }
419 break;
420
421 case Expression::kTest:
422 // For simplicity we always test the accumulator register.
423 __ Drop(count);
424 if (!reg.is(result_register())) __ mov(result_register(), reg);
425 DoTest(context);
426 break;
427
428 case Expression::kValueTest:
429 case Expression::kTestValue:
430 switch (location_) {
431 case kAccumulator:
432 __ Drop(count);
433 if (!reg.is(result_register())) __ mov(result_register(), reg);
434 break;
435 case kStack:
436 if (count > 1) __ Drop(count - 1);
437 __ mov(result_register(), reg);
438 __ mov(Operand(esp, 0), result_register());
439 break;
440 }
441 DoTest(context);
442 break;
443 }
444}
445
446
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000447void FullCodeGenerator::PrepareTest(Label* materialize_true,
448 Label* materialize_false,
449 Label** if_true,
450 Label** if_false) {
451 switch (context_) {
452 case Expression::kUninitialized:
453 UNREACHABLE();
454 break;
455 case Expression::kEffect:
456 // In an effect context, the true and the false case branch to the
457 // same label.
458 *if_true = *if_false = materialize_true;
459 break;
460 case Expression::kValue:
461 *if_true = materialize_true;
462 *if_false = materialize_false;
463 break;
464 case Expression::kTest:
465 *if_true = true_label_;
466 *if_false = false_label_;
467 break;
468 case Expression::kValueTest:
469 *if_true = materialize_true;
470 *if_false = false_label_;
471 break;
472 case Expression::kTestValue:
473 *if_true = true_label_;
474 *if_false = materialize_false;
475 break;
476 }
477}
478
479
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000480void FullCodeGenerator::Apply(Expression::Context context,
481 Label* materialize_true,
482 Label* materialize_false) {
483 switch (context) {
484 case Expression::kUninitialized:
485
486 case Expression::kEffect:
487 ASSERT_EQ(materialize_true, materialize_false);
488 __ bind(materialize_true);
489 break;
490
491 case Expression::kValue: {
492 Label done;
493 switch (location_) {
494 case kAccumulator:
495 __ bind(materialize_true);
496 __ mov(result_register(), Factory::true_value());
497 __ jmp(&done);
498 __ bind(materialize_false);
499 __ mov(result_register(), Factory::false_value());
500 break;
501 case kStack:
502 __ bind(materialize_true);
503 __ push(Immediate(Factory::true_value()));
504 __ jmp(&done);
505 __ bind(materialize_false);
506 __ push(Immediate(Factory::false_value()));
507 break;
508 }
509 __ bind(&done);
510 break;
511 }
512
513 case Expression::kTest:
514 break;
515
516 case Expression::kValueTest:
517 __ bind(materialize_true);
518 switch (location_) {
519 case kAccumulator:
520 __ mov(result_register(), Factory::true_value());
521 break;
522 case kStack:
523 __ push(Immediate(Factory::true_value()));
524 break;
525 }
526 __ jmp(true_label_);
527 break;
528
529 case Expression::kTestValue:
530 __ bind(materialize_false);
531 switch (location_) {
532 case kAccumulator:
533 __ mov(result_register(), Factory::false_value());
534 break;
535 case kStack:
536 __ push(Immediate(Factory::false_value()));
537 break;
538 }
539 __ jmp(false_label_);
540 break;
541 }
542}
543
544
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000545// Convert constant control flow (true or false) to the result expected for
546// a given expression context.
547void FullCodeGenerator::Apply(Expression::Context context, bool flag) {
548 switch (context) {
549 case Expression::kUninitialized:
550 UNREACHABLE();
551 break;
552 case Expression::kEffect:
553 break;
554 case Expression::kValue: {
555 Handle<Object> value =
556 flag ? Factory::true_value() : Factory::false_value();
557 switch (location_) {
558 case kAccumulator:
559 __ mov(result_register(), value);
560 break;
561 case kStack:
562 __ push(Immediate(value));
563 break;
564 }
565 break;
566 }
567 case Expression::kTest:
568 __ jmp(flag ? true_label_ : false_label_);
569 break;
570 case Expression::kTestValue:
571 switch (location_) {
572 case kAccumulator:
573 // If value is false it's needed.
574 if (!flag) __ mov(result_register(), Factory::false_value());
575 break;
576 case kStack:
577 // If value is false it's needed.
578 if (!flag) __ push(Immediate(Factory::false_value()));
579 break;
580 }
581 __ jmp(flag ? true_label_ : false_label_);
582 break;
583 case Expression::kValueTest:
584 switch (location_) {
585 case kAccumulator:
586 // If value is true it's needed.
587 if (flag) __ mov(result_register(), Factory::true_value());
588 break;
589 case kStack:
590 // If value is true it's needed.
591 if (flag) __ push(Immediate(Factory::true_value()));
592 break;
593 }
594 __ jmp(flag ? true_label_ : false_label_);
595 break;
596 }
597}
598
599
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000600void FullCodeGenerator::DoTest(Expression::Context context) {
601 // The value to test is in the accumulator. If the value might be needed
602 // on the stack (value/test and test/value contexts with a stack location
603 // desired), then the value is already duplicated on the stack.
604 ASSERT_NE(NULL, true_label_);
605 ASSERT_NE(NULL, false_label_);
606
607 // In value/test and test/value expression contexts with stack as the
608 // desired location, there is already an extra value on the stack. Use a
609 // label to discard it if unneeded.
610 Label discard;
611 Label* if_true = true_label_;
612 Label* if_false = false_label_;
613 switch (context) {
614 case Expression::kUninitialized:
615 case Expression::kEffect:
616 case Expression::kValue:
617 UNREACHABLE();
618 case Expression::kTest:
619 break;
620 case Expression::kValueTest:
621 switch (location_) {
622 case kAccumulator:
623 break;
624 case kStack:
625 if_false = &discard;
626 break;
627 }
628 break;
629 case Expression::kTestValue:
630 switch (location_) {
631 case kAccumulator:
632 break;
633 case kStack:
634 if_true = &discard;
635 break;
636 }
637 break;
638 }
639
640 // Emit the inlined tests assumed by the stub.
641 __ cmp(result_register(), Factory::undefined_value());
642 __ j(equal, if_false);
643 __ cmp(result_register(), Factory::true_value());
644 __ j(equal, if_true);
645 __ cmp(result_register(), Factory::false_value());
646 __ j(equal, if_false);
647 ASSERT_EQ(0, kSmiTag);
648 __ test(result_register(), Operand(result_register()));
649 __ j(zero, if_false);
650 __ test(result_register(), Immediate(kSmiTagMask));
651 __ j(zero, if_true);
652
653 // Save a copy of the value if it may be needed and isn't already saved.
654 switch (context) {
655 case Expression::kUninitialized:
656 case Expression::kEffect:
657 case Expression::kValue:
658 UNREACHABLE();
659 case Expression::kTest:
660 break;
661 case Expression::kValueTest:
662 switch (location_) {
663 case kAccumulator:
664 __ push(result_register());
665 break;
666 case kStack:
667 break;
668 }
669 break;
670 case Expression::kTestValue:
671 switch (location_) {
672 case kAccumulator:
673 __ push(result_register());
674 break;
675 case kStack:
676 break;
677 }
678 break;
679 }
680
681 // Call the ToBoolean stub for all other cases.
682 ToBooleanStub stub;
683 __ push(result_register());
684 __ CallStub(&stub);
685 __ test(eax, Operand(eax));
686
687 // The stub returns nonzero for true. Complete based on the context.
688 switch (context) {
689 case Expression::kUninitialized:
690 case Expression::kEffect:
691 case Expression::kValue:
692 UNREACHABLE();
693
694 case Expression::kTest:
695 __ j(not_zero, true_label_);
696 __ jmp(false_label_);
697 break;
698
699 case Expression::kValueTest:
700 switch (location_) {
701 case kAccumulator:
702 __ j(zero, &discard);
703 __ pop(result_register());
704 __ jmp(true_label_);
705 break;
706 case kStack:
707 __ j(not_zero, true_label_);
708 break;
709 }
710 __ bind(&discard);
711 __ Drop(1);
712 __ jmp(false_label_);
713 break;
714
715 case Expression::kTestValue:
716 switch (location_) {
717 case kAccumulator:
718 __ j(not_zero, &discard);
719 __ pop(result_register());
720 __ jmp(false_label_);
721 break;
722 case kStack:
723 __ j(zero, false_label_);
724 break;
725 }
726 __ bind(&discard);
727 __ Drop(1);
728 __ jmp(true_label_);
729 break;
730 }
731}
732
733
734MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) {
735 switch (slot->type()) {
736 case Slot::PARAMETER:
737 case Slot::LOCAL:
738 return Operand(ebp, SlotOffset(slot));
739 case Slot::CONTEXT: {
740 int context_chain_length =
ager@chromium.org5c838252010-02-19 08:53:10 +0000741 scope()->ContextChainLength(slot->var()->scope());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000742 __ LoadContext(scratch, context_chain_length);
743 return CodeGenerator::ContextOperand(scratch, slot->index());
744 }
745 case Slot::LOOKUP:
746 UNREACHABLE();
747 }
748 UNREACHABLE();
749 return Operand(eax, 0);
750}
751
752
753void FullCodeGenerator::Move(Register destination, Slot* source) {
754 MemOperand location = EmitSlotSearch(source, destination);
755 __ mov(destination, location);
756}
757
758
759void FullCodeGenerator::Move(Slot* dst,
760 Register src,
761 Register scratch1,
762 Register scratch2) {
763 ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented.
764 ASSERT(!scratch1.is(src) && !scratch2.is(src));
765 MemOperand location = EmitSlotSearch(dst, scratch1);
766 __ mov(location, src);
767 // Emit the write barrier code if the location is in the heap.
768 if (dst->type() == Slot::CONTEXT) {
769 int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize;
770 __ RecordWrite(scratch1, offset, src, scratch2);
771 }
772}
773
774
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000775void FullCodeGenerator::EmitDeclaration(Variable* variable,
776 Variable::Mode mode,
777 FunctionLiteral* function) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000778 Comment cmnt(masm_, "[ Declaration");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000779 ASSERT(variable != NULL); // Must have been resolved.
780 Slot* slot = variable->slot();
781 Property* prop = variable->AsProperty();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000782 if (slot != NULL) {
783 switch (slot->type()) {
784 case Slot::PARAMETER:
785 case Slot::LOCAL:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000786 if (mode == Variable::CONST) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000787 __ mov(Operand(ebp, SlotOffset(slot)),
788 Immediate(Factory::the_hole_value()));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000789 } else if (function != NULL) {
790 VisitForValue(function, kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000791 __ mov(Operand(ebp, SlotOffset(slot)), result_register());
792 }
793 break;
794
795 case Slot::CONTEXT:
796 // We bypass the general EmitSlotSearch because we know more about
797 // this specific context.
798
799 // The variable in the decl always resides in the current context.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000800 ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000801 if (FLAG_debug_code) {
802 // Check if we have the correct context pointer.
803 __ mov(ebx,
804 CodeGenerator::ContextOperand(esi, Context::FCONTEXT_INDEX));
805 __ cmp(ebx, Operand(esi));
806 __ Check(equal, "Unexpected declaration in current context.");
807 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000808 if (mode == Variable::CONST) {
809 __ mov(CodeGenerator::ContextOperand(esi, slot->index()),
810 Immediate(Factory::the_hole_value()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000811 // No write barrier since the hole value is in old space.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000812 } else if (function != NULL) {
813 VisitForValue(function, kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000814 __ mov(CodeGenerator::ContextOperand(esi, slot->index()),
815 result_register());
816 int offset = Context::SlotOffset(slot->index());
817 __ mov(ebx, esi);
818 __ RecordWrite(ebx, offset, result_register(), ecx);
819 }
820 break;
821
822 case Slot::LOOKUP: {
823 __ push(esi);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000824 __ push(Immediate(variable->name()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000825 // Declaration nodes are always introduced in one of two modes.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000826 ASSERT(mode == Variable::VAR || mode == Variable::CONST);
827 PropertyAttributes attr = (mode == Variable::VAR) ? NONE : READ_ONLY;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000828 __ push(Immediate(Smi::FromInt(attr)));
829 // Push initial value, if any.
830 // Note: For variables we must not push an initial value (such as
831 // 'undefined') because we may have a (legal) redeclaration and we
832 // must not destroy the current value.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000833 if (mode == Variable::CONST) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000834 __ push(Immediate(Factory::the_hole_value()));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000835 } else if (function != NULL) {
836 VisitForValue(function, kStack);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000837 } else {
838 __ push(Immediate(Smi::FromInt(0))); // No initial value!
839 }
840 __ CallRuntime(Runtime::kDeclareContextSlot, 4);
841 break;
842 }
843 }
844
845 } else if (prop != NULL) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000846 if (function != NULL || mode == Variable::CONST) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000847 // We are declaring a function or constant that rewrites to a
848 // property. Use (keyed) IC to set the initial value.
849 VisitForValue(prop->obj(), kStack);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000850 if (function != NULL) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000851 VisitForValue(prop->key(), kStack);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000852 VisitForValue(function, kAccumulator);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000853 __ pop(ecx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000854 } else {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000855 VisitForValue(prop->key(), kAccumulator);
856 __ mov(ecx, result_register());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000857 __ mov(result_register(), Factory::the_hole_value());
858 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000859 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000860
861 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
862 __ call(ic, RelocInfo::CODE_TARGET);
863 // Absence of a test eax instruction following the call
864 // indicates that none of the load was inlined.
865 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000866 }
867 }
868}
869
870
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000871void FullCodeGenerator::VisitDeclaration(Declaration* decl) {
872 EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun());
873}
874
875
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000876void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
877 // Call the runtime to declare the globals.
878 __ push(esi); // The context is the first argument.
879 __ push(Immediate(pairs));
ager@chromium.org5c838252010-02-19 08:53:10 +0000880 __ push(Immediate(Smi::FromInt(is_eval() ? 1 : 0)));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000881 __ CallRuntime(Runtime::kDeclareGlobals, 3);
882 // Return value is ignored.
883}
884
885
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000886void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
887 Comment cmnt(masm_, "[ SwitchStatement");
888 Breakable nested_statement(this, stmt);
889 SetStatementPosition(stmt);
890 // Keep the switch value on the stack until a case matches.
891 VisitForValue(stmt->tag(), kStack);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000892
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000893 ZoneList<CaseClause*>* clauses = stmt->cases();
894 CaseClause* default_clause = NULL; // Can occur anywhere in the list.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000895
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000896 Label next_test; // Recycled for each test.
897 // Compile all the tests with branches to their bodies.
898 for (int i = 0; i < clauses->length(); i++) {
899 CaseClause* clause = clauses->at(i);
900 // The default is not a test, but remember it as final fall through.
901 if (clause->is_default()) {
902 default_clause = clause;
903 continue;
904 }
905
906 Comment cmnt(masm_, "[ Case comparison");
907 __ bind(&next_test);
908 next_test.Unuse();
909
910 // Compile the label expression.
911 VisitForValue(clause->label(), kAccumulator);
912
913 // Perform the comparison as if via '==='. The comparison stub expects
914 // the smi vs. smi case to be handled before it is called.
915 Label slow_case;
916 __ mov(edx, Operand(esp, 0)); // Switch value.
917 __ mov(ecx, edx);
918 __ or_(ecx, Operand(eax));
919 __ test(ecx, Immediate(kSmiTagMask));
920 __ j(not_zero, &slow_case, not_taken);
921 __ cmp(edx, Operand(eax));
922 __ j(not_equal, &next_test);
923 __ Drop(1); // Switch value is no longer needed.
924 __ jmp(clause->body_target()->entry_label());
925
926 __ bind(&slow_case);
927 CompareStub stub(equal, true);
928 __ CallStub(&stub);
929 __ test(eax, Operand(eax));
930 __ j(not_equal, &next_test);
931 __ Drop(1); // Switch value is no longer needed.
932 __ jmp(clause->body_target()->entry_label());
933 }
934
935 // Discard the test value and jump to the default if present, otherwise to
936 // the end of the statement.
937 __ bind(&next_test);
938 __ Drop(1); // Switch value is no longer needed.
939 if (default_clause == NULL) {
940 __ jmp(nested_statement.break_target());
941 } else {
942 __ jmp(default_clause->body_target()->entry_label());
943 }
944
945 // Compile all the case bodies.
946 for (int i = 0; i < clauses->length(); i++) {
947 Comment cmnt(masm_, "[ Case body");
948 CaseClause* clause = clauses->at(i);
949 __ bind(clause->body_target()->entry_label());
950 VisitStatements(clause->statements());
951 }
952
953 __ bind(nested_statement.break_target());
954}
955
956
957void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
958 Comment cmnt(masm_, "[ ForInStatement");
959 SetStatementPosition(stmt);
960
961 Label loop, exit;
962 ForIn loop_statement(this, stmt);
963 increment_loop_depth();
964
965 // Get the object to enumerate over. Both SpiderMonkey and JSC
966 // ignore null and undefined in contrast to the specification; see
967 // ECMA-262 section 12.6.4.
968 VisitForValue(stmt->enumerable(), kAccumulator);
969 __ cmp(eax, Factory::undefined_value());
970 __ j(equal, &exit);
971 __ cmp(eax, Factory::null_value());
972 __ j(equal, &exit);
973
974 // Convert the object to a JS object.
975 Label convert, done_convert;
976 __ test(eax, Immediate(kSmiTagMask));
977 __ j(zero, &convert);
978 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
979 __ j(above_equal, &done_convert);
980 __ bind(&convert);
981 __ push(eax);
982 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
983 __ bind(&done_convert);
984 __ push(eax);
985
986 // TODO(kasperl): Check cache validity in generated code. This is a
987 // fast case for the JSObject::IsSimpleEnum cache validity
988 // checks. If we cannot guarantee cache validity, call the runtime
989 // system to check cache validity or get the property names in a
990 // fixed array.
991
992 // Get the set of properties to enumerate.
993 __ push(eax); // Duplicate the enumerable object on the stack.
994 __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
995
996 // If we got a map from the runtime call, we can do a fast
997 // modification check. Otherwise, we got a fixed array, and we have
998 // to do a slow check.
999 Label fixed_array;
1000 __ cmp(FieldOperand(eax, HeapObject::kMapOffset), Factory::meta_map());
1001 __ j(not_equal, &fixed_array);
1002
1003 // We got a map in register eax. Get the enumeration cache from it.
1004 __ mov(ecx, FieldOperand(eax, Map::kInstanceDescriptorsOffset));
1005 __ mov(ecx, FieldOperand(ecx, DescriptorArray::kEnumerationIndexOffset));
1006 __ mov(edx, FieldOperand(ecx, DescriptorArray::kEnumCacheBridgeCacheOffset));
1007
1008 // Setup the four remaining stack slots.
1009 __ push(eax); // Map.
1010 __ push(edx); // Enumeration cache.
1011 __ mov(eax, FieldOperand(edx, FixedArray::kLengthOffset));
1012 __ SmiTag(eax);
1013 __ push(eax); // Enumeration cache length (as smi).
1014 __ push(Immediate(Smi::FromInt(0))); // Initial index.
1015 __ jmp(&loop);
1016
1017 // We got a fixed array in register eax. Iterate through that.
1018 __ bind(&fixed_array);
1019 __ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check.
1020 __ push(eax);
1021 __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
1022 __ SmiTag(eax);
1023 __ push(eax); // Fixed array length (as smi).
1024 __ push(Immediate(Smi::FromInt(0))); // Initial index.
1025
1026 // Generate code for doing the condition check.
1027 __ bind(&loop);
1028 __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index.
1029 __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length.
1030 __ j(above_equal, loop_statement.break_target());
1031
1032 // Get the current entry of the array into register ebx.
1033 __ mov(ebx, Operand(esp, 2 * kPointerSize));
1034 __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize));
1035
1036 // Get the expected map from the stack or a zero map in the
1037 // permanent slow case into register edx.
1038 __ mov(edx, Operand(esp, 3 * kPointerSize));
1039
1040 // Check if the expected map still matches that of the enumerable.
1041 // If not, we have to filter the key.
1042 Label update_each;
1043 __ mov(ecx, Operand(esp, 4 * kPointerSize));
1044 __ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset));
1045 __ j(equal, &update_each);
1046
1047 // Convert the entry to a string or null if it isn't a property
1048 // anymore. If the property has been removed while iterating, we
1049 // just skip it.
1050 __ push(ecx); // Enumerable.
1051 __ push(ebx); // Current entry.
1052 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
1053 __ cmp(eax, Factory::null_value());
1054 __ j(equal, loop_statement.continue_target());
1055 __ mov(ebx, Operand(eax));
1056
1057 // Update the 'each' property or variable from the possibly filtered
1058 // entry in register ebx.
1059 __ bind(&update_each);
1060 __ mov(result_register(), ebx);
1061 // Perform the assignment as if via '='.
1062 EmitAssignment(stmt->each());
1063
1064 // Generate code for the body of the loop.
1065 Label stack_limit_hit, stack_check_done;
1066 Visit(stmt->body());
1067
1068 __ StackLimitCheck(&stack_limit_hit);
1069 __ bind(&stack_check_done);
1070
1071 // Generate code for going to the next element by incrementing the
1072 // index (smi) stored on top of the stack.
1073 __ bind(loop_statement.continue_target());
1074 __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1)));
1075 __ jmp(&loop);
1076
1077 // Slow case for the stack limit check.
1078 StackCheckStub stack_check_stub;
1079 __ bind(&stack_limit_hit);
1080 __ CallStub(&stack_check_stub);
1081 __ jmp(&stack_check_done);
1082
1083 // Remove the pointers stored on the stack.
1084 __ bind(loop_statement.break_target());
1085 __ add(Operand(esp), Immediate(5 * kPointerSize));
1086
1087 // Exit and decrement the loop depth.
1088 __ bind(&exit);
1089 decrement_loop_depth();
1090}
1091
1092
1093void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) {
1094 // Use the fast case closure allocation code that allocates in new
1095 // space for nested functions that don't need literals cloning.
1096 if (scope()->is_function_scope() && info->num_literals() == 0) {
1097 FastNewClosureStub stub;
1098 __ push(Immediate(info));
1099 __ CallStub(&stub);
1100 } else {
1101 __ push(esi);
1102 __ push(Immediate(info));
1103 __ CallRuntime(Runtime::kNewClosure, 2);
1104 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001105 Apply(context_, eax);
1106}
1107
1108
1109void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
1110 Comment cmnt(masm_, "[ VariableProxy");
1111 EmitVariableLoad(expr->var(), context_);
1112}
1113
1114
1115void FullCodeGenerator::EmitVariableLoad(Variable* var,
1116 Expression::Context context) {
1117 // Four cases: non-this global variables, lookup slots, all other
1118 // types of slots, and parameters that rewrite to explicit property
1119 // accesses on the arguments object.
1120 Slot* slot = var->slot();
1121 Property* property = var->AsProperty();
1122
1123 if (var->is_global() && !var->is_this()) {
1124 Comment cmnt(masm_, "Global variable");
1125 // Use inline caching. Variable name is passed in ecx and the global
1126 // object on the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00001127 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001128 __ mov(ecx, var->name());
1129 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
1130 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
1131 // By emitting a nop we make sure that we do not have a test eax
1132 // instruction after the call it is treated specially by the LoadIC code
1133 // Remember that the assembler may choose to do peephole optimization
1134 // (eg, push/pop elimination).
1135 __ nop();
ager@chromium.org5c838252010-02-19 08:53:10 +00001136 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001137
1138 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
1139 Comment cmnt(masm_, "Lookup slot");
1140 __ push(esi); // Context.
1141 __ push(Immediate(var->name()));
1142 __ CallRuntime(Runtime::kLoadContextSlot, 2);
1143 Apply(context, eax);
1144
1145 } else if (slot != NULL) {
1146 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT)
1147 ? "Context slot"
1148 : "Stack slot");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001149 if (var->mode() == Variable::CONST) {
1150 // Constants may be the hole value if they have not been initialized.
1151 // Unhole them.
1152 Label done;
1153 MemOperand slot_operand = EmitSlotSearch(slot, eax);
1154 __ mov(eax, slot_operand);
1155 __ cmp(eax, Factory::the_hole_value());
1156 __ j(not_equal, &done);
1157 __ mov(eax, Factory::undefined_value());
1158 __ bind(&done);
1159 Apply(context, eax);
1160 } else {
1161 Apply(context, slot);
1162 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001163
1164 } else {
1165 Comment cmnt(masm_, "Rewritten parameter");
1166 ASSERT_NOT_NULL(property);
1167 // Rewritten parameter accesses are of the form "slot[literal]".
1168
1169 // Assert that the object is in a slot.
1170 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
1171 ASSERT_NOT_NULL(object_var);
1172 Slot* object_slot = object_var->slot();
1173 ASSERT_NOT_NULL(object_slot);
1174
1175 // Load the object.
1176 MemOperand object_loc = EmitSlotSearch(object_slot, eax);
ager@chromium.org5c838252010-02-19 08:53:10 +00001177 __ mov(edx, object_loc);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001178
1179 // Assert that the key is a smi.
1180 Literal* key_literal = property->key()->AsLiteral();
1181 ASSERT_NOT_NULL(key_literal);
1182 ASSERT(key_literal->handle()->IsSmi());
1183
1184 // Load the key.
ager@chromium.org5c838252010-02-19 08:53:10 +00001185 __ mov(eax, Immediate(key_literal->handle()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001186
1187 // Do a keyed property load.
1188 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1189 __ call(ic, RelocInfo::CODE_TARGET);
1190 // Notice: We must not have a "test eax, ..." instruction after the
1191 // call. It is treated specially by the LoadIC code.
1192 __ nop();
1193 // Drop key and object left on the stack by IC.
ager@chromium.org5c838252010-02-19 08:53:10 +00001194 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001195 }
1196}
1197
1198
1199void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
1200 Comment cmnt(masm_, "[ RegExpLiteral");
1201 Label done;
1202 // Registers will be used as follows:
1203 // edi = JS function.
1204 // ebx = literals array.
1205 // eax = regexp literal.
1206 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1207 __ mov(ebx, FieldOperand(edi, JSFunction::kLiteralsOffset));
1208 int literal_offset =
1209 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
1210 __ mov(eax, FieldOperand(ebx, literal_offset));
1211 __ cmp(eax, Factory::undefined_value());
1212 __ j(not_equal, &done);
1213 // Create regexp literal using runtime function
1214 // Result will be in eax.
1215 __ push(ebx);
1216 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1217 __ push(Immediate(expr->pattern()));
1218 __ push(Immediate(expr->flags()));
1219 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
1220 // Label done:
1221 __ bind(&done);
1222 Apply(context_, eax);
1223}
1224
1225
1226void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
1227 Comment cmnt(masm_, "[ ObjectLiteral");
1228 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1229 __ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
1230 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1231 __ push(Immediate(expr->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001232 __ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001233 if (expr->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001234 __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001235 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001236 __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001237 }
1238
1239 // If result_saved is true the result is on top of the stack. If
1240 // result_saved is false the result is in eax.
1241 bool result_saved = false;
1242
1243 for (int i = 0; i < expr->properties()->length(); i++) {
1244 ObjectLiteral::Property* property = expr->properties()->at(i);
1245 if (property->IsCompileTimeValue()) continue;
1246
1247 Literal* key = property->key();
1248 Expression* value = property->value();
1249 if (!result_saved) {
1250 __ push(eax); // Save result on the stack
1251 result_saved = true;
1252 }
1253 switch (property->kind()) {
1254 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
1255 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
1256 // Fall through.
1257 case ObjectLiteral::Property::COMPUTED:
1258 if (key->handle()->IsSymbol()) {
1259 VisitForValue(value, kAccumulator);
1260 __ mov(ecx, Immediate(key->handle()));
1261 __ mov(edx, Operand(esp, 0));
1262 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1263 __ call(ic, RelocInfo::CODE_TARGET);
1264 __ nop();
1265 break;
1266 }
1267 // Fall through.
1268 case ObjectLiteral::Property::PROTOTYPE:
1269 __ push(Operand(esp, 0)); // Duplicate receiver.
1270 VisitForValue(key, kStack);
1271 VisitForValue(value, kStack);
1272 __ CallRuntime(Runtime::kSetProperty, 3);
1273 break;
1274 case ObjectLiteral::Property::SETTER:
1275 case ObjectLiteral::Property::GETTER:
1276 __ push(Operand(esp, 0)); // Duplicate receiver.
1277 VisitForValue(key, kStack);
1278 __ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ?
1279 Smi::FromInt(1) :
1280 Smi::FromInt(0)));
1281 VisitForValue(value, kStack);
1282 __ CallRuntime(Runtime::kDefineAccessor, 4);
1283 break;
1284 default: UNREACHABLE();
1285 }
1286 }
1287
1288 if (result_saved) {
1289 ApplyTOS(context_);
1290 } else {
1291 Apply(context_, eax);
1292 }
1293}
1294
1295
1296void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
1297 Comment cmnt(masm_, "[ ArrayLiteral");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001298
1299 ZoneList<Expression*>* subexprs = expr->values();
1300 int length = subexprs->length();
1301
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001302 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1303 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
1304 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1305 __ push(Immediate(expr->constant_elements()));
1306 if (expr->depth() > 1) {
1307 __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001308 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001309 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001310 } else {
1311 FastCloneShallowArrayStub stub(length);
1312 __ CallStub(&stub);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001313 }
1314
1315 bool result_saved = false; // Is the result saved to the stack?
1316
1317 // Emit code to evaluate all the non-constant subexpressions and to store
1318 // them into the newly cloned array.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001319 for (int i = 0; i < length; i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001320 Expression* subexpr = subexprs->at(i);
1321 // If the subexpression is a literal or a simple materialized literal it
1322 // is already set in the cloned array.
1323 if (subexpr->AsLiteral() != NULL ||
1324 CompileTimeValue::IsCompileTimeValue(subexpr)) {
1325 continue;
1326 }
1327
1328 if (!result_saved) {
1329 __ push(eax);
1330 result_saved = true;
1331 }
1332 VisitForValue(subexpr, kAccumulator);
1333
1334 // Store the subexpression value in the array's elements.
1335 __ mov(ebx, Operand(esp, 0)); // Copy of array literal.
1336 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
1337 int offset = FixedArray::kHeaderSize + (i * kPointerSize);
1338 __ mov(FieldOperand(ebx, offset), result_register());
1339
1340 // Update the write barrier for the array store.
1341 __ RecordWrite(ebx, offset, result_register(), ecx);
1342 }
1343
1344 if (result_saved) {
1345 ApplyTOS(context_);
1346 } else {
1347 Apply(context_, eax);
1348 }
1349}
1350
1351
ager@chromium.org5c838252010-02-19 08:53:10 +00001352void FullCodeGenerator::VisitAssignment(Assignment* expr) {
1353 Comment cmnt(masm_, "[ Assignment");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001354 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
1355 // on the left-hand side.
1356 if (!expr->target()->IsValidLeftHandSide()) {
1357 VisitForEffect(expr->target());
1358 return;
1359 }
1360
ager@chromium.org5c838252010-02-19 08:53:10 +00001361 // Left-hand side can only be a property, a global or a (parameter or local)
1362 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1363 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1364 LhsKind assign_type = VARIABLE;
1365 Property* prop = expr->target()->AsProperty();
1366 if (prop != NULL) {
1367 assign_type =
1368 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
1369 }
1370
1371 // Evaluate LHS expression.
1372 switch (assign_type) {
1373 case VARIABLE:
1374 // Nothing to do here.
1375 break;
1376 case NAMED_PROPERTY:
1377 if (expr->is_compound()) {
1378 // We need the receiver both on the stack and in the accumulator.
1379 VisitForValue(prop->obj(), kAccumulator);
1380 __ push(result_register());
1381 } else {
1382 VisitForValue(prop->obj(), kStack);
1383 }
1384 break;
1385 case KEYED_PROPERTY:
1386 if (expr->is_compound()) {
1387 VisitForValue(prop->obj(), kStack);
1388 VisitForValue(prop->key(), kAccumulator);
1389 __ mov(edx, Operand(esp, 0));
1390 __ push(eax);
1391 } else {
1392 VisitForValue(prop->obj(), kStack);
1393 VisitForValue(prop->key(), kStack);
1394 }
1395 break;
1396 }
1397
1398 // If we have a compound assignment: Get value of LHS expression and
1399 // store in on top of the stack.
1400 if (expr->is_compound()) {
1401 Location saved_location = location_;
1402 location_ = kStack;
1403 switch (assign_type) {
1404 case VARIABLE:
1405 EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
1406 Expression::kValue);
1407 break;
1408 case NAMED_PROPERTY:
1409 EmitNamedPropertyLoad(prop);
1410 __ push(result_register());
1411 break;
1412 case KEYED_PROPERTY:
1413 EmitKeyedPropertyLoad(prop);
1414 __ push(result_register());
1415 break;
1416 }
1417 location_ = saved_location;
1418 }
1419
1420 // Evaluate RHS expression.
1421 Expression* rhs = expr->value();
1422 VisitForValue(rhs, kAccumulator);
1423
1424 // If we have a compound assignment: Apply operator.
1425 if (expr->is_compound()) {
1426 Location saved_location = location_;
1427 location_ = kAccumulator;
1428 EmitBinaryOp(expr->binary_op(), Expression::kValue);
1429 location_ = saved_location;
1430 }
1431
1432 // Record source position before possible IC call.
1433 SetSourcePosition(expr->position());
1434
1435 // Store the value.
1436 switch (assign_type) {
1437 case VARIABLE:
1438 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001439 expr->op(),
ager@chromium.org5c838252010-02-19 08:53:10 +00001440 context_);
1441 break;
1442 case NAMED_PROPERTY:
1443 EmitNamedPropertyAssignment(expr);
1444 break;
1445 case KEYED_PROPERTY:
1446 EmitKeyedPropertyAssignment(expr);
1447 break;
1448 }
1449}
1450
1451
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001452void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
1453 SetSourcePosition(prop->position());
1454 Literal* key = prop->key()->AsLiteral();
1455 __ mov(ecx, Immediate(key->handle()));
1456 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
1457 __ call(ic, RelocInfo::CODE_TARGET);
1458 __ nop();
1459}
1460
1461
1462void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
1463 SetSourcePosition(prop->position());
1464 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1465 __ call(ic, RelocInfo::CODE_TARGET);
1466 __ nop();
1467}
1468
1469
1470void FullCodeGenerator::EmitBinaryOp(Token::Value op,
1471 Expression::Context context) {
1472 __ push(result_register());
1473 GenericBinaryOpStub stub(op,
1474 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001475 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001476 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001477 __ CallStub(&stub);
1478 Apply(context, eax);
1479}
1480
1481
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001482void FullCodeGenerator::EmitAssignment(Expression* expr) {
1483 // Invalid left-hand sides are rewritten to have a 'throw
1484 // ReferenceError' on the left-hand side.
1485 if (!expr->IsValidLeftHandSide()) {
1486 VisitForEffect(expr);
1487 return;
1488 }
1489
1490 // Left-hand side can only be a property, a global or a (parameter or local)
1491 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1492 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1493 LhsKind assign_type = VARIABLE;
1494 Property* prop = expr->AsProperty();
1495 if (prop != NULL) {
1496 assign_type = (prop->key()->IsPropertyName())
1497 ? NAMED_PROPERTY
1498 : KEYED_PROPERTY;
1499 }
1500
1501 switch (assign_type) {
1502 case VARIABLE: {
1503 Variable* var = expr->AsVariableProxy()->var();
1504 EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect);
1505 break;
1506 }
1507 case NAMED_PROPERTY: {
1508 __ push(eax); // Preserve value.
1509 VisitForValue(prop->obj(), kAccumulator);
1510 __ mov(edx, eax);
1511 __ pop(eax); // Restore value.
1512 __ mov(ecx, prop->key()->AsLiteral()->handle());
1513 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1514 __ call(ic, RelocInfo::CODE_TARGET);
1515 __ nop(); // Signal no inlined code.
1516 break;
1517 }
1518 case KEYED_PROPERTY: {
1519 __ push(eax); // Preserve value.
1520 VisitForValue(prop->obj(), kStack);
1521 VisitForValue(prop->key(), kAccumulator);
1522 __ mov(ecx, eax);
1523 __ pop(edx);
1524 __ pop(eax); // Restore value.
1525 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1526 __ call(ic, RelocInfo::CODE_TARGET);
1527 __ nop(); // Signal no inlined code.
1528 break;
1529 }
1530 }
1531}
1532
1533
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001534void FullCodeGenerator::EmitVariableAssignment(Variable* var,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001535 Token::Value op,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001536 Expression::Context context) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001537 // Left-hand sides that rewrite to explicit property accesses do not reach
1538 // here.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001539 ASSERT(var != NULL);
1540 ASSERT(var->is_global() || var->slot() != NULL);
1541
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001542 if (var->is_global()) {
1543 ASSERT(!var->is_this());
1544 // Assignment to a global variable. Use inline caching for the
1545 // assignment. Right-hand-side value is passed in eax, variable name in
1546 // ecx, and the global object on the stack.
1547 __ mov(ecx, var->name());
1548 __ mov(edx, CodeGenerator::GlobalObject());
1549 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1550 __ call(ic, RelocInfo::CODE_TARGET);
1551 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001552
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001553 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) {
1554 // Perform the assignment for non-const variables and for initialization
1555 // of const variables. Const assignments are simply skipped.
1556 Label done;
1557 Slot* slot = var->slot();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001558 switch (slot->type()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001559 case Slot::PARAMETER:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001560 case Slot::LOCAL:
1561 if (op == Token::INIT_CONST) {
1562 // Detect const reinitialization by checking for the hole value.
1563 __ mov(edx, Operand(ebp, SlotOffset(slot)));
1564 __ cmp(edx, Factory::the_hole_value());
1565 __ j(not_equal, &done);
1566 }
1567 // Perform the assignment.
1568 __ mov(Operand(ebp, SlotOffset(slot)), eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001569 break;
1570
1571 case Slot::CONTEXT: {
1572 MemOperand target = EmitSlotSearch(slot, ecx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001573 if (op == Token::INIT_CONST) {
1574 // Detect const reinitialization by checking for the hole value.
1575 __ mov(edx, target);
1576 __ cmp(edx, Factory::the_hole_value());
1577 __ j(not_equal, &done);
1578 }
1579 // Perform the assignment and issue the write barrier.
1580 __ mov(target, eax);
1581 // The value of the assignment is in eax. RecordWrite clobbers its
1582 // register arguments.
1583 __ mov(edx, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001584 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
1585 __ RecordWrite(ecx, offset, edx, ebx);
1586 break;
1587 }
1588
1589 case Slot::LOOKUP:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001590 // Call the runtime for the assignment. The runtime will ignore
1591 // const reinitialization.
1592 __ push(eax); // Value.
1593 __ push(esi); // Context.
1594 __ push(Immediate(var->name()));
1595 if (op == Token::INIT_CONST) {
1596 // The runtime will ignore const redeclaration.
1597 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
1598 } else {
1599 __ CallRuntime(Runtime::kStoreContextSlot, 3);
1600 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001601 break;
1602 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001603 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001604 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001605
1606 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001607}
1608
1609
1610void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
1611 // Assignment to a property, using a named store IC.
1612 Property* prop = expr->target()->AsProperty();
1613 ASSERT(prop != NULL);
1614 ASSERT(prop->key()->AsLiteral() != NULL);
1615
1616 // If the assignment starts a block of assignments to the same object,
1617 // change to slow case to avoid the quadratic behavior of repeatedly
1618 // adding fast properties.
1619 if (expr->starts_initialization_block()) {
1620 __ push(result_register());
1621 __ push(Operand(esp, kPointerSize)); // Receiver is now under value.
1622 __ CallRuntime(Runtime::kToSlowProperties, 1);
1623 __ pop(result_register());
1624 }
1625
1626 // Record source code position before IC call.
1627 SetSourcePosition(expr->position());
1628 __ mov(ecx, prop->key()->AsLiteral()->handle());
1629 if (expr->ends_initialization_block()) {
1630 __ mov(edx, Operand(esp, 0));
1631 } else {
1632 __ pop(edx);
1633 }
1634 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1635 __ call(ic, RelocInfo::CODE_TARGET);
1636 __ nop();
1637
1638 // If the assignment ends an initialization block, revert to fast case.
1639 if (expr->ends_initialization_block()) {
1640 __ push(eax); // Result of assignment, saved even if not needed.
1641 __ push(Operand(esp, kPointerSize)); // Receiver is under value.
1642 __ CallRuntime(Runtime::kToFastProperties, 1);
1643 __ pop(eax);
1644 DropAndApply(1, context_, eax);
1645 } else {
1646 Apply(context_, eax);
1647 }
1648}
1649
1650
1651void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
1652 // Assignment to a property, using a keyed store IC.
1653
1654 // If the assignment starts a block of assignments to the same object,
1655 // change to slow case to avoid the quadratic behavior of repeatedly
1656 // adding fast properties.
1657 if (expr->starts_initialization_block()) {
1658 __ push(result_register());
1659 // Receiver is now under the key and value.
1660 __ push(Operand(esp, 2 * kPointerSize));
1661 __ CallRuntime(Runtime::kToSlowProperties, 1);
1662 __ pop(result_register());
1663 }
1664
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001665 __ pop(ecx);
1666 if (expr->ends_initialization_block()) {
1667 __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later.
1668 } else {
1669 __ pop(edx);
1670 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001671 // Record source code position before IC call.
1672 SetSourcePosition(expr->position());
1673 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1674 __ call(ic, RelocInfo::CODE_TARGET);
1675 // This nop signals to the IC that there is no inlined code at the call
1676 // site for it to patch.
1677 __ nop();
1678
1679 // If the assignment ends an initialization block, revert to fast case.
1680 if (expr->ends_initialization_block()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001681 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001682 __ push(eax); // Result of assignment, saved even if not needed.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001683 __ push(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001684 __ CallRuntime(Runtime::kToFastProperties, 1);
1685 __ pop(eax);
1686 }
1687
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001688 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001689}
1690
1691
1692void FullCodeGenerator::VisitProperty(Property* expr) {
1693 Comment cmnt(masm_, "[ Property");
1694 Expression* key = expr->key();
1695
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001696 if (key->IsPropertyName()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00001697 VisitForValue(expr->obj(), kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001698 EmitNamedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001699 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001700 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00001701 VisitForValue(expr->obj(), kStack);
1702 VisitForValue(expr->key(), kAccumulator);
1703 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001704 EmitKeyedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001705 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001706 }
1707}
1708
1709
1710void FullCodeGenerator::EmitCallWithIC(Call* expr,
1711 Handle<Object> name,
1712 RelocInfo::Mode mode) {
1713 // Code common for calls using the IC.
1714 ZoneList<Expression*>* args = expr->arguments();
1715 int arg_count = args->length();
1716 for (int i = 0; i < arg_count; i++) {
1717 VisitForValue(args->at(i), kStack);
1718 }
1719 __ Set(ecx, Immediate(name));
1720 // Record source position of the IC call.
1721 SetSourcePosition(expr->position());
1722 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1723 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
1724 __ call(ic, mode);
1725 // Restore context register.
1726 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1727 Apply(context_, eax);
1728}
1729
1730
1731void FullCodeGenerator::EmitCallWithStub(Call* expr) {
1732 // Code common for calls using the call stub.
1733 ZoneList<Expression*>* args = expr->arguments();
1734 int arg_count = args->length();
1735 for (int i = 0; i < arg_count; i++) {
1736 VisitForValue(args->at(i), kStack);
1737 }
1738 // Record source position for debugger.
1739 SetSourcePosition(expr->position());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001740 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1741 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001742 __ CallStub(&stub);
1743 // Restore context register.
1744 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1745 DropAndApply(1, context_, eax);
1746}
1747
1748
1749void FullCodeGenerator::VisitCall(Call* expr) {
1750 Comment cmnt(masm_, "[ Call");
1751 Expression* fun = expr->expression();
1752 Variable* var = fun->AsVariableProxy()->AsVariable();
1753
1754 if (var != NULL && var->is_possibly_eval()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001755 // In a call to eval, we first call %ResolvePossiblyDirectEval to
1756 // resolve the function we need to call and the receiver of the
1757 // call. Then we call the resolved function using the given
1758 // arguments.
1759 VisitForValue(fun, kStack);
1760 __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
1761
1762 // Push the arguments.
1763 ZoneList<Expression*>* args = expr->arguments();
1764 int arg_count = args->length();
1765 for (int i = 0; i < arg_count; i++) {
1766 VisitForValue(args->at(i), kStack);
1767 }
1768
1769 // Push copy of the function - found below the arguments.
1770 __ push(Operand(esp, (arg_count + 1) * kPointerSize));
1771
1772 // Push copy of the first argument or undefined if it doesn't exist.
1773 if (arg_count > 0) {
1774 __ push(Operand(esp, arg_count * kPointerSize));
1775 } else {
1776 __ push(Immediate(Factory::undefined_value()));
1777 }
1778
1779 // Push the receiver of the enclosing function and do runtime call.
1780 __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
1781 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
1782
1783 // The runtime call returns a pair of values in eax (function) and
1784 // edx (receiver). Touch up the stack with the right values.
1785 __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
1786 __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
1787
1788 // Record source position for debugger.
1789 SetSourcePosition(expr->position());
1790 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1791 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
1792 __ CallStub(&stub);
1793 // Restore context register.
1794 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1795 DropAndApply(1, context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001796 } else if (var != NULL && !var->is_this() && var->is_global()) {
1797 // Push global object as receiver for the call IC.
1798 __ push(CodeGenerator::GlobalObject());
1799 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
1800 } else if (var != NULL && var->slot() != NULL &&
1801 var->slot()->type() == Slot::LOOKUP) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001802 // Call to a lookup slot (dynamically introduced variable). Call the
1803 // runtime to find the function to call (returned in eax) and the object
1804 // holding it (returned in edx).
1805 __ push(context_register());
1806 __ push(Immediate(var->name()));
1807 __ CallRuntime(Runtime::kLoadContextSlot, 2);
1808 __ push(eax); // Function.
1809 __ push(edx); // Receiver.
1810 EmitCallWithStub(expr);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001811 } else if (fun->AsProperty() != NULL) {
1812 // Call to an object property.
1813 Property* prop = fun->AsProperty();
1814 Literal* key = prop->key()->AsLiteral();
1815 if (key != NULL && key->handle()->IsSymbol()) {
1816 // Call to a named property, use call IC.
1817 VisitForValue(prop->obj(), kStack);
1818 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
1819 } else {
1820 // Call to a keyed property, use keyed load IC followed by function
1821 // call.
1822 VisitForValue(prop->obj(), kStack);
ager@chromium.org5c838252010-02-19 08:53:10 +00001823 VisitForValue(prop->key(), kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001824 // Record source code position for IC call.
1825 SetSourcePosition(prop->position());
ager@chromium.org5c838252010-02-19 08:53:10 +00001826 if (prop->is_synthetic()) {
1827 __ pop(edx); // We do not need to keep the receiver.
1828 } else {
1829 __ mov(edx, Operand(esp, 0)); // Keep receiver, to call function on.
1830 }
1831
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001832 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1833 __ call(ic, RelocInfo::CODE_TARGET);
1834 // By emitting a nop we make sure that we do not have a "test eax,..."
1835 // instruction after the call it is treated specially by the LoadIC code.
1836 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001837 if (prop->is_synthetic()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00001838 // Push result (function).
1839 __ push(eax);
1840 // Push Global receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001841 __ mov(ecx, CodeGenerator::GlobalObject());
1842 __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
1843 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00001844 // Pop receiver.
1845 __ pop(ebx);
1846 // Push result (function).
1847 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001848 __ push(ebx);
1849 }
1850 EmitCallWithStub(expr);
1851 }
1852 } else {
1853 // Call to some other expression. If the expression is an anonymous
1854 // function literal not called in a loop, mark it as one that should
1855 // also use the full code generator.
1856 FunctionLiteral* lit = fun->AsFunctionLiteral();
1857 if (lit != NULL &&
1858 lit->name()->Equals(Heap::empty_string()) &&
1859 loop_depth() == 0) {
1860 lit->set_try_full_codegen(true);
1861 }
1862 VisitForValue(fun, kStack);
1863 // Load global receiver object.
1864 __ mov(ebx, CodeGenerator::GlobalObject());
1865 __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
1866 // Emit function call.
1867 EmitCallWithStub(expr);
1868 }
1869}
1870
1871
1872void FullCodeGenerator::VisitCallNew(CallNew* expr) {
1873 Comment cmnt(masm_, "[ CallNew");
1874 // According to ECMA-262, section 11.2.2, page 44, the function
1875 // expression in new calls must be evaluated before the
1876 // arguments.
1877 // Push function on the stack.
1878 VisitForValue(expr->expression(), kStack);
1879
1880 // Push global object (receiver).
1881 __ push(CodeGenerator::GlobalObject());
1882
1883 // Push the arguments ("left-to-right") on the stack.
1884 ZoneList<Expression*>* args = expr->arguments();
1885 int arg_count = args->length();
1886 for (int i = 0; i < arg_count; i++) {
1887 VisitForValue(args->at(i), kStack);
1888 }
1889
1890 // Call the construct call builtin that handles allocation and
1891 // constructor invocation.
1892 SetSourcePosition(expr->position());
1893
1894 // Load function, arg_count into edi and eax.
1895 __ Set(eax, Immediate(arg_count));
1896 // Function is in esp[arg_count + 1].
1897 __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize));
1898
1899 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall));
1900 __ call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
1901
1902 // Replace function on TOS with result in eax, or pop it.
1903 DropAndApply(1, context_, eax);
1904}
1905
1906
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001907void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
1908 Handle<String> name = expr->name();
1909 if (strcmp("_IsSmi", *name->ToCString()) == 0) {
1910 EmitIsSmi(expr->arguments());
1911 } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
1912 EmitIsNonNegativeSmi(expr->arguments());
1913 } else if (strcmp("_IsObject", *name->ToCString()) == 0) {
1914 EmitIsObject(expr->arguments());
1915 } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
1916 EmitIsUndetectableObject(expr->arguments());
1917 } else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
1918 EmitIsFunction(expr->arguments());
1919 } else if (strcmp("_IsArray", *name->ToCString()) == 0) {
1920 EmitIsArray(expr->arguments());
1921 } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
1922 EmitIsRegExp(expr->arguments());
1923 } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
1924 EmitIsConstructCall(expr->arguments());
1925 } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
1926 EmitObjectEquals(expr->arguments());
1927 } else if (strcmp("_Arguments", *name->ToCString()) == 0) {
1928 EmitArguments(expr->arguments());
1929 } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
1930 EmitArgumentsLength(expr->arguments());
1931 } else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
1932 EmitClassOf(expr->arguments());
1933 } else if (strcmp("_Log", *name->ToCString()) == 0) {
1934 EmitLog(expr->arguments());
1935 } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
1936 EmitRandomHeapNumber(expr->arguments());
1937 } else if (strcmp("_SubString", *name->ToCString()) == 0) {
1938 EmitSubString(expr->arguments());
1939 } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
1940 EmitRegExpExec(expr->arguments());
1941 } else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
1942 EmitValueOf(expr->arguments());
1943 } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
1944 EmitSetValueOf(expr->arguments());
1945 } else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
1946 EmitNumberToString(expr->arguments());
1947 } else if (strcmp("_CharFromCode", *name->ToCString()) == 0) {
1948 EmitCharFromCode(expr->arguments());
1949 } else if (strcmp("_FastCharCodeAt", *name->ToCString()) == 0) {
1950 EmitFastCharCodeAt(expr->arguments());
1951 } else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
1952 EmitStringAdd(expr->arguments());
1953 } else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
1954 EmitStringCompare(expr->arguments());
1955 } else if (strcmp("_MathPow", *name->ToCString()) == 0) {
1956 EmitMathPow(expr->arguments());
1957 } else if (strcmp("_MathSin", *name->ToCString()) == 0) {
1958 EmitMathSin(expr->arguments());
1959 } else if (strcmp("_MathCos", *name->ToCString()) == 0) {
1960 EmitMathCos(expr->arguments());
1961 } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
1962 EmitMathSqrt(expr->arguments());
1963 } else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
1964 EmitCallFunction(expr->arguments());
1965 } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
1966 EmitRegExpConstructResult(expr->arguments());
1967 } else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
1968 EmitSwapElements(expr->arguments());
1969 } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
1970 EmitGetFromCache(expr->arguments());
1971 } else {
1972 UNREACHABLE();
1973 }
1974}
1975
1976
1977void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
1978 ASSERT(args->length() == 1);
1979
1980 VisitForValue(args->at(0), kAccumulator);
1981
1982 Label materialize_true, materialize_false;
1983 Label* if_true = NULL;
1984 Label* if_false = NULL;
1985 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1986
1987 __ test(eax, Immediate(kSmiTagMask));
1988 __ j(zero, if_true);
1989 __ jmp(if_false);
1990
1991 Apply(context_, if_true, if_false);
1992}
1993
1994
1995void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) {
1996 ASSERT(args->length() == 1);
1997
1998 VisitForValue(args->at(0), kAccumulator);
1999
2000 Label materialize_true, materialize_false;
2001 Label* if_true = NULL;
2002 Label* if_false = NULL;
2003 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2004
2005 __ test(eax, Immediate(kSmiTagMask | 0x80000000));
2006 __ j(zero, if_true);
2007 __ jmp(if_false);
2008
2009 Apply(context_, if_true, if_false);
2010}
2011
2012
2013void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
2014 ASSERT(args->length() == 1);
2015
2016 VisitForValue(args->at(0), kAccumulator);
2017
2018 Label materialize_true, materialize_false;
2019 Label* if_true = NULL;
2020 Label* if_false = NULL;
2021 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2022
2023 __ test(eax, Immediate(kSmiTagMask));
2024 __ j(zero, if_false);
2025 __ cmp(eax, Factory::null_value());
2026 __ j(equal, if_true);
2027 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2028 // Undetectable objects behave like undefined when tested with typeof.
2029 __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset));
2030 __ test(ecx, Immediate(1 << Map::kIsUndetectable));
2031 __ j(not_zero, if_false);
2032 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset));
2033 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
2034 __ j(below, if_false);
2035 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
2036 __ j(below_equal, if_true);
2037 __ jmp(if_false);
2038
2039 Apply(context_, if_true, if_false);
2040}
2041
2042
2043void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
2044 ASSERT(args->length() == 1);
2045
2046 VisitForValue(args->at(0), kAccumulator);
2047
2048 Label materialize_true, materialize_false;
2049 Label* if_true = NULL;
2050 Label* if_false = NULL;
2051 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2052
2053 __ test(eax, Immediate(kSmiTagMask));
2054 __ j(zero, if_false);
2055 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2056 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
2057 __ test(ebx, Immediate(1 << Map::kIsUndetectable));
2058 __ j(not_zero, if_true);
2059 __ jmp(if_false);
2060
2061 Apply(context_, if_true, if_false);
2062}
2063
2064
2065void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
2066 ASSERT(args->length() == 1);
2067
2068 VisitForValue(args->at(0), kAccumulator);
2069
2070 Label materialize_true, materialize_false;
2071 Label* if_true = NULL;
2072 Label* if_false = NULL;
2073 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2074
2075 __ test(eax, Immediate(kSmiTagMask));
2076 __ j(zero, if_false);
2077 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2078 __ j(equal, if_true);
2079 __ jmp(if_false);
2080
2081 Apply(context_, if_true, if_false);
2082}
2083
2084
2085void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) {
2086 ASSERT(args->length() == 1);
2087
2088 VisitForValue(args->at(0), kAccumulator);
2089
2090 Label materialize_true, materialize_false;
2091 Label* if_true = NULL;
2092 Label* if_false = NULL;
2093 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2094
2095 __ test(eax, Immediate(kSmiTagMask));
2096 __ j(equal, if_false);
2097 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
2098 __ j(equal, if_true);
2099 __ jmp(if_false);
2100
2101 Apply(context_, if_true, if_false);
2102}
2103
2104
2105void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) {
2106 ASSERT(args->length() == 1);
2107
2108 VisitForValue(args->at(0), kAccumulator);
2109
2110 Label materialize_true, materialize_false;
2111 Label* if_true = NULL;
2112 Label* if_false = NULL;
2113 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2114
2115 __ test(eax, Immediate(kSmiTagMask));
2116 __ j(equal, if_false);
2117 __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx);
2118 __ j(equal, if_true);
2119 __ jmp(if_false);
2120
2121 Apply(context_, if_true, if_false);
2122}
2123
2124
2125
2126void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) {
2127 ASSERT(args->length() == 0);
2128
2129 Label materialize_true, materialize_false;
2130 Label* if_true = NULL;
2131 Label* if_false = NULL;
2132 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2133
2134 // Get the frame pointer for the calling frame.
2135 __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2136
2137 // Skip the arguments adaptor frame if it exists.
2138 Label check_frame_marker;
2139 __ cmp(Operand(eax, StandardFrameConstants::kContextOffset),
2140 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2141 __ j(not_equal, &check_frame_marker);
2142 __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset));
2143
2144 // Check the marker in the calling frame.
2145 __ bind(&check_frame_marker);
2146 __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset),
2147 Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
2148 __ j(equal, if_true);
2149 __ jmp(if_false);
2150
2151 Apply(context_, if_true, if_false);
2152}
2153
2154
2155void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) {
2156 ASSERT(args->length() == 2);
2157
2158 // Load the two objects into registers and perform the comparison.
2159 VisitForValue(args->at(0), kStack);
2160 VisitForValue(args->at(1), kAccumulator);
2161
2162 Label materialize_true, materialize_false;
2163 Label* if_true = NULL;
2164 Label* if_false = NULL;
2165 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2166
2167 __ pop(ebx);
2168 __ cmp(eax, Operand(ebx));
2169 __ j(equal, if_true);
2170 __ jmp(if_false);
2171
2172 Apply(context_, if_true, if_false);
2173}
2174
2175
2176void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) {
2177 ASSERT(args->length() == 1);
2178
2179 // ArgumentsAccessStub expects the key in edx and the formal
2180 // parameter count in eax.
2181 VisitForValue(args->at(0), kAccumulator);
2182 __ mov(edx, eax);
2183 __ mov(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2184 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
2185 __ CallStub(&stub);
2186 Apply(context_, eax);
2187}
2188
2189
2190void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
2191 ASSERT(args->length() == 0);
2192
2193 Label exit;
2194 // Get the number of formal parameters.
2195 __ Set(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2196
2197 // Check if the calling frame is an arguments adaptor frame.
2198 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2199 __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset),
2200 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2201 __ j(not_equal, &exit);
2202
2203 // Arguments adaptor case: Read the arguments length from the
2204 // adaptor frame.
2205 __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
2206
2207 __ bind(&exit);
2208 if (FLAG_debug_code) __ AbortIfNotSmi(eax);
2209 Apply(context_, eax);
2210}
2211
2212
2213void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) {
2214 ASSERT(args->length() == 1);
2215 Label done, null, function, non_function_constructor;
2216
2217 VisitForValue(args->at(0), kAccumulator);
2218
2219 // If the object is a smi, we return null.
2220 __ test(eax, Immediate(kSmiTagMask));
2221 __ j(zero, &null);
2222
2223 // Check that the object is a JS object but take special care of JS
2224 // functions to make sure they have 'Function' as their class.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002225 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, eax); // Map is now in eax.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002226 __ j(below, &null);
2227
2228 // As long as JS_FUNCTION_TYPE is the last instance type and it is
2229 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
2230 // LAST_JS_OBJECT_TYPE.
2231 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
2232 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
2233 __ cmp(ebx, JS_FUNCTION_TYPE);
2234 __ j(equal, &function);
2235
2236 // Check if the constructor in the map is a function.
2237 __ mov(eax, FieldOperand(eax, Map::kConstructorOffset));
2238 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2239 __ j(not_equal, &non_function_constructor);
2240
2241 // eax now contains the constructor function. Grab the
2242 // instance class name from there.
2243 __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
2244 __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset));
2245 __ jmp(&done);
2246
2247 // Functions have class 'Function'.
2248 __ bind(&function);
2249 __ mov(eax, Factory::function_class_symbol());
2250 __ jmp(&done);
2251
2252 // Objects with a non-function constructor have class 'Object'.
2253 __ bind(&non_function_constructor);
2254 __ mov(eax, Factory::Object_symbol());
2255 __ jmp(&done);
2256
2257 // Non-JS objects have class null.
2258 __ bind(&null);
2259 __ mov(eax, Factory::null_value());
2260
2261 // All done.
2262 __ bind(&done);
2263
2264 Apply(context_, eax);
2265}
2266
2267
2268void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) {
2269 // Conditionally generate a log call.
2270 // Args:
2271 // 0 (literal string): The type of logging (corresponds to the flags).
2272 // This is used to determine whether or not to generate the log call.
2273 // 1 (string): Format string. Access the string at argument index 2
2274 // with '%2s' (see Logger::LogRuntime for all the formats).
2275 // 2 (array): Arguments to the format string.
2276 ASSERT_EQ(args->length(), 3);
2277#ifdef ENABLE_LOGGING_AND_PROFILING
2278 if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
2279 VisitForValue(args->at(1), kStack);
2280 VisitForValue(args->at(2), kStack);
2281 __ CallRuntime(Runtime::kLog, 2);
2282 }
2283#endif
2284 // Finally, we're expected to leave a value on the top of the stack.
2285 __ mov(eax, Factory::undefined_value());
2286 Apply(context_, eax);
2287}
2288
2289
2290void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
2291 ASSERT(args->length() == 0);
2292
2293 Label slow_allocate_heapnumber;
2294 Label heapnumber_allocated;
2295
2296 __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
2297 __ jmp(&heapnumber_allocated);
2298
2299 __ bind(&slow_allocate_heapnumber);
2300 // To allocate a heap number, and ensure that it is not a smi, we
2301 // call the runtime function FUnaryMinus on 0, returning the double
2302 // -0.0. A new, distinct heap number is returned each time.
2303 __ push(Immediate(Smi::FromInt(0)));
2304 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
2305 __ mov(edi, eax);
2306
2307 __ bind(&heapnumber_allocated);
2308
2309 __ PrepareCallCFunction(0, ebx);
2310 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
2311
2312 // Convert 32 random bits in eax to 0.(32 random bits) in a double
2313 // by computing:
2314 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
2315 // This is implemented on both SSE2 and FPU.
2316 if (CpuFeatures::IsSupported(SSE2)) {
2317 CpuFeatures::Scope fscope(SSE2);
2318 __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
2319 __ movd(xmm1, Operand(ebx));
2320 __ movd(xmm0, Operand(eax));
2321 __ cvtss2sd(xmm1, xmm1);
2322 __ pxor(xmm0, xmm1);
2323 __ subsd(xmm0, xmm1);
2324 __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
2325 } else {
2326 // 0x4130000000000000 is 1.0 x 2^20 as a double.
2327 __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
2328 Immediate(0x41300000));
2329 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
2330 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2331 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
2332 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2333 __ fsubp(1);
2334 __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
2335 }
2336 __ mov(eax, edi);
2337 Apply(context_, eax);
2338}
2339
2340
2341void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) {
2342 // Load the arguments on the stack and call the stub.
2343 SubStringStub stub;
2344 ASSERT(args->length() == 3);
2345 VisitForValue(args->at(0), kStack);
2346 VisitForValue(args->at(1), kStack);
2347 VisitForValue(args->at(2), kStack);
2348 __ CallStub(&stub);
2349 Apply(context_, eax);
2350}
2351
2352
2353void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) {
2354 // Load the arguments on the stack and call the stub.
2355 RegExpExecStub stub;
2356 ASSERT(args->length() == 4);
2357 VisitForValue(args->at(0), kStack);
2358 VisitForValue(args->at(1), kStack);
2359 VisitForValue(args->at(2), kStack);
2360 VisitForValue(args->at(3), kStack);
2361 __ CallStub(&stub);
2362 Apply(context_, eax);
2363}
2364
2365
2366void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
2367 ASSERT(args->length() == 1);
2368
2369 VisitForValue(args->at(0), kAccumulator); // Load the object.
2370
2371 Label done;
2372 // If the object is a smi return the object.
2373 __ test(eax, Immediate(kSmiTagMask));
2374 __ j(zero, &done);
2375 // If the object is not a value type, return the object.
2376 __ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
2377 __ j(not_equal, &done);
2378 __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
2379
2380 __ bind(&done);
2381 Apply(context_, eax);
2382}
2383
2384
2385void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) {
2386 // Load the arguments on the stack and call the runtime function.
2387 ASSERT(args->length() == 2);
2388 VisitForValue(args->at(0), kStack);
2389 VisitForValue(args->at(1), kStack);
2390 __ CallRuntime(Runtime::kMath_pow, 2);
2391 Apply(context_, eax);
2392}
2393
2394
2395void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
2396 ASSERT(args->length() == 2);
2397
2398 VisitForValue(args->at(0), kStack); // Load the object.
2399 VisitForValue(args->at(1), kAccumulator); // Load the value.
2400 __ pop(ebx); // eax = value. ebx = object.
2401
2402 Label done;
2403 // If the object is a smi, return the value.
2404 __ test(ebx, Immediate(kSmiTagMask));
2405 __ j(zero, &done);
2406
2407 // If the object is not a value type, return the value.
2408 __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
2409 __ j(not_equal, &done);
2410
2411 // Store the value.
2412 __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax);
2413 // Update the write barrier. Save the value as it will be
2414 // overwritten by the write barrier code and is needed afterward.
2415 __ mov(edx, eax);
2416 __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx);
2417
2418 __ bind(&done);
2419 Apply(context_, eax);
2420}
2421
2422
2423void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
2424 ASSERT_EQ(args->length(), 1);
2425
2426 // Load the argument on the stack and call the stub.
2427 VisitForValue(args->at(0), kStack);
2428
2429 NumberToStringStub stub;
2430 __ CallStub(&stub);
2431 Apply(context_, eax);
2432}
2433
2434
2435void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
2436 ASSERT(args->length() == 1);
2437
2438 VisitForValue(args->at(0), kAccumulator);
2439
2440 Label slow_case, done;
2441 // Fast case of Heap::LookupSingleCharacterStringFromCode.
2442 ASSERT(kSmiTag == 0);
2443 ASSERT(kSmiShiftSize == 0);
2444 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
2445 __ test(eax,
2446 Immediate(kSmiTagMask |
2447 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
2448 __ j(not_zero, &slow_case);
2449 __ Set(ebx, Immediate(Factory::single_character_string_cache()));
2450 ASSERT(kSmiTag == 0);
2451 ASSERT(kSmiTagSize == 1);
2452 ASSERT(kSmiShiftSize == 0);
2453 // At this point code register contains smi tagged ascii char code.
2454 __ mov(ebx, FieldOperand(ebx,
2455 eax, times_half_pointer_size,
2456 FixedArray::kHeaderSize));
2457 __ cmp(ebx, Factory::undefined_value());
2458 __ j(equal, &slow_case);
2459 __ mov(eax, ebx);
2460 __ jmp(&done);
2461
2462 __ bind(&slow_case);
2463 __ push(eax);
2464 __ CallRuntime(Runtime::kCharFromCode, 1);
2465
2466 __ bind(&done);
2467 Apply(context_, eax);
2468}
2469
2470
2471void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
2472 // TODO(fsc): Port the complete implementation from the classic back-end.
2473 // Move the undefined value into the result register, which will
2474 // trigger the slow case.
2475 __ Set(eax, Immediate(Factory::undefined_value()));
2476 Apply(context_, eax);
2477}
2478
2479void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
2480 ASSERT_EQ(2, args->length());
2481
2482 VisitForValue(args->at(0), kStack);
2483 VisitForValue(args->at(1), kStack);
2484
2485 StringAddStub stub(NO_STRING_ADD_FLAGS);
2486 __ CallStub(&stub);
2487 Apply(context_, eax);
2488}
2489
2490
2491void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) {
2492 ASSERT_EQ(2, args->length());
2493
2494 VisitForValue(args->at(0), kStack);
2495 VisitForValue(args->at(1), kStack);
2496
2497 StringCompareStub stub;
2498 __ CallStub(&stub);
2499 Apply(context_, eax);
2500}
2501
2502
2503void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) {
2504 // Load the argument on the stack and call the stub.
2505 TranscendentalCacheStub stub(TranscendentalCache::SIN);
2506 ASSERT(args->length() == 1);
2507 VisitForValue(args->at(0), kStack);
2508 __ CallStub(&stub);
2509 Apply(context_, eax);
2510}
2511
2512
2513void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
2514 // Load the argument on the stack and call the stub.
2515 TranscendentalCacheStub stub(TranscendentalCache::COS);
2516 ASSERT(args->length() == 1);
2517 VisitForValue(args->at(0), kStack);
2518 __ CallStub(&stub);
2519 Apply(context_, eax);
2520}
2521
2522
2523void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
2524 // Load the argument on the stack and call the runtime function.
2525 ASSERT(args->length() == 1);
2526 VisitForValue(args->at(0), kStack);
2527 __ CallRuntime(Runtime::kMath_sqrt, 1);
2528 Apply(context_, eax);
2529}
2530
2531
2532void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
2533 ASSERT(args->length() >= 2);
2534
2535 int arg_count = args->length() - 2; // For receiver and function.
2536 VisitForValue(args->at(0), kStack); // Receiver.
2537 for (int i = 0; i < arg_count; i++) {
2538 VisitForValue(args->at(i + 1), kStack);
2539 }
2540 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function.
2541
2542 // InvokeFunction requires function in edi. Move it in there.
2543 if (!result_register().is(edi)) __ mov(edi, result_register());
2544 ParameterCount count(arg_count);
2545 __ InvokeFunction(edi, count, CALL_FUNCTION);
2546 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2547 Apply(context_, eax);
2548}
2549
2550
2551void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) {
2552 ASSERT(args->length() == 3);
2553 VisitForValue(args->at(0), kStack);
2554 VisitForValue(args->at(1), kStack);
2555 VisitForValue(args->at(2), kStack);
2556 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
2557 Apply(context_, eax);
2558}
2559
2560
2561void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
2562 ASSERT(args->length() == 3);
2563 VisitForValue(args->at(0), kStack);
2564 VisitForValue(args->at(1), kStack);
2565 VisitForValue(args->at(2), kStack);
2566 __ CallRuntime(Runtime::kSwapElements, 3);
2567 Apply(context_, eax);
2568}
2569
2570
2571void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
2572 ASSERT_EQ(2, args->length());
2573
2574 ASSERT_NE(NULL, args->at(0)->AsLiteral());
2575 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
2576
2577 Handle<FixedArray> jsfunction_result_caches(
2578 Top::global_context()->jsfunction_result_caches());
2579 if (jsfunction_result_caches->length() <= cache_id) {
2580 __ Abort("Attempt to use undefined cache.");
2581 __ mov(eax, Factory::undefined_value());
2582 Apply(context_, eax);
2583 return;
2584 }
2585
2586 VisitForValue(args->at(1), kAccumulator);
2587
2588 Register key = eax;
2589 Register cache = ebx;
2590 Register tmp = ecx;
2591 __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX));
2592 __ mov(cache,
2593 FieldOperand(cache, GlobalObject::kGlobalContextOffset));
2594 __ mov(cache,
2595 CodeGenerator::ContextOperand(
2596 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
2597 __ mov(cache,
2598 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
2599
2600 Label done, not_found;
2601 // tmp now holds finger offset as a smi.
2602 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
2603 __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
2604 __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp));
2605 __ j(not_equal, &not_found);
2606
2607 __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1));
2608 __ jmp(&done);
2609
2610 __ bind(&not_found);
2611 // Call runtime to perform the lookup.
2612 __ push(cache);
2613 __ push(key);
2614 __ CallRuntime(Runtime::kGetFromCache, 2);
2615
2616 __ bind(&done);
2617 Apply(context_, eax);
2618}
2619
2620
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002621void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002622 Handle<String> name = expr->name();
2623 if (name->length() > 0 && name->Get(0) == '_') {
2624 Comment cmnt(masm_, "[ InlineRuntimeCall");
2625 EmitInlineRuntimeCall(expr);
2626 return;
2627 }
2628
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002629 Comment cmnt(masm_, "[ CallRuntime");
2630 ZoneList<Expression*>* args = expr->arguments();
2631
2632 if (expr->is_jsruntime()) {
2633 // Prepare for calling JS runtime function.
2634 __ mov(eax, CodeGenerator::GlobalObject());
2635 __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
2636 }
2637
2638 // Push the arguments ("left-to-right").
2639 int arg_count = args->length();
2640 for (int i = 0; i < arg_count; i++) {
2641 VisitForValue(args->at(i), kStack);
2642 }
2643
2644 if (expr->is_jsruntime()) {
2645 // Call the JS runtime function via a call IC.
2646 __ Set(ecx, Immediate(expr->name()));
2647 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
2648 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
2649 __ call(ic, RelocInfo::CODE_TARGET);
2650 // Restore context register.
2651 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2652 } else {
2653 // Call the C runtime function.
2654 __ CallRuntime(expr->function(), arg_count);
2655 }
2656 Apply(context_, eax);
2657}
2658
2659
2660void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
2661 switch (expr->op()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002662 case Token::DELETE: {
2663 Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
2664 Property* prop = expr->expression()->AsProperty();
2665 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
2666 if (prop == NULL && var == NULL) {
2667 // Result of deleting non-property, non-variable reference is true.
2668 // The subexpression may have side effects.
2669 VisitForEffect(expr->expression());
2670 Apply(context_, true);
2671 } else if (var != NULL &&
2672 !var->is_global() &&
2673 var->slot() != NULL &&
2674 var->slot()->type() != Slot::LOOKUP) {
2675 // Result of deleting non-global, non-dynamic variables is false.
2676 // The subexpression does not have side effects.
2677 Apply(context_, false);
2678 } else {
2679 // Property or variable reference. Call the delete builtin with
2680 // object and property name as arguments.
2681 if (prop != NULL) {
2682 VisitForValue(prop->obj(), kStack);
2683 VisitForValue(prop->key(), kStack);
2684 } else if (var->is_global()) {
2685 __ push(CodeGenerator::GlobalObject());
2686 __ push(Immediate(var->name()));
2687 } else {
2688 // Non-global variable. Call the runtime to look up the context
2689 // where the variable was introduced.
2690 __ push(context_register());
2691 __ push(Immediate(var->name()));
2692 __ CallRuntime(Runtime::kLookupContext, 2);
2693 __ push(eax);
2694 __ push(Immediate(var->name()));
2695 }
2696 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
2697 Apply(context_, eax);
2698 }
2699 break;
2700 }
2701
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002702 case Token::VOID: {
2703 Comment cmnt(masm_, "[ UnaryOperation (VOID)");
2704 VisitForEffect(expr->expression());
2705 switch (context_) {
2706 case Expression::kUninitialized:
2707 UNREACHABLE();
2708 break;
2709 case Expression::kEffect:
2710 break;
2711 case Expression::kValue:
2712 switch (location_) {
2713 case kAccumulator:
2714 __ mov(result_register(), Factory::undefined_value());
2715 break;
2716 case kStack:
2717 __ push(Immediate(Factory::undefined_value()));
2718 break;
2719 }
2720 break;
2721 case Expression::kTestValue:
2722 // Value is false so it's needed.
2723 switch (location_) {
2724 case kAccumulator:
2725 __ mov(result_register(), Factory::undefined_value());
2726 break;
2727 case kStack:
2728 __ push(Immediate(Factory::undefined_value()));
2729 break;
2730 }
2731 // Fall through.
2732 case Expression::kTest:
2733 case Expression::kValueTest:
2734 __ jmp(false_label_);
2735 break;
2736 }
2737 break;
2738 }
2739
2740 case Token::NOT: {
2741 Comment cmnt(masm_, "[ UnaryOperation (NOT)");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002742 Label materialize_true, materialize_false;
2743 Label* if_true = NULL;
2744 Label* if_false = NULL;
2745
2746 // Notice that the labels are swapped.
2747 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true);
2748
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002749 VisitForControl(expr->expression(), if_true, if_false);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002750
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002751 Apply(context_, if_false, if_true); // Labels swapped.
2752 break;
2753 }
2754
2755 case Token::TYPEOF: {
2756 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
2757 VariableProxy* proxy = expr->expression()->AsVariableProxy();
2758 if (proxy != NULL &&
2759 !proxy->var()->is_this() &&
2760 proxy->var()->is_global()) {
2761 Comment cmnt(masm_, "Global variable");
ager@chromium.org5c838252010-02-19 08:53:10 +00002762 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002763 __ mov(ecx, Immediate(proxy->name()));
2764 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
2765 // Use a regular load, not a contextual load, to avoid a reference
2766 // error.
2767 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org5c838252010-02-19 08:53:10 +00002768 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002769 } else if (proxy != NULL &&
2770 proxy->var()->slot() != NULL &&
2771 proxy->var()->slot()->type() == Slot::LOOKUP) {
2772 __ push(esi);
2773 __ push(Immediate(proxy->name()));
2774 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
2775 __ push(eax);
2776 } else {
2777 // This expression cannot throw a reference error at the top level.
2778 VisitForValue(expr->expression(), kStack);
2779 }
2780
2781 __ CallRuntime(Runtime::kTypeof, 1);
2782 Apply(context_, eax);
2783 break;
2784 }
2785
2786 case Token::ADD: {
2787 Comment cmt(masm_, "[ UnaryOperation (ADD)");
2788 VisitForValue(expr->expression(), kAccumulator);
2789 Label no_conversion;
2790 __ test(result_register(), Immediate(kSmiTagMask));
2791 __ j(zero, &no_conversion);
2792 __ push(result_register());
2793 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2794 __ bind(&no_conversion);
2795 Apply(context_, result_register());
2796 break;
2797 }
2798
2799 case Token::SUB: {
2800 Comment cmt(masm_, "[ UnaryOperation (SUB)");
2801 bool overwrite =
2802 (expr->expression()->AsBinaryOperation() != NULL &&
2803 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2804 GenericUnaryOpStub stub(Token::SUB, overwrite);
2805 // GenericUnaryOpStub expects the argument to be in the
2806 // accumulator register eax.
2807 VisitForValue(expr->expression(), kAccumulator);
2808 __ CallStub(&stub);
2809 Apply(context_, eax);
2810 break;
2811 }
2812
2813 case Token::BIT_NOT: {
2814 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
2815 bool overwrite =
2816 (expr->expression()->AsBinaryOperation() != NULL &&
2817 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2818 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
2819 // GenericUnaryOpStub expects the argument to be in the
2820 // accumulator register eax.
2821 VisitForValue(expr->expression(), kAccumulator);
2822 // Avoid calling the stub for Smis.
2823 Label smi, done;
2824 __ test(result_register(), Immediate(kSmiTagMask));
2825 __ j(zero, &smi);
2826 // Non-smi: call stub leaving result in accumulator register.
2827 __ CallStub(&stub);
2828 __ jmp(&done);
2829 // Perform operation directly on Smis.
2830 __ bind(&smi);
2831 __ not_(result_register());
2832 __ and_(result_register(), ~kSmiTagMask); // Remove inverted smi-tag.
2833 __ bind(&done);
2834 Apply(context_, result_register());
2835 break;
2836 }
2837
2838 default:
2839 UNREACHABLE();
2840 }
2841}
2842
2843
2844void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
2845 Comment cmnt(masm_, "[ CountOperation");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002846 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
2847 // as the left-hand side.
2848 if (!expr->expression()->IsValidLeftHandSide()) {
2849 VisitForEffect(expr->expression());
2850 return;
2851 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002852
2853 // Expression can only be a property, a global or a (parameter or local)
2854 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
2855 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
2856 LhsKind assign_type = VARIABLE;
2857 Property* prop = expr->expression()->AsProperty();
2858 // In case of a property we use the uninitialized expression context
2859 // of the key to detect a named property.
2860 if (prop != NULL) {
2861 assign_type =
2862 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
2863 }
2864
2865 // Evaluate expression and get value.
2866 if (assign_type == VARIABLE) {
2867 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
2868 Location saved_location = location_;
2869 location_ = kAccumulator;
2870 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
2871 Expression::kValue);
2872 location_ = saved_location;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002873 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002874 // Reserve space for result of postfix operation.
2875 if (expr->is_postfix() && context_ != Expression::kEffect) {
2876 __ push(Immediate(Smi::FromInt(0)));
2877 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002878 if (assign_type == NAMED_PROPERTY) {
ager@chromium.org5c838252010-02-19 08:53:10 +00002879 // Put the object both on the stack and in the accumulator.
2880 VisitForValue(prop->obj(), kAccumulator);
2881 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002882 EmitNamedPropertyLoad(prop);
2883 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00002884 VisitForValue(prop->obj(), kStack);
2885 VisitForValue(prop->key(), kAccumulator);
2886 __ mov(edx, Operand(esp, 0));
2887 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002888 EmitKeyedPropertyLoad(prop);
2889 }
2890 }
2891
2892 // Call ToNumber only if operand is not a smi.
2893 Label no_conversion;
2894 __ test(eax, Immediate(kSmiTagMask));
2895 __ j(zero, &no_conversion);
2896 __ push(eax);
2897 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2898 __ bind(&no_conversion);
2899
2900 // Save result for postfix expressions.
2901 if (expr->is_postfix()) {
2902 switch (context_) {
2903 case Expression::kUninitialized:
2904 UNREACHABLE();
2905 case Expression::kEffect:
2906 // Do not save result.
2907 break;
2908 case Expression::kValue:
2909 case Expression::kTest:
2910 case Expression::kValueTest:
2911 case Expression::kTestValue:
2912 // Save the result on the stack. If we have a named or keyed property
2913 // we store the result under the receiver that is currently on top
2914 // of the stack.
2915 switch (assign_type) {
2916 case VARIABLE:
2917 __ push(eax);
2918 break;
2919 case NAMED_PROPERTY:
2920 __ mov(Operand(esp, kPointerSize), eax);
2921 break;
2922 case KEYED_PROPERTY:
2923 __ mov(Operand(esp, 2 * kPointerSize), eax);
2924 break;
2925 }
2926 break;
2927 }
2928 }
2929
2930 // Inline smi case if we are in a loop.
2931 Label stub_call, done;
2932 if (loop_depth() > 0) {
2933 if (expr->op() == Token::INC) {
2934 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2935 } else {
2936 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2937 }
2938 __ j(overflow, &stub_call);
2939 // We could eliminate this smi check if we split the code at
2940 // the first smi check before calling ToNumber.
2941 __ test(eax, Immediate(kSmiTagMask));
2942 __ j(zero, &done);
2943 __ bind(&stub_call);
2944 // Call stub. Undo operation first.
2945 if (expr->op() == Token::INC) {
2946 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2947 } else {
2948 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2949 }
2950 }
2951 // Call stub for +1/-1.
2952 GenericBinaryOpStub stub(expr->binary_op(),
2953 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002954 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002955 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002956 stub.GenerateCall(masm(), eax, Smi::FromInt(1));
2957 __ bind(&done);
2958
2959 // Store the value returned in eax.
2960 switch (assign_type) {
2961 case VARIABLE:
2962 if (expr->is_postfix()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002963 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002964 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002965 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002966 Expression::kEffect);
2967 // For all contexts except kEffect: We have the result on
2968 // top of the stack.
2969 if (context_ != Expression::kEffect) {
2970 ApplyTOS(context_);
2971 }
2972 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002973 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002974 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002975 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002976 context_);
2977 }
2978 break;
2979 case NAMED_PROPERTY: {
2980 __ mov(ecx, prop->key()->AsLiteral()->handle());
2981 __ pop(edx);
2982 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
2983 __ call(ic, RelocInfo::CODE_TARGET);
2984 // This nop signals to the IC that there is no inlined code at the call
2985 // site for it to patch.
2986 __ nop();
2987 if (expr->is_postfix()) {
2988 if (context_ != Expression::kEffect) {
2989 ApplyTOS(context_);
2990 }
2991 } else {
2992 Apply(context_, eax);
2993 }
2994 break;
2995 }
2996 case KEYED_PROPERTY: {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002997 __ pop(ecx);
2998 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002999 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
3000 __ call(ic, RelocInfo::CODE_TARGET);
3001 // This nop signals to the IC that there is no inlined code at the call
3002 // site for it to patch.
3003 __ nop();
3004 if (expr->is_postfix()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003005 // Result is on the stack
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003006 if (context_ != Expression::kEffect) {
3007 ApplyTOS(context_);
3008 }
3009 } else {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003010 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003011 }
3012 break;
3013 }
3014 }
3015}
3016
3017
3018void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
3019 Comment cmnt(masm_, "[ BinaryOperation");
3020 switch (expr->op()) {
3021 case Token::COMMA:
3022 VisitForEffect(expr->left());
3023 Visit(expr->right());
3024 break;
3025
3026 case Token::OR:
3027 case Token::AND:
3028 EmitLogicalOperation(expr);
3029 break;
3030
3031 case Token::ADD:
3032 case Token::SUB:
3033 case Token::DIV:
3034 case Token::MOD:
3035 case Token::MUL:
3036 case Token::BIT_OR:
3037 case Token::BIT_AND:
3038 case Token::BIT_XOR:
3039 case Token::SHL:
3040 case Token::SHR:
3041 case Token::SAR:
3042 VisitForValue(expr->left(), kStack);
3043 VisitForValue(expr->right(), kAccumulator);
3044 EmitBinaryOp(expr->op(), context_);
3045 break;
3046
3047 default:
3048 UNREACHABLE();
3049 }
3050}
3051
3052
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003053void FullCodeGenerator::EmitNullCompare(bool strict,
3054 Register obj,
3055 Register null_const,
3056 Label* if_true,
3057 Label* if_false,
3058 Register scratch) {
3059 __ cmp(obj, Operand(null_const));
3060 if (strict) {
3061 __ j(equal, if_true);
3062 } else {
3063 __ j(equal, if_true);
3064 __ cmp(obj, Factory::undefined_value());
3065 __ j(equal, if_true);
3066 __ test(obj, Immediate(kSmiTagMask));
3067 __ j(zero, if_false);
3068 // It can be an undetectable object.
3069 __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset));
3070 __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
3071 __ test(scratch, Immediate(1 << Map::kIsUndetectable));
3072 __ j(not_zero, if_true);
3073 }
3074 __ jmp(if_false);
3075}
3076
3077
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003078void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
3079 Comment cmnt(masm_, "[ CompareOperation");
3080
3081 // Always perform the comparison for its control flow. Pack the result
3082 // into the expression's context after the comparison is performed.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003083
3084 Label materialize_true, materialize_false;
3085 Label* if_true = NULL;
3086 Label* if_false = NULL;
3087 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003088
3089 VisitForValue(expr->left(), kStack);
3090 switch (expr->op()) {
3091 case Token::IN:
3092 VisitForValue(expr->right(), kStack);
3093 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
3094 __ cmp(eax, Factory::true_value());
3095 __ j(equal, if_true);
3096 __ jmp(if_false);
3097 break;
3098
3099 case Token::INSTANCEOF: {
3100 VisitForValue(expr->right(), kStack);
3101 InstanceofStub stub;
3102 __ CallStub(&stub);
3103 __ test(eax, Operand(eax));
3104 __ j(zero, if_true); // The stub returns 0 for true.
3105 __ jmp(if_false);
3106 break;
3107 }
3108
3109 default: {
3110 VisitForValue(expr->right(), kAccumulator);
3111 Condition cc = no_condition;
3112 bool strict = false;
3113 switch (expr->op()) {
3114 case Token::EQ_STRICT:
3115 strict = true;
3116 // Fall through
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003117 case Token::EQ: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003118 cc = equal;
3119 __ pop(edx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003120 // If either operand is constant null we do a fast compare
3121 // against null.
3122 Literal* right_literal = expr->right()->AsLiteral();
3123 Literal* left_literal = expr->left()->AsLiteral();
3124 if (right_literal != NULL && right_literal->handle()->IsNull()) {
3125 EmitNullCompare(strict, edx, eax, if_true, if_false, ecx);
3126 Apply(context_, if_true, if_false);
3127 return;
3128 } else if (left_literal != NULL && left_literal->handle()->IsNull()) {
3129 EmitNullCompare(strict, eax, edx, if_true, if_false, ecx);
3130 Apply(context_, if_true, if_false);
3131 return;
3132 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003133 break;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003134 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003135 case Token::LT:
3136 cc = less;
3137 __ pop(edx);
3138 break;
3139 case Token::GT:
3140 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3141 cc = less;
3142 __ mov(edx, result_register());
3143 __ pop(eax);
3144 break;
3145 case Token::LTE:
3146 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3147 cc = greater_equal;
3148 __ mov(edx, result_register());
3149 __ pop(eax);
3150 break;
3151 case Token::GTE:
3152 cc = greater_equal;
3153 __ pop(edx);
3154 break;
3155 case Token::IN:
3156 case Token::INSTANCEOF:
3157 default:
3158 UNREACHABLE();
3159 }
3160
3161 // The comparison stub expects the smi vs. smi case to be handled
3162 // before it is called.
3163 Label slow_case;
3164 __ mov(ecx, Operand(edx));
3165 __ or_(ecx, Operand(eax));
3166 __ test(ecx, Immediate(kSmiTagMask));
3167 __ j(not_zero, &slow_case, not_taken);
3168 __ cmp(edx, Operand(eax));
3169 __ j(cc, if_true);
3170 __ jmp(if_false);
3171
3172 __ bind(&slow_case);
3173 CompareStub stub(cc, strict);
3174 __ CallStub(&stub);
3175 __ test(eax, Operand(eax));
3176 __ j(cc, if_true);
3177 __ jmp(if_false);
3178 }
3179 }
3180
3181 // Convert the result of the comparison into one expected for this
3182 // expression's context.
3183 Apply(context_, if_true, if_false);
3184}
3185
3186
3187void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
3188 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
3189 Apply(context_, eax);
3190}
3191
3192
3193Register FullCodeGenerator::result_register() { return eax; }
3194
3195
3196Register FullCodeGenerator::context_register() { return esi; }
3197
3198
3199void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
3200 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
3201 __ mov(Operand(ebp, frame_offset), value);
3202}
3203
3204
3205void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
3206 __ mov(dst, CodeGenerator::ContextOperand(esi, context_index));
3207}
3208
3209
3210// ----------------------------------------------------------------------------
3211// Non-local control flow support.
3212
3213void FullCodeGenerator::EnterFinallyBlock() {
3214 // Cook return address on top of stack (smi encoded Code* delta)
3215 ASSERT(!result_register().is(edx));
3216 __ mov(edx, Operand(esp, 0));
3217 __ sub(Operand(edx), Immediate(masm_->CodeObject()));
3218 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
3219 ASSERT_EQ(0, kSmiTag);
3220 __ add(edx, Operand(edx)); // Convert to smi.
3221 __ mov(Operand(esp, 0), edx);
3222 // Store result register while executing finally block.
3223 __ push(result_register());
3224}
3225
3226
3227void FullCodeGenerator::ExitFinallyBlock() {
3228 ASSERT(!result_register().is(edx));
3229 // Restore result register from stack.
3230 __ pop(result_register());
3231 // Uncook return address.
3232 __ mov(edx, Operand(esp, 0));
3233 __ sar(edx, 1); // Convert smi to int.
3234 __ add(Operand(edx), Immediate(masm_->CodeObject()));
3235 __ mov(Operand(esp, 0), edx);
3236 // And return.
3237 __ ret(0);
3238}
3239
3240
3241#undef __
3242
3243} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003244
3245#endif // V8_TARGET_ARCH_IA32