blob: 368a8eeb0b56b1d42c4428cfa94d9d2f70272493 [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.
2225 __ mov(eax, FieldOperand(eax, HeapObject::kMapOffset));
2226 __ movzx_b(ebx, FieldOperand(eax, Map::kInstanceTypeOffset));
2227 __ cmp(ebx, FIRST_JS_OBJECT_TYPE);
2228 __ j(below, &null);
2229
2230 // As long as JS_FUNCTION_TYPE is the last instance type and it is
2231 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
2232 // LAST_JS_OBJECT_TYPE.
2233 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
2234 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
2235 __ cmp(ebx, JS_FUNCTION_TYPE);
2236 __ j(equal, &function);
2237
2238 // Check if the constructor in the map is a function.
2239 __ mov(eax, FieldOperand(eax, Map::kConstructorOffset));
2240 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2241 __ j(not_equal, &non_function_constructor);
2242
2243 // eax now contains the constructor function. Grab the
2244 // instance class name from there.
2245 __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
2246 __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset));
2247 __ jmp(&done);
2248
2249 // Functions have class 'Function'.
2250 __ bind(&function);
2251 __ mov(eax, Factory::function_class_symbol());
2252 __ jmp(&done);
2253
2254 // Objects with a non-function constructor have class 'Object'.
2255 __ bind(&non_function_constructor);
2256 __ mov(eax, Factory::Object_symbol());
2257 __ jmp(&done);
2258
2259 // Non-JS objects have class null.
2260 __ bind(&null);
2261 __ mov(eax, Factory::null_value());
2262
2263 // All done.
2264 __ bind(&done);
2265
2266 Apply(context_, eax);
2267}
2268
2269
2270void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) {
2271 // Conditionally generate a log call.
2272 // Args:
2273 // 0 (literal string): The type of logging (corresponds to the flags).
2274 // This is used to determine whether or not to generate the log call.
2275 // 1 (string): Format string. Access the string at argument index 2
2276 // with '%2s' (see Logger::LogRuntime for all the formats).
2277 // 2 (array): Arguments to the format string.
2278 ASSERT_EQ(args->length(), 3);
2279#ifdef ENABLE_LOGGING_AND_PROFILING
2280 if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
2281 VisitForValue(args->at(1), kStack);
2282 VisitForValue(args->at(2), kStack);
2283 __ CallRuntime(Runtime::kLog, 2);
2284 }
2285#endif
2286 // Finally, we're expected to leave a value on the top of the stack.
2287 __ mov(eax, Factory::undefined_value());
2288 Apply(context_, eax);
2289}
2290
2291
2292void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
2293 ASSERT(args->length() == 0);
2294
2295 Label slow_allocate_heapnumber;
2296 Label heapnumber_allocated;
2297
2298 __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
2299 __ jmp(&heapnumber_allocated);
2300
2301 __ bind(&slow_allocate_heapnumber);
2302 // To allocate a heap number, and ensure that it is not a smi, we
2303 // call the runtime function FUnaryMinus on 0, returning the double
2304 // -0.0. A new, distinct heap number is returned each time.
2305 __ push(Immediate(Smi::FromInt(0)));
2306 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
2307 __ mov(edi, eax);
2308
2309 __ bind(&heapnumber_allocated);
2310
2311 __ PrepareCallCFunction(0, ebx);
2312 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
2313
2314 // Convert 32 random bits in eax to 0.(32 random bits) in a double
2315 // by computing:
2316 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
2317 // This is implemented on both SSE2 and FPU.
2318 if (CpuFeatures::IsSupported(SSE2)) {
2319 CpuFeatures::Scope fscope(SSE2);
2320 __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
2321 __ movd(xmm1, Operand(ebx));
2322 __ movd(xmm0, Operand(eax));
2323 __ cvtss2sd(xmm1, xmm1);
2324 __ pxor(xmm0, xmm1);
2325 __ subsd(xmm0, xmm1);
2326 __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
2327 } else {
2328 // 0x4130000000000000 is 1.0 x 2^20 as a double.
2329 __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
2330 Immediate(0x41300000));
2331 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
2332 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2333 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
2334 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2335 __ fsubp(1);
2336 __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
2337 }
2338 __ mov(eax, edi);
2339 Apply(context_, eax);
2340}
2341
2342
2343void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) {
2344 // Load the arguments on the stack and call the stub.
2345 SubStringStub stub;
2346 ASSERT(args->length() == 3);
2347 VisitForValue(args->at(0), kStack);
2348 VisitForValue(args->at(1), kStack);
2349 VisitForValue(args->at(2), kStack);
2350 __ CallStub(&stub);
2351 Apply(context_, eax);
2352}
2353
2354
2355void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) {
2356 // Load the arguments on the stack and call the stub.
2357 RegExpExecStub stub;
2358 ASSERT(args->length() == 4);
2359 VisitForValue(args->at(0), kStack);
2360 VisitForValue(args->at(1), kStack);
2361 VisitForValue(args->at(2), kStack);
2362 VisitForValue(args->at(3), kStack);
2363 __ CallStub(&stub);
2364 Apply(context_, eax);
2365}
2366
2367
2368void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
2369 ASSERT(args->length() == 1);
2370
2371 VisitForValue(args->at(0), kAccumulator); // Load the object.
2372
2373 Label done;
2374 // If the object is a smi return the object.
2375 __ test(eax, Immediate(kSmiTagMask));
2376 __ j(zero, &done);
2377 // If the object is not a value type, return the object.
2378 __ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
2379 __ j(not_equal, &done);
2380 __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
2381
2382 __ bind(&done);
2383 Apply(context_, eax);
2384}
2385
2386
2387void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) {
2388 // Load the arguments on the stack and call the runtime function.
2389 ASSERT(args->length() == 2);
2390 VisitForValue(args->at(0), kStack);
2391 VisitForValue(args->at(1), kStack);
2392 __ CallRuntime(Runtime::kMath_pow, 2);
2393 Apply(context_, eax);
2394}
2395
2396
2397void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
2398 ASSERT(args->length() == 2);
2399
2400 VisitForValue(args->at(0), kStack); // Load the object.
2401 VisitForValue(args->at(1), kAccumulator); // Load the value.
2402 __ pop(ebx); // eax = value. ebx = object.
2403
2404 Label done;
2405 // If the object is a smi, return the value.
2406 __ test(ebx, Immediate(kSmiTagMask));
2407 __ j(zero, &done);
2408
2409 // If the object is not a value type, return the value.
2410 __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
2411 __ j(not_equal, &done);
2412
2413 // Store the value.
2414 __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax);
2415 // Update the write barrier. Save the value as it will be
2416 // overwritten by the write barrier code and is needed afterward.
2417 __ mov(edx, eax);
2418 __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx);
2419
2420 __ bind(&done);
2421 Apply(context_, eax);
2422}
2423
2424
2425void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
2426 ASSERT_EQ(args->length(), 1);
2427
2428 // Load the argument on the stack and call the stub.
2429 VisitForValue(args->at(0), kStack);
2430
2431 NumberToStringStub stub;
2432 __ CallStub(&stub);
2433 Apply(context_, eax);
2434}
2435
2436
2437void FullCodeGenerator::EmitCharFromCode(ZoneList<Expression*>* args) {
2438 ASSERT(args->length() == 1);
2439
2440 VisitForValue(args->at(0), kAccumulator);
2441
2442 Label slow_case, done;
2443 // Fast case of Heap::LookupSingleCharacterStringFromCode.
2444 ASSERT(kSmiTag == 0);
2445 ASSERT(kSmiShiftSize == 0);
2446 ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
2447 __ test(eax,
2448 Immediate(kSmiTagMask |
2449 ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
2450 __ j(not_zero, &slow_case);
2451 __ Set(ebx, Immediate(Factory::single_character_string_cache()));
2452 ASSERT(kSmiTag == 0);
2453 ASSERT(kSmiTagSize == 1);
2454 ASSERT(kSmiShiftSize == 0);
2455 // At this point code register contains smi tagged ascii char code.
2456 __ mov(ebx, FieldOperand(ebx,
2457 eax, times_half_pointer_size,
2458 FixedArray::kHeaderSize));
2459 __ cmp(ebx, Factory::undefined_value());
2460 __ j(equal, &slow_case);
2461 __ mov(eax, ebx);
2462 __ jmp(&done);
2463
2464 __ bind(&slow_case);
2465 __ push(eax);
2466 __ CallRuntime(Runtime::kCharFromCode, 1);
2467
2468 __ bind(&done);
2469 Apply(context_, eax);
2470}
2471
2472
2473void FullCodeGenerator::EmitFastCharCodeAt(ZoneList<Expression*>* args) {
2474 // TODO(fsc): Port the complete implementation from the classic back-end.
2475 // Move the undefined value into the result register, which will
2476 // trigger the slow case.
2477 __ Set(eax, Immediate(Factory::undefined_value()));
2478 Apply(context_, eax);
2479}
2480
2481void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
2482 ASSERT_EQ(2, args->length());
2483
2484 VisitForValue(args->at(0), kStack);
2485 VisitForValue(args->at(1), kStack);
2486
2487 StringAddStub stub(NO_STRING_ADD_FLAGS);
2488 __ CallStub(&stub);
2489 Apply(context_, eax);
2490}
2491
2492
2493void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) {
2494 ASSERT_EQ(2, args->length());
2495
2496 VisitForValue(args->at(0), kStack);
2497 VisitForValue(args->at(1), kStack);
2498
2499 StringCompareStub stub;
2500 __ CallStub(&stub);
2501 Apply(context_, eax);
2502}
2503
2504
2505void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) {
2506 // Load the argument on the stack and call the stub.
2507 TranscendentalCacheStub stub(TranscendentalCache::SIN);
2508 ASSERT(args->length() == 1);
2509 VisitForValue(args->at(0), kStack);
2510 __ CallStub(&stub);
2511 Apply(context_, eax);
2512}
2513
2514
2515void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
2516 // Load the argument on the stack and call the stub.
2517 TranscendentalCacheStub stub(TranscendentalCache::COS);
2518 ASSERT(args->length() == 1);
2519 VisitForValue(args->at(0), kStack);
2520 __ CallStub(&stub);
2521 Apply(context_, eax);
2522}
2523
2524
2525void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
2526 // Load the argument on the stack and call the runtime function.
2527 ASSERT(args->length() == 1);
2528 VisitForValue(args->at(0), kStack);
2529 __ CallRuntime(Runtime::kMath_sqrt, 1);
2530 Apply(context_, eax);
2531}
2532
2533
2534void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
2535 ASSERT(args->length() >= 2);
2536
2537 int arg_count = args->length() - 2; // For receiver and function.
2538 VisitForValue(args->at(0), kStack); // Receiver.
2539 for (int i = 0; i < arg_count; i++) {
2540 VisitForValue(args->at(i + 1), kStack);
2541 }
2542 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function.
2543
2544 // InvokeFunction requires function in edi. Move it in there.
2545 if (!result_register().is(edi)) __ mov(edi, result_register());
2546 ParameterCount count(arg_count);
2547 __ InvokeFunction(edi, count, CALL_FUNCTION);
2548 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2549 Apply(context_, eax);
2550}
2551
2552
2553void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) {
2554 ASSERT(args->length() == 3);
2555 VisitForValue(args->at(0), kStack);
2556 VisitForValue(args->at(1), kStack);
2557 VisitForValue(args->at(2), kStack);
2558 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
2559 Apply(context_, eax);
2560}
2561
2562
2563void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
2564 ASSERT(args->length() == 3);
2565 VisitForValue(args->at(0), kStack);
2566 VisitForValue(args->at(1), kStack);
2567 VisitForValue(args->at(2), kStack);
2568 __ CallRuntime(Runtime::kSwapElements, 3);
2569 Apply(context_, eax);
2570}
2571
2572
2573void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
2574 ASSERT_EQ(2, args->length());
2575
2576 ASSERT_NE(NULL, args->at(0)->AsLiteral());
2577 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
2578
2579 Handle<FixedArray> jsfunction_result_caches(
2580 Top::global_context()->jsfunction_result_caches());
2581 if (jsfunction_result_caches->length() <= cache_id) {
2582 __ Abort("Attempt to use undefined cache.");
2583 __ mov(eax, Factory::undefined_value());
2584 Apply(context_, eax);
2585 return;
2586 }
2587
2588 VisitForValue(args->at(1), kAccumulator);
2589
2590 Register key = eax;
2591 Register cache = ebx;
2592 Register tmp = ecx;
2593 __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX));
2594 __ mov(cache,
2595 FieldOperand(cache, GlobalObject::kGlobalContextOffset));
2596 __ mov(cache,
2597 CodeGenerator::ContextOperand(
2598 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
2599 __ mov(cache,
2600 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
2601
2602 Label done, not_found;
2603 // tmp now holds finger offset as a smi.
2604 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
2605 __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
2606 __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp));
2607 __ j(not_equal, &not_found);
2608
2609 __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1));
2610 __ jmp(&done);
2611
2612 __ bind(&not_found);
2613 // Call runtime to perform the lookup.
2614 __ push(cache);
2615 __ push(key);
2616 __ CallRuntime(Runtime::kGetFromCache, 2);
2617
2618 __ bind(&done);
2619 Apply(context_, eax);
2620}
2621
2622
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002623void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002624 Handle<String> name = expr->name();
2625 if (name->length() > 0 && name->Get(0) == '_') {
2626 Comment cmnt(masm_, "[ InlineRuntimeCall");
2627 EmitInlineRuntimeCall(expr);
2628 return;
2629 }
2630
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002631 Comment cmnt(masm_, "[ CallRuntime");
2632 ZoneList<Expression*>* args = expr->arguments();
2633
2634 if (expr->is_jsruntime()) {
2635 // Prepare for calling JS runtime function.
2636 __ mov(eax, CodeGenerator::GlobalObject());
2637 __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
2638 }
2639
2640 // Push the arguments ("left-to-right").
2641 int arg_count = args->length();
2642 for (int i = 0; i < arg_count; i++) {
2643 VisitForValue(args->at(i), kStack);
2644 }
2645
2646 if (expr->is_jsruntime()) {
2647 // Call the JS runtime function via a call IC.
2648 __ Set(ecx, Immediate(expr->name()));
2649 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
2650 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
2651 __ call(ic, RelocInfo::CODE_TARGET);
2652 // Restore context register.
2653 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2654 } else {
2655 // Call the C runtime function.
2656 __ CallRuntime(expr->function(), arg_count);
2657 }
2658 Apply(context_, eax);
2659}
2660
2661
2662void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
2663 switch (expr->op()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002664 case Token::DELETE: {
2665 Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
2666 Property* prop = expr->expression()->AsProperty();
2667 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
2668 if (prop == NULL && var == NULL) {
2669 // Result of deleting non-property, non-variable reference is true.
2670 // The subexpression may have side effects.
2671 VisitForEffect(expr->expression());
2672 Apply(context_, true);
2673 } else if (var != NULL &&
2674 !var->is_global() &&
2675 var->slot() != NULL &&
2676 var->slot()->type() != Slot::LOOKUP) {
2677 // Result of deleting non-global, non-dynamic variables is false.
2678 // The subexpression does not have side effects.
2679 Apply(context_, false);
2680 } else {
2681 // Property or variable reference. Call the delete builtin with
2682 // object and property name as arguments.
2683 if (prop != NULL) {
2684 VisitForValue(prop->obj(), kStack);
2685 VisitForValue(prop->key(), kStack);
2686 } else if (var->is_global()) {
2687 __ push(CodeGenerator::GlobalObject());
2688 __ push(Immediate(var->name()));
2689 } else {
2690 // Non-global variable. Call the runtime to look up the context
2691 // where the variable was introduced.
2692 __ push(context_register());
2693 __ push(Immediate(var->name()));
2694 __ CallRuntime(Runtime::kLookupContext, 2);
2695 __ push(eax);
2696 __ push(Immediate(var->name()));
2697 }
2698 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
2699 Apply(context_, eax);
2700 }
2701 break;
2702 }
2703
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002704 case Token::VOID: {
2705 Comment cmnt(masm_, "[ UnaryOperation (VOID)");
2706 VisitForEffect(expr->expression());
2707 switch (context_) {
2708 case Expression::kUninitialized:
2709 UNREACHABLE();
2710 break;
2711 case Expression::kEffect:
2712 break;
2713 case Expression::kValue:
2714 switch (location_) {
2715 case kAccumulator:
2716 __ mov(result_register(), Factory::undefined_value());
2717 break;
2718 case kStack:
2719 __ push(Immediate(Factory::undefined_value()));
2720 break;
2721 }
2722 break;
2723 case Expression::kTestValue:
2724 // Value is false so it's needed.
2725 switch (location_) {
2726 case kAccumulator:
2727 __ mov(result_register(), Factory::undefined_value());
2728 break;
2729 case kStack:
2730 __ push(Immediate(Factory::undefined_value()));
2731 break;
2732 }
2733 // Fall through.
2734 case Expression::kTest:
2735 case Expression::kValueTest:
2736 __ jmp(false_label_);
2737 break;
2738 }
2739 break;
2740 }
2741
2742 case Token::NOT: {
2743 Comment cmnt(masm_, "[ UnaryOperation (NOT)");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002744 Label materialize_true, materialize_false;
2745 Label* if_true = NULL;
2746 Label* if_false = NULL;
2747
2748 // Notice that the labels are swapped.
2749 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true);
2750
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002751 VisitForControl(expr->expression(), if_true, if_false);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002752
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002753 Apply(context_, if_false, if_true); // Labels swapped.
2754 break;
2755 }
2756
2757 case Token::TYPEOF: {
2758 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
2759 VariableProxy* proxy = expr->expression()->AsVariableProxy();
2760 if (proxy != NULL &&
2761 !proxy->var()->is_this() &&
2762 proxy->var()->is_global()) {
2763 Comment cmnt(masm_, "Global variable");
ager@chromium.org5c838252010-02-19 08:53:10 +00002764 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002765 __ mov(ecx, Immediate(proxy->name()));
2766 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
2767 // Use a regular load, not a contextual load, to avoid a reference
2768 // error.
2769 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org5c838252010-02-19 08:53:10 +00002770 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002771 } else if (proxy != NULL &&
2772 proxy->var()->slot() != NULL &&
2773 proxy->var()->slot()->type() == Slot::LOOKUP) {
2774 __ push(esi);
2775 __ push(Immediate(proxy->name()));
2776 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
2777 __ push(eax);
2778 } else {
2779 // This expression cannot throw a reference error at the top level.
2780 VisitForValue(expr->expression(), kStack);
2781 }
2782
2783 __ CallRuntime(Runtime::kTypeof, 1);
2784 Apply(context_, eax);
2785 break;
2786 }
2787
2788 case Token::ADD: {
2789 Comment cmt(masm_, "[ UnaryOperation (ADD)");
2790 VisitForValue(expr->expression(), kAccumulator);
2791 Label no_conversion;
2792 __ test(result_register(), Immediate(kSmiTagMask));
2793 __ j(zero, &no_conversion);
2794 __ push(result_register());
2795 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2796 __ bind(&no_conversion);
2797 Apply(context_, result_register());
2798 break;
2799 }
2800
2801 case Token::SUB: {
2802 Comment cmt(masm_, "[ UnaryOperation (SUB)");
2803 bool overwrite =
2804 (expr->expression()->AsBinaryOperation() != NULL &&
2805 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2806 GenericUnaryOpStub stub(Token::SUB, overwrite);
2807 // GenericUnaryOpStub expects the argument to be in the
2808 // accumulator register eax.
2809 VisitForValue(expr->expression(), kAccumulator);
2810 __ CallStub(&stub);
2811 Apply(context_, eax);
2812 break;
2813 }
2814
2815 case Token::BIT_NOT: {
2816 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
2817 bool overwrite =
2818 (expr->expression()->AsBinaryOperation() != NULL &&
2819 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2820 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
2821 // GenericUnaryOpStub expects the argument to be in the
2822 // accumulator register eax.
2823 VisitForValue(expr->expression(), kAccumulator);
2824 // Avoid calling the stub for Smis.
2825 Label smi, done;
2826 __ test(result_register(), Immediate(kSmiTagMask));
2827 __ j(zero, &smi);
2828 // Non-smi: call stub leaving result in accumulator register.
2829 __ CallStub(&stub);
2830 __ jmp(&done);
2831 // Perform operation directly on Smis.
2832 __ bind(&smi);
2833 __ not_(result_register());
2834 __ and_(result_register(), ~kSmiTagMask); // Remove inverted smi-tag.
2835 __ bind(&done);
2836 Apply(context_, result_register());
2837 break;
2838 }
2839
2840 default:
2841 UNREACHABLE();
2842 }
2843}
2844
2845
2846void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
2847 Comment cmnt(masm_, "[ CountOperation");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002848 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
2849 // as the left-hand side.
2850 if (!expr->expression()->IsValidLeftHandSide()) {
2851 VisitForEffect(expr->expression());
2852 return;
2853 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002854
2855 // Expression can only be a property, a global or a (parameter or local)
2856 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
2857 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
2858 LhsKind assign_type = VARIABLE;
2859 Property* prop = expr->expression()->AsProperty();
2860 // In case of a property we use the uninitialized expression context
2861 // of the key to detect a named property.
2862 if (prop != NULL) {
2863 assign_type =
2864 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
2865 }
2866
2867 // Evaluate expression and get value.
2868 if (assign_type == VARIABLE) {
2869 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
2870 Location saved_location = location_;
2871 location_ = kAccumulator;
2872 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
2873 Expression::kValue);
2874 location_ = saved_location;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002875 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002876 // Reserve space for result of postfix operation.
2877 if (expr->is_postfix() && context_ != Expression::kEffect) {
2878 __ push(Immediate(Smi::FromInt(0)));
2879 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002880 if (assign_type == NAMED_PROPERTY) {
ager@chromium.org5c838252010-02-19 08:53:10 +00002881 // Put the object both on the stack and in the accumulator.
2882 VisitForValue(prop->obj(), kAccumulator);
2883 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002884 EmitNamedPropertyLoad(prop);
2885 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00002886 VisitForValue(prop->obj(), kStack);
2887 VisitForValue(prop->key(), kAccumulator);
2888 __ mov(edx, Operand(esp, 0));
2889 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002890 EmitKeyedPropertyLoad(prop);
2891 }
2892 }
2893
2894 // Call ToNumber only if operand is not a smi.
2895 Label no_conversion;
2896 __ test(eax, Immediate(kSmiTagMask));
2897 __ j(zero, &no_conversion);
2898 __ push(eax);
2899 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2900 __ bind(&no_conversion);
2901
2902 // Save result for postfix expressions.
2903 if (expr->is_postfix()) {
2904 switch (context_) {
2905 case Expression::kUninitialized:
2906 UNREACHABLE();
2907 case Expression::kEffect:
2908 // Do not save result.
2909 break;
2910 case Expression::kValue:
2911 case Expression::kTest:
2912 case Expression::kValueTest:
2913 case Expression::kTestValue:
2914 // Save the result on the stack. If we have a named or keyed property
2915 // we store the result under the receiver that is currently on top
2916 // of the stack.
2917 switch (assign_type) {
2918 case VARIABLE:
2919 __ push(eax);
2920 break;
2921 case NAMED_PROPERTY:
2922 __ mov(Operand(esp, kPointerSize), eax);
2923 break;
2924 case KEYED_PROPERTY:
2925 __ mov(Operand(esp, 2 * kPointerSize), eax);
2926 break;
2927 }
2928 break;
2929 }
2930 }
2931
2932 // Inline smi case if we are in a loop.
2933 Label stub_call, done;
2934 if (loop_depth() > 0) {
2935 if (expr->op() == Token::INC) {
2936 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2937 } else {
2938 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2939 }
2940 __ j(overflow, &stub_call);
2941 // We could eliminate this smi check if we split the code at
2942 // the first smi check before calling ToNumber.
2943 __ test(eax, Immediate(kSmiTagMask));
2944 __ j(zero, &done);
2945 __ bind(&stub_call);
2946 // Call stub. Undo operation first.
2947 if (expr->op() == Token::INC) {
2948 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2949 } else {
2950 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2951 }
2952 }
2953 // Call stub for +1/-1.
2954 GenericBinaryOpStub stub(expr->binary_op(),
2955 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002956 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002957 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002958 stub.GenerateCall(masm(), eax, Smi::FromInt(1));
2959 __ bind(&done);
2960
2961 // Store the value returned in eax.
2962 switch (assign_type) {
2963 case VARIABLE:
2964 if (expr->is_postfix()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002965 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002966 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002967 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002968 Expression::kEffect);
2969 // For all contexts except kEffect: We have the result on
2970 // top of the stack.
2971 if (context_ != Expression::kEffect) {
2972 ApplyTOS(context_);
2973 }
2974 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002975 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002976 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002977 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002978 context_);
2979 }
2980 break;
2981 case NAMED_PROPERTY: {
2982 __ mov(ecx, prop->key()->AsLiteral()->handle());
2983 __ pop(edx);
2984 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
2985 __ call(ic, RelocInfo::CODE_TARGET);
2986 // This nop signals to the IC that there is no inlined code at the call
2987 // site for it to patch.
2988 __ nop();
2989 if (expr->is_postfix()) {
2990 if (context_ != Expression::kEffect) {
2991 ApplyTOS(context_);
2992 }
2993 } else {
2994 Apply(context_, eax);
2995 }
2996 break;
2997 }
2998 case KEYED_PROPERTY: {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002999 __ pop(ecx);
3000 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003001 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
3002 __ call(ic, RelocInfo::CODE_TARGET);
3003 // This nop signals to the IC that there is no inlined code at the call
3004 // site for it to patch.
3005 __ nop();
3006 if (expr->is_postfix()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003007 // Result is on the stack
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003008 if (context_ != Expression::kEffect) {
3009 ApplyTOS(context_);
3010 }
3011 } else {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003012 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003013 }
3014 break;
3015 }
3016 }
3017}
3018
3019
3020void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
3021 Comment cmnt(masm_, "[ BinaryOperation");
3022 switch (expr->op()) {
3023 case Token::COMMA:
3024 VisitForEffect(expr->left());
3025 Visit(expr->right());
3026 break;
3027
3028 case Token::OR:
3029 case Token::AND:
3030 EmitLogicalOperation(expr);
3031 break;
3032
3033 case Token::ADD:
3034 case Token::SUB:
3035 case Token::DIV:
3036 case Token::MOD:
3037 case Token::MUL:
3038 case Token::BIT_OR:
3039 case Token::BIT_AND:
3040 case Token::BIT_XOR:
3041 case Token::SHL:
3042 case Token::SHR:
3043 case Token::SAR:
3044 VisitForValue(expr->left(), kStack);
3045 VisitForValue(expr->right(), kAccumulator);
3046 EmitBinaryOp(expr->op(), context_);
3047 break;
3048
3049 default:
3050 UNREACHABLE();
3051 }
3052}
3053
3054
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003055void FullCodeGenerator::EmitNullCompare(bool strict,
3056 Register obj,
3057 Register null_const,
3058 Label* if_true,
3059 Label* if_false,
3060 Register scratch) {
3061 __ cmp(obj, Operand(null_const));
3062 if (strict) {
3063 __ j(equal, if_true);
3064 } else {
3065 __ j(equal, if_true);
3066 __ cmp(obj, Factory::undefined_value());
3067 __ j(equal, if_true);
3068 __ test(obj, Immediate(kSmiTagMask));
3069 __ j(zero, if_false);
3070 // It can be an undetectable object.
3071 __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset));
3072 __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
3073 __ test(scratch, Immediate(1 << Map::kIsUndetectable));
3074 __ j(not_zero, if_true);
3075 }
3076 __ jmp(if_false);
3077}
3078
3079
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003080void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
3081 Comment cmnt(masm_, "[ CompareOperation");
3082
3083 // Always perform the comparison for its control flow. Pack the result
3084 // into the expression's context after the comparison is performed.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003085
3086 Label materialize_true, materialize_false;
3087 Label* if_true = NULL;
3088 Label* if_false = NULL;
3089 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003090
3091 VisitForValue(expr->left(), kStack);
3092 switch (expr->op()) {
3093 case Token::IN:
3094 VisitForValue(expr->right(), kStack);
3095 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
3096 __ cmp(eax, Factory::true_value());
3097 __ j(equal, if_true);
3098 __ jmp(if_false);
3099 break;
3100
3101 case Token::INSTANCEOF: {
3102 VisitForValue(expr->right(), kStack);
3103 InstanceofStub stub;
3104 __ CallStub(&stub);
3105 __ test(eax, Operand(eax));
3106 __ j(zero, if_true); // The stub returns 0 for true.
3107 __ jmp(if_false);
3108 break;
3109 }
3110
3111 default: {
3112 VisitForValue(expr->right(), kAccumulator);
3113 Condition cc = no_condition;
3114 bool strict = false;
3115 switch (expr->op()) {
3116 case Token::EQ_STRICT:
3117 strict = true;
3118 // Fall through
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003119 case Token::EQ: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003120 cc = equal;
3121 __ pop(edx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003122 // If either operand is constant null we do a fast compare
3123 // against null.
3124 Literal* right_literal = expr->right()->AsLiteral();
3125 Literal* left_literal = expr->left()->AsLiteral();
3126 if (right_literal != NULL && right_literal->handle()->IsNull()) {
3127 EmitNullCompare(strict, edx, eax, if_true, if_false, ecx);
3128 Apply(context_, if_true, if_false);
3129 return;
3130 } else if (left_literal != NULL && left_literal->handle()->IsNull()) {
3131 EmitNullCompare(strict, eax, edx, if_true, if_false, ecx);
3132 Apply(context_, if_true, if_false);
3133 return;
3134 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003135 break;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003136 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003137 case Token::LT:
3138 cc = less;
3139 __ pop(edx);
3140 break;
3141 case Token::GT:
3142 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3143 cc = less;
3144 __ mov(edx, result_register());
3145 __ pop(eax);
3146 break;
3147 case Token::LTE:
3148 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3149 cc = greater_equal;
3150 __ mov(edx, result_register());
3151 __ pop(eax);
3152 break;
3153 case Token::GTE:
3154 cc = greater_equal;
3155 __ pop(edx);
3156 break;
3157 case Token::IN:
3158 case Token::INSTANCEOF:
3159 default:
3160 UNREACHABLE();
3161 }
3162
3163 // The comparison stub expects the smi vs. smi case to be handled
3164 // before it is called.
3165 Label slow_case;
3166 __ mov(ecx, Operand(edx));
3167 __ or_(ecx, Operand(eax));
3168 __ test(ecx, Immediate(kSmiTagMask));
3169 __ j(not_zero, &slow_case, not_taken);
3170 __ cmp(edx, Operand(eax));
3171 __ j(cc, if_true);
3172 __ jmp(if_false);
3173
3174 __ bind(&slow_case);
3175 CompareStub stub(cc, strict);
3176 __ CallStub(&stub);
3177 __ test(eax, Operand(eax));
3178 __ j(cc, if_true);
3179 __ jmp(if_false);
3180 }
3181 }
3182
3183 // Convert the result of the comparison into one expected for this
3184 // expression's context.
3185 Apply(context_, if_true, if_false);
3186}
3187
3188
3189void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
3190 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
3191 Apply(context_, eax);
3192}
3193
3194
3195Register FullCodeGenerator::result_register() { return eax; }
3196
3197
3198Register FullCodeGenerator::context_register() { return esi; }
3199
3200
3201void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
3202 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
3203 __ mov(Operand(ebp, frame_offset), value);
3204}
3205
3206
3207void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
3208 __ mov(dst, CodeGenerator::ContextOperand(esi, context_index));
3209}
3210
3211
3212// ----------------------------------------------------------------------------
3213// Non-local control flow support.
3214
3215void FullCodeGenerator::EnterFinallyBlock() {
3216 // Cook return address on top of stack (smi encoded Code* delta)
3217 ASSERT(!result_register().is(edx));
3218 __ mov(edx, Operand(esp, 0));
3219 __ sub(Operand(edx), Immediate(masm_->CodeObject()));
3220 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
3221 ASSERT_EQ(0, kSmiTag);
3222 __ add(edx, Operand(edx)); // Convert to smi.
3223 __ mov(Operand(esp, 0), edx);
3224 // Store result register while executing finally block.
3225 __ push(result_register());
3226}
3227
3228
3229void FullCodeGenerator::ExitFinallyBlock() {
3230 ASSERT(!result_register().is(edx));
3231 // Restore result register from stack.
3232 __ pop(result_register());
3233 // Uncook return address.
3234 __ mov(edx, Operand(esp, 0));
3235 __ sar(edx, 1); // Convert smi to int.
3236 __ add(Operand(edx), Immediate(masm_->CodeObject()));
3237 __ mov(Operand(esp, 0), edx);
3238 // And return.
3239 __ ret(0);
3240}
3241
3242
3243#undef __
3244
3245} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003246
3247#endif // V8_TARGET_ARCH_IA32