blob: 13173e2b92173d88e5fe413be216cc0d9bbff49b [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.org2cc82ae2010-06-14 07:35:38 +0000189 EmitReturnSequence();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000190 }
191}
192
193
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000194void FullCodeGenerator::EmitReturnSequence() {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000195 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
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000210 CodeGenerator::RecordPositions(masm_, function()->end_position());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000211 __ 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));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001012 __ push(eax); // Enumeration cache length (as smi).
1013 __ push(Immediate(Smi::FromInt(0))); // Initial index.
1014 __ jmp(&loop);
1015
1016 // We got a fixed array in register eax. Iterate through that.
1017 __ bind(&fixed_array);
1018 __ push(Immediate(Smi::FromInt(0))); // Map (0) - force slow check.
1019 __ push(eax);
1020 __ mov(eax, FieldOperand(eax, FixedArray::kLengthOffset));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001021 __ push(eax); // Fixed array length (as smi).
1022 __ push(Immediate(Smi::FromInt(0))); // Initial index.
1023
1024 // Generate code for doing the condition check.
1025 __ bind(&loop);
1026 __ mov(eax, Operand(esp, 0 * kPointerSize)); // Get the current index.
1027 __ cmp(eax, Operand(esp, 1 * kPointerSize)); // Compare to the array length.
1028 __ j(above_equal, loop_statement.break_target());
1029
1030 // Get the current entry of the array into register ebx.
1031 __ mov(ebx, Operand(esp, 2 * kPointerSize));
1032 __ mov(ebx, FieldOperand(ebx, eax, times_2, FixedArray::kHeaderSize));
1033
1034 // Get the expected map from the stack or a zero map in the
1035 // permanent slow case into register edx.
1036 __ mov(edx, Operand(esp, 3 * kPointerSize));
1037
1038 // Check if the expected map still matches that of the enumerable.
1039 // If not, we have to filter the key.
1040 Label update_each;
1041 __ mov(ecx, Operand(esp, 4 * kPointerSize));
1042 __ cmp(edx, FieldOperand(ecx, HeapObject::kMapOffset));
1043 __ j(equal, &update_each);
1044
1045 // Convert the entry to a string or null if it isn't a property
1046 // anymore. If the property has been removed while iterating, we
1047 // just skip it.
1048 __ push(ecx); // Enumerable.
1049 __ push(ebx); // Current entry.
1050 __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
1051 __ cmp(eax, Factory::null_value());
1052 __ j(equal, loop_statement.continue_target());
1053 __ mov(ebx, Operand(eax));
1054
1055 // Update the 'each' property or variable from the possibly filtered
1056 // entry in register ebx.
1057 __ bind(&update_each);
1058 __ mov(result_register(), ebx);
1059 // Perform the assignment as if via '='.
1060 EmitAssignment(stmt->each());
1061
1062 // Generate code for the body of the loop.
1063 Label stack_limit_hit, stack_check_done;
1064 Visit(stmt->body());
1065
1066 __ StackLimitCheck(&stack_limit_hit);
1067 __ bind(&stack_check_done);
1068
1069 // Generate code for going to the next element by incrementing the
1070 // index (smi) stored on top of the stack.
1071 __ bind(loop_statement.continue_target());
1072 __ add(Operand(esp, 0 * kPointerSize), Immediate(Smi::FromInt(1)));
1073 __ jmp(&loop);
1074
1075 // Slow case for the stack limit check.
1076 StackCheckStub stack_check_stub;
1077 __ bind(&stack_limit_hit);
1078 __ CallStub(&stack_check_stub);
1079 __ jmp(&stack_check_done);
1080
1081 // Remove the pointers stored on the stack.
1082 __ bind(loop_statement.break_target());
1083 __ add(Operand(esp), Immediate(5 * kPointerSize));
1084
1085 // Exit and decrement the loop depth.
1086 __ bind(&exit);
1087 decrement_loop_depth();
1088}
1089
1090
1091void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) {
1092 // Use the fast case closure allocation code that allocates in new
1093 // space for nested functions that don't need literals cloning.
1094 if (scope()->is_function_scope() && info->num_literals() == 0) {
1095 FastNewClosureStub stub;
1096 __ push(Immediate(info));
1097 __ CallStub(&stub);
1098 } else {
1099 __ push(esi);
1100 __ push(Immediate(info));
1101 __ CallRuntime(Runtime::kNewClosure, 2);
1102 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001103 Apply(context_, eax);
1104}
1105
1106
1107void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
1108 Comment cmnt(masm_, "[ VariableProxy");
1109 EmitVariableLoad(expr->var(), context_);
1110}
1111
1112
1113void FullCodeGenerator::EmitVariableLoad(Variable* var,
1114 Expression::Context context) {
1115 // Four cases: non-this global variables, lookup slots, all other
1116 // types of slots, and parameters that rewrite to explicit property
1117 // accesses on the arguments object.
1118 Slot* slot = var->slot();
1119 Property* property = var->AsProperty();
1120
1121 if (var->is_global() && !var->is_this()) {
1122 Comment cmnt(masm_, "Global variable");
1123 // Use inline caching. Variable name is passed in ecx and the global
1124 // object on the stack.
ager@chromium.org5c838252010-02-19 08:53:10 +00001125 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001126 __ mov(ecx, var->name());
1127 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
1128 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
1129 // By emitting a nop we make sure that we do not have a test eax
1130 // instruction after the call it is treated specially by the LoadIC code
1131 // Remember that the assembler may choose to do peephole optimization
1132 // (eg, push/pop elimination).
1133 __ nop();
ager@chromium.org5c838252010-02-19 08:53:10 +00001134 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001135
1136 } else if (slot != NULL && slot->type() == Slot::LOOKUP) {
1137 Comment cmnt(masm_, "Lookup slot");
1138 __ push(esi); // Context.
1139 __ push(Immediate(var->name()));
1140 __ CallRuntime(Runtime::kLoadContextSlot, 2);
1141 Apply(context, eax);
1142
1143 } else if (slot != NULL) {
1144 Comment cmnt(masm_, (slot->type() == Slot::CONTEXT)
1145 ? "Context slot"
1146 : "Stack slot");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001147 if (var->mode() == Variable::CONST) {
1148 // Constants may be the hole value if they have not been initialized.
1149 // Unhole them.
1150 Label done;
1151 MemOperand slot_operand = EmitSlotSearch(slot, eax);
1152 __ mov(eax, slot_operand);
1153 __ cmp(eax, Factory::the_hole_value());
1154 __ j(not_equal, &done);
1155 __ mov(eax, Factory::undefined_value());
1156 __ bind(&done);
1157 Apply(context, eax);
1158 } else {
1159 Apply(context, slot);
1160 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001161
1162 } else {
1163 Comment cmnt(masm_, "Rewritten parameter");
1164 ASSERT_NOT_NULL(property);
1165 // Rewritten parameter accesses are of the form "slot[literal]".
1166
1167 // Assert that the object is in a slot.
1168 Variable* object_var = property->obj()->AsVariableProxy()->AsVariable();
1169 ASSERT_NOT_NULL(object_var);
1170 Slot* object_slot = object_var->slot();
1171 ASSERT_NOT_NULL(object_slot);
1172
1173 // Load the object.
1174 MemOperand object_loc = EmitSlotSearch(object_slot, eax);
ager@chromium.org5c838252010-02-19 08:53:10 +00001175 __ mov(edx, object_loc);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001176
1177 // Assert that the key is a smi.
1178 Literal* key_literal = property->key()->AsLiteral();
1179 ASSERT_NOT_NULL(key_literal);
1180 ASSERT(key_literal->handle()->IsSmi());
1181
1182 // Load the key.
ager@chromium.org5c838252010-02-19 08:53:10 +00001183 __ mov(eax, Immediate(key_literal->handle()));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001184
1185 // Do a keyed property load.
1186 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1187 __ call(ic, RelocInfo::CODE_TARGET);
1188 // Notice: We must not have a "test eax, ..." instruction after the
1189 // call. It is treated specially by the LoadIC code.
1190 __ nop();
1191 // Drop key and object left on the stack by IC.
ager@chromium.org5c838252010-02-19 08:53:10 +00001192 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001193 }
1194}
1195
1196
1197void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
1198 Comment cmnt(masm_, "[ RegExpLiteral");
1199 Label done;
1200 // Registers will be used as follows:
1201 // edi = JS function.
1202 // ebx = literals array.
1203 // eax = regexp literal.
1204 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1205 __ mov(ebx, FieldOperand(edi, JSFunction::kLiteralsOffset));
1206 int literal_offset =
1207 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
1208 __ mov(eax, FieldOperand(ebx, literal_offset));
1209 __ cmp(eax, Factory::undefined_value());
1210 __ j(not_equal, &done);
1211 // Create regexp literal using runtime function
1212 // Result will be in eax.
1213 __ push(ebx);
1214 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1215 __ push(Immediate(expr->pattern()));
1216 __ push(Immediate(expr->flags()));
1217 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
1218 // Label done:
1219 __ bind(&done);
1220 Apply(context_, eax);
1221}
1222
1223
1224void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
1225 Comment cmnt(masm_, "[ ObjectLiteral");
1226 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1227 __ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
1228 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1229 __ push(Immediate(expr->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001230 __ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001231 if (expr->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001232 __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001233 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001234 __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001235 }
1236
1237 // If result_saved is true the result is on top of the stack. If
1238 // result_saved is false the result is in eax.
1239 bool result_saved = false;
1240
1241 for (int i = 0; i < expr->properties()->length(); i++) {
1242 ObjectLiteral::Property* property = expr->properties()->at(i);
1243 if (property->IsCompileTimeValue()) continue;
1244
1245 Literal* key = property->key();
1246 Expression* value = property->value();
1247 if (!result_saved) {
1248 __ push(eax); // Save result on the stack
1249 result_saved = true;
1250 }
1251 switch (property->kind()) {
1252 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
1253 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
1254 // Fall through.
1255 case ObjectLiteral::Property::COMPUTED:
1256 if (key->handle()->IsSymbol()) {
1257 VisitForValue(value, kAccumulator);
1258 __ mov(ecx, Immediate(key->handle()));
1259 __ mov(edx, Operand(esp, 0));
1260 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1261 __ call(ic, RelocInfo::CODE_TARGET);
1262 __ nop();
1263 break;
1264 }
1265 // Fall through.
1266 case ObjectLiteral::Property::PROTOTYPE:
1267 __ push(Operand(esp, 0)); // Duplicate receiver.
1268 VisitForValue(key, kStack);
1269 VisitForValue(value, kStack);
1270 __ CallRuntime(Runtime::kSetProperty, 3);
1271 break;
1272 case ObjectLiteral::Property::SETTER:
1273 case ObjectLiteral::Property::GETTER:
1274 __ push(Operand(esp, 0)); // Duplicate receiver.
1275 VisitForValue(key, kStack);
1276 __ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ?
1277 Smi::FromInt(1) :
1278 Smi::FromInt(0)));
1279 VisitForValue(value, kStack);
1280 __ CallRuntime(Runtime::kDefineAccessor, 4);
1281 break;
1282 default: UNREACHABLE();
1283 }
1284 }
1285
1286 if (result_saved) {
1287 ApplyTOS(context_);
1288 } else {
1289 Apply(context_, eax);
1290 }
1291}
1292
1293
1294void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
1295 Comment cmnt(masm_, "[ ArrayLiteral");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001296
1297 ZoneList<Expression*>* subexprs = expr->values();
1298 int length = subexprs->length();
1299
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001300 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1301 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
1302 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1303 __ push(Immediate(expr->constant_elements()));
1304 if (expr->depth() > 1) {
1305 __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001306 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001307 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001308 } else {
1309 FastCloneShallowArrayStub stub(length);
1310 __ CallStub(&stub);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001311 }
1312
1313 bool result_saved = false; // Is the result saved to the stack?
1314
1315 // Emit code to evaluate all the non-constant subexpressions and to store
1316 // them into the newly cloned array.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001317 for (int i = 0; i < length; i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001318 Expression* subexpr = subexprs->at(i);
1319 // If the subexpression is a literal or a simple materialized literal it
1320 // is already set in the cloned array.
1321 if (subexpr->AsLiteral() != NULL ||
1322 CompileTimeValue::IsCompileTimeValue(subexpr)) {
1323 continue;
1324 }
1325
1326 if (!result_saved) {
1327 __ push(eax);
1328 result_saved = true;
1329 }
1330 VisitForValue(subexpr, kAccumulator);
1331
1332 // Store the subexpression value in the array's elements.
1333 __ mov(ebx, Operand(esp, 0)); // Copy of array literal.
1334 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
1335 int offset = FixedArray::kHeaderSize + (i * kPointerSize);
1336 __ mov(FieldOperand(ebx, offset), result_register());
1337
1338 // Update the write barrier for the array store.
1339 __ RecordWrite(ebx, offset, result_register(), ecx);
1340 }
1341
1342 if (result_saved) {
1343 ApplyTOS(context_);
1344 } else {
1345 Apply(context_, eax);
1346 }
1347}
1348
1349
ager@chromium.org5c838252010-02-19 08:53:10 +00001350void FullCodeGenerator::VisitAssignment(Assignment* expr) {
1351 Comment cmnt(masm_, "[ Assignment");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001352 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
1353 // on the left-hand side.
1354 if (!expr->target()->IsValidLeftHandSide()) {
1355 VisitForEffect(expr->target());
1356 return;
1357 }
1358
ager@chromium.org5c838252010-02-19 08:53:10 +00001359 // Left-hand side can only be a property, a global or a (parameter or local)
1360 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1361 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1362 LhsKind assign_type = VARIABLE;
1363 Property* prop = expr->target()->AsProperty();
1364 if (prop != NULL) {
1365 assign_type =
1366 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
1367 }
1368
1369 // Evaluate LHS expression.
1370 switch (assign_type) {
1371 case VARIABLE:
1372 // Nothing to do here.
1373 break;
1374 case NAMED_PROPERTY:
1375 if (expr->is_compound()) {
1376 // We need the receiver both on the stack and in the accumulator.
1377 VisitForValue(prop->obj(), kAccumulator);
1378 __ push(result_register());
1379 } else {
1380 VisitForValue(prop->obj(), kStack);
1381 }
1382 break;
1383 case KEYED_PROPERTY:
1384 if (expr->is_compound()) {
1385 VisitForValue(prop->obj(), kStack);
1386 VisitForValue(prop->key(), kAccumulator);
1387 __ mov(edx, Operand(esp, 0));
1388 __ push(eax);
1389 } else {
1390 VisitForValue(prop->obj(), kStack);
1391 VisitForValue(prop->key(), kStack);
1392 }
1393 break;
1394 }
1395
1396 // If we have a compound assignment: Get value of LHS expression and
1397 // store in on top of the stack.
1398 if (expr->is_compound()) {
1399 Location saved_location = location_;
1400 location_ = kStack;
1401 switch (assign_type) {
1402 case VARIABLE:
1403 EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
1404 Expression::kValue);
1405 break;
1406 case NAMED_PROPERTY:
1407 EmitNamedPropertyLoad(prop);
1408 __ push(result_register());
1409 break;
1410 case KEYED_PROPERTY:
1411 EmitKeyedPropertyLoad(prop);
1412 __ push(result_register());
1413 break;
1414 }
1415 location_ = saved_location;
1416 }
1417
1418 // Evaluate RHS expression.
1419 Expression* rhs = expr->value();
1420 VisitForValue(rhs, kAccumulator);
1421
1422 // If we have a compound assignment: Apply operator.
1423 if (expr->is_compound()) {
1424 Location saved_location = location_;
1425 location_ = kAccumulator;
1426 EmitBinaryOp(expr->binary_op(), Expression::kValue);
1427 location_ = saved_location;
1428 }
1429
1430 // Record source position before possible IC call.
1431 SetSourcePosition(expr->position());
1432
1433 // Store the value.
1434 switch (assign_type) {
1435 case VARIABLE:
1436 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001437 expr->op(),
ager@chromium.org5c838252010-02-19 08:53:10 +00001438 context_);
1439 break;
1440 case NAMED_PROPERTY:
1441 EmitNamedPropertyAssignment(expr);
1442 break;
1443 case KEYED_PROPERTY:
1444 EmitKeyedPropertyAssignment(expr);
1445 break;
1446 }
1447}
1448
1449
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001450void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
1451 SetSourcePosition(prop->position());
1452 Literal* key = prop->key()->AsLiteral();
1453 __ mov(ecx, Immediate(key->handle()));
1454 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
1455 __ call(ic, RelocInfo::CODE_TARGET);
1456 __ nop();
1457}
1458
1459
1460void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
1461 SetSourcePosition(prop->position());
1462 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1463 __ call(ic, RelocInfo::CODE_TARGET);
1464 __ nop();
1465}
1466
1467
1468void FullCodeGenerator::EmitBinaryOp(Token::Value op,
1469 Expression::Context context) {
1470 __ push(result_register());
1471 GenericBinaryOpStub stub(op,
1472 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001473 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001474 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001475 __ CallStub(&stub);
1476 Apply(context, eax);
1477}
1478
1479
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001480void FullCodeGenerator::EmitAssignment(Expression* expr) {
1481 // Invalid left-hand sides are rewritten to have a 'throw
1482 // ReferenceError' on the left-hand side.
1483 if (!expr->IsValidLeftHandSide()) {
1484 VisitForEffect(expr);
1485 return;
1486 }
1487
1488 // Left-hand side can only be a property, a global or a (parameter or local)
1489 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1490 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1491 LhsKind assign_type = VARIABLE;
1492 Property* prop = expr->AsProperty();
1493 if (prop != NULL) {
1494 assign_type = (prop->key()->IsPropertyName())
1495 ? NAMED_PROPERTY
1496 : KEYED_PROPERTY;
1497 }
1498
1499 switch (assign_type) {
1500 case VARIABLE: {
1501 Variable* var = expr->AsVariableProxy()->var();
1502 EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect);
1503 break;
1504 }
1505 case NAMED_PROPERTY: {
1506 __ push(eax); // Preserve value.
1507 VisitForValue(prop->obj(), kAccumulator);
1508 __ mov(edx, eax);
1509 __ pop(eax); // Restore value.
1510 __ mov(ecx, prop->key()->AsLiteral()->handle());
1511 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1512 __ call(ic, RelocInfo::CODE_TARGET);
1513 __ nop(); // Signal no inlined code.
1514 break;
1515 }
1516 case KEYED_PROPERTY: {
1517 __ push(eax); // Preserve value.
1518 VisitForValue(prop->obj(), kStack);
1519 VisitForValue(prop->key(), kAccumulator);
1520 __ mov(ecx, eax);
1521 __ pop(edx);
1522 __ pop(eax); // Restore value.
1523 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1524 __ call(ic, RelocInfo::CODE_TARGET);
1525 __ nop(); // Signal no inlined code.
1526 break;
1527 }
1528 }
1529}
1530
1531
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001532void FullCodeGenerator::EmitVariableAssignment(Variable* var,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001533 Token::Value op,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001534 Expression::Context context) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001535 // Left-hand sides that rewrite to explicit property accesses do not reach
1536 // here.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001537 ASSERT(var != NULL);
1538 ASSERT(var->is_global() || var->slot() != NULL);
1539
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001540 if (var->is_global()) {
1541 ASSERT(!var->is_this());
1542 // Assignment to a global variable. Use inline caching for the
1543 // assignment. Right-hand-side value is passed in eax, variable name in
1544 // ecx, and the global object on the stack.
1545 __ mov(ecx, var->name());
1546 __ mov(edx, CodeGenerator::GlobalObject());
1547 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1548 __ call(ic, RelocInfo::CODE_TARGET);
1549 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001550
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001551 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) {
1552 // Perform the assignment for non-const variables and for initialization
1553 // of const variables. Const assignments are simply skipped.
1554 Label done;
1555 Slot* slot = var->slot();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001556 switch (slot->type()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001557 case Slot::PARAMETER:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001558 case Slot::LOCAL:
1559 if (op == Token::INIT_CONST) {
1560 // Detect const reinitialization by checking for the hole value.
1561 __ mov(edx, Operand(ebp, SlotOffset(slot)));
1562 __ cmp(edx, Factory::the_hole_value());
1563 __ j(not_equal, &done);
1564 }
1565 // Perform the assignment.
1566 __ mov(Operand(ebp, SlotOffset(slot)), eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001567 break;
1568
1569 case Slot::CONTEXT: {
1570 MemOperand target = EmitSlotSearch(slot, ecx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001571 if (op == Token::INIT_CONST) {
1572 // Detect const reinitialization by checking for the hole value.
1573 __ mov(edx, target);
1574 __ cmp(edx, Factory::the_hole_value());
1575 __ j(not_equal, &done);
1576 }
1577 // Perform the assignment and issue the write barrier.
1578 __ mov(target, eax);
1579 // The value of the assignment is in eax. RecordWrite clobbers its
1580 // register arguments.
1581 __ mov(edx, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001582 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
1583 __ RecordWrite(ecx, offset, edx, ebx);
1584 break;
1585 }
1586
1587 case Slot::LOOKUP:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001588 // Call the runtime for the assignment. The runtime will ignore
1589 // const reinitialization.
1590 __ push(eax); // Value.
1591 __ push(esi); // Context.
1592 __ push(Immediate(var->name()));
1593 if (op == Token::INIT_CONST) {
1594 // The runtime will ignore const redeclaration.
1595 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
1596 } else {
1597 __ CallRuntime(Runtime::kStoreContextSlot, 3);
1598 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001599 break;
1600 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001601 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001602 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001603
1604 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001605}
1606
1607
1608void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
1609 // Assignment to a property, using a named store IC.
1610 Property* prop = expr->target()->AsProperty();
1611 ASSERT(prop != NULL);
1612 ASSERT(prop->key()->AsLiteral() != NULL);
1613
1614 // If the assignment starts a block of assignments to the same object,
1615 // change to slow case to avoid the quadratic behavior of repeatedly
1616 // adding fast properties.
1617 if (expr->starts_initialization_block()) {
1618 __ push(result_register());
1619 __ push(Operand(esp, kPointerSize)); // Receiver is now under value.
1620 __ CallRuntime(Runtime::kToSlowProperties, 1);
1621 __ pop(result_register());
1622 }
1623
1624 // Record source code position before IC call.
1625 SetSourcePosition(expr->position());
1626 __ mov(ecx, prop->key()->AsLiteral()->handle());
1627 if (expr->ends_initialization_block()) {
1628 __ mov(edx, Operand(esp, 0));
1629 } else {
1630 __ pop(edx);
1631 }
1632 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1633 __ call(ic, RelocInfo::CODE_TARGET);
1634 __ nop();
1635
1636 // If the assignment ends an initialization block, revert to fast case.
1637 if (expr->ends_initialization_block()) {
1638 __ push(eax); // Result of assignment, saved even if not needed.
1639 __ push(Operand(esp, kPointerSize)); // Receiver is under value.
1640 __ CallRuntime(Runtime::kToFastProperties, 1);
1641 __ pop(eax);
1642 DropAndApply(1, context_, eax);
1643 } else {
1644 Apply(context_, eax);
1645 }
1646}
1647
1648
1649void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
1650 // Assignment to a property, using a keyed store IC.
1651
1652 // If the assignment starts a block of assignments to the same object,
1653 // change to slow case to avoid the quadratic behavior of repeatedly
1654 // adding fast properties.
1655 if (expr->starts_initialization_block()) {
1656 __ push(result_register());
1657 // Receiver is now under the key and value.
1658 __ push(Operand(esp, 2 * kPointerSize));
1659 __ CallRuntime(Runtime::kToSlowProperties, 1);
1660 __ pop(result_register());
1661 }
1662
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001663 __ pop(ecx);
1664 if (expr->ends_initialization_block()) {
1665 __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later.
1666 } else {
1667 __ pop(edx);
1668 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001669 // Record source code position before IC call.
1670 SetSourcePosition(expr->position());
1671 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1672 __ call(ic, RelocInfo::CODE_TARGET);
1673 // This nop signals to the IC that there is no inlined code at the call
1674 // site for it to patch.
1675 __ nop();
1676
1677 // If the assignment ends an initialization block, revert to fast case.
1678 if (expr->ends_initialization_block()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001679 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001680 __ push(eax); // Result of assignment, saved even if not needed.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001681 __ push(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001682 __ CallRuntime(Runtime::kToFastProperties, 1);
1683 __ pop(eax);
1684 }
1685
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001686 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001687}
1688
1689
1690void FullCodeGenerator::VisitProperty(Property* expr) {
1691 Comment cmnt(masm_, "[ Property");
1692 Expression* key = expr->key();
1693
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001694 if (key->IsPropertyName()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00001695 VisitForValue(expr->obj(), kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001696 EmitNamedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001697 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001698 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00001699 VisitForValue(expr->obj(), kStack);
1700 VisitForValue(expr->key(), kAccumulator);
1701 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001702 EmitKeyedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001703 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001704 }
1705}
1706
1707
1708void FullCodeGenerator::EmitCallWithIC(Call* expr,
1709 Handle<Object> name,
1710 RelocInfo::Mode mode) {
1711 // Code common for calls using the IC.
1712 ZoneList<Expression*>* args = expr->arguments();
1713 int arg_count = args->length();
1714 for (int i = 0; i < arg_count; i++) {
1715 VisitForValue(args->at(i), kStack);
1716 }
1717 __ Set(ecx, Immediate(name));
1718 // Record source position of the IC call.
1719 SetSourcePosition(expr->position());
1720 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1721 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
1722 __ call(ic, mode);
1723 // Restore context register.
1724 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1725 Apply(context_, eax);
1726}
1727
1728
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001729void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
1730 Expression* key,
1731 RelocInfo::Mode mode) {
1732 // Code common for calls using the IC.
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 VisitForValue(key, kAccumulator);
1739 __ mov(ecx, eax);
1740 // Record source position of the IC call.
1741 SetSourcePosition(expr->position());
1742 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1743 Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
1744 arg_count, in_loop);
1745 __ call(ic, mode);
1746 // Restore context register.
1747 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1748 Apply(context_, eax);
1749}
1750
1751
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001752void FullCodeGenerator::EmitCallWithStub(Call* expr) {
1753 // Code common for calls using the call stub.
1754 ZoneList<Expression*>* args = expr->arguments();
1755 int arg_count = args->length();
1756 for (int i = 0; i < arg_count; i++) {
1757 VisitForValue(args->at(i), kStack);
1758 }
1759 // Record source position for debugger.
1760 SetSourcePosition(expr->position());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001761 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1762 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001763 __ CallStub(&stub);
1764 // Restore context register.
1765 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1766 DropAndApply(1, context_, eax);
1767}
1768
1769
1770void FullCodeGenerator::VisitCall(Call* expr) {
1771 Comment cmnt(masm_, "[ Call");
1772 Expression* fun = expr->expression();
1773 Variable* var = fun->AsVariableProxy()->AsVariable();
1774
1775 if (var != NULL && var->is_possibly_eval()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001776 // In a call to eval, we first call %ResolvePossiblyDirectEval to
1777 // resolve the function we need to call and the receiver of the
1778 // call. Then we call the resolved function using the given
1779 // arguments.
1780 VisitForValue(fun, kStack);
1781 __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
1782
1783 // Push the arguments.
1784 ZoneList<Expression*>* args = expr->arguments();
1785 int arg_count = args->length();
1786 for (int i = 0; i < arg_count; i++) {
1787 VisitForValue(args->at(i), kStack);
1788 }
1789
1790 // Push copy of the function - found below the arguments.
1791 __ push(Operand(esp, (arg_count + 1) * kPointerSize));
1792
1793 // Push copy of the first argument or undefined if it doesn't exist.
1794 if (arg_count > 0) {
1795 __ push(Operand(esp, arg_count * kPointerSize));
1796 } else {
1797 __ push(Immediate(Factory::undefined_value()));
1798 }
1799
1800 // Push the receiver of the enclosing function and do runtime call.
1801 __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
1802 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
1803
1804 // The runtime call returns a pair of values in eax (function) and
1805 // edx (receiver). Touch up the stack with the right values.
1806 __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
1807 __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
1808
1809 // Record source position for debugger.
1810 SetSourcePosition(expr->position());
1811 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1812 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
1813 __ CallStub(&stub);
1814 // Restore context register.
1815 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1816 DropAndApply(1, context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001817 } else if (var != NULL && !var->is_this() && var->is_global()) {
1818 // Push global object as receiver for the call IC.
1819 __ push(CodeGenerator::GlobalObject());
1820 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
1821 } else if (var != NULL && var->slot() != NULL &&
1822 var->slot()->type() == Slot::LOOKUP) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001823 // Call to a lookup slot (dynamically introduced variable). Call the
1824 // runtime to find the function to call (returned in eax) and the object
1825 // holding it (returned in edx).
1826 __ push(context_register());
1827 __ push(Immediate(var->name()));
1828 __ CallRuntime(Runtime::kLoadContextSlot, 2);
1829 __ push(eax); // Function.
1830 __ push(edx); // Receiver.
1831 EmitCallWithStub(expr);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001832 } else if (fun->AsProperty() != NULL) {
1833 // Call to an object property.
1834 Property* prop = fun->AsProperty();
1835 Literal* key = prop->key()->AsLiteral();
1836 if (key != NULL && key->handle()->IsSymbol()) {
1837 // Call to a named property, use call IC.
1838 VisitForValue(prop->obj(), kStack);
1839 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
1840 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001841 // Call to a keyed property.
1842 // For a synthetic property use keyed load IC followed by function call,
1843 // for a regular property use keyed CallIC.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001844 VisitForValue(prop->obj(), kStack);
ager@chromium.org5c838252010-02-19 08:53:10 +00001845 if (prop->is_synthetic()) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001846 VisitForValue(prop->key(), kAccumulator);
1847 // Record source code position for IC call.
1848 SetSourcePosition(prop->position());
ager@chromium.org5c838252010-02-19 08:53:10 +00001849 __ pop(edx); // We do not need to keep the receiver.
ager@chromium.org5c838252010-02-19 08:53:10 +00001850
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001851 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1852 __ call(ic, RelocInfo::CODE_TARGET);
1853 // By emitting a nop we make sure that we do not have a "test eax,..."
1854 // instruction after the call as it is treated specially
1855 // by the LoadIC code.
1856 __ nop();
ager@chromium.org5c838252010-02-19 08:53:10 +00001857 // Push result (function).
1858 __ push(eax);
1859 // Push Global receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001860 __ mov(ecx, CodeGenerator::GlobalObject());
1861 __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001862 EmitCallWithStub(expr);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001863 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001864 EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001865 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001866 }
1867 } else {
1868 // Call to some other expression. If the expression is an anonymous
1869 // function literal not called in a loop, mark it as one that should
1870 // also use the full code generator.
1871 FunctionLiteral* lit = fun->AsFunctionLiteral();
1872 if (lit != NULL &&
1873 lit->name()->Equals(Heap::empty_string()) &&
1874 loop_depth() == 0) {
1875 lit->set_try_full_codegen(true);
1876 }
1877 VisitForValue(fun, kStack);
1878 // Load global receiver object.
1879 __ mov(ebx, CodeGenerator::GlobalObject());
1880 __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
1881 // Emit function call.
1882 EmitCallWithStub(expr);
1883 }
1884}
1885
1886
1887void FullCodeGenerator::VisitCallNew(CallNew* expr) {
1888 Comment cmnt(masm_, "[ CallNew");
1889 // According to ECMA-262, section 11.2.2, page 44, the function
1890 // expression in new calls must be evaluated before the
1891 // arguments.
1892 // Push function on the stack.
1893 VisitForValue(expr->expression(), kStack);
1894
1895 // Push global object (receiver).
1896 __ push(CodeGenerator::GlobalObject());
1897
1898 // Push the arguments ("left-to-right") on the stack.
1899 ZoneList<Expression*>* args = expr->arguments();
1900 int arg_count = args->length();
1901 for (int i = 0; i < arg_count; i++) {
1902 VisitForValue(args->at(i), kStack);
1903 }
1904
1905 // Call the construct call builtin that handles allocation and
1906 // constructor invocation.
1907 SetSourcePosition(expr->position());
1908
1909 // Load function, arg_count into edi and eax.
1910 __ Set(eax, Immediate(arg_count));
1911 // Function is in esp[arg_count + 1].
1912 __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize));
1913
1914 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall));
1915 __ call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
1916
1917 // Replace function on TOS with result in eax, or pop it.
1918 DropAndApply(1, context_, eax);
1919}
1920
1921
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001922void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
1923 ASSERT(args->length() == 1);
1924
1925 VisitForValue(args->at(0), kAccumulator);
1926
1927 Label materialize_true, materialize_false;
1928 Label* if_true = NULL;
1929 Label* if_false = NULL;
1930 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1931
1932 __ test(eax, Immediate(kSmiTagMask));
1933 __ j(zero, if_true);
1934 __ jmp(if_false);
1935
1936 Apply(context_, if_true, if_false);
1937}
1938
1939
1940void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) {
1941 ASSERT(args->length() == 1);
1942
1943 VisitForValue(args->at(0), kAccumulator);
1944
1945 Label materialize_true, materialize_false;
1946 Label* if_true = NULL;
1947 Label* if_false = NULL;
1948 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1949
1950 __ test(eax, Immediate(kSmiTagMask | 0x80000000));
1951 __ j(zero, if_true);
1952 __ jmp(if_false);
1953
1954 Apply(context_, if_true, if_false);
1955}
1956
1957
1958void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
1959 ASSERT(args->length() == 1);
1960
1961 VisitForValue(args->at(0), kAccumulator);
1962
1963 Label materialize_true, materialize_false;
1964 Label* if_true = NULL;
1965 Label* if_false = NULL;
1966 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1967
1968 __ test(eax, Immediate(kSmiTagMask));
1969 __ j(zero, if_false);
1970 __ cmp(eax, Factory::null_value());
1971 __ j(equal, if_true);
1972 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
1973 // Undetectable objects behave like undefined when tested with typeof.
1974 __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset));
1975 __ test(ecx, Immediate(1 << Map::kIsUndetectable));
1976 __ j(not_zero, if_false);
1977 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset));
1978 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
1979 __ j(below, if_false);
1980 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
1981 __ j(below_equal, if_true);
1982 __ jmp(if_false);
1983
1984 Apply(context_, if_true, if_false);
1985}
1986
1987
1988void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
1989 ASSERT(args->length() == 1);
1990
1991 VisitForValue(args->at(0), kAccumulator);
1992
1993 Label materialize_true, materialize_false;
1994 Label* if_true = NULL;
1995 Label* if_false = NULL;
1996 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1997
1998 __ test(eax, Immediate(kSmiTagMask));
1999 __ j(zero, if_false);
2000 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2001 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
2002 __ test(ebx, Immediate(1 << Map::kIsUndetectable));
2003 __ j(not_zero, if_true);
2004 __ jmp(if_false);
2005
2006 Apply(context_, if_true, if_false);
2007}
2008
2009
2010void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
2011 ASSERT(args->length() == 1);
2012
2013 VisitForValue(args->at(0), kAccumulator);
2014
2015 Label materialize_true, materialize_false;
2016 Label* if_true = NULL;
2017 Label* if_false = NULL;
2018 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2019
2020 __ test(eax, Immediate(kSmiTagMask));
2021 __ j(zero, if_false);
2022 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2023 __ j(equal, if_true);
2024 __ jmp(if_false);
2025
2026 Apply(context_, if_true, if_false);
2027}
2028
2029
2030void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) {
2031 ASSERT(args->length() == 1);
2032
2033 VisitForValue(args->at(0), kAccumulator);
2034
2035 Label materialize_true, materialize_false;
2036 Label* if_true = NULL;
2037 Label* if_false = NULL;
2038 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2039
2040 __ test(eax, Immediate(kSmiTagMask));
2041 __ j(equal, if_false);
2042 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
2043 __ j(equal, if_true);
2044 __ jmp(if_false);
2045
2046 Apply(context_, if_true, if_false);
2047}
2048
2049
2050void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) {
2051 ASSERT(args->length() == 1);
2052
2053 VisitForValue(args->at(0), kAccumulator);
2054
2055 Label materialize_true, materialize_false;
2056 Label* if_true = NULL;
2057 Label* if_false = NULL;
2058 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2059
2060 __ test(eax, Immediate(kSmiTagMask));
2061 __ j(equal, if_false);
2062 __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx);
2063 __ j(equal, if_true);
2064 __ jmp(if_false);
2065
2066 Apply(context_, if_true, if_false);
2067}
2068
2069
2070
2071void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) {
2072 ASSERT(args->length() == 0);
2073
2074 Label materialize_true, materialize_false;
2075 Label* if_true = NULL;
2076 Label* if_false = NULL;
2077 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2078
2079 // Get the frame pointer for the calling frame.
2080 __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2081
2082 // Skip the arguments adaptor frame if it exists.
2083 Label check_frame_marker;
2084 __ cmp(Operand(eax, StandardFrameConstants::kContextOffset),
2085 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2086 __ j(not_equal, &check_frame_marker);
2087 __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset));
2088
2089 // Check the marker in the calling frame.
2090 __ bind(&check_frame_marker);
2091 __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset),
2092 Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
2093 __ j(equal, if_true);
2094 __ jmp(if_false);
2095
2096 Apply(context_, if_true, if_false);
2097}
2098
2099
2100void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) {
2101 ASSERT(args->length() == 2);
2102
2103 // Load the two objects into registers and perform the comparison.
2104 VisitForValue(args->at(0), kStack);
2105 VisitForValue(args->at(1), kAccumulator);
2106
2107 Label materialize_true, materialize_false;
2108 Label* if_true = NULL;
2109 Label* if_false = NULL;
2110 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2111
2112 __ pop(ebx);
2113 __ cmp(eax, Operand(ebx));
2114 __ j(equal, if_true);
2115 __ jmp(if_false);
2116
2117 Apply(context_, if_true, if_false);
2118}
2119
2120
2121void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) {
2122 ASSERT(args->length() == 1);
2123
2124 // ArgumentsAccessStub expects the key in edx and the formal
2125 // parameter count in eax.
2126 VisitForValue(args->at(0), kAccumulator);
2127 __ mov(edx, eax);
2128 __ mov(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2129 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
2130 __ CallStub(&stub);
2131 Apply(context_, eax);
2132}
2133
2134
2135void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
2136 ASSERT(args->length() == 0);
2137
2138 Label exit;
2139 // Get the number of formal parameters.
2140 __ Set(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2141
2142 // Check if the calling frame is an arguments adaptor frame.
2143 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2144 __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset),
2145 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2146 __ j(not_equal, &exit);
2147
2148 // Arguments adaptor case: Read the arguments length from the
2149 // adaptor frame.
2150 __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
2151
2152 __ bind(&exit);
2153 if (FLAG_debug_code) __ AbortIfNotSmi(eax);
2154 Apply(context_, eax);
2155}
2156
2157
2158void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) {
2159 ASSERT(args->length() == 1);
2160 Label done, null, function, non_function_constructor;
2161
2162 VisitForValue(args->at(0), kAccumulator);
2163
2164 // If the object is a smi, we return null.
2165 __ test(eax, Immediate(kSmiTagMask));
2166 __ j(zero, &null);
2167
2168 // Check that the object is a JS object but take special care of JS
2169 // functions to make sure they have 'Function' as their class.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002170 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, eax); // Map is now in eax.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002171 __ j(below, &null);
2172
2173 // As long as JS_FUNCTION_TYPE is the last instance type and it is
2174 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
2175 // LAST_JS_OBJECT_TYPE.
2176 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
2177 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002178 __ CmpInstanceType(eax, JS_FUNCTION_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002179 __ j(equal, &function);
2180
2181 // Check if the constructor in the map is a function.
2182 __ mov(eax, FieldOperand(eax, Map::kConstructorOffset));
2183 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2184 __ j(not_equal, &non_function_constructor);
2185
2186 // eax now contains the constructor function. Grab the
2187 // instance class name from there.
2188 __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
2189 __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset));
2190 __ jmp(&done);
2191
2192 // Functions have class 'Function'.
2193 __ bind(&function);
2194 __ mov(eax, Factory::function_class_symbol());
2195 __ jmp(&done);
2196
2197 // Objects with a non-function constructor have class 'Object'.
2198 __ bind(&non_function_constructor);
2199 __ mov(eax, Factory::Object_symbol());
2200 __ jmp(&done);
2201
2202 // Non-JS objects have class null.
2203 __ bind(&null);
2204 __ mov(eax, Factory::null_value());
2205
2206 // All done.
2207 __ bind(&done);
2208
2209 Apply(context_, eax);
2210}
2211
2212
2213void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) {
2214 // Conditionally generate a log call.
2215 // Args:
2216 // 0 (literal string): The type of logging (corresponds to the flags).
2217 // This is used to determine whether or not to generate the log call.
2218 // 1 (string): Format string. Access the string at argument index 2
2219 // with '%2s' (see Logger::LogRuntime for all the formats).
2220 // 2 (array): Arguments to the format string.
2221 ASSERT_EQ(args->length(), 3);
2222#ifdef ENABLE_LOGGING_AND_PROFILING
2223 if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
2224 VisitForValue(args->at(1), kStack);
2225 VisitForValue(args->at(2), kStack);
2226 __ CallRuntime(Runtime::kLog, 2);
2227 }
2228#endif
2229 // Finally, we're expected to leave a value on the top of the stack.
2230 __ mov(eax, Factory::undefined_value());
2231 Apply(context_, eax);
2232}
2233
2234
2235void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
2236 ASSERT(args->length() == 0);
2237
2238 Label slow_allocate_heapnumber;
2239 Label heapnumber_allocated;
2240
2241 __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
2242 __ jmp(&heapnumber_allocated);
2243
2244 __ bind(&slow_allocate_heapnumber);
2245 // To allocate a heap number, and ensure that it is not a smi, we
2246 // call the runtime function FUnaryMinus on 0, returning the double
2247 // -0.0. A new, distinct heap number is returned each time.
2248 __ push(Immediate(Smi::FromInt(0)));
2249 __ CallRuntime(Runtime::kNumberUnaryMinus, 1);
2250 __ mov(edi, eax);
2251
2252 __ bind(&heapnumber_allocated);
2253
2254 __ PrepareCallCFunction(0, ebx);
2255 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
2256
2257 // Convert 32 random bits in eax to 0.(32 random bits) in a double
2258 // by computing:
2259 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
2260 // This is implemented on both SSE2 and FPU.
2261 if (CpuFeatures::IsSupported(SSE2)) {
2262 CpuFeatures::Scope fscope(SSE2);
2263 __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
2264 __ movd(xmm1, Operand(ebx));
2265 __ movd(xmm0, Operand(eax));
2266 __ cvtss2sd(xmm1, xmm1);
2267 __ pxor(xmm0, xmm1);
2268 __ subsd(xmm0, xmm1);
2269 __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
2270 } else {
2271 // 0x4130000000000000 is 1.0 x 2^20 as a double.
2272 __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
2273 Immediate(0x41300000));
2274 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
2275 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2276 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
2277 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2278 __ fsubp(1);
2279 __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
2280 }
2281 __ mov(eax, edi);
2282 Apply(context_, eax);
2283}
2284
2285
2286void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) {
2287 // Load the arguments on the stack and call the stub.
2288 SubStringStub stub;
2289 ASSERT(args->length() == 3);
2290 VisitForValue(args->at(0), kStack);
2291 VisitForValue(args->at(1), kStack);
2292 VisitForValue(args->at(2), kStack);
2293 __ CallStub(&stub);
2294 Apply(context_, eax);
2295}
2296
2297
2298void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) {
2299 // Load the arguments on the stack and call the stub.
2300 RegExpExecStub stub;
2301 ASSERT(args->length() == 4);
2302 VisitForValue(args->at(0), kStack);
2303 VisitForValue(args->at(1), kStack);
2304 VisitForValue(args->at(2), kStack);
2305 VisitForValue(args->at(3), kStack);
2306 __ CallStub(&stub);
2307 Apply(context_, eax);
2308}
2309
2310
2311void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
2312 ASSERT(args->length() == 1);
2313
2314 VisitForValue(args->at(0), kAccumulator); // Load the object.
2315
2316 Label done;
2317 // If the object is a smi return the object.
2318 __ test(eax, Immediate(kSmiTagMask));
2319 __ j(zero, &done);
2320 // If the object is not a value type, return the object.
2321 __ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
2322 __ j(not_equal, &done);
2323 __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
2324
2325 __ bind(&done);
2326 Apply(context_, eax);
2327}
2328
2329
2330void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) {
2331 // Load the arguments on the stack and call the runtime function.
2332 ASSERT(args->length() == 2);
2333 VisitForValue(args->at(0), kStack);
2334 VisitForValue(args->at(1), kStack);
2335 __ CallRuntime(Runtime::kMath_pow, 2);
2336 Apply(context_, eax);
2337}
2338
2339
2340void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
2341 ASSERT(args->length() == 2);
2342
2343 VisitForValue(args->at(0), kStack); // Load the object.
2344 VisitForValue(args->at(1), kAccumulator); // Load the value.
2345 __ pop(ebx); // eax = value. ebx = object.
2346
2347 Label done;
2348 // If the object is a smi, return the value.
2349 __ test(ebx, Immediate(kSmiTagMask));
2350 __ j(zero, &done);
2351
2352 // If the object is not a value type, return the value.
2353 __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
2354 __ j(not_equal, &done);
2355
2356 // Store the value.
2357 __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax);
2358 // Update the write barrier. Save the value as it will be
2359 // overwritten by the write barrier code and is needed afterward.
2360 __ mov(edx, eax);
2361 __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx);
2362
2363 __ bind(&done);
2364 Apply(context_, eax);
2365}
2366
2367
2368void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
2369 ASSERT_EQ(args->length(), 1);
2370
2371 // Load the argument on the stack and call the stub.
2372 VisitForValue(args->at(0), kStack);
2373
2374 NumberToStringStub stub;
2375 __ CallStub(&stub);
2376 Apply(context_, eax);
2377}
2378
2379
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002380void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002381 ASSERT(args->length() == 1);
2382
2383 VisitForValue(args->at(0), kAccumulator);
2384
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002385 Label done;
2386 StringCharFromCodeGenerator generator(eax, ebx);
2387 generator.GenerateFast(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002388 __ jmp(&done);
2389
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002390 NopRuntimeCallHelper call_helper;
2391 generator.GenerateSlow(masm_, call_helper);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002392
2393 __ bind(&done);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002394 Apply(context_, ebx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002395}
2396
2397
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002398void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
2399 ASSERT(args->length() == 2);
2400
2401 VisitForValue(args->at(0), kStack);
2402 VisitForValue(args->at(1), kAccumulator);
2403
2404 Register object = ebx;
2405 Register index = eax;
2406 Register scratch = ecx;
2407 Register result = edx;
2408
2409 __ pop(object);
2410
2411 Label need_conversion;
2412 Label index_out_of_range;
2413 Label done;
2414 StringCharCodeAtGenerator generator(object,
2415 index,
2416 scratch,
2417 result,
2418 &need_conversion,
2419 &need_conversion,
2420 &index_out_of_range,
2421 STRING_INDEX_IS_NUMBER);
2422 generator.GenerateFast(masm_);
2423 __ jmp(&done);
2424
2425 __ bind(&index_out_of_range);
2426 // When the index is out of range, the spec requires us to return
2427 // NaN.
2428 __ Set(result, Immediate(Factory::nan_value()));
2429 __ jmp(&done);
2430
2431 __ bind(&need_conversion);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002432 // Move the undefined value into the result register, which will
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002433 // trigger conversion.
2434 __ Set(result, Immediate(Factory::undefined_value()));
2435 __ jmp(&done);
2436
2437 NopRuntimeCallHelper call_helper;
2438 generator.GenerateSlow(masm_, call_helper);
2439
2440 __ bind(&done);
2441 Apply(context_, result);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002442}
2443
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002444
2445void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
2446 ASSERT(args->length() == 2);
2447
2448 VisitForValue(args->at(0), kStack);
2449 VisitForValue(args->at(1), kAccumulator);
2450
2451 Register object = ebx;
2452 Register index = eax;
2453 Register scratch1 = ecx;
2454 Register scratch2 = edx;
2455 Register result = eax;
2456
2457 __ pop(object);
2458
2459 Label need_conversion;
2460 Label index_out_of_range;
2461 Label done;
2462 StringCharAtGenerator generator(object,
2463 index,
2464 scratch1,
2465 scratch2,
2466 result,
2467 &need_conversion,
2468 &need_conversion,
2469 &index_out_of_range,
2470 STRING_INDEX_IS_NUMBER);
2471 generator.GenerateFast(masm_);
2472 __ jmp(&done);
2473
2474 __ bind(&index_out_of_range);
2475 // When the index is out of range, the spec requires us to return
2476 // the empty string.
2477 __ Set(result, Immediate(Factory::empty_string()));
2478 __ jmp(&done);
2479
2480 __ bind(&need_conversion);
2481 // Move smi zero into the result register, which will trigger
2482 // conversion.
2483 __ Set(result, Immediate(Smi::FromInt(0)));
2484 __ jmp(&done);
2485
2486 NopRuntimeCallHelper call_helper;
2487 generator.GenerateSlow(masm_, call_helper);
2488
2489 __ bind(&done);
2490 Apply(context_, result);
2491}
2492
2493
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002494void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
2495 ASSERT_EQ(2, args->length());
2496
2497 VisitForValue(args->at(0), kStack);
2498 VisitForValue(args->at(1), kStack);
2499
2500 StringAddStub stub(NO_STRING_ADD_FLAGS);
2501 __ CallStub(&stub);
2502 Apply(context_, eax);
2503}
2504
2505
2506void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) {
2507 ASSERT_EQ(2, args->length());
2508
2509 VisitForValue(args->at(0), kStack);
2510 VisitForValue(args->at(1), kStack);
2511
2512 StringCompareStub stub;
2513 __ CallStub(&stub);
2514 Apply(context_, eax);
2515}
2516
2517
2518void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) {
2519 // Load the argument on the stack and call the stub.
2520 TranscendentalCacheStub stub(TranscendentalCache::SIN);
2521 ASSERT(args->length() == 1);
2522 VisitForValue(args->at(0), kStack);
2523 __ CallStub(&stub);
2524 Apply(context_, eax);
2525}
2526
2527
2528void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
2529 // Load the argument on the stack and call the stub.
2530 TranscendentalCacheStub stub(TranscendentalCache::COS);
2531 ASSERT(args->length() == 1);
2532 VisitForValue(args->at(0), kStack);
2533 __ CallStub(&stub);
2534 Apply(context_, eax);
2535}
2536
2537
2538void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
2539 // Load the argument on the stack and call the runtime function.
2540 ASSERT(args->length() == 1);
2541 VisitForValue(args->at(0), kStack);
2542 __ CallRuntime(Runtime::kMath_sqrt, 1);
2543 Apply(context_, eax);
2544}
2545
2546
2547void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
2548 ASSERT(args->length() >= 2);
2549
2550 int arg_count = args->length() - 2; // For receiver and function.
2551 VisitForValue(args->at(0), kStack); // Receiver.
2552 for (int i = 0; i < arg_count; i++) {
2553 VisitForValue(args->at(i + 1), kStack);
2554 }
2555 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function.
2556
2557 // InvokeFunction requires function in edi. Move it in there.
2558 if (!result_register().is(edi)) __ mov(edi, result_register());
2559 ParameterCount count(arg_count);
2560 __ InvokeFunction(edi, count, CALL_FUNCTION);
2561 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2562 Apply(context_, eax);
2563}
2564
2565
2566void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) {
2567 ASSERT(args->length() == 3);
2568 VisitForValue(args->at(0), kStack);
2569 VisitForValue(args->at(1), kStack);
2570 VisitForValue(args->at(2), kStack);
2571 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
2572 Apply(context_, eax);
2573}
2574
2575
2576void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
2577 ASSERT(args->length() == 3);
2578 VisitForValue(args->at(0), kStack);
2579 VisitForValue(args->at(1), kStack);
2580 VisitForValue(args->at(2), kStack);
2581 __ CallRuntime(Runtime::kSwapElements, 3);
2582 Apply(context_, eax);
2583}
2584
2585
2586void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
2587 ASSERT_EQ(2, args->length());
2588
2589 ASSERT_NE(NULL, args->at(0)->AsLiteral());
2590 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
2591
2592 Handle<FixedArray> jsfunction_result_caches(
2593 Top::global_context()->jsfunction_result_caches());
2594 if (jsfunction_result_caches->length() <= cache_id) {
2595 __ Abort("Attempt to use undefined cache.");
2596 __ mov(eax, Factory::undefined_value());
2597 Apply(context_, eax);
2598 return;
2599 }
2600
2601 VisitForValue(args->at(1), kAccumulator);
2602
2603 Register key = eax;
2604 Register cache = ebx;
2605 Register tmp = ecx;
2606 __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX));
2607 __ mov(cache,
2608 FieldOperand(cache, GlobalObject::kGlobalContextOffset));
2609 __ mov(cache,
2610 CodeGenerator::ContextOperand(
2611 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
2612 __ mov(cache,
2613 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
2614
2615 Label done, not_found;
2616 // tmp now holds finger offset as a smi.
2617 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
2618 __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
2619 __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp));
2620 __ j(not_equal, &not_found);
2621
2622 __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1));
2623 __ jmp(&done);
2624
2625 __ bind(&not_found);
2626 // Call runtime to perform the lookup.
2627 __ push(cache);
2628 __ push(key);
2629 __ CallRuntime(Runtime::kGetFromCache, 2);
2630
2631 __ bind(&done);
2632 Apply(context_, eax);
2633}
2634
2635
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002636void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002637 Handle<String> name = expr->name();
2638 if (name->length() > 0 && name->Get(0) == '_') {
2639 Comment cmnt(masm_, "[ InlineRuntimeCall");
2640 EmitInlineRuntimeCall(expr);
2641 return;
2642 }
2643
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002644 Comment cmnt(masm_, "[ CallRuntime");
2645 ZoneList<Expression*>* args = expr->arguments();
2646
2647 if (expr->is_jsruntime()) {
2648 // Prepare for calling JS runtime function.
2649 __ mov(eax, CodeGenerator::GlobalObject());
2650 __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
2651 }
2652
2653 // Push the arguments ("left-to-right").
2654 int arg_count = args->length();
2655 for (int i = 0; i < arg_count; i++) {
2656 VisitForValue(args->at(i), kStack);
2657 }
2658
2659 if (expr->is_jsruntime()) {
2660 // Call the JS runtime function via a call IC.
2661 __ Set(ecx, Immediate(expr->name()));
2662 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
2663 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
2664 __ call(ic, RelocInfo::CODE_TARGET);
2665 // Restore context register.
2666 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2667 } else {
2668 // Call the C runtime function.
2669 __ CallRuntime(expr->function(), arg_count);
2670 }
2671 Apply(context_, eax);
2672}
2673
2674
2675void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
2676 switch (expr->op()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002677 case Token::DELETE: {
2678 Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
2679 Property* prop = expr->expression()->AsProperty();
2680 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
2681 if (prop == NULL && var == NULL) {
2682 // Result of deleting non-property, non-variable reference is true.
2683 // The subexpression may have side effects.
2684 VisitForEffect(expr->expression());
2685 Apply(context_, true);
2686 } else if (var != NULL &&
2687 !var->is_global() &&
2688 var->slot() != NULL &&
2689 var->slot()->type() != Slot::LOOKUP) {
2690 // Result of deleting non-global, non-dynamic variables is false.
2691 // The subexpression does not have side effects.
2692 Apply(context_, false);
2693 } else {
2694 // Property or variable reference. Call the delete builtin with
2695 // object and property name as arguments.
2696 if (prop != NULL) {
2697 VisitForValue(prop->obj(), kStack);
2698 VisitForValue(prop->key(), kStack);
2699 } else if (var->is_global()) {
2700 __ push(CodeGenerator::GlobalObject());
2701 __ push(Immediate(var->name()));
2702 } else {
2703 // Non-global variable. Call the runtime to look up the context
2704 // where the variable was introduced.
2705 __ push(context_register());
2706 __ push(Immediate(var->name()));
2707 __ CallRuntime(Runtime::kLookupContext, 2);
2708 __ push(eax);
2709 __ push(Immediate(var->name()));
2710 }
2711 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
2712 Apply(context_, eax);
2713 }
2714 break;
2715 }
2716
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002717 case Token::VOID: {
2718 Comment cmnt(masm_, "[ UnaryOperation (VOID)");
2719 VisitForEffect(expr->expression());
2720 switch (context_) {
2721 case Expression::kUninitialized:
2722 UNREACHABLE();
2723 break;
2724 case Expression::kEffect:
2725 break;
2726 case Expression::kValue:
2727 switch (location_) {
2728 case kAccumulator:
2729 __ mov(result_register(), Factory::undefined_value());
2730 break;
2731 case kStack:
2732 __ push(Immediate(Factory::undefined_value()));
2733 break;
2734 }
2735 break;
2736 case Expression::kTestValue:
2737 // Value is false so it's needed.
2738 switch (location_) {
2739 case kAccumulator:
2740 __ mov(result_register(), Factory::undefined_value());
2741 break;
2742 case kStack:
2743 __ push(Immediate(Factory::undefined_value()));
2744 break;
2745 }
2746 // Fall through.
2747 case Expression::kTest:
2748 case Expression::kValueTest:
2749 __ jmp(false_label_);
2750 break;
2751 }
2752 break;
2753 }
2754
2755 case Token::NOT: {
2756 Comment cmnt(masm_, "[ UnaryOperation (NOT)");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002757 Label materialize_true, materialize_false;
2758 Label* if_true = NULL;
2759 Label* if_false = NULL;
2760
2761 // Notice that the labels are swapped.
2762 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true);
2763
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002764 VisitForControl(expr->expression(), if_true, if_false);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002765
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002766 Apply(context_, if_false, if_true); // Labels swapped.
2767 break;
2768 }
2769
2770 case Token::TYPEOF: {
2771 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
2772 VariableProxy* proxy = expr->expression()->AsVariableProxy();
2773 if (proxy != NULL &&
2774 !proxy->var()->is_this() &&
2775 proxy->var()->is_global()) {
2776 Comment cmnt(masm_, "Global variable");
ager@chromium.org5c838252010-02-19 08:53:10 +00002777 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002778 __ mov(ecx, Immediate(proxy->name()));
2779 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
2780 // Use a regular load, not a contextual load, to avoid a reference
2781 // error.
2782 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org5c838252010-02-19 08:53:10 +00002783 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002784 } else if (proxy != NULL &&
2785 proxy->var()->slot() != NULL &&
2786 proxy->var()->slot()->type() == Slot::LOOKUP) {
2787 __ push(esi);
2788 __ push(Immediate(proxy->name()));
2789 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
2790 __ push(eax);
2791 } else {
2792 // This expression cannot throw a reference error at the top level.
2793 VisitForValue(expr->expression(), kStack);
2794 }
2795
2796 __ CallRuntime(Runtime::kTypeof, 1);
2797 Apply(context_, eax);
2798 break;
2799 }
2800
2801 case Token::ADD: {
2802 Comment cmt(masm_, "[ UnaryOperation (ADD)");
2803 VisitForValue(expr->expression(), kAccumulator);
2804 Label no_conversion;
2805 __ test(result_register(), Immediate(kSmiTagMask));
2806 __ j(zero, &no_conversion);
2807 __ push(result_register());
2808 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2809 __ bind(&no_conversion);
2810 Apply(context_, result_register());
2811 break;
2812 }
2813
2814 case Token::SUB: {
2815 Comment cmt(masm_, "[ UnaryOperation (SUB)");
2816 bool overwrite =
2817 (expr->expression()->AsBinaryOperation() != NULL &&
2818 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2819 GenericUnaryOpStub stub(Token::SUB, overwrite);
2820 // GenericUnaryOpStub expects the argument to be in the
2821 // accumulator register eax.
2822 VisitForValue(expr->expression(), kAccumulator);
2823 __ CallStub(&stub);
2824 Apply(context_, eax);
2825 break;
2826 }
2827
2828 case Token::BIT_NOT: {
2829 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
2830 bool overwrite =
2831 (expr->expression()->AsBinaryOperation() != NULL &&
2832 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
2833 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
2834 // GenericUnaryOpStub expects the argument to be in the
2835 // accumulator register eax.
2836 VisitForValue(expr->expression(), kAccumulator);
2837 // Avoid calling the stub for Smis.
2838 Label smi, done;
2839 __ test(result_register(), Immediate(kSmiTagMask));
2840 __ j(zero, &smi);
2841 // Non-smi: call stub leaving result in accumulator register.
2842 __ CallStub(&stub);
2843 __ jmp(&done);
2844 // Perform operation directly on Smis.
2845 __ bind(&smi);
2846 __ not_(result_register());
2847 __ and_(result_register(), ~kSmiTagMask); // Remove inverted smi-tag.
2848 __ bind(&done);
2849 Apply(context_, result_register());
2850 break;
2851 }
2852
2853 default:
2854 UNREACHABLE();
2855 }
2856}
2857
2858
2859void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
2860 Comment cmnt(masm_, "[ CountOperation");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002861 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
2862 // as the left-hand side.
2863 if (!expr->expression()->IsValidLeftHandSide()) {
2864 VisitForEffect(expr->expression());
2865 return;
2866 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002867
2868 // Expression can only be a property, a global or a (parameter or local)
2869 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
2870 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
2871 LhsKind assign_type = VARIABLE;
2872 Property* prop = expr->expression()->AsProperty();
2873 // In case of a property we use the uninitialized expression context
2874 // of the key to detect a named property.
2875 if (prop != NULL) {
2876 assign_type =
2877 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
2878 }
2879
2880 // Evaluate expression and get value.
2881 if (assign_type == VARIABLE) {
2882 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
2883 Location saved_location = location_;
2884 location_ = kAccumulator;
2885 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
2886 Expression::kValue);
2887 location_ = saved_location;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002888 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002889 // Reserve space for result of postfix operation.
2890 if (expr->is_postfix() && context_ != Expression::kEffect) {
2891 __ push(Immediate(Smi::FromInt(0)));
2892 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002893 if (assign_type == NAMED_PROPERTY) {
ager@chromium.org5c838252010-02-19 08:53:10 +00002894 // Put the object both on the stack and in the accumulator.
2895 VisitForValue(prop->obj(), kAccumulator);
2896 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002897 EmitNamedPropertyLoad(prop);
2898 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00002899 VisitForValue(prop->obj(), kStack);
2900 VisitForValue(prop->key(), kAccumulator);
2901 __ mov(edx, Operand(esp, 0));
2902 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002903 EmitKeyedPropertyLoad(prop);
2904 }
2905 }
2906
2907 // Call ToNumber only if operand is not a smi.
2908 Label no_conversion;
2909 __ test(eax, Immediate(kSmiTagMask));
2910 __ j(zero, &no_conversion);
2911 __ push(eax);
2912 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2913 __ bind(&no_conversion);
2914
2915 // Save result for postfix expressions.
2916 if (expr->is_postfix()) {
2917 switch (context_) {
2918 case Expression::kUninitialized:
2919 UNREACHABLE();
2920 case Expression::kEffect:
2921 // Do not save result.
2922 break;
2923 case Expression::kValue:
2924 case Expression::kTest:
2925 case Expression::kValueTest:
2926 case Expression::kTestValue:
2927 // Save the result on the stack. If we have a named or keyed property
2928 // we store the result under the receiver that is currently on top
2929 // of the stack.
2930 switch (assign_type) {
2931 case VARIABLE:
2932 __ push(eax);
2933 break;
2934 case NAMED_PROPERTY:
2935 __ mov(Operand(esp, kPointerSize), eax);
2936 break;
2937 case KEYED_PROPERTY:
2938 __ mov(Operand(esp, 2 * kPointerSize), eax);
2939 break;
2940 }
2941 break;
2942 }
2943 }
2944
2945 // Inline smi case if we are in a loop.
2946 Label stub_call, done;
2947 if (loop_depth() > 0) {
2948 if (expr->op() == Token::INC) {
2949 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2950 } else {
2951 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2952 }
2953 __ j(overflow, &stub_call);
2954 // We could eliminate this smi check if we split the code at
2955 // the first smi check before calling ToNumber.
2956 __ test(eax, Immediate(kSmiTagMask));
2957 __ j(zero, &done);
2958 __ bind(&stub_call);
2959 // Call stub. Undo operation first.
2960 if (expr->op() == Token::INC) {
2961 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
2962 } else {
2963 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
2964 }
2965 }
2966 // Call stub for +1/-1.
2967 GenericBinaryOpStub stub(expr->binary_op(),
2968 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00002969 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00002970 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002971 stub.GenerateCall(masm(), eax, Smi::FromInt(1));
2972 __ bind(&done);
2973
2974 // Store the value returned in eax.
2975 switch (assign_type) {
2976 case VARIABLE:
2977 if (expr->is_postfix()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002978 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002979 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002980 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002981 Expression::kEffect);
2982 // For all contexts except kEffect: We have the result on
2983 // top of the stack.
2984 if (context_ != Expression::kEffect) {
2985 ApplyTOS(context_);
2986 }
2987 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002988 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002989 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002990 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002991 context_);
2992 }
2993 break;
2994 case NAMED_PROPERTY: {
2995 __ mov(ecx, prop->key()->AsLiteral()->handle());
2996 __ pop(edx);
2997 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
2998 __ call(ic, RelocInfo::CODE_TARGET);
2999 // This nop signals to the IC that there is no inlined code at the call
3000 // site for it to patch.
3001 __ nop();
3002 if (expr->is_postfix()) {
3003 if (context_ != Expression::kEffect) {
3004 ApplyTOS(context_);
3005 }
3006 } else {
3007 Apply(context_, eax);
3008 }
3009 break;
3010 }
3011 case KEYED_PROPERTY: {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003012 __ pop(ecx);
3013 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003014 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
3015 __ call(ic, RelocInfo::CODE_TARGET);
3016 // This nop signals to the IC that there is no inlined code at the call
3017 // site for it to patch.
3018 __ nop();
3019 if (expr->is_postfix()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003020 // Result is on the stack
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003021 if (context_ != Expression::kEffect) {
3022 ApplyTOS(context_);
3023 }
3024 } else {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003025 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003026 }
3027 break;
3028 }
3029 }
3030}
3031
3032
3033void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
3034 Comment cmnt(masm_, "[ BinaryOperation");
3035 switch (expr->op()) {
3036 case Token::COMMA:
3037 VisitForEffect(expr->left());
3038 Visit(expr->right());
3039 break;
3040
3041 case Token::OR:
3042 case Token::AND:
3043 EmitLogicalOperation(expr);
3044 break;
3045
3046 case Token::ADD:
3047 case Token::SUB:
3048 case Token::DIV:
3049 case Token::MOD:
3050 case Token::MUL:
3051 case Token::BIT_OR:
3052 case Token::BIT_AND:
3053 case Token::BIT_XOR:
3054 case Token::SHL:
3055 case Token::SHR:
3056 case Token::SAR:
3057 VisitForValue(expr->left(), kStack);
3058 VisitForValue(expr->right(), kAccumulator);
3059 EmitBinaryOp(expr->op(), context_);
3060 break;
3061
3062 default:
3063 UNREACHABLE();
3064 }
3065}
3066
3067
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003068void FullCodeGenerator::EmitNullCompare(bool strict,
3069 Register obj,
3070 Register null_const,
3071 Label* if_true,
3072 Label* if_false,
3073 Register scratch) {
3074 __ cmp(obj, Operand(null_const));
3075 if (strict) {
3076 __ j(equal, if_true);
3077 } else {
3078 __ j(equal, if_true);
3079 __ cmp(obj, Factory::undefined_value());
3080 __ j(equal, if_true);
3081 __ test(obj, Immediate(kSmiTagMask));
3082 __ j(zero, if_false);
3083 // It can be an undetectable object.
3084 __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset));
3085 __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
3086 __ test(scratch, Immediate(1 << Map::kIsUndetectable));
3087 __ j(not_zero, if_true);
3088 }
3089 __ jmp(if_false);
3090}
3091
3092
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003093void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
3094 Comment cmnt(masm_, "[ CompareOperation");
3095
3096 // Always perform the comparison for its control flow. Pack the result
3097 // into the expression's context after the comparison is performed.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003098
3099 Label materialize_true, materialize_false;
3100 Label* if_true = NULL;
3101 Label* if_false = NULL;
3102 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003103
3104 VisitForValue(expr->left(), kStack);
3105 switch (expr->op()) {
3106 case Token::IN:
3107 VisitForValue(expr->right(), kStack);
3108 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
3109 __ cmp(eax, Factory::true_value());
3110 __ j(equal, if_true);
3111 __ jmp(if_false);
3112 break;
3113
3114 case Token::INSTANCEOF: {
3115 VisitForValue(expr->right(), kStack);
3116 InstanceofStub stub;
3117 __ CallStub(&stub);
3118 __ test(eax, Operand(eax));
3119 __ j(zero, if_true); // The stub returns 0 for true.
3120 __ jmp(if_false);
3121 break;
3122 }
3123
3124 default: {
3125 VisitForValue(expr->right(), kAccumulator);
3126 Condition cc = no_condition;
3127 bool strict = false;
3128 switch (expr->op()) {
3129 case Token::EQ_STRICT:
3130 strict = true;
3131 // Fall through
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003132 case Token::EQ: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003133 cc = equal;
3134 __ pop(edx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003135 // If either operand is constant null we do a fast compare
3136 // against null.
3137 Literal* right_literal = expr->right()->AsLiteral();
3138 Literal* left_literal = expr->left()->AsLiteral();
3139 if (right_literal != NULL && right_literal->handle()->IsNull()) {
3140 EmitNullCompare(strict, edx, eax, if_true, if_false, ecx);
3141 Apply(context_, if_true, if_false);
3142 return;
3143 } else if (left_literal != NULL && left_literal->handle()->IsNull()) {
3144 EmitNullCompare(strict, eax, edx, if_true, if_false, ecx);
3145 Apply(context_, if_true, if_false);
3146 return;
3147 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003148 break;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003149 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003150 case Token::LT:
3151 cc = less;
3152 __ pop(edx);
3153 break;
3154 case Token::GT:
3155 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3156 cc = less;
3157 __ mov(edx, result_register());
3158 __ pop(eax);
3159 break;
3160 case Token::LTE:
3161 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3162 cc = greater_equal;
3163 __ mov(edx, result_register());
3164 __ pop(eax);
3165 break;
3166 case Token::GTE:
3167 cc = greater_equal;
3168 __ pop(edx);
3169 break;
3170 case Token::IN:
3171 case Token::INSTANCEOF:
3172 default:
3173 UNREACHABLE();
3174 }
3175
3176 // The comparison stub expects the smi vs. smi case to be handled
3177 // before it is called.
3178 Label slow_case;
3179 __ mov(ecx, Operand(edx));
3180 __ or_(ecx, Operand(eax));
3181 __ test(ecx, Immediate(kSmiTagMask));
3182 __ j(not_zero, &slow_case, not_taken);
3183 __ cmp(edx, Operand(eax));
3184 __ j(cc, if_true);
3185 __ jmp(if_false);
3186
3187 __ bind(&slow_case);
3188 CompareStub stub(cc, strict);
3189 __ CallStub(&stub);
3190 __ test(eax, Operand(eax));
3191 __ j(cc, if_true);
3192 __ jmp(if_false);
3193 }
3194 }
3195
3196 // Convert the result of the comparison into one expected for this
3197 // expression's context.
3198 Apply(context_, if_true, if_false);
3199}
3200
3201
3202void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
3203 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
3204 Apply(context_, eax);
3205}
3206
3207
3208Register FullCodeGenerator::result_register() { return eax; }
3209
3210
3211Register FullCodeGenerator::context_register() { return esi; }
3212
3213
3214void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
3215 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
3216 __ mov(Operand(ebp, frame_offset), value);
3217}
3218
3219
3220void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
3221 __ mov(dst, CodeGenerator::ContextOperand(esi, context_index));
3222}
3223
3224
3225// ----------------------------------------------------------------------------
3226// Non-local control flow support.
3227
3228void FullCodeGenerator::EnterFinallyBlock() {
3229 // Cook return address on top of stack (smi encoded Code* delta)
3230 ASSERT(!result_register().is(edx));
3231 __ mov(edx, Operand(esp, 0));
3232 __ sub(Operand(edx), Immediate(masm_->CodeObject()));
3233 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
3234 ASSERT_EQ(0, kSmiTag);
3235 __ add(edx, Operand(edx)); // Convert to smi.
3236 __ mov(Operand(esp, 0), edx);
3237 // Store result register while executing finally block.
3238 __ push(result_register());
3239}
3240
3241
3242void FullCodeGenerator::ExitFinallyBlock() {
3243 ASSERT(!result_register().is(edx));
3244 // Restore result register from stack.
3245 __ pop(result_register());
3246 // Uncook return address.
3247 __ mov(edx, Operand(esp, 0));
3248 __ sar(edx, 1); // Convert smi to int.
3249 __ add(Operand(edx), Immediate(masm_->CodeObject()));
3250 __ mov(Operand(esp, 0), edx);
3251 // And return.
3252 __ ret(0);
3253}
3254
3255
3256#undef __
3257
3258} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003259
3260#endif // V8_TARGET_ARCH_IA32