blob: fe65d34a084b2af8915514e97f8549d262307591 [file] [log] [blame]
ager@chromium.org5ec48922009-05-05 07:25:34 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
ager@chromium.orgeadaf222009-06-16 09:43:10 +000028#include "v8.h"
29
30#include "codegen-inl.h"
31#include "register-allocator-inl.h"
32#include "scopes.h"
33
34namespace v8 {
35namespace internal {
36
37#define __ ACCESS_MASM(masm())
38
39// -------------------------------------------------------------------------
40// VirtualFrame implementation.
41
42// On entry to a function, the virtual frame already contains the receiver,
43// the parameters, and a return address. All frame elements are in memory.
44VirtualFrame::VirtualFrame()
45 : elements_(parameter_count() + local_count() + kPreallocatedElements),
46 stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
47 for (int i = 0; i <= stack_pointer_; i++) {
48 elements_.Add(FrameElement::MemoryElement());
49 }
50 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
51 register_locations_[i] = kIllegalIndex;
52 }
53}
54
55
56void VirtualFrame::Enter() {
57 // Registers live on entry to a JS frame:
58 // rsp: stack pointer, points to return address from this function.
59 // rbp: base pointer, points to previous JS, ArgumentsAdaptor, or
60 // Trampoline frame.
61 // rsi: context of this function call.
62 // rdi: pointer to this function object.
63 Comment cmnt(masm(), "[ Enter JS frame");
64
65#ifdef DEBUG
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +000066 if (FLAG_debug_code) {
67 // Verify that rdi contains a JS function. The following code
68 // relies on rax being available for use.
69 Condition not_smi = NegateCondition(masm()->CheckSmi(rdi));
70 __ Check(not_smi,
71 "VirtualFrame::Enter - rdi is not a function (smi check).");
72 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
73 __ Check(equal,
74 "VirtualFrame::Enter - rdi is not a function (map check).");
75 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +000076#endif
77
78 EmitPush(rbp);
79
80 __ movq(rbp, rsp);
81
82 // Store the context in the frame. The context is kept in rsi and a
83 // copy is stored in the frame. The external reference to rsi
84 // remains.
85 EmitPush(rsi);
86
87 // Store the function in the frame. The frame owns the register
88 // reference now (ie, it can keep it in rdi or spill it later).
89 Push(rdi);
ager@chromium.org5aa501c2009-06-23 07:57:28 +000090 SyncElementAt(element_count() - 1);
ager@chromium.orgeadaf222009-06-16 09:43:10 +000091 cgen()->allocator()->Unuse(rdi);
92}
93
94
95void VirtualFrame::Exit() {
96 Comment cmnt(masm(), "[ Exit JS frame");
97 // Record the location of the JS exit code for patching when setting
98 // break point.
99 __ RecordJSReturn();
100
101 // Avoid using the leave instruction here, because it is too
102 // short. We need the return sequence to be a least the size of a
103 // call instruction to support patching the exit code in the
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000104 // debugger. See GenerateReturnSequence for the full return sequence.
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000105 // TODO(X64): A patched call will be very long now. Make sure we
106 // have enough room.
107 __ movq(rsp, rbp);
108 stack_pointer_ = frame_pointer();
109 for (int i = element_count() - 1; i > stack_pointer_; i--) {
110 FrameElement last = elements_.RemoveLast();
111 if (last.is_register()) {
112 Unuse(last.reg());
113 }
114 }
115
116 EmitPop(rbp);
117}
118
119
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000120void VirtualFrame::AllocateStackSlots() {
121 int count = local_count();
122 if (count > 0) {
123 Comment cmnt(masm(), "[ Allocate space for locals");
124 // The locals are initialized to a constant (the undefined value), but
125 // we sync them with the actual frame to allocate space for spilling
126 // them later. First sync everything above the stack pointer so we can
127 // use pushes to allocate and initialize the locals.
128 SyncRange(stack_pointer_ + 1, element_count() - 1);
129 Handle<Object> undefined = Factory::undefined_value();
130 FrameElement initial_value =
131 FrameElement::ConstantElement(undefined, FrameElement::SYNCED);
132 __ movq(kScratchRegister, undefined, RelocInfo::EMBEDDED_OBJECT);
133 for (int i = 0; i < count; i++) {
134 elements_.Add(initial_value);
135 stack_pointer_++;
136 __ push(kScratchRegister);
137 }
138 }
139}
140
141
142void VirtualFrame::SaveContextRegister() {
143 ASSERT(elements_[context_index()].is_memory());
144 __ movq(Operand(rbp, fp_relative(context_index())), rsi);
145}
146
147
148void VirtualFrame::RestoreContextRegister() {
149 ASSERT(elements_[context_index()].is_memory());
150 __ movq(rsi, Operand(rbp, fp_relative(context_index())));
151}
152
153
154void VirtualFrame::PushReceiverSlotAddress() {
155 Result temp = cgen()->allocator()->Allocate();
156 ASSERT(temp.is_valid());
157 __ lea(temp.reg(), ParameterAt(-1));
158 Push(&temp);
159}
160
161
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000162void VirtualFrame::EmitPop(Register reg) {
163 ASSERT(stack_pointer_ == element_count() - 1);
164 stack_pointer_--;
165 elements_.RemoveLast();
166 __ pop(reg);
167}
168
169
170void VirtualFrame::EmitPop(const Operand& operand) {
171 ASSERT(stack_pointer_ == element_count() - 1);
172 stack_pointer_--;
173 elements_.RemoveLast();
174 __ pop(operand);
175}
176
177
178void VirtualFrame::EmitPush(Register reg) {
179 ASSERT(stack_pointer_ == element_count() - 1);
180 elements_.Add(FrameElement::MemoryElement());
181 stack_pointer_++;
182 __ push(reg);
183}
184
185
186void VirtualFrame::EmitPush(const Operand& operand) {
187 ASSERT(stack_pointer_ == element_count() - 1);
188 elements_.Add(FrameElement::MemoryElement());
189 stack_pointer_++;
190 __ push(operand);
191}
192
193
194void VirtualFrame::EmitPush(Immediate immediate) {
195 ASSERT(stack_pointer_ == element_count() - 1);
196 elements_.Add(FrameElement::MemoryElement());
197 stack_pointer_++;
198 __ push(immediate);
199}
200
201
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000202void VirtualFrame::EmitPush(Smi* smi_value) {
203 ASSERT(stack_pointer_ == element_count() - 1);
204 elements_.Add(FrameElement::MemoryElement());
205 stack_pointer_++;
206 __ Push(smi_value);
207}
208
209
ager@chromium.org3e875802009-06-29 08:26:34 +0000210void VirtualFrame::EmitPush(Handle<Object> value) {
211 ASSERT(stack_pointer_ == element_count() - 1);
212 elements_.Add(FrameElement::MemoryElement());
213 stack_pointer_++;
214 __ Push(value);
215}
216
217
ager@chromium.org18ad94b2009-09-02 08:22:29 +0000218void VirtualFrame::EmitPush(Heap::RootListIndex index) {
219 ASSERT(stack_pointer_ == element_count() - 1);
220 elements_.Add(FrameElement::MemoryElement());
221 stack_pointer_++;
222 __ PushRoot(index);
223}
224
225
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000226void VirtualFrame::Drop(int count) {
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +0000227 ASSERT(count >= 0);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000228 ASSERT(height() >= count);
229 int num_virtual_elements = (element_count() - 1) - stack_pointer_;
230
231 // Emit code to lower the stack pointer if necessary.
232 if (num_virtual_elements < count) {
233 int num_dropped = count - num_virtual_elements;
234 stack_pointer_ -= num_dropped;
235 __ addq(rsp, Immediate(num_dropped * kPointerSize));
236 }
237
238 // Discard elements from the virtual frame and free any registers.
239 for (int i = 0; i < count; i++) {
240 FrameElement dropped = elements_.RemoveLast();
241 if (dropped.is_register()) {
242 Unuse(dropped.reg());
243 }
244 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000245}
246
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000247
248int VirtualFrame::InvalidateFrameSlotAt(int index) {
249 FrameElement original = elements_[index];
250
251 // Is this element the backing store of any copies?
252 int new_backing_index = kIllegalIndex;
253 if (original.is_copied()) {
254 // Verify it is copied, and find first copy.
255 for (int i = index + 1; i < element_count(); i++) {
256 if (elements_[i].is_copy() && elements_[i].index() == index) {
257 new_backing_index = i;
258 break;
259 }
260 }
261 }
262
263 if (new_backing_index == kIllegalIndex) {
264 // No copies found, return kIllegalIndex.
265 if (original.is_register()) {
266 Unuse(original.reg());
267 }
268 elements_[index] = FrameElement::InvalidElement();
269 return kIllegalIndex;
270 }
271
272 // This is the backing store of copies.
273 Register backing_reg;
274 if (original.is_memory()) {
275 Result fresh = cgen()->allocator()->Allocate();
276 ASSERT(fresh.is_valid());
277 Use(fresh.reg(), new_backing_index);
278 backing_reg = fresh.reg();
279 __ movq(backing_reg, Operand(rbp, fp_relative(index)));
280 } else {
281 // The original was in a register.
282 backing_reg = original.reg();
283 set_register_location(backing_reg, new_backing_index);
284 }
285 // Invalidate the element at index.
286 elements_[index] = FrameElement::InvalidElement();
287 // Set the new backing element.
288 if (elements_[new_backing_index].is_synced()) {
289 elements_[new_backing_index] =
290 FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED);
291 } else {
292 elements_[new_backing_index] =
293 FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED);
294 }
295 // Update the other copies.
296 for (int i = new_backing_index + 1; i < element_count(); i++) {
297 if (elements_[i].is_copy() && elements_[i].index() == index) {
298 elements_[i].set_index(new_backing_index);
299 elements_[new_backing_index].set_copied();
300 }
301 }
302 return new_backing_index;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000303}
304
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000305
ager@chromium.org3e875802009-06-29 08:26:34 +0000306void VirtualFrame::TakeFrameSlotAt(int index) {
307 ASSERT(index >= 0);
308 ASSERT(index <= element_count());
309 FrameElement original = elements_[index];
310 int new_backing_store_index = InvalidateFrameSlotAt(index);
311 if (new_backing_store_index != kIllegalIndex) {
312 elements_.Add(CopyElementAt(new_backing_store_index));
313 return;
314 }
315
316 switch (original.type()) {
317 case FrameElement::MEMORY: {
318 // Emit code to load the original element's data into a register.
319 // Push that register as a FrameElement on top of the frame.
320 Result fresh = cgen()->allocator()->Allocate();
321 ASSERT(fresh.is_valid());
322 FrameElement new_element =
323 FrameElement::RegisterElement(fresh.reg(),
324 FrameElement::NOT_SYNCED);
325 Use(fresh.reg(), element_count());
326 elements_.Add(new_element);
327 __ movq(fresh.reg(), Operand(rbp, fp_relative(index)));
328 break;
329 }
330 case FrameElement::REGISTER:
331 Use(original.reg(), element_count());
332 // Fall through.
333 case FrameElement::CONSTANT:
334 case FrameElement::COPY:
335 original.clear_sync();
336 elements_.Add(original);
337 break;
338 case FrameElement::INVALID:
339 UNREACHABLE();
340 break;
341 }
342}
343
344
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000345void VirtualFrame::StoreToFrameSlotAt(int index) {
346 // Store the value on top of the frame to the virtual frame slot at
347 // a given index. The value on top of the frame is left in place.
348 // This is a duplicating operation, so it can create copies.
349 ASSERT(index >= 0);
350 ASSERT(index < element_count());
351
352 int top_index = element_count() - 1;
353 FrameElement top = elements_[top_index];
354 FrameElement original = elements_[index];
355 if (top.is_copy() && top.index() == index) return;
356 ASSERT(top.is_valid());
357
358 InvalidateFrameSlotAt(index);
359
360 // InvalidateFrameSlotAt can potentially change any frame element, due
361 // to spilling registers to allocate temporaries in order to preserve
362 // the copy-on-write semantics of aliased elements. Reload top from
363 // the frame.
364 top = elements_[top_index];
365
366 if (top.is_copy()) {
367 // There are two cases based on the relative positions of the
368 // stored-to slot and the backing slot of the top element.
369 int backing_index = top.index();
370 ASSERT(backing_index != index);
371 if (backing_index < index) {
372 // 1. The top element is a copy of a slot below the stored-to
373 // slot. The stored-to slot becomes an unsynced copy of that
374 // same backing slot.
375 elements_[index] = CopyElementAt(backing_index);
376 } else {
377 // 2. The top element is a copy of a slot above the stored-to
378 // slot. The stored-to slot becomes the new (unsynced) backing
379 // slot and both the top element and the element at the former
380 // backing slot become copies of it. The sync state of the top
381 // and former backing elements is preserved.
382 FrameElement backing_element = elements_[backing_index];
383 ASSERT(backing_element.is_memory() || backing_element.is_register());
384 if (backing_element.is_memory()) {
385 // Because sets of copies are canonicalized to be backed by
386 // their lowest frame element, and because memory frame
387 // elements are backed by the corresponding stack address, we
388 // have to move the actual value down in the stack.
389 //
390 // TODO(209): considering allocating the stored-to slot to the
391 // temp register. Alternatively, allow copies to appear in
392 // any order in the frame and lazily move the value down to
393 // the slot.
394 __ movq(kScratchRegister, Operand(rbp, fp_relative(backing_index)));
395 __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
396 } else {
397 set_register_location(backing_element.reg(), index);
398 if (backing_element.is_synced()) {
399 // If the element is a register, we will not actually move
400 // anything on the stack but only update the virtual frame
401 // element.
402 backing_element.clear_sync();
403 }
404 }
405 elements_[index] = backing_element;
406
407 // The old backing element becomes a copy of the new backing
408 // element.
409 FrameElement new_element = CopyElementAt(index);
410 elements_[backing_index] = new_element;
411 if (backing_element.is_synced()) {
412 elements_[backing_index].set_sync();
413 }
414
415 // All the copies of the old backing element (including the top
416 // element) become copies of the new backing element.
417 for (int i = backing_index + 1; i < element_count(); i++) {
418 if (elements_[i].is_copy() && elements_[i].index() == backing_index) {
419 elements_[i].set_index(index);
420 }
421 }
422 }
423 return;
424 }
425
426 // Move the top element to the stored-to slot and replace it (the
427 // top element) with a copy.
428 elements_[index] = top;
429 if (top.is_memory()) {
430 // TODO(209): consider allocating the stored-to slot to the temp
431 // register. Alternatively, allow copies to appear in any order
432 // in the frame and lazily move the value down to the slot.
433 FrameElement new_top = CopyElementAt(index);
434 new_top.set_sync();
435 elements_[top_index] = new_top;
436
437 // The sync state of the former top element is correct (synced).
438 // Emit code to move the value down in the frame.
439 __ movq(kScratchRegister, Operand(rsp, 0));
440 __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
441 } else if (top.is_register()) {
442 set_register_location(top.reg(), index);
443 // The stored-to slot has the (unsynced) register reference and
444 // the top element becomes a copy. The sync state of the top is
445 // preserved.
446 FrameElement new_top = CopyElementAt(index);
447 if (top.is_synced()) {
448 new_top.set_sync();
449 elements_[index].clear_sync();
450 }
451 elements_[top_index] = new_top;
452 } else {
453 // The stored-to slot holds the same value as the top but
454 // unsynced. (We do not have copies of constants yet.)
455 ASSERT(top.is_constant());
456 elements_[index].clear_sync();
457 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000458}
459
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000460
461void VirtualFrame::MakeMergable() {
462 for (int i = 0; i < element_count(); i++) {
463 FrameElement element = elements_[i];
464
465 if (element.is_constant() || element.is_copy()) {
466 if (element.is_synced()) {
467 // Just spill.
468 elements_[i] = FrameElement::MemoryElement();
469 } else {
470 // Allocate to a register.
471 FrameElement backing_element; // Invalid if not a copy.
472 if (element.is_copy()) {
473 backing_element = elements_[element.index()];
474 }
475 Result fresh = cgen()->allocator()->Allocate();
476 ASSERT(fresh.is_valid()); // A register was spilled if all were in use.
477 elements_[i] =
478 FrameElement::RegisterElement(fresh.reg(),
479 FrameElement::NOT_SYNCED);
480 Use(fresh.reg(), i);
481
482 // Emit a move.
483 if (element.is_constant()) {
484 __ Move(fresh.reg(), element.handle());
485 } else {
486 ASSERT(element.is_copy());
487 // Copies are only backed by register or memory locations.
488 if (backing_element.is_register()) {
489 // The backing store may have been spilled by allocating,
490 // but that's OK. If it was, the value is right where we
491 // want it.
492 if (!fresh.reg().is(backing_element.reg())) {
493 __ movq(fresh.reg(), backing_element.reg());
494 }
495 } else {
496 ASSERT(backing_element.is_memory());
497 __ movq(fresh.reg(), Operand(rbp, fp_relative(element.index())));
498 }
499 }
500 }
ager@chromium.org3e875802009-06-29 08:26:34 +0000501 // No need to set the copied flag --- there are no copies.
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000502 } else {
503 // Clear the copy flag of non-constant, non-copy elements.
504 // They cannot be copied because copies are not allowed.
505 // The copy flag is not relied on before the end of this loop,
506 // including when registers are spilled.
507 elements_[i].clear_copied();
508 }
509 }
510}
511
512
513void VirtualFrame::MergeTo(VirtualFrame* expected) {
514 Comment cmnt(masm(), "[ Merge frame");
515 // We should always be merging the code generator's current frame to an
516 // expected frame.
517 ASSERT(cgen()->frame() == this);
518
519 // Adjust the stack pointer upward (toward the top of the virtual
520 // frame) if necessary.
521 if (stack_pointer_ < expected->stack_pointer_) {
522 int difference = expected->stack_pointer_ - stack_pointer_;
523 stack_pointer_ = expected->stack_pointer_;
524 __ subq(rsp, Immediate(difference * kPointerSize));
525 }
526
527 MergeMoveRegistersToMemory(expected);
528 MergeMoveRegistersToRegisters(expected);
529 MergeMoveMemoryToRegisters(expected);
530
531 // Adjust the stack pointer downward if necessary.
532 if (stack_pointer_ > expected->stack_pointer_) {
533 int difference = stack_pointer_ - expected->stack_pointer_;
534 stack_pointer_ = expected->stack_pointer_;
535 __ addq(rsp, Immediate(difference * kPointerSize));
536 }
537
538 // At this point, the frames should be identical.
539 ASSERT(Equals(expected));
540}
541
542
543void VirtualFrame::MergeMoveRegistersToMemory(VirtualFrame* expected) {
544 ASSERT(stack_pointer_ >= expected->stack_pointer_);
545
546 // Move registers, constants, and copies to memory. Perform moves
547 // from the top downward in the frame in order to leave the backing
548 // stores of copies in registers.
549 for (int i = element_count() - 1; i >= 0; i--) {
550 FrameElement target = expected->elements_[i];
551 if (target.is_register()) continue; // Handle registers later.
552 if (target.is_memory()) {
553 FrameElement source = elements_[i];
554 switch (source.type()) {
555 case FrameElement::INVALID:
556 // Not a legal merge move.
557 UNREACHABLE();
558 break;
559
560 case FrameElement::MEMORY:
561 // Already in place.
562 break;
563
564 case FrameElement::REGISTER:
565 Unuse(source.reg());
566 if (!source.is_synced()) {
567 __ movq(Operand(rbp, fp_relative(i)), source.reg());
568 }
569 break;
570
571 case FrameElement::CONSTANT:
572 if (!source.is_synced()) {
573 __ Move(Operand(rbp, fp_relative(i)), source.handle());
574 }
575 break;
576
577 case FrameElement::COPY:
578 if (!source.is_synced()) {
579 int backing_index = source.index();
580 FrameElement backing_element = elements_[backing_index];
581 if (backing_element.is_memory()) {
582 __ movq(kScratchRegister,
583 Operand(rbp, fp_relative(backing_index)));
584 __ movq(Operand(rbp, fp_relative(i)), kScratchRegister);
585 } else {
586 ASSERT(backing_element.is_register());
587 __ movq(Operand(rbp, fp_relative(i)), backing_element.reg());
588 }
589 }
590 break;
591 }
592 }
593 elements_[i] = target;
594 }
595}
596
597
598void VirtualFrame::MergeMoveRegistersToRegisters(VirtualFrame* expected) {
599 // We have already done X-to-memory moves.
600 ASSERT(stack_pointer_ >= expected->stack_pointer_);
601
602 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
603 // Move the right value into register i if it is currently in a register.
604 int index = expected->register_location(i);
605 int use_index = register_location(i);
606 // Skip if register i is unused in the target or else if source is
607 // not a register (this is not a register-to-register move).
608 if (index == kIllegalIndex || !elements_[index].is_register()) continue;
609
610 Register target = RegisterAllocator::ToRegister(i);
611 Register source = elements_[index].reg();
612 if (index != use_index) {
613 if (use_index == kIllegalIndex) { // Target is currently unused.
614 // Copy contents of source from source to target.
615 // Set frame element register to target.
616 Use(target, index);
617 Unuse(source);
618 __ movq(target, source);
619 } else {
620 // Exchange contents of registers source and target.
621 // Nothing except the register backing use_index has changed.
622 elements_[use_index].set_reg(source);
623 set_register_location(target, index);
624 set_register_location(source, use_index);
625 __ xchg(source, target);
626 }
627 }
628
629 if (!elements_[index].is_synced() &&
630 expected->elements_[index].is_synced()) {
631 __ movq(Operand(rbp, fp_relative(index)), target);
632 }
633 elements_[index] = expected->elements_[index];
634 }
635}
636
637
638void VirtualFrame::MergeMoveMemoryToRegisters(VirtualFrame* expected) {
639 // Move memory, constants, and copies to registers. This is the
640 // final step and since it is not done from the bottom up, but in
641 // register code order, we have special code to ensure that the backing
642 // elements of copies are in their correct locations when we
643 // encounter the copies.
644 for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
645 int index = expected->register_location(i);
646 if (index != kIllegalIndex) {
647 FrameElement source = elements_[index];
648 FrameElement target = expected->elements_[index];
649 Register target_reg = RegisterAllocator::ToRegister(i);
650 ASSERT(target.reg().is(target_reg));
651 switch (source.type()) {
652 case FrameElement::INVALID: // Fall through.
653 UNREACHABLE();
654 break;
655 case FrameElement::REGISTER:
656 ASSERT(source.Equals(target));
657 // Go to next iteration. Skips Use(target_reg) and syncing
658 // below. It is safe to skip syncing because a target
659 // register frame element would only be synced if all source
660 // elements were.
661 continue;
662 break;
663 case FrameElement::MEMORY:
664 ASSERT(index <= stack_pointer_);
665 __ movq(target_reg, Operand(rbp, fp_relative(index)));
666 break;
667
668 case FrameElement::CONSTANT:
669 __ Move(target_reg, source.handle());
670 break;
671
672 case FrameElement::COPY: {
673 int backing_index = source.index();
674 FrameElement backing = elements_[backing_index];
675 ASSERT(backing.is_memory() || backing.is_register());
676 if (backing.is_memory()) {
677 ASSERT(backing_index <= stack_pointer_);
678 // Code optimization if backing store should also move
679 // to a register: move backing store to its register first.
680 if (expected->elements_[backing_index].is_register()) {
681 FrameElement new_backing = expected->elements_[backing_index];
682 Register new_backing_reg = new_backing.reg();
683 ASSERT(!is_used(new_backing_reg));
684 elements_[backing_index] = new_backing;
685 Use(new_backing_reg, backing_index);
686 __ movq(new_backing_reg,
687 Operand(rbp, fp_relative(backing_index)));
688 __ movq(target_reg, new_backing_reg);
689 } else {
690 __ movq(target_reg, Operand(rbp, fp_relative(backing_index)));
691 }
692 } else {
693 __ movq(target_reg, backing.reg());
694 }
695 }
696 }
697 // Ensure the proper sync state.
698 if (target.is_synced() && !source.is_synced()) {
699 __ movq(Operand(rbp, fp_relative(index)), target_reg);
700 }
701 Use(target_reg, index);
702 elements_[index] = target;
703 }
704 }
705}
706
707
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000708Result VirtualFrame::Pop() {
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000709 FrameElement element = elements_.RemoveLast();
710 int index = element_count();
711 ASSERT(element.is_valid());
712
713 bool pop_needed = (stack_pointer_ == index);
714 if (pop_needed) {
715 stack_pointer_--;
716 if (element.is_memory()) {
717 Result temp = cgen()->allocator()->Allocate();
718 ASSERT(temp.is_valid());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000719 __ pop(temp.reg());
720 return temp;
721 }
722
723 __ addq(rsp, Immediate(kPointerSize));
724 }
725 ASSERT(!element.is_memory());
726
727 // The top element is a register, constant, or a copy. Unuse
728 // registers and follow copies to their backing store.
729 if (element.is_register()) {
730 Unuse(element.reg());
731 } else if (element.is_copy()) {
732 ASSERT(element.index() < index);
733 index = element.index();
734 element = elements_[index];
735 }
736 ASSERT(!element.is_copy());
737
738 // The element is memory, a register, or a constant.
739 if (element.is_memory()) {
740 // Memory elements could only be the backing store of a copy.
741 // Allocate the original to a register.
742 ASSERT(index <= stack_pointer_);
743 Result temp = cgen()->allocator()->Allocate();
744 ASSERT(temp.is_valid());
745 Use(temp.reg(), index);
746 FrameElement new_element =
747 FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
748 // Preserve the copy flag on the element.
749 if (element.is_copied()) new_element.set_copied();
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000750 elements_[index] = new_element;
751 __ movq(temp.reg(), Operand(rbp, fp_relative(index)));
ager@chromium.org3e875802009-06-29 08:26:34 +0000752 return Result(temp.reg());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000753 } else if (element.is_register()) {
ager@chromium.org3e875802009-06-29 08:26:34 +0000754 return Result(element.reg());
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000755 } else {
756 ASSERT(element.is_constant());
757 return Result(element.handle());
758 }
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000759}
760
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000761
762Result VirtualFrame::RawCallStub(CodeStub* stub) {
763 ASSERT(cgen()->HasValidEntryRegisters());
764 __ CallStub(stub);
765 Result result = cgen()->allocator()->Allocate(rax);
766 ASSERT(result.is_valid());
767 return result;
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000768}
769
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000770
ager@chromium.org3e875802009-06-29 08:26:34 +0000771Result VirtualFrame::CallStub(CodeStub* stub, Result* arg) {
772 PrepareForCall(0, 0);
773 arg->ToRegister(rax);
774 arg->Unuse();
775 return RawCallStub(stub);
776}
777
778
779Result VirtualFrame::CallStub(CodeStub* stub, Result* arg0, Result* arg1) {
780 PrepareForCall(0, 0);
781
782 if (arg0->is_register() && arg0->reg().is(rax)) {
783 if (arg1->is_register() && arg1->reg().is(rdx)) {
784 // Wrong registers.
785 __ xchg(rax, rdx);
786 } else {
787 // Register rdx is free for arg0, which frees rax for arg1.
788 arg0->ToRegister(rdx);
789 arg1->ToRegister(rax);
790 }
791 } else {
792 // Register rax is free for arg1, which guarantees rdx is free for
793 // arg0.
794 arg1->ToRegister(rax);
795 arg0->ToRegister(rdx);
796 }
797
798 arg0->Unuse();
799 arg1->Unuse();
800 return RawCallStub(stub);
801}
802
803
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000804void VirtualFrame::SyncElementBelowStackPointer(int index) {
805 // Emit code to write elements below the stack pointer to their
806 // (already allocated) stack address.
807 ASSERT(index <= stack_pointer_);
808 FrameElement element = elements_[index];
809 ASSERT(!element.is_synced());
810 switch (element.type()) {
811 case FrameElement::INVALID:
812 break;
813
814 case FrameElement::MEMORY:
815 // This function should not be called with synced elements.
816 // (memory elements are always synced).
817 UNREACHABLE();
818 break;
819
820 case FrameElement::REGISTER:
821 __ movq(Operand(rbp, fp_relative(index)), element.reg());
822 break;
823
824 case FrameElement::CONSTANT:
825 __ Move(Operand(rbp, fp_relative(index)), element.handle());
826 break;
827
828 case FrameElement::COPY: {
829 int backing_index = element.index();
830 FrameElement backing_element = elements_[backing_index];
831 if (backing_element.is_memory()) {
832 __ movq(kScratchRegister, Operand(rbp, fp_relative(backing_index)));
833 __ movq(Operand(rbp, fp_relative(index)), kScratchRegister);
834 } else {
835 ASSERT(backing_element.is_register());
836 __ movq(Operand(rbp, fp_relative(index)), backing_element.reg());
837 }
838 break;
839 }
840 }
841 elements_[index].set_sync();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000842}
843
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000844
845void VirtualFrame::SyncElementByPushing(int index) {
846 // Sync an element of the frame that is just above the stack pointer
847 // by pushing it.
848 ASSERT(index == stack_pointer_ + 1);
849 stack_pointer_++;
850 FrameElement element = elements_[index];
851
852 switch (element.type()) {
853 case FrameElement::INVALID:
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000854 __ Push(Smi::FromInt(0));
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000855 break;
856
857 case FrameElement::MEMORY:
858 // No memory elements exist above the stack pointer.
859 UNREACHABLE();
860 break;
861
862 case FrameElement::REGISTER:
863 __ push(element.reg());
864 break;
865
866 case FrameElement::CONSTANT:
867 __ Move(kScratchRegister, element.handle());
868 __ push(kScratchRegister);
869 break;
870
871 case FrameElement::COPY: {
872 int backing_index = element.index();
873 FrameElement backing = elements_[backing_index];
874 ASSERT(backing.is_memory() || backing.is_register());
875 if (backing.is_memory()) {
876 __ push(Operand(rbp, fp_relative(backing_index)));
877 } else {
878 __ push(backing.reg());
879 }
880 break;
881 }
882 }
883 elements_[index].set_sync();
ager@chromium.orgeadaf222009-06-16 09:43:10 +0000884}
885
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000886
887// Clear the dirty bits for the range of elements in
888// [min(stack_pointer_ + 1,begin), end].
889void VirtualFrame::SyncRange(int begin, int end) {
890 ASSERT(begin >= 0);
891 ASSERT(end < element_count());
892 // Sync elements below the range if they have not been materialized
893 // on the stack.
894 int start = Min(begin, stack_pointer_ + 1);
895
ager@chromium.org7cf3b1d2009-11-11 09:17:04 +0000896 // If positive we have to adjust the stack pointer.
897 int delta = end - stack_pointer_;
898 if (delta > 0) {
899 stack_pointer_ = end;
900 __ subq(rsp, Immediate(delta * kPointerSize));
901 }
902
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000903 for (int i = start; i <= end; i++) {
ager@chromium.org7cf3b1d2009-11-11 09:17:04 +0000904 if (!elements_[i].is_synced()) SyncElementBelowStackPointer(i);
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000905 }
906}
907
ager@chromium.org3e875802009-06-29 08:26:34 +0000908
909Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
910 InvokeFlag flag,
911 int arg_count) {
912 PrepareForCall(arg_count, arg_count);
913 ASSERT(cgen()->HasValidEntryRegisters());
914 __ InvokeBuiltin(id, flag);
915 Result result = cgen()->allocator()->Allocate(rax);
916 ASSERT(result.is_valid());
917 return result;
918}
919
920
ager@chromium.org5aa501c2009-06-23 07:57:28 +0000921//------------------------------------------------------------------------------
922// Virtual frame stub and IC calling functions.
923
924Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
925 RelocInfo::Mode rmode) {
926 ASSERT(cgen()->HasValidEntryRegisters());
927 __ Call(code, rmode);
928 Result result = cgen()->allocator()->Allocate(rax);
929 ASSERT(result.is_valid());
930 return result;
931}
932
933
934Result VirtualFrame::CallRuntime(Runtime::Function* f, int arg_count) {
935 PrepareForCall(arg_count, arg_count);
936 ASSERT(cgen()->HasValidEntryRegisters());
937 __ CallRuntime(f, arg_count);
938 Result result = cgen()->allocator()->Allocate(rax);
939 ASSERT(result.is_valid());
940 return result;
941}
942
943
944Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
945 PrepareForCall(arg_count, arg_count);
946 ASSERT(cgen()->HasValidEntryRegisters());
947 __ CallRuntime(id, arg_count);
948 Result result = cgen()->allocator()->Allocate(rax);
949 ASSERT(result.is_valid());
950 return result;
951}
952
953
954Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
955 // Name and receiver are on the top of the frame. The IC expects
956 // name in rcx and receiver on the stack. It does not drop the
957 // receiver.
958 Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
959 Result name = Pop();
960 PrepareForCall(1, 0); // One stack arg, not callee-dropped.
961 name.ToRegister(rcx);
962 name.Unuse();
963 return RawCallCodeObject(ic, mode);
964}
965
966
967Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
968 // Key and receiver are on top of the frame. The IC expects them on
969 // the stack. It does not drop them.
970 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
971 PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
972 return RawCallCodeObject(ic, mode);
973}
974
975
976Result VirtualFrame::CallKeyedStoreIC() {
977 // Value, key, and receiver are on the top of the frame. The IC
978 // expects value in rax and key and receiver on the stack. It does
979 // not drop the key and receiver.
980 Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
981 // TODO(1222589): Make the IC grab the values from the stack.
982 Result value = Pop();
983 PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
984 value.ToRegister(rax);
985 value.Unuse();
986 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
987}
988
989
990Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
991 int arg_count,
992 int loop_nesting) {
993 // Arguments, receiver, and function name are on top of the frame.
994 // The IC expects them on the stack. It does not drop the function
995 // name slot (but it does drop the rest).
996 InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
997 Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop);
998 // Spill args, receiver, and function. The call will drop args and
999 // receiver.
1000 PrepareForCall(arg_count + 2, arg_count + 1);
1001 return RawCallCodeObject(ic, mode);
1002}
1003
1004
ager@chromium.org3e875802009-06-29 08:26:34 +00001005Result VirtualFrame::CallConstructor(int arg_count) {
1006 // Arguments, receiver, and function are on top of the frame. The
1007 // IC expects arg count in rax, function in rdi, and the arguments
1008 // and receiver on the stack.
1009 Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
1010 // Duplicate the function before preparing the frame.
1011 PushElementAt(arg_count + 1);
1012 Result function = Pop();
1013 PrepareForCall(arg_count + 1, arg_count + 1); // Spill args and receiver.
1014 function.ToRegister(rdi);
1015
1016 // Constructors are called with the number of arguments in register
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +00001017 // rax for now. Another option would be to have separate construct
ager@chromium.org3e875802009-06-29 08:26:34 +00001018 // call trampolines per different arguments counts encountered.
1019 Result num_args = cgen()->allocator()->Allocate(rax);
1020 ASSERT(num_args.is_valid());
1021 __ movq(num_args.reg(), Immediate(arg_count));
1022
1023 function.Unuse();
1024 num_args.Unuse();
1025 return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL);
1026}
1027
1028
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001029Result VirtualFrame::CallStoreIC() {
1030 // Name, value, and receiver are on top of the frame. The IC
1031 // expects name in rcx, value in rax, and receiver on the stack. It
1032 // does not drop the receiver.
1033 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
1034 Result name = Pop();
1035 Result value = Pop();
1036 PrepareForCall(1, 0); // One stack arg, not callee-dropped.
1037
1038 if (value.is_register() && value.reg().is(rcx)) {
1039 if (name.is_register() && name.reg().is(rax)) {
1040 // Wrong registers.
1041 __ xchg(rax, rcx);
1042 } else {
1043 // Register rax is free for value, which frees rcx for name.
1044 value.ToRegister(rax);
1045 name.ToRegister(rcx);
1046 }
1047 } else {
1048 // Register rcx is free for name, which guarantees rax is free for
1049 // value.
1050 name.ToRegister(rcx);
1051 value.ToRegister(rax);
1052 }
1053
1054 name.Unuse();
1055 value.Unuse();
1056 return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001057}
1058
1059
ager@chromium.org3e875802009-06-29 08:26:34 +00001060void VirtualFrame::PushTryHandler(HandlerType type) {
1061 ASSERT(cgen()->HasValidEntryRegisters());
1062 // Grow the expression stack by handler size less one (the return
1063 // address is already pushed by a call instruction).
1064 Adjust(kHandlerSize - 1);
1065 __ PushTryHandler(IN_JAVASCRIPT, type);
1066}
1067
1068
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001069#undef __
1070
1071} } // namespace v8::internal