blob: eb944e6f78169025c17985802d37e3f09c611eb8 [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
whesse@chromium.orge90029b2010-08-02 11:52:17 +0000210 CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
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");
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001199 Label materialized;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001200 // Registers will be used as follows:
1201 // edi = JS function.
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001202 // ecx = literals array.
1203 // ebx = regexp literal.
1204 // eax = regexp literal clone.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001205 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001206 __ mov(ecx, FieldOperand(edi, JSFunction::kLiteralsOffset));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001207 int literal_offset =
1208 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001209 __ mov(ebx, FieldOperand(ecx, literal_offset));
1210 __ cmp(ebx, Factory::undefined_value());
1211 __ j(not_equal, &materialized);
1212
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001213 // Create regexp literal using runtime function
1214 // Result will be in eax.
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001215 __ push(ecx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001216 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1217 __ push(Immediate(expr->pattern()));
1218 __ push(Immediate(expr->flags()));
1219 __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00001220 __ mov(ebx, eax);
1221
1222 __ bind(&materialized);
1223 int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
1224 Label allocated, runtime_allocate;
1225 __ AllocateInNewSpace(size, eax, ecx, edx, &runtime_allocate, TAG_OBJECT);
1226 __ jmp(&allocated);
1227
1228 __ bind(&runtime_allocate);
1229 __ push(ebx);
1230 __ push(Immediate(Smi::FromInt(size)));
1231 __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
1232 __ pop(ebx);
1233
1234 __ bind(&allocated);
1235 // Copy the content into the newly allocated memory.
1236 // (Unroll copy loop once for better throughput).
1237 for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) {
1238 __ mov(edx, FieldOperand(ebx, i));
1239 __ mov(ecx, FieldOperand(ebx, i + kPointerSize));
1240 __ mov(FieldOperand(eax, i), edx);
1241 __ mov(FieldOperand(eax, i + kPointerSize), ecx);
1242 }
1243 if ((size % (2 * kPointerSize)) != 0) {
1244 __ mov(edx, FieldOperand(ebx, size - kPointerSize));
1245 __ mov(FieldOperand(eax, size - kPointerSize), edx);
1246 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001247 Apply(context_, eax);
1248}
1249
1250
1251void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
1252 Comment cmnt(masm_, "[ ObjectLiteral");
1253 __ mov(edi, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1254 __ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
1255 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1256 __ push(Immediate(expr->constant_properties()));
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001257 __ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001258 if (expr->depth() > 1) {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001259 __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001260 } else {
vegorov@chromium.orgf8372902010-03-15 10:26:20 +00001261 __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001262 }
1263
1264 // If result_saved is true the result is on top of the stack. If
1265 // result_saved is false the result is in eax.
1266 bool result_saved = false;
1267
1268 for (int i = 0; i < expr->properties()->length(); i++) {
1269 ObjectLiteral::Property* property = expr->properties()->at(i);
1270 if (property->IsCompileTimeValue()) continue;
1271
1272 Literal* key = property->key();
1273 Expression* value = property->value();
1274 if (!result_saved) {
1275 __ push(eax); // Save result on the stack
1276 result_saved = true;
1277 }
1278 switch (property->kind()) {
1279 case ObjectLiteral::Property::MATERIALIZED_LITERAL:
1280 ASSERT(!CompileTimeValue::IsCompileTimeValue(value));
1281 // Fall through.
1282 case ObjectLiteral::Property::COMPUTED:
1283 if (key->handle()->IsSymbol()) {
1284 VisitForValue(value, kAccumulator);
1285 __ mov(ecx, Immediate(key->handle()));
1286 __ mov(edx, Operand(esp, 0));
1287 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1288 __ call(ic, RelocInfo::CODE_TARGET);
1289 __ nop();
1290 break;
1291 }
1292 // Fall through.
1293 case ObjectLiteral::Property::PROTOTYPE:
1294 __ push(Operand(esp, 0)); // Duplicate receiver.
1295 VisitForValue(key, kStack);
1296 VisitForValue(value, kStack);
1297 __ CallRuntime(Runtime::kSetProperty, 3);
1298 break;
1299 case ObjectLiteral::Property::SETTER:
1300 case ObjectLiteral::Property::GETTER:
1301 __ push(Operand(esp, 0)); // Duplicate receiver.
1302 VisitForValue(key, kStack);
1303 __ push(Immediate(property->kind() == ObjectLiteral::Property::SETTER ?
1304 Smi::FromInt(1) :
1305 Smi::FromInt(0)));
1306 VisitForValue(value, kStack);
1307 __ CallRuntime(Runtime::kDefineAccessor, 4);
1308 break;
1309 default: UNREACHABLE();
1310 }
1311 }
1312
1313 if (result_saved) {
1314 ApplyTOS(context_);
1315 } else {
1316 Apply(context_, eax);
1317 }
1318}
1319
1320
1321void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
1322 Comment cmnt(masm_, "[ ArrayLiteral");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001323
1324 ZoneList<Expression*>* subexprs = expr->values();
1325 int length = subexprs->length();
1326
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001327 __ mov(ebx, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
1328 __ push(FieldOperand(ebx, JSFunction::kLiteralsOffset));
1329 __ push(Immediate(Smi::FromInt(expr->literal_index())));
1330 __ push(Immediate(expr->constant_elements()));
1331 if (expr->depth() > 1) {
1332 __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001333 } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001334 __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001335 } else {
1336 FastCloneShallowArrayStub stub(length);
1337 __ CallStub(&stub);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001338 }
1339
1340 bool result_saved = false; // Is the result saved to the stack?
1341
1342 // Emit code to evaluate all the non-constant subexpressions and to store
1343 // them into the newly cloned array.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001344 for (int i = 0; i < length; i++) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001345 Expression* subexpr = subexprs->at(i);
1346 // If the subexpression is a literal or a simple materialized literal it
1347 // is already set in the cloned array.
1348 if (subexpr->AsLiteral() != NULL ||
1349 CompileTimeValue::IsCompileTimeValue(subexpr)) {
1350 continue;
1351 }
1352
1353 if (!result_saved) {
1354 __ push(eax);
1355 result_saved = true;
1356 }
1357 VisitForValue(subexpr, kAccumulator);
1358
1359 // Store the subexpression value in the array's elements.
1360 __ mov(ebx, Operand(esp, 0)); // Copy of array literal.
1361 __ mov(ebx, FieldOperand(ebx, JSObject::kElementsOffset));
1362 int offset = FixedArray::kHeaderSize + (i * kPointerSize);
1363 __ mov(FieldOperand(ebx, offset), result_register());
1364
1365 // Update the write barrier for the array store.
1366 __ RecordWrite(ebx, offset, result_register(), ecx);
1367 }
1368
1369 if (result_saved) {
1370 ApplyTOS(context_);
1371 } else {
1372 Apply(context_, eax);
1373 }
1374}
1375
1376
ager@chromium.org5c838252010-02-19 08:53:10 +00001377void FullCodeGenerator::VisitAssignment(Assignment* expr) {
1378 Comment cmnt(masm_, "[ Assignment");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001379 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
1380 // on the left-hand side.
1381 if (!expr->target()->IsValidLeftHandSide()) {
1382 VisitForEffect(expr->target());
1383 return;
1384 }
1385
ager@chromium.org5c838252010-02-19 08:53:10 +00001386 // Left-hand side can only be a property, a global or a (parameter or local)
1387 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1388 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1389 LhsKind assign_type = VARIABLE;
1390 Property* prop = expr->target()->AsProperty();
1391 if (prop != NULL) {
1392 assign_type =
1393 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
1394 }
1395
1396 // Evaluate LHS expression.
1397 switch (assign_type) {
1398 case VARIABLE:
1399 // Nothing to do here.
1400 break;
1401 case NAMED_PROPERTY:
1402 if (expr->is_compound()) {
1403 // We need the receiver both on the stack and in the accumulator.
1404 VisitForValue(prop->obj(), kAccumulator);
1405 __ push(result_register());
1406 } else {
1407 VisitForValue(prop->obj(), kStack);
1408 }
1409 break;
1410 case KEYED_PROPERTY:
1411 if (expr->is_compound()) {
1412 VisitForValue(prop->obj(), kStack);
1413 VisitForValue(prop->key(), kAccumulator);
1414 __ mov(edx, Operand(esp, 0));
1415 __ push(eax);
1416 } else {
1417 VisitForValue(prop->obj(), kStack);
1418 VisitForValue(prop->key(), kStack);
1419 }
1420 break;
1421 }
1422
1423 // If we have a compound assignment: Get value of LHS expression and
1424 // store in on top of the stack.
1425 if (expr->is_compound()) {
1426 Location saved_location = location_;
1427 location_ = kStack;
1428 switch (assign_type) {
1429 case VARIABLE:
1430 EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
1431 Expression::kValue);
1432 break;
1433 case NAMED_PROPERTY:
1434 EmitNamedPropertyLoad(prop);
1435 __ push(result_register());
1436 break;
1437 case KEYED_PROPERTY:
1438 EmitKeyedPropertyLoad(prop);
1439 __ push(result_register());
1440 break;
1441 }
1442 location_ = saved_location;
1443 }
1444
1445 // Evaluate RHS expression.
1446 Expression* rhs = expr->value();
1447 VisitForValue(rhs, kAccumulator);
1448
1449 // If we have a compound assignment: Apply operator.
1450 if (expr->is_compound()) {
1451 Location saved_location = location_;
1452 location_ = kAccumulator;
1453 EmitBinaryOp(expr->binary_op(), Expression::kValue);
1454 location_ = saved_location;
1455 }
1456
1457 // Record source position before possible IC call.
1458 SetSourcePosition(expr->position());
1459
1460 // Store the value.
1461 switch (assign_type) {
1462 case VARIABLE:
1463 EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001464 expr->op(),
ager@chromium.org5c838252010-02-19 08:53:10 +00001465 context_);
1466 break;
1467 case NAMED_PROPERTY:
1468 EmitNamedPropertyAssignment(expr);
1469 break;
1470 case KEYED_PROPERTY:
1471 EmitKeyedPropertyAssignment(expr);
1472 break;
1473 }
1474}
1475
1476
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001477void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
1478 SetSourcePosition(prop->position());
1479 Literal* key = prop->key()->AsLiteral();
1480 __ mov(ecx, Immediate(key->handle()));
1481 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
1482 __ call(ic, RelocInfo::CODE_TARGET);
1483 __ nop();
1484}
1485
1486
1487void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
1488 SetSourcePosition(prop->position());
1489 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1490 __ call(ic, RelocInfo::CODE_TARGET);
1491 __ nop();
1492}
1493
1494
1495void FullCodeGenerator::EmitBinaryOp(Token::Value op,
1496 Expression::Context context) {
1497 __ push(result_register());
1498 GenericBinaryOpStub stub(op,
1499 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001500 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00001501 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001502 __ CallStub(&stub);
1503 Apply(context, eax);
1504}
1505
1506
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001507void FullCodeGenerator::EmitAssignment(Expression* expr) {
1508 // Invalid left-hand sides are rewritten to have a 'throw
1509 // ReferenceError' on the left-hand side.
1510 if (!expr->IsValidLeftHandSide()) {
1511 VisitForEffect(expr);
1512 return;
1513 }
1514
1515 // Left-hand side can only be a property, a global or a (parameter or local)
1516 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
1517 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
1518 LhsKind assign_type = VARIABLE;
1519 Property* prop = expr->AsProperty();
1520 if (prop != NULL) {
1521 assign_type = (prop->key()->IsPropertyName())
1522 ? NAMED_PROPERTY
1523 : KEYED_PROPERTY;
1524 }
1525
1526 switch (assign_type) {
1527 case VARIABLE: {
1528 Variable* var = expr->AsVariableProxy()->var();
1529 EmitVariableAssignment(var, Token::ASSIGN, Expression::kEffect);
1530 break;
1531 }
1532 case NAMED_PROPERTY: {
1533 __ push(eax); // Preserve value.
1534 VisitForValue(prop->obj(), kAccumulator);
1535 __ mov(edx, eax);
1536 __ pop(eax); // Restore value.
1537 __ mov(ecx, prop->key()->AsLiteral()->handle());
1538 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1539 __ call(ic, RelocInfo::CODE_TARGET);
1540 __ nop(); // Signal no inlined code.
1541 break;
1542 }
1543 case KEYED_PROPERTY: {
1544 __ push(eax); // Preserve value.
1545 VisitForValue(prop->obj(), kStack);
1546 VisitForValue(prop->key(), kAccumulator);
1547 __ mov(ecx, eax);
1548 __ pop(edx);
1549 __ pop(eax); // Restore value.
1550 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1551 __ call(ic, RelocInfo::CODE_TARGET);
1552 __ nop(); // Signal no inlined code.
1553 break;
1554 }
1555 }
1556}
1557
1558
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001559void FullCodeGenerator::EmitVariableAssignment(Variable* var,
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001560 Token::Value op,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001561 Expression::Context context) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001562 // Left-hand sides that rewrite to explicit property accesses do not reach
1563 // here.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001564 ASSERT(var != NULL);
1565 ASSERT(var->is_global() || var->slot() != NULL);
1566
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001567 if (var->is_global()) {
1568 ASSERT(!var->is_this());
1569 // Assignment to a global variable. Use inline caching for the
1570 // assignment. Right-hand-side value is passed in eax, variable name in
1571 // ecx, and the global object on the stack.
1572 __ mov(ecx, var->name());
1573 __ mov(edx, CodeGenerator::GlobalObject());
1574 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1575 __ call(ic, RelocInfo::CODE_TARGET);
1576 __ nop();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001577
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001578 } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) {
1579 // Perform the assignment for non-const variables and for initialization
1580 // of const variables. Const assignments are simply skipped.
1581 Label done;
1582 Slot* slot = var->slot();
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001583 switch (slot->type()) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001584 case Slot::PARAMETER:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001585 case Slot::LOCAL:
1586 if (op == Token::INIT_CONST) {
1587 // Detect const reinitialization by checking for the hole value.
1588 __ mov(edx, Operand(ebp, SlotOffset(slot)));
1589 __ cmp(edx, Factory::the_hole_value());
1590 __ j(not_equal, &done);
1591 }
1592 // Perform the assignment.
1593 __ mov(Operand(ebp, SlotOffset(slot)), eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001594 break;
1595
1596 case Slot::CONTEXT: {
1597 MemOperand target = EmitSlotSearch(slot, ecx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001598 if (op == Token::INIT_CONST) {
1599 // Detect const reinitialization by checking for the hole value.
1600 __ mov(edx, target);
1601 __ cmp(edx, Factory::the_hole_value());
1602 __ j(not_equal, &done);
1603 }
1604 // Perform the assignment and issue the write barrier.
1605 __ mov(target, eax);
1606 // The value of the assignment is in eax. RecordWrite clobbers its
1607 // register arguments.
1608 __ mov(edx, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001609 int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize;
1610 __ RecordWrite(ecx, offset, edx, ebx);
1611 break;
1612 }
1613
1614 case Slot::LOOKUP:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001615 // Call the runtime for the assignment. The runtime will ignore
1616 // const reinitialization.
1617 __ push(eax); // Value.
1618 __ push(esi); // Context.
1619 __ push(Immediate(var->name()));
1620 if (op == Token::INIT_CONST) {
1621 // The runtime will ignore const redeclaration.
1622 __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
1623 } else {
1624 __ CallRuntime(Runtime::kStoreContextSlot, 3);
1625 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001626 break;
1627 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001628 __ bind(&done);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001629 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001630
1631 Apply(context, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001632}
1633
1634
1635void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
1636 // Assignment to a property, using a named store IC.
1637 Property* prop = expr->target()->AsProperty();
1638 ASSERT(prop != NULL);
1639 ASSERT(prop->key()->AsLiteral() != NULL);
1640
1641 // If the assignment starts a block of assignments to the same object,
1642 // change to slow case to avoid the quadratic behavior of repeatedly
1643 // adding fast properties.
1644 if (expr->starts_initialization_block()) {
1645 __ push(result_register());
1646 __ push(Operand(esp, kPointerSize)); // Receiver is now under value.
1647 __ CallRuntime(Runtime::kToSlowProperties, 1);
1648 __ pop(result_register());
1649 }
1650
1651 // Record source code position before IC call.
1652 SetSourcePosition(expr->position());
1653 __ mov(ecx, prop->key()->AsLiteral()->handle());
1654 if (expr->ends_initialization_block()) {
1655 __ mov(edx, Operand(esp, 0));
1656 } else {
1657 __ pop(edx);
1658 }
1659 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1660 __ call(ic, RelocInfo::CODE_TARGET);
1661 __ nop();
1662
1663 // If the assignment ends an initialization block, revert to fast case.
1664 if (expr->ends_initialization_block()) {
1665 __ push(eax); // Result of assignment, saved even if not needed.
1666 __ push(Operand(esp, kPointerSize)); // Receiver is under value.
1667 __ CallRuntime(Runtime::kToFastProperties, 1);
1668 __ pop(eax);
1669 DropAndApply(1, context_, eax);
1670 } else {
1671 Apply(context_, eax);
1672 }
1673}
1674
1675
1676void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
1677 // Assignment to a property, using a keyed store IC.
1678
1679 // If the assignment starts a block of assignments to the same object,
1680 // change to slow case to avoid the quadratic behavior of repeatedly
1681 // adding fast properties.
1682 if (expr->starts_initialization_block()) {
1683 __ push(result_register());
1684 // Receiver is now under the key and value.
1685 __ push(Operand(esp, 2 * kPointerSize));
1686 __ CallRuntime(Runtime::kToSlowProperties, 1);
1687 __ pop(result_register());
1688 }
1689
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001690 __ pop(ecx);
1691 if (expr->ends_initialization_block()) {
1692 __ mov(edx, Operand(esp, 0)); // Leave receiver on the stack for later.
1693 } else {
1694 __ pop(edx);
1695 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001696 // Record source code position before IC call.
1697 SetSourcePosition(expr->position());
1698 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
1699 __ call(ic, RelocInfo::CODE_TARGET);
1700 // This nop signals to the IC that there is no inlined code at the call
1701 // site for it to patch.
1702 __ nop();
1703
1704 // If the assignment ends an initialization block, revert to fast case.
1705 if (expr->ends_initialization_block()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001706 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001707 __ push(eax); // Result of assignment, saved even if not needed.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001708 __ push(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001709 __ CallRuntime(Runtime::kToFastProperties, 1);
1710 __ pop(eax);
1711 }
1712
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001713 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001714}
1715
1716
1717void FullCodeGenerator::VisitProperty(Property* expr) {
1718 Comment cmnt(masm_, "[ Property");
1719 Expression* key = expr->key();
1720
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001721 if (key->IsPropertyName()) {
ager@chromium.org5c838252010-02-19 08:53:10 +00001722 VisitForValue(expr->obj(), kAccumulator);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001723 EmitNamedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001724 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001725 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00001726 VisitForValue(expr->obj(), kStack);
1727 VisitForValue(expr->key(), kAccumulator);
1728 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001729 EmitKeyedPropertyLoad(expr);
ager@chromium.org5c838252010-02-19 08:53:10 +00001730 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001731 }
1732}
1733
1734
1735void FullCodeGenerator::EmitCallWithIC(Call* expr,
1736 Handle<Object> name,
1737 RelocInfo::Mode mode) {
1738 // Code common for calls using the IC.
1739 ZoneList<Expression*>* args = expr->arguments();
1740 int arg_count = args->length();
1741 for (int i = 0; i < arg_count; i++) {
1742 VisitForValue(args->at(i), kStack);
1743 }
1744 __ Set(ecx, Immediate(name));
1745 // Record source position of the IC call.
1746 SetSourcePosition(expr->position());
1747 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1748 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
1749 __ call(ic, mode);
1750 // Restore context register.
1751 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1752 Apply(context_, eax);
1753}
1754
1755
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001756void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
1757 Expression* key,
1758 RelocInfo::Mode mode) {
1759 // Code common for calls using the IC.
1760 ZoneList<Expression*>* args = expr->arguments();
1761 int arg_count = args->length();
1762 for (int i = 0; i < arg_count; i++) {
1763 VisitForValue(args->at(i), kStack);
1764 }
1765 VisitForValue(key, kAccumulator);
1766 __ mov(ecx, eax);
1767 // Record source position of the IC call.
1768 SetSourcePosition(expr->position());
1769 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1770 Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
1771 arg_count, in_loop);
1772 __ call(ic, mode);
1773 // Restore context register.
1774 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1775 Apply(context_, eax);
1776}
1777
1778
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001779void FullCodeGenerator::EmitCallWithStub(Call* expr) {
1780 // Code common for calls using the call stub.
1781 ZoneList<Expression*>* args = expr->arguments();
1782 int arg_count = args->length();
1783 for (int i = 0; i < arg_count; i++) {
1784 VisitForValue(args->at(i), kStack);
1785 }
1786 // Record source position for debugger.
1787 SetSourcePosition(expr->position());
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001788 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1789 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001790 __ CallStub(&stub);
1791 // Restore context register.
1792 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1793 DropAndApply(1, context_, eax);
1794}
1795
1796
1797void FullCodeGenerator::VisitCall(Call* expr) {
1798 Comment cmnt(masm_, "[ Call");
1799 Expression* fun = expr->expression();
1800 Variable* var = fun->AsVariableProxy()->AsVariable();
1801
1802 if (var != NULL && var->is_possibly_eval()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001803 // In a call to eval, we first call %ResolvePossiblyDirectEval to
1804 // resolve the function we need to call and the receiver of the
1805 // call. Then we call the resolved function using the given
1806 // arguments.
1807 VisitForValue(fun, kStack);
1808 __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
1809
1810 // Push the arguments.
1811 ZoneList<Expression*>* args = expr->arguments();
1812 int arg_count = args->length();
1813 for (int i = 0; i < arg_count; i++) {
1814 VisitForValue(args->at(i), kStack);
1815 }
1816
1817 // Push copy of the function - found below the arguments.
1818 __ push(Operand(esp, (arg_count + 1) * kPointerSize));
1819
1820 // Push copy of the first argument or undefined if it doesn't exist.
1821 if (arg_count > 0) {
1822 __ push(Operand(esp, arg_count * kPointerSize));
1823 } else {
1824 __ push(Immediate(Factory::undefined_value()));
1825 }
1826
1827 // Push the receiver of the enclosing function and do runtime call.
1828 __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
1829 __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
1830
1831 // The runtime call returns a pair of values in eax (function) and
1832 // edx (receiver). Touch up the stack with the right values.
1833 __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
1834 __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
1835
1836 // Record source position for debugger.
1837 SetSourcePosition(expr->position());
1838 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
1839 CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
1840 __ CallStub(&stub);
1841 // Restore context register.
1842 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
1843 DropAndApply(1, context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001844 } else if (var != NULL && !var->is_this() && var->is_global()) {
1845 // Push global object as receiver for the call IC.
1846 __ push(CodeGenerator::GlobalObject());
1847 EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
1848 } else if (var != NULL && var->slot() != NULL &&
1849 var->slot()->type() == Slot::LOOKUP) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001850 // Call to a lookup slot (dynamically introduced variable). Call the
1851 // runtime to find the function to call (returned in eax) and the object
1852 // holding it (returned in edx).
1853 __ push(context_register());
1854 __ push(Immediate(var->name()));
1855 __ CallRuntime(Runtime::kLoadContextSlot, 2);
1856 __ push(eax); // Function.
1857 __ push(edx); // Receiver.
1858 EmitCallWithStub(expr);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001859 } else if (fun->AsProperty() != NULL) {
1860 // Call to an object property.
1861 Property* prop = fun->AsProperty();
1862 Literal* key = prop->key()->AsLiteral();
1863 if (key != NULL && key->handle()->IsSymbol()) {
1864 // Call to a named property, use call IC.
1865 VisitForValue(prop->obj(), kStack);
1866 EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
1867 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001868 // Call to a keyed property.
1869 // For a synthetic property use keyed load IC followed by function call,
1870 // for a regular property use keyed CallIC.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001871 VisitForValue(prop->obj(), kStack);
ager@chromium.org5c838252010-02-19 08:53:10 +00001872 if (prop->is_synthetic()) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001873 VisitForValue(prop->key(), kAccumulator);
1874 // Record source code position for IC call.
1875 SetSourcePosition(prop->position());
ager@chromium.org5c838252010-02-19 08:53:10 +00001876 __ pop(edx); // We do not need to keep the receiver.
ager@chromium.org5c838252010-02-19 08:53:10 +00001877
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001878 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
1879 __ call(ic, RelocInfo::CODE_TARGET);
1880 // By emitting a nop we make sure that we do not have a "test eax,..."
1881 // instruction after the call as it is treated specially
1882 // by the LoadIC code.
1883 __ nop();
ager@chromium.org5c838252010-02-19 08:53:10 +00001884 // Push result (function).
1885 __ push(eax);
1886 // Push Global receiver.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001887 __ mov(ecx, CodeGenerator::GlobalObject());
1888 __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001889 EmitCallWithStub(expr);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001890 } else {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001891 EmitKeyedCallWithIC(expr, prop->key(), RelocInfo::CODE_TARGET);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001892 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00001893 }
1894 } else {
1895 // Call to some other expression. If the expression is an anonymous
1896 // function literal not called in a loop, mark it as one that should
1897 // also use the full code generator.
1898 FunctionLiteral* lit = fun->AsFunctionLiteral();
1899 if (lit != NULL &&
1900 lit->name()->Equals(Heap::empty_string()) &&
1901 loop_depth() == 0) {
1902 lit->set_try_full_codegen(true);
1903 }
1904 VisitForValue(fun, kStack);
1905 // Load global receiver object.
1906 __ mov(ebx, CodeGenerator::GlobalObject());
1907 __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
1908 // Emit function call.
1909 EmitCallWithStub(expr);
1910 }
1911}
1912
1913
1914void FullCodeGenerator::VisitCallNew(CallNew* expr) {
1915 Comment cmnt(masm_, "[ CallNew");
1916 // According to ECMA-262, section 11.2.2, page 44, the function
1917 // expression in new calls must be evaluated before the
1918 // arguments.
1919 // Push function on the stack.
1920 VisitForValue(expr->expression(), kStack);
1921
1922 // Push global object (receiver).
1923 __ push(CodeGenerator::GlobalObject());
1924
1925 // Push the arguments ("left-to-right") on the stack.
1926 ZoneList<Expression*>* args = expr->arguments();
1927 int arg_count = args->length();
1928 for (int i = 0; i < arg_count; i++) {
1929 VisitForValue(args->at(i), kStack);
1930 }
1931
1932 // Call the construct call builtin that handles allocation and
1933 // constructor invocation.
1934 SetSourcePosition(expr->position());
1935
1936 // Load function, arg_count into edi and eax.
1937 __ Set(eax, Immediate(arg_count));
1938 // Function is in esp[arg_count + 1].
1939 __ mov(edi, Operand(esp, eax, times_pointer_size, kPointerSize));
1940
1941 Handle<Code> construct_builtin(Builtins::builtin(Builtins::JSConstructCall));
1942 __ call(construct_builtin, RelocInfo::CONSTRUCT_CALL);
1943
1944 // Replace function on TOS with result in eax, or pop it.
1945 DropAndApply(1, context_, eax);
1946}
1947
1948
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00001949void FullCodeGenerator::EmitIsSmi(ZoneList<Expression*>* args) {
1950 ASSERT(args->length() == 1);
1951
1952 VisitForValue(args->at(0), kAccumulator);
1953
1954 Label materialize_true, materialize_false;
1955 Label* if_true = NULL;
1956 Label* if_false = NULL;
1957 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1958
1959 __ test(eax, Immediate(kSmiTagMask));
1960 __ j(zero, if_true);
1961 __ jmp(if_false);
1962
1963 Apply(context_, if_true, if_false);
1964}
1965
1966
1967void FullCodeGenerator::EmitIsNonNegativeSmi(ZoneList<Expression*>* args) {
1968 ASSERT(args->length() == 1);
1969
1970 VisitForValue(args->at(0), kAccumulator);
1971
1972 Label materialize_true, materialize_false;
1973 Label* if_true = NULL;
1974 Label* if_false = NULL;
1975 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1976
1977 __ test(eax, Immediate(kSmiTagMask | 0x80000000));
1978 __ j(zero, if_true);
1979 __ jmp(if_false);
1980
1981 Apply(context_, if_true, if_false);
1982}
1983
1984
1985void FullCodeGenerator::EmitIsObject(ZoneList<Expression*>* args) {
1986 ASSERT(args->length() == 1);
1987
1988 VisitForValue(args->at(0), kAccumulator);
1989
1990 Label materialize_true, materialize_false;
1991 Label* if_true = NULL;
1992 Label* if_false = NULL;
1993 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
1994
1995 __ test(eax, Immediate(kSmiTagMask));
1996 __ j(zero, if_false);
1997 __ cmp(eax, Factory::null_value());
1998 __ j(equal, if_true);
1999 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2000 // Undetectable objects behave like undefined when tested with typeof.
2001 __ movzx_b(ecx, FieldOperand(ebx, Map::kBitFieldOffset));
2002 __ test(ecx, Immediate(1 << Map::kIsUndetectable));
2003 __ j(not_zero, if_false);
2004 __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceTypeOffset));
2005 __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
2006 __ j(below, if_false);
2007 __ cmp(ecx, LAST_JS_OBJECT_TYPE);
2008 __ j(below_equal, if_true);
2009 __ jmp(if_false);
2010
2011 Apply(context_, if_true, if_false);
2012}
2013
2014
ricow@chromium.org4980dff2010-07-19 08:33:45 +00002015void FullCodeGenerator::EmitIsSpecObject(ZoneList<Expression*>* args) {
2016 ASSERT(args->length() == 1);
2017
2018 VisitForValue(args->at(0), kAccumulator);
2019
2020 Label materialize_true, materialize_false;
2021 Label* if_true = NULL;
2022 Label* if_false = NULL;
2023 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2024
2025 __ test(eax, Immediate(kSmiTagMask));
2026 __ j(equal, if_false);
2027 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ebx);
2028 __ j(above_equal, if_true);
2029 __ jmp(if_false);
2030
2031 Apply(context_, if_true, if_false);
2032}
2033
2034
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002035void FullCodeGenerator::EmitIsUndetectableObject(ZoneList<Expression*>* args) {
2036 ASSERT(args->length() == 1);
2037
2038 VisitForValue(args->at(0), kAccumulator);
2039
2040 Label materialize_true, materialize_false;
2041 Label* if_true = NULL;
2042 Label* if_false = NULL;
2043 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2044
2045 __ test(eax, Immediate(kSmiTagMask));
2046 __ j(zero, if_false);
2047 __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
2048 __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
2049 __ test(ebx, Immediate(1 << Map::kIsUndetectable));
2050 __ j(not_zero, if_true);
2051 __ jmp(if_false);
2052
2053 Apply(context_, if_true, if_false);
2054}
2055
2056
2057void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) {
2058 ASSERT(args->length() == 1);
2059
2060 VisitForValue(args->at(0), kAccumulator);
2061
2062 Label materialize_true, materialize_false;
2063 Label* if_true = NULL;
2064 Label* if_false = NULL;
2065 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2066
2067 __ test(eax, Immediate(kSmiTagMask));
2068 __ j(zero, if_false);
2069 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2070 __ j(equal, if_true);
2071 __ jmp(if_false);
2072
2073 Apply(context_, if_true, if_false);
2074}
2075
2076
2077void FullCodeGenerator::EmitIsArray(ZoneList<Expression*>* args) {
2078 ASSERT(args->length() == 1);
2079
2080 VisitForValue(args->at(0), kAccumulator);
2081
2082 Label materialize_true, materialize_false;
2083 Label* if_true = NULL;
2084 Label* if_false = NULL;
2085 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2086
2087 __ test(eax, Immediate(kSmiTagMask));
2088 __ j(equal, if_false);
2089 __ CmpObjectType(eax, JS_ARRAY_TYPE, ebx);
2090 __ j(equal, if_true);
2091 __ jmp(if_false);
2092
2093 Apply(context_, if_true, if_false);
2094}
2095
2096
2097void FullCodeGenerator::EmitIsRegExp(ZoneList<Expression*>* args) {
2098 ASSERT(args->length() == 1);
2099
2100 VisitForValue(args->at(0), kAccumulator);
2101
2102 Label materialize_true, materialize_false;
2103 Label* if_true = NULL;
2104 Label* if_false = NULL;
2105 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2106
2107 __ test(eax, Immediate(kSmiTagMask));
2108 __ j(equal, if_false);
2109 __ CmpObjectType(eax, JS_REGEXP_TYPE, ebx);
2110 __ j(equal, if_true);
2111 __ jmp(if_false);
2112
2113 Apply(context_, if_true, if_false);
2114}
2115
2116
2117
2118void FullCodeGenerator::EmitIsConstructCall(ZoneList<Expression*>* args) {
2119 ASSERT(args->length() == 0);
2120
2121 Label materialize_true, materialize_false;
2122 Label* if_true = NULL;
2123 Label* if_false = NULL;
2124 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2125
2126 // Get the frame pointer for the calling frame.
2127 __ mov(eax, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2128
2129 // Skip the arguments adaptor frame if it exists.
2130 Label check_frame_marker;
2131 __ cmp(Operand(eax, StandardFrameConstants::kContextOffset),
2132 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2133 __ j(not_equal, &check_frame_marker);
2134 __ mov(eax, Operand(eax, StandardFrameConstants::kCallerFPOffset));
2135
2136 // Check the marker in the calling frame.
2137 __ bind(&check_frame_marker);
2138 __ cmp(Operand(eax, StandardFrameConstants::kMarkerOffset),
2139 Immediate(Smi::FromInt(StackFrame::CONSTRUCT)));
2140 __ j(equal, if_true);
2141 __ jmp(if_false);
2142
2143 Apply(context_, if_true, if_false);
2144}
2145
2146
2147void FullCodeGenerator::EmitObjectEquals(ZoneList<Expression*>* args) {
2148 ASSERT(args->length() == 2);
2149
2150 // Load the two objects into registers and perform the comparison.
2151 VisitForValue(args->at(0), kStack);
2152 VisitForValue(args->at(1), kAccumulator);
2153
2154 Label materialize_true, materialize_false;
2155 Label* if_true = NULL;
2156 Label* if_false = NULL;
2157 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
2158
2159 __ pop(ebx);
2160 __ cmp(eax, Operand(ebx));
2161 __ j(equal, if_true);
2162 __ jmp(if_false);
2163
2164 Apply(context_, if_true, if_false);
2165}
2166
2167
2168void FullCodeGenerator::EmitArguments(ZoneList<Expression*>* args) {
2169 ASSERT(args->length() == 1);
2170
2171 // ArgumentsAccessStub expects the key in edx and the formal
2172 // parameter count in eax.
2173 VisitForValue(args->at(0), kAccumulator);
2174 __ mov(edx, eax);
2175 __ mov(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2176 ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
2177 __ CallStub(&stub);
2178 Apply(context_, eax);
2179}
2180
2181
2182void FullCodeGenerator::EmitArgumentsLength(ZoneList<Expression*>* args) {
2183 ASSERT(args->length() == 0);
2184
2185 Label exit;
2186 // Get the number of formal parameters.
2187 __ Set(eax, Immediate(Smi::FromInt(scope()->num_parameters())));
2188
2189 // Check if the calling frame is an arguments adaptor frame.
2190 __ mov(ebx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
2191 __ cmp(Operand(ebx, StandardFrameConstants::kContextOffset),
2192 Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
2193 __ j(not_equal, &exit);
2194
2195 // Arguments adaptor case: Read the arguments length from the
2196 // adaptor frame.
2197 __ mov(eax, Operand(ebx, ArgumentsAdaptorFrameConstants::kLengthOffset));
2198
2199 __ bind(&exit);
2200 if (FLAG_debug_code) __ AbortIfNotSmi(eax);
2201 Apply(context_, eax);
2202}
2203
2204
2205void FullCodeGenerator::EmitClassOf(ZoneList<Expression*>* args) {
2206 ASSERT(args->length() == 1);
2207 Label done, null, function, non_function_constructor;
2208
2209 VisitForValue(args->at(0), kAccumulator);
2210
2211 // If the object is a smi, we return null.
2212 __ test(eax, Immediate(kSmiTagMask));
2213 __ j(zero, &null);
2214
2215 // Check that the object is a JS object but take special care of JS
2216 // functions to make sure they have 'Function' as their class.
kmillikin@chromium.org9155e252010-05-26 13:27:57 +00002217 __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, eax); // Map is now in eax.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002218 __ j(below, &null);
2219
2220 // As long as JS_FUNCTION_TYPE is the last instance type and it is
2221 // right after LAST_JS_OBJECT_TYPE, we can avoid checking for
2222 // LAST_JS_OBJECT_TYPE.
2223 ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
2224 ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00002225 __ CmpInstanceType(eax, JS_FUNCTION_TYPE);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002226 __ j(equal, &function);
2227
2228 // Check if the constructor in the map is a function.
2229 __ mov(eax, FieldOperand(eax, Map::kConstructorOffset));
2230 __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx);
2231 __ j(not_equal, &non_function_constructor);
2232
2233 // eax now contains the constructor function. Grab the
2234 // instance class name from there.
2235 __ mov(eax, FieldOperand(eax, JSFunction::kSharedFunctionInfoOffset));
2236 __ mov(eax, FieldOperand(eax, SharedFunctionInfo::kInstanceClassNameOffset));
2237 __ jmp(&done);
2238
2239 // Functions have class 'Function'.
2240 __ bind(&function);
2241 __ mov(eax, Factory::function_class_symbol());
2242 __ jmp(&done);
2243
2244 // Objects with a non-function constructor have class 'Object'.
2245 __ bind(&non_function_constructor);
2246 __ mov(eax, Factory::Object_symbol());
2247 __ jmp(&done);
2248
2249 // Non-JS objects have class null.
2250 __ bind(&null);
2251 __ mov(eax, Factory::null_value());
2252
2253 // All done.
2254 __ bind(&done);
2255
2256 Apply(context_, eax);
2257}
2258
2259
2260void FullCodeGenerator::EmitLog(ZoneList<Expression*>* args) {
2261 // Conditionally generate a log call.
2262 // Args:
2263 // 0 (literal string): The type of logging (corresponds to the flags).
2264 // This is used to determine whether or not to generate the log call.
2265 // 1 (string): Format string. Access the string at argument index 2
2266 // with '%2s' (see Logger::LogRuntime for all the formats).
2267 // 2 (array): Arguments to the format string.
2268 ASSERT_EQ(args->length(), 3);
2269#ifdef ENABLE_LOGGING_AND_PROFILING
2270 if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
2271 VisitForValue(args->at(1), kStack);
2272 VisitForValue(args->at(2), kStack);
2273 __ CallRuntime(Runtime::kLog, 2);
2274 }
2275#endif
2276 // Finally, we're expected to leave a value on the top of the stack.
2277 __ mov(eax, Factory::undefined_value());
2278 Apply(context_, eax);
2279}
2280
2281
2282void FullCodeGenerator::EmitRandomHeapNumber(ZoneList<Expression*>* args) {
2283 ASSERT(args->length() == 0);
2284
2285 Label slow_allocate_heapnumber;
2286 Label heapnumber_allocated;
2287
2288 __ AllocateHeapNumber(edi, ebx, ecx, &slow_allocate_heapnumber);
2289 __ jmp(&heapnumber_allocated);
2290
2291 __ bind(&slow_allocate_heapnumber);
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00002292 // Allocate a heap number.
2293 __ CallRuntime(Runtime::kNumberAlloc, 0);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002294 __ mov(edi, eax);
2295
2296 __ bind(&heapnumber_allocated);
2297
2298 __ PrepareCallCFunction(0, ebx);
2299 __ CallCFunction(ExternalReference::random_uint32_function(), 0);
2300
2301 // Convert 32 random bits in eax to 0.(32 random bits) in a double
2302 // by computing:
2303 // ( 1.(20 0s)(32 random bits) x 2^20 ) - (1.0 x 2^20)).
2304 // This is implemented on both SSE2 and FPU.
2305 if (CpuFeatures::IsSupported(SSE2)) {
2306 CpuFeatures::Scope fscope(SSE2);
2307 __ mov(ebx, Immediate(0x49800000)); // 1.0 x 2^20 as single.
2308 __ movd(xmm1, Operand(ebx));
2309 __ movd(xmm0, Operand(eax));
2310 __ cvtss2sd(xmm1, xmm1);
2311 __ pxor(xmm0, xmm1);
2312 __ subsd(xmm0, xmm1);
2313 __ movdbl(FieldOperand(edi, HeapNumber::kValueOffset), xmm0);
2314 } else {
2315 // 0x4130000000000000 is 1.0 x 2^20 as a double.
2316 __ mov(FieldOperand(edi, HeapNumber::kExponentOffset),
2317 Immediate(0x41300000));
2318 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), eax);
2319 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2320 __ mov(FieldOperand(edi, HeapNumber::kMantissaOffset), Immediate(0));
2321 __ fld_d(FieldOperand(edi, HeapNumber::kValueOffset));
2322 __ fsubp(1);
2323 __ fstp_d(FieldOperand(edi, HeapNumber::kValueOffset));
2324 }
2325 __ mov(eax, edi);
2326 Apply(context_, eax);
2327}
2328
2329
2330void FullCodeGenerator::EmitSubString(ZoneList<Expression*>* args) {
2331 // Load the arguments on the stack and call the stub.
2332 SubStringStub stub;
2333 ASSERT(args->length() == 3);
2334 VisitForValue(args->at(0), kStack);
2335 VisitForValue(args->at(1), kStack);
2336 VisitForValue(args->at(2), kStack);
2337 __ CallStub(&stub);
2338 Apply(context_, eax);
2339}
2340
2341
2342void FullCodeGenerator::EmitRegExpExec(ZoneList<Expression*>* args) {
2343 // Load the arguments on the stack and call the stub.
2344 RegExpExecStub stub;
2345 ASSERT(args->length() == 4);
2346 VisitForValue(args->at(0), kStack);
2347 VisitForValue(args->at(1), kStack);
2348 VisitForValue(args->at(2), kStack);
2349 VisitForValue(args->at(3), kStack);
2350 __ CallStub(&stub);
2351 Apply(context_, eax);
2352}
2353
2354
2355void FullCodeGenerator::EmitValueOf(ZoneList<Expression*>* args) {
2356 ASSERT(args->length() == 1);
2357
2358 VisitForValue(args->at(0), kAccumulator); // Load the object.
2359
2360 Label done;
2361 // If the object is a smi return the object.
2362 __ test(eax, Immediate(kSmiTagMask));
2363 __ j(zero, &done);
2364 // If the object is not a value type, return the object.
2365 __ CmpObjectType(eax, JS_VALUE_TYPE, ebx);
2366 __ j(not_equal, &done);
2367 __ mov(eax, FieldOperand(eax, JSValue::kValueOffset));
2368
2369 __ bind(&done);
2370 Apply(context_, eax);
2371}
2372
2373
2374void FullCodeGenerator::EmitMathPow(ZoneList<Expression*>* args) {
2375 // Load the arguments on the stack and call the runtime function.
2376 ASSERT(args->length() == 2);
2377 VisitForValue(args->at(0), kStack);
2378 VisitForValue(args->at(1), kStack);
2379 __ CallRuntime(Runtime::kMath_pow, 2);
2380 Apply(context_, eax);
2381}
2382
2383
2384void FullCodeGenerator::EmitSetValueOf(ZoneList<Expression*>* args) {
2385 ASSERT(args->length() == 2);
2386
2387 VisitForValue(args->at(0), kStack); // Load the object.
2388 VisitForValue(args->at(1), kAccumulator); // Load the value.
2389 __ pop(ebx); // eax = value. ebx = object.
2390
2391 Label done;
2392 // If the object is a smi, return the value.
2393 __ test(ebx, Immediate(kSmiTagMask));
2394 __ j(zero, &done);
2395
2396 // If the object is not a value type, return the value.
2397 __ CmpObjectType(ebx, JS_VALUE_TYPE, ecx);
2398 __ j(not_equal, &done);
2399
2400 // Store the value.
2401 __ mov(FieldOperand(ebx, JSValue::kValueOffset), eax);
2402 // Update the write barrier. Save the value as it will be
2403 // overwritten by the write barrier code and is needed afterward.
2404 __ mov(edx, eax);
2405 __ RecordWrite(ebx, JSValue::kValueOffset, edx, ecx);
2406
2407 __ bind(&done);
2408 Apply(context_, eax);
2409}
2410
2411
2412void FullCodeGenerator::EmitNumberToString(ZoneList<Expression*>* args) {
2413 ASSERT_EQ(args->length(), 1);
2414
2415 // Load the argument on the stack and call the stub.
2416 VisitForValue(args->at(0), kStack);
2417
2418 NumberToStringStub stub;
2419 __ CallStub(&stub);
2420 Apply(context_, eax);
2421}
2422
2423
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002424void FullCodeGenerator::EmitStringCharFromCode(ZoneList<Expression*>* args) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002425 ASSERT(args->length() == 1);
2426
2427 VisitForValue(args->at(0), kAccumulator);
2428
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002429 Label done;
2430 StringCharFromCodeGenerator generator(eax, ebx);
2431 generator.GenerateFast(masm_);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002432 __ jmp(&done);
2433
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002434 NopRuntimeCallHelper call_helper;
2435 generator.GenerateSlow(masm_, call_helper);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002436
2437 __ bind(&done);
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002438 Apply(context_, ebx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002439}
2440
2441
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002442void FullCodeGenerator::EmitStringCharCodeAt(ZoneList<Expression*>* args) {
2443 ASSERT(args->length() == 2);
2444
2445 VisitForValue(args->at(0), kStack);
2446 VisitForValue(args->at(1), kAccumulator);
2447
2448 Register object = ebx;
2449 Register index = eax;
2450 Register scratch = ecx;
2451 Register result = edx;
2452
2453 __ pop(object);
2454
2455 Label need_conversion;
2456 Label index_out_of_range;
2457 Label done;
2458 StringCharCodeAtGenerator generator(object,
2459 index,
2460 scratch,
2461 result,
2462 &need_conversion,
2463 &need_conversion,
2464 &index_out_of_range,
2465 STRING_INDEX_IS_NUMBER);
2466 generator.GenerateFast(masm_);
2467 __ jmp(&done);
2468
2469 __ bind(&index_out_of_range);
2470 // When the index is out of range, the spec requires us to return
2471 // NaN.
2472 __ Set(result, Immediate(Factory::nan_value()));
2473 __ jmp(&done);
2474
2475 __ bind(&need_conversion);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002476 // Move the undefined value into the result register, which will
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002477 // trigger conversion.
2478 __ Set(result, Immediate(Factory::undefined_value()));
2479 __ jmp(&done);
2480
2481 NopRuntimeCallHelper call_helper;
2482 generator.GenerateSlow(masm_, call_helper);
2483
2484 __ bind(&done);
2485 Apply(context_, result);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002486}
2487
ricow@chromium.org30ce4112010-05-31 10:38:25 +00002488
2489void FullCodeGenerator::EmitStringCharAt(ZoneList<Expression*>* args) {
2490 ASSERT(args->length() == 2);
2491
2492 VisitForValue(args->at(0), kStack);
2493 VisitForValue(args->at(1), kAccumulator);
2494
2495 Register object = ebx;
2496 Register index = eax;
2497 Register scratch1 = ecx;
2498 Register scratch2 = edx;
2499 Register result = eax;
2500
2501 __ pop(object);
2502
2503 Label need_conversion;
2504 Label index_out_of_range;
2505 Label done;
2506 StringCharAtGenerator generator(object,
2507 index,
2508 scratch1,
2509 scratch2,
2510 result,
2511 &need_conversion,
2512 &need_conversion,
2513 &index_out_of_range,
2514 STRING_INDEX_IS_NUMBER);
2515 generator.GenerateFast(masm_);
2516 __ jmp(&done);
2517
2518 __ bind(&index_out_of_range);
2519 // When the index is out of range, the spec requires us to return
2520 // the empty string.
2521 __ Set(result, Immediate(Factory::empty_string()));
2522 __ jmp(&done);
2523
2524 __ bind(&need_conversion);
2525 // Move smi zero into the result register, which will trigger
2526 // conversion.
2527 __ Set(result, Immediate(Smi::FromInt(0)));
2528 __ jmp(&done);
2529
2530 NopRuntimeCallHelper call_helper;
2531 generator.GenerateSlow(masm_, call_helper);
2532
2533 __ bind(&done);
2534 Apply(context_, result);
2535}
2536
2537
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002538void FullCodeGenerator::EmitStringAdd(ZoneList<Expression*>* args) {
2539 ASSERT_EQ(2, args->length());
2540
2541 VisitForValue(args->at(0), kStack);
2542 VisitForValue(args->at(1), kStack);
2543
2544 StringAddStub stub(NO_STRING_ADD_FLAGS);
2545 __ CallStub(&stub);
2546 Apply(context_, eax);
2547}
2548
2549
2550void FullCodeGenerator::EmitStringCompare(ZoneList<Expression*>* args) {
2551 ASSERT_EQ(2, args->length());
2552
2553 VisitForValue(args->at(0), kStack);
2554 VisitForValue(args->at(1), kStack);
2555
2556 StringCompareStub stub;
2557 __ CallStub(&stub);
2558 Apply(context_, eax);
2559}
2560
2561
2562void FullCodeGenerator::EmitMathSin(ZoneList<Expression*>* args) {
2563 // Load the argument on the stack and call the stub.
2564 TranscendentalCacheStub stub(TranscendentalCache::SIN);
2565 ASSERT(args->length() == 1);
2566 VisitForValue(args->at(0), kStack);
2567 __ CallStub(&stub);
2568 Apply(context_, eax);
2569}
2570
2571
2572void FullCodeGenerator::EmitMathCos(ZoneList<Expression*>* args) {
2573 // Load the argument on the stack and call the stub.
2574 TranscendentalCacheStub stub(TranscendentalCache::COS);
2575 ASSERT(args->length() == 1);
2576 VisitForValue(args->at(0), kStack);
2577 __ CallStub(&stub);
2578 Apply(context_, eax);
2579}
2580
2581
2582void FullCodeGenerator::EmitMathSqrt(ZoneList<Expression*>* args) {
2583 // Load the argument on the stack and call the runtime function.
2584 ASSERT(args->length() == 1);
2585 VisitForValue(args->at(0), kStack);
2586 __ CallRuntime(Runtime::kMath_sqrt, 1);
2587 Apply(context_, eax);
2588}
2589
2590
2591void FullCodeGenerator::EmitCallFunction(ZoneList<Expression*>* args) {
2592 ASSERT(args->length() >= 2);
2593
2594 int arg_count = args->length() - 2; // For receiver and function.
2595 VisitForValue(args->at(0), kStack); // Receiver.
2596 for (int i = 0; i < arg_count; i++) {
2597 VisitForValue(args->at(i + 1), kStack);
2598 }
2599 VisitForValue(args->at(arg_count + 1), kAccumulator); // Function.
2600
2601 // InvokeFunction requires function in edi. Move it in there.
2602 if (!result_register().is(edi)) __ mov(edi, result_register());
2603 ParameterCount count(arg_count);
2604 __ InvokeFunction(edi, count, CALL_FUNCTION);
2605 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2606 Apply(context_, eax);
2607}
2608
2609
2610void FullCodeGenerator::EmitRegExpConstructResult(ZoneList<Expression*>* args) {
2611 ASSERT(args->length() == 3);
2612 VisitForValue(args->at(0), kStack);
2613 VisitForValue(args->at(1), kStack);
2614 VisitForValue(args->at(2), kStack);
2615 __ CallRuntime(Runtime::kRegExpConstructResult, 3);
2616 Apply(context_, eax);
2617}
2618
2619
2620void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
2621 ASSERT(args->length() == 3);
2622 VisitForValue(args->at(0), kStack);
2623 VisitForValue(args->at(1), kStack);
2624 VisitForValue(args->at(2), kStack);
2625 __ CallRuntime(Runtime::kSwapElements, 3);
2626 Apply(context_, eax);
2627}
2628
2629
2630void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) {
2631 ASSERT_EQ(2, args->length());
2632
2633 ASSERT_NE(NULL, args->at(0)->AsLiteral());
2634 int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
2635
2636 Handle<FixedArray> jsfunction_result_caches(
2637 Top::global_context()->jsfunction_result_caches());
2638 if (jsfunction_result_caches->length() <= cache_id) {
2639 __ Abort("Attempt to use undefined cache.");
2640 __ mov(eax, Factory::undefined_value());
2641 Apply(context_, eax);
2642 return;
2643 }
2644
2645 VisitForValue(args->at(1), kAccumulator);
2646
2647 Register key = eax;
2648 Register cache = ebx;
2649 Register tmp = ecx;
2650 __ mov(cache, CodeGenerator::ContextOperand(esi, Context::GLOBAL_INDEX));
2651 __ mov(cache,
2652 FieldOperand(cache, GlobalObject::kGlobalContextOffset));
2653 __ mov(cache,
2654 CodeGenerator::ContextOperand(
2655 cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
2656 __ mov(cache,
2657 FieldOperand(cache, FixedArray::OffsetOfElementAt(cache_id)));
2658
2659 Label done, not_found;
2660 // tmp now holds finger offset as a smi.
2661 ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
2662 __ mov(tmp, FieldOperand(cache, JSFunctionResultCache::kFingerOffset));
2663 __ cmp(key, CodeGenerator::FixedArrayElementOperand(cache, tmp));
2664 __ j(not_equal, &not_found);
2665
2666 __ mov(eax, CodeGenerator::FixedArrayElementOperand(cache, tmp, 1));
2667 __ jmp(&done);
2668
2669 __ bind(&not_found);
2670 // Call runtime to perform the lookup.
2671 __ push(cache);
2672 __ push(key);
2673 __ CallRuntime(Runtime::kGetFromCache, 2);
2674
2675 __ bind(&done);
2676 Apply(context_, eax);
2677}
2678
2679
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00002680void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
2681 ASSERT_EQ(2, args->length());
2682
2683 Register right = eax;
2684 Register left = ebx;
2685 Register tmp = ecx;
2686
2687 VisitForValue(args->at(0), kStack);
2688 VisitForValue(args->at(1), kAccumulator);
2689 __ pop(left);
2690
2691 Label done, fail, ok;
2692 __ cmp(left, Operand(right));
2693 __ j(equal, &ok);
2694 // Fail if either is a non-HeapObject.
2695 __ mov(tmp, left);
2696 __ and_(Operand(tmp), right);
2697 __ test(Operand(tmp), Immediate(kSmiTagMask));
2698 __ j(zero, &fail);
lrn@chromium.org19375882010-08-09 12:49:57 +00002699 __ CmpObjectType(left, JS_REGEXP_TYPE, tmp);
lrn@chromium.orgc4e51ac2010-08-09 09:47:21 +00002700 __ j(not_equal, &fail);
2701 __ cmp(tmp, FieldOperand(right, HeapObject::kMapOffset));
2702 __ j(not_equal, &fail);
2703 __ mov(tmp, FieldOperand(left, JSRegExp::kDataOffset));
2704 __ cmp(tmp, FieldOperand(right, JSRegExp::kDataOffset));
2705 __ j(equal, &ok);
2706 __ bind(&fail);
2707 __ mov(eax, Immediate(Factory::false_value()));
2708 __ jmp(&done);
2709 __ bind(&ok);
2710 __ mov(eax, Immediate(Factory::true_value()));
2711 __ bind(&done);
2712
2713 Apply(context_, eax);
2714}
2715
2716
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002717void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002718 Handle<String> name = expr->name();
2719 if (name->length() > 0 && name->Get(0) == '_') {
2720 Comment cmnt(masm_, "[ InlineRuntimeCall");
2721 EmitInlineRuntimeCall(expr);
2722 return;
2723 }
2724
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002725 Comment cmnt(masm_, "[ CallRuntime");
2726 ZoneList<Expression*>* args = expr->arguments();
2727
2728 if (expr->is_jsruntime()) {
2729 // Prepare for calling JS runtime function.
2730 __ mov(eax, CodeGenerator::GlobalObject());
2731 __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset));
2732 }
2733
2734 // Push the arguments ("left-to-right").
2735 int arg_count = args->length();
2736 for (int i = 0; i < arg_count; i++) {
2737 VisitForValue(args->at(i), kStack);
2738 }
2739
2740 if (expr->is_jsruntime()) {
2741 // Call the JS runtime function via a call IC.
2742 __ Set(ecx, Immediate(expr->name()));
2743 InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
2744 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
2745 __ call(ic, RelocInfo::CODE_TARGET);
2746 // Restore context register.
2747 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
2748 } else {
2749 // Call the C runtime function.
2750 __ CallRuntime(expr->function(), arg_count);
2751 }
2752 Apply(context_, eax);
2753}
2754
2755
2756void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
2757 switch (expr->op()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002758 case Token::DELETE: {
2759 Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
2760 Property* prop = expr->expression()->AsProperty();
2761 Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
2762 if (prop == NULL && var == NULL) {
2763 // Result of deleting non-property, non-variable reference is true.
2764 // The subexpression may have side effects.
2765 VisitForEffect(expr->expression());
2766 Apply(context_, true);
2767 } else if (var != NULL &&
2768 !var->is_global() &&
2769 var->slot() != NULL &&
2770 var->slot()->type() != Slot::LOOKUP) {
2771 // Result of deleting non-global, non-dynamic variables is false.
2772 // The subexpression does not have side effects.
2773 Apply(context_, false);
2774 } else {
2775 // Property or variable reference. Call the delete builtin with
2776 // object and property name as arguments.
2777 if (prop != NULL) {
2778 VisitForValue(prop->obj(), kStack);
2779 VisitForValue(prop->key(), kStack);
2780 } else if (var->is_global()) {
2781 __ push(CodeGenerator::GlobalObject());
2782 __ push(Immediate(var->name()));
2783 } else {
2784 // Non-global variable. Call the runtime to look up the context
2785 // where the variable was introduced.
2786 __ push(context_register());
2787 __ push(Immediate(var->name()));
2788 __ CallRuntime(Runtime::kLookupContext, 2);
2789 __ push(eax);
2790 __ push(Immediate(var->name()));
2791 }
2792 __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
2793 Apply(context_, eax);
2794 }
2795 break;
2796 }
2797
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002798 case Token::VOID: {
2799 Comment cmnt(masm_, "[ UnaryOperation (VOID)");
2800 VisitForEffect(expr->expression());
2801 switch (context_) {
2802 case Expression::kUninitialized:
2803 UNREACHABLE();
2804 break;
2805 case Expression::kEffect:
2806 break;
2807 case Expression::kValue:
2808 switch (location_) {
2809 case kAccumulator:
2810 __ mov(result_register(), Factory::undefined_value());
2811 break;
2812 case kStack:
2813 __ push(Immediate(Factory::undefined_value()));
2814 break;
2815 }
2816 break;
2817 case Expression::kTestValue:
2818 // Value is false so it's needed.
2819 switch (location_) {
2820 case kAccumulator:
2821 __ mov(result_register(), Factory::undefined_value());
2822 break;
2823 case kStack:
2824 __ push(Immediate(Factory::undefined_value()));
2825 break;
2826 }
2827 // Fall through.
2828 case Expression::kTest:
2829 case Expression::kValueTest:
2830 __ jmp(false_label_);
2831 break;
2832 }
2833 break;
2834 }
2835
2836 case Token::NOT: {
2837 Comment cmnt(masm_, "[ UnaryOperation (NOT)");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002838 Label materialize_true, materialize_false;
2839 Label* if_true = NULL;
2840 Label* if_false = NULL;
2841
2842 // Notice that the labels are swapped.
2843 PrepareTest(&materialize_true, &materialize_false, &if_false, &if_true);
2844
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002845 VisitForControl(expr->expression(), if_true, if_false);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002846
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002847 Apply(context_, if_false, if_true); // Labels swapped.
2848 break;
2849 }
2850
2851 case Token::TYPEOF: {
2852 Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
2853 VariableProxy* proxy = expr->expression()->AsVariableProxy();
2854 if (proxy != NULL &&
2855 !proxy->var()->is_this() &&
2856 proxy->var()->is_global()) {
2857 Comment cmnt(masm_, "Global variable");
ager@chromium.org5c838252010-02-19 08:53:10 +00002858 __ mov(eax, CodeGenerator::GlobalObject());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002859 __ mov(ecx, Immediate(proxy->name()));
2860 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
2861 // Use a regular load, not a contextual load, to avoid a reference
2862 // error.
2863 __ call(ic, RelocInfo::CODE_TARGET);
ager@chromium.org5c838252010-02-19 08:53:10 +00002864 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002865 } else if (proxy != NULL &&
2866 proxy->var()->slot() != NULL &&
2867 proxy->var()->slot()->type() == Slot::LOOKUP) {
2868 __ push(esi);
2869 __ push(Immediate(proxy->name()));
2870 __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
2871 __ push(eax);
2872 } else {
2873 // This expression cannot throw a reference error at the top level.
2874 VisitForValue(expr->expression(), kStack);
2875 }
2876
2877 __ CallRuntime(Runtime::kTypeof, 1);
2878 Apply(context_, eax);
2879 break;
2880 }
2881
2882 case Token::ADD: {
2883 Comment cmt(masm_, "[ UnaryOperation (ADD)");
2884 VisitForValue(expr->expression(), kAccumulator);
2885 Label no_conversion;
2886 __ test(result_register(), Immediate(kSmiTagMask));
2887 __ j(zero, &no_conversion);
2888 __ push(result_register());
2889 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2890 __ bind(&no_conversion);
2891 Apply(context_, result_register());
2892 break;
2893 }
2894
2895 case Token::SUB: {
2896 Comment cmt(masm_, "[ UnaryOperation (SUB)");
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002897 bool can_overwrite =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002898 (expr->expression()->AsBinaryOperation() != NULL &&
2899 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002900 UnaryOverwriteMode overwrite =
2901 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002902 GenericUnaryOpStub stub(Token::SUB, overwrite);
2903 // GenericUnaryOpStub expects the argument to be in the
2904 // accumulator register eax.
2905 VisitForValue(expr->expression(), kAccumulator);
2906 __ CallStub(&stub);
2907 Apply(context_, eax);
2908 break;
2909 }
2910
2911 case Token::BIT_NOT: {
2912 Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002913 bool can_overwrite =
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002914 (expr->expression()->AsBinaryOperation() != NULL &&
2915 expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00002916 UnaryOverwriteMode overwrite =
2917 can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002918 GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
2919 // GenericUnaryOpStub expects the argument to be in the
2920 // accumulator register eax.
2921 VisitForValue(expr->expression(), kAccumulator);
2922 // Avoid calling the stub for Smis.
2923 Label smi, done;
2924 __ test(result_register(), Immediate(kSmiTagMask));
2925 __ j(zero, &smi);
2926 // Non-smi: call stub leaving result in accumulator register.
2927 __ CallStub(&stub);
2928 __ jmp(&done);
2929 // Perform operation directly on Smis.
2930 __ bind(&smi);
2931 __ not_(result_register());
2932 __ and_(result_register(), ~kSmiTagMask); // Remove inverted smi-tag.
2933 __ bind(&done);
2934 Apply(context_, result_register());
2935 break;
2936 }
2937
2938 default:
2939 UNREACHABLE();
2940 }
2941}
2942
2943
2944void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
2945 Comment cmnt(masm_, "[ CountOperation");
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002946 // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
2947 // as the left-hand side.
2948 if (!expr->expression()->IsValidLeftHandSide()) {
2949 VisitForEffect(expr->expression());
2950 return;
2951 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002952
2953 // Expression can only be a property, a global or a (parameter or local)
2954 // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
2955 enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
2956 LhsKind assign_type = VARIABLE;
2957 Property* prop = expr->expression()->AsProperty();
2958 // In case of a property we use the uninitialized expression context
2959 // of the key to detect a named property.
2960 if (prop != NULL) {
2961 assign_type =
2962 (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
2963 }
2964
2965 // Evaluate expression and get value.
2966 if (assign_type == VARIABLE) {
2967 ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
2968 Location saved_location = location_;
2969 location_ = kAccumulator;
2970 EmitVariableLoad(expr->expression()->AsVariableProxy()->var(),
2971 Expression::kValue);
2972 location_ = saved_location;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00002973 } else {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002974 // Reserve space for result of postfix operation.
2975 if (expr->is_postfix() && context_ != Expression::kEffect) {
2976 __ push(Immediate(Smi::FromInt(0)));
2977 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002978 if (assign_type == NAMED_PROPERTY) {
ager@chromium.org5c838252010-02-19 08:53:10 +00002979 // Put the object both on the stack and in the accumulator.
2980 VisitForValue(prop->obj(), kAccumulator);
2981 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002982 EmitNamedPropertyLoad(prop);
2983 } else {
ager@chromium.org5c838252010-02-19 08:53:10 +00002984 VisitForValue(prop->obj(), kStack);
2985 VisitForValue(prop->key(), kAccumulator);
2986 __ mov(edx, Operand(esp, 0));
2987 __ push(eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00002988 EmitKeyedPropertyLoad(prop);
2989 }
2990 }
2991
2992 // Call ToNumber only if operand is not a smi.
2993 Label no_conversion;
2994 __ test(eax, Immediate(kSmiTagMask));
2995 __ j(zero, &no_conversion);
2996 __ push(eax);
2997 __ InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION);
2998 __ bind(&no_conversion);
2999
3000 // Save result for postfix expressions.
3001 if (expr->is_postfix()) {
3002 switch (context_) {
3003 case Expression::kUninitialized:
3004 UNREACHABLE();
3005 case Expression::kEffect:
3006 // Do not save result.
3007 break;
3008 case Expression::kValue:
3009 case Expression::kTest:
3010 case Expression::kValueTest:
3011 case Expression::kTestValue:
3012 // Save the result on the stack. If we have a named or keyed property
3013 // we store the result under the receiver that is currently on top
3014 // of the stack.
3015 switch (assign_type) {
3016 case VARIABLE:
3017 __ push(eax);
3018 break;
3019 case NAMED_PROPERTY:
3020 __ mov(Operand(esp, kPointerSize), eax);
3021 break;
3022 case KEYED_PROPERTY:
3023 __ mov(Operand(esp, 2 * kPointerSize), eax);
3024 break;
3025 }
3026 break;
3027 }
3028 }
3029
3030 // Inline smi case if we are in a loop.
3031 Label stub_call, done;
3032 if (loop_depth() > 0) {
3033 if (expr->op() == Token::INC) {
3034 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
3035 } else {
3036 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
3037 }
3038 __ j(overflow, &stub_call);
3039 // We could eliminate this smi check if we split the code at
3040 // the first smi check before calling ToNumber.
3041 __ test(eax, Immediate(kSmiTagMask));
3042 __ j(zero, &done);
3043 __ bind(&stub_call);
3044 // Call stub. Undo operation first.
3045 if (expr->op() == Token::INC) {
3046 __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
3047 } else {
3048 __ add(Operand(eax), Immediate(Smi::FromInt(1)));
3049 }
3050 }
3051 // Call stub for +1/-1.
3052 GenericBinaryOpStub stub(expr->binary_op(),
3053 NO_OVERWRITE,
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003054 NO_GENERIC_BINARY_FLAGS,
sgjesse@chromium.orgdf7a2842010-03-25 14:34:15 +00003055 TypeInfo::Unknown());
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003056 stub.GenerateCall(masm(), eax, Smi::FromInt(1));
3057 __ bind(&done);
3058
3059 // Store the value returned in eax.
3060 switch (assign_type) {
3061 case VARIABLE:
3062 if (expr->is_postfix()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003063 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003064 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003065 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003066 Expression::kEffect);
3067 // For all contexts except kEffect: We have the result on
3068 // top of the stack.
3069 if (context_ != Expression::kEffect) {
3070 ApplyTOS(context_);
3071 }
3072 } else {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003073 // Perform the assignment as if via '='.
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003074 EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003075 Token::ASSIGN,
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003076 context_);
3077 }
3078 break;
3079 case NAMED_PROPERTY: {
3080 __ mov(ecx, prop->key()->AsLiteral()->handle());
3081 __ pop(edx);
3082 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
3083 __ call(ic, RelocInfo::CODE_TARGET);
3084 // This nop signals to the IC that there is no inlined code at the call
3085 // site for it to patch.
3086 __ nop();
3087 if (expr->is_postfix()) {
3088 if (context_ != Expression::kEffect) {
3089 ApplyTOS(context_);
3090 }
3091 } else {
3092 Apply(context_, eax);
3093 }
3094 break;
3095 }
3096 case KEYED_PROPERTY: {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003097 __ pop(ecx);
3098 __ pop(edx);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003099 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
3100 __ call(ic, RelocInfo::CODE_TARGET);
3101 // This nop signals to the IC that there is no inlined code at the call
3102 // site for it to patch.
3103 __ nop();
3104 if (expr->is_postfix()) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003105 // Result is on the stack
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003106 if (context_ != Expression::kEffect) {
3107 ApplyTOS(context_);
3108 }
3109 } else {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00003110 Apply(context_, eax);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003111 }
3112 break;
3113 }
3114 }
3115}
3116
3117
3118void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
3119 Comment cmnt(masm_, "[ BinaryOperation");
3120 switch (expr->op()) {
3121 case Token::COMMA:
3122 VisitForEffect(expr->left());
3123 Visit(expr->right());
3124 break;
3125
3126 case Token::OR:
3127 case Token::AND:
3128 EmitLogicalOperation(expr);
3129 break;
3130
3131 case Token::ADD:
3132 case Token::SUB:
3133 case Token::DIV:
3134 case Token::MOD:
3135 case Token::MUL:
3136 case Token::BIT_OR:
3137 case Token::BIT_AND:
3138 case Token::BIT_XOR:
3139 case Token::SHL:
3140 case Token::SHR:
3141 case Token::SAR:
3142 VisitForValue(expr->left(), kStack);
3143 VisitForValue(expr->right(), kAccumulator);
3144 EmitBinaryOp(expr->op(), context_);
3145 break;
3146
3147 default:
3148 UNREACHABLE();
3149 }
3150}
3151
3152
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003153void FullCodeGenerator::EmitNullCompare(bool strict,
3154 Register obj,
3155 Register null_const,
3156 Label* if_true,
3157 Label* if_false,
3158 Register scratch) {
3159 __ cmp(obj, Operand(null_const));
3160 if (strict) {
3161 __ j(equal, if_true);
3162 } else {
3163 __ j(equal, if_true);
3164 __ cmp(obj, Factory::undefined_value());
3165 __ j(equal, if_true);
3166 __ test(obj, Immediate(kSmiTagMask));
3167 __ j(zero, if_false);
3168 // It can be an undetectable object.
3169 __ mov(scratch, FieldOperand(obj, HeapObject::kMapOffset));
3170 __ movzx_b(scratch, FieldOperand(scratch, Map::kBitFieldOffset));
3171 __ test(scratch, Immediate(1 << Map::kIsUndetectable));
3172 __ j(not_zero, if_true);
3173 }
3174 __ jmp(if_false);
3175}
3176
3177
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003178void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
3179 Comment cmnt(masm_, "[ CompareOperation");
3180
3181 // Always perform the comparison for its control flow. Pack the result
3182 // into the expression's context after the comparison is performed.
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003183
3184 Label materialize_true, materialize_false;
3185 Label* if_true = NULL;
3186 Label* if_false = NULL;
3187 PrepareTest(&materialize_true, &materialize_false, &if_true, &if_false);
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003188
3189 VisitForValue(expr->left(), kStack);
3190 switch (expr->op()) {
3191 case Token::IN:
3192 VisitForValue(expr->right(), kStack);
3193 __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
3194 __ cmp(eax, Factory::true_value());
3195 __ j(equal, if_true);
3196 __ jmp(if_false);
3197 break;
3198
3199 case Token::INSTANCEOF: {
3200 VisitForValue(expr->right(), kStack);
3201 InstanceofStub stub;
3202 __ CallStub(&stub);
3203 __ test(eax, Operand(eax));
3204 __ j(zero, if_true); // The stub returns 0 for true.
3205 __ jmp(if_false);
3206 break;
3207 }
3208
3209 default: {
3210 VisitForValue(expr->right(), kAccumulator);
3211 Condition cc = no_condition;
3212 bool strict = false;
3213 switch (expr->op()) {
3214 case Token::EQ_STRICT:
3215 strict = true;
3216 // Fall through
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003217 case Token::EQ: {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003218 cc = equal;
3219 __ pop(edx);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003220 // If either operand is constant null we do a fast compare
3221 // against null.
3222 Literal* right_literal = expr->right()->AsLiteral();
3223 Literal* left_literal = expr->left()->AsLiteral();
3224 if (right_literal != NULL && right_literal->handle()->IsNull()) {
3225 EmitNullCompare(strict, edx, eax, if_true, if_false, ecx);
3226 Apply(context_, if_true, if_false);
3227 return;
3228 } else if (left_literal != NULL && left_literal->handle()->IsNull()) {
3229 EmitNullCompare(strict, eax, edx, if_true, if_false, ecx);
3230 Apply(context_, if_true, if_false);
3231 return;
3232 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003233 break;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003234 }
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +00003235 case Token::LT:
3236 cc = less;
3237 __ pop(edx);
3238 break;
3239 case Token::GT:
3240 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3241 cc = less;
3242 __ mov(edx, result_register());
3243 __ pop(eax);
3244 break;
3245 case Token::LTE:
3246 // Reverse left and right sizes to obtain ECMA-262 conversion order.
3247 cc = greater_equal;
3248 __ mov(edx, result_register());
3249 __ pop(eax);
3250 break;
3251 case Token::GTE:
3252 cc = greater_equal;
3253 __ pop(edx);
3254 break;
3255 case Token::IN:
3256 case Token::INSTANCEOF:
3257 default:
3258 UNREACHABLE();
3259 }
3260
3261 // The comparison stub expects the smi vs. smi case to be handled
3262 // before it is called.
3263 Label slow_case;
3264 __ mov(ecx, Operand(edx));
3265 __ or_(ecx, Operand(eax));
3266 __ test(ecx, Immediate(kSmiTagMask));
3267 __ j(not_zero, &slow_case, not_taken);
3268 __ cmp(edx, Operand(eax));
3269 __ j(cc, if_true);
3270 __ jmp(if_false);
3271
3272 __ bind(&slow_case);
3273 CompareStub stub(cc, strict);
3274 __ CallStub(&stub);
3275 __ test(eax, Operand(eax));
3276 __ j(cc, if_true);
3277 __ jmp(if_false);
3278 }
3279 }
3280
3281 // Convert the result of the comparison into one expected for this
3282 // expression's context.
3283 Apply(context_, if_true, if_false);
3284}
3285
3286
3287void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
3288 __ mov(eax, Operand(ebp, JavaScriptFrameConstants::kFunctionOffset));
3289 Apply(context_, eax);
3290}
3291
3292
3293Register FullCodeGenerator::result_register() { return eax; }
3294
3295
3296Register FullCodeGenerator::context_register() { return esi; }
3297
3298
3299void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
3300 ASSERT_EQ(POINTER_SIZE_ALIGN(frame_offset), frame_offset);
3301 __ mov(Operand(ebp, frame_offset), value);
3302}
3303
3304
3305void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
3306 __ mov(dst, CodeGenerator::ContextOperand(esi, context_index));
3307}
3308
3309
3310// ----------------------------------------------------------------------------
3311// Non-local control flow support.
3312
3313void FullCodeGenerator::EnterFinallyBlock() {
3314 // Cook return address on top of stack (smi encoded Code* delta)
3315 ASSERT(!result_register().is(edx));
3316 __ mov(edx, Operand(esp, 0));
3317 __ sub(Operand(edx), Immediate(masm_->CodeObject()));
3318 ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
3319 ASSERT_EQ(0, kSmiTag);
3320 __ add(edx, Operand(edx)); // Convert to smi.
3321 __ mov(Operand(esp, 0), edx);
3322 // Store result register while executing finally block.
3323 __ push(result_register());
3324}
3325
3326
3327void FullCodeGenerator::ExitFinallyBlock() {
3328 ASSERT(!result_register().is(edx));
3329 // Restore result register from stack.
3330 __ pop(result_register());
3331 // Uncook return address.
3332 __ mov(edx, Operand(esp, 0));
3333 __ sar(edx, 1); // Convert smi to int.
3334 __ add(Operand(edx), Immediate(masm_->CodeObject()));
3335 __ mov(Operand(esp, 0), edx);
3336 // And return.
3337 __ ret(0);
3338}
3339
3340
3341#undef __
3342
3343} } // namespace v8::internal
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +00003344
3345#endif // V8_TARGET_ARCH_IA32